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