@node-edit-utils/core 2.2.9 → 2.3.1
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/lib/viewport/createViewport.d.ts +1 -1
- package/dist/node-edit-utils.cjs.js +23 -36
- package/dist/node-edit-utils.esm.js +23 -36
- package/dist/node-edit-utils.umd.js +23 -36
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/package.json +1 -1
- package/src/lib/node-tools/text/events/setupMutationObserver.ts +1 -1
- package/src/lib/node-tools/text/helpers/handleTextChange.ts +4 -3
- package/src/lib/node-tools/text/nodeText.ts +4 -0
- package/src/lib/viewport/createViewport.ts +8 -7
- package/src/lib/viewport/events/setupEventListener.ts +9 -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,2 +1,2 @@
|
|
|
1
1
|
import type { Viewport } from "./types";
|
|
2
|
-
export declare const createViewport: (container: HTMLElement) => Viewport;
|
|
2
|
+
export declare const createViewport: (container: HTMLElement, initialWidth?: number) => Viewport;
|
|
@@ -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.1
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -645,24 +645,25 @@ const connectMutationObserver = (element, handler) => {
|
|
|
645
645
|
return mutationObserver;
|
|
646
646
|
};
|
|
647
647
|
|
|
648
|
-
const handleTextChange = (node, mutations) => {
|
|
648
|
+
const handleTextChange = (node, mutations, final = false) => {
|
|
649
649
|
// Check if any mutation is a text content change
|
|
650
650
|
const hasTextChange = mutations.some((mutation) => {
|
|
651
651
|
return (mutation.type === "characterData" ||
|
|
652
652
|
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
|
|
653
653
|
});
|
|
654
|
-
if (!hasTextChange) {
|
|
654
|
+
if (!hasTextChange && !final) {
|
|
655
655
|
return;
|
|
656
656
|
}
|
|
657
657
|
// Get the text content of the node
|
|
658
658
|
const textContent = node.textContent ?? "";
|
|
659
659
|
// Get the node ID
|
|
660
660
|
const nodeId = node.getAttribute("data-node-id");
|
|
661
|
-
console.log("textContentChanged",
|
|
661
|
+
console.log("textContentChanged", textContent, final);
|
|
662
662
|
// Send postMessage with the text change
|
|
663
663
|
sendPostMessage("textContentChanged", {
|
|
664
664
|
nodeId,
|
|
665
665
|
textContent,
|
|
666
|
+
final,
|
|
666
667
|
});
|
|
667
668
|
};
|
|
668
669
|
|
|
@@ -675,7 +676,7 @@ const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
|
|
|
675
676
|
if (pendingMutations.length > 0) {
|
|
676
677
|
const mutationsToProcess = [...pendingMutations];
|
|
677
678
|
pendingMutations = [];
|
|
678
|
-
handleTextChange(node, mutationsToProcess);
|
|
679
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
679
680
|
}
|
|
680
681
|
};
|
|
681
682
|
const scheduleProcess = () => {
|
|
@@ -775,6 +776,8 @@ const nodeText = (canvasName = "canvas") => {
|
|
|
775
776
|
}
|
|
776
777
|
blurInProgress = true;
|
|
777
778
|
const nodeToCleanup = editableNode;
|
|
779
|
+
// Send final textContentChanged message before cleanup
|
|
780
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
778
781
|
makeNodeNonEditable(nodeToCleanup);
|
|
779
782
|
disableCanvasTextMode(canvasName);
|
|
780
783
|
cleanup?.();
|
|
@@ -924,32 +927,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
924
927
|
return nodeTools;
|
|
925
928
|
};
|
|
926
929
|
|
|
927
|
-
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
928
|
-
function withRAFThrottle(func) {
|
|
929
|
-
let rafId = null;
|
|
930
|
-
let lastArgs = null;
|
|
931
|
-
const throttled = (...args) => {
|
|
932
|
-
lastArgs = args;
|
|
933
|
-
if (rafId === null) {
|
|
934
|
-
rafId = requestAnimationFrame(() => {
|
|
935
|
-
if (lastArgs) {
|
|
936
|
-
func(...lastArgs);
|
|
937
|
-
}
|
|
938
|
-
rafId = null;
|
|
939
|
-
lastArgs = null;
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
};
|
|
943
|
-
throttled.cleanup = () => {
|
|
944
|
-
if (rafId !== null) {
|
|
945
|
-
cancelAnimationFrame(rafId);
|
|
946
|
-
rafId = null;
|
|
947
|
-
lastArgs = null;
|
|
948
|
-
}
|
|
949
|
-
};
|
|
950
|
-
return throttled;
|
|
951
|
-
}
|
|
952
|
-
|
|
953
930
|
const DEFAULT_WIDTH = 400;
|
|
954
931
|
const RESIZE_CONFIG = {
|
|
955
932
|
minWidth: 320,
|
|
@@ -984,14 +961,22 @@ const RESIZE_PRESETS = [
|
|
|
984
961
|
];
|
|
985
962
|
|
|
986
963
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
964
|
+
const handleMouseLeave = (event) => {
|
|
965
|
+
// Check if mouse is leaving the window/document
|
|
966
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
967
|
+
blurResize();
|
|
968
|
+
}
|
|
969
|
+
};
|
|
987
970
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
988
971
|
document.addEventListener("mousemove", handleResize);
|
|
989
972
|
document.addEventListener("mouseup", stopResize);
|
|
973
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
990
974
|
window.addEventListener("blur", blurResize);
|
|
991
975
|
return () => {
|
|
992
976
|
resizeHandle.removeEventListener("mousedown", startResize);
|
|
993
977
|
document.removeEventListener("mousemove", handleResize);
|
|
994
978
|
document.removeEventListener("mouseup", stopResize);
|
|
979
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
995
980
|
window.removeEventListener("blur", blurResize);
|
|
996
981
|
};
|
|
997
982
|
};
|
|
@@ -1051,7 +1036,7 @@ const updateWidth = (container, width) => {
|
|
|
1051
1036
|
updateActivePreset(container, width);
|
|
1052
1037
|
};
|
|
1053
1038
|
|
|
1054
|
-
const createViewport = (container) => {
|
|
1039
|
+
const createViewport = (container, initialWidth) => {
|
|
1055
1040
|
const canvas = getCanvasContainer();
|
|
1056
1041
|
// Remove any existing resize handle to prevent duplicates
|
|
1057
1042
|
const existingHandle = container.querySelector(".resize-handle");
|
|
@@ -1059,7 +1044,8 @@ const createViewport = (container) => {
|
|
|
1059
1044
|
existingHandle.remove();
|
|
1060
1045
|
}
|
|
1061
1046
|
const resizeHandle = createResizeHandle(container);
|
|
1062
|
-
|
|
1047
|
+
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
1048
|
+
container.style.setProperty("--container-width", `${width}px`);
|
|
1063
1049
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
1064
1050
|
let isDragging = false;
|
|
1065
1051
|
let startX = 0;
|
|
@@ -1080,7 +1066,6 @@ const createViewport = (container) => {
|
|
|
1080
1066
|
const width = calcWidth(event, startX, startWidth);
|
|
1081
1067
|
updateWidth(container, width);
|
|
1082
1068
|
};
|
|
1083
|
-
const throttledHandleResize = withRAFThrottle(handleResize);
|
|
1084
1069
|
const stopResize = (event) => {
|
|
1085
1070
|
event.preventDefault();
|
|
1086
1071
|
event.stopPropagation();
|
|
@@ -1090,12 +1075,14 @@ const createViewport = (container) => {
|
|
|
1090
1075
|
isDragging = false;
|
|
1091
1076
|
};
|
|
1092
1077
|
const blurResize = () => {
|
|
1078
|
+
if (canvas) {
|
|
1079
|
+
canvas.style.cursor = "default";
|
|
1080
|
+
}
|
|
1093
1081
|
isDragging = false;
|
|
1094
1082
|
};
|
|
1095
|
-
const removeListeners = setupEventListener(resizeHandle, startResize,
|
|
1083
|
+
const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
|
|
1096
1084
|
const cleanup = () => {
|
|
1097
1085
|
isDragging = false;
|
|
1098
|
-
throttledHandleResize?.cleanup();
|
|
1099
1086
|
removeListeners();
|
|
1100
1087
|
resizeHandle.remove();
|
|
1101
1088
|
};
|
|
@@ -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.1
|
|
5
5
|
*/
|
|
6
6
|
const getCanvasWindowValue = (path, canvasName = "canvas") => {
|
|
7
7
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
@@ -643,24 +643,25 @@ const connectMutationObserver = (element, handler) => {
|
|
|
643
643
|
return mutationObserver;
|
|
644
644
|
};
|
|
645
645
|
|
|
646
|
-
const handleTextChange = (node, mutations) => {
|
|
646
|
+
const handleTextChange = (node, mutations, final = false) => {
|
|
647
647
|
// Check if any mutation is a text content change
|
|
648
648
|
const hasTextChange = mutations.some((mutation) => {
|
|
649
649
|
return (mutation.type === "characterData" ||
|
|
650
650
|
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
|
|
651
651
|
});
|
|
652
|
-
if (!hasTextChange) {
|
|
652
|
+
if (!hasTextChange && !final) {
|
|
653
653
|
return;
|
|
654
654
|
}
|
|
655
655
|
// Get the text content of the node
|
|
656
656
|
const textContent = node.textContent ?? "";
|
|
657
657
|
// Get the node ID
|
|
658
658
|
const nodeId = node.getAttribute("data-node-id");
|
|
659
|
-
console.log("textContentChanged",
|
|
659
|
+
console.log("textContentChanged", textContent, final);
|
|
660
660
|
// Send postMessage with the text change
|
|
661
661
|
sendPostMessage("textContentChanged", {
|
|
662
662
|
nodeId,
|
|
663
663
|
textContent,
|
|
664
|
+
final,
|
|
664
665
|
});
|
|
665
666
|
};
|
|
666
667
|
|
|
@@ -673,7 +674,7 @@ const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
|
|
|
673
674
|
if (pendingMutations.length > 0) {
|
|
674
675
|
const mutationsToProcess = [...pendingMutations];
|
|
675
676
|
pendingMutations = [];
|
|
676
|
-
handleTextChange(node, mutationsToProcess);
|
|
677
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
677
678
|
}
|
|
678
679
|
};
|
|
679
680
|
const scheduleProcess = () => {
|
|
@@ -773,6 +774,8 @@ const nodeText = (canvasName = "canvas") => {
|
|
|
773
774
|
}
|
|
774
775
|
blurInProgress = true;
|
|
775
776
|
const nodeToCleanup = editableNode;
|
|
777
|
+
// Send final textContentChanged message before cleanup
|
|
778
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
776
779
|
makeNodeNonEditable(nodeToCleanup);
|
|
777
780
|
disableCanvasTextMode(canvasName);
|
|
778
781
|
cleanup?.();
|
|
@@ -922,32 +925,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
922
925
|
return nodeTools;
|
|
923
926
|
};
|
|
924
927
|
|
|
925
|
-
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
926
|
-
function withRAFThrottle(func) {
|
|
927
|
-
let rafId = null;
|
|
928
|
-
let lastArgs = null;
|
|
929
|
-
const throttled = (...args) => {
|
|
930
|
-
lastArgs = args;
|
|
931
|
-
if (rafId === null) {
|
|
932
|
-
rafId = requestAnimationFrame(() => {
|
|
933
|
-
if (lastArgs) {
|
|
934
|
-
func(...lastArgs);
|
|
935
|
-
}
|
|
936
|
-
rafId = null;
|
|
937
|
-
lastArgs = null;
|
|
938
|
-
});
|
|
939
|
-
}
|
|
940
|
-
};
|
|
941
|
-
throttled.cleanup = () => {
|
|
942
|
-
if (rafId !== null) {
|
|
943
|
-
cancelAnimationFrame(rafId);
|
|
944
|
-
rafId = null;
|
|
945
|
-
lastArgs = null;
|
|
946
|
-
}
|
|
947
|
-
};
|
|
948
|
-
return throttled;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
928
|
const DEFAULT_WIDTH = 400;
|
|
952
929
|
const RESIZE_CONFIG = {
|
|
953
930
|
minWidth: 320,
|
|
@@ -982,14 +959,22 @@ const RESIZE_PRESETS = [
|
|
|
982
959
|
];
|
|
983
960
|
|
|
984
961
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
962
|
+
const handleMouseLeave = (event) => {
|
|
963
|
+
// Check if mouse is leaving the window/document
|
|
964
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
965
|
+
blurResize();
|
|
966
|
+
}
|
|
967
|
+
};
|
|
985
968
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
986
969
|
document.addEventListener("mousemove", handleResize);
|
|
987
970
|
document.addEventListener("mouseup", stopResize);
|
|
971
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
988
972
|
window.addEventListener("blur", blurResize);
|
|
989
973
|
return () => {
|
|
990
974
|
resizeHandle.removeEventListener("mousedown", startResize);
|
|
991
975
|
document.removeEventListener("mousemove", handleResize);
|
|
992
976
|
document.removeEventListener("mouseup", stopResize);
|
|
977
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
993
978
|
window.removeEventListener("blur", blurResize);
|
|
994
979
|
};
|
|
995
980
|
};
|
|
@@ -1049,7 +1034,7 @@ const updateWidth = (container, width) => {
|
|
|
1049
1034
|
updateActivePreset(container, width);
|
|
1050
1035
|
};
|
|
1051
1036
|
|
|
1052
|
-
const createViewport = (container) => {
|
|
1037
|
+
const createViewport = (container, initialWidth) => {
|
|
1053
1038
|
const canvas = getCanvasContainer();
|
|
1054
1039
|
// Remove any existing resize handle to prevent duplicates
|
|
1055
1040
|
const existingHandle = container.querySelector(".resize-handle");
|
|
@@ -1057,7 +1042,8 @@ const createViewport = (container) => {
|
|
|
1057
1042
|
existingHandle.remove();
|
|
1058
1043
|
}
|
|
1059
1044
|
const resizeHandle = createResizeHandle(container);
|
|
1060
|
-
|
|
1045
|
+
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
1046
|
+
container.style.setProperty("--container-width", `${width}px`);
|
|
1061
1047
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
1062
1048
|
let isDragging = false;
|
|
1063
1049
|
let startX = 0;
|
|
@@ -1078,7 +1064,6 @@ const createViewport = (container) => {
|
|
|
1078
1064
|
const width = calcWidth(event, startX, startWidth);
|
|
1079
1065
|
updateWidth(container, width);
|
|
1080
1066
|
};
|
|
1081
|
-
const throttledHandleResize = withRAFThrottle(handleResize);
|
|
1082
1067
|
const stopResize = (event) => {
|
|
1083
1068
|
event.preventDefault();
|
|
1084
1069
|
event.stopPropagation();
|
|
@@ -1088,12 +1073,14 @@ const createViewport = (container) => {
|
|
|
1088
1073
|
isDragging = false;
|
|
1089
1074
|
};
|
|
1090
1075
|
const blurResize = () => {
|
|
1076
|
+
if (canvas) {
|
|
1077
|
+
canvas.style.cursor = "default";
|
|
1078
|
+
}
|
|
1091
1079
|
isDragging = false;
|
|
1092
1080
|
};
|
|
1093
|
-
const removeListeners = setupEventListener(resizeHandle, startResize,
|
|
1081
|
+
const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
|
|
1094
1082
|
const cleanup = () => {
|
|
1095
1083
|
isDragging = false;
|
|
1096
|
-
throttledHandleResize?.cleanup();
|
|
1097
1084
|
removeListeners();
|
|
1098
1085
|
resizeHandle.remove();
|
|
1099
1086
|
};
|
|
@@ -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.1
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -649,24 +649,25 @@
|
|
|
649
649
|
return mutationObserver;
|
|
650
650
|
};
|
|
651
651
|
|
|
652
|
-
const handleTextChange = (node, mutations) => {
|
|
652
|
+
const handleTextChange = (node, mutations, final = false) => {
|
|
653
653
|
// Check if any mutation is a text content change
|
|
654
654
|
const hasTextChange = mutations.some((mutation) => {
|
|
655
655
|
return (mutation.type === "characterData" ||
|
|
656
656
|
(mutation.type === "childList" && (mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)));
|
|
657
657
|
});
|
|
658
|
-
if (!hasTextChange) {
|
|
658
|
+
if (!hasTextChange && !final) {
|
|
659
659
|
return;
|
|
660
660
|
}
|
|
661
661
|
// Get the text content of the node
|
|
662
662
|
const textContent = node.textContent ?? "";
|
|
663
663
|
// Get the node ID
|
|
664
664
|
const nodeId = node.getAttribute("data-node-id");
|
|
665
|
-
console.log("textContentChanged",
|
|
665
|
+
console.log("textContentChanged", textContent, final);
|
|
666
666
|
// Send postMessage with the text change
|
|
667
667
|
sendPostMessage("textContentChanged", {
|
|
668
668
|
nodeId,
|
|
669
669
|
textContent,
|
|
670
|
+
final,
|
|
670
671
|
});
|
|
671
672
|
};
|
|
672
673
|
|
|
@@ -679,7 +680,7 @@
|
|
|
679
680
|
if (pendingMutations.length > 0) {
|
|
680
681
|
const mutationsToProcess = [...pendingMutations];
|
|
681
682
|
pendingMutations = [];
|
|
682
|
-
handleTextChange(node, mutationsToProcess);
|
|
683
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
683
684
|
}
|
|
684
685
|
};
|
|
685
686
|
const scheduleProcess = () => {
|
|
@@ -779,6 +780,8 @@
|
|
|
779
780
|
}
|
|
780
781
|
blurInProgress = true;
|
|
781
782
|
const nodeToCleanup = editableNode;
|
|
783
|
+
// Send final textContentChanged message before cleanup
|
|
784
|
+
handleTextChange(nodeToCleanup, [], true);
|
|
782
785
|
makeNodeNonEditable(nodeToCleanup);
|
|
783
786
|
disableCanvasTextMode(canvasName);
|
|
784
787
|
cleanup?.();
|
|
@@ -928,32 +931,6 @@
|
|
|
928
931
|
return nodeTools;
|
|
929
932
|
};
|
|
930
933
|
|
|
931
|
-
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
932
|
-
function withRAFThrottle(func) {
|
|
933
|
-
let rafId = null;
|
|
934
|
-
let lastArgs = null;
|
|
935
|
-
const throttled = (...args) => {
|
|
936
|
-
lastArgs = args;
|
|
937
|
-
if (rafId === null) {
|
|
938
|
-
rafId = requestAnimationFrame(() => {
|
|
939
|
-
if (lastArgs) {
|
|
940
|
-
func(...lastArgs);
|
|
941
|
-
}
|
|
942
|
-
rafId = null;
|
|
943
|
-
lastArgs = null;
|
|
944
|
-
});
|
|
945
|
-
}
|
|
946
|
-
};
|
|
947
|
-
throttled.cleanup = () => {
|
|
948
|
-
if (rafId !== null) {
|
|
949
|
-
cancelAnimationFrame(rafId);
|
|
950
|
-
rafId = null;
|
|
951
|
-
lastArgs = null;
|
|
952
|
-
}
|
|
953
|
-
};
|
|
954
|
-
return throttled;
|
|
955
|
-
}
|
|
956
|
-
|
|
957
934
|
const DEFAULT_WIDTH = 400;
|
|
958
935
|
const RESIZE_CONFIG = {
|
|
959
936
|
minWidth: 320,
|
|
@@ -988,14 +965,22 @@
|
|
|
988
965
|
];
|
|
989
966
|
|
|
990
967
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
968
|
+
const handleMouseLeave = (event) => {
|
|
969
|
+
// Check if mouse is leaving the window/document
|
|
970
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
971
|
+
blurResize();
|
|
972
|
+
}
|
|
973
|
+
};
|
|
991
974
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
992
975
|
document.addEventListener("mousemove", handleResize);
|
|
993
976
|
document.addEventListener("mouseup", stopResize);
|
|
977
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
994
978
|
window.addEventListener("blur", blurResize);
|
|
995
979
|
return () => {
|
|
996
980
|
resizeHandle.removeEventListener("mousedown", startResize);
|
|
997
981
|
document.removeEventListener("mousemove", handleResize);
|
|
998
982
|
document.removeEventListener("mouseup", stopResize);
|
|
983
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
999
984
|
window.removeEventListener("blur", blurResize);
|
|
1000
985
|
};
|
|
1001
986
|
};
|
|
@@ -1055,7 +1040,7 @@
|
|
|
1055
1040
|
updateActivePreset(container, width);
|
|
1056
1041
|
};
|
|
1057
1042
|
|
|
1058
|
-
const createViewport = (container) => {
|
|
1043
|
+
const createViewport = (container, initialWidth) => {
|
|
1059
1044
|
const canvas = getCanvasContainer();
|
|
1060
1045
|
// Remove any existing resize handle to prevent duplicates
|
|
1061
1046
|
const existingHandle = container.querySelector(".resize-handle");
|
|
@@ -1063,7 +1048,8 @@
|
|
|
1063
1048
|
existingHandle.remove();
|
|
1064
1049
|
}
|
|
1065
1050
|
const resizeHandle = createResizeHandle(container);
|
|
1066
|
-
|
|
1051
|
+
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
1052
|
+
container.style.setProperty("--container-width", `${width}px`);
|
|
1067
1053
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
1068
1054
|
let isDragging = false;
|
|
1069
1055
|
let startX = 0;
|
|
@@ -1084,7 +1070,6 @@
|
|
|
1084
1070
|
const width = calcWidth(event, startX, startWidth);
|
|
1085
1071
|
updateWidth(container, width);
|
|
1086
1072
|
};
|
|
1087
|
-
const throttledHandleResize = withRAFThrottle(handleResize);
|
|
1088
1073
|
const stopResize = (event) => {
|
|
1089
1074
|
event.preventDefault();
|
|
1090
1075
|
event.stopPropagation();
|
|
@@ -1094,12 +1079,14 @@
|
|
|
1094
1079
|
isDragging = false;
|
|
1095
1080
|
};
|
|
1096
1081
|
const blurResize = () => {
|
|
1082
|
+
if (canvas) {
|
|
1083
|
+
canvas.style.cursor = "default";
|
|
1084
|
+
}
|
|
1097
1085
|
isDragging = false;
|
|
1098
1086
|
};
|
|
1099
|
-
const removeListeners = setupEventListener(resizeHandle, startResize,
|
|
1087
|
+
const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
|
|
1100
1088
|
const cleanup = () => {
|
|
1101
1089
|
isDragging = false;
|
|
1102
|
-
throttledHandleResize?.cleanup();
|
|
1103
1090
|
removeListeners();
|
|
1104
1091
|
resizeHandle.remove();
|
|
1105
1092
|
};
|
|
@@ -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()},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="canvas")=>{let r=[],i=null,s=null;const a=()=>{if(r.length>0){const t=[...r];r=[],((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??"",r=e.getAttribute("data-node-id");console.log("textContentChanged",r,o),n("textContentChanged",{nodeId:r,textContent:o})})(e,t)}},l=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,n=>{r.push(...n),null===i&&(i=requestAnimationFrame(()=>{s=requestAnimationFrame(()=>{a(),i=null,s=null})})),E(e,t,o)});return()=>{l.disconnect(),null!==i&&(cancelAnimationFrame(i),i=null),null!==s&&(cancelAnimationFrame(s),s=null),r=[]}},S=(e="canvas")=>{let n=null,o=!1,r=null;const i=()=>{if(o||!n)return;o=!0;var i;(i=n).contentEditable="false",i.classList.remove("is-editable"),i.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=x(e,t,o);return()=>{e.removeEventListener("blur",n),r(),i?.()}})(o,s,i,e))},blurEditMode:i,getEditableNode:()=>n,isEditing:()=>null!==n}};const C=320,k=1680,N=[{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"}],q=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(C,Math.min(k,n))})(n,(e.clientX-t)/o);return r},M=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<N.length&&(N[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=S(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",N.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,M);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=q(n,s,a);M(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=>{M(e,t)},cleanup:()=>{i=!1,l?.cleanup(),d(),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()},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 v=o();return v?v.appendChild(l):document.body.appendChild(l),l},v={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"},b=(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||v[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"),v=t(["zoom","current"],r)??1,b=g(e),{top:E,left:L,width:A,height:x}=b,S=Math.max(A,3),C=E+x;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",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&&(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",(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)`),v<=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??"",s=e.getAttribute("data-node-id");console.log("textContentChanged",r,o),n("textContentChanged",{nodeId:s,textContent:r,final:o})},S=(e,t,n="canvas")=>{let o=[],r=null,s=null;const i=()=>{null===r&&(r=requestAnimationFrame(()=>{s=requestAnimationFrame(()=>{(()=>{if(o.length>0){const t=[...o];o=[],x(e,t,!1)}})(),r=null,s=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),i(),E(e,t,n)});return()=>{a.disconnect(),null!==r&&(cancelAnimationFrame(r),r=null),null!==s&&(cancelAnimationFrame(s),s=null),o=[]}},C=(e="canvas")=>{let n=null,o=!1,r=null;const s=()=>{if(o||!n)return;o=!0;const s=n;var i;x(s,[],!0),(i=s).contentEditable="false",i.classList.remove("is-editable"),i.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=S(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",b(e,h,s,i),n?n.appendChild(h):document.body.appendChild(h)})(e),e&&s&&(L(e),L(e))}},v=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:()=>{v(),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,t)=>{const n=o(),r=e.querySelector(".resize-handle");r&&r.remove();const s=(e=>{const t=document.createElement("div");return t.className="resize-handle",e.appendChild(t),t})(e),i=t??400;e.style.setProperty("--container-width",`${i}px`),((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)})(s,e,P);let a=!1,d=0,l=0;const c=((e,t,n,o,r)=>{const s=e=>{e.relatedTarget||e.target!==document&&e.target!==document.documentElement||r()};return e.addEventListener("mousedown",t),document.addEventListener("mousemove",n),document.addEventListener("mouseup",o),document.addEventListener("mouseleave",s),window.addEventListener("blur",r),()=>{e.removeEventListener("mousedown",t),document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o),document.removeEventListener("mouseleave",s),window.removeEventListener("blur",r)}})(s,t=>{t.preventDefault(),t.stopPropagation(),a=!0,d=t.clientX,l=e.offsetWidth},t=>{if(!a)return;n&&(n.style.cursor="ew-resize");const o=M(t,d,l);P(e,o)},e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),a=!1},()=>{n&&(n.style.cursor="default"),a=!1});return{setWidth:t=>{P(e,t)},cleanup:()=>{a=!1,c(),s.remove()}}}});
|
package/package.json
CHANGED
|
@@ -16,7 +16,7 @@ export const setupMutationObserver = (
|
|
|
16
16
|
if (pendingMutations.length > 0) {
|
|
17
17
|
const mutationsToProcess = [...pendingMutations];
|
|
18
18
|
pendingMutations = [];
|
|
19
|
-
handleTextChange(node, mutationsToProcess);
|
|
19
|
+
handleTextChange(node, mutationsToProcess, false);
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
22
|
|
|
@@ -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,11 +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",
|
|
22
|
+
console.log("textContentChanged", textContent, final);
|
|
23
23
|
|
|
24
24
|
// Send postMessage with the text change
|
|
25
25
|
sendPostMessage("textContentChanged", {
|
|
26
26
|
nodeId,
|
|
27
27
|
textContent,
|
|
28
|
+
final,
|
|
28
29
|
});
|
|
29
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?.();
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { getCanvasContainer } from "../canvas/helpers/getCanvasContainer";
|
|
2
|
-
import { withRAFThrottle } from "../helpers";
|
|
3
2
|
import { DEFAULT_WIDTH } from "./constants";
|
|
4
3
|
import { setupEventListener } from "./events/setupEventListener";
|
|
5
4
|
import { createResizeHandle } from "./resize/createResizeHandle";
|
|
@@ -8,7 +7,7 @@ import type { Viewport } from "./types";
|
|
|
8
7
|
import { calcWidth } from "./width/calcWidth";
|
|
9
8
|
import { updateWidth } from "./width/updateWidth";
|
|
10
9
|
|
|
11
|
-
export const createViewport = (container: HTMLElement): Viewport => {
|
|
10
|
+
export const createViewport = (container: HTMLElement, initialWidth?: number): Viewport => {
|
|
12
11
|
const canvas: HTMLElement | null = getCanvasContainer();
|
|
13
12
|
|
|
14
13
|
// Remove any existing resize handle to prevent duplicates
|
|
@@ -18,7 +17,8 @@ export const createViewport = (container: HTMLElement): Viewport => {
|
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
const resizeHandle = createResizeHandle(container);
|
|
21
|
-
|
|
20
|
+
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
21
|
+
container.style.setProperty("--container-width", `${width}px`);
|
|
22
22
|
|
|
23
23
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
24
24
|
|
|
@@ -46,8 +46,6 @@ export const createViewport = (container: HTMLElement): Viewport => {
|
|
|
46
46
|
updateWidth(container, width);
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
const throttledHandleResize = withRAFThrottle(handleResize);
|
|
50
|
-
|
|
51
49
|
const stopResize = (event: MouseEvent): void => {
|
|
52
50
|
event.preventDefault();
|
|
53
51
|
event.stopPropagation();
|
|
@@ -60,14 +58,17 @@ export const createViewport = (container: HTMLElement): Viewport => {
|
|
|
60
58
|
};
|
|
61
59
|
|
|
62
60
|
const blurResize = (): void => {
|
|
61
|
+
if (canvas) {
|
|
62
|
+
canvas.style.cursor = "default";
|
|
63
|
+
}
|
|
64
|
+
|
|
63
65
|
isDragging = false;
|
|
64
66
|
};
|
|
65
67
|
|
|
66
|
-
const removeListeners = setupEventListener(resizeHandle, startResize,
|
|
68
|
+
const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
|
|
67
69
|
|
|
68
70
|
const cleanup = (): void => {
|
|
69
71
|
isDragging = false;
|
|
70
|
-
throttledHandleResize?.cleanup();
|
|
71
72
|
removeListeners();
|
|
72
73
|
resizeHandle.remove();
|
|
73
74
|
};
|
|
@@ -5,9 +5,17 @@ export const setupEventListener = (
|
|
|
5
5
|
stopResize: (event: MouseEvent) => void,
|
|
6
6
|
blurResize: () => void
|
|
7
7
|
): (() => void) => {
|
|
8
|
+
const handleMouseLeave = (event: MouseEvent): void => {
|
|
9
|
+
// Check if mouse is leaving the window/document
|
|
10
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
11
|
+
blurResize();
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
8
15
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
9
16
|
document.addEventListener("mousemove", handleResize);
|
|
10
17
|
document.addEventListener("mouseup", stopResize);
|
|
18
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
11
19
|
|
|
12
20
|
window.addEventListener("blur", blurResize);
|
|
13
21
|
|
|
@@ -15,6 +23,7 @@ export const setupEventListener = (
|
|
|
15
23
|
resizeHandle.removeEventListener("mousedown", startResize);
|
|
16
24
|
document.removeEventListener("mousemove", handleResize);
|
|
17
25
|
document.removeEventListener("mouseup", stopResize);
|
|
26
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
18
27
|
window.removeEventListener("blur", blurResize);
|
|
19
28
|
};
|
|
20
29
|
};
|