@node-edit-utils/core 2.1.6 → 2.1.8
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 +47 -18
- package/dist/node-edit-utils.esm.js +47 -18
- package/dist/node-edit-utils.umd.js +47 -18
- 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 +11 -20
|
@@ -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.8
|
|
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
|
|
@@ -458,11 +494,11 @@ const nodeText = () => {
|
|
|
458
494
|
const createNodeTools = (element) => {
|
|
459
495
|
const nodeProvider = element;
|
|
460
496
|
let resizeObserver = null;
|
|
497
|
+
let nodeResizeObserver = null;
|
|
461
498
|
let mutationObserver = null;
|
|
462
|
-
let parentMutationObserver = null;
|
|
463
499
|
let selectedNode = null;
|
|
464
500
|
const text = nodeText();
|
|
465
|
-
const throttledFrameRefresh =
|
|
501
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
466
502
|
const handleEscape = () => {
|
|
467
503
|
if (text.isEditing()) {
|
|
468
504
|
text.blurEditMode();
|
|
@@ -472,8 +508,8 @@ const createNodeTools = (element) => {
|
|
|
472
508
|
clearHighlightFrame(nodeProvider);
|
|
473
509
|
selectedNode = null;
|
|
474
510
|
resizeObserver?.disconnect();
|
|
511
|
+
nodeResizeObserver?.disconnect();
|
|
475
512
|
mutationObserver?.disconnect();
|
|
476
|
-
parentMutationObserver?.disconnect();
|
|
477
513
|
}
|
|
478
514
|
}
|
|
479
515
|
};
|
|
@@ -488,8 +524,8 @@ const createNodeTools = (element) => {
|
|
|
488
524
|
}
|
|
489
525
|
}
|
|
490
526
|
resizeObserver?.disconnect();
|
|
527
|
+
nodeResizeObserver?.disconnect();
|
|
491
528
|
mutationObserver?.disconnect();
|
|
492
|
-
parentMutationObserver?.disconnect();
|
|
493
529
|
if (node && nodeProvider) {
|
|
494
530
|
text.enableEditMode(node, nodeProvider);
|
|
495
531
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
@@ -505,17 +541,10 @@ const createNodeTools = (element) => {
|
|
|
505
541
|
childList: true,
|
|
506
542
|
characterData: true,
|
|
507
543
|
});
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
updateHighlightFrameVisibility(node, nodeProvider);
|
|
513
|
-
});
|
|
514
|
-
parentMutationObserver.observe(parent, {
|
|
515
|
-
childList: true,
|
|
516
|
-
subtree: true,
|
|
517
|
-
});
|
|
518
|
-
}
|
|
544
|
+
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
545
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
546
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
547
|
+
});
|
|
519
548
|
}
|
|
520
549
|
selectedNode = node;
|
|
521
550
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
@@ -529,8 +558,8 @@ const createNodeTools = (element) => {
|
|
|
529
558
|
const cleanup = () => {
|
|
530
559
|
removeListeners();
|
|
531
560
|
resizeObserver?.disconnect();
|
|
561
|
+
nodeResizeObserver?.disconnect();
|
|
532
562
|
mutationObserver?.disconnect();
|
|
533
|
-
parentMutationObserver?.disconnect();
|
|
534
563
|
text.blurEditMode();
|
|
535
564
|
throttledFrameRefresh.cleanup();
|
|
536
565
|
};
|
|
@@ -544,8 +573,8 @@ const createNodeTools = (element) => {
|
|
|
544
573
|
clearHighlightFrame(nodeProvider);
|
|
545
574
|
selectedNode = null;
|
|
546
575
|
resizeObserver?.disconnect();
|
|
576
|
+
nodeResizeObserver?.disconnect();
|
|
547
577
|
mutationObserver?.disconnect();
|
|
548
|
-
parentMutationObserver?.disconnect();
|
|
549
578
|
},
|
|
550
579
|
getEditableNode: () => text.getEditableNode(),
|
|
551
580
|
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.1.
|
|
4
|
+
* @version 2.1.8
|
|
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
|
|
@@ -456,11 +492,11 @@ const nodeText = () => {
|
|
|
456
492
|
const createNodeTools = (element) => {
|
|
457
493
|
const nodeProvider = element;
|
|
458
494
|
let resizeObserver = null;
|
|
495
|
+
let nodeResizeObserver = null;
|
|
459
496
|
let mutationObserver = null;
|
|
460
|
-
let parentMutationObserver = null;
|
|
461
497
|
let selectedNode = null;
|
|
462
498
|
const text = nodeText();
|
|
463
|
-
const throttledFrameRefresh =
|
|
499
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
464
500
|
const handleEscape = () => {
|
|
465
501
|
if (text.isEditing()) {
|
|
466
502
|
text.blurEditMode();
|
|
@@ -470,8 +506,8 @@ const createNodeTools = (element) => {
|
|
|
470
506
|
clearHighlightFrame(nodeProvider);
|
|
471
507
|
selectedNode = null;
|
|
472
508
|
resizeObserver?.disconnect();
|
|
509
|
+
nodeResizeObserver?.disconnect();
|
|
473
510
|
mutationObserver?.disconnect();
|
|
474
|
-
parentMutationObserver?.disconnect();
|
|
475
511
|
}
|
|
476
512
|
}
|
|
477
513
|
};
|
|
@@ -486,8 +522,8 @@ const createNodeTools = (element) => {
|
|
|
486
522
|
}
|
|
487
523
|
}
|
|
488
524
|
resizeObserver?.disconnect();
|
|
525
|
+
nodeResizeObserver?.disconnect();
|
|
489
526
|
mutationObserver?.disconnect();
|
|
490
|
-
parentMutationObserver?.disconnect();
|
|
491
527
|
if (node && nodeProvider) {
|
|
492
528
|
text.enableEditMode(node, nodeProvider);
|
|
493
529
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
@@ -503,17 +539,10 @@ const createNodeTools = (element) => {
|
|
|
503
539
|
childList: true,
|
|
504
540
|
characterData: true,
|
|
505
541
|
});
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
updateHighlightFrameVisibility(node, nodeProvider);
|
|
511
|
-
});
|
|
512
|
-
parentMutationObserver.observe(parent, {
|
|
513
|
-
childList: true,
|
|
514
|
-
subtree: true,
|
|
515
|
-
});
|
|
516
|
-
}
|
|
542
|
+
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
543
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
544
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
545
|
+
});
|
|
517
546
|
}
|
|
518
547
|
selectedNode = node;
|
|
519
548
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
@@ -527,8 +556,8 @@ const createNodeTools = (element) => {
|
|
|
527
556
|
const cleanup = () => {
|
|
528
557
|
removeListeners();
|
|
529
558
|
resizeObserver?.disconnect();
|
|
559
|
+
nodeResizeObserver?.disconnect();
|
|
530
560
|
mutationObserver?.disconnect();
|
|
531
|
-
parentMutationObserver?.disconnect();
|
|
532
561
|
text.blurEditMode();
|
|
533
562
|
throttledFrameRefresh.cleanup();
|
|
534
563
|
};
|
|
@@ -542,8 +571,8 @@ const createNodeTools = (element) => {
|
|
|
542
571
|
clearHighlightFrame(nodeProvider);
|
|
543
572
|
selectedNode = null;
|
|
544
573
|
resizeObserver?.disconnect();
|
|
574
|
+
nodeResizeObserver?.disconnect();
|
|
545
575
|
mutationObserver?.disconnect();
|
|
546
|
-
parentMutationObserver?.disconnect();
|
|
547
576
|
},
|
|
548
577
|
getEditableNode: () => text.getEditableNode(),
|
|
549
578
|
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.1.
|
|
4
|
+
* @version 2.1.8
|
|
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
|
|
@@ -462,11 +498,11 @@
|
|
|
462
498
|
const createNodeTools = (element) => {
|
|
463
499
|
const nodeProvider = element;
|
|
464
500
|
let resizeObserver = null;
|
|
501
|
+
let nodeResizeObserver = null;
|
|
465
502
|
let mutationObserver = null;
|
|
466
|
-
let parentMutationObserver = null;
|
|
467
503
|
let selectedNode = null;
|
|
468
504
|
const text = nodeText();
|
|
469
|
-
const throttledFrameRefresh =
|
|
505
|
+
const throttledFrameRefresh = withDoubleRAF(refreshHighlightFrame);
|
|
470
506
|
const handleEscape = () => {
|
|
471
507
|
if (text.isEditing()) {
|
|
472
508
|
text.blurEditMode();
|
|
@@ -476,8 +512,8 @@
|
|
|
476
512
|
clearHighlightFrame(nodeProvider);
|
|
477
513
|
selectedNode = null;
|
|
478
514
|
resizeObserver?.disconnect();
|
|
515
|
+
nodeResizeObserver?.disconnect();
|
|
479
516
|
mutationObserver?.disconnect();
|
|
480
|
-
parentMutationObserver?.disconnect();
|
|
481
517
|
}
|
|
482
518
|
}
|
|
483
519
|
};
|
|
@@ -492,8 +528,8 @@
|
|
|
492
528
|
}
|
|
493
529
|
}
|
|
494
530
|
resizeObserver?.disconnect();
|
|
531
|
+
nodeResizeObserver?.disconnect();
|
|
495
532
|
mutationObserver?.disconnect();
|
|
496
|
-
parentMutationObserver?.disconnect();
|
|
497
533
|
if (node && nodeProvider) {
|
|
498
534
|
text.enableEditMode(node, nodeProvider);
|
|
499
535
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
@@ -509,17 +545,10 @@
|
|
|
509
545
|
childList: true,
|
|
510
546
|
characterData: true,
|
|
511
547
|
});
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
updateHighlightFrameVisibility(node, nodeProvider);
|
|
517
|
-
});
|
|
518
|
-
parentMutationObserver.observe(parent, {
|
|
519
|
-
childList: true,
|
|
520
|
-
subtree: true,
|
|
521
|
-
});
|
|
522
|
-
}
|
|
548
|
+
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
549
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
550
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
551
|
+
});
|
|
523
552
|
}
|
|
524
553
|
selectedNode = node;
|
|
525
554
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
@@ -533,8 +562,8 @@
|
|
|
533
562
|
const cleanup = () => {
|
|
534
563
|
removeListeners();
|
|
535
564
|
resizeObserver?.disconnect();
|
|
565
|
+
nodeResizeObserver?.disconnect();
|
|
536
566
|
mutationObserver?.disconnect();
|
|
537
|
-
parentMutationObserver?.disconnect();
|
|
538
567
|
text.blurEditMode();
|
|
539
568
|
throttledFrameRefresh.cleanup();
|
|
540
569
|
};
|
|
@@ -548,8 +577,8 @@
|
|
|
548
577
|
clearHighlightFrame(nodeProvider);
|
|
549
578
|
selectedNode = null;
|
|
550
579
|
resizeObserver?.disconnect();
|
|
580
|
+
nodeResizeObserver?.disconnect();
|
|
551
581
|
mutationObserver?.disconnect();
|
|
552
|
-
parentMutationObserver?.disconnect();
|
|
553
582
|
},
|
|
554
583
|
getEditableNode: () => text.getEditableNode(),
|
|
555
584
|
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)};
|
|
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(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(!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`)},v=(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)},f=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=f(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),v(e,t)}),l.observe(e,{attributes:!0,subtree:!0,childList:!0,characterData:!0}),r=o(e,()=>{d(e,t),v(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&&v(e,t)}},m=c(t,u,()=>{i.isEditing()&&i.blurEditMode(),a&&t&&(s(t),a=null,n?.disconnect(),r?.disconnect(),l?.disconnect())},i.getEditableNode),f={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=f,"undefined"!=typeof window&&(window[y]=b),f},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";
|
|
@@ -14,12 +14,12 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
14
14
|
const nodeProvider = element;
|
|
15
15
|
|
|
16
16
|
let resizeObserver: ResizeObserver | null = null;
|
|
17
|
+
let nodeResizeObserver: ResizeObserver | null = null;
|
|
17
18
|
let mutationObserver: MutationObserver | null = null;
|
|
18
|
-
let parentMutationObserver: MutationObserver | null = null;
|
|
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()) {
|
|
@@ -32,8 +32,8 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
32
32
|
selectedNode = null;
|
|
33
33
|
|
|
34
34
|
resizeObserver?.disconnect();
|
|
35
|
+
nodeResizeObserver?.disconnect();
|
|
35
36
|
mutationObserver?.disconnect();
|
|
36
|
-
parentMutationObserver?.disconnect();
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
};
|
|
@@ -51,8 +51,8 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
resizeObserver?.disconnect();
|
|
54
|
+
nodeResizeObserver?.disconnect();
|
|
54
55
|
mutationObserver?.disconnect();
|
|
55
|
-
parentMutationObserver?.disconnect();
|
|
56
56
|
|
|
57
57
|
if (node && nodeProvider) {
|
|
58
58
|
text.enableEditMode(node, nodeProvider);
|
|
@@ -73,19 +73,10 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
73
73
|
characterData: true,
|
|
74
74
|
});
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
throttledFrameRefresh(node, nodeProvider);
|
|
81
|
-
updateHighlightFrameVisibility(node, nodeProvider);
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
parentMutationObserver.observe(parent, {
|
|
85
|
-
childList: true,
|
|
86
|
-
subtree: true,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
76
|
+
nodeResizeObserver = connectResizeObserver(node, () => {
|
|
77
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
78
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
79
|
+
});
|
|
89
80
|
}
|
|
90
81
|
|
|
91
82
|
selectedNode = node;
|
|
@@ -103,8 +94,8 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
103
94
|
const cleanup = (): void => {
|
|
104
95
|
removeListeners();
|
|
105
96
|
resizeObserver?.disconnect();
|
|
97
|
+
nodeResizeObserver?.disconnect();
|
|
106
98
|
mutationObserver?.disconnect();
|
|
107
|
-
parentMutationObserver?.disconnect();
|
|
108
99
|
|
|
109
100
|
text.blurEditMode();
|
|
110
101
|
throttledFrameRefresh.cleanup();
|
|
@@ -120,8 +111,8 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
120
111
|
clearHighlightFrame(nodeProvider);
|
|
121
112
|
selectedNode = null;
|
|
122
113
|
resizeObserver?.disconnect();
|
|
114
|
+
nodeResizeObserver?.disconnect();
|
|
123
115
|
mutationObserver?.disconnect();
|
|
124
|
-
parentMutationObserver?.disconnect();
|
|
125
116
|
},
|
|
126
117
|
getEditableNode: () => text.getEditableNode(),
|
|
127
118
|
cleanup,
|