OnShape helper

Various tweaks for OnShape, such as remap F2 for rename (SHIFT + N)

// ==UserScript==
// @name         OnShape helper
// @namespace    V@no
// @version      25.6.3-005254
// @description  Various tweaks for OnShape, such as remap F2 for rename (SHIFT + N)
// @author       V@no
// @license      MIT
// @match        https://cad.onshape.com/documents/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=onshape.com
// @grant        none
// ==/UserScript==

{
	"use strict";
/*
^ = CTRL
! = ALT
+ = SHIFT
*/
	const map = {
		"F2": {key: "N", code: "KeyN", keyCode: 78, shiftKey: true}
	};

	let mouseEvent = {};
	document.addEventListener("mousemove", evt =>
	{
		mouseEvent = evt;
	}, false);

	document.body.addEventListener("keydown", evt =>
	{
		let modifier = "";
		modifier = evt.altKey ? "!" : "";
		modifier = evt.shiftKey ? "+" : "";
		modifier = evt.ctrlKey || evt.metaKey ? "^" : "";
		const key = modifier + evt.code;
		if (!evt.isTrusted || !(key in map) || evt.altKey || evt.shiftKey || evt.ctrlKey || evt.metaKey)
			return console.log(evt, mouseEvent);

		if (mouseEvent.target)
		{
			evt.target.dispatchEvent(new KeyboardEvent(evt.type, Object.assign({}, evt, {key: " ", code: "space", keyCode: 32}, {bubbles: true})));
			mouseEvent.target.dispatchEvent(new PointerEvent("click", mouseEvent));
		}

		evt.target.dispatchEvent(new KeyboardEvent(evt.type, Object.assign({}, evt, map[key], {bubbles: true})));
	}, true);
	const css = `
.dimension-edit-container .ns-feature-parameter .bti-numeric-text,
.dimension-edit-container os-quantity-parameter input,
.dimension-edit
{
	max-width: unset;
}

div.OSH::before,
div.OSH::after {
  box-sizing: border-box;
}

div.OSH {
  display: inline-grid;
  vertical-align: top;
  align-items: center;
  position: relative;
}

div.OSH::after,
div.OSH input
{
  width: auto;
  min-width: 1em;
  grid-area: 1/2;
  font: inherit;
  padding: 0 0.25em 0 0;
  margin: 0;
  resize: none;
  background: none;
  -webkit-appearance: none;
     -moz-appearance: none;
          appearance: none;
  border: none;
}

div.OSH::after {
  content: attr(data-value) " ";
  visibility: hidden;
  white-space: pre-wrap;
}

div.OSH_conf_row > .OSH_conf {
	font-size: x-large;
	padding: 0 0.2em;
	line-height: 1em;
}

div.OSH_conf_row > .OSH_conf:hover {
	background-color: var(--os-table-cell-fill--hover);
}

div.OSH_conf_row > .OSH_conf.UP {
  order: 1;
}

div.OSH_conf_row > .OSH_conf.DOWN {
  order: 2;
}

div.OSH_conf_row > :not(.OSH_conf) {
  order: 3;
}

div.moved {
  background-color: var(--os-alert-background-success);
}

div.single-table-container.os-virtual-scroll-section:first-child .UP,
div.single-table-container.os-virtual-scroll-section:last-child .DOWN {
	opacity: 0.5;
  	pointer-events: none;
}

`;
	const elStyle = document.createElement("style");
	elStyle.id = "onShapeHelper";
	elStyle.textContent = css;
	document.head.append(elStyle);

	const dataValue = (el, value) =>
	{
		el.dataset.value = value;
	};
	const observer = new MutationObserver((mutationList, _observer) =>
	{
		for (const mutation of mutationList)
		{
			for(const node of mutation.addedNodes)
			{
				if (node.nodeType !== 1)
					continue;
				if (node.matches("input:not(.OSH)"))
				{
					node.classList.add("OSH");
					node.parentElement.classList.add("OSH");
					dataValue(node.parentElement, node.value);//node.dataset.bsOriginalTitle);
					node.addEventListener("input", evt => dataValue(evt.target.parentElement, evt.target.value));
				}
				if (!node.classList.contains("OSH_conf"))
				{
					const nlNodes = node.querySelectorAll(`a:not(.OSH_conf)[ng-click="configurationTable.moveParameterUp()"], a:not(.OSH_conf)[ng-click="configurationTable.moveParameterDown()"`);
					if (nlNodes.length === 0)
						continue;

					node.classList.add("OSH_conf");
					for(let i = 0; i < nlNodes.length; i++)
					{
						const elA = nlNodes[i];
						elA.classList.add("OSH_conf");
						const elParent = elA.closest("div.os-table-header-responsive-last-row>div.d-flex");
						elParent.classList.add("OSH_conf_row");
						if (elParent.upDown === undefined)
							elParent.upDown = {};

						const type = elA.matches(`[ng-click="configurationTable.moveParameterUp()"]`);
						if (elParent.upDown[type])
							elParent.upDown[type].replaceWith(elA);
						else
							elParent.prepend(elA);


						elParent.upDown[type] = elA;
						elA.classList.add(type ? "UP" : "DOWN");
						elA.title = elA.textContent;
						elA.textContent = type ? "▲" : "▼";

						elA.addEventListener("click", () => moved(elA.parentElement.parentElement.parentElement));
					}
				}
			}
		}
	});
	observer.observe(document.body, {
		childList: true,
		subtree: true,
	});

	const moved = el =>
	{
		moved.clear();
		el.classList.add("moved");
		moved.el = el;
		moved.timer = setTimeout(moved.clear, 2000);

	};
	moved.clear = () =>
	{
		clearTimeout(moved.timer);
		if (moved.el)
		{
			moved.el.classList.remove("moved");
			moved.el = null;
		}
	};
	console.log("OnShape helper loaded", "https://greasyfork.org/en/scripts/522636");
}