@node-edit-utils/core 2.0.5 → 2.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/node-tools/highlight/updateHighlightFrameVisibility.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +29 -2
- package/dist/node-edit-utils.esm.js +29 -2
- package/dist/node-edit-utils.umd.js +29 -2
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/lib/node-tools/createNodeTools.ts +24 -0
- package/src/lib/node-tools/highlight/updateHighlightFrameVisibility.ts +10 -0
- package/src/lib/node-tools/select/selectNode.ts +1 -1
- package/src/lib/styles/styles.css +7 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const updateHighlightFrameVisibility: (node: HTMLElement, nodeProvider: HTMLElement) => void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.0.
|
|
4
|
+
* @version 2.0.6
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -156,7 +156,7 @@ const selectNode = (event, editableNode) => {
|
|
|
156
156
|
const clickX = event.clientX;
|
|
157
157
|
const clickY = event.clientY;
|
|
158
158
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
159
|
-
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
|
|
159
|
+
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
|
|
160
160
|
if (editableNode && candidates.includes(editableNode)) {
|
|
161
161
|
return editableNode;
|
|
162
162
|
}
|
|
@@ -304,6 +304,14 @@ const refreshHighlightFrame = (node, nodeProvider) => {
|
|
|
304
304
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
305
305
|
};
|
|
306
306
|
|
|
307
|
+
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
308
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
309
|
+
if (!frame)
|
|
310
|
+
return;
|
|
311
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
312
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
313
|
+
};
|
|
314
|
+
|
|
307
315
|
const disableCanvasKeyboard = () => {
|
|
308
316
|
const disable = getCanvasWindowValue(["keyboard", "disable"]);
|
|
309
317
|
disable?.();
|
|
@@ -440,6 +448,7 @@ const nodeText = () => {
|
|
|
440
448
|
const createNodeTools = (element) => {
|
|
441
449
|
const nodeProvider = element;
|
|
442
450
|
let resizeObserver = null;
|
|
451
|
+
let mutationObserver = null;
|
|
443
452
|
let selectedNode = null;
|
|
444
453
|
const text = nodeText();
|
|
445
454
|
const throttledFrameRefresh = withRAFThrottle(refreshHighlightFrame);
|
|
@@ -452,10 +461,14 @@ const createNodeTools = (element) => {
|
|
|
452
461
|
clearHighlightFrame(nodeProvider);
|
|
453
462
|
selectedNode = null;
|
|
454
463
|
resizeObserver?.disconnect();
|
|
464
|
+
mutationObserver?.disconnect();
|
|
455
465
|
}
|
|
456
466
|
}
|
|
457
467
|
};
|
|
458
468
|
const selectNode = (node) => {
|
|
469
|
+
if (selectedNode === node) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
459
472
|
if (text.isEditing()) {
|
|
460
473
|
const currentEditable = text.getEditableNode();
|
|
461
474
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -463,21 +476,34 @@ const createNodeTools = (element) => {
|
|
|
463
476
|
}
|
|
464
477
|
}
|
|
465
478
|
resizeObserver?.disconnect();
|
|
479
|
+
mutationObserver?.disconnect();
|
|
466
480
|
if (node && nodeProvider) {
|
|
467
481
|
text.enableEditMode(node, nodeProvider);
|
|
468
482
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
469
483
|
throttledFrameRefresh(node, nodeProvider);
|
|
470
484
|
});
|
|
485
|
+
mutationObserver = new MutationObserver(() => {
|
|
486
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
487
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
488
|
+
});
|
|
489
|
+
mutationObserver.observe(node, {
|
|
490
|
+
attributes: true,
|
|
491
|
+
attributeFilter: ["class"],
|
|
492
|
+
});
|
|
471
493
|
}
|
|
472
494
|
selectedNode = node;
|
|
473
495
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
474
496
|
highlightNode(node, nodeProvider) ?? null;
|
|
497
|
+
if (node && nodeProvider) {
|
|
498
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
499
|
+
}
|
|
475
500
|
};
|
|
476
501
|
// Setup event listener
|
|
477
502
|
const removeListeners = setupEventListener$1(nodeProvider, selectNode, handleEscape, text.getEditableNode);
|
|
478
503
|
const cleanup = () => {
|
|
479
504
|
removeListeners();
|
|
480
505
|
resizeObserver?.disconnect();
|
|
506
|
+
mutationObserver?.disconnect();
|
|
481
507
|
text.blurEditMode();
|
|
482
508
|
throttledFrameRefresh.cleanup();
|
|
483
509
|
};
|
|
@@ -491,6 +517,7 @@ const createNodeTools = (element) => {
|
|
|
491
517
|
clearHighlightFrame(nodeProvider);
|
|
492
518
|
selectedNode = null;
|
|
493
519
|
resizeObserver?.disconnect();
|
|
520
|
+
mutationObserver?.disconnect();
|
|
494
521
|
},
|
|
495
522
|
getEditableNode: () => text.getEditableNode(),
|
|
496
523
|
cleanup,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.0.
|
|
4
|
+
* @version 2.0.6
|
|
5
5
|
*/
|
|
6
6
|
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
7
7
|
function withRAFThrottle(func) {
|
|
@@ -154,7 +154,7 @@ const selectNode = (event, editableNode) => {
|
|
|
154
154
|
const clickX = event.clientX;
|
|
155
155
|
const clickY = event.clientY;
|
|
156
156
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
157
|
-
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
|
|
157
|
+
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
|
|
158
158
|
if (editableNode && candidates.includes(editableNode)) {
|
|
159
159
|
return editableNode;
|
|
160
160
|
}
|
|
@@ -302,6 +302,14 @@ const refreshHighlightFrame = (node, nodeProvider) => {
|
|
|
302
302
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
303
303
|
};
|
|
304
304
|
|
|
305
|
+
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
306
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
307
|
+
if (!frame)
|
|
308
|
+
return;
|
|
309
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
310
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
311
|
+
};
|
|
312
|
+
|
|
305
313
|
const disableCanvasKeyboard = () => {
|
|
306
314
|
const disable = getCanvasWindowValue(["keyboard", "disable"]);
|
|
307
315
|
disable?.();
|
|
@@ -438,6 +446,7 @@ const nodeText = () => {
|
|
|
438
446
|
const createNodeTools = (element) => {
|
|
439
447
|
const nodeProvider = element;
|
|
440
448
|
let resizeObserver = null;
|
|
449
|
+
let mutationObserver = null;
|
|
441
450
|
let selectedNode = null;
|
|
442
451
|
const text = nodeText();
|
|
443
452
|
const throttledFrameRefresh = withRAFThrottle(refreshHighlightFrame);
|
|
@@ -450,10 +459,14 @@ const createNodeTools = (element) => {
|
|
|
450
459
|
clearHighlightFrame(nodeProvider);
|
|
451
460
|
selectedNode = null;
|
|
452
461
|
resizeObserver?.disconnect();
|
|
462
|
+
mutationObserver?.disconnect();
|
|
453
463
|
}
|
|
454
464
|
}
|
|
455
465
|
};
|
|
456
466
|
const selectNode = (node) => {
|
|
467
|
+
if (selectedNode === node) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
457
470
|
if (text.isEditing()) {
|
|
458
471
|
const currentEditable = text.getEditableNode();
|
|
459
472
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -461,21 +474,34 @@ const createNodeTools = (element) => {
|
|
|
461
474
|
}
|
|
462
475
|
}
|
|
463
476
|
resizeObserver?.disconnect();
|
|
477
|
+
mutationObserver?.disconnect();
|
|
464
478
|
if (node && nodeProvider) {
|
|
465
479
|
text.enableEditMode(node, nodeProvider);
|
|
466
480
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
467
481
|
throttledFrameRefresh(node, nodeProvider);
|
|
468
482
|
});
|
|
483
|
+
mutationObserver = new MutationObserver(() => {
|
|
484
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
485
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
486
|
+
});
|
|
487
|
+
mutationObserver.observe(node, {
|
|
488
|
+
attributes: true,
|
|
489
|
+
attributeFilter: ["class"],
|
|
490
|
+
});
|
|
469
491
|
}
|
|
470
492
|
selectedNode = node;
|
|
471
493
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
472
494
|
highlightNode(node, nodeProvider) ?? null;
|
|
495
|
+
if (node && nodeProvider) {
|
|
496
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
497
|
+
}
|
|
473
498
|
};
|
|
474
499
|
// Setup event listener
|
|
475
500
|
const removeListeners = setupEventListener$1(nodeProvider, selectNode, handleEscape, text.getEditableNode);
|
|
476
501
|
const cleanup = () => {
|
|
477
502
|
removeListeners();
|
|
478
503
|
resizeObserver?.disconnect();
|
|
504
|
+
mutationObserver?.disconnect();
|
|
479
505
|
text.blurEditMode();
|
|
480
506
|
throttledFrameRefresh.cleanup();
|
|
481
507
|
};
|
|
@@ -489,6 +515,7 @@ const createNodeTools = (element) => {
|
|
|
489
515
|
clearHighlightFrame(nodeProvider);
|
|
490
516
|
selectedNode = null;
|
|
491
517
|
resizeObserver?.disconnect();
|
|
518
|
+
mutationObserver?.disconnect();
|
|
492
519
|
},
|
|
493
520
|
getEditableNode: () => text.getEditableNode(),
|
|
494
521
|
cleanup,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.0.
|
|
4
|
+
* @version 2.0.6
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
const clickX = event.clientX;
|
|
161
161
|
const clickY = event.clientY;
|
|
162
162
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
163
|
-
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
|
|
163
|
+
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
|
|
164
164
|
if (editableNode && candidates.includes(editableNode)) {
|
|
165
165
|
return editableNode;
|
|
166
166
|
}
|
|
@@ -308,6 +308,14 @@
|
|
|
308
308
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
309
309
|
};
|
|
310
310
|
|
|
311
|
+
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
312
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
313
|
+
if (!frame)
|
|
314
|
+
return;
|
|
315
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
316
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
317
|
+
};
|
|
318
|
+
|
|
311
319
|
const disableCanvasKeyboard = () => {
|
|
312
320
|
const disable = getCanvasWindowValue(["keyboard", "disable"]);
|
|
313
321
|
disable?.();
|
|
@@ -444,6 +452,7 @@
|
|
|
444
452
|
const createNodeTools = (element) => {
|
|
445
453
|
const nodeProvider = element;
|
|
446
454
|
let resizeObserver = null;
|
|
455
|
+
let mutationObserver = null;
|
|
447
456
|
let selectedNode = null;
|
|
448
457
|
const text = nodeText();
|
|
449
458
|
const throttledFrameRefresh = withRAFThrottle(refreshHighlightFrame);
|
|
@@ -456,10 +465,14 @@
|
|
|
456
465
|
clearHighlightFrame(nodeProvider);
|
|
457
466
|
selectedNode = null;
|
|
458
467
|
resizeObserver?.disconnect();
|
|
468
|
+
mutationObserver?.disconnect();
|
|
459
469
|
}
|
|
460
470
|
}
|
|
461
471
|
};
|
|
462
472
|
const selectNode = (node) => {
|
|
473
|
+
if (selectedNode === node) {
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
463
476
|
if (text.isEditing()) {
|
|
464
477
|
const currentEditable = text.getEditableNode();
|
|
465
478
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -467,21 +480,34 @@
|
|
|
467
480
|
}
|
|
468
481
|
}
|
|
469
482
|
resizeObserver?.disconnect();
|
|
483
|
+
mutationObserver?.disconnect();
|
|
470
484
|
if (node && nodeProvider) {
|
|
471
485
|
text.enableEditMode(node, nodeProvider);
|
|
472
486
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
473
487
|
throttledFrameRefresh(node, nodeProvider);
|
|
474
488
|
});
|
|
489
|
+
mutationObserver = new MutationObserver(() => {
|
|
490
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
491
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
492
|
+
});
|
|
493
|
+
mutationObserver.observe(node, {
|
|
494
|
+
attributes: true,
|
|
495
|
+
attributeFilter: ["class"],
|
|
496
|
+
});
|
|
475
497
|
}
|
|
476
498
|
selectedNode = node;
|
|
477
499
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
478
500
|
highlightNode(node, nodeProvider) ?? null;
|
|
501
|
+
if (node && nodeProvider) {
|
|
502
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
503
|
+
}
|
|
479
504
|
};
|
|
480
505
|
// Setup event listener
|
|
481
506
|
const removeListeners = setupEventListener$1(nodeProvider, selectNode, handleEscape, text.getEditableNode);
|
|
482
507
|
const cleanup = () => {
|
|
483
508
|
removeListeners();
|
|
484
509
|
resizeObserver?.disconnect();
|
|
510
|
+
mutationObserver?.disconnect();
|
|
485
511
|
text.blurEditMode();
|
|
486
512
|
throttledFrameRefresh.cleanup();
|
|
487
513
|
};
|
|
@@ -495,6 +521,7 @@
|
|
|
495
521
|
clearHighlightFrame(nodeProvider);
|
|
496
522
|
selectedNode = null;
|
|
497
523
|
resizeObserver?.disconnect();
|
|
524
|
+
mutationObserver?.disconnect();
|
|
498
525
|
},
|
|
499
526
|
getEditableNode: () => text.getEditableNode(),
|
|
500
527
|
cleanup,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas={})}(this,function(e){"use strict";function t(e){let t=null,n=null;const o=(...o)=>{n=o,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return o.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},o}const n=e=>{const t=window.canvas;return e.reduce((e,t)=>e?.[t],t)};function o(e){return e.querySelector(".highlight-frame")}const r=e=>{if(!e)return;const t=o(e);t&&t.remove()},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas={})}(this,function(e){"use strict";function t(e){let t=null,n=null;const o=(...o)=>{n=o,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return o.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},o}const n=e=>{const t=window.canvas;return e.reduce((e,t)=>e?.[t],t)};function o(e){return e.querySelector(".highlight-frame")}const r=e=>{if(!e)return;const t=o(e);t&&t.remove()},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],i=0;const l=(e,t)=>{let n=null;const o=e.clientX,r=e.clientY,l=e.metaKey||e.ctrlKey,d=((e,t)=>{const n=document.elementsFromPoint(e,t);return Array.from(n).reduce((e,t)=>e.found?e:"node-provider"===t.getAttribute("data-role")?(e.found=!0,e):(e.elements.push(t),e),{elements:[],found:!1}).elements})(o,r).filter(e=>!s.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none"));if(t&&d.includes(t))return t;if(l)return a=[],n=d[0],n;var c,u;u=d,(c=a).length===u.length&&c.every((e,t)=>e===u[t])?i<=d.length&&i++:i=0;return n=d[d.length-1-i],a=d,n},d=(e,t,n,o)=>{const s=e=>{((e,t)=>{if("markup-canvas"===e.data.source&&"canvas"===e.data.canvasName&&"zoom"===e.data.action){const t=e.data.data;console.log("zoom",t)}if("application"===e.data.source&&"selectedNodeChanged"===e.data.action){const n=e.data.data;console.log("selectedNodeChanged in node-edit-utils",n);const o=document.querySelector(`[data-node-id="${n}"]`);o&&(console.log("selectedNode",o),t?.(o))}})(e,t)},a=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(t),void o(null);o(l(e,n))})(n,e,o(),t)},i=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",s),document.addEventListener("click",a),document.addEventListener("keydown",i),()=>{window.removeEventListener("message",s),document.removeEventListener("click",a),document.removeEventListener("keydown",i)}};function c(e,t){const o=e.getBoundingClientRect(),r=t.getBoundingClientRect(),s=o.top-r.top,a=o.left-r.left,i=n(["zoom","current"])??1;return{top:parseFloat((s/i).toFixed(5)),left:parseFloat((a/i).toFixed(5)),width:Math.max(4,parseFloat((o.width/i).toFixed(5))),height:parseFloat((o.height/i).toFixed(5))}}const u=(e,t)=>{const n=document.createElement("div");n.className="node-tools",t.appendChild(n),((e,t)=>{const n=document.createElement("div");n.className="tag-label",n.textContent=e.tagName.toLowerCase(),t.appendChild(n)})(e,n)},m=(e,t)=>{if(!e)return;const r=o(t);r&&r.remove();const s=((e,t)=>{const{top:o,left:r,width:s,height:a}=c(e,t),i=n(["zoom","current"])??1;document.body.style.setProperty("--zoom",i.toString()),document.body.style.setProperty("--stroke-width",(2/i).toFixed(3));const l=document.createElement("div");l.classList.add("highlight-frame"),l.style.setProperty("--frame-top",`${o}px`),l.style.setProperty("--frame-left",`${r}px`),l.style.setProperty("--frame-width",`${s}px`),l.style.setProperty("--frame-height",`${a}px`);const d=document.createElementNS("http://www.w3.org/2000/svg","svg");d.classList.add("highlight-frame-svg");const u=document.createElementNS("http://www.w3.org/2000/svg","rect");return u.setAttribute("x","0"),u.setAttribute("y","0"),u.setAttribute("width","100%"),u.setAttribute("height","100%"),u.classList.add("highlight-frame-rect"),d.appendChild(u),l.appendChild(d),l})(e,t);"true"===e.contentEditable&&s.classList.add("is-editable"),u(e,s),t.appendChild(s)},p=(e,t)=>{const r=o(t),s=n(["zoom","current"])??1;if(!r)return;s>=.3?t.style.setProperty("--tool-opacity","1"):t.style.setProperty("--tool-opacity","0");const{top:a,left:i,width:l,height:d}=c(e,t);r.style.setProperty("--frame-top",`${a}px`),r.style.setProperty("--frame-left",`${i}px`),r.style.setProperty("--frame-width",`${l}px`),r.style.setProperty("--frame-height",`${d}px`)},v=(e,t)=>{const n=o(t);if(!n)return;const r=e.classList.contains("hidden")||e.classList.contains("select-none");n.style.display=r?"none":""},h=e=>{const t=e=>{"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),(()=>{const e=window.getSelection();if(e&&e.rangeCount>0){const t=e.getRangeAt(0);t.deleteContents();const n=document.createElement("br");t.insertNode(n),t.setStartAfter(n),t.setEndAfter(n),e.removeAllRanges(),e.addRange(t)}})())};return e.addEventListener("keydown",t),()=>{e.removeEventListener("keydown",t)}},f=(e,t)=>{const n=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,()=>{p(e,t)});return()=>n.disconnect()},y=()=>{let e=null,t=!1,o=null;const r=()=>{if(t||!e)return;t=!0;var r;(r=e).contentEditable="false",r.classList.remove("is-editable"),r.style.outline="none",(()=>{const e=n(["keyboard","enable"]);e?.()})(),o?.(),e=null,t=!1};return{enableEditMode:(t,s)=>{if(e===t)return;e&&e!==t&&r();const a=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(t);a&&(e=t,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(t),(()=>{const e=n(["keyboard","disable"]);e?.()})(),o=((e,t,n)=>{if(!t)return()=>{};e.addEventListener("blur",n);const o=h(e),r=f(e,t);return()=>{e.removeEventListener("blur",n),o(),r?.()}})(t,s,r))},blurEditMode:r,getEditableNode:()=>e,isEditing:()=>null!==e}},g=320,b=1680,w=[{name:"Mobile",rawValue:390,value:"320px"},{name:"Tablet Portrait",rawValue:768,value:"768px"},{name:"Tablet Landscape",rawValue:1024,value:"1024px"},{name:"Notebook",rawValue:1280,value:"1280px"},{name:"Desktop",rawValue:1680,value:"1680px"}],E=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(g,Math.min(b,n))})(n,(e.clientX-t)/o);return r},x=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{console.log("updateActivePreset",t);const n=e.querySelectorAll(".resize-preset-button");console.log("presetButtons",n),n.forEach((e,n)=>{n<w.length&&(w[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(){const e=document.querySelector(".transform-layer");if(!e)return{disconnect:()=>{}};const o=t(()=>{(()=>{const e=n(["zoom","current"]);document.body.style.setProperty("--zoom",e.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/e).toFixed(3)),document.body.dataset.zoom=e.toFixed(5),document.body.dataset.strokeWidth=(2/e).toFixed(3)})()}),r=new MutationObserver(()=>{o()});return r.observe(e,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0}),{disconnect:function(){o.cleanup(),r.disconnect()}}},e.createNodeTools=e=>{const n=e;let o=null,s=null,a=null;const i=y(),l=t(p),c=e=>{if(a!==e){if(i.isEditing()){const t=i.getEditableNode();t&&t!==e&&i.blurEditMode()}var t,r;o?.disconnect(),s?.disconnect(),e&&n&&(i.enableEditMode(e,n),o=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(n,()=>{l(e,n)}),s=new MutationObserver(()=>{l(e,n),v(e,n)}),s.observe(e,{attributes:!0,attributeFilter:["class"]})),a=e,t="selectedNodeChanged",r=e?.getAttribute("data-node-id")??null,window.parent.postMessage({source:"node-edit-utils",action:t,data:r,timestamp:Date.now()},"*"),m(e,n),e&&n&&v(e,n)}},u=d(n,c,()=>{i.isEditing()&&i.blurEditMode(),a&&n&&(r(n),a=null,o?.disconnect(),s?.disconnect())},i.getEditableNode),h={selectNode:c,getSelectedNode:()=>a,refreshHighlightFrame:()=>{l(a,n)},clearSelectedNode:()=>{r(n),a=null,o?.disconnect(),s?.disconnect()},getEditableNode:()=>i.getEditableNode(),cleanup:()=>{u(),o?.disconnect(),s?.disconnect(),i.blurEditMode(),l.cleanup()}};var f,g;return f="nodeTools",g=h,"undefined"!=typeof window&&(window[f]=g),h},e.createViewport=e=>{const n=document.querySelector(".canvas-container"),o=e.querySelector(".resize-handle");o&&o.remove();const r=(e=>{const t=document.createElement("div");return t.className="resize-handle",e.appendChild(t),t})(e);e.style.setProperty("--container-width","400px"),((e,t,n)=>{const o=document.createElement("div");o.className="resize-presets",w.forEach(e=>{const r=document.createElement("button");r.textContent=e.name,r.className="resize-preset-button",r.addEventListener("click",()=>{n(t,e.rawValue)}),o.appendChild(r)}),e.appendChild(o)})(r,e,x);let s=!1,a=0,i=0;const l=t(t=>{if(!s)return;n&&(n.style.cursor="ew-resize");const o=E(t,a,i);x(e,o)}),d=((e,t,n,o,r)=>(e.addEventListener("mousedown",t),document.addEventListener("mousemove",n),document.addEventListener("mouseup",o),window.addEventListener("blur",r),()=>{e.removeEventListener("mousedown",t),document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o),window.removeEventListener("blur",r)}))(r,t=>{t.preventDefault(),t.stopPropagation(),s=!0,a=t.clientX,i=e.offsetWidth},l,e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{x(e,t)},cleanup:()=>{s=!1,l?.cleanup(),d(),r.remove()}}}});
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;display:flex;gap:.25rem;justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:.25rem;position:absolute;transform:scale(calc(1/var(--zoom))) translate3d(0,100%,0);transform-origin:bottom left;transition:opacity .1s ease;z-index:10000}.highlight-frame{height:var(--frame-height);left:var(--frame-left);pointer-events:none;position:absolute;top:var(--frame-top);width:var(--frame-width);z-index:1000}.highlight-frame-svg{height:100%;left:0;overflow:visible;position:absolute;top:0;width:100%}.highlight-frame-rect{fill:none;stroke:oklch(
|
|
1
|
+
.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;display:flex;gap:.25rem;justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:.25rem;position:absolute;top:0;transform:scale(calc(1/var(--zoom))) translate3d(0,100%,0);transform-origin:bottom left;transition:opacity .1s ease;z-index:10000}.highlight-frame{font-family:Manrope,sans-serif;height:var(--frame-height);left:var(--frame-left);letter-spacing:.02em;pointer-events:none;position:absolute;top:var(--frame-top);width:var(--frame-width);z-index:1000}.highlight-frame-svg{height:100%;left:0;overflow:visible;position:absolute;top:0;width:100%}.highlight-frame-rect{fill:none;stroke:oklch(45.7% .24 277.023);stroke-width:var(--stroke-width)}.tag-label{align-items:center;background-color:oklch(45.7% .24 277.023);border-radius:.375rem;color:#fff;display:flex;font-size:.575rem;font-weight:500;height:1.25rem;justify-content:center;line-height:1;padding:0 .375rem;text-transform:uppercase}.viewport{position:relative;width:var(--container-width)}.resize-handle{align-items:center;cursor:ew-resize;display:flex;height:40px;justify-content:center;opacity:0;pointer-events:auto;position:absolute;right:-12px;top:1.25rem;transform:scale(calc(1/var(--zoom)));transform-origin:top left;transition:opacity .1s ease-in-out,visibility .1s ease-in-out;visibility:hidden;width:12px;z-index:10000}.viewport:hover .resize-handle{opacity:1;visibility:visible}.resize-handle:before{background:oklch(55.2% .016 285.938);border-radius:4px;content:"";height:32px;position:relative;right:0;top:0;transition:transform .2s ease-in-out;width:3px}.resize-handle:hover:before{transform:scaleY(1.3)}.resize-presets{display:flex;flex-direction:column;font-family:Manrope,sans-serif;gap:.25rem;left:0;letter-spacing:.02em;opacity:0;padding-left:1.25rem;position:absolute;top:0;transition:visibility .1s ease-in-out,opacity .1s ease-in-out;visibility:hidden}.resize-handle:hover .resize-presets{opacity:1;visibility:visible}.resize-preset-button{text-wrap:nowrap;backdrop-filter:blur(8px);background:oklch(55.2% .016 285.938/.7);border:none;border-radius:.5rem;color:#fff;cursor:pointer;font-size:.6875rem;padding:.25rem .5rem;text-align:left;transition:background .1s ease-in-out;width:fit-content;&:hover{background:oklch(55.2% .016 285.938/1)}&.is-active{background:oklch(45.7% .24 277.023)}}.is-editable{outline:none;user-select:text;&::selection{background:oklch(62.7% .265 303.9/.3)}&::-moz-selection{background:oklch(62.7% .265 303.9/.3)}&::-webkit-selection{background:oklch(62.7% .265 303.9/.3)}}
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import { setupEventListener } from "./events/setupEventListener";
|
|
|
6
6
|
import { clearHighlightFrame } from "./highlight/clearHighlightFrame";
|
|
7
7
|
import { highlightNode } from "./highlight/highlightNode";
|
|
8
8
|
import { refreshHighlightFrame } from "./highlight/refreshHighlightFrame";
|
|
9
|
+
import { updateHighlightFrameVisibility } from "./highlight/updateHighlightFrameVisibility";
|
|
9
10
|
import { nodeText } from "./text/nodeText";
|
|
10
11
|
import type { NodeTools } from "./types";
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
13
14
|
const nodeProvider = element;
|
|
14
15
|
|
|
15
16
|
let resizeObserver: ResizeObserver | null = null;
|
|
17
|
+
let mutationObserver: MutationObserver | null = null;
|
|
16
18
|
let selectedNode: HTMLElement | null = null;
|
|
17
19
|
|
|
18
20
|
const text = nodeText();
|
|
@@ -29,11 +31,16 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
29
31
|
selectedNode = null;
|
|
30
32
|
|
|
31
33
|
resizeObserver?.disconnect();
|
|
34
|
+
mutationObserver?.disconnect();
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
};
|
|
35
38
|
|
|
36
39
|
const selectNode = (node: HTMLElement | null): void => {
|
|
40
|
+
if (selectedNode === node) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
if (text.isEditing()) {
|
|
38
45
|
const currentEditable = text.getEditableNode();
|
|
39
46
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -42,6 +49,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
resizeObserver?.disconnect();
|
|
52
|
+
mutationObserver?.disconnect();
|
|
45
53
|
|
|
46
54
|
if (node && nodeProvider) {
|
|
47
55
|
text.enableEditMode(node, nodeProvider);
|
|
@@ -49,11 +57,25 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
49
57
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
50
58
|
throttledFrameRefresh(node, nodeProvider);
|
|
51
59
|
});
|
|
60
|
+
|
|
61
|
+
mutationObserver = new MutationObserver(() => {
|
|
62
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
63
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
mutationObserver.observe(node, {
|
|
67
|
+
attributes: true,
|
|
68
|
+
attributeFilter: ["class"],
|
|
69
|
+
});
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
selectedNode = node;
|
|
55
73
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
56
74
|
highlightNode(node, nodeProvider as HTMLElement) ?? null;
|
|
75
|
+
|
|
76
|
+
if (node && nodeProvider) {
|
|
77
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
78
|
+
}
|
|
57
79
|
};
|
|
58
80
|
|
|
59
81
|
// Setup event listener
|
|
@@ -62,6 +84,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
62
84
|
const cleanup = (): void => {
|
|
63
85
|
removeListeners();
|
|
64
86
|
resizeObserver?.disconnect();
|
|
87
|
+
mutationObserver?.disconnect();
|
|
65
88
|
|
|
66
89
|
text.blurEditMode();
|
|
67
90
|
throttledFrameRefresh.cleanup();
|
|
@@ -77,6 +100,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
77
100
|
clearHighlightFrame(nodeProvider);
|
|
78
101
|
selectedNode = null;
|
|
79
102
|
resizeObserver?.disconnect();
|
|
103
|
+
mutationObserver?.disconnect();
|
|
80
104
|
},
|
|
81
105
|
getEditableNode: () => text.getEditableNode(),
|
|
82
106
|
cleanup,
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getHighlightFrameElement } from "./helpers/getHighlightFrameElement";
|
|
2
|
+
|
|
3
|
+
export const updateHighlightFrameVisibility = (node: HTMLElement, nodeProvider: HTMLElement): void => {
|
|
4
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
5
|
+
if (!frame) return;
|
|
6
|
+
|
|
7
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
8
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
9
|
+
};
|
|
10
|
+
|
|
@@ -14,7 +14,7 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
|
|
|
14
14
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
15
15
|
|
|
16
16
|
const candidates = getElementsFromPoint(clickX, clickY).filter(
|
|
17
|
-
(element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase())
|
|
17
|
+
(element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none")
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
if (editableNode && candidates.includes(editableNode)) {
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
position: absolute;
|
|
17
17
|
left: 0;
|
|
18
18
|
bottom: 0;
|
|
19
|
+
top: 0;
|
|
19
20
|
gap: 0.25rem;
|
|
20
21
|
z-index: 10000;
|
|
21
22
|
transform: scale(calc(1 / var(--zoom))) translate3d(0, 100%, 0);
|
|
@@ -35,6 +36,8 @@
|
|
|
35
36
|
height: var(--frame-height);
|
|
36
37
|
z-index: 1000;
|
|
37
38
|
pointer-events: none;
|
|
39
|
+
font-family: "Manrope", sans-serif;
|
|
40
|
+
letter-spacing: 0.02em;
|
|
38
41
|
/* outline: calc(0.125em / var(--zoom)) solid oklch(62.7% 0.265 303.9); */
|
|
39
42
|
}
|
|
40
43
|
|
|
@@ -49,12 +52,12 @@
|
|
|
49
52
|
|
|
50
53
|
.highlight-frame-rect {
|
|
51
54
|
fill: none;
|
|
52
|
-
stroke: oklch(
|
|
55
|
+
stroke: oklch(45.7% 0.24 277.023);
|
|
53
56
|
stroke-width: var(--stroke-width);
|
|
54
57
|
}
|
|
55
58
|
|
|
56
59
|
.tag-label {
|
|
57
|
-
background-color: oklch(
|
|
60
|
+
background-color: oklch(45.7% 0.24 277.023);
|
|
58
61
|
color: white;
|
|
59
62
|
font-size: 0.575rem;
|
|
60
63
|
font-weight: 500;
|
|
@@ -125,6 +128,8 @@
|
|
|
125
128
|
gap: 0.25rem;
|
|
126
129
|
visibility: hidden;
|
|
127
130
|
opacity: 0;
|
|
131
|
+
font-family: "Manrope", sans-serif;
|
|
132
|
+
letter-spacing: 0.02em;
|
|
128
133
|
transition:
|
|
129
134
|
visibility 0.1s ease-in-out,
|
|
130
135
|
opacity 0.1s ease-in-out;
|