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