@node-edit-utils/core 2.3.2 → 2.3.4
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/canvas/helpers/getCanvasContainerOrBody.d.ts +1 -0
- package/dist/lib/canvas/helpers/getCanvasWindowValue.d.ts +1 -1
- package/dist/lib/helpers/adjustForZoom.d.ts +1 -0
- package/dist/lib/helpers/createDragHandler.d.ts +69 -0
- package/dist/lib/helpers/getNodeProvider.d.ts +1 -0
- package/dist/lib/helpers/getNodeTools.d.ts +2 -0
- package/dist/lib/helpers/getViewportDimensions.d.ts +4 -0
- package/dist/lib/helpers/index.d.ts +9 -1
- package/dist/lib/helpers/parseTransform.d.ts +8 -0
- package/dist/lib/helpers/toggleClass.d.ts +1 -0
- package/dist/lib/viewport/label/getViewportLabelOverlay.d.ts +1 -0
- package/dist/lib/viewport/label/helpers/selectFirstViewportNode.d.ts +5 -0
- package/dist/lib/viewport/label/index.d.ts +5 -3
- package/dist/lib/viewport/label/isViewportDragging.d.ts +2 -0
- package/dist/lib/viewport/label/refreshViewportLabel.d.ts +8 -0
- package/dist/lib/viewport/label/removeViewportLabel.d.ts +5 -0
- package/dist/lib/viewport/label/setupViewportDrag.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +342 -280
- package/dist/node-edit-utils.esm.js +342 -280
- package/dist/node-edit-utils.umd.js +342 -280
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +7 -2
- package/src/lib/canvas/createCanvasObserver.test.ts +242 -0
- package/src/lib/canvas/createCanvasObserver.ts +2 -2
- package/src/lib/canvas/disableCanvasKeyboard.test.ts +53 -0
- package/src/lib/canvas/disableCanvasKeyboard.ts +1 -1
- package/src/lib/canvas/disableCanvasTextMode.test.ts +53 -0
- package/src/lib/canvas/disableCanvasTextMode.ts +1 -1
- package/src/lib/canvas/enableCanvasKeyboard.test.ts +53 -0
- package/src/lib/canvas/enableCanvasKeyboard.ts +1 -1
- package/src/lib/canvas/enableCanvasTextMode.test.ts +53 -0
- package/src/lib/canvas/enableCanvasTextMode.ts +1 -1
- package/src/lib/canvas/helpers/applyCanvasState.test.ts +119 -0
- package/src/lib/canvas/helpers/applyCanvasState.ts +1 -1
- package/src/lib/canvas/helpers/getCanvasContainer.test.ts +62 -0
- package/src/lib/canvas/helpers/getCanvasContainerOrBody.test.ts +51 -0
- package/src/lib/canvas/helpers/getCanvasContainerOrBody.ts +6 -0
- package/src/lib/canvas/helpers/getCanvasWindowValue.test.ts +116 -0
- package/src/lib/canvas/helpers/getCanvasWindowValue.ts +2 -3
- package/src/lib/helpers/adjustForZoom.test.ts +65 -0
- package/src/lib/helpers/adjustForZoom.ts +4 -0
- package/src/lib/helpers/createDragHandler.test.ts +325 -0
- package/src/lib/helpers/createDragHandler.ts +171 -0
- package/src/lib/helpers/getNodeProvider.test.ts +71 -0
- package/src/lib/helpers/getNodeProvider.ts +4 -0
- package/src/lib/helpers/getNodeTools.test.ts +50 -0
- package/src/lib/helpers/getNodeTools.ts +6 -0
- package/src/lib/helpers/getViewportDimensions.test.ts +93 -0
- package/src/lib/helpers/getViewportDimensions.ts +7 -0
- package/src/lib/helpers/index.ts +9 -1
- package/src/lib/helpers/observer/connectMutationObserver.test.ts +127 -0
- package/src/lib/helpers/observer/connectResizeObserver.test.ts +147 -0
- package/src/lib/helpers/parseTransform.test.ts +117 -0
- package/src/lib/helpers/parseTransform.ts +9 -0
- package/src/lib/helpers/toggleClass.test.ts +71 -0
- package/src/lib/helpers/toggleClass.ts +9 -0
- package/src/lib/helpers/withRAF.test.ts +439 -0
- package/src/lib/node-tools/createNodeTools.test.ts +373 -0
- package/src/lib/node-tools/createNodeTools.ts +0 -1
- package/src/lib/node-tools/events/click/handleNodeClick.test.ts +109 -0
- package/src/lib/node-tools/events/setupEventListener.test.ts +136 -0
- package/src/lib/node-tools/highlight/clearHighlightFrame.test.ts +88 -0
- package/src/lib/node-tools/highlight/clearHighlightFrame.ts +2 -3
- package/src/lib/node-tools/highlight/createCornerHandles.test.ts +150 -0
- package/src/lib/node-tools/highlight/createHighlightFrame.test.ts +237 -0
- package/src/lib/node-tools/highlight/createHighlightFrame.ts +5 -9
- package/src/lib/node-tools/highlight/createTagLabel.test.ts +135 -0
- package/src/lib/node-tools/highlight/createToolsContainer.test.ts +97 -0
- package/src/lib/node-tools/highlight/createToolsContainer.ts +3 -6
- package/src/lib/node-tools/highlight/helpers/getElementBounds.test.ts +158 -0
- package/src/lib/node-tools/highlight/helpers/getElementBounds.ts +6 -5
- package/src/lib/node-tools/highlight/helpers/getHighlightFrameElement.test.ts +78 -0
- package/src/lib/node-tools/highlight/helpers/getHighlightFrameElement.ts +2 -3
- package/src/lib/node-tools/highlight/helpers/getScreenBounds.test.ts +133 -0
- package/src/lib/node-tools/highlight/highlightNode.test.ts +213 -0
- package/src/lib/node-tools/highlight/highlightNode.ts +7 -15
- package/src/lib/node-tools/highlight/refreshHighlightFrame.test.ts +323 -0
- package/src/lib/node-tools/highlight/refreshHighlightFrame.ts +12 -42
- package/src/lib/node-tools/highlight/updateHighlightFrameVisibility.test.ts +110 -0
- package/src/lib/node-tools/highlight/updateHighlightFrameVisibility.ts +2 -3
- package/src/lib/node-tools/select/helpers/getElementsFromPoint.test.ts +109 -0
- package/src/lib/node-tools/select/helpers/isInsideComponent.test.ts +81 -0
- package/src/lib/node-tools/select/helpers/isInsideViewport.test.ts +82 -0
- package/src/lib/node-tools/select/helpers/targetSameCandidates.test.ts +81 -0
- package/src/lib/node-tools/select/selectNode.test.ts +238 -0
- package/src/lib/node-tools/text/events/setupKeydownHandler.test.ts +91 -0
- package/src/lib/node-tools/text/events/setupMutationObserver.test.ts +213 -0
- package/src/lib/node-tools/text/events/setupNodeListeners.test.ts +133 -0
- package/src/lib/node-tools/text/helpers/enterTextEditMode.test.ts +50 -0
- package/src/lib/node-tools/text/helpers/handleTextChange.test.ts +201 -0
- package/src/lib/node-tools/text/helpers/hasTextContent.test.ts +101 -0
- package/src/lib/node-tools/text/helpers/insertLineBreak.test.ts +96 -0
- package/src/lib/node-tools/text/helpers/makeNodeEditable.test.ts +56 -0
- package/src/lib/node-tools/text/helpers/makeNodeNonEditable.test.ts +57 -0
- package/src/lib/node-tools/text/helpers/shouldEnterTextEditMode.test.ts +61 -0
- package/src/lib/node-tools/text/nodeText.test.ts +233 -0
- package/src/lib/post-message/processPostMessage.test.ts +218 -0
- package/src/lib/post-message/sendPostMessage.test.ts +120 -0
- package/src/lib/styles/styles.css +3 -3
- package/src/lib/viewport/createViewport.test.ts +267 -0
- package/src/lib/viewport/createViewport.ts +51 -51
- package/src/lib/viewport/events/setupEventListener.test.ts +103 -0
- package/src/lib/viewport/label/getViewportLabelOverlay.test.ts +77 -0
- package/src/lib/viewport/label/{getViewportLabelsOverlay.ts → getViewportLabelOverlay.ts} +6 -6
- package/src/lib/viewport/label/helpers/getLabelPosition.test.ts +51 -0
- package/src/lib/viewport/label/helpers/getLabelPosition.ts +3 -5
- package/src/lib/viewport/label/helpers/getTransformValues.test.ts +59 -0
- package/src/lib/viewport/label/helpers/getTransformValues.ts +3 -5
- package/src/lib/viewport/label/helpers/getZoomValue.test.ts +53 -0
- package/src/lib/viewport/label/helpers/selectFirstViewportNode.test.ts +105 -0
- package/src/lib/viewport/label/helpers/selectFirstViewportNode.ts +26 -0
- package/src/lib/viewport/label/index.ts +5 -3
- package/src/lib/viewport/label/isViewportDragging.test.ts +35 -0
- package/src/lib/viewport/label/isViewportDragging.ts +9 -0
- package/src/lib/viewport/label/refreshViewportLabel.test.ts +105 -0
- package/src/lib/viewport/label/refreshViewportLabel.ts +50 -0
- package/src/lib/viewport/label/refreshViewportLabels.test.ts +107 -0
- package/src/lib/viewport/label/refreshViewportLabels.ts +19 -52
- package/src/lib/viewport/label/removeViewportLabel.test.ts +67 -0
- package/src/lib/viewport/label/removeViewportLabel.ts +20 -0
- package/src/lib/viewport/label/setupViewportDrag.test.ts +249 -0
- package/src/lib/viewport/label/setupViewportDrag.ts +70 -0
- package/src/lib/viewport/resize/createResizeHandle.test.ts +37 -0
- package/src/lib/viewport/resize/createResizePresets.test.ts +75 -0
- package/src/lib/viewport/resize/updateActivePreset.test.ts +92 -0
- package/src/lib/viewport/width/calcConstrainedWidth.test.ts +47 -0
- package/src/lib/viewport/width/calcWidth.test.ts +68 -0
- package/src/lib/viewport/width/updateWidth.test.ts +78 -0
- package/src/lib/window/bindToWindow.test.ts +166 -0
- package/src/lib/window/bindToWindow.ts +1 -2
- package/dist/lib/viewport/label/getViewportLabelsOverlay.d.ts +0 -1
- package/dist/lib/viewport/label/isViewportLabelDragging.d.ts +0 -2
- package/dist/lib/viewport/label/setupViewportLabelDrag.d.ts +0 -1
- package/src/lib/viewport/label/isViewportLabelDragging.ts +0 -9
- package/src/lib/viewport/label/setupViewportLabelDrag.ts +0 -98
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.3.
|
|
4
|
+
* @version 2.3.4
|
|
5
5
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const getNodeTools = () => {
|
|
7
|
+
return window.nodeTools;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
const getViewportDimensions = () => {
|
|
8
11
|
return {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
width: rect.width,
|
|
12
|
-
height: rect.height,
|
|
12
|
+
width: document.documentElement.clientWidth || window.innerWidth,
|
|
13
|
+
height: document.documentElement.clientHeight || window.innerHeight,
|
|
13
14
|
};
|
|
14
|
-
}
|
|
15
|
+
};
|
|
15
16
|
|
|
16
17
|
const getCanvasContainer = () => {
|
|
17
18
|
return document.querySelector(".canvas-container");
|
|
18
19
|
};
|
|
19
20
|
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
const getCanvasContainerOrBody = () => {
|
|
22
|
+
return getCanvasContainer() || document.body;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const getViewportLabelOverlay = () => {
|
|
26
|
+
const container = getCanvasContainerOrBody();
|
|
23
27
|
// Check if overlay already exists
|
|
24
28
|
let overlay = container.querySelector(".viewport-labels-overlay");
|
|
25
29
|
if (!overlay) {
|
|
@@ -34,8 +38,7 @@ const getViewportLabelsOverlay = () => {
|
|
|
34
38
|
overlay.style.height = "100vh";
|
|
35
39
|
overlay.style.pointerEvents = "none";
|
|
36
40
|
overlay.style.zIndex = "500";
|
|
37
|
-
const viewportWidth
|
|
38
|
-
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
41
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
39
42
|
overlay.setAttribute("width", viewportWidth.toString());
|
|
40
43
|
overlay.setAttribute("height", viewportHeight.toString());
|
|
41
44
|
container.appendChild(overlay);
|
|
@@ -45,11 +48,112 @@ const getViewportLabelsOverlay = () => {
|
|
|
45
48
|
|
|
46
49
|
// Global flag to prevent refreshViewportLabels during drag
|
|
47
50
|
let globalIsDragging = false;
|
|
48
|
-
const
|
|
49
|
-
const
|
|
51
|
+
const isViewportDragging = () => globalIsDragging;
|
|
52
|
+
const setViewportDragging = (isDragging) => {
|
|
50
53
|
globalIsDragging = isDragging;
|
|
51
54
|
};
|
|
52
55
|
|
|
56
|
+
function getScreenBounds(element) {
|
|
57
|
+
const rect = element.getBoundingClientRect();
|
|
58
|
+
return {
|
|
59
|
+
top: rect.top,
|
|
60
|
+
left: rect.left,
|
|
61
|
+
width: rect.width,
|
|
62
|
+
height: rect.height,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Creates a reusable drag handler for mouse drag operations
|
|
68
|
+
* @param element - Element that triggers the drag (mousedown listener attached here)
|
|
69
|
+
* @param callbacks - Callbacks for drag lifecycle events
|
|
70
|
+
* @param options - Configuration options
|
|
71
|
+
* @returns Cleanup function to remove all event listeners
|
|
72
|
+
*/
|
|
73
|
+
function createDragHandler(element, callbacks, options = {}) {
|
|
74
|
+
const { preventDefault = true, stopPropagation = true } = options;
|
|
75
|
+
const state = {
|
|
76
|
+
isDragging: false,
|
|
77
|
+
hasDragged: false,
|
|
78
|
+
startX: 0,
|
|
79
|
+
startY: 0,
|
|
80
|
+
};
|
|
81
|
+
const startDrag = (event) => {
|
|
82
|
+
if (preventDefault) {
|
|
83
|
+
event.preventDefault();
|
|
84
|
+
}
|
|
85
|
+
if (stopPropagation) {
|
|
86
|
+
event.stopPropagation();
|
|
87
|
+
}
|
|
88
|
+
state.isDragging = true;
|
|
89
|
+
state.hasDragged = false;
|
|
90
|
+
state.startX = event.clientX;
|
|
91
|
+
state.startY = event.clientY;
|
|
92
|
+
callbacks.onStart?.(event, state);
|
|
93
|
+
};
|
|
94
|
+
const handleDrag = (event) => {
|
|
95
|
+
if (!state.isDragging)
|
|
96
|
+
return;
|
|
97
|
+
const deltaX = event.clientX - state.startX;
|
|
98
|
+
const deltaY = event.clientY - state.startY;
|
|
99
|
+
state.hasDragged = true;
|
|
100
|
+
callbacks.onDrag(event, {
|
|
101
|
+
...state,
|
|
102
|
+
deltaX,
|
|
103
|
+
deltaY,
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const stopDrag = (event) => {
|
|
107
|
+
if (!state.isDragging)
|
|
108
|
+
return;
|
|
109
|
+
if (preventDefault) {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
}
|
|
112
|
+
if (stopPropagation) {
|
|
113
|
+
event.stopPropagation();
|
|
114
|
+
}
|
|
115
|
+
state.isDragging = false;
|
|
116
|
+
callbacks.onStop?.(event, state);
|
|
117
|
+
};
|
|
118
|
+
const cancelDrag = () => {
|
|
119
|
+
if (!state.isDragging)
|
|
120
|
+
return;
|
|
121
|
+
state.isDragging = false;
|
|
122
|
+
callbacks.onCancel?.(state);
|
|
123
|
+
};
|
|
124
|
+
const preventClick = (event) => {
|
|
125
|
+
if (preventDefault) {
|
|
126
|
+
event.preventDefault();
|
|
127
|
+
}
|
|
128
|
+
if (stopPropagation) {
|
|
129
|
+
event.stopPropagation();
|
|
130
|
+
}
|
|
131
|
+
callbacks.onPreventClick?.(event, state);
|
|
132
|
+
// Reset hasDragged flag after handling the click
|
|
133
|
+
if (state.hasDragged) {
|
|
134
|
+
state.hasDragged = false;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
// Attach event listeners
|
|
138
|
+
element.addEventListener("mousedown", startDrag);
|
|
139
|
+
if (callbacks.onPreventClick) {
|
|
140
|
+
element.addEventListener("click", preventClick);
|
|
141
|
+
}
|
|
142
|
+
document.addEventListener("mousemove", handleDrag);
|
|
143
|
+
document.addEventListener("mouseup", stopDrag);
|
|
144
|
+
window.addEventListener("blur", cancelDrag);
|
|
145
|
+
// Return cleanup function
|
|
146
|
+
return () => {
|
|
147
|
+
element.removeEventListener("mousedown", startDrag);
|
|
148
|
+
if (callbacks.onPreventClick) {
|
|
149
|
+
element.removeEventListener("click", preventClick);
|
|
150
|
+
}
|
|
151
|
+
document.removeEventListener("mousemove", handleDrag);
|
|
152
|
+
document.removeEventListener("mouseup", stopDrag);
|
|
153
|
+
window.removeEventListener("blur", cancelDrag);
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
53
157
|
function sendPostMessage(action, data) {
|
|
54
158
|
window.parent.postMessage({
|
|
55
159
|
source: "node-edit-utils",
|
|
@@ -59,22 +163,23 @@ function sendPostMessage(action, data) {
|
|
|
59
163
|
}, "*");
|
|
60
164
|
}
|
|
61
165
|
|
|
166
|
+
const parseTransform3d = (transform) => {
|
|
167
|
+
const match = transform.match(/translate3d\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/);
|
|
168
|
+
return match ? { x: parseFloat(match[1]), y: parseFloat(match[2]) } : { x: 0, y: 0 };
|
|
169
|
+
};
|
|
170
|
+
const parseTransform2d = (transform) => {
|
|
171
|
+
const match = transform?.match(/translate\((-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\)/);
|
|
172
|
+
return match ? { x: parseFloat(match[1]), y: parseFloat(match[2]) } : { x: 0, y: 0 };
|
|
173
|
+
};
|
|
174
|
+
|
|
62
175
|
const getLabelPosition = (labelGroup) => {
|
|
63
176
|
const transform = labelGroup.getAttribute("transform");
|
|
64
|
-
|
|
65
|
-
if (match) {
|
|
66
|
-
return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
|
|
67
|
-
}
|
|
68
|
-
return { x: 0, y: 0 };
|
|
177
|
+
return parseTransform2d(transform);
|
|
69
178
|
};
|
|
70
179
|
|
|
71
180
|
const getTransformValues = (element) => {
|
|
72
181
|
const style = element.style.transform;
|
|
73
|
-
|
|
74
|
-
if (match) {
|
|
75
|
-
return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
|
|
76
|
-
}
|
|
77
|
-
return { x: 0, y: 0 };
|
|
182
|
+
return parseTransform3d(style);
|
|
78
183
|
};
|
|
79
184
|
|
|
80
185
|
const getZoomValue = () => {
|
|
@@ -82,119 +187,100 @@ const getZoomValue = () => {
|
|
|
82
187
|
return zoomValue ? parseFloat(zoomValue) : 1;
|
|
83
188
|
};
|
|
84
189
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
190
|
+
/**
|
|
191
|
+
* Selects the first child node inside a viewport element.
|
|
192
|
+
* Skips the resize-handle element if present.
|
|
193
|
+
*/
|
|
194
|
+
const selectFirstViewportNode = (viewportElement) => {
|
|
195
|
+
const firstChild = Array.from(viewportElement.children).find((child) => !child.classList.contains("resize-handle"));
|
|
196
|
+
if (firstChild) {
|
|
197
|
+
const nodeTools = getNodeTools();
|
|
198
|
+
if (nodeTools?.selectNode) {
|
|
199
|
+
const wasAlreadySelected = nodeTools.getSelectedNode() === firstChild;
|
|
200
|
+
nodeTools.selectNode(firstChild);
|
|
201
|
+
// Always emit postMessage when selecting via viewport label click,
|
|
202
|
+
// even if the node was already selected (to match behavior of direct node clicks)
|
|
203
|
+
if (wasAlreadySelected) {
|
|
204
|
+
sendPostMessage("selectedNodeChanged", firstChild.getAttribute("data-node-id") ?? null);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
const setupViewportDrag = (labelElement, viewportElement, viewportName) => {
|
|
91
211
|
// Get the parent group element that contains the label
|
|
92
212
|
const labelGroup = labelElement.parentElement;
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
});
|
|
142
|
-
};
|
|
143
|
-
const cancelDrag = () => {
|
|
144
|
-
isDragging = false;
|
|
145
|
-
setViewportLabelDragging(false);
|
|
146
|
-
};
|
|
147
|
-
// Attach event listeners
|
|
148
|
-
labelElement.addEventListener("mousedown", startDrag);
|
|
149
|
-
document.addEventListener("mousemove", handleDrag);
|
|
150
|
-
document.addEventListener("mouseup", stopDrag);
|
|
151
|
-
window.addEventListener("blur", cancelDrag);
|
|
152
|
-
// Return cleanup function
|
|
153
|
-
return () => {
|
|
154
|
-
labelElement.removeEventListener("mousedown", startDrag);
|
|
155
|
-
document.removeEventListener("mousemove", handleDrag);
|
|
156
|
-
document.removeEventListener("mouseup", stopDrag);
|
|
157
|
-
window.removeEventListener("blur", cancelDrag);
|
|
158
|
-
};
|
|
213
|
+
// Track initial positions for calculations
|
|
214
|
+
let initialTransform = { x: 0, y: 0 };
|
|
215
|
+
let initialLabelPosition = { x: 0, y: 0 };
|
|
216
|
+
return createDragHandler(labelElement, {
|
|
217
|
+
onStart: () => {
|
|
218
|
+
setViewportDragging(true);
|
|
219
|
+
initialTransform = getTransformValues(viewportElement);
|
|
220
|
+
initialLabelPosition = getLabelPosition(labelGroup);
|
|
221
|
+
selectFirstViewportNode(viewportElement);
|
|
222
|
+
},
|
|
223
|
+
onDrag: (_event, { deltaX, deltaY }) => {
|
|
224
|
+
const zoom = getZoomValue();
|
|
225
|
+
// Adjust delta for zoom level (viewport is in canvas space)
|
|
226
|
+
const deltaXZoomed = deltaX / zoom;
|
|
227
|
+
const deltaYZoomed = deltaY / zoom;
|
|
228
|
+
// Calculate new positions
|
|
229
|
+
const newX = initialTransform.x + deltaXZoomed;
|
|
230
|
+
const newY = initialTransform.y + deltaYZoomed;
|
|
231
|
+
// Update label position with raw delta (labels are in screen space)
|
|
232
|
+
const newLabelX = initialLabelPosition.x + deltaX;
|
|
233
|
+
const newLabelY = initialLabelPosition.y + deltaY;
|
|
234
|
+
labelGroup.setAttribute("transform", `translate(${newLabelX}, ${newLabelY})`);
|
|
235
|
+
// Update viewport position with zoom-adjusted delta
|
|
236
|
+
viewportElement.style.transform = `translate3d(${newX}px, ${newY}px, 0)`;
|
|
237
|
+
},
|
|
238
|
+
onStop: (_event, { hasDragged }) => {
|
|
239
|
+
setViewportDragging(false);
|
|
240
|
+
const finalTransform = getTransformValues(viewportElement);
|
|
241
|
+
// If it was a drag, handle drag completion
|
|
242
|
+
if (hasDragged) {
|
|
243
|
+
// Trigger refresh after drag completes to update highlight frame and labels
|
|
244
|
+
const nodeTools = getNodeTools();
|
|
245
|
+
if (nodeTools?.refreshHighlightFrame) {
|
|
246
|
+
nodeTools.refreshHighlightFrame();
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Always notify parent about the new position on drag stop
|
|
250
|
+
sendPostMessage("viewport-position-changed", {
|
|
251
|
+
viewportName,
|
|
252
|
+
x: finalTransform.x,
|
|
253
|
+
y: finalTransform.y,
|
|
254
|
+
});
|
|
255
|
+
},
|
|
256
|
+
onCancel: () => {
|
|
257
|
+
setViewportDragging(false);
|
|
258
|
+
},
|
|
259
|
+
onPreventClick: () => { },
|
|
260
|
+
});
|
|
159
261
|
};
|
|
160
262
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
263
|
+
/**
|
|
264
|
+
* Refreshes (updates) a viewport label for a single viewport element.
|
|
265
|
+
* Creates the label if it doesn't exist, or updates its position if it does.
|
|
266
|
+
* Similar to refreshHighlightFrame - updates existing elements rather than recreating.
|
|
267
|
+
*
|
|
268
|
+
* @param viewportElement - The viewport element to refresh the label for
|
|
269
|
+
*/
|
|
270
|
+
const refreshViewportLabel = (viewportElement) => {
|
|
271
|
+
const viewportName = viewportElement.getAttribute("data-viewport-name");
|
|
272
|
+
if (!viewportName) {
|
|
166
273
|
return;
|
|
167
274
|
}
|
|
168
|
-
const overlay =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
overlay.setAttribute("height", viewportHeight.toString());
|
|
174
|
-
// Find all viewports with names
|
|
175
|
-
const viewports = document.querySelectorAll(".viewport[data-viewport-name]");
|
|
176
|
-
// Clean up existing drag listeners
|
|
177
|
-
dragCleanupFunctions.forEach((cleanup) => {
|
|
178
|
-
cleanup();
|
|
179
|
-
});
|
|
180
|
-
dragCleanupFunctions.clear();
|
|
181
|
-
// Remove existing label groups
|
|
182
|
-
const existingGroups = overlay.querySelectorAll(".viewport-label-group");
|
|
183
|
-
existingGroups.forEach((group) => {
|
|
184
|
-
group.remove();
|
|
185
|
-
});
|
|
186
|
-
// Create/update labels for each viewport
|
|
187
|
-
viewports.forEach((viewport) => {
|
|
188
|
-
const viewportElement = viewport;
|
|
189
|
-
const viewportName = viewportElement.getAttribute("data-viewport-name");
|
|
190
|
-
if (!viewportName)
|
|
191
|
-
return;
|
|
192
|
-
const bounds = getScreenBounds(viewportElement);
|
|
275
|
+
const overlay = getViewportLabelOverlay();
|
|
276
|
+
const bounds = getScreenBounds(viewportElement);
|
|
277
|
+
// Get existing label group or create if it doesn't exist
|
|
278
|
+
let group = overlay.querySelector(`[data-viewport-name="${viewportName}"]`);
|
|
279
|
+
if (!group) {
|
|
193
280
|
// Create group for this viewport label
|
|
194
|
-
|
|
281
|
+
group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
195
282
|
group.classList.add("viewport-label-group");
|
|
196
283
|
group.setAttribute("data-viewport-name", viewportName);
|
|
197
|
-
group.setAttribute("transform", `translate(${bounds.left}, ${bounds.top})`);
|
|
198
284
|
// Create text element
|
|
199
285
|
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
200
286
|
text.classList.add("viewport-label-text");
|
|
@@ -205,14 +291,42 @@ const refreshViewportLabels = () => {
|
|
|
205
291
|
text.textContent = viewportName;
|
|
206
292
|
group.appendChild(text);
|
|
207
293
|
overlay.appendChild(group);
|
|
208
|
-
// Setup drag functionality
|
|
209
|
-
|
|
210
|
-
|
|
294
|
+
// Setup drag functionality only when creating new label
|
|
295
|
+
setupViewportDrag(text, viewportElement, viewportName);
|
|
296
|
+
}
|
|
297
|
+
// Update label position (this is the refresh part - updates existing label)
|
|
298
|
+
group.setAttribute("transform", `translate(${bounds.left}, ${bounds.top})`);
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
const refreshViewportLabels = () => {
|
|
302
|
+
// Skip refresh if a viewport label is being dragged
|
|
303
|
+
if (isViewportDragging()) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
const overlay = getViewportLabelOverlay();
|
|
307
|
+
// Update SVG dimensions to match current viewport (handles window resize and ensures coordinate system is correct)
|
|
308
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
309
|
+
overlay.setAttribute("width", viewportWidth.toString());
|
|
310
|
+
overlay.setAttribute("height", viewportHeight.toString());
|
|
311
|
+
// Find all viewports with names and refresh each label
|
|
312
|
+
const viewports = document.querySelectorAll(".viewport[data-viewport-name]");
|
|
313
|
+
viewports.forEach((viewport) => {
|
|
314
|
+
refreshViewportLabel(viewport);
|
|
315
|
+
});
|
|
316
|
+
// Remove labels for viewports that no longer exist
|
|
317
|
+
const existingGroups = overlay.querySelectorAll(".viewport-label-group");
|
|
318
|
+
existingGroups.forEach((group) => {
|
|
319
|
+
const viewportName = group.getAttribute("data-viewport-name");
|
|
320
|
+
if (viewportName) {
|
|
321
|
+
const viewportExists = Array.from(viewports).some((viewport) => viewport.getAttribute("data-viewport-name") === viewportName);
|
|
322
|
+
if (!viewportExists) {
|
|
323
|
+
group.remove();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
211
326
|
});
|
|
212
327
|
};
|
|
213
328
|
|
|
214
329
|
const getCanvasWindowValue = (path, canvasName = "canvas") => {
|
|
215
|
-
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
216
330
|
const canvas = window[canvasName];
|
|
217
331
|
return path.reduce((obj, prop) => obj?.[prop], canvas);
|
|
218
332
|
};
|
|
@@ -235,8 +349,7 @@ function createCanvasObserver(canvasName = "canvas") {
|
|
|
235
349
|
const observer = new MutationObserver(() => {
|
|
236
350
|
applyCanvasState(canvasName);
|
|
237
351
|
// Refresh highlight frame (throttled via withRAFThrottle)
|
|
238
|
-
|
|
239
|
-
const nodeTools = window.nodeTools;
|
|
352
|
+
const nodeTools = getNodeTools();
|
|
240
353
|
if (nodeTools?.refreshHighlightFrame) {
|
|
241
354
|
nodeTools.refreshHighlightFrame();
|
|
242
355
|
}
|
|
@@ -275,7 +388,6 @@ const connectResizeObserver = (element, handler) => {
|
|
|
275
388
|
|
|
276
389
|
const bindToWindow = (key, value) => {
|
|
277
390
|
if (typeof window !== "undefined") {
|
|
278
|
-
// biome-ignore lint/suspicious/noExplicitAny: global window extension requires flexibility
|
|
279
391
|
window[key] = value;
|
|
280
392
|
}
|
|
281
393
|
};
|
|
@@ -301,8 +413,7 @@ const processPostMessage = (event, onNodeSelected) => {
|
|
|
301
413
|
};
|
|
302
414
|
|
|
303
415
|
const clearHighlightFrame = () => {
|
|
304
|
-
const
|
|
305
|
-
const container = canvasContainer || document.body;
|
|
416
|
+
const container = getCanvasContainerOrBody();
|
|
306
417
|
const frame = container.querySelector(".highlight-frame-overlay");
|
|
307
418
|
if (frame) {
|
|
308
419
|
frame.remove();
|
|
@@ -431,7 +542,7 @@ const handleNodeClick = (event, nodeProvider, text, onNodeSelected) => {
|
|
|
431
542
|
onNodeSelected(selectedNode);
|
|
432
543
|
};
|
|
433
544
|
|
|
434
|
-
const setupEventListener
|
|
545
|
+
const setupEventListener = (nodeProvider, onNodeSelected, onEscapePressed, text) => {
|
|
435
546
|
const messageHandler = (event) => {
|
|
436
547
|
processPostMessage(event, onNodeSelected);
|
|
437
548
|
};
|
|
@@ -455,6 +566,17 @@ const setupEventListener$1 = (nodeProvider, onNodeSelected, onEscapePressed, tex
|
|
|
455
566
|
};
|
|
456
567
|
};
|
|
457
568
|
|
|
569
|
+
const toggleClass = (element, className, condition) => {
|
|
570
|
+
if (!element)
|
|
571
|
+
return;
|
|
572
|
+
if (condition) {
|
|
573
|
+
element.classList.add(className);
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
element.classList.remove(className);
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
|
|
458
580
|
const isComponentInstance = (element) => {
|
|
459
581
|
return element.getAttribute("data-instance") === "true";
|
|
460
582
|
};
|
|
@@ -520,8 +642,7 @@ const createHighlightFrame = (node, isInstance = false, isTextEdit = false) => {
|
|
|
520
642
|
svg.style.height = "100vh";
|
|
521
643
|
svg.style.pointerEvents = "none";
|
|
522
644
|
svg.style.zIndex = "500";
|
|
523
|
-
const viewportWidth
|
|
524
|
-
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
645
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
525
646
|
svg.setAttribute("width", viewportWidth.toString());
|
|
526
647
|
svg.setAttribute("height", viewportHeight.toString());
|
|
527
648
|
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
@@ -544,13 +665,8 @@ const createHighlightFrame = (node, isInstance = false, isTextEdit = false) => {
|
|
|
544
665
|
group.appendChild(rect);
|
|
545
666
|
createCornerHandles(group, minWidth, height, isInstance, isTextEdit);
|
|
546
667
|
svg.appendChild(group);
|
|
547
|
-
const
|
|
548
|
-
|
|
549
|
-
canvasContainer.appendChild(svg);
|
|
550
|
-
}
|
|
551
|
-
else {
|
|
552
|
-
document.body.appendChild(svg);
|
|
553
|
-
}
|
|
668
|
+
const container = getCanvasContainerOrBody();
|
|
669
|
+
container.appendChild(svg);
|
|
554
670
|
return svg;
|
|
555
671
|
};
|
|
556
672
|
|
|
@@ -587,19 +703,14 @@ const createTagLabel = (node, nodeTools) => {
|
|
|
587
703
|
const createToolsContainer = (node, highlightFrame, isInstance = false, isTextEdit = false) => {
|
|
588
704
|
const nodeTools = document.createElement("div");
|
|
589
705
|
nodeTools.className = "node-tools";
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
}
|
|
593
|
-
if (isTextEdit) {
|
|
594
|
-
nodeTools.classList.add("is-text-edit");
|
|
595
|
-
}
|
|
706
|
+
toggleClass(nodeTools, "is-instance", isInstance);
|
|
707
|
+
toggleClass(nodeTools, "is-text-edit", isTextEdit);
|
|
596
708
|
highlightFrame.appendChild(nodeTools);
|
|
597
709
|
createTagLabel(node, nodeTools);
|
|
598
710
|
};
|
|
599
711
|
|
|
600
712
|
function getHighlightFrameElement() {
|
|
601
|
-
const
|
|
602
|
-
const container = canvasContainer || document.body;
|
|
713
|
+
const container = getCanvasContainerOrBody();
|
|
603
714
|
return container.querySelector(".highlight-frame-overlay");
|
|
604
715
|
}
|
|
605
716
|
|
|
@@ -607,8 +718,8 @@ const highlightNode = (node) => {
|
|
|
607
718
|
if (!node)
|
|
608
719
|
return;
|
|
609
720
|
const existingHighlightFrame = getHighlightFrameElement();
|
|
610
|
-
const
|
|
611
|
-
const existingToolsWrapper =
|
|
721
|
+
const container = getCanvasContainerOrBody();
|
|
722
|
+
const existingToolsWrapper = container.querySelector(".highlight-frame-tools-wrapper");
|
|
612
723
|
if (existingHighlightFrame) {
|
|
613
724
|
existingHighlightFrame.remove();
|
|
614
725
|
}
|
|
@@ -627,24 +738,15 @@ const highlightNode = (node) => {
|
|
|
627
738
|
// Create tools wrapper using CSS transform (GPU-accelerated)
|
|
628
739
|
const toolsWrapper = document.createElement("div");
|
|
629
740
|
toolsWrapper.classList.add("highlight-frame-tools-wrapper");
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
}
|
|
633
|
-
if (isTextEdit) {
|
|
634
|
-
toolsWrapper.classList.add("is-text-edit");
|
|
635
|
-
}
|
|
741
|
+
toggleClass(toolsWrapper, "is-instance", isInstance);
|
|
742
|
+
toggleClass(toolsWrapper, "is-text-edit", isTextEdit);
|
|
636
743
|
toolsWrapper.style.position = "absolute";
|
|
637
744
|
toolsWrapper.style.transform = `translate(${left}px, ${bottomY}px)`;
|
|
638
745
|
toolsWrapper.style.transformOrigin = "left center";
|
|
639
746
|
toolsWrapper.style.pointerEvents = "none";
|
|
640
747
|
toolsWrapper.style.zIndex = "500";
|
|
641
748
|
createToolsContainer(node, toolsWrapper, isInstance, isTextEdit);
|
|
642
|
-
|
|
643
|
-
canvasContainer.appendChild(toolsWrapper);
|
|
644
|
-
}
|
|
645
|
-
else {
|
|
646
|
-
document.body.appendChild(toolsWrapper);
|
|
647
|
-
}
|
|
749
|
+
container.appendChild(toolsWrapper);
|
|
648
750
|
};
|
|
649
751
|
|
|
650
752
|
const getComponentColor = () => {
|
|
@@ -662,24 +764,13 @@ const refreshHighlightFrame = (node, nodeProvider, canvasName = "canvas") => {
|
|
|
662
764
|
const isTextEdit = node.contentEditable === "true";
|
|
663
765
|
// Update SVG dimensions to match current viewport (handles window resize and ensures coordinate system is correct)
|
|
664
766
|
// Use clientWidth/Height to match getBoundingClientRect() coordinate system (excludes scrollbars)
|
|
665
|
-
const viewportWidth
|
|
666
|
-
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
767
|
+
const { width: viewportWidth, height: viewportHeight } = getViewportDimensions();
|
|
667
768
|
frame.setAttribute("width", viewportWidth.toString());
|
|
668
769
|
frame.setAttribute("height", viewportHeight.toString());
|
|
669
770
|
// Update instance class
|
|
670
|
-
|
|
671
|
-
frame.classList.add("is-instance");
|
|
672
|
-
}
|
|
673
|
-
else {
|
|
674
|
-
frame.classList.remove("is-instance");
|
|
675
|
-
}
|
|
771
|
+
toggleClass(frame, "is-instance", isInstance);
|
|
676
772
|
// Update text edit class
|
|
677
|
-
|
|
678
|
-
frame.classList.add("is-text-edit");
|
|
679
|
-
}
|
|
680
|
-
else {
|
|
681
|
-
frame.classList.remove("is-text-edit");
|
|
682
|
-
}
|
|
773
|
+
toggleClass(frame, "is-text-edit", isTextEdit);
|
|
683
774
|
const group = frame.querySelector(".highlight-frame-group");
|
|
684
775
|
if (!group)
|
|
685
776
|
return;
|
|
@@ -696,8 +787,7 @@ const refreshHighlightFrame = (node, nodeProvider, canvasName = "canvas") => {
|
|
|
696
787
|
else {
|
|
697
788
|
rect.removeAttribute("stroke"); // Use CSS default
|
|
698
789
|
}
|
|
699
|
-
const
|
|
700
|
-
const container = canvasContainer || document.body;
|
|
790
|
+
const container = getCanvasContainerOrBody();
|
|
701
791
|
const toolsWrapper = container.querySelector(".highlight-frame-tools-wrapper");
|
|
702
792
|
const nodeTools = toolsWrapper?.querySelector(".node-tools");
|
|
703
793
|
const zoom = getCanvasWindowValue(["zoom", "current"], canvasName) ?? 1;
|
|
@@ -708,36 +798,10 @@ const refreshHighlightFrame = (node, nodeProvider, canvasName = "canvas") => {
|
|
|
708
798
|
const minWidth = Math.max(width, 3);
|
|
709
799
|
const bottomY = top + height;
|
|
710
800
|
// Update instance classes on tools wrapper and node tools
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
else {
|
|
716
|
-
toolsWrapper.classList.remove("is-instance");
|
|
717
|
-
}
|
|
718
|
-
// Update text edit class
|
|
719
|
-
if (isTextEdit) {
|
|
720
|
-
toolsWrapper.classList.add("is-text-edit");
|
|
721
|
-
}
|
|
722
|
-
else {
|
|
723
|
-
toolsWrapper.classList.remove("is-text-edit");
|
|
724
|
-
}
|
|
725
|
-
}
|
|
726
|
-
if (nodeTools) {
|
|
727
|
-
if (isInstance) {
|
|
728
|
-
nodeTools.classList.add("is-instance");
|
|
729
|
-
}
|
|
730
|
-
else {
|
|
731
|
-
nodeTools.classList.remove("is-instance");
|
|
732
|
-
}
|
|
733
|
-
// Update text edit class
|
|
734
|
-
if (isTextEdit) {
|
|
735
|
-
nodeTools.classList.add("is-text-edit");
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
nodeTools.classList.remove("is-text-edit");
|
|
739
|
-
}
|
|
740
|
-
}
|
|
801
|
+
toggleClass(toolsWrapper, "is-instance", isInstance);
|
|
802
|
+
toggleClass(toolsWrapper, "is-text-edit", isTextEdit);
|
|
803
|
+
toggleClass(nodeTools, "is-instance", isInstance);
|
|
804
|
+
toggleClass(nodeTools, "is-text-edit", isTextEdit);
|
|
741
805
|
// Batch all DOM writes (single paint pass)
|
|
742
806
|
// Update group transform to move entire group (rect + handles) at once
|
|
743
807
|
group.setAttribute("transform", `translate(${left}, ${top})`);
|
|
@@ -803,8 +867,7 @@ const updateHighlightFrameVisibility = (node) => {
|
|
|
803
867
|
const displayValue = hasHiddenClass ? "none" : "";
|
|
804
868
|
// Batch DOM writes
|
|
805
869
|
frame.style.display = displayValue;
|
|
806
|
-
const
|
|
807
|
-
const container = canvasContainer || document.body;
|
|
870
|
+
const container = getCanvasContainerOrBody();
|
|
808
871
|
const toolsWrapper = container.querySelector(".highlight-frame-tools-wrapper");
|
|
809
872
|
if (toolsWrapper) {
|
|
810
873
|
toolsWrapper.style.display = displayValue;
|
|
@@ -1110,7 +1173,6 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
1110
1173
|
highlightNode(node);
|
|
1111
1174
|
if (nodeProvider) {
|
|
1112
1175
|
updateHighlightFrameVisibility(node);
|
|
1113
|
-
updateHighlightFrameVisibility(node);
|
|
1114
1176
|
}
|
|
1115
1177
|
}
|
|
1116
1178
|
else {
|
|
@@ -1118,7 +1180,7 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
1118
1180
|
}
|
|
1119
1181
|
};
|
|
1120
1182
|
// Setup event listener
|
|
1121
|
-
const removeListeners = setupEventListener
|
|
1183
|
+
const removeListeners = setupEventListener(nodeProvider, selectNode, handleEscape, text);
|
|
1122
1184
|
const cleanup = () => {
|
|
1123
1185
|
removeListeners();
|
|
1124
1186
|
resizeObserver?.disconnect();
|
|
@@ -1153,6 +1215,10 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
1153
1215
|
return nodeTools;
|
|
1154
1216
|
};
|
|
1155
1217
|
|
|
1218
|
+
const getNodeProvider = () => {
|
|
1219
|
+
return document.querySelector('[data-role="node-provider"]');
|
|
1220
|
+
};
|
|
1221
|
+
|
|
1156
1222
|
const DEFAULT_WIDTH = 400;
|
|
1157
1223
|
const RESIZE_CONFIG = {
|
|
1158
1224
|
minWidth: 4,
|
|
@@ -1186,25 +1252,20 @@ const RESIZE_PRESETS = [
|
|
|
1186
1252
|
},
|
|
1187
1253
|
];
|
|
1188
1254
|
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
document.removeEventListener("mousemove", handleResize);
|
|
1204
|
-
document.removeEventListener("mouseup", stopResize);
|
|
1205
|
-
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
1206
|
-
window.removeEventListener("blur", blurResize);
|
|
1207
|
-
};
|
|
1255
|
+
/**
|
|
1256
|
+
* Removes a viewport label for a single viewport element.
|
|
1257
|
+
* @param viewportElement - The viewport element to remove the label for
|
|
1258
|
+
*/
|
|
1259
|
+
const removeViewportLabel = (viewportElement) => {
|
|
1260
|
+
const viewportName = viewportElement.getAttribute("data-viewport-name");
|
|
1261
|
+
if (!viewportName) {
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
const overlay = getViewportLabelOverlay();
|
|
1265
|
+
const labelGroup = overlay.querySelector(`[data-viewport-name="${viewportName}"]`);
|
|
1266
|
+
if (labelGroup) {
|
|
1267
|
+
labelGroup.remove();
|
|
1268
|
+
}
|
|
1208
1269
|
};
|
|
1209
1270
|
|
|
1210
1271
|
const createResizeHandle = (container) => {
|
|
@@ -1273,58 +1334,59 @@ const createViewport = (container, initialWidth) => {
|
|
|
1273
1334
|
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
1274
1335
|
container.style.setProperty("--container-width", `${width}px`);
|
|
1275
1336
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
1276
|
-
|
|
1337
|
+
// Track initial values for resize calculation
|
|
1277
1338
|
let startX = 0;
|
|
1278
1339
|
let startWidth = 0;
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
const handleResize = (event) => {
|
|
1287
|
-
if (!isDragging)
|
|
1288
|
-
return;
|
|
1289
|
-
if (canvas) {
|
|
1290
|
-
canvas.style.cursor = "ew-resize";
|
|
1291
|
-
}
|
|
1292
|
-
const width = calcWidth(event, startX, startWidth);
|
|
1293
|
-
updateWidth(container, width);
|
|
1294
|
-
};
|
|
1295
|
-
const stopResize = (event) => {
|
|
1296
|
-
event.preventDefault();
|
|
1297
|
-
event.stopPropagation();
|
|
1298
|
-
if (canvas) {
|
|
1299
|
-
canvas.style.cursor = "default";
|
|
1300
|
-
}
|
|
1301
|
-
isDragging = false;
|
|
1302
|
-
};
|
|
1303
|
-
const blurResize = () => {
|
|
1304
|
-
if (canvas) {
|
|
1305
|
-
canvas.style.cursor = "default";
|
|
1340
|
+
// Handle mouse leave for resize (specific to resize use case)
|
|
1341
|
+
const handleMouseLeave = (event) => {
|
|
1342
|
+
// Check if mouse is leaving the window/document
|
|
1343
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
1344
|
+
if (canvas) {
|
|
1345
|
+
canvas.style.cursor = "default";
|
|
1346
|
+
}
|
|
1306
1347
|
}
|
|
1307
|
-
isDragging = false;
|
|
1308
1348
|
};
|
|
1309
|
-
const
|
|
1310
|
-
|
|
1311
|
-
|
|
1349
|
+
const removeDragListeners = createDragHandler(resizeHandle, {
|
|
1350
|
+
onStart: (_event, { startX: dragStartX }) => {
|
|
1351
|
+
startX = dragStartX;
|
|
1352
|
+
startWidth = container.offsetWidth;
|
|
1353
|
+
},
|
|
1354
|
+
onDrag: (event) => {
|
|
1355
|
+
if (canvas) {
|
|
1356
|
+
canvas.style.cursor = "ew-resize";
|
|
1357
|
+
}
|
|
1358
|
+
const width = calcWidth(event, startX, startWidth);
|
|
1359
|
+
updateWidth(container, width);
|
|
1360
|
+
},
|
|
1361
|
+
onStop: () => {
|
|
1362
|
+
if (canvas) {
|
|
1363
|
+
canvas.style.cursor = "default";
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
onCancel: () => {
|
|
1367
|
+
if (canvas) {
|
|
1368
|
+
canvas.style.cursor = "default";
|
|
1369
|
+
}
|
|
1370
|
+
},
|
|
1371
|
+
onPreventClick: () => { },
|
|
1372
|
+
});
|
|
1373
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
1374
|
+
// Create/refresh the label for this viewport
|
|
1375
|
+
refreshViewportLabel(container);
|
|
1312
1376
|
const cleanup = () => {
|
|
1313
|
-
|
|
1314
|
-
|
|
1377
|
+
removeDragListeners();
|
|
1378
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
1315
1379
|
resizeHandle.remove();
|
|
1316
|
-
//
|
|
1317
|
-
|
|
1380
|
+
// Remove the label for this viewport
|
|
1381
|
+
removeViewportLabel(container);
|
|
1318
1382
|
};
|
|
1319
1383
|
return {
|
|
1320
1384
|
setWidth: (width) => {
|
|
1321
1385
|
updateWidth(container, width);
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
1325
|
-
const nodeTools = window.nodeTools;
|
|
1386
|
+
refreshViewportLabel(container);
|
|
1387
|
+
const nodeTools = getNodeTools();
|
|
1326
1388
|
const selectedNode = nodeTools?.getSelectedNode?.();
|
|
1327
|
-
const nodeProvider =
|
|
1389
|
+
const nodeProvider = getNodeProvider();
|
|
1328
1390
|
if (selectedNode && nodeProvider) {
|
|
1329
1391
|
refreshHighlightFrame(selectedNode, nodeProvider);
|
|
1330
1392
|
}
|