@node-edit-utils/core 2.3.0 → 2.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/node-tools/select/helpers/isInsideViewport.d.ts +1 -0
- package/dist/lib/viewport/constants.d.ts +2 -2
- package/dist/lib/viewport/createViewport.d.ts +1 -1
- package/dist/lib/viewport/label/getViewportLabelsOverlay.d.ts +1 -0
- package/dist/lib/viewport/label/helpers/getLabelPosition.d.ts +4 -0
- package/dist/lib/viewport/label/helpers/getTransformValues.d.ts +4 -0
- package/dist/lib/viewport/label/helpers/getZoomValue.d.ts +1 -0
- package/dist/lib/viewport/label/index.d.ts +4 -0
- package/dist/lib/viewport/label/isViewportLabelDragging.d.ts +2 -0
- package/dist/lib/viewport/label/refreshViewportLabels.d.ts +1 -0
- package/dist/lib/viewport/label/setupViewportLabelDrag.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +288 -63
- package/dist/node-edit-utils.esm.js +288 -63
- package/dist/node-edit-utils.umd.js +288 -63
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/lib/canvas/createCanvasObserver.ts +14 -0
- package/src/lib/node-tools/createNodeTools.ts +12 -5
- package/src/lib/node-tools/select/helpers/isInsideViewport.ts +19 -0
- package/src/lib/node-tools/select/selectNode.ts +13 -1
- package/src/lib/node-tools/text/events/setupMutationObserver.ts +1 -0
- package/src/lib/styles/styles.css +48 -1
- package/src/lib/viewport/constants.ts +2 -2
- package/src/lib/viewport/createViewport.ts +26 -7
- package/src/lib/viewport/events/setupEventListener.ts +9 -0
- package/src/lib/viewport/label/getViewportLabelsOverlay.ts +33 -0
- package/src/lib/viewport/label/helpers/getLabelPosition.ts +8 -0
- package/src/lib/viewport/label/helpers/getTransformValues.ts +8 -0
- package/src/lib/viewport/label/helpers/getZoomValue.ts +4 -0
- package/src/lib/viewport/label/index.ts +4 -0
- package/src/lib/viewport/label/isViewportLabelDragging.ts +9 -0
- package/src/lib/viewport/label/refreshViewportLabels.ts +69 -0
- package/src/lib/viewport/label/setupViewportLabelDrag.ts +98 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const isInsideViewport: (element: Element) => boolean;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const DEFAULT_WIDTH = 400;
|
|
2
2
|
export declare const RESIZE_CONFIG: {
|
|
3
|
-
readonly minWidth:
|
|
4
|
-
readonly maxWidth:
|
|
3
|
+
readonly minWidth: 4;
|
|
4
|
+
readonly maxWidth: 2560;
|
|
5
5
|
};
|
|
6
6
|
export declare const RESIZE_PRESETS: readonly [{
|
|
7
7
|
readonly name: "Mobile";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { Viewport } from "./types";
|
|
2
|
-
export declare const createViewport: (container: HTMLElement) => Viewport;
|
|
2
|
+
export declare const createViewport: (container: HTMLElement, initialWidth?: number) => Viewport;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getViewportLabelsOverlay: () => SVGSVGElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getZoomValue: () => number;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { getViewportLabelsOverlay } from "./getViewportLabelsOverlay";
|
|
2
|
+
export { isViewportLabelDragging } from "./isViewportLabelDragging";
|
|
3
|
+
export { refreshViewportLabels } from "./refreshViewportLabels";
|
|
4
|
+
export { setupViewportLabelDrag } from "./setupViewportLabelDrag";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const refreshViewportLabels: () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const setupViewportLabelDrag: (labelElement: SVGTextElement, viewportElement: HTMLElement, viewportName: string) => (() => void);
|
|
@@ -1,10 +1,218 @@
|
|
|
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.2
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
8
|
+
function getScreenBounds(element) {
|
|
9
|
+
const rect = element.getBoundingClientRect();
|
|
10
|
+
return {
|
|
11
|
+
top: rect.top,
|
|
12
|
+
left: rect.left,
|
|
13
|
+
width: rect.width,
|
|
14
|
+
height: rect.height,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const getCanvasContainer = () => {
|
|
19
|
+
return document.querySelector(".canvas-container");
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const getViewportLabelsOverlay = () => {
|
|
23
|
+
const canvasContainer = getCanvasContainer();
|
|
24
|
+
const container = canvasContainer || document.body;
|
|
25
|
+
// Check if overlay already exists
|
|
26
|
+
let overlay = container.querySelector(".viewport-labels-overlay");
|
|
27
|
+
if (!overlay) {
|
|
28
|
+
// Create new SVG overlay
|
|
29
|
+
overlay = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
|
30
|
+
overlay.classList.add("viewport-labels-overlay");
|
|
31
|
+
// Set fixed positioning
|
|
32
|
+
overlay.style.position = "absolute";
|
|
33
|
+
overlay.style.top = "0";
|
|
34
|
+
overlay.style.left = "0";
|
|
35
|
+
overlay.style.width = "100vw";
|
|
36
|
+
overlay.style.height = "100vh";
|
|
37
|
+
overlay.style.pointerEvents = "none";
|
|
38
|
+
overlay.style.zIndex = "500";
|
|
39
|
+
const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
|
|
40
|
+
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
41
|
+
overlay.setAttribute("width", viewportWidth.toString());
|
|
42
|
+
overlay.setAttribute("height", viewportHeight.toString());
|
|
43
|
+
container.appendChild(overlay);
|
|
44
|
+
}
|
|
45
|
+
return overlay;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Global flag to prevent refreshViewportLabels during drag
|
|
49
|
+
let globalIsDragging = false;
|
|
50
|
+
const isViewportLabelDragging = () => globalIsDragging;
|
|
51
|
+
const setViewportLabelDragging = (isDragging) => {
|
|
52
|
+
globalIsDragging = isDragging;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
function sendPostMessage(action, data) {
|
|
56
|
+
window.parent.postMessage({
|
|
57
|
+
source: "node-edit-utils",
|
|
58
|
+
action,
|
|
59
|
+
data,
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
}, "*");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const getLabelPosition = (labelGroup) => {
|
|
65
|
+
const transform = labelGroup.getAttribute("transform");
|
|
66
|
+
const match = transform?.match(/translate\((-?\d+(?:\.\d+)?),\s*(-?\d+(?:\.\d+)?)\)/);
|
|
67
|
+
if (match) {
|
|
68
|
+
return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
|
|
69
|
+
}
|
|
70
|
+
return { x: 0, y: 0 };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const getTransformValues = (element) => {
|
|
74
|
+
const style = element.style.transform;
|
|
75
|
+
const match = style.match(/translate3d\((-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px,\s*(-?\d+(?:\.\d+)?)px\)/);
|
|
76
|
+
if (match) {
|
|
77
|
+
return { x: parseFloat(match[1]), y: parseFloat(match[2]) };
|
|
78
|
+
}
|
|
79
|
+
return { x: 0, y: 0 };
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const getZoomValue = () => {
|
|
83
|
+
const zoomValue = getComputedStyle(document.body).getPropertyValue("--zoom").trim();
|
|
84
|
+
return zoomValue ? parseFloat(zoomValue) : 1;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const setupViewportLabelDrag = (labelElement, viewportElement, viewportName) => {
|
|
88
|
+
let isDragging = false;
|
|
89
|
+
let startX = 0;
|
|
90
|
+
let startY = 0;
|
|
91
|
+
let initialTransform = { x: 0, y: 0 };
|
|
92
|
+
let initialLabelPosition = { x: 0, y: 0 };
|
|
93
|
+
// Get the parent group element that contains the label
|
|
94
|
+
const labelGroup = labelElement.parentElement;
|
|
95
|
+
const startDrag = (event) => {
|
|
96
|
+
event.preventDefault();
|
|
97
|
+
event.stopPropagation();
|
|
98
|
+
isDragging = true;
|
|
99
|
+
setViewportLabelDragging(true);
|
|
100
|
+
startX = event.clientX;
|
|
101
|
+
startY = event.clientY;
|
|
102
|
+
initialTransform = getTransformValues(viewportElement);
|
|
103
|
+
initialLabelPosition = getLabelPosition(labelGroup);
|
|
104
|
+
};
|
|
105
|
+
const handleDrag = (event) => {
|
|
106
|
+
if (!isDragging)
|
|
107
|
+
return;
|
|
108
|
+
const zoom = getZoomValue();
|
|
109
|
+
// Calculate mouse delta
|
|
110
|
+
const rawDeltaX = event.clientX - startX;
|
|
111
|
+
const rawDeltaY = event.clientY - startY;
|
|
112
|
+
// Adjust delta for zoom level
|
|
113
|
+
const deltaX = rawDeltaX / zoom;
|
|
114
|
+
const deltaY = rawDeltaY / zoom;
|
|
115
|
+
const newX = initialTransform.x + deltaX;
|
|
116
|
+
const newY = initialTransform.y + deltaY;
|
|
117
|
+
// Update label position with raw delta (labels are in screen space)
|
|
118
|
+
const newLabelX = initialLabelPosition.x + rawDeltaX;
|
|
119
|
+
const newLabelY = initialLabelPosition.y + rawDeltaY;
|
|
120
|
+
labelGroup.setAttribute("transform", `translate(${newLabelX}, ${newLabelY})`);
|
|
121
|
+
// Update viewport position with zoom-adjusted delta
|
|
122
|
+
viewportElement.style.transform = `translate3d(${newX}px, ${newY}px, 0)`;
|
|
123
|
+
};
|
|
124
|
+
const stopDrag = (event) => {
|
|
125
|
+
if (!isDragging)
|
|
126
|
+
return;
|
|
127
|
+
event.preventDefault();
|
|
128
|
+
event.stopPropagation();
|
|
129
|
+
isDragging = false;
|
|
130
|
+
setViewportLabelDragging(false);
|
|
131
|
+
const finalTransform = getTransformValues(viewportElement);
|
|
132
|
+
// Trigger refresh after drag completes to update highlight frame and labels
|
|
133
|
+
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
134
|
+
const nodeTools = window.nodeTools;
|
|
135
|
+
if (nodeTools?.refreshHighlightFrame) {
|
|
136
|
+
nodeTools.refreshHighlightFrame();
|
|
137
|
+
}
|
|
138
|
+
// Notify parent about the new position
|
|
139
|
+
sendPostMessage("viewport-position-changed", {
|
|
140
|
+
viewportName,
|
|
141
|
+
x: finalTransform.x,
|
|
142
|
+
y: finalTransform.y,
|
|
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
|
+
};
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Store cleanup functions for drag listeners
|
|
164
|
+
const dragCleanupFunctions = new Map();
|
|
165
|
+
const refreshViewportLabels = () => {
|
|
166
|
+
// Skip refresh if a viewport label is being dragged
|
|
167
|
+
if (isViewportLabelDragging()) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const overlay = getViewportLabelsOverlay();
|
|
171
|
+
// Update SVG dimensions to match current viewport
|
|
172
|
+
const viewportWidth = document.documentElement.clientWidth || window.innerWidth;
|
|
173
|
+
const viewportHeight = document.documentElement.clientHeight || window.innerHeight;
|
|
174
|
+
overlay.setAttribute("width", viewportWidth.toString());
|
|
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);
|
|
195
|
+
// Create group for this viewport label
|
|
196
|
+
const group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
197
|
+
group.classList.add("viewport-label-group");
|
|
198
|
+
group.setAttribute("data-viewport-name", viewportName);
|
|
199
|
+
group.setAttribute("transform", `translate(${bounds.left}, ${bounds.top})`);
|
|
200
|
+
// Create text element
|
|
201
|
+
const text = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
202
|
+
text.classList.add("viewport-label-text");
|
|
203
|
+
text.setAttribute("x", "0");
|
|
204
|
+
text.setAttribute("y", "-8");
|
|
205
|
+
text.setAttribute("vector-effect", "non-scaling-stroke");
|
|
206
|
+
text.setAttribute("pointer-events", "auto");
|
|
207
|
+
text.textContent = viewportName;
|
|
208
|
+
group.appendChild(text);
|
|
209
|
+
overlay.appendChild(group);
|
|
210
|
+
// Setup drag functionality for this label
|
|
211
|
+
const cleanup = setupViewportLabelDrag(text, viewportElement, viewportName);
|
|
212
|
+
dragCleanupFunctions.set(viewportName, cleanup);
|
|
213
|
+
});
|
|
214
|
+
};
|
|
215
|
+
|
|
8
216
|
const getCanvasWindowValue = (path, canvasName = "canvas") => {
|
|
9
217
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
10
218
|
const canvas = window[canvasName];
|
|
@@ -34,6 +242,8 @@ function createCanvasObserver(canvasName = "canvas") {
|
|
|
34
242
|
if (nodeTools?.refreshHighlightFrame) {
|
|
35
243
|
nodeTools.refreshHighlightFrame();
|
|
36
244
|
}
|
|
245
|
+
// Refresh viewport labels
|
|
246
|
+
refreshViewportLabels();
|
|
37
247
|
});
|
|
38
248
|
observer.observe(transformLayer, {
|
|
39
249
|
attributes: true,
|
|
@@ -41,8 +251,16 @@ function createCanvasObserver(canvasName = "canvas") {
|
|
|
41
251
|
subtree: true,
|
|
42
252
|
childList: true,
|
|
43
253
|
});
|
|
254
|
+
// Handle window resize for viewport labels
|
|
255
|
+
const handleResize = () => {
|
|
256
|
+
refreshViewportLabels();
|
|
257
|
+
};
|
|
258
|
+
window.addEventListener("resize", handleResize);
|
|
259
|
+
// Initial refresh of viewport labels
|
|
260
|
+
refreshViewportLabels();
|
|
44
261
|
function disconnect() {
|
|
45
262
|
observer.disconnect();
|
|
263
|
+
window.removeEventListener("resize", handleResize);
|
|
46
264
|
}
|
|
47
265
|
return {
|
|
48
266
|
disconnect,
|
|
@@ -57,15 +275,6 @@ const connectResizeObserver = (element, handler) => {
|
|
|
57
275
|
return resizeObserver;
|
|
58
276
|
};
|
|
59
277
|
|
|
60
|
-
function sendPostMessage(action, data) {
|
|
61
|
-
window.parent.postMessage({
|
|
62
|
-
source: "node-edit-utils",
|
|
63
|
-
action,
|
|
64
|
-
data,
|
|
65
|
-
timestamp: Date.now(),
|
|
66
|
-
}, "*");
|
|
67
|
-
}
|
|
68
|
-
|
|
69
278
|
const bindToWindow = (key, value) => {
|
|
70
279
|
if (typeof window !== "undefined") {
|
|
71
280
|
// biome-ignore lint/suspicious/noExplicitAny: global window extension requires flexibility
|
|
@@ -93,10 +302,6 @@ const processPostMessage = (event, onNodeSelected) => {
|
|
|
93
302
|
}
|
|
94
303
|
};
|
|
95
304
|
|
|
96
|
-
const getCanvasContainer = () => {
|
|
97
|
-
return document.querySelector(".canvas-container");
|
|
98
|
-
};
|
|
99
|
-
|
|
100
305
|
const clearHighlightFrame = () => {
|
|
101
306
|
const canvasContainer = getCanvasContainer();
|
|
102
307
|
const container = canvasContainer || document.body;
|
|
@@ -148,6 +353,21 @@ const isInsideComponent = (element) => {
|
|
|
148
353
|
return false;
|
|
149
354
|
};
|
|
150
355
|
|
|
356
|
+
const isInsideViewport = (element) => {
|
|
357
|
+
let current = element;
|
|
358
|
+
while (current) {
|
|
359
|
+
if (current.classList.contains("viewport")) {
|
|
360
|
+
return true;
|
|
361
|
+
}
|
|
362
|
+
// Stop at node-provider to avoid checking beyond the editable area
|
|
363
|
+
if (current.getAttribute("data-role") === "node-provider") {
|
|
364
|
+
break;
|
|
365
|
+
}
|
|
366
|
+
current = current.parentElement;
|
|
367
|
+
}
|
|
368
|
+
return false;
|
|
369
|
+
};
|
|
370
|
+
|
|
151
371
|
const targetSameCandidates = (cache, current) => cache.length === current.length && cache.every((el, i) => el === current[i]);
|
|
152
372
|
|
|
153
373
|
let candidateCache = [];
|
|
@@ -160,7 +380,16 @@ const selectNode = (event, nodeProvider, text) => {
|
|
|
160
380
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
161
381
|
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) &&
|
|
162
382
|
!element.classList.contains("select-none") &&
|
|
163
|
-
!
|
|
383
|
+
!element.classList.contains("content-layer") &&
|
|
384
|
+
!element.classList.contains("resize-handle") &&
|
|
385
|
+
!element.classList.contains("resize-presets") &&
|
|
386
|
+
!isInsideComponent(element) &&
|
|
387
|
+
isInsideViewport(element));
|
|
388
|
+
console.log("candidates", candidates);
|
|
389
|
+
if (candidates.length === 0) {
|
|
390
|
+
lastSelectedNode = null;
|
|
391
|
+
return null;
|
|
392
|
+
}
|
|
164
393
|
const editableNode = text.getEditableNode();
|
|
165
394
|
if (editableNode && candidates.includes(editableNode)) {
|
|
166
395
|
selectedNode = editableNode;
|
|
@@ -265,16 +494,6 @@ const createCornerHandles = (group, width, height, isInstance = false, isTextEdi
|
|
|
265
494
|
createCornerHandle(group, 0, height, "handle-bottom-left", isInstance, isTextEdit);
|
|
266
495
|
};
|
|
267
496
|
|
|
268
|
-
function getScreenBounds(element) {
|
|
269
|
-
const rect = element.getBoundingClientRect();
|
|
270
|
-
return {
|
|
271
|
-
top: rect.top,
|
|
272
|
-
left: rect.left,
|
|
273
|
-
width: rect.width,
|
|
274
|
-
height: rect.height,
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
|
|
278
497
|
const getComponentColor$1 = () => {
|
|
279
498
|
return getComputedStyle(document.documentElement).getPropertyValue("--component-color").trim() || "oklch(65.6% 0.241 354.308)";
|
|
280
499
|
};
|
|
@@ -707,6 +926,7 @@ const setupMutationObserver = (node, nodeProvider, canvasName = "canvas") => {
|
|
|
707
926
|
// Accumulate mutations instead of replacing
|
|
708
927
|
pendingMutations.push(...mutations);
|
|
709
928
|
scheduleProcess();
|
|
929
|
+
console.log("refreshHighlightFrame in mutationObserver");
|
|
710
930
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
711
931
|
});
|
|
712
932
|
return () => {
|
|
@@ -844,14 +1064,15 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
844
1064
|
checkNodeExists();
|
|
845
1065
|
if (!document.contains(node))
|
|
846
1066
|
return;
|
|
1067
|
+
console.log("refreshHighlightFrame in mutationObserver 2");
|
|
847
1068
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
848
1069
|
updateHighlightFrameVisibility(node);
|
|
849
1070
|
});
|
|
850
1071
|
mutationObserver.observe(node, {
|
|
851
1072
|
attributes: true,
|
|
852
1073
|
characterData: true,
|
|
853
|
-
childList: true,
|
|
854
|
-
subtree: true,
|
|
1074
|
+
//childList: true,
|
|
1075
|
+
//subtree: true,
|
|
855
1076
|
});
|
|
856
1077
|
// Also observe parent node to catch when this node is removed
|
|
857
1078
|
const parentNode = node.parentElement;
|
|
@@ -880,15 +1101,22 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
880
1101
|
if (!document.contains(node))
|
|
881
1102
|
return; // Exit early if node was removed
|
|
882
1103
|
refreshHighlightFrame(node, nodeProvider, canvasName);
|
|
1104
|
+
console.log("refreshHighlightFrame in resizeObserver");
|
|
883
1105
|
updateHighlightFrameVisibility(node);
|
|
884
1106
|
});
|
|
885
1107
|
}
|
|
886
1108
|
selectedNode = node;
|
|
887
1109
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
888
1110
|
highlightNode(node) ?? null;
|
|
889
|
-
if (node
|
|
890
|
-
|
|
891
|
-
|
|
1111
|
+
if (node) {
|
|
1112
|
+
highlightNode(node);
|
|
1113
|
+
if (nodeProvider) {
|
|
1114
|
+
updateHighlightFrameVisibility(node);
|
|
1115
|
+
updateHighlightFrameVisibility(node);
|
|
1116
|
+
}
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
clearHighlightFrame();
|
|
892
1120
|
}
|
|
893
1121
|
};
|
|
894
1122
|
// Setup event listener
|
|
@@ -927,36 +1155,10 @@ const createNodeTools = (element, canvasName = "canvas") => {
|
|
|
927
1155
|
return nodeTools;
|
|
928
1156
|
};
|
|
929
1157
|
|
|
930
|
-
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
931
|
-
function withRAFThrottle(func) {
|
|
932
|
-
let rafId = null;
|
|
933
|
-
let lastArgs = null;
|
|
934
|
-
const throttled = (...args) => {
|
|
935
|
-
lastArgs = args;
|
|
936
|
-
if (rafId === null) {
|
|
937
|
-
rafId = requestAnimationFrame(() => {
|
|
938
|
-
if (lastArgs) {
|
|
939
|
-
func(...lastArgs);
|
|
940
|
-
}
|
|
941
|
-
rafId = null;
|
|
942
|
-
lastArgs = null;
|
|
943
|
-
});
|
|
944
|
-
}
|
|
945
|
-
};
|
|
946
|
-
throttled.cleanup = () => {
|
|
947
|
-
if (rafId !== null) {
|
|
948
|
-
cancelAnimationFrame(rafId);
|
|
949
|
-
rafId = null;
|
|
950
|
-
lastArgs = null;
|
|
951
|
-
}
|
|
952
|
-
};
|
|
953
|
-
return throttled;
|
|
954
|
-
}
|
|
955
|
-
|
|
956
1158
|
const DEFAULT_WIDTH = 400;
|
|
957
1159
|
const RESIZE_CONFIG = {
|
|
958
|
-
minWidth:
|
|
959
|
-
maxWidth:
|
|
1160
|
+
minWidth: 4,
|
|
1161
|
+
maxWidth: 2560,
|
|
960
1162
|
};
|
|
961
1163
|
const RESIZE_PRESETS = [
|
|
962
1164
|
{
|
|
@@ -987,14 +1189,22 @@ const RESIZE_PRESETS = [
|
|
|
987
1189
|
];
|
|
988
1190
|
|
|
989
1191
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
1192
|
+
const handleMouseLeave = (event) => {
|
|
1193
|
+
// Check if mouse is leaving the window/document
|
|
1194
|
+
if (!event.relatedTarget && (event.target === document || event.target === document.documentElement)) {
|
|
1195
|
+
blurResize();
|
|
1196
|
+
}
|
|
1197
|
+
};
|
|
990
1198
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
991
1199
|
document.addEventListener("mousemove", handleResize);
|
|
992
1200
|
document.addEventListener("mouseup", stopResize);
|
|
1201
|
+
document.addEventListener("mouseleave", handleMouseLeave);
|
|
993
1202
|
window.addEventListener("blur", blurResize);
|
|
994
1203
|
return () => {
|
|
995
1204
|
resizeHandle.removeEventListener("mousedown", startResize);
|
|
996
1205
|
document.removeEventListener("mousemove", handleResize);
|
|
997
1206
|
document.removeEventListener("mouseup", stopResize);
|
|
1207
|
+
document.removeEventListener("mouseleave", handleMouseLeave);
|
|
998
1208
|
window.removeEventListener("blur", blurResize);
|
|
999
1209
|
};
|
|
1000
1210
|
};
|
|
@@ -1054,7 +1264,7 @@ const updateWidth = (container, width) => {
|
|
|
1054
1264
|
updateActivePreset(container, width);
|
|
1055
1265
|
};
|
|
1056
1266
|
|
|
1057
|
-
const createViewport = (container) => {
|
|
1267
|
+
const createViewport = (container, initialWidth) => {
|
|
1058
1268
|
const canvas = getCanvasContainer();
|
|
1059
1269
|
// Remove any existing resize handle to prevent duplicates
|
|
1060
1270
|
const existingHandle = container.querySelector(".resize-handle");
|
|
@@ -1062,7 +1272,8 @@ const createViewport = (container) => {
|
|
|
1062
1272
|
existingHandle.remove();
|
|
1063
1273
|
}
|
|
1064
1274
|
const resizeHandle = createResizeHandle(container);
|
|
1065
|
-
|
|
1275
|
+
const width = initialWidth ?? DEFAULT_WIDTH;
|
|
1276
|
+
container.style.setProperty("--container-width", `${width}px`);
|
|
1066
1277
|
createResizePresets(resizeHandle, container, updateWidth);
|
|
1067
1278
|
let isDragging = false;
|
|
1068
1279
|
let startX = 0;
|
|
@@ -1083,7 +1294,6 @@ const createViewport = (container) => {
|
|
|
1083
1294
|
const width = calcWidth(event, startX, startWidth);
|
|
1084
1295
|
updateWidth(container, width);
|
|
1085
1296
|
};
|
|
1086
|
-
const throttledHandleResize = withRAFThrottle(handleResize);
|
|
1087
1297
|
const stopResize = (event) => {
|
|
1088
1298
|
event.preventDefault();
|
|
1089
1299
|
event.stopPropagation();
|
|
@@ -1093,18 +1303,33 @@ const createViewport = (container) => {
|
|
|
1093
1303
|
isDragging = false;
|
|
1094
1304
|
};
|
|
1095
1305
|
const blurResize = () => {
|
|
1306
|
+
if (canvas) {
|
|
1307
|
+
canvas.style.cursor = "default";
|
|
1308
|
+
}
|
|
1096
1309
|
isDragging = false;
|
|
1097
1310
|
};
|
|
1098
|
-
const removeListeners = setupEventListener(resizeHandle, startResize,
|
|
1311
|
+
const removeListeners = setupEventListener(resizeHandle, startResize, handleResize, stopResize, blurResize);
|
|
1312
|
+
// Refresh viewport labels when viewport is created
|
|
1313
|
+
refreshViewportLabels();
|
|
1099
1314
|
const cleanup = () => {
|
|
1100
1315
|
isDragging = false;
|
|
1101
|
-
throttledHandleResize?.cleanup();
|
|
1102
1316
|
removeListeners();
|
|
1103
1317
|
resizeHandle.remove();
|
|
1318
|
+
// Refresh labels after cleanup to remove this viewport's label if needed
|
|
1319
|
+
refreshViewportLabels();
|
|
1104
1320
|
};
|
|
1105
1321
|
return {
|
|
1106
1322
|
setWidth: (width) => {
|
|
1107
1323
|
updateWidth(container, width);
|
|
1324
|
+
refreshViewportLabels();
|
|
1325
|
+
// Refresh highlight frame when viewport width changes to update node positions
|
|
1326
|
+
// biome-ignore lint/suspicious/noExplicitAny: global window extension
|
|
1327
|
+
const nodeTools = window.nodeTools;
|
|
1328
|
+
const selectedNode = nodeTools?.getSelectedNode?.();
|
|
1329
|
+
const nodeProvider = document.querySelector('[data-role="node-provider"]');
|
|
1330
|
+
if (selectedNode && nodeProvider) {
|
|
1331
|
+
refreshHighlightFrame(selectedNode, nodeProvider);
|
|
1332
|
+
}
|
|
1108
1333
|
},
|
|
1109
1334
|
cleanup,
|
|
1110
1335
|
};
|