@node-edit-utils/core 2.2.8 → 2.3.0
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/text/helpers/handleTextChange.d.ts +1 -1
- package/dist/node-edit-utils.cjs.js +72 -34
- package/dist/node-edit-utils.esm.js +72 -34
- package/dist/node-edit-utils.umd.js +72 -34
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/package.json +1 -1
- package/src/lib/node-tools/text/events/setupMutationObserver.ts +42 -6
- package/src/lib/node-tools/text/helpers/handleTextChange.ts +5 -2
- package/src/lib/node-tools/text/nodeText.ts +4 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const handleTextChange: (node: HTMLElement, mutations: MutationRecord[]) => void;
|
|
1
|
+
export declare const handleTextChange: (node: HTMLElement, mutations: MutationRecord[], final?: boolean) => 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.
|
|
4
|
+
* @version 2.3.0
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -645,63 +645,73 @@ const connectMutationObserver = (element, handler) => {
|
|
|
645
645
|
return mutationObserver;
|
|
646
646
|
};
|
|
647
647
|
|
|
648
|
-
|
|
649
|
-
function withRAFThrottle(func) {
|
|
650
|
-
let rafId = null;
|
|
651
|
-
let lastArgs = null;
|
|
652
|
-
const throttled = (...args) => {
|
|
653
|
-
lastArgs = args;
|
|
654
|
-
if (rafId === null) {
|
|
655
|
-
rafId = requestAnimationFrame(() => {
|
|
656
|
-
if (lastArgs) {
|
|
657
|
-
func(...lastArgs);
|
|
658
|
-
}
|
|
659
|
-
rafId = null;
|
|
660
|
-
lastArgs = null;
|
|
661
|
-
});
|
|
662
|
-
}
|
|
663
|
-
};
|
|
664
|
-
throttled.cleanup = () => {
|
|
665
|
-
if (rafId !== null) {
|
|
666
|
-
cancelAnimationFrame(rafId);
|
|
667
|
-
rafId = null;
|
|
668
|
-
lastArgs = null;
|
|
669
|
-
}
|
|
670
|
-
};
|
|
671
|
-
return throttled;
|
|
672
|
-
}
|
|
673
|
-
|
|
674
|
-
const handleTextChange = (node, mutations) => {
|
|
648
|
+
const handleTextChange = (node, mutations, final = false) => {
|
|
675
649
|
// Check if any mutation is a text content change
|
|
676
650
|
const hasTextChange = mutations.some((mutation) => {
|
|
677
651
|
return (mutation.type === "characterData" ||
|
|
678
652
|
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
|
|
679
653
|
});
|
|
680
|
-
if (!hasTextChange) {
|
|
654
|
+
if (!hasTextChange && !final) {
|
|
681
655
|
return;
|
|
682
656
|
}
|
|
683
657
|
// Get the text content of the node
|
|
684
658
|
const textContent = node.textContent ?? "";
|
|
685
659
|
// Get the node ID
|
|
686
660
|
const nodeId = node.getAttribute("data-node-id");
|
|
661
|
+
console.log("textContentChanged", textContent, final);
|
|
687
662
|
// Send postMessage with the text change
|
|
688
663
|
sendPostMessage("textContentChanged", {
|
|
689
664
|
nodeId,
|
|
690
665
|
textContent,
|
|
666
|
+
final,
|
|
691
667
|
});
|
|
692
668
|
};
|
|
693
669
|
|
|
694
670
|
const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
671
|
+
// Accumulate mutations instead of replacing them
|
|
672
|
+
let pendingMutations = [];
|
|
673
|
+
let rafId1 = null;
|
|
674
|
+
let rafId2 = null;
|
|
675
|
+
const processMutations = () => {
|
|
676
|
+
if (pendingMutations.length > 0) {
|
|
677
|
+
const mutationsToProcess = [...pendingMutations];
|
|
678
|
+
pendingMutations = [];
|
|
679
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
680
|
+
}
|
|
681
|
+
};
|
|
682
|
+
const scheduleProcess = () => {
|
|
683
|
+
if (rafId1 === null) {
|
|
684
|
+
rafId1 = requestAnimationFrame(() => {
|
|
685
|
+
// First RAF: let browser complete layout
|
|
686
|
+
rafId2 = requestAnimationFrame(() => {
|
|
687
|
+
// Second RAF: read textContent after layout is complete
|
|
688
|
+
processMutations();
|
|
689
|
+
rafId1 = null;
|
|
690
|
+
rafId2 = null;
|
|
691
|
+
});
|
|
692
|
+
});
|
|
693
|
+
}
|
|
694
|
+
};
|
|
695
|
+
const cleanup = () => {
|
|
696
|
+
if (rafId1 !== null) {
|
|
697
|
+
cancelAnimationFrame(rafId1);
|
|
698
|
+
rafId1 = null;
|
|
699
|
+
}
|
|
700
|
+
if (rafId2 !== null) {
|
|
701
|
+
cancelAnimationFrame(rafId2);
|
|
702
|
+
rafId2 = null;
|
|
703
|
+
}
|
|
704
|
+
pendingMutations = [];
|
|
705
|
+
};
|
|
698
706
|
const mutationObserver = connectMutationObserver(node, (mutations) => {
|
|
699
|
-
|
|
707
|
+
// Accumulate mutations instead of replacing
|
|
708
|
+
pendingMutations.push(...mutations);
|
|
709
|
+
scheduleProcess();
|
|
700
710
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
701
711
|
});
|
|
702
712
|
return () => {
|
|
703
713
|
mutationObserver.disconnect();
|
|
704
|
-
|
|
714
|
+
cleanup();
|
|
705
715
|
};
|
|
706
716
|
};
|
|
707
717
|
|
|
@@ -766,6 +776,8 @@ const nodeText = (canvasName = "canvas") => {
|
|
|
766
776
|
}
|
|
767
777
|
blurInProgress = true;
|
|
768
778
|
const nodeToCleanup = editableNode;
|
|
779
|
+
// Send final textContentChanged message before cleanup
|
|
780
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
769
781
|
makeNodeNonEditable(nodeToCleanup);
|
|
770
782
|
disableCanvasTextMode(canvasName);
|
|
771
783
|
cleanup?.();
|
|
@@ -915,6 +927,32 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
915
927
|
return nodeTools;
|
|
916
928
|
};
|
|
917
929
|
|
|
930
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
931
|
+
function withRAFThrottle(func) {
|
|
932
|
+
let rafId = null;
|
|
933
|
+
let lastArgs = null;
|
|
934
|
+
const throttled = (...args) => {
|
|
935
|
+
lastArgs = args;
|
|
936
|
+
if (rafId === null) {
|
|
937
|
+
rafId = requestAnimationFrame(() => {
|
|
938
|
+
if (lastArgs) {
|
|
939
|
+
func(...lastArgs);
|
|
940
|
+
}
|
|
941
|
+
rafId = null;
|
|
942
|
+
lastArgs = null;
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
throttled.cleanup = () => {
|
|
947
|
+
if (rafId !== null) {
|
|
948
|
+
cancelAnimationFrame(rafId);
|
|
949
|
+
rafId = null;
|
|
950
|
+
lastArgs = null;
|
|
951
|
+
}
|
|
952
|
+
};
|
|
953
|
+
return throttled;
|
|
954
|
+
}
|
|
955
|
+
|
|
918
956
|
const DEFAULT_WIDTH = 400;
|
|
919
957
|
const RESIZE_CONFIG = {
|
|
920
958
|
minWidth: 320,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.
|
|
4
|
+
* @version 2.3.0
|
|
5
5
|
*/
|
|
6
6
|
const getCanvasWindowValue = (path, canvasName = "canvas") => {
|
|
7
7
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
@@ -643,63 +643,73 @@ const connectMutationObserver = (element, handler) => {
|
|
|
643
643
|
return mutationObserver;
|
|
644
644
|
};
|
|
645
645
|
|
|
646
|
-
|
|
647
|
-
function withRAFThrottle(func) {
|
|
648
|
-
let rafId = null;
|
|
649
|
-
let lastArgs = null;
|
|
650
|
-
const throttled = (...args) => {
|
|
651
|
-
lastArgs = args;
|
|
652
|
-
if (rafId === null) {
|
|
653
|
-
rafId = requestAnimationFrame(() => {
|
|
654
|
-
if (lastArgs) {
|
|
655
|
-
func(...lastArgs);
|
|
656
|
-
}
|
|
657
|
-
rafId = null;
|
|
658
|
-
lastArgs = null;
|
|
659
|
-
});
|
|
660
|
-
}
|
|
661
|
-
};
|
|
662
|
-
throttled.cleanup = () => {
|
|
663
|
-
if (rafId !== null) {
|
|
664
|
-
cancelAnimationFrame(rafId);
|
|
665
|
-
rafId = null;
|
|
666
|
-
lastArgs = null;
|
|
667
|
-
}
|
|
668
|
-
};
|
|
669
|
-
return throttled;
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
const handleTextChange = (node, mutations) => {
|
|
646
|
+
const handleTextChange = (node, mutations, final = false) => {
|
|
673
647
|
// Check if any mutation is a text content change
|
|
674
648
|
const hasTextChange = mutations.some((mutation) => {
|
|
675
649
|
return (mutation.type === "characterData" ||
|
|
676
650
|
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
|
|
677
651
|
});
|
|
678
|
-
if (!hasTextChange) {
|
|
652
|
+
if (!hasTextChange && !final) {
|
|
679
653
|
return;
|
|
680
654
|
}
|
|
681
655
|
// Get the text content of the node
|
|
682
656
|
const textContent = node.textContent ?? "";
|
|
683
657
|
// Get the node ID
|
|
684
658
|
const nodeId = node.getAttribute("data-node-id");
|
|
659
|
+
console.log("textContentChanged", textContent, final);
|
|
685
660
|
// Send postMessage with the text change
|
|
686
661
|
sendPostMessage("textContentChanged", {
|
|
687
662
|
nodeId,
|
|
688
663
|
textContent,
|
|
664
|
+
final,
|
|
689
665
|
});
|
|
690
666
|
};
|
|
691
667
|
|
|
692
668
|
const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
669
|
+
// Accumulate mutations instead of replacing them
|
|
670
|
+
let pendingMutations = [];
|
|
671
|
+
let rafId1 = null;
|
|
672
|
+
let rafId2 = null;
|
|
673
|
+
const processMutations = () => {
|
|
674
|
+
if (pendingMutations.length > 0) {
|
|
675
|
+
const mutationsToProcess = [...pendingMutations];
|
|
676
|
+
pendingMutations = [];
|
|
677
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
678
|
+
}
|
|
679
|
+
};
|
|
680
|
+
const scheduleProcess = () => {
|
|
681
|
+
if (rafId1 === null) {
|
|
682
|
+
rafId1 = requestAnimationFrame(() => {
|
|
683
|
+
// First RAF: let browser complete layout
|
|
684
|
+
rafId2 = requestAnimationFrame(() => {
|
|
685
|
+
// Second RAF: read textContent after layout is complete
|
|
686
|
+
processMutations();
|
|
687
|
+
rafId1 = null;
|
|
688
|
+
rafId2 = null;
|
|
689
|
+
});
|
|
690
|
+
});
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
const cleanup = () => {
|
|
694
|
+
if (rafId1 !== null) {
|
|
695
|
+
cancelAnimationFrame(rafId1);
|
|
696
|
+
rafId1 = null;
|
|
697
|
+
}
|
|
698
|
+
if (rafId2 !== null) {
|
|
699
|
+
cancelAnimationFrame(rafId2);
|
|
700
|
+
rafId2 = null;
|
|
701
|
+
}
|
|
702
|
+
pendingMutations = [];
|
|
703
|
+
};
|
|
696
704
|
const mutationObserver = connectMutationObserver(node, (mutations) => {
|
|
697
|
-
|
|
705
|
+
// Accumulate mutations instead of replacing
|
|
706
|
+
pendingMutations.push(...mutations);
|
|
707
|
+
scheduleProcess();
|
|
698
708
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
699
709
|
});
|
|
700
710
|
return () => {
|
|
701
711
|
mutationObserver.disconnect();
|
|
702
|
-
|
|
712
|
+
cleanup();
|
|
703
713
|
};
|
|
704
714
|
};
|
|
705
715
|
|
|
@@ -764,6 +774,8 @@ const nodeText = (canvasName = "canvas") => {
|
|
|
764
774
|
}
|
|
765
775
|
blurInProgress = true;
|
|
766
776
|
const nodeToCleanup = editableNode;
|
|
777
|
+
// Send final textContentChanged message before cleanup
|
|
778
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
767
779
|
makeNodeNonEditable(nodeToCleanup);
|
|
768
780
|
disableCanvasTextMode(canvasName);
|
|
769
781
|
cleanup?.();
|
|
@@ -913,6 +925,32 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
913
925
|
return nodeTools;
|
|
914
926
|
};
|
|
915
927
|
|
|
928
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
929
|
+
function withRAFThrottle(func) {
|
|
930
|
+
let rafId = null;
|
|
931
|
+
let lastArgs = null;
|
|
932
|
+
const throttled = (...args) => {
|
|
933
|
+
lastArgs = args;
|
|
934
|
+
if (rafId === null) {
|
|
935
|
+
rafId = requestAnimationFrame(() => {
|
|
936
|
+
if (lastArgs) {
|
|
937
|
+
func(...lastArgs);
|
|
938
|
+
}
|
|
939
|
+
rafId = null;
|
|
940
|
+
lastArgs = null;
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
};
|
|
944
|
+
throttled.cleanup = () => {
|
|
945
|
+
if (rafId !== null) {
|
|
946
|
+
cancelAnimationFrame(rafId);
|
|
947
|
+
rafId = null;
|
|
948
|
+
lastArgs = null;
|
|
949
|
+
}
|
|
950
|
+
};
|
|
951
|
+
return throttled;
|
|
952
|
+
}
|
|
953
|
+
|
|
916
954
|
const DEFAULT_WIDTH = 400;
|
|
917
955
|
const RESIZE_CONFIG = {
|
|
918
956
|
minWidth: 320,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.
|
|
4
|
+
* @version 2.3.0
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -649,63 +649,73 @@
|
|
|
649
649
|
return mutationObserver;
|
|
650
650
|
};
|
|
651
651
|
|
|
652
|
-
|
|
653
|
-
function withRAFThrottle(func) {
|
|
654
|
-
let rafId = null;
|
|
655
|
-
let lastArgs = null;
|
|
656
|
-
const throttled = (...args) => {
|
|
657
|
-
lastArgs = args;
|
|
658
|
-
if (rafId === null) {
|
|
659
|
-
rafId = requestAnimationFrame(() => {
|
|
660
|
-
if (lastArgs) {
|
|
661
|
-
func(...lastArgs);
|
|
662
|
-
}
|
|
663
|
-
rafId = null;
|
|
664
|
-
lastArgs = null;
|
|
665
|
-
});
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
throttled.cleanup = () => {
|
|
669
|
-
if (rafId !== null) {
|
|
670
|
-
cancelAnimationFrame(rafId);
|
|
671
|
-
rafId = null;
|
|
672
|
-
lastArgs = null;
|
|
673
|
-
}
|
|
674
|
-
};
|
|
675
|
-
return throttled;
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
const handleTextChange = (node, mutations) => {
|
|
652
|
+
const handleTextChange = (node, mutations, final = false) => {
|
|
679
653
|
// Check if any mutation is a text content change
|
|
680
654
|
const hasTextChange = mutations.some((mutation) => {
|
|
681
655
|
return (mutation.type === "characterData" ||
|
|
682
656
|
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
|
|
683
657
|
});
|
|
684
|
-
if (!hasTextChange) {
|
|
658
|
+
if (!hasTextChange && !final) {
|
|
685
659
|
return;
|
|
686
660
|
}
|
|
687
661
|
// Get the text content of the node
|
|
688
662
|
const textContent = node.textContent ?? "";
|
|
689
663
|
// Get the node ID
|
|
690
664
|
const nodeId = node.getAttribute("data-node-id");
|
|
665
|
+
console.log("textContentChanged", textContent, final);
|
|
691
666
|
// Send postMessage with the text change
|
|
692
667
|
sendPostMessage("textContentChanged", {
|
|
693
668
|
nodeId,
|
|
694
669
|
textContent,
|
|
670
|
+
final,
|
|
695
671
|
});
|
|
696
672
|
};
|
|
697
673
|
|
|
698
674
|
const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
675
|
+
// Accumulate mutations instead of replacing them
|
|
676
|
+
let pendingMutations = [];
|
|
677
|
+
let rafId1 = null;
|
|
678
|
+
let rafId2 = null;
|
|
679
|
+
const processMutations = () => {
|
|
680
|
+
if (pendingMutations.length > 0) {
|
|
681
|
+
const mutationsToProcess = [...pendingMutations];
|
|
682
|
+
pendingMutations = [];
|
|
683
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
684
|
+
}
|
|
685
|
+
};
|
|
686
|
+
const scheduleProcess = () => {
|
|
687
|
+
if (rafId1 === null) {
|
|
688
|
+
rafId1 = requestAnimationFrame(() => {
|
|
689
|
+
// First RAF: let browser complete layout
|
|
690
|
+
rafId2 = requestAnimationFrame(() => {
|
|
691
|
+
// Second RAF: read textContent after layout is complete
|
|
692
|
+
processMutations();
|
|
693
|
+
rafId1 = null;
|
|
694
|
+
rafId2 = null;
|
|
695
|
+
});
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
const cleanup = () => {
|
|
700
|
+
if (rafId1 !== null) {
|
|
701
|
+
cancelAnimationFrame(rafId1);
|
|
702
|
+
rafId1 = null;
|
|
703
|
+
}
|
|
704
|
+
if (rafId2 !== null) {
|
|
705
|
+
cancelAnimationFrame(rafId2);
|
|
706
|
+
rafId2 = null;
|
|
707
|
+
}
|
|
708
|
+
pendingMutations = [];
|
|
709
|
+
};
|
|
702
710
|
const mutationObserver = connectMutationObserver(node, (mutations) => {
|
|
703
|
-
|
|
711
|
+
// Accumulate mutations instead of replacing
|
|
712
|
+
pendingMutations.push(...mutations);
|
|
713
|
+
scheduleProcess();
|
|
704
714
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
705
715
|
});
|
|
706
716
|
return () => {
|
|
707
717
|
mutationObserver.disconnect();
|
|
708
|
-
|
|
718
|
+
cleanup();
|
|
709
719
|
};
|
|
710
720
|
};
|
|
711
721
|
|
|
@@ -770,6 +780,8 @@
|
|
|
770
780
|
}
|
|
771
781
|
blurInProgress = true;
|
|
772
782
|
const nodeToCleanup = editableNode;
|
|
783
|
+
// Send final textContentChanged message before cleanup
|
|
784
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
773
785
|
makeNodeNonEditable(nodeToCleanup);
|
|
774
786
|
disableCanvasTextMode(canvasName);
|
|
775
787
|
cleanup?.();
|
|
@@ -919,6 +931,32 @@
|
|
|
919
931
|
return nodeTools;
|
|
920
932
|
};
|
|
921
933
|
|
|
934
|
+
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
935
|
+
function withRAFThrottle(func) {
|
|
936
|
+
let rafId = null;
|
|
937
|
+
let lastArgs = null;
|
|
938
|
+
const throttled = (...args) => {
|
|
939
|
+
lastArgs = args;
|
|
940
|
+
if (rafId === null) {
|
|
941
|
+
rafId = requestAnimationFrame(() => {
|
|
942
|
+
if (lastArgs) {
|
|
943
|
+
func(...lastArgs);
|
|
944
|
+
}
|
|
945
|
+
rafId = null;
|
|
946
|
+
lastArgs = null;
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
};
|
|
950
|
+
throttled.cleanup = () => {
|
|
951
|
+
if (rafId !== null) {
|
|
952
|
+
cancelAnimationFrame(rafId);
|
|
953
|
+
rafId = null;
|
|
954
|
+
lastArgs = null;
|
|
955
|
+
}
|
|
956
|
+
};
|
|
957
|
+
return throttled;
|
|
958
|
+
}
|
|
959
|
+
|
|
922
960
|
const DEFAULT_WIDTH = 400;
|
|
923
961
|
const RESIZE_CONFIG = {
|
|
924
962
|
minWidth: 320,
|
|
@@ -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";const t=(e,t="canvas")=>{const n=window[t];return e.reduce((e,t)=>e?.[t],n)};function n(e,t){window.parent.postMessage({source:"node-edit-utils",action:e,data:t,timestamp:Date.now()},"*")}const o=()=>document.querySelector(".canvas-container"),r=()=>{const e=o()||document.body,t=e.querySelector(".highlight-frame-overlay");t&&t.remove();const n=e.querySelector(".highlight-frame-tools-wrapper");n&&n.remove()},s=(e,t,n)=>{e&&t&&n.enableEditMode(e,t)},i=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],d=0,l=null;const c=(e,t,n)=>{let o=null;const r=e.clientX,c=e.clientY,u=e.metaKey||e.ctrlKey,m=((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})(r,c).filter(e=>!i.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none")&&!(e=>{let t=e.parentElement;for(;t;){if("true"===t.getAttribute("data-instance"))return!0;if("node-provider"===t.getAttribute("data-role"))break;t=t.parentElement}return!1})(e)),h=n.getEditableNode();if(h&&m.includes(h))return o=h,l=o,o;if(u)return a=[],o=m[0],l&&l===o&&s(o,t,n),l=o,o;var g,p;p=m,(g=a).length===p.length&&g.every((e,t)=>e===p[t])?d<=m.length-2&&d++:d=0;return o=m[m.length-1-d],a=m,l&&l===o&&s(o,t,n),l=o,o},u=(e,t,n,o)=>{const s=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)},i=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(),void o(null);o(c(e,t,n))})(n,e,o,t)},a=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",s),document.addEventListener("click",i),document.addEventListener("keydown",a),()=>{window.removeEventListener("message",s),document.removeEventListener("click",i),document.removeEventListener("keydown",a)}},m=e=>"true"===e.getAttribute("data-instance"),h=(e,t,n,o,r=!1,s=!1)=>{const i=document.createElementNS("http://www.w3.org/2000/svg","rect");return i.setAttribute("x",(t-3).toString()),i.setAttribute("y",(n-3).toString()),i.setAttribute("width",6..toString()),i.setAttribute("height",6..toString()),i.setAttribute("vector-effect","non-scaling-stroke"),i.classList.add("highlight-frame-handle",o),r?i.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):s&&i.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),e.appendChild(i),i};function g(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const p=(e,t=!1,n=!1)=>{const{top:r,left:s,width:i,height:a}=g(e),d=Math.max(i,3),l=document.createElementNS("http://www.w3.org/2000/svg","svg");l.classList.add("highlight-frame-overlay"),t&&l.classList.add("is-instance"),n&&l.classList.add("is-text-edit"),l.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),l.style.position="absolute",l.style.top="0",l.style.left="0",l.style.width="100vw",l.style.height="100vh",l.style.pointerEvents="none",l.style.zIndex="500";const c=document.documentElement.clientWidth||window.innerWidth,u=document.documentElement.clientHeight||window.innerHeight;l.setAttribute("width",c.toString()),l.setAttribute("height",u.toString());const m=document.createElementNS("http://www.w3.org/2000/svg","g");m.classList.add("highlight-frame-group"),m.setAttribute("transform",`translate(${s}, ${r})`);const p=document.createElementNS("http://www.w3.org/2000/svg","rect");p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("width",d.toString()),p.setAttribute("height",a.toString()),p.setAttribute("vector-effect","non-scaling-stroke"),p.classList.add("highlight-frame-rect"),t?p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):n&&p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),m.appendChild(p),((e,t,n,o=!1,r=!1)=>{h(e,0,0,"handle-top-left",o,r),h(e,t,0,"handle-top-right",o,r),h(e,t,n,"handle-bottom-right",o,r),h(e,0,n,"handle-bottom-left",o,r)})(m,d,a,t,n),l.appendChild(m);const b=o();return b?b.appendChild(l):document.body.appendChild(l),l},b={div:"Container",h1:"Heading 1",h2:"Heading 2",h3:"Heading 3",h4:"Heading 4",h5:"Heading 5",h6:"Heading 6",p:"Text",li:"List Item",ul:"Unordered List",ol:"Ordered List",img:"Image",a:"Link"},v=(e,t,n=!1,o=!1)=>{const r=document.createElement("div");r.className="node-tools",n&&r.classList.add("is-instance"),o&&r.classList.add("is-text-edit"),t.appendChild(r),((e,t)=>{const n=document.createElement("div");n.className="tag-label";const o=e.getAttribute("data-instance-name"),r=e.tagName.toLowerCase(),s=o||b[r]||r;var i;n.textContent=(i=s)?i.charAt(0).toUpperCase()+i.slice(1):i,t.appendChild(n)})(e,r)};function y(){return(o()||document.body).querySelector(".highlight-frame-overlay")}const f=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",w=()=>getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)",E=(e,n,r="canvas")=>{const s=y();if(!s)return;const i=m(e),a="true"===e.contentEditable,d=document.documentElement.clientWidth||window.innerWidth,l=document.documentElement.clientHeight||window.innerHeight;s.setAttribute("width",d.toString()),s.setAttribute("height",l.toString()),i?s.classList.add("is-instance"):s.classList.remove("is-instance"),a?s.classList.add("is-text-edit"):s.classList.remove("is-text-edit");const c=s.querySelector(".highlight-frame-group");if(!c)return;const u=c.querySelector("rect");if(!u)return;i?u.setAttribute("stroke",f()):a?u.setAttribute("stroke",w()):u.removeAttribute("stroke");const h=(o()||document.body).querySelector(".highlight-frame-tools-wrapper"),p=h?.querySelector(".node-tools"),b=t(["zoom","current"],r)??1,v=g(e),{top:E,left:L,width:A,height:S}=v,x=Math.max(A,3),C=E+S;h&&(i?h.classList.add("is-instance"):h.classList.remove("is-instance"),a?h.classList.add("is-text-edit"):h.classList.remove("is-text-edit")),p&&(i?p.classList.add("is-instance"):p.classList.remove("is-instance"),a?p.classList.add("is-text-edit"):p.classList.remove("is-text-edit")),c.setAttribute("transform",`translate(${L}, ${E})`),u.setAttribute("width",x.toString()),u.setAttribute("height",S.toString());const k=c.querySelector(".handle-top-left"),N=c.querySelector(".handle-top-right"),q=c.querySelector(".handle-bottom-right"),M=c.querySelector(".handle-bottom-left");[k,N,q,M].forEach(e=>{e&&(i?e.setAttribute("stroke",f()):a?e.setAttribute("stroke",w()):e.removeAttribute("stroke"))}),k&&(k.setAttribute("x",(-3).toString()),k.setAttribute("y",(-3).toString())),N&&(N.setAttribute("x",(x-3).toString()),N.setAttribute("y",(-3).toString())),q&&(q.setAttribute("x",(x-3).toString()),q.setAttribute("y",(S-3).toString())),M&&(M.setAttribute("x",(-3).toString()),M.setAttribute("y",(S-3).toString())),h&&(h.style.transform=`translate(${L}px, ${C}px)`),b<=10?n.style.setProperty("--tool-opacity","1"):n.style.setProperty("--tool-opacity","0")},L=e=>{const t=y();if(!t)return;const n=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=n;const r=(o()||document.body).querySelector(".highlight-frame-tools-wrapper");r&&(r.style.display=n)},A=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)}};function S(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 x=(e,t,o="canvas")=>{const r=S(t=>{((e,t)=>{if(!t.some(e=>"characterData"===e.type||"childList"===e.type&&(e.addedNodes.length>0||e.removedNodes.length>0)))return;const o=e.textContent??"";n("textContentChanged",{nodeId:e.getAttribute("data-node-id"),textContent:o})})(e,t)}),s=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,n=>{r(n),E(e,t,o)});return()=>{s.disconnect(),r.cleanup()}},C=(e="canvas")=>{let n=null,o=!1,r=null;const s=()=>{if(o||!n)return;o=!0;var s;(s=n).contentEditable="false",s.classList.remove("is-editable"),s.style.outline="none",((e="canvas")=>{const n=t(["keyboard","disableTextEditMode"],e);n?.()})(e),r?.(),n=null,o=!1};return{enableEditMode:(o,i)=>{if(n===o)return;n&&n!==o&&s();const a=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(o);a&&(n=o,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(o),((e="canvas")=>{const n=t(["keyboard","enableTextEditMode"],e);n?.()})(e),r=((e,t,n,o="canvas")=>{if(!t)return()=>{};e.addEventListener("blur",n);const r=A(e),s=x(e,t,o);return()=>{e.removeEventListener("blur",n),r(),s?.()}})(o,i,s,e))},blurEditMode:s,getEditableNode:()=>n,isEditing:()=>null!==n}},k=320,N=1680,q=[{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"}],M=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(k,Math.min(N,n))})(n,(e.clientX-t)/o);return r},P=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<q.length&&(q[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(e="canvas"){const n=document.querySelector(".transform-layer");if(!n)return{disconnect:()=>{}};const o=new MutationObserver(()=>{((e="canvas")=>{const n=t(["zoom","current"],e)??1;document.body.style.setProperty("--zoom",n.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/n).toFixed(3)),document.body.dataset.zoom=n.toFixed(5),document.body.dataset.strokeWidth=(2/n).toFixed(3)})(e);const n=window.nodeTools;n?.refreshHighlightFrame&&n.refreshHighlightFrame()});return o.observe(n,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0}),{disconnect:function(){o.disconnect()}}},e.createNodeTools=(e,t="canvas")=>{const s=e;let i=null,a=null,d=null,l=null;const c=C(t),h=e=>{if(l!==e){if(c.isEditing()){const t=c.getEditableNode();t&&t!==e&&c.blurEditMode()}if(i?.disconnect(),a?.disconnect(),d?.disconnect(),e&&s){const o=()=>{if(!document.contains(e))return r(),l=null,i?.disconnect(),a?.disconnect(),d?.disconnect(),void n("selectedNodeChanged",null)};a=new MutationObserver(()=>{o(),document.contains(e)&&(E(e,s,t),L(e))}),a.observe(e,{attributes:!0,characterData:!0,childList:!0,subtree:!0});const c=e.parentElement;c&&(d=new MutationObserver(t=>{for(const n of t)if("childList"===n.type)for(const t of Array.from(n.removedNodes))if(t===e||t instanceof Node&&t.contains(e))return void o()}),d.observe(c,{childList:!0,subtree:!1})),i=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{o(),document.contains(e)&&(E(e,s,t),L(e))})}l=e,n("selectedNodeChanged",e?.getAttribute("data-node-id")??null),(e=>{if(!e)return;const t=y(),n=o(),r=n?.querySelector(".highlight-frame-tools-wrapper")||document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove(),r&&r.remove();const s=m(e),i="true"===e.contentEditable,a=p(e,s,i);"true"===e.contentEditable&&a.classList.add("is-editable");const{left:d,top:l,height:c}=g(e),u=l+c,h=document.createElement("div");h.classList.add("highlight-frame-tools-wrapper"),s&&h.classList.add("is-instance"),i&&h.classList.add("is-text-edit"),h.style.position="absolute",h.style.transform=`translate(${d}px, ${u}px)`,h.style.transformOrigin="left center",h.style.pointerEvents="none",h.style.zIndex="500",v(e,h,s,i),n?n.appendChild(h):document.body.appendChild(h)})(e),e&&s&&(L(e),L(e))}},b=u(s,h,()=>{c.isEditing()&&c.blurEditMode(),l&&s&&(r(),l=null,i?.disconnect(),a?.disconnect(),d?.disconnect())},c),f={selectNode:h,getSelectedNode:()=>l,refreshHighlightFrame:()=>{l&&s&&(E(l,s,t),L(l))},clearSelectedNode:()=>{r(),l=null,i?.disconnect(),a?.disconnect(),d?.disconnect()},getEditableNode:()=>c.getEditableNode(),cleanup:()=>{b(),i?.disconnect(),a?.disconnect(),d?.disconnect(),c.blurEditMode(),r(),l=null,n("selectedNodeChanged",null)}};var w,A;return w="nodeTools",A=f,"undefined"!=typeof window&&(window[w]=A),f},e.createViewport=e=>{const t=o(),n=e.querySelector(".resize-handle");n&&n.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",q.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,P);let s=!1,i=0,a=0;const d=S(n=>{if(!s)return;t&&(t.style.cursor="ew-resize");const o=M(n,i,a);P(e,o)}),l=((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,i=t.clientX,a=e.offsetWidth},d,e=>{e.preventDefault(),e.stopPropagation(),t&&(t.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{P(e,t)},cleanup:()=>{s=!1,d?.cleanup(),l(),r.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";const t=(e,t="canvas")=>{const n=window[t];return e.reduce((e,t)=>e?.[t],n)};function n(e,t){window.parent.postMessage({source:"node-edit-utils",action:e,data:t,timestamp:Date.now()},"*")}const o=()=>document.querySelector(".canvas-container"),r=()=>{const e=o()||document.body,t=e.querySelector(".highlight-frame-overlay");t&&t.remove();const n=e.querySelector(".highlight-frame-tools-wrapper");n&&n.remove()},i=(e,t,n)=>{e&&t&&n.enableEditMode(e,t)},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],l=0,d=null;const c=(e,t,n)=>{let o=null;const r=e.clientX,c=e.clientY,u=e.metaKey||e.ctrlKey,m=((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})(r,c).filter(e=>!s.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none")&&!(e=>{let t=e.parentElement;for(;t;){if("true"===t.getAttribute("data-instance"))return!0;if("node-provider"===t.getAttribute("data-role"))break;t=t.parentElement}return!1})(e)),h=n.getEditableNode();if(h&&m.includes(h))return o=h,d=o,o;if(u)return a=[],o=m[0],d&&d===o&&i(o,t,n),d=o,o;var g,p;p=m,(g=a).length===p.length&&g.every((e,t)=>e===p[t])?l<=m.length-2&&l++:l=0;return o=m[m.length-1-l],a=m,d&&d===o&&i(o,t,n),d=o,o},u=(e,t,n,o)=>{const i=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)},s=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(),void o(null);o(c(e,t,n))})(n,e,o,t)},a=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",i),document.addEventListener("click",s),document.addEventListener("keydown",a),()=>{window.removeEventListener("message",i),document.removeEventListener("click",s),document.removeEventListener("keydown",a)}},m=e=>"true"===e.getAttribute("data-instance"),h=(e,t,n,o,r=!1,i=!1)=>{const s=document.createElementNS("http://www.w3.org/2000/svg","rect");return s.setAttribute("x",(t-3).toString()),s.setAttribute("y",(n-3).toString()),s.setAttribute("width",6..toString()),s.setAttribute("height",6..toString()),s.setAttribute("vector-effect","non-scaling-stroke"),s.classList.add("highlight-frame-handle",o),r?s.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):i&&s.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),e.appendChild(s),s};function g(e){const t=e.getBoundingClientRect();return{top:t.top,left:t.left,width:t.width,height:t.height}}const p=(e,t=!1,n=!1)=>{const{top:r,left:i,width:s,height:a}=g(e),l=Math.max(s,3),d=document.createElementNS("http://www.w3.org/2000/svg","svg");d.classList.add("highlight-frame-overlay"),t&&d.classList.add("is-instance"),n&&d.classList.add("is-text-edit"),d.setAttribute("data-node-id",e.getAttribute("data-node-id")||""),d.style.position="absolute",d.style.top="0",d.style.left="0",d.style.width="100vw",d.style.height="100vh",d.style.pointerEvents="none",d.style.zIndex="500";const c=document.documentElement.clientWidth||window.innerWidth,u=document.documentElement.clientHeight||window.innerHeight;d.setAttribute("width",c.toString()),d.setAttribute("height",u.toString());const m=document.createElementNS("http://www.w3.org/2000/svg","g");m.classList.add("highlight-frame-group"),m.setAttribute("transform",`translate(${i}, ${r})`);const p=document.createElementNS("http://www.w3.org/2000/svg","rect");p.setAttribute("x","0"),p.setAttribute("y","0"),p.setAttribute("width",l.toString()),p.setAttribute("height",a.toString()),p.setAttribute("vector-effect","non-scaling-stroke"),p.classList.add("highlight-frame-rect"),t?p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)"):n&&p.setAttribute("stroke",getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)"),m.appendChild(p),((e,t,n,o=!1,r=!1)=>{h(e,0,0,"handle-top-left",o,r),h(e,t,0,"handle-top-right",o,r),h(e,t,n,"handle-bottom-right",o,r),h(e,0,n,"handle-bottom-left",o,r)})(m,l,a,t,n),d.appendChild(m);const b=o();return b?b.appendChild(d):document.body.appendChild(d),d},b={div:"Container",h1:"Heading 1",h2:"Heading 2",h3:"Heading 3",h4:"Heading 4",h5:"Heading 5",h6:"Heading 6",p:"Text",li:"List Item",ul:"Unordered List",ol:"Ordered List",img:"Image",a:"Link"},v=(e,t,n=!1,o=!1)=>{const r=document.createElement("div");r.className="node-tools",n&&r.classList.add("is-instance"),o&&r.classList.add("is-text-edit"),t.appendChild(r),((e,t)=>{const n=document.createElement("div");n.className="tag-label";const o=e.getAttribute("data-instance-name"),r=e.tagName.toLowerCase(),i=o||b[r]||r;var s;n.textContent=(s=i)?s.charAt(0).toUpperCase()+s.slice(1):s,t.appendChild(n)})(e,r)};function y(){return(o()||document.body).querySelector(".highlight-frame-overlay")}const f=()=>getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim()||"oklch(65.6% 0.241 354.308)",w=()=>getComputedStyle(document.documentElement).getPropertyValue("--text-edit-color").trim()||"oklch(62.3% 0.214 259.815)",E=(e,n,r="canvas")=>{const i=y();if(!i)return;const s=m(e),a="true"===e.contentEditable,l=document.documentElement.clientWidth||window.innerWidth,d=document.documentElement.clientHeight||window.innerHeight;i.setAttribute("width",l.toString()),i.setAttribute("height",d.toString()),s?i.classList.add("is-instance"):i.classList.remove("is-instance"),a?i.classList.add("is-text-edit"):i.classList.remove("is-text-edit");const c=i.querySelector(".highlight-frame-group");if(!c)return;const u=c.querySelector("rect");if(!u)return;s?u.setAttribute("stroke",f()):a?u.setAttribute("stroke",w()):u.removeAttribute("stroke");const h=(o()||document.body).querySelector(".highlight-frame-tools-wrapper"),p=h?.querySelector(".node-tools"),b=t(["zoom","current"],r)??1,v=g(e),{top:E,left:L,width:A,height:x}=v,S=Math.max(A,3),C=E+x;h&&(s?h.classList.add("is-instance"):h.classList.remove("is-instance"),a?h.classList.add("is-text-edit"):h.classList.remove("is-text-edit")),p&&(s?p.classList.add("is-instance"):p.classList.remove("is-instance"),a?p.classList.add("is-text-edit"):p.classList.remove("is-text-edit")),c.setAttribute("transform",`translate(${L}, ${E})`),u.setAttribute("width",S.toString()),u.setAttribute("height",x.toString());const k=c.querySelector(".handle-top-left"),N=c.querySelector(".handle-top-right"),q=c.querySelector(".handle-bottom-right"),M=c.querySelector(".handle-bottom-left");[k,N,q,M].forEach(e=>{e&&(s?e.setAttribute("stroke",f()):a?e.setAttribute("stroke",w()):e.removeAttribute("stroke"))}),k&&(k.setAttribute("x",(-3).toString()),k.setAttribute("y",(-3).toString())),N&&(N.setAttribute("x",(S-3).toString()),N.setAttribute("y",(-3).toString())),q&&(q.setAttribute("x",(S-3).toString()),q.setAttribute("y",(x-3).toString())),M&&(M.setAttribute("x",(-3).toString()),M.setAttribute("y",(x-3).toString())),h&&(h.style.transform=`translate(${L}px, ${C}px)`),b<=10?n.style.setProperty("--tool-opacity","1"):n.style.setProperty("--tool-opacity","0")},L=e=>{const t=y();if(!t)return;const n=e.classList.contains("hidden")||e.classList.contains("select-none")?"none":"";t.style.display=n;const r=(o()||document.body).querySelector(".highlight-frame-tools-wrapper");r&&(r.style.display=n)},A=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)}},x=(e,t,o=!1)=>{if(!t.some(e=>"characterData"===e.type||"childList"===e.type&&(e.addedNodes.length>0||e.removedNodes.length>0))&&!o)return;const r=e.textContent??"",i=e.getAttribute("data-node-id");console.log("textContentChanged",r,o),n("textContentChanged",{nodeId:i,textContent:r,final:o})},S=(e,t,n="canvas")=>{let o=[],r=null,i=null;const s=()=>{null===r&&(r=requestAnimationFrame(()=>{i=requestAnimationFrame(()=>{(()=>{if(o.length>0){const t=[...o];o=[],x(e,t,!1)}})(),r=null,i=null})}))},a=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,r=>{o.push(...r),s(),E(e,t,n)});return()=>{a.disconnect(),null!==r&&(cancelAnimationFrame(r),r=null),null!==i&&(cancelAnimationFrame(i),i=null),o=[]}},C=(e="canvas")=>{let n=null,o=!1,r=null;const i=()=>{if(o||!n)return;o=!0;const i=n;var s;x(i,[],!0),(s=i).contentEditable="false",s.classList.remove("is-editable"),s.style.outline="none",((e="canvas")=>{const n=t(["keyboard","disableTextEditMode"],e);n?.()})(e),r?.(),n=null,o=!1};return{enableEditMode:(o,s)=>{if(n===o)return;n&&n!==o&&i();const a=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(o);a&&(n=o,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(o),((e="canvas")=>{const n=t(["keyboard","enableTextEditMode"],e);n?.()})(e),r=((e,t,n,o="canvas")=>{if(!t)return()=>{};e.addEventListener("blur",n);const r=A(e),i=S(e,t,o);return()=>{e.removeEventListener("blur",n),r(),i?.()}})(o,s,i,e))},blurEditMode:i,getEditableNode:()=>n,isEditing:()=>null!==n}};const k=320,N=1680,q=[{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"}],M=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(k,Math.min(N,n))})(n,(e.clientX-t)/o);return r},P=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<q.length&&(q[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(e="canvas"){const n=document.querySelector(".transform-layer");if(!n)return{disconnect:()=>{}};const o=new MutationObserver(()=>{((e="canvas")=>{const n=t(["zoom","current"],e)??1;document.body.style.setProperty("--zoom",n.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/n).toFixed(3)),document.body.dataset.zoom=n.toFixed(5),document.body.dataset.strokeWidth=(2/n).toFixed(3)})(e);const n=window.nodeTools;n?.refreshHighlightFrame&&n.refreshHighlightFrame()});return o.observe(n,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0}),{disconnect:function(){o.disconnect()}}},e.createNodeTools=(e,t="canvas")=>{const i=e;let s=null,a=null,l=null,d=null;const c=C(t),h=e=>{if(d!==e){if(c.isEditing()){const t=c.getEditableNode();t&&t!==e&&c.blurEditMode()}if(s?.disconnect(),a?.disconnect(),l?.disconnect(),e&&i){const o=()=>{if(!document.contains(e))return r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect(),void n("selectedNodeChanged",null)};a=new MutationObserver(()=>{o(),document.contains(e)&&(E(e,i,t),L(e))}),a.observe(e,{attributes:!0,characterData:!0,childList:!0,subtree:!0});const c=e.parentElement;c&&(l=new MutationObserver(t=>{for(const n of t)if("childList"===n.type)for(const t of Array.from(n.removedNodes))if(t===e||t instanceof Node&&t.contains(e))return void o()}),l.observe(c,{childList:!0,subtree:!1})),s=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(e,()=>{o(),document.contains(e)&&(E(e,i,t),L(e))})}d=e,n("selectedNodeChanged",e?.getAttribute("data-node-id")??null),(e=>{if(!e)return;const t=y(),n=o(),r=n?.querySelector(".highlight-frame-tools-wrapper")||document.body.querySelector(".highlight-frame-tools-wrapper");t&&t.remove(),r&&r.remove();const i=m(e),s="true"===e.contentEditable,a=p(e,i,s);"true"===e.contentEditable&&a.classList.add("is-editable");const{left:l,top:d,height:c}=g(e),u=d+c,h=document.createElement("div");h.classList.add("highlight-frame-tools-wrapper"),i&&h.classList.add("is-instance"),s&&h.classList.add("is-text-edit"),h.style.position="absolute",h.style.transform=`translate(${l}px, ${u}px)`,h.style.transformOrigin="left center",h.style.pointerEvents="none",h.style.zIndex="500",v(e,h,i,s),n?n.appendChild(h):document.body.appendChild(h)})(e),e&&i&&(L(e),L(e))}},b=u(i,h,()=>{c.isEditing()&&c.blurEditMode(),d&&i&&(r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect())},c),f={selectNode:h,getSelectedNode:()=>d,refreshHighlightFrame:()=>{d&&i&&(E(d,i,t),L(d))},clearSelectedNode:()=>{r(),d=null,s?.disconnect(),a?.disconnect(),l?.disconnect()},getEditableNode:()=>c.getEditableNode(),cleanup:()=>{b(),s?.disconnect(),a?.disconnect(),l?.disconnect(),c.blurEditMode(),r(),d=null,n("selectedNodeChanged",null)}};var w,A;return w="nodeTools",A=f,"undefined"!=typeof window&&(window[w]=A),f},e.createViewport=e=>{const t=o(),n=e.querySelector(".resize-handle");n&&n.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",q.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,P);let i=!1,s=0,a=0;const l=function(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}(n=>{if(!i)return;t&&(t.style.cursor="ew-resize");const o=M(n,s,a);P(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(),i=!0,s=t.clientX,a=e.offsetWidth},l,e=>{e.preventDefault(),e.stopPropagation(),t&&(t.style.cursor="default"),i=!1},()=>{i=!1});return{setWidth:t=>{P(e,t)},cleanup:()=>{i=!1,l?.cleanup(),d(),r.remove()}}}});
|
package/package.json
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { connectMutationObserver } from "../../../helpers/observer/connectMutationObserver";
|
|
2
|
-
import { withRAFThrottle } from "../../../helpers/withRAF";
|
|
3
2
|
import { refreshHighlightFrame } from "../../highlight/refreshHighlightFrame";
|
|
4
3
|
import { handleTextChange } from "../helpers/handleTextChange";
|
|
5
4
|
|
|
@@ -8,17 +7,54 @@ export const setupMutationObserver = (
|
|
|
8
7
|
nodeProvider: HTMLElement,
|
|
9
8
|
canvasName: string = "canvas"
|
|
10
9
|
): (() => void) | undefined => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
// Accumulate mutations instead of replacing them
|
|
11
|
+
let pendingMutations: MutationRecord[] = [];
|
|
12
|
+
let rafId1: number | null = null;
|
|
13
|
+
let rafId2: number | null = null;
|
|
14
|
+
|
|
15
|
+
const processMutations = () => {
|
|
16
|
+
if (pendingMutations.length > 0) {
|
|
17
|
+
const mutationsToProcess = [...pendingMutations];
|
|
18
|
+
pendingMutations = [];
|
|
19
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const scheduleProcess = () => {
|
|
24
|
+
if (rafId1 === null) {
|
|
25
|
+
rafId1 = requestAnimationFrame(() => {
|
|
26
|
+
// First RAF: let browser complete layout
|
|
27
|
+
rafId2 = requestAnimationFrame(() => {
|
|
28
|
+
// Second RAF: read textContent after layout is complete
|
|
29
|
+
processMutations();
|
|
30
|
+
rafId1 = null;
|
|
31
|
+
rafId2 = null;
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const cleanup = () => {
|
|
38
|
+
if (rafId1 !== null) {
|
|
39
|
+
cancelAnimationFrame(rafId1);
|
|
40
|
+
rafId1 = null;
|
|
41
|
+
}
|
|
42
|
+
if (rafId2 !== null) {
|
|
43
|
+
cancelAnimationFrame(rafId2);
|
|
44
|
+
rafId2 = null;
|
|
45
|
+
}
|
|
46
|
+
pendingMutations = [];
|
|
47
|
+
};
|
|
14
48
|
|
|
15
49
|
const mutationObserver = connectMutationObserver(node, (mutations) => {
|
|
16
|
-
|
|
50
|
+
// Accumulate mutations instead of replacing
|
|
51
|
+
pendingMutations.push(...mutations);
|
|
52
|
+
scheduleProcess();
|
|
17
53
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
18
54
|
});
|
|
19
55
|
|
|
20
56
|
return () => {
|
|
21
57
|
mutationObserver.disconnect();
|
|
22
|
-
|
|
58
|
+
cleanup();
|
|
23
59
|
};
|
|
24
60
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { sendPostMessage } from "@/lib/post-message/sendPostMessage";
|
|
2
2
|
|
|
3
|
-
export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[]): void => {
|
|
3
|
+
export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[], final: boolean = false): void => {
|
|
4
4
|
// Check if any mutation is a text content change
|
|
5
5
|
const hasTextChange = mutations.some((mutation) => {
|
|
6
6
|
return (
|
|
@@ -9,7 +9,7 @@ export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[])
|
|
|
9
9
|
);
|
|
10
10
|
});
|
|
11
11
|
|
|
12
|
-
if (!hasTextChange) {
|
|
12
|
+
if (!hasTextChange && !final) {
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -19,9 +19,12 @@ export const handleTextChange = (node: HTMLElement, mutations: MutationRecord[])
|
|
|
19
19
|
// Get the node ID
|
|
20
20
|
const nodeId = node.getAttribute("data-node-id");
|
|
21
21
|
|
|
22
|
+
console.log("textContentChanged", textContent, final);
|
|
23
|
+
|
|
22
24
|
// Send postMessage with the text change
|
|
23
25
|
sendPostMessage("textContentChanged", {
|
|
24
26
|
nodeId,
|
|
25
27
|
textContent,
|
|
28
|
+
final,
|
|
26
29
|
});
|
|
27
30
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { disableCanvasTextMode } from "@/lib/canvas/disableCanvasTextMode";
|
|
2
2
|
import { enableCanvasTextMode } from "@/lib/canvas/enableCanvasTextMode";
|
|
3
3
|
import { setupNodeListeners } from "./events/setupNodeListeners";
|
|
4
|
+
import { handleTextChange } from "./helpers/handleTextChange";
|
|
4
5
|
import { hasTextContent } from "./helpers/hasTextContent";
|
|
5
6
|
import { makeNodeEditable } from "./helpers/makeNodeEditable";
|
|
6
7
|
import { makeNodeNonEditable } from "./helpers/makeNodeNonEditable";
|
|
@@ -49,6 +50,9 @@ export const nodeText = (canvasName: string = "canvas"): NodeText => {
|
|
|
49
50
|
|
|
50
51
|
const nodeToCleanup = editableNode;
|
|
51
52
|
|
|
53
|
+
// Send final textContentChanged message before cleanup
|
|
54
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
55
|
+
|
|
52
56
|
makeNodeNonEditable(nodeToCleanup);
|
|
53
57
|
disableCanvasTextMode(canvasName);
|
|
54
58
|
cleanup?.();
|