@node-edit-utils/core 2.1.7 → 2.1.9
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/helpers/index.d.ts +1 -1
- package/dist/lib/helpers/withRAF.d.ts +3 -0
- package/dist/node-edit-utils.cjs.js +41 -9
- package/dist/node-edit-utils.esm.js +41 -9
- package/dist/node-edit-utils.umd.js +41 -9
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/package.json +1 -1
- package/src/lib/helpers/index.ts +1 -1
- package/src/lib/helpers/withRAF.ts +41 -0
- package/src/lib/node-tools/createNodeTools.ts +2 -4
- package/src/lib/node-tools/highlight/refreshHighlightFrame.ts +4 -0
- package/src/lib/post-message/processPostMessage.ts +2 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
export { withRAF, withRAFThrottle } from "./withRAF";
|
|
1
|
+
export { withRAF, withRAFThrottle, withDoubleRAF } from "./withRAF";
|
|
@@ -2,3 +2,6 @@ export declare function withRAF(operation: () => void): () => void;
|
|
|
2
2
|
export declare function withRAFThrottle<T extends (...args: any[]) => void>(func: T): T & {
|
|
3
3
|
cleanup: () => void;
|
|
4
4
|
};
|
|
5
|
+
export declare function withDoubleRAF<T extends (...args: any[]) => void>(func: T): T & {
|
|
6
|
+
cleanup: () => void;
|
|
7
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.1.
|
|
4
|
+
* @version 2.1.9
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -30,6 +30,42 @@ function withRAFThrottle(func) {
|
|
|
30
30
|
};
|
|
31
31
|
return throttled;
|
|
32
32
|
}
|
|
33
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
34
|
+
function withDoubleRAF(func) {
|
|
35
|
+
let rafId1 = null;
|
|
36
|
+
let rafId2 = null;
|
|
37
|
+
let lastArgs = null;
|
|
38
|
+
const throttled = (...args) => {
|
|
39
|
+
lastArgs = args;
|
|
40
|
+
if (rafId1 === null) {
|
|
41
|
+
rafId1 = requestAnimationFrame(() => {
|
|
42
|
+
// First RAF: let browser complete layout
|
|
43
|
+
rafId2 = requestAnimationFrame(() => {
|
|
44
|
+
// Second RAF: read bounds after layout is complete
|
|
45
|
+
if (lastArgs) {
|
|
46
|
+
const currentArgs = lastArgs;
|
|
47
|
+
rafId1 = null;
|
|
48
|
+
rafId2 = null;
|
|
49
|
+
lastArgs = null;
|
|
50
|
+
func(...currentArgs);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
throttled.cleanup = () => {
|
|
57
|
+
if (rafId1 !== null) {
|
|
58
|
+
cancelAnimationFrame(rafId1);
|
|
59
|
+
rafId1 = null;
|
|
60
|
+
}
|
|
61
|
+
if (rafId2 !== null) {
|
|
62
|
+
cancelAnimationFrame(rafId2);
|
|
63
|
+
rafId2 = null;
|
|
64
|
+
}
|
|
65
|
+
lastArgs = null;
|
|
66
|
+
};
|
|
67
|
+
return throttled;
|
|
68
|
+
}
|
|
33
69
|
|
|
34
70
|
const getCanvasWindowValue = (path) => {
|
|
35
71
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
@@ -102,15 +138,11 @@ const isLocked = (node) => {
|
|
|
102
138
|
};
|
|
103
139
|
|
|
104
140
|
const processPostMessage = (event, onNodeSelected) => {
|
|
105
|
-
// if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
106
|
-
// if (event.data.action === "zoom") {
|
|
107
|
-
// // Zoom handling can be implemented here if needed
|
|
108
|
-
// }
|
|
109
|
-
// }
|
|
110
141
|
if (event.data.source === "application") {
|
|
111
142
|
if (event.data.action === "selectedNodeChanged") {
|
|
112
143
|
const nodeId = event.data.data;
|
|
113
144
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
145
|
+
console.log("selectedNode", selectedNode);
|
|
114
146
|
if (isLocked(selectedNode)) {
|
|
115
147
|
onNodeSelected?.(null);
|
|
116
148
|
return;
|
|
@@ -294,6 +326,7 @@ const highlightNode = (node, nodeProvider) => {
|
|
|
294
326
|
const refreshHighlightFrame = (node, nodeProvider) => {
|
|
295
327
|
const frame = getHighlightFrameElement(nodeProvider);
|
|
296
328
|
const zoom = getCanvasWindowValue(["zoom", "current"]) ?? 1;
|
|
329
|
+
console.log("1. refreshHighlightFrame", node);
|
|
297
330
|
if (!frame)
|
|
298
331
|
return;
|
|
299
332
|
if (zoom >= 0.3) {
|
|
@@ -307,6 +340,7 @@ const refreshHighlightFrame = (node, nodeProvider) => {
|
|
|
307
340
|
frame.style.setProperty("--frame-left", `${left}px`);
|
|
308
341
|
frame.style.setProperty("--frame-width", `${width}px`);
|
|
309
342
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
343
|
+
console.log("2. refreshHighlightFrame", top, left, width, height);
|
|
310
344
|
};
|
|
311
345
|
|
|
312
346
|
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
@@ -462,7 +496,7 @@ const createNodeTools = (element) => {
|
|
|
462
496
|
let mutationObserver = null;
|
|
463
497
|
let selectedNode = null;
|
|
464
498
|
const text = nodeText();
|
|
465
|
-
const throttledFrameRefresh =
|
|
499
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
466
500
|
const handleEscape = () => {
|
|
467
501
|
if (text.isEditing()) {
|
|
468
502
|
text.blurEditMode();
|
|
@@ -501,8 +535,6 @@ const createNodeTools = (element) => {
|
|
|
501
535
|
});
|
|
502
536
|
mutationObserver.observe(node, {
|
|
503
537
|
attributes: true,
|
|
504
|
-
subtree: true,
|
|
505
|
-
childList: true,
|
|
506
538
|
characterData: true,
|
|
507
539
|
});
|
|
508
540
|
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.1.
|
|
4
|
+
* @version 2.1.9
|
|
5
5
|
*/
|
|
6
6
|
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
7
7
|
function withRAFThrottle(func) {
|
|
@@ -28,6 +28,42 @@ function withRAFThrottle(func) {
|
|
|
28
28
|
};
|
|
29
29
|
return throttled;
|
|
30
30
|
}
|
|
31
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
32
|
+
function withDoubleRAF(func) {
|
|
33
|
+
let rafId1 = null;
|
|
34
|
+
let rafId2 = null;
|
|
35
|
+
let lastArgs = null;
|
|
36
|
+
const throttled = (...args) => {
|
|
37
|
+
lastArgs = args;
|
|
38
|
+
if (rafId1 === null) {
|
|
39
|
+
rafId1 = requestAnimationFrame(() => {
|
|
40
|
+
// First RAF: let browser complete layout
|
|
41
|
+
rafId2 = requestAnimationFrame(() => {
|
|
42
|
+
// Second RAF: read bounds after layout is complete
|
|
43
|
+
if (lastArgs) {
|
|
44
|
+
const currentArgs = lastArgs;
|
|
45
|
+
rafId1 = null;
|
|
46
|
+
rafId2 = null;
|
|
47
|
+
lastArgs = null;
|
|
48
|
+
func(...currentArgs);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
throttled.cleanup = () => {
|
|
55
|
+
if (rafId1 !== null) {
|
|
56
|
+
cancelAnimationFrame(rafId1);
|
|
57
|
+
rafId1 = null;
|
|
58
|
+
}
|
|
59
|
+
if (rafId2 !== null) {
|
|
60
|
+
cancelAnimationFrame(rafId2);
|
|
61
|
+
rafId2 = null;
|
|
62
|
+
}
|
|
63
|
+
lastArgs = null;
|
|
64
|
+
};
|
|
65
|
+
return throttled;
|
|
66
|
+
}
|
|
31
67
|
|
|
32
68
|
const getCanvasWindowValue = (path) => {
|
|
33
69
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
@@ -100,15 +136,11 @@ const isLocked = (node) => {
|
|
|
100
136
|
};
|
|
101
137
|
|
|
102
138
|
const processPostMessage = (event, onNodeSelected) => {
|
|
103
|
-
// if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
104
|
-
// if (event.data.action === "zoom") {
|
|
105
|
-
// // Zoom handling can be implemented here if needed
|
|
106
|
-
// }
|
|
107
|
-
// }
|
|
108
139
|
if (event.data.source === "application") {
|
|
109
140
|
if (event.data.action === "selectedNodeChanged") {
|
|
110
141
|
const nodeId = event.data.data;
|
|
111
142
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
143
|
+
console.log("selectedNode", selectedNode);
|
|
112
144
|
if (isLocked(selectedNode)) {
|
|
113
145
|
onNodeSelected?.(null);
|
|
114
146
|
return;
|
|
@@ -292,6 +324,7 @@ const highlightNode = (node, nodeProvider) => {
|
|
|
292
324
|
const refreshHighlightFrame = (node, nodeProvider) => {
|
|
293
325
|
const frame = getHighlightFrameElement(nodeProvider);
|
|
294
326
|
const zoom = getCanvasWindowValue(["zoom", "current"]) ?? 1;
|
|
327
|
+
console.log("1. refreshHighlightFrame", node);
|
|
295
328
|
if (!frame)
|
|
296
329
|
return;
|
|
297
330
|
if (zoom >= 0.3) {
|
|
@@ -305,6 +338,7 @@ const refreshHighlightFrame = (node, nodeProvider) => {
|
|
|
305
338
|
frame.style.setProperty("--frame-left", `${left}px`);
|
|
306
339
|
frame.style.setProperty("--frame-width", `${width}px`);
|
|
307
340
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
341
|
+
console.log("2. refreshHighlightFrame", top, left, width, height);
|
|
308
342
|
};
|
|
309
343
|
|
|
310
344
|
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
@@ -460,7 +494,7 @@ const createNodeTools = (element) => {
|
|
|
460
494
|
let mutationObserver = null;
|
|
461
495
|
let selectedNode = null;
|
|
462
496
|
const text = nodeText();
|
|
463
|
-
const throttledFrameRefresh =
|
|
497
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
464
498
|
const handleEscape = () => {
|
|
465
499
|
if (text.isEditing()) {
|
|
466
500
|
text.blurEditMode();
|
|
@@ -499,8 +533,6 @@ const createNodeTools = (element) => {
|
|
|
499
533
|
});
|
|
500
534
|
mutationObserver.observe(node, {
|
|
501
535
|
attributes: true,
|
|
502
|
-
subtree: true,
|
|
503
|
-
childList: true,
|
|
504
536
|
characterData: true,
|
|
505
537
|
});
|
|
506
538
|
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.1.
|
|
4
|
+
* @version 2.1.9
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -34,6 +34,42 @@
|
|
|
34
34
|
};
|
|
35
35
|
return throttled;
|
|
36
36
|
}
|
|
37
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
38
|
+
function withDoubleRAF(func) {
|
|
39
|
+
let rafId1 = null;
|
|
40
|
+
let rafId2 = null;
|
|
41
|
+
let lastArgs = null;
|
|
42
|
+
const throttled = (...args) => {
|
|
43
|
+
lastArgs = args;
|
|
44
|
+
if (rafId1 === null) {
|
|
45
|
+
rafId1 = requestAnimationFrame(() => {
|
|
46
|
+
// First RAF: let browser complete layout
|
|
47
|
+
rafId2 = requestAnimationFrame(() => {
|
|
48
|
+
// Second RAF: read bounds after layout is complete
|
|
49
|
+
if (lastArgs) {
|
|
50
|
+
const currentArgs = lastArgs;
|
|
51
|
+
rafId1 = null;
|
|
52
|
+
rafId2 = null;
|
|
53
|
+
lastArgs = null;
|
|
54
|
+
func(...currentArgs);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
throttled.cleanup = () => {
|
|
61
|
+
if (rafId1 !== null) {
|
|
62
|
+
cancelAnimationFrame(rafId1);
|
|
63
|
+
rafId1 = null;
|
|
64
|
+
}
|
|
65
|
+
if (rafId2 !== null) {
|
|
66
|
+
cancelAnimationFrame(rafId2);
|
|
67
|
+
rafId2 = null;
|
|
68
|
+
}
|
|
69
|
+
lastArgs = null;
|
|
70
|
+
};
|
|
71
|
+
return throttled;
|
|
72
|
+
}
|
|
37
73
|
|
|
38
74
|
const getCanvasWindowValue = (path) => {
|
|
39
75
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
@@ -106,15 +142,11 @@
|
|
|
106
142
|
};
|
|
107
143
|
|
|
108
144
|
const processPostMessage = (event, onNodeSelected) => {
|
|
109
|
-
// if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
110
|
-
// if (event.data.action === "zoom") {
|
|
111
|
-
// // Zoom handling can be implemented here if needed
|
|
112
|
-
// }
|
|
113
|
-
// }
|
|
114
145
|
if (event.data.source === "application") {
|
|
115
146
|
if (event.data.action === "selectedNodeChanged") {
|
|
116
147
|
const nodeId = event.data.data;
|
|
117
148
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
149
|
+
console.log("selectedNode", selectedNode);
|
|
118
150
|
if (isLocked(selectedNode)) {
|
|
119
151
|
onNodeSelected?.(null);
|
|
120
152
|
return;
|
|
@@ -298,6 +330,7 @@
|
|
|
298
330
|
const refreshHighlightFrame = (node, nodeProvider) => {
|
|
299
331
|
const frame = getHighlightFrameElement(nodeProvider);
|
|
300
332
|
const zoom = getCanvasWindowValue(["zoom", "current"]) ?? 1;
|
|
333
|
+
console.log("1. refreshHighlightFrame", node);
|
|
301
334
|
if (!frame)
|
|
302
335
|
return;
|
|
303
336
|
if (zoom >= 0.3) {
|
|
@@ -311,6 +344,7 @@
|
|
|
311
344
|
frame.style.setProperty("--frame-left", `${left}px`);
|
|
312
345
|
frame.style.setProperty("--frame-width", `${width}px`);
|
|
313
346
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
347
|
+
console.log("2. refreshHighlightFrame", top, left, width, height);
|
|
314
348
|
};
|
|
315
349
|
|
|
316
350
|
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
@@ -466,7 +500,7 @@
|
|
|
466
500
|
let mutationObserver = null;
|
|
467
501
|
let selectedNode = null;
|
|
468
502
|
const text = nodeText();
|
|
469
|
-
const throttledFrameRefresh =
|
|
503
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
470
504
|
const handleEscape = () => {
|
|
471
505
|
if (text.isEditing()) {
|
|
472
506
|
text.blurEditMode();
|
|
@@ -505,8 +539,6 @@
|
|
|
505
539
|
});
|
|
506
540
|
mutationObserver.observe(node, {
|
|
507
541
|
attributes: true,
|
|
508
|
-
subtree: true,
|
|
509
|
-
childList: true,
|
|
510
542
|
characterData: true,
|
|
511
543
|
});
|
|
512
544
|
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
@@ -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)};const o=(e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n};function r(e){return e.querySelector(".highlight-frame")}const s=e=>{if(!e)return;const t=r(e);t&&t.remove()},
|
|
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)};const o=(e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n};function r(e){return e.querySelector(".highlight-frame")}const s=e=>{if(!e)return;const t=r(e);t&&t.remove()},l=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],i=0;const d=(e,t)=>{let n=null;const o=e.clientX,r=e.clientY,s=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=>!l.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none"));if(t&&d.includes(t))return t;if(s)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},c=(e,t,n,o)=>{const r=e=>{((e,t)=>{if("application"===e.data.source&&"selectedNodeChanged"===e.data.action){const o=e.data.data,r=document.querySelector(`[data-node-id="${o}"]`);if(console.log("selectedNode",r),n=r,n?.classList.contains("select-none"))return void t?.(null);r&&t?.(r)}var n})(e,t)},l=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return s(t),void o(null);o(d(e,n))})(n,e,o(),t)},a=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",r),document.addEventListener("click",l),document.addEventListener("keydown",a),()=>{window.removeEventListener("message",r),document.removeEventListener("click",l),document.removeEventListener("keydown",a)}};function u(e,t){const o=e.getBoundingClientRect(),r=t.getBoundingClientRect(),s=o.top-r.top,l=o.left-r.left,a=n(["zoom","current"])??1;return{top:parseFloat((s/a).toFixed(5)),left:parseFloat((l/a).toFixed(5)),width:Math.max(4,parseFloat((o.width/a).toFixed(5))),height:parseFloat((o.height/a).toFixed(5))}}const m=(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)},p=(e,t)=>{if(!e)return;const o=r(t);o&&o.remove();const s=((e,t)=>{const{top:o,left:r,width:s,height:l}=u(e,t),a=n(["zoom","current"])??1;document.body.style.setProperty("--zoom",a.toString()),document.body.style.setProperty("--stroke-width",(2/a).toFixed(3));const i=document.createElement("div");i.classList.add("highlight-frame"),i.style.setProperty("--frame-top",`${o}px`),i.style.setProperty("--frame-left",`${r}px`),i.style.setProperty("--frame-width",`${s}px`),i.style.setProperty("--frame-height",`${l}px`);const d=document.createElementNS("http://www.w3.org/2000/svg","svg");d.classList.add("highlight-frame-svg");const c=document.createElementNS("http://www.w3.org/2000/svg","rect");return c.setAttribute("x","0"),c.setAttribute("y","0"),c.setAttribute("width","100%"),c.setAttribute("height","100%"),c.classList.add("highlight-frame-rect"),d.appendChild(c),i.appendChild(d),i})(e,t);"true"===e.contentEditable&&s.classList.add("is-editable"),m(e,s),t.appendChild(s)},h=(e,t)=>{const o=r(t),s=n(["zoom","current"])??1;if(console.log("1. refreshHighlightFrame",e),!o)return;s>=.3?t.style.setProperty("--tool-opacity","1"):t.style.setProperty("--tool-opacity","0");const{top:l,left:a,width:i,height:d}=u(e,t);o.style.setProperty("--frame-top",`${l}px`),o.style.setProperty("--frame-left",`${a}px`),o.style.setProperty("--frame-width",`${i}px`),o.style.setProperty("--frame-height",`${d}px`),console.log("2. refreshHighlightFrame",l,a,i,d)},f=(e,t)=>{const n=r(t);if(!n)return;const o=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";n.style.display=o;const s=n.querySelector(".tag-label");s&&(s.style.display=o)},v=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)}},y=(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,()=>{h(e,t)});return()=>n.disconnect()},g=()=>{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","disableTextEditMode"]);e?.()})(),o?.(),e=null,t=!1};return{enableEditMode:(t,s)=>{if(e===t)return;e&&e!==t&&r();const l=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(t);l&&(e=t,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(t),(()=>{const e=n(["keyboard","enableTextEditMode"]);e?.()})(),o=((e,t,n)=>{if(!t)return()=>{};e.addEventListener("blur",n);const o=v(e),r=y(e,t);return()=>{e.removeEventListener("blur",n),o(),r?.()}})(t,s,r))},blurEditMode:r,getEditableNode:()=>e,isEditing:()=>null!==e}},b=320,w=1680,E=[{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"}],x=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(b,Math.min(w,n))})(n,(e.clientX-t)/o);return r},L=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<E.length&&(E[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 t=e;let n=null,r=null,l=null,a=null;const i=g(),d=function(e){let t=null,n=null,o=null;const r=(...r)=>{o=r,null===t&&(t=requestAnimationFrame(()=>{n=requestAnimationFrame(()=>{if(o){const r=o;t=null,n=null,o=null,e(...r)}})}))};return r.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null),null!==n&&(cancelAnimationFrame(n),n=null),o=null},r}(h),u=e=>{if(a!==e){if(i.isEditing()){const t=i.getEditableNode();t&&t!==e&&i.blurEditMode()}var s,c;n?.disconnect(),r?.disconnect(),l?.disconnect(),e&&t&&(i.enableEditMode(e,t),n=o(t,()=>{d(e,t)}),l=new MutationObserver(()=>{d(e,t),f(e,t)}),l.observe(e,{attributes:!0,characterData:!0}),r=o(e,()=>{d(e,t),f(e,t)})),a=e,s="selectedNodeChanged",c=e?.getAttribute("data-node-id")??null,window.parent.postMessage({source:"node-edit-utils",action:s,data:c,timestamp:Date.now()},"*"),p(e,t),e&&t&&f(e,t)}},m=c(t,u,()=>{i.isEditing()&&i.blurEditMode(),a&&t&&(s(t),a=null,n?.disconnect(),r?.disconnect(),l?.disconnect())},i.getEditableNode),v={selectNode:u,getSelectedNode:()=>a,refreshHighlightFrame:()=>{d(a,t)},clearSelectedNode:()=>{s(t),a=null,n?.disconnect(),r?.disconnect(),l?.disconnect()},getEditableNode:()=>i.getEditableNode(),cleanup:()=>{m(),n?.disconnect(),r?.disconnect(),l?.disconnect(),i.blurEditMode(),d.cleanup()}};var y,b;return y="nodeTools",b=v,"undefined"!=typeof window&&(window[y]=b),v},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",E.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,L);let s=!1,l=0,a=0;const i=t(t=>{if(!s)return;n&&(n.style.cursor="ew-resize");const o=x(t,l,a);L(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,l=t.clientX,a=e.offsetWidth},i,e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{L(e,t)},cleanup:()=>{s=!1,i?.cleanup(),d(),r.remove()}}}});
|
package/package.json
CHANGED
package/src/lib/helpers/index.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { withRAF, withRAFThrottle } from "./withRAF";
|
|
1
|
+
export { withRAF, withRAFThrottle, withDoubleRAF } from "./withRAF";
|
|
@@ -37,3 +37,44 @@ export function withRAFThrottle<T extends (...args: any[]) => void>(func: T): T
|
|
|
37
37
|
|
|
38
38
|
return throttled as T & { cleanup: () => void };
|
|
39
39
|
}
|
|
40
|
+
|
|
41
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
42
|
+
export function withDoubleRAF<T extends (...args: any[]) => void>(func: T): T & { cleanup: () => void } {
|
|
43
|
+
let rafId1: number | null = null;
|
|
44
|
+
let rafId2: number | null = null;
|
|
45
|
+
let lastArgs: Parameters<T> | null = null;
|
|
46
|
+
|
|
47
|
+
const throttled = (...args: Parameters<T>) => {
|
|
48
|
+
lastArgs = args;
|
|
49
|
+
|
|
50
|
+
if (rafId1 === null) {
|
|
51
|
+
rafId1 = requestAnimationFrame(() => {
|
|
52
|
+
// First RAF: let browser complete layout
|
|
53
|
+
rafId2 = requestAnimationFrame(() => {
|
|
54
|
+
// Second RAF: read bounds after layout is complete
|
|
55
|
+
if (lastArgs) {
|
|
56
|
+
const currentArgs = lastArgs;
|
|
57
|
+
rafId1 = null;
|
|
58
|
+
rafId2 = null;
|
|
59
|
+
lastArgs = null;
|
|
60
|
+
func(...currentArgs);
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
throttled.cleanup = () => {
|
|
68
|
+
if (rafId1 !== null) {
|
|
69
|
+
cancelAnimationFrame(rafId1);
|
|
70
|
+
rafId1 = null;
|
|
71
|
+
}
|
|
72
|
+
if (rafId2 !== null) {
|
|
73
|
+
cancelAnimationFrame(rafId2);
|
|
74
|
+
rafId2 = null;
|
|
75
|
+
}
|
|
76
|
+
lastArgs = null;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
return throttled as T & { cleanup: () => void };
|
|
80
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { withDoubleRAF } from "../helpers";
|
|
2
2
|
import { connectResizeObserver } from "../helpers/observer/connectResizeObserver";
|
|
3
3
|
import { sendPostMessage } from "../post-message/sendPostMessage";
|
|
4
4
|
import { bindToWindow } from "../window/bindToWindow";
|
|
@@ -19,7 +19,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
19
19
|
let selectedNode: HTMLElement | null = null;
|
|
20
20
|
|
|
21
21
|
const text = nodeText();
|
|
22
|
-
const throttledFrameRefresh =
|
|
22
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
23
23
|
|
|
24
24
|
const handleEscape = (): void => {
|
|
25
25
|
if (text.isEditing()) {
|
|
@@ -68,8 +68,6 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
68
68
|
|
|
69
69
|
mutationObserver.observe(node, {
|
|
70
70
|
attributes: true,
|
|
71
|
-
subtree: true,
|
|
72
|
-
childList: true,
|
|
73
71
|
characterData: true,
|
|
74
72
|
});
|
|
75
73
|
|
|
@@ -6,6 +6,8 @@ export const refreshHighlightFrame = (node: HTMLElement, nodeProvider: HTMLEleme
|
|
|
6
6
|
const frame = getHighlightFrameElement(nodeProvider);
|
|
7
7
|
const zoom = getCanvasWindowValue(["zoom", "current"]) ?? 1;
|
|
8
8
|
|
|
9
|
+
console.log("1. refreshHighlightFrame", node);
|
|
10
|
+
|
|
9
11
|
if (!frame) return;
|
|
10
12
|
|
|
11
13
|
if (zoom >= 0.3) {
|
|
@@ -20,4 +22,6 @@ export const refreshHighlightFrame = (node: HTMLElement, nodeProvider: HTMLEleme
|
|
|
20
22
|
frame.style.setProperty("--frame-left", `${left}px`);
|
|
21
23
|
frame.style.setProperty("--frame-width", `${width}px`);
|
|
22
24
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
25
|
+
|
|
26
|
+
console.log("2. refreshHighlightFrame", top, left, width, height);
|
|
23
27
|
};
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
import { isLocked } from "../node-tools/select/helpers/isLocked";
|
|
2
2
|
|
|
3
3
|
export const processPostMessage = (event: MessageEvent, onNodeSelected?: (node: HTMLElement | null) => void) => {
|
|
4
|
-
// if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
5
|
-
// if (event.data.action === "zoom") {
|
|
6
|
-
// // Zoom handling can be implemented here if needed
|
|
7
|
-
// }
|
|
8
|
-
// }
|
|
9
|
-
|
|
10
4
|
if (event.data.source === "application") {
|
|
11
5
|
if (event.data.action === "selectedNodeChanged") {
|
|
12
6
|
const nodeId = event.data.data;
|
|
13
7
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`) as HTMLElement | null;
|
|
14
8
|
|
|
9
|
+
console.log("selectedNode", selectedNode);
|
|
10
|
+
|
|
15
11
|
if (isLocked(selectedNode)) {
|
|
16
12
|
onNodeSelected?.(null);
|
|
17
13
|
return;
|