@node-edit-utils/core 2.0.5 → 2.0.7
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/disableCanvasTextMode.d.ts +1 -0
- package/dist/lib/canvas/enableCanvasTextMode.d.ts +1 -0
- package/dist/lib/node-tools/highlight/updateHighlightFrameVisibility.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +39 -18
- package/dist/node-edit-utils.esm.js +39 -18
- package/dist/node-edit-utils.umd.js +39 -18
- 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/disableCanvasTextMode.ts +7 -0
- package/src/lib/canvas/enableCanvasTextMode.ts +7 -0
- package/src/lib/node-tools/createNodeTools.ts +24 -0
- package/src/lib/node-tools/events/setupEventListener.ts +2 -2
- package/src/lib/node-tools/highlight/updateHighlightFrameVisibility.ts +10 -0
- package/src/lib/node-tools/select/selectNode.ts +1 -1
- package/src/lib/node-tools/text/nodeText.ts +4 -5
- package/src/lib/post-message/processPostMessage.ts +0 -3
- package/src/lib/styles/styles.css +62 -33
- package/src/lib/viewport/resize/updateActivePreset.ts +0 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const disableCanvasTextMode: () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const enableCanvasTextMode: () => void;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const updateHighlightFrameVisibility: (node: HTMLElement, nodeProvider: HTMLElement) => void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.0.
|
|
4
|
+
* @version 2.0.7
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -100,17 +100,14 @@ const bindToWindow = (key, value) => {
|
|
|
100
100
|
const processPostMessage = (event, onNodeSelected) => {
|
|
101
101
|
if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
102
102
|
if (event.data.action === "zoom") {
|
|
103
|
-
|
|
104
|
-
console.log("zoom", zoom);
|
|
103
|
+
event.data.data;
|
|
105
104
|
}
|
|
106
105
|
}
|
|
107
106
|
if (event.data.source === "application") {
|
|
108
107
|
if (event.data.action === "selectedNodeChanged") {
|
|
109
108
|
const nodeId = event.data.data;
|
|
110
|
-
console.log("selectedNodeChanged in node-edit-utils", nodeId);
|
|
111
109
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
112
110
|
if (selectedNode) {
|
|
113
|
-
console.log("selectedNode", selectedNode);
|
|
114
111
|
onNodeSelected?.(selectedNode);
|
|
115
112
|
}
|
|
116
113
|
}
|
|
@@ -156,7 +153,7 @@ const selectNode = (event, editableNode) => {
|
|
|
156
153
|
const clickX = event.clientX;
|
|
157
154
|
const clickY = event.clientY;
|
|
158
155
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
159
|
-
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
|
|
156
|
+
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
|
|
160
157
|
if (editableNode && candidates.includes(editableNode)) {
|
|
161
158
|
return editableNode;
|
|
162
159
|
}
|
|
@@ -197,9 +194,8 @@ const setupEventListener$1 = (nodeProvider, onNodeSelected, onEscapePressed, get
|
|
|
197
194
|
handleNodeClick(event, nodeProvider, getEditableNode(), onNodeSelected);
|
|
198
195
|
};
|
|
199
196
|
const documentKeydownHandler = (event) => {
|
|
197
|
+
console.log("Esacpe Handler", event);
|
|
200
198
|
if (event.key === "Escape") {
|
|
201
|
-
event.preventDefault();
|
|
202
|
-
event.stopPropagation();
|
|
203
199
|
onEscapePressed?.();
|
|
204
200
|
}
|
|
205
201
|
};
|
|
@@ -304,14 +300,22 @@ const refreshHighlightFrame = (node, nodeProvider) => {
|
|
|
304
300
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
305
301
|
};
|
|
306
302
|
|
|
307
|
-
const
|
|
308
|
-
const
|
|
309
|
-
|
|
303
|
+
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
304
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
305
|
+
if (!frame)
|
|
306
|
+
return;
|
|
307
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
308
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const disableCanvasTextMode = () => {
|
|
312
|
+
const disableTextEditMode = getCanvasWindowValue(["keyboard", "disableTextEditMode"]);
|
|
313
|
+
disableTextEditMode?.();
|
|
310
314
|
};
|
|
311
315
|
|
|
312
|
-
const
|
|
313
|
-
const
|
|
314
|
-
|
|
316
|
+
const enableCanvasTextMode = () => {
|
|
317
|
+
const enableTextEditMode = getCanvasWindowValue(["keyboard", "enableTextEditMode"]);
|
|
318
|
+
enableTextEditMode?.();
|
|
315
319
|
};
|
|
316
320
|
|
|
317
321
|
const insertLineBreak = () => {
|
|
@@ -407,7 +411,7 @@ const nodeText = () => {
|
|
|
407
411
|
if (editable) {
|
|
408
412
|
editableNode = node;
|
|
409
413
|
makeNodeEditable(node);
|
|
410
|
-
|
|
414
|
+
enableCanvasTextMode();
|
|
411
415
|
cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
|
|
412
416
|
}
|
|
413
417
|
};
|
|
@@ -424,7 +428,7 @@ const nodeText = () => {
|
|
|
424
428
|
blurInProgress = true;
|
|
425
429
|
const nodeToCleanup = editableNode;
|
|
426
430
|
makeNodeNonEditable(nodeToCleanup);
|
|
427
|
-
|
|
431
|
+
disableCanvasTextMode();
|
|
428
432
|
cleanup?.();
|
|
429
433
|
editableNode = null;
|
|
430
434
|
blurInProgress = false;
|
|
@@ -440,6 +444,7 @@ const nodeText = () => {
|
|
|
440
444
|
const createNodeTools = (element) => {
|
|
441
445
|
const nodeProvider = element;
|
|
442
446
|
let resizeObserver = null;
|
|
447
|
+
let mutationObserver = null;
|
|
443
448
|
let selectedNode = null;
|
|
444
449
|
const text = nodeText();
|
|
445
450
|
const throttledFrameRefresh = withRAFThrottle(refreshHighlightFrame);
|
|
@@ -452,10 +457,14 @@ const createNodeTools = (element) => {
|
|
|
452
457
|
clearHighlightFrame(nodeProvider);
|
|
453
458
|
selectedNode = null;
|
|
454
459
|
resizeObserver?.disconnect();
|
|
460
|
+
mutationObserver?.disconnect();
|
|
455
461
|
}
|
|
456
462
|
}
|
|
457
463
|
};
|
|
458
464
|
const selectNode = (node) => {
|
|
465
|
+
if (selectedNode === node) {
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
459
468
|
if (text.isEditing()) {
|
|
460
469
|
const currentEditable = text.getEditableNode();
|
|
461
470
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -463,21 +472,34 @@ const createNodeTools = (element) => {
|
|
|
463
472
|
}
|
|
464
473
|
}
|
|
465
474
|
resizeObserver?.disconnect();
|
|
475
|
+
mutationObserver?.disconnect();
|
|
466
476
|
if (node && nodeProvider) {
|
|
467
477
|
text.enableEditMode(node, nodeProvider);
|
|
468
478
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
469
479
|
throttledFrameRefresh(node, nodeProvider);
|
|
470
480
|
});
|
|
481
|
+
mutationObserver = new MutationObserver(() => {
|
|
482
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
483
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
484
|
+
});
|
|
485
|
+
mutationObserver.observe(node, {
|
|
486
|
+
attributes: true,
|
|
487
|
+
attributeFilter: ["class"],
|
|
488
|
+
});
|
|
471
489
|
}
|
|
472
490
|
selectedNode = node;
|
|
473
491
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
474
492
|
highlightNode(node, nodeProvider) ?? null;
|
|
493
|
+
if (node && nodeProvider) {
|
|
494
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
495
|
+
}
|
|
475
496
|
};
|
|
476
497
|
// Setup event listener
|
|
477
498
|
const removeListeners = setupEventListener$1(nodeProvider, selectNode, handleEscape, text.getEditableNode);
|
|
478
499
|
const cleanup = () => {
|
|
479
500
|
removeListeners();
|
|
480
501
|
resizeObserver?.disconnect();
|
|
502
|
+
mutationObserver?.disconnect();
|
|
481
503
|
text.blurEditMode();
|
|
482
504
|
throttledFrameRefresh.cleanup();
|
|
483
505
|
};
|
|
@@ -491,6 +513,7 @@ const createNodeTools = (element) => {
|
|
|
491
513
|
clearHighlightFrame(nodeProvider);
|
|
492
514
|
selectedNode = null;
|
|
493
515
|
resizeObserver?.disconnect();
|
|
516
|
+
mutationObserver?.disconnect();
|
|
494
517
|
},
|
|
495
518
|
getEditableNode: () => text.getEditableNode(),
|
|
496
519
|
cleanup,
|
|
@@ -585,9 +608,7 @@ const calcWidth = (event, startX, startWidth) => {
|
|
|
585
608
|
};
|
|
586
609
|
|
|
587
610
|
const updateActivePreset = (container, width) => {
|
|
588
|
-
console.log("updateActivePreset", width);
|
|
589
611
|
const presetButtons = container.querySelectorAll(".resize-preset-button");
|
|
590
|
-
console.log("presetButtons", presetButtons);
|
|
591
612
|
presetButtons.forEach((button, index) => {
|
|
592
613
|
if (index < RESIZE_PRESETS.length) {
|
|
593
614
|
const preset = RESIZE_PRESETS[index];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.0.
|
|
4
|
+
* @version 2.0.7
|
|
5
5
|
*/
|
|
6
6
|
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
7
7
|
function withRAFThrottle(func) {
|
|
@@ -98,17 +98,14 @@ const bindToWindow = (key, value) => {
|
|
|
98
98
|
const processPostMessage = (event, onNodeSelected) => {
|
|
99
99
|
if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
100
100
|
if (event.data.action === "zoom") {
|
|
101
|
-
|
|
102
|
-
console.log("zoom", zoom);
|
|
101
|
+
event.data.data;
|
|
103
102
|
}
|
|
104
103
|
}
|
|
105
104
|
if (event.data.source === "application") {
|
|
106
105
|
if (event.data.action === "selectedNodeChanged") {
|
|
107
106
|
const nodeId = event.data.data;
|
|
108
|
-
console.log("selectedNodeChanged in node-edit-utils", nodeId);
|
|
109
107
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
110
108
|
if (selectedNode) {
|
|
111
|
-
console.log("selectedNode", selectedNode);
|
|
112
109
|
onNodeSelected?.(selectedNode);
|
|
113
110
|
}
|
|
114
111
|
}
|
|
@@ -154,7 +151,7 @@ const selectNode = (event, editableNode) => {
|
|
|
154
151
|
const clickX = event.clientX;
|
|
155
152
|
const clickY = event.clientY;
|
|
156
153
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
157
|
-
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
|
|
154
|
+
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
|
|
158
155
|
if (editableNode && candidates.includes(editableNode)) {
|
|
159
156
|
return editableNode;
|
|
160
157
|
}
|
|
@@ -195,9 +192,8 @@ const setupEventListener$1 = (nodeProvider, onNodeSelected, onEscapePressed, get
|
|
|
195
192
|
handleNodeClick(event, nodeProvider, getEditableNode(), onNodeSelected);
|
|
196
193
|
};
|
|
197
194
|
const documentKeydownHandler = (event) => {
|
|
195
|
+
console.log("Esacpe Handler", event);
|
|
198
196
|
if (event.key === "Escape") {
|
|
199
|
-
event.preventDefault();
|
|
200
|
-
event.stopPropagation();
|
|
201
197
|
onEscapePressed?.();
|
|
202
198
|
}
|
|
203
199
|
};
|
|
@@ -302,14 +298,22 @@ const refreshHighlightFrame = (node, nodeProvider) => {
|
|
|
302
298
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
303
299
|
};
|
|
304
300
|
|
|
305
|
-
const
|
|
306
|
-
const
|
|
307
|
-
|
|
301
|
+
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
302
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
303
|
+
if (!frame)
|
|
304
|
+
return;
|
|
305
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
306
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
const disableCanvasTextMode = () => {
|
|
310
|
+
const disableTextEditMode = getCanvasWindowValue(["keyboard", "disableTextEditMode"]);
|
|
311
|
+
disableTextEditMode?.();
|
|
308
312
|
};
|
|
309
313
|
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
|
|
314
|
+
const enableCanvasTextMode = () => {
|
|
315
|
+
const enableTextEditMode = getCanvasWindowValue(["keyboard", "enableTextEditMode"]);
|
|
316
|
+
enableTextEditMode?.();
|
|
313
317
|
};
|
|
314
318
|
|
|
315
319
|
const insertLineBreak = () => {
|
|
@@ -405,7 +409,7 @@ const nodeText = () => {
|
|
|
405
409
|
if (editable) {
|
|
406
410
|
editableNode = node;
|
|
407
411
|
makeNodeEditable(node);
|
|
408
|
-
|
|
412
|
+
enableCanvasTextMode();
|
|
409
413
|
cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
|
|
410
414
|
}
|
|
411
415
|
};
|
|
@@ -422,7 +426,7 @@ const nodeText = () => {
|
|
|
422
426
|
blurInProgress = true;
|
|
423
427
|
const nodeToCleanup = editableNode;
|
|
424
428
|
makeNodeNonEditable(nodeToCleanup);
|
|
425
|
-
|
|
429
|
+
disableCanvasTextMode();
|
|
426
430
|
cleanup?.();
|
|
427
431
|
editableNode = null;
|
|
428
432
|
blurInProgress = false;
|
|
@@ -438,6 +442,7 @@ const nodeText = () => {
|
|
|
438
442
|
const createNodeTools = (element) => {
|
|
439
443
|
const nodeProvider = element;
|
|
440
444
|
let resizeObserver = null;
|
|
445
|
+
let mutationObserver = null;
|
|
441
446
|
let selectedNode = null;
|
|
442
447
|
const text = nodeText();
|
|
443
448
|
const throttledFrameRefresh = withRAFThrottle(refreshHighlightFrame);
|
|
@@ -450,10 +455,14 @@ const createNodeTools = (element) => {
|
|
|
450
455
|
clearHighlightFrame(nodeProvider);
|
|
451
456
|
selectedNode = null;
|
|
452
457
|
resizeObserver?.disconnect();
|
|
458
|
+
mutationObserver?.disconnect();
|
|
453
459
|
}
|
|
454
460
|
}
|
|
455
461
|
};
|
|
456
462
|
const selectNode = (node) => {
|
|
463
|
+
if (selectedNode === node) {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
457
466
|
if (text.isEditing()) {
|
|
458
467
|
const currentEditable = text.getEditableNode();
|
|
459
468
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -461,21 +470,34 @@ const createNodeTools = (element) => {
|
|
|
461
470
|
}
|
|
462
471
|
}
|
|
463
472
|
resizeObserver?.disconnect();
|
|
473
|
+
mutationObserver?.disconnect();
|
|
464
474
|
if (node && nodeProvider) {
|
|
465
475
|
text.enableEditMode(node, nodeProvider);
|
|
466
476
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
467
477
|
throttledFrameRefresh(node, nodeProvider);
|
|
468
478
|
});
|
|
479
|
+
mutationObserver = new MutationObserver(() => {
|
|
480
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
481
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
482
|
+
});
|
|
483
|
+
mutationObserver.observe(node, {
|
|
484
|
+
attributes: true,
|
|
485
|
+
attributeFilter: ["class"],
|
|
486
|
+
});
|
|
469
487
|
}
|
|
470
488
|
selectedNode = node;
|
|
471
489
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
472
490
|
highlightNode(node, nodeProvider) ?? null;
|
|
491
|
+
if (node && nodeProvider) {
|
|
492
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
493
|
+
}
|
|
473
494
|
};
|
|
474
495
|
// Setup event listener
|
|
475
496
|
const removeListeners = setupEventListener$1(nodeProvider, selectNode, handleEscape, text.getEditableNode);
|
|
476
497
|
const cleanup = () => {
|
|
477
498
|
removeListeners();
|
|
478
499
|
resizeObserver?.disconnect();
|
|
500
|
+
mutationObserver?.disconnect();
|
|
479
501
|
text.blurEditMode();
|
|
480
502
|
throttledFrameRefresh.cleanup();
|
|
481
503
|
};
|
|
@@ -489,6 +511,7 @@ const createNodeTools = (element) => {
|
|
|
489
511
|
clearHighlightFrame(nodeProvider);
|
|
490
512
|
selectedNode = null;
|
|
491
513
|
resizeObserver?.disconnect();
|
|
514
|
+
mutationObserver?.disconnect();
|
|
492
515
|
},
|
|
493
516
|
getEditableNode: () => text.getEditableNode(),
|
|
494
517
|
cleanup,
|
|
@@ -583,9 +606,7 @@ const calcWidth = (event, startX, startWidth) => {
|
|
|
583
606
|
};
|
|
584
607
|
|
|
585
608
|
const updateActivePreset = (container, width) => {
|
|
586
|
-
console.log("updateActivePreset", width);
|
|
587
609
|
const presetButtons = container.querySelectorAll(".resize-preset-button");
|
|
588
|
-
console.log("presetButtons", presetButtons);
|
|
589
610
|
presetButtons.forEach((button, index) => {
|
|
590
611
|
if (index < RESIZE_PRESETS.length) {
|
|
591
612
|
const preset = RESIZE_PRESETS[index];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Markup Canvas
|
|
3
3
|
* High-performance markup canvas with zoom and pan capabilities
|
|
4
|
-
* @version 2.0.
|
|
4
|
+
* @version 2.0.7
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -104,17 +104,14 @@
|
|
|
104
104
|
const processPostMessage = (event, onNodeSelected) => {
|
|
105
105
|
if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
106
106
|
if (event.data.action === "zoom") {
|
|
107
|
-
|
|
108
|
-
console.log("zoom", zoom);
|
|
107
|
+
event.data.data;
|
|
109
108
|
}
|
|
110
109
|
}
|
|
111
110
|
if (event.data.source === "application") {
|
|
112
111
|
if (event.data.action === "selectedNodeChanged") {
|
|
113
112
|
const nodeId = event.data.data;
|
|
114
|
-
console.log("selectedNodeChanged in node-edit-utils", nodeId);
|
|
115
113
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
116
114
|
if (selectedNode) {
|
|
117
|
-
console.log("selectedNode", selectedNode);
|
|
118
115
|
onNodeSelected?.(selectedNode);
|
|
119
116
|
}
|
|
120
117
|
}
|
|
@@ -160,7 +157,7 @@
|
|
|
160
157
|
const clickX = event.clientX;
|
|
161
158
|
const clickY = event.clientY;
|
|
162
159
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
163
|
-
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
|
|
160
|
+
const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
|
|
164
161
|
if (editableNode && candidates.includes(editableNode)) {
|
|
165
162
|
return editableNode;
|
|
166
163
|
}
|
|
@@ -201,9 +198,8 @@
|
|
|
201
198
|
handleNodeClick(event, nodeProvider, getEditableNode(), onNodeSelected);
|
|
202
199
|
};
|
|
203
200
|
const documentKeydownHandler = (event) => {
|
|
201
|
+
console.log("Esacpe Handler", event);
|
|
204
202
|
if (event.key === "Escape") {
|
|
205
|
-
event.preventDefault();
|
|
206
|
-
event.stopPropagation();
|
|
207
203
|
onEscapePressed?.();
|
|
208
204
|
}
|
|
209
205
|
};
|
|
@@ -308,14 +304,22 @@
|
|
|
308
304
|
frame.style.setProperty("--frame-height", `${height}px`);
|
|
309
305
|
};
|
|
310
306
|
|
|
311
|
-
const
|
|
312
|
-
const
|
|
313
|
-
|
|
307
|
+
const updateHighlightFrameVisibility = (node, nodeProvider) => {
|
|
308
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
309
|
+
if (!frame)
|
|
310
|
+
return;
|
|
311
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
312
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const disableCanvasTextMode = () => {
|
|
316
|
+
const disableTextEditMode = getCanvasWindowValue(["keyboard", "disableTextEditMode"]);
|
|
317
|
+
disableTextEditMode?.();
|
|
314
318
|
};
|
|
315
319
|
|
|
316
|
-
const
|
|
317
|
-
const
|
|
318
|
-
|
|
320
|
+
const enableCanvasTextMode = () => {
|
|
321
|
+
const enableTextEditMode = getCanvasWindowValue(["keyboard", "enableTextEditMode"]);
|
|
322
|
+
enableTextEditMode?.();
|
|
319
323
|
};
|
|
320
324
|
|
|
321
325
|
const insertLineBreak = () => {
|
|
@@ -411,7 +415,7 @@
|
|
|
411
415
|
if (editable) {
|
|
412
416
|
editableNode = node;
|
|
413
417
|
makeNodeEditable(node);
|
|
414
|
-
|
|
418
|
+
enableCanvasTextMode();
|
|
415
419
|
cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
|
|
416
420
|
}
|
|
417
421
|
};
|
|
@@ -428,7 +432,7 @@
|
|
|
428
432
|
blurInProgress = true;
|
|
429
433
|
const nodeToCleanup = editableNode;
|
|
430
434
|
makeNodeNonEditable(nodeToCleanup);
|
|
431
|
-
|
|
435
|
+
disableCanvasTextMode();
|
|
432
436
|
cleanup?.();
|
|
433
437
|
editableNode = null;
|
|
434
438
|
blurInProgress = false;
|
|
@@ -444,6 +448,7 @@
|
|
|
444
448
|
const createNodeTools = (element) => {
|
|
445
449
|
const nodeProvider = element;
|
|
446
450
|
let resizeObserver = null;
|
|
451
|
+
let mutationObserver = null;
|
|
447
452
|
let selectedNode = null;
|
|
448
453
|
const text = nodeText();
|
|
449
454
|
const throttledFrameRefresh = withRAFThrottle(refreshHighlightFrame);
|
|
@@ -456,10 +461,14 @@
|
|
|
456
461
|
clearHighlightFrame(nodeProvider);
|
|
457
462
|
selectedNode = null;
|
|
458
463
|
resizeObserver?.disconnect();
|
|
464
|
+
mutationObserver?.disconnect();
|
|
459
465
|
}
|
|
460
466
|
}
|
|
461
467
|
};
|
|
462
468
|
const selectNode = (node) => {
|
|
469
|
+
if (selectedNode === node) {
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
463
472
|
if (text.isEditing()) {
|
|
464
473
|
const currentEditable = text.getEditableNode();
|
|
465
474
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -467,21 +476,34 @@
|
|
|
467
476
|
}
|
|
468
477
|
}
|
|
469
478
|
resizeObserver?.disconnect();
|
|
479
|
+
mutationObserver?.disconnect();
|
|
470
480
|
if (node && nodeProvider) {
|
|
471
481
|
text.enableEditMode(node, nodeProvider);
|
|
472
482
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
473
483
|
throttledFrameRefresh(node, nodeProvider);
|
|
474
484
|
});
|
|
485
|
+
mutationObserver = new MutationObserver(() => {
|
|
486
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
487
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
488
|
+
});
|
|
489
|
+
mutationObserver.observe(node, {
|
|
490
|
+
attributes: true,
|
|
491
|
+
attributeFilter: ["class"],
|
|
492
|
+
});
|
|
475
493
|
}
|
|
476
494
|
selectedNode = node;
|
|
477
495
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
478
496
|
highlightNode(node, nodeProvider) ?? null;
|
|
497
|
+
if (node && nodeProvider) {
|
|
498
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
499
|
+
}
|
|
479
500
|
};
|
|
480
501
|
// Setup event listener
|
|
481
502
|
const removeListeners = setupEventListener$1(nodeProvider, selectNode, handleEscape, text.getEditableNode);
|
|
482
503
|
const cleanup = () => {
|
|
483
504
|
removeListeners();
|
|
484
505
|
resizeObserver?.disconnect();
|
|
506
|
+
mutationObserver?.disconnect();
|
|
485
507
|
text.blurEditMode();
|
|
486
508
|
throttledFrameRefresh.cleanup();
|
|
487
509
|
};
|
|
@@ -495,6 +517,7 @@
|
|
|
495
517
|
clearHighlightFrame(nodeProvider);
|
|
496
518
|
selectedNode = null;
|
|
497
519
|
resizeObserver?.disconnect();
|
|
520
|
+
mutationObserver?.disconnect();
|
|
498
521
|
},
|
|
499
522
|
getEditableNode: () => text.getEditableNode(),
|
|
500
523
|
cleanup,
|
|
@@ -589,9 +612,7 @@
|
|
|
589
612
|
};
|
|
590
613
|
|
|
591
614
|
const updateActivePreset = (container, width) => {
|
|
592
|
-
console.log("updateActivePreset", width);
|
|
593
615
|
const presetButtons = container.querySelectorAll(".resize-preset-button");
|
|
594
|
-
console.log("presetButtons", presetButtons);
|
|
595
616
|
presetButtons.forEach((button, index) => {
|
|
596
617
|
if (index < RESIZE_PRESETS.length) {
|
|
597
618
|
const preset = RESIZE_PRESETS[index];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas={})}(this,function(e){"use strict";function t(e){let t=null,n=null;const o=(...o)=>{n=o,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return o.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},o}const n=e=>{const t=window.canvas;return e.reduce((e,t)=>e?.[t],t)};function o(e){return e.querySelector(".highlight-frame")}const r=e=>{if(!e)return;const t=o(e);t&&t.remove()},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).MarkupCanvas={})}(this,function(e){"use strict";function t(e){let t=null,n=null;const o=(...o)=>{n=o,null===t&&(t=requestAnimationFrame(()=>{n&&e(...n),t=null,n=null}))};return o.cleanup=()=>{null!==t&&(cancelAnimationFrame(t),t=null,n=null)},o}const n=e=>{const t=window.canvas;return e.reduce((e,t)=>e?.[t],t)};function o(e){return e.querySelector(".highlight-frame")}const r=e=>{if(!e)return;const t=o(e);t&&t.remove()},s=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let a=[],i=0;const d=(e,t)=>{let n=null;const o=e.clientX,r=e.clientY,d=e.metaKey||e.ctrlKey,l=((e,t)=>{const n=document.elementsFromPoint(e,t);return Array.from(n).reduce((e,t)=>e.found?e:"node-provider"===t.getAttribute("data-role")?(e.found=!0,e):(e.elements.push(t),e),{elements:[],found:!1}).elements})(o,r).filter(e=>!s.includes(e.tagName.toLowerCase())&&!e.classList.contains("select-none"));if(t&&l.includes(t))return t;if(d)return a=[],n=l[0],n;var c,u;u=l,(c=a).length===u.length&&c.every((e,t)=>e===u[t])?i<=l.length&&i++:i=0;return n=l[l.length-1-i],a=l,n},l=(e,t,n,o)=>{const s=e=>{((e,t)=>{if("markup-canvas"===e.data.source&&"canvas"===e.data.canvasName&&"zoom"===e.data.action&&e.data.data,"application"===e.data.source&&"selectedNodeChanged"===e.data.action){const n=e.data.data,o=document.querySelector(`[data-node-id="${n}"]`);o&&t?.(o)}})(e,t)},a=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(t),void o(null);o(d(e,n))})(n,e,o(),t)},i=e=>{console.log("Esacpe Handler",e),"Escape"===e.key&&n?.()};return window.addEventListener("message",s),document.addEventListener("click",a),document.addEventListener("keydown",i),()=>{window.removeEventListener("message",s),document.removeEventListener("click",a),document.removeEventListener("keydown",i)}};function c(e,t){const o=e.getBoundingClientRect(),r=t.getBoundingClientRect(),s=o.top-r.top,a=o.left-r.left,i=n(["zoom","current"])??1;return{top:parseFloat((s/i).toFixed(5)),left:parseFloat((a/i).toFixed(5)),width:Math.max(4,parseFloat((o.width/i).toFixed(5))),height:parseFloat((o.height/i).toFixed(5))}}const u=(e,t)=>{const n=document.createElement("div");n.className="node-tools",t.appendChild(n),((e,t)=>{const n=document.createElement("div");n.className="tag-label",n.textContent=e.tagName.toLowerCase(),t.appendChild(n)})(e,n)},m=(e,t)=>{if(!e)return;const r=o(t);r&&r.remove();const s=((e,t)=>{const{top:o,left:r,width:s,height:a}=c(e,t),i=n(["zoom","current"])??1;document.body.style.setProperty("--zoom",i.toString()),document.body.style.setProperty("--stroke-width",(2/i).toFixed(3));const d=document.createElement("div");d.classList.add("highlight-frame"),d.style.setProperty("--frame-top",`${o}px`),d.style.setProperty("--frame-left",`${r}px`),d.style.setProperty("--frame-width",`${s}px`),d.style.setProperty("--frame-height",`${a}px`);const l=document.createElementNS("http://www.w3.org/2000/svg","svg");l.classList.add("highlight-frame-svg");const u=document.createElementNS("http://www.w3.org/2000/svg","rect");return u.setAttribute("x","0"),u.setAttribute("y","0"),u.setAttribute("width","100%"),u.setAttribute("height","100%"),u.classList.add("highlight-frame-rect"),l.appendChild(u),d.appendChild(l),d})(e,t);"true"===e.contentEditable&&s.classList.add("is-editable"),u(e,s),t.appendChild(s)},p=(e,t)=>{const r=o(t),s=n(["zoom","current"])??1;if(!r)return;s>=.3?t.style.setProperty("--tool-opacity","1"):t.style.setProperty("--tool-opacity","0");const{top:a,left:i,width:d,height:l}=c(e,t);r.style.setProperty("--frame-top",`${a}px`),r.style.setProperty("--frame-left",`${i}px`),r.style.setProperty("--frame-width",`${d}px`),r.style.setProperty("--frame-height",`${l}px`)},v=(e,t)=>{const n=o(t);if(!n)return;const r=e.classList.contains("hidden")||e.classList.contains("select-none");n.style.display=r?"none":""},h=e=>{const t=e=>{"Enter"===e.key&&(e.preventDefault(),e.stopPropagation(),(()=>{const e=window.getSelection();if(e&&e.rangeCount>0){const t=e.getRangeAt(0);t.deleteContents();const n=document.createElement("br");t.insertNode(n),t.setStartAfter(n),t.setEndAfter(n),e.removeAllRanges(),e.addRange(t)}})())};return e.addEventListener("keydown",t),()=>{e.removeEventListener("keydown",t)}},y=(e,t)=>{const n=((e,t)=>{const n=new MutationObserver(e=>{t(e)});return n.observe(e,{subtree:!0,childList:!0,characterData:!0}),n})(e,()=>{p(e,t)});return()=>n.disconnect()},f=()=>{let e=null,t=!1,o=null;const r=()=>{if(t||!e)return;t=!0;var r;(r=e).contentEditable="false",r.classList.remove("is-editable"),r.style.outline="none",(()=>{const e=n(["keyboard","disableTextEditMode"]);e?.()})(),o?.(),e=null,t=!1};return{enableEditMode:(t,s)=>{if(e===t)return;e&&e!==t&&r();const a=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(t);a&&(e=t,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(t),(()=>{const e=n(["keyboard","enableTextEditMode"]);e?.()})(),o=((e,t,n)=>{if(!t)return()=>{};e.addEventListener("blur",n);const o=h(e),r=y(e,t);return()=>{e.removeEventListener("blur",n),o(),r?.()}})(t,s,r))},blurEditMode:r,getEditableNode:()=>e,isEditing:()=>null!==e}},g=320,b=1680,w=[{name:"Mobile",rawValue:390,value:"320px"},{name:"Tablet Portrait",rawValue:768,value:"768px"},{name:"Tablet Landscape",rawValue:1024,value:"1024px"},{name:"Notebook",rawValue:1280,value:"1280px"},{name:"Desktop",rawValue:1680,value:"1680px"}],E=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(g,Math.min(b,n))})(n,(e.clientX-t)/o);return r},x=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<w.length&&(w[n].rawValue===t?e.classList.add("is-active"):e.classList.remove("is-active"))})})(e,t)};e.createCanvasObserver=function(){const e=document.querySelector(".transform-layer");if(!e)return{disconnect:()=>{}};const o=t(()=>{(()=>{const e=n(["zoom","current"]);document.body.style.setProperty("--zoom",e.toFixed(5)),document.body.style.setProperty("--stroke-width",(2/e).toFixed(3)),document.body.dataset.zoom=e.toFixed(5),document.body.dataset.strokeWidth=(2/e).toFixed(3)})()}),r=new MutationObserver(()=>{o()});return r.observe(e,{attributes:!0,attributeOldValue:!0,subtree:!0,childList:!0}),{disconnect:function(){o.cleanup(),r.disconnect()}}},e.createNodeTools=e=>{const n=e;let o=null,s=null,a=null;const i=f(),d=t(p),c=e=>{if(a!==e){if(i.isEditing()){const t=i.getEditableNode();t&&t!==e&&i.blurEditMode()}var t,r;o?.disconnect(),s?.disconnect(),e&&n&&(i.enableEditMode(e,n),o=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(n,()=>{d(e,n)}),s=new MutationObserver(()=>{d(e,n),v(e,n)}),s.observe(e,{attributes:!0,attributeFilter:["class"]})),a=e,t="selectedNodeChanged",r=e?.getAttribute("data-node-id")??null,window.parent.postMessage({source:"node-edit-utils",action:t,data:r,timestamp:Date.now()},"*"),m(e,n),e&&n&&v(e,n)}},u=l(n,c,()=>{i.isEditing()&&i.blurEditMode(),a&&n&&(r(n),a=null,o?.disconnect(),s?.disconnect())},i.getEditableNode),h={selectNode:c,getSelectedNode:()=>a,refreshHighlightFrame:()=>{d(a,n)},clearSelectedNode:()=>{r(n),a=null,o?.disconnect(),s?.disconnect()},getEditableNode:()=>i.getEditableNode(),cleanup:()=>{u(),o?.disconnect(),s?.disconnect(),i.blurEditMode(),d.cleanup()}};var y,g;return y="nodeTools",g=h,"undefined"!=typeof window&&(window[y]=g),h},e.createViewport=e=>{const n=document.querySelector(".canvas-container"),o=e.querySelector(".resize-handle");o&&o.remove();const r=(e=>{const t=document.createElement("div");return t.className="resize-handle",e.appendChild(t),t})(e);e.style.setProperty("--container-width","400px"),((e,t,n)=>{const o=document.createElement("div");o.className="resize-presets",w.forEach(e=>{const r=document.createElement("button");r.textContent=e.name,r.className="resize-preset-button",r.addEventListener("click",()=>{n(t,e.rawValue)}),o.appendChild(r)}),e.appendChild(o)})(r,e,x);let s=!1,a=0,i=0;const d=t(t=>{if(!s)return;n&&(n.style.cursor="ew-resize");const o=E(t,a,i);x(e,o)}),l=((e,t,n,o,r)=>(e.addEventListener("mousedown",t),document.addEventListener("mousemove",n),document.addEventListener("mouseup",o),window.addEventListener("blur",r),()=>{e.removeEventListener("mousedown",t),document.removeEventListener("mousemove",n),document.removeEventListener("mouseup",o),window.removeEventListener("blur",r)}))(r,t=>{t.preventDefault(),t.stopPropagation(),s=!0,a=t.clientX,i=e.offsetWidth},d,e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{x(e,t)},cleanup:()=>{s=!1,d?.cleanup(),l(),r.remove()}}}});
|
package/dist/styles.css
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;display:flex;gap
|
|
1
|
+
:root{--primary-color:oklch(0.6235 0.22 294);--uncode-color:oklch(45.7% 0.24 277.023);--handle-color:oklch(55.2% 0.016 285.938);--handle-color-transparent:oklch(55.2% 0.016 285.938/0.7);--primary-color-selection:oklch(0.59 0.18 294/0.3);--text-color-white:#fff;--spacing-xs:0.25rem;--spacing-sm:0.375rem;--spacing-md:0.5rem;--spacing-lg:1.25rem;--transition-fast:0.1s ease-in-out;--transition-medium:0.2s ease-in-out;--z-index-high:10000;--z-index-medium:1000;--letter-spacing:0.02em;--font-family-primary:"Manrope",sans-serif}.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;display:flex;gap:var(--spacing-xs);justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:var(--spacing-xs);position:absolute;top:0;transform:scale(calc(1/var(--zoom))) translate3d(0,100%,0);transform-origin:bottom left;transition:opacity var(--transition-fast);z-index:var(--z-index-high)}.highlight-frame{font-family:var(--font-family-primary);height:var(--frame-height);left:var(--frame-left);letter-spacing:var(--letter-spacing);pointer-events:none;position:absolute;top:var(--frame-top);width:var(--frame-width);z-index:var(--z-index-medium)}.highlight-frame-svg{height:100%;left:0;overflow:visible;position:absolute;top:0;width:100%}.highlight-frame-rect{fill:none;stroke:var(--primary-color);stroke-width:var(--stroke-width)}.tag-label{align-items:center;background-color:var(--primary-color);border-radius:var(--spacing-sm);color:var(--text-color-white);display:flex;font-size:.575rem;font-weight:500;height:1rem;justify-content:center;line-height:1;padding:.5625rem var(--spacing-sm);text-transform:uppercase}.viewport{position:relative;width:var(--container-width)}.resize-handle{align-items:center;cursor:ew-resize;display:flex;height:40px;justify-content:center;opacity:0;pointer-events:auto;position:absolute;right:-12px;top:var(--spacing-lg);transform:scale(calc(1/var(--zoom)));transform-origin:top left;transition:opacity var(--transition-fast),visibility var(--transition-fast);visibility:hidden;width:12px;z-index:var(--z-index-high)}.viewport:hover .resize-handle{opacity:1;visibility:visible}.resize-handle:before{background:var(--handle-color);border-radius:4px;content:"";height:32px;position:relative;right:0;top:0;transition:transform var(--transition-medium);width:3px}.resize-handle:hover:before{transform:scaleY(1.3)}.resize-presets{display:flex;flex-direction:column;font-family:var(--font-family-primary);gap:var(--spacing-xs);left:0;letter-spacing:var(--letter-spacing);opacity:0;padding-left:var(--spacing-lg);position:absolute;top:0;transition:visibility var(--transition-fast),opacity var(--transition-fast);visibility:hidden}.resize-handle:hover .resize-presets{opacity:1;visibility:visible}.resize-preset-button{text-wrap:nowrap;backdrop-filter:blur(8px);background:var(--handle-color-transparent);border:none;border-radius:var(--spacing-md);color:var(--text-color-white);cursor:pointer;font-size:.625rem;padding:var(--spacing-xs) var(--spacing-md);text-align:left;transition:background var(--transition-fast);width:fit-content;&:hover{background:var(--handle-color)}&.is-active{background:var(--uncode-color)}}.is-editable{outline:none;user-select:text;&::selection{background:var(--primary-color-selection)}&::-moz-selection{background:var(--primary-color-selection)}&::-webkit-selection{background:var(--primary-color-selection)}}
|
package/package.json
CHANGED
|
@@ -6,6 +6,7 @@ import { setupEventListener } from "./events/setupEventListener";
|
|
|
6
6
|
import { clearHighlightFrame } from "./highlight/clearHighlightFrame";
|
|
7
7
|
import { highlightNode } from "./highlight/highlightNode";
|
|
8
8
|
import { refreshHighlightFrame } from "./highlight/refreshHighlightFrame";
|
|
9
|
+
import { updateHighlightFrameVisibility } from "./highlight/updateHighlightFrameVisibility";
|
|
9
10
|
import { nodeText } from "./text/nodeText";
|
|
10
11
|
import type { NodeTools } from "./types";
|
|
11
12
|
|
|
@@ -13,6 +14,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
13
14
|
const nodeProvider = element;
|
|
14
15
|
|
|
15
16
|
let resizeObserver: ResizeObserver | null = null;
|
|
17
|
+
let mutationObserver: MutationObserver | null = null;
|
|
16
18
|
let selectedNode: HTMLElement | null = null;
|
|
17
19
|
|
|
18
20
|
const text = nodeText();
|
|
@@ -29,11 +31,16 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
29
31
|
selectedNode = null;
|
|
30
32
|
|
|
31
33
|
resizeObserver?.disconnect();
|
|
34
|
+
mutationObserver?.disconnect();
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
};
|
|
35
38
|
|
|
36
39
|
const selectNode = (node: HTMLElement | null): void => {
|
|
40
|
+
if (selectedNode === node) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
37
44
|
if (text.isEditing()) {
|
|
38
45
|
const currentEditable = text.getEditableNode();
|
|
39
46
|
if (currentEditable && currentEditable !== node) {
|
|
@@ -42,6 +49,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
42
49
|
}
|
|
43
50
|
|
|
44
51
|
resizeObserver?.disconnect();
|
|
52
|
+
mutationObserver?.disconnect();
|
|
45
53
|
|
|
46
54
|
if (node && nodeProvider) {
|
|
47
55
|
text.enableEditMode(node, nodeProvider);
|
|
@@ -49,11 +57,25 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
49
57
|
resizeObserver = connectResizeObserver(nodeProvider, () => {
|
|
50
58
|
throttledFrameRefresh(node, nodeProvider);
|
|
51
59
|
});
|
|
60
|
+
|
|
61
|
+
mutationObserver = new MutationObserver(() => {
|
|
62
|
+
throttledFrameRefresh(node, nodeProvider);
|
|
63
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
mutationObserver.observe(node, {
|
|
67
|
+
attributes: true,
|
|
68
|
+
attributeFilter: ["class"],
|
|
69
|
+
});
|
|
52
70
|
}
|
|
53
71
|
|
|
54
72
|
selectedNode = node;
|
|
55
73
|
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
56
74
|
highlightNode(node, nodeProvider as HTMLElement) ?? null;
|
|
75
|
+
|
|
76
|
+
if (node && nodeProvider) {
|
|
77
|
+
updateHighlightFrameVisibility(node, nodeProvider);
|
|
78
|
+
}
|
|
57
79
|
};
|
|
58
80
|
|
|
59
81
|
// Setup event listener
|
|
@@ -62,6 +84,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
62
84
|
const cleanup = (): void => {
|
|
63
85
|
removeListeners();
|
|
64
86
|
resizeObserver?.disconnect();
|
|
87
|
+
mutationObserver?.disconnect();
|
|
65
88
|
|
|
66
89
|
text.blurEditMode();
|
|
67
90
|
throttledFrameRefresh.cleanup();
|
|
@@ -77,6 +100,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
77
100
|
clearHighlightFrame(nodeProvider);
|
|
78
101
|
selectedNode = null;
|
|
79
102
|
resizeObserver?.disconnect();
|
|
103
|
+
mutationObserver?.disconnect();
|
|
80
104
|
},
|
|
81
105
|
getEditableNode: () => text.getEditableNode(),
|
|
82
106
|
cleanup,
|
|
@@ -16,9 +16,9 @@ export const setupEventListener = (
|
|
|
16
16
|
};
|
|
17
17
|
|
|
18
18
|
const documentKeydownHandler = (event: KeyboardEvent) => {
|
|
19
|
+
console.log("Esacpe Handler", event);
|
|
20
|
+
|
|
19
21
|
if (event.key === "Escape") {
|
|
20
|
-
event.preventDefault();
|
|
21
|
-
event.stopPropagation();
|
|
22
22
|
onEscapePressed?.();
|
|
23
23
|
}
|
|
24
24
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { getHighlightFrameElement } from "./helpers/getHighlightFrameElement";
|
|
2
|
+
|
|
3
|
+
export const updateHighlightFrameVisibility = (node: HTMLElement, nodeProvider: HTMLElement): void => {
|
|
4
|
+
const frame = getHighlightFrameElement(nodeProvider);
|
|
5
|
+
if (!frame) return;
|
|
6
|
+
|
|
7
|
+
const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
|
|
8
|
+
frame.style.display = hasHiddenClass ? "none" : "";
|
|
9
|
+
};
|
|
10
|
+
|
|
@@ -14,7 +14,7 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
|
|
|
14
14
|
const clickThrough = event.metaKey || event.ctrlKey;
|
|
15
15
|
|
|
16
16
|
const candidates = getElementsFromPoint(clickX, clickY).filter(
|
|
17
|
-
(element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase())
|
|
17
|
+
(element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none")
|
|
18
18
|
);
|
|
19
19
|
|
|
20
20
|
if (editableNode && candidates.includes(editableNode)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { disableCanvasTextMode } from "../../canvas/disableCanvasTextMode";
|
|
2
|
+
import { enableCanvasTextMode } from "../../canvas/enableCanvasTextMode";
|
|
3
3
|
import { setupNodeListeners } from "./events/setupNodeListeners";
|
|
4
4
|
import { hasTextContent } from "./helpers/hasTextContent";
|
|
5
5
|
import { makeNodeEditable } from "./helpers/makeNodeEditable";
|
|
@@ -26,7 +26,7 @@ export const nodeText = (): NodeText => {
|
|
|
26
26
|
editableNode = node;
|
|
27
27
|
|
|
28
28
|
makeNodeEditable(node);
|
|
29
|
-
|
|
29
|
+
enableCanvasTextMode();
|
|
30
30
|
|
|
31
31
|
cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
|
|
32
32
|
}
|
|
@@ -50,8 +50,7 @@ export const nodeText = (): NodeText => {
|
|
|
50
50
|
const nodeToCleanup = editableNode;
|
|
51
51
|
|
|
52
52
|
makeNodeNonEditable(nodeToCleanup);
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
disableCanvasTextMode();
|
|
55
54
|
cleanup?.();
|
|
56
55
|
|
|
57
56
|
editableNode = null;
|
|
@@ -2,18 +2,15 @@ export const processPostMessage = (event: MessageEvent, onNodeSelected?: (node:
|
|
|
2
2
|
if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
|
|
3
3
|
if (event.data.action === "zoom") {
|
|
4
4
|
const zoom = event.data.data;
|
|
5
|
-
console.log("zoom", zoom);
|
|
6
5
|
}
|
|
7
6
|
}
|
|
8
7
|
|
|
9
8
|
if (event.data.source === "application") {
|
|
10
9
|
if (event.data.action === "selectedNodeChanged") {
|
|
11
10
|
const nodeId = event.data.data;
|
|
12
|
-
console.log("selectedNodeChanged in node-edit-utils", nodeId);
|
|
13
11
|
const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
|
|
14
12
|
|
|
15
13
|
if (selectedNode) {
|
|
16
|
-
console.log("selectedNode", selectedNode);
|
|
17
14
|
onNodeSelected?.(selectedNode as HTMLElement);
|
|
18
15
|
}
|
|
19
16
|
}
|
|
@@ -1,3 +1,28 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--primary-color: oklch(0.6235 0.22 294);
|
|
3
|
+
--uncode-color: oklch(45.7% 0.24 277.023);
|
|
4
|
+
|
|
5
|
+
--handle-color: oklch(55.2% 0.016 285.938);
|
|
6
|
+
--handle-color-transparent: oklch(55.2% 0.016 285.938 / 0.7);
|
|
7
|
+
--primary-color-selection: oklch(0.59 0.18 294 / 0.3);
|
|
8
|
+
|
|
9
|
+
--text-color-white: white;
|
|
10
|
+
|
|
11
|
+
--spacing-xs: 0.25rem;
|
|
12
|
+
--spacing-sm: 0.375rem;
|
|
13
|
+
--spacing-md: 0.5rem;
|
|
14
|
+
--spacing-lg: 1.25rem;
|
|
15
|
+
|
|
16
|
+
--transition-fast: 0.1s ease-in-out;
|
|
17
|
+
--transition-medium: 0.2s ease-in-out;
|
|
18
|
+
|
|
19
|
+
--z-index-high: 10000;
|
|
20
|
+
--z-index-medium: 1000;
|
|
21
|
+
|
|
22
|
+
--letter-spacing: 0.02em;
|
|
23
|
+
--font-family-primary: "Manrope", sans-serif;
|
|
24
|
+
}
|
|
25
|
+
|
|
1
26
|
.node-provider {
|
|
2
27
|
::selection {
|
|
3
28
|
background: transparent;
|
|
@@ -16,13 +41,14 @@
|
|
|
16
41
|
position: absolute;
|
|
17
42
|
left: 0;
|
|
18
43
|
bottom: 0;
|
|
19
|
-
|
|
20
|
-
|
|
44
|
+
top: 0;
|
|
45
|
+
gap: var(--spacing-xs);
|
|
46
|
+
z-index: var(--z-index-high);
|
|
21
47
|
transform: scale(calc(1 / var(--zoom))) translate3d(0, 100%, 0);
|
|
22
48
|
transform-origin: bottom left;
|
|
23
|
-
transition: opacity
|
|
49
|
+
transition: opacity var(--transition-fast);
|
|
24
50
|
opacity: var(--tool-opacity);
|
|
25
|
-
padding-top:
|
|
51
|
+
padding-top: var(--spacing-xs);
|
|
26
52
|
display: flex;
|
|
27
53
|
justify-content: center;
|
|
28
54
|
}
|
|
@@ -33,9 +59,10 @@
|
|
|
33
59
|
left: var(--frame-left);
|
|
34
60
|
width: var(--frame-width);
|
|
35
61
|
height: var(--frame-height);
|
|
36
|
-
z-index:
|
|
62
|
+
z-index: var(--z-index-medium);
|
|
37
63
|
pointer-events: none;
|
|
38
|
-
|
|
64
|
+
font-family: var(--font-family-primary);
|
|
65
|
+
letter-spacing: var(--letter-spacing);
|
|
39
66
|
}
|
|
40
67
|
|
|
41
68
|
.highlight-frame-svg {
|
|
@@ -49,20 +76,20 @@
|
|
|
49
76
|
|
|
50
77
|
.highlight-frame-rect {
|
|
51
78
|
fill: none;
|
|
52
|
-
stroke:
|
|
79
|
+
stroke: var(--primary-color);
|
|
53
80
|
stroke-width: var(--stroke-width);
|
|
54
81
|
}
|
|
55
82
|
|
|
56
83
|
.tag-label {
|
|
57
|
-
background-color:
|
|
58
|
-
color: white;
|
|
84
|
+
background-color: var(--primary-color);
|
|
85
|
+
color: var(--text-color-white);
|
|
59
86
|
font-size: 0.575rem;
|
|
60
87
|
font-weight: 500;
|
|
61
88
|
text-transform: uppercase;
|
|
62
|
-
height:
|
|
63
|
-
padding: 0
|
|
89
|
+
height: 1rem;
|
|
90
|
+
padding: 0.5625rem var(--spacing-sm);
|
|
64
91
|
line-height: 1;
|
|
65
|
-
border-radius:
|
|
92
|
+
border-radius: var(--spacing-sm);
|
|
66
93
|
display: flex;
|
|
67
94
|
align-items: center;
|
|
68
95
|
justify-content: center;
|
|
@@ -75,12 +102,12 @@
|
|
|
75
102
|
|
|
76
103
|
.resize-handle {
|
|
77
104
|
position: absolute;
|
|
78
|
-
top:
|
|
105
|
+
top: var(--spacing-lg);
|
|
79
106
|
right: -12px;
|
|
80
107
|
width: 12px;
|
|
81
108
|
height: 40px;
|
|
82
109
|
cursor: ew-resize;
|
|
83
|
-
z-index:
|
|
110
|
+
z-index: var(--z-index-high);
|
|
84
111
|
display: flex;
|
|
85
112
|
opacity: 0;
|
|
86
113
|
visibility: hidden;
|
|
@@ -90,8 +117,8 @@
|
|
|
90
117
|
transform: scale(calc(1 / var(--zoom)));
|
|
91
118
|
transform-origin: top left;
|
|
92
119
|
transition:
|
|
93
|
-
opacity
|
|
94
|
-
visibility
|
|
120
|
+
opacity var(--transition-fast),
|
|
121
|
+
visibility var(--transition-fast);
|
|
95
122
|
}
|
|
96
123
|
|
|
97
124
|
.viewport:hover .resize-handle {
|
|
@@ -106,9 +133,9 @@
|
|
|
106
133
|
right: 0;
|
|
107
134
|
width: 3px;
|
|
108
135
|
height: 32px;
|
|
109
|
-
background:
|
|
136
|
+
background: var(--handle-color);
|
|
110
137
|
border-radius: 4px;
|
|
111
|
-
transition: transform
|
|
138
|
+
transition: transform var(--transition-medium);
|
|
112
139
|
}
|
|
113
140
|
|
|
114
141
|
.resize-handle:hover::before {
|
|
@@ -119,15 +146,17 @@
|
|
|
119
146
|
position: absolute;
|
|
120
147
|
top: 0;
|
|
121
148
|
left: 0;
|
|
122
|
-
padding-left:
|
|
149
|
+
padding-left: var(--spacing-lg);
|
|
123
150
|
display: flex;
|
|
124
151
|
flex-direction: column;
|
|
125
|
-
gap:
|
|
152
|
+
gap: var(--spacing-xs);
|
|
126
153
|
visibility: hidden;
|
|
127
154
|
opacity: 0;
|
|
155
|
+
font-family: var(--font-family-primary);
|
|
156
|
+
letter-spacing: var(--letter-spacing);
|
|
128
157
|
transition:
|
|
129
|
-
visibility
|
|
130
|
-
opacity
|
|
158
|
+
visibility var(--transition-fast),
|
|
159
|
+
opacity var(--transition-fast);
|
|
131
160
|
}
|
|
132
161
|
|
|
133
162
|
.resize-handle:hover .resize-presets {
|
|
@@ -136,25 +165,25 @@
|
|
|
136
165
|
}
|
|
137
166
|
|
|
138
167
|
.resize-preset-button {
|
|
139
|
-
background:
|
|
168
|
+
background: var(--handle-color-transparent);
|
|
140
169
|
border: none;
|
|
141
170
|
cursor: pointer;
|
|
142
|
-
padding:
|
|
143
|
-
border-radius:
|
|
144
|
-
font-size: 0.
|
|
171
|
+
padding: var(--spacing-xs) var(--spacing-md);
|
|
172
|
+
border-radius: var(--spacing-md);
|
|
173
|
+
font-size: 0.625rem;
|
|
145
174
|
text-wrap: nowrap;
|
|
146
175
|
text-align: left;
|
|
147
176
|
backdrop-filter: blur(8px);
|
|
148
|
-
transition: background
|
|
149
|
-
color: white;
|
|
177
|
+
transition: background var(--transition-fast);
|
|
178
|
+
color: var(--text-color-white);
|
|
150
179
|
width: fit-content;
|
|
151
180
|
|
|
152
181
|
&:hover {
|
|
153
|
-
background:
|
|
182
|
+
background: var(--handle-color);
|
|
154
183
|
}
|
|
155
184
|
|
|
156
185
|
&.is-active {
|
|
157
|
-
background:
|
|
186
|
+
background: var(--uncode-color);
|
|
158
187
|
}
|
|
159
188
|
}
|
|
160
189
|
|
|
@@ -163,14 +192,14 @@
|
|
|
163
192
|
user-select: text;
|
|
164
193
|
|
|
165
194
|
&::selection {
|
|
166
|
-
background:
|
|
195
|
+
background: var(--primary-color-selection);
|
|
167
196
|
}
|
|
168
197
|
|
|
169
198
|
&::-moz-selection {
|
|
170
|
-
background:
|
|
199
|
+
background: var(--primary-color-selection);
|
|
171
200
|
}
|
|
172
201
|
|
|
173
202
|
&::-webkit-selection {
|
|
174
|
-
background:
|
|
203
|
+
background: var(--primary-color-selection);
|
|
175
204
|
}
|
|
176
205
|
}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { RESIZE_PRESETS } from "../constants";
|
|
2
2
|
|
|
3
3
|
export const updateActivePreset = (container: HTMLElement, width: number): void => {
|
|
4
|
-
console.log("updateActivePreset", width);
|
|
5
4
|
const presetButtons = container.querySelectorAll<HTMLButtonElement>(".resize-preset-button");
|
|
6
|
-
console.log("presetButtons", presetButtons);
|
|
7
5
|
presetButtons.forEach((button, index) => {
|
|
8
6
|
if (index < RESIZE_PRESETS.length) {
|
|
9
7
|
const preset = RESIZE_PRESETS[index];
|