From 21cf0a130106a5e46c6dd02d1efe96ebdd44a570 Mon Sep 17 00:00:00 2001 From: Ryan Tsang Date: Fri, 8 Nov 2024 23:13:11 -0500 Subject: [PATCH 1/3] WIP for number dragger outside editor window --- package-lock.json | 160 +++++++++--------- .../InteractiveWidgets/numberDragger.ts | 58 +++++++ 2 files changed, 138 insertions(+), 80 deletions(-) diff --git a/package-lock.json b/package-lock.json index a914c29d..ddc7f514 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1788,9 +1788,10 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "license": "MIT", "peer": true, "bin": { "acorn": "bin/acorn" @@ -1799,6 +1800,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-typescript": { + "version": "1.4.13", + "resolved": "https://registry.npmjs.org/acorn-typescript/-/acorn-typescript-1.4.13.tgz", + "integrity": "sha512-xsc9Xv0xlVfwp2o7sQ+GCQ1PgbkdcpWdTzrwXxO3xDMTAywVS3oXVOcOHuRjAPkS4P9b+yc/qNF15460v+jp4Q==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "acorn": ">=8.9.0" + } + }, "node_modules/agentkeepalive": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", @@ -1832,12 +1843,13 @@ } }, "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "license": "Apache-2.0", "peer": true, - "dependencies": { - "dequal": "^2.0.3" + "engines": { + "node": ">= 0.4" } }, "node_modules/array-flatten": { @@ -1870,12 +1882,13 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axobject-query": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", - "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "license": "Apache-2.0", "peer": true, - "dependencies": { - "dequal": "^2.0.3" + "engines": { + "node": ">= 0.4" } }, "node_modules/body-parser": { @@ -2132,19 +2145,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/code-red": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz", - "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==", - "peer": true, - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.4.15", - "@types/estree": "^1.0.1", - "acorn": "^8.10.0", - "estree-walker": "^3.0.3", - "periscopic": "^3.1.0" - } - }, "node_modules/codemirror": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz", @@ -2347,9 +2347,10 @@ "dev": true }, "node_modules/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -2396,19 +2397,6 @@ "node": ">= 8" } }, - "node_modules/css-tree": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz", - "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==", - "peer": true, - "dependencies": { - "mdn-data": "2.0.30", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -2747,10 +2735,29 @@ "node": ">=0.8.0" } }, + "node_modules/esm-env": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/esm-env/-/esm-env-1.1.4.tgz", + "integrity": "sha512-oO82nKPHKkzIj/hbtuDYy/JHqBHFlMIW36SDiPCVsj87ntDLcWN+sJ1erdVryd4NxODacFTsdrIE3b7IamqbOg==", + "license": "MIT", + "peer": true + }, + "node_modules/esrap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/esrap/-/esrap-1.2.2.tgz", + "integrity": "sha512-F2pSJklxx1BlQIQgooczXCPHmcWpn6EsP5oo73LQfonG9fIlIENQ8vMmfGXeojP9MrkzUNAfyU5vdFlR9shHAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15", + "@types/estree": "^1.0.1" + } + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, "dependencies": { "@types/estree": "^1.0.0" } @@ -2772,16 +2779,17 @@ } }, "node_modules/express": { - "version": "4.21.0", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", - "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", + "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", + "license": "MIT", "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.6.0", + "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -3322,6 +3330,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz", "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==", + "license": "MIT", "peer": true, "dependencies": { "@types/estree": "*" @@ -3474,12 +3483,6 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, - "node_modules/mdn-data": { - "version": "2.0.30", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz", - "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==", - "peer": true - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -3821,17 +3824,6 @@ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, - "node_modules/periscopic": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz", - "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==", - "peer": true, - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^3.0.0", - "is-reference": "^3.0.0" - } - }, "node_modules/picocolors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", @@ -4466,6 +4458,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, "engines": { "node": ">=0.10.0" } @@ -4534,28 +4527,28 @@ } }, "node_modules/svelte": { - "version": "4.2.12", - "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.12.tgz", - "integrity": "sha512-d8+wsh5TfPwqVzbm4/HCXC783/KPHV60NvwitJnyTA5lWn1elhXMNWhXGCJ7PwPa8qFUnyJNIyuIRt2mT0WMug==", + "version": "5.1.13", + "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.1.13.tgz", + "integrity": "sha512-xVNk8yLsZNfkyqWzVg8+nfU9ewiSjVW0S4qyTxfKa6Y7P5ZBhA+LDsh2cHWIXJQMltikQAk6W3sqGdQZSH58PA==", + "license": "MIT", "peer": true, "dependencies": { - "@ampproject/remapping": "^2.2.1", - "@jridgewell/sourcemap-codec": "^1.4.15", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/estree": "^1.0.1", - "acorn": "^8.9.0", - "aria-query": "^5.3.0", - "axobject-query": "^4.0.0", - "code-red": "^1.0.3", - "css-tree": "^2.3.1", - "estree-walker": "^3.0.3", - "is-reference": "^3.0.1", + "@ampproject/remapping": "^2.3.0", + "@jridgewell/sourcemap-codec": "^1.5.0", + "@types/estree": "^1.0.5", + "acorn": "^8.12.1", + "acorn-typescript": "^1.4.13", + "aria-query": "^5.3.1", + "axobject-query": "^4.1.0", + "esm-env": "^1.0.0", + "esrap": "^1.2.2", + "is-reference": "^3.0.2", "locate-character": "^3.0.0", - "magic-string": "^0.30.4", - "periscopic": "^3.1.0" + "magic-string": "^0.30.11", + "zimmerframe": "^1.1.2" }, "engines": { - "node": ">=16" + "node": ">=18" } }, "node_modules/tinybench": { @@ -5078,6 +5071,13 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } + }, + "node_modules/zimmerframe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/zimmerframe/-/zimmerframe-1.1.2.tgz", + "integrity": "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w==", + "license": "MIT", + "peer": true } } } diff --git a/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts b/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts index 1d83a81b..4fc9b03e 100644 --- a/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +++ b/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts @@ -18,3 +18,61 @@ export const numberDragger = ( if (onInteract) onInteract(); }, }); + +/* + //WIP + // Error: Dragging number offscreen currently changes value randomly + let dragging = false; + + let globalText: string; + let globalSetText: (newText: string) => void; + + + const handleMouseMove = (e: MouseEvent, text: string, setText: (newText: string) => void) => { + + if (!dragging) return; + + const newVal = Number(text) + e.movementX; + // Change newVal to save current start position and non dragging position to calculate new position? + if (!isNaN(newVal)) { + setText(newVal.toString()); + if (onInteract) onInteract(); + } + }; + + const handleMouseUp = () => { + dragging = false; + + window.removeEventListener('mousemove', handleMouseMoveWrapper); + window.removeEventListener('mouseup', handleMouseUp); + + }; + + const handleMouseMoveWrapper = (e: MouseEvent) => { + if (dragging) { + e.preventDefault(); + } + + handleMouseMove(e, globalText, globalSetText); + }; + + + + return { + // Handle user drag value + regexp: numberDraggerRegex, + cursor: 'ew-resize', + onDrag: (text, setText, e) => { + dragging = true; + globalText = text; + globalSetText = setText; + + // Listen for global events + window.addEventListener('mousemove', handleMouseMoveWrapper); + window.addEventListener('mouseup', handleMouseUp); + }, + }; +}; +*/ + + From cac1112886589acaef1b0bb5148b5a24a35e0964 Mon Sep 17 00:00:00 2001 From: Ryan Tsang Date: Mon, 18 Nov 2024 23:35:15 -0500 Subject: [PATCH 2/3] WIP for number dragging outside of code editor window --- .../InteractiveWidgets/numberDragger.ts | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts b/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts index 4fc9b03e..7413bbcc 100644 --- a/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +++ b/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts @@ -1,78 +1,78 @@ import { InteractRule } from '@replit/codemirror-interact'; -const numberDraggerRegex = /(? void, -): InteractRule => ({ - // the regexp matching the value - regexp: numberDraggerRegex, - // set cursor to "ew-resize" on hover - cursor: 'ew-resize', - // change number value based on mouse X movement on drag - onDrag: (text, setText, e) => { - const newVal = Number(text) + e.movementX; - if (isNaN(newVal)) return; - setText(newVal.toString()); - if (onInteract) onInteract(); - }, -}); - -/* - //WIP - // Error: Dragging number offscreen currently changes value randomly - let dragging = false; +let currentValue = 0; +let startValue = 0; +let startX = 0; - let globalText: string; - let globalSetText: (newText: string) => void; +// Mouse move tracker +// If user it not moving mouse or not setting text return +// Update position to user position + the starting value of when suer drags value +const GlobalMouseMove = (e) => { + if (!isDragging || !activeSetText) return; - const handleMouseMove = (e: MouseEvent, text: string, setText: (newText: string) => void) => { + const delta = e.clientX - startX; + currentValue = startValue + delta; + if (!isNaN(currentValue)) { + activeSetText(currentValue.toString()); + } +}; - if (!dragging) return; - - const newVal = Number(text) + e.movementX; - // Change newVal to save current start position and non dragging position to calculate new position? - if (!isNaN(newVal)) { - setText(newVal.toString()); - if (onInteract) onInteract(); - } - }; +// Global mouse up listener +const GlobalMouseUp = () => { + if (!isDragging) return; + isDragging = false; + activeSetText = null; - const handleMouseUp = () => { - dragging = false; + document.removeEventListener('mousemove', GlobalMouseMove); + document.removeEventListener('mouseup', GlobalMouseUp); +}; - window.removeEventListener('mousemove', handleMouseMoveWrapper); - window.removeEventListener('mouseup', handleMouseUp); - - }; +export const numberDragger = ( + onInteract?: () => void, +): InteractRule => ({ - const handleMouseMoveWrapper = (e: MouseEvent) => { - if (dragging) { - e.preventDefault(); + regexp: /(? { + // Initialize drag state + if (!isDragging) { + isDragging = true; + startValue = Number(text); + currentValue = startValue; + startX = e.clientX; + activeSetText = setText; + + document.addEventListener('mousemove', GlobalMouseMove); + document.addEventListener('mouseup', GlobalMouseUp); } - handleMouseMove(e, globalText, globalSetText); - }; + if (onInteract) onInteract(); + }, +}); - - return { - // Handle user drag value - regexp: numberDraggerRegex, - cursor: 'ew-resize', - onDrag: (text, setText, e) => { - dragging = true; - globalText = text; - globalSetText = setText; - - // Listen for global events - window.addEventListener('mousemove', handleMouseMoveWrapper); - window.addEventListener('mouseup', handleMouseUp); - }, - }; -}; -*/ + +// const numberDraggerRegex = /(? void, +// ): InteractRule => ({ +// // the regexp matching the value +// regexp: numberDraggerRegex, +// // set cursor to "ew-resize" on hover +// cursor: 'ew-resize', +// // change number value based on mouse X movement on drag +// onDrag: (text, setText, e) => { +// const newVal = Number(text) + e.movementX; +// if (isNaN(newVal)) return; +// setText(newVal.toString()); +// if (onInteract) onInteract(); +// }, +//}); From 591cef4056bd13aba132d796a7f228698c83f6c3 Mon Sep 17 00:00:00 2001 From: Ryan Tsang Date: Tue, 19 Nov 2024 16:25:17 -0500 Subject: [PATCH 3/3] User can drag number outside of editor with number preview above number --- .../InteractiveWidgets/numberDragger.ts | 86 ++++++++++++++----- 1 file changed, 65 insertions(+), 21 deletions(-) diff --git a/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts b/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts index 7413bbcc..c71feda0 100644 --- a/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts +++ b/src/client/CodeEditor/InteractiveWidgets/numberDragger.ts @@ -1,56 +1,100 @@ import { InteractRule } from '@replit/codemirror-interact'; - let isDragging = false; let activeSetText = null; - -let currentValue = 0; let startValue = 0; let startX = 0; +let preview = null; + +// Create preview element to prevent number drag issues +/*Note: + Previous edit had bug where if user drags the number from 3 digit value + to 4 digit value the number being dragged would have text set to 4 digit value + and the 4th digit would be printed to the right + + If user dragged it to left then going from 3 to 2 digit value would delete the character + to the right and lines after the number value each time the value was dragged to the left. +*/ +const createPreview = () => { + const previewElement = document.createElement('div'); + previewElement.style.position = 'fixed'; + previewElement.style.backgroundColor = '#2a2a2a'; + previewElement.style.color = 'white'; + previewElement.style.padding = '4px 8px'; + previewElement.style.borderRadius = '4px'; + previewElement.style.pointerEvents = 'none'; + previewElement.style.zIndex = '100'; + previewElement.style.fontFamily = 'monospace'; + previewElement.id = 'number-preview'; + document.body.appendChild(previewElement); + + return previewElement; +}; +// Replace updating number with set text make a preview number that is constantly updated +const updatePreview = (originalElement, value, initialPosition = false) => { + if (!preview) preview = createPreview(); + + preview.textContent = value.toString(); + const previewPos = originalElement.getBoundingClientRect(); + + preview.style.left = `${previewPos.left}px`; + preview.style.top = `${previewPos.top - preview.offsetHeight - 5}px`; +}; -// Mouse move tracker -// If user it not moving mouse or not setting text return -// Update position to user position + the starting value of when suer drags value +// Global mouse move handler const GlobalMouseMove = (e) => { if (!isDragging || !activeSetText) return; - const delta = e.clientX - startX; - currentValue = startValue + delta; + const currentValue = startValue + delta; + if (!isNaN(currentValue)) { - activeSetText(currentValue.toString()); + preview.textContent = currentValue.toString(); } }; -// Global mouse up listener -const GlobalMouseUp = () => { +// Global mouse up handler +const GlobalMouseUp = (e) => { if (!isDragging) return; + + if (preview && activeSetText) { + const finalValue = parseInt(preview.textContent || '0', 10); + // ParseInt to get integer value | 10 for ints base 10 + if (!isNaN(finalValue)) { + activeSetText(finalValue.toString()); + } + + document.body.removeChild(preview); + preview = null; + } + isDragging = false; activeSetText = null; - document.removeEventListener('mousemove', GlobalMouseMove); - document.removeEventListener('mouseup', GlobalMouseUp); + window.removeEventListener('mousemove', GlobalMouseMove); + window.removeEventListener('mouseup', GlobalMouseUp); }; export const numberDragger = ( onInteract?: () => void, ): InteractRule => ({ - - regexp: /(? { - // Initialize drag state if (!isDragging) { + isDragging = true; - startValue = Number(text); - currentValue = startValue; + startValue = parseInt(text, 10); startX = e.clientX; activeSetText = setText; - document.addEventListener('mousemove', GlobalMouseMove); - document.addEventListener('mouseup', GlobalMouseUp); - } + preview = createPreview(); + updatePreview(e.target, startValue); + + window.addEventListener('mousemove', GlobalMouseMove); + window.addEventListener('mouseup', GlobalMouseUp); + } if (onInteract) onInteract(); }, });