@node-edit-utils/core 2.0.2 → 2.0.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/viewport/constants.d.ts +21 -0
- package/dist/lib/viewport/resize/createResizePresets.d.ts +1 -0
- package/dist/lib/viewport/resize/updateActivePreset.d.ts +1 -0
- package/dist/node-edit-utils.cjs.js +71 -3
- package/dist/node-edit-utils.esm.js +71 -3
- package/dist/node-edit-utils.umd.js +71 -3
- package/dist/node-edit-utils.umd.min.js +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/lib/node-tools/createNodeTools.ts +1 -1
- package/src/lib/styles/styles.css +48 -5
- package/src/lib/viewport/constants.ts +28 -0
- package/src/lib/viewport/createViewport.ts +11 -0
- package/src/lib/viewport/resize/createResizeHandle.ts +1 -1
- package/src/lib/viewport/resize/createResizePresets.ts +24 -0
- package/src/lib/viewport/resize/updateActivePreset.ts +17 -0
- package/src/lib/viewport/width/updateWidth.ts +3 -0
|
@@ -3,3 +3,24 @@ export declare const RESIZE_CONFIG: {
|
|
|
3
3
|
readonly minWidth: 320;
|
|
4
4
|
readonly maxWidth: 1680;
|
|
5
5
|
};
|
|
6
|
+
export declare const RESIZE_PRESETS: readonly [{
|
|
7
|
+
readonly name: "Mobile";
|
|
8
|
+
readonly rawValue: 390;
|
|
9
|
+
readonly value: "320px";
|
|
10
|
+
}, {
|
|
11
|
+
readonly name: "Tablet Portrait";
|
|
12
|
+
readonly rawValue: 768;
|
|
13
|
+
readonly value: "768px";
|
|
14
|
+
}, {
|
|
15
|
+
readonly name: "Tablet Landscape";
|
|
16
|
+
readonly rawValue: 1024;
|
|
17
|
+
readonly value: "1024px";
|
|
18
|
+
}, {
|
|
19
|
+
readonly name: "Notebook";
|
|
20
|
+
readonly rawValue: 1280;
|
|
21
|
+
readonly value: "1280px";
|
|
22
|
+
}, {
|
|
23
|
+
readonly name: "Desktop";
|
|
24
|
+
readonly rawValue: 1680;
|
|
25
|
+
readonly value: "1680px";
|
|
26
|
+
}];
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const createResizePresets: (resizeHandle: HTMLElement, container: HTMLElement, updateWidth: (container: HTMLElement, width: number) => void) => HTMLElement;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const updateActivePreset: (container: HTMLElement, width: number) => 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.4
|
|
5
5
|
*/
|
|
6
6
|
'use strict';
|
|
7
7
|
|
|
@@ -459,7 +459,7 @@ const createNodeTools = (element) => {
|
|
|
459
459
|
});
|
|
460
460
|
}
|
|
461
461
|
selectedNode = node;
|
|
462
|
-
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-
|
|
462
|
+
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
463
463
|
highlightNode(node, nodeProvider) ?? null;
|
|
464
464
|
};
|
|
465
465
|
// Setup event listener
|
|
@@ -497,6 +497,33 @@ const RESIZE_CONFIG = {
|
|
|
497
497
|
minWidth: 320,
|
|
498
498
|
maxWidth: 1680,
|
|
499
499
|
};
|
|
500
|
+
const RESIZE_PRESETS = [
|
|
501
|
+
{
|
|
502
|
+
name: "Mobile",
|
|
503
|
+
rawValue: 390,
|
|
504
|
+
value: "320px",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: "Tablet Portrait",
|
|
508
|
+
rawValue: 768,
|
|
509
|
+
value: "768px",
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
name: "Tablet Landscape",
|
|
513
|
+
rawValue: 1024,
|
|
514
|
+
value: "1024px",
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
name: "Notebook",
|
|
518
|
+
rawValue: 1280,
|
|
519
|
+
value: "1280px",
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
name: "Desktop",
|
|
523
|
+
rawValue: 1680,
|
|
524
|
+
value: "1680px",
|
|
525
|
+
},
|
|
526
|
+
];
|
|
500
527
|
|
|
501
528
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
502
529
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
@@ -513,11 +540,27 @@ const setupEventListener = (resizeHandle, startResize, handleResize, stopResize,
|
|
|
513
540
|
|
|
514
541
|
const createResizeHandle = (container) => {
|
|
515
542
|
const handle = document.createElement("div");
|
|
516
|
-
handle.className = "
|
|
543
|
+
handle.className = "resize-handle";
|
|
517
544
|
container.appendChild(handle);
|
|
518
545
|
return handle;
|
|
519
546
|
};
|
|
520
547
|
|
|
548
|
+
const createResizePresets = (resizeHandle, container, updateWidth) => {
|
|
549
|
+
const presets = document.createElement("div");
|
|
550
|
+
presets.className = "resize-presets";
|
|
551
|
+
RESIZE_PRESETS.forEach((preset) => {
|
|
552
|
+
const button = document.createElement("button");
|
|
553
|
+
button.textContent = preset.name;
|
|
554
|
+
button.className = "resize-preset-button";
|
|
555
|
+
button.addEventListener("click", () => {
|
|
556
|
+
updateWidth(container, preset.rawValue);
|
|
557
|
+
});
|
|
558
|
+
presets.appendChild(button);
|
|
559
|
+
});
|
|
560
|
+
resizeHandle.appendChild(presets);
|
|
561
|
+
return presets;
|
|
562
|
+
};
|
|
563
|
+
|
|
521
564
|
const calcConstrainedWidth = (startWidth, deltaX) => {
|
|
522
565
|
const newWidth = startWidth + Math.round(deltaX);
|
|
523
566
|
return Math.max(RESIZE_CONFIG.minWidth, Math.min(RESIZE_CONFIG.maxWidth, newWidth));
|
|
@@ -530,14 +573,38 @@ const calcWidth = (event, startX, startWidth) => {
|
|
|
530
573
|
return width;
|
|
531
574
|
};
|
|
532
575
|
|
|
576
|
+
const updateActivePreset = (container, width) => {
|
|
577
|
+
console.log("updateActivePreset", width);
|
|
578
|
+
const presetButtons = container.querySelectorAll(".resize-preset-button");
|
|
579
|
+
console.log("presetButtons", presetButtons);
|
|
580
|
+
presetButtons.forEach((button, index) => {
|
|
581
|
+
if (index < RESIZE_PRESETS.length) {
|
|
582
|
+
const preset = RESIZE_PRESETS[index];
|
|
583
|
+
if (preset.rawValue === width) {
|
|
584
|
+
button.classList.add("is-active");
|
|
585
|
+
}
|
|
586
|
+
else {
|
|
587
|
+
button.classList.remove("is-active");
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
|
|
533
593
|
const updateWidth = (container, width) => {
|
|
534
594
|
container.style.setProperty("--container-width", `${width}px`);
|
|
595
|
+
updateActivePreset(container, width);
|
|
535
596
|
};
|
|
536
597
|
|
|
537
598
|
const createViewport = (container) => {
|
|
538
599
|
const canvas = getCanvasContainer();
|
|
600
|
+
// Remove any existing resize handle to prevent duplicates
|
|
601
|
+
const existingHandle = container.querySelector(".resize-handle");
|
|
602
|
+
if (existingHandle) {
|
|
603
|
+
existingHandle.remove();
|
|
604
|
+
}
|
|
539
605
|
const resizeHandle = createResizeHandle(container);
|
|
540
606
|
container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
|
|
607
|
+
createResizePresets(resizeHandle, container, updateWidth);
|
|
541
608
|
let isDragging = false;
|
|
542
609
|
let startX = 0;
|
|
543
610
|
let startWidth = 0;
|
|
@@ -574,6 +641,7 @@ const createViewport = (container) => {
|
|
|
574
641
|
isDragging = false;
|
|
575
642
|
throttledHandleResize?.cleanup();
|
|
576
643
|
removeListeners();
|
|
644
|
+
resizeHandle.remove();
|
|
577
645
|
};
|
|
578
646
|
return {
|
|
579
647
|
setWidth: (width) => {
|
|
@@ -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.4
|
|
5
5
|
*/
|
|
6
6
|
// biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
|
|
7
7
|
function withRAFThrottle(func) {
|
|
@@ -457,7 +457,7 @@ const createNodeTools = (element) => {
|
|
|
457
457
|
});
|
|
458
458
|
}
|
|
459
459
|
selectedNode = node;
|
|
460
|
-
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-
|
|
460
|
+
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
461
461
|
highlightNode(node, nodeProvider) ?? null;
|
|
462
462
|
};
|
|
463
463
|
// Setup event listener
|
|
@@ -495,6 +495,33 @@ const RESIZE_CONFIG = {
|
|
|
495
495
|
minWidth: 320,
|
|
496
496
|
maxWidth: 1680,
|
|
497
497
|
};
|
|
498
|
+
const RESIZE_PRESETS = [
|
|
499
|
+
{
|
|
500
|
+
name: "Mobile",
|
|
501
|
+
rawValue: 390,
|
|
502
|
+
value: "320px",
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
name: "Tablet Portrait",
|
|
506
|
+
rawValue: 768,
|
|
507
|
+
value: "768px",
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: "Tablet Landscape",
|
|
511
|
+
rawValue: 1024,
|
|
512
|
+
value: "1024px",
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: "Notebook",
|
|
516
|
+
rawValue: 1280,
|
|
517
|
+
value: "1280px",
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: "Desktop",
|
|
521
|
+
rawValue: 1680,
|
|
522
|
+
value: "1680px",
|
|
523
|
+
},
|
|
524
|
+
];
|
|
498
525
|
|
|
499
526
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
500
527
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
@@ -511,11 +538,27 @@ const setupEventListener = (resizeHandle, startResize, handleResize, stopResize,
|
|
|
511
538
|
|
|
512
539
|
const createResizeHandle = (container) => {
|
|
513
540
|
const handle = document.createElement("div");
|
|
514
|
-
handle.className = "
|
|
541
|
+
handle.className = "resize-handle";
|
|
515
542
|
container.appendChild(handle);
|
|
516
543
|
return handle;
|
|
517
544
|
};
|
|
518
545
|
|
|
546
|
+
const createResizePresets = (resizeHandle, container, updateWidth) => {
|
|
547
|
+
const presets = document.createElement("div");
|
|
548
|
+
presets.className = "resize-presets";
|
|
549
|
+
RESIZE_PRESETS.forEach((preset) => {
|
|
550
|
+
const button = document.createElement("button");
|
|
551
|
+
button.textContent = preset.name;
|
|
552
|
+
button.className = "resize-preset-button";
|
|
553
|
+
button.addEventListener("click", () => {
|
|
554
|
+
updateWidth(container, preset.rawValue);
|
|
555
|
+
});
|
|
556
|
+
presets.appendChild(button);
|
|
557
|
+
});
|
|
558
|
+
resizeHandle.appendChild(presets);
|
|
559
|
+
return presets;
|
|
560
|
+
};
|
|
561
|
+
|
|
519
562
|
const calcConstrainedWidth = (startWidth, deltaX) => {
|
|
520
563
|
const newWidth = startWidth + Math.round(deltaX);
|
|
521
564
|
return Math.max(RESIZE_CONFIG.minWidth, Math.min(RESIZE_CONFIG.maxWidth, newWidth));
|
|
@@ -528,14 +571,38 @@ const calcWidth = (event, startX, startWidth) => {
|
|
|
528
571
|
return width;
|
|
529
572
|
};
|
|
530
573
|
|
|
574
|
+
const updateActivePreset = (container, width) => {
|
|
575
|
+
console.log("updateActivePreset", width);
|
|
576
|
+
const presetButtons = container.querySelectorAll(".resize-preset-button");
|
|
577
|
+
console.log("presetButtons", presetButtons);
|
|
578
|
+
presetButtons.forEach((button, index) => {
|
|
579
|
+
if (index < RESIZE_PRESETS.length) {
|
|
580
|
+
const preset = RESIZE_PRESETS[index];
|
|
581
|
+
if (preset.rawValue === width) {
|
|
582
|
+
button.classList.add("is-active");
|
|
583
|
+
}
|
|
584
|
+
else {
|
|
585
|
+
button.classList.remove("is-active");
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
});
|
|
589
|
+
};
|
|
590
|
+
|
|
531
591
|
const updateWidth = (container, width) => {
|
|
532
592
|
container.style.setProperty("--container-width", `${width}px`);
|
|
593
|
+
updateActivePreset(container, width);
|
|
533
594
|
};
|
|
534
595
|
|
|
535
596
|
const createViewport = (container) => {
|
|
536
597
|
const canvas = getCanvasContainer();
|
|
598
|
+
// Remove any existing resize handle to prevent duplicates
|
|
599
|
+
const existingHandle = container.querySelector(".resize-handle");
|
|
600
|
+
if (existingHandle) {
|
|
601
|
+
existingHandle.remove();
|
|
602
|
+
}
|
|
537
603
|
const resizeHandle = createResizeHandle(container);
|
|
538
604
|
container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
|
|
605
|
+
createResizePresets(resizeHandle, container, updateWidth);
|
|
539
606
|
let isDragging = false;
|
|
540
607
|
let startX = 0;
|
|
541
608
|
let startWidth = 0;
|
|
@@ -572,6 +639,7 @@ const createViewport = (container) => {
|
|
|
572
639
|
isDragging = false;
|
|
573
640
|
throttledHandleResize?.cleanup();
|
|
574
641
|
removeListeners();
|
|
642
|
+
resizeHandle.remove();
|
|
575
643
|
};
|
|
576
644
|
return {
|
|
577
645
|
setWidth: (width) => {
|
|
@@ -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.4
|
|
5
5
|
*/
|
|
6
6
|
(function (global, factory) {
|
|
7
7
|
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
|
@@ -463,7 +463,7 @@
|
|
|
463
463
|
});
|
|
464
464
|
}
|
|
465
465
|
selectedNode = node;
|
|
466
|
-
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-
|
|
466
|
+
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
467
467
|
highlightNode(node, nodeProvider) ?? null;
|
|
468
468
|
};
|
|
469
469
|
// Setup event listener
|
|
@@ -501,6 +501,33 @@
|
|
|
501
501
|
minWidth: 320,
|
|
502
502
|
maxWidth: 1680,
|
|
503
503
|
};
|
|
504
|
+
const RESIZE_PRESETS = [
|
|
505
|
+
{
|
|
506
|
+
name: "Mobile",
|
|
507
|
+
rawValue: 390,
|
|
508
|
+
value: "320px",
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: "Tablet Portrait",
|
|
512
|
+
rawValue: 768,
|
|
513
|
+
value: "768px",
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
name: "Tablet Landscape",
|
|
517
|
+
rawValue: 1024,
|
|
518
|
+
value: "1024px",
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
name: "Notebook",
|
|
522
|
+
rawValue: 1280,
|
|
523
|
+
value: "1280px",
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: "Desktop",
|
|
527
|
+
rawValue: 1680,
|
|
528
|
+
value: "1680px",
|
|
529
|
+
},
|
|
530
|
+
];
|
|
504
531
|
|
|
505
532
|
const setupEventListener = (resizeHandle, startResize, handleResize, stopResize, blurResize) => {
|
|
506
533
|
resizeHandle.addEventListener("mousedown", startResize);
|
|
@@ -517,11 +544,27 @@
|
|
|
517
544
|
|
|
518
545
|
const createResizeHandle = (container) => {
|
|
519
546
|
const handle = document.createElement("div");
|
|
520
|
-
handle.className = "
|
|
547
|
+
handle.className = "resize-handle";
|
|
521
548
|
container.appendChild(handle);
|
|
522
549
|
return handle;
|
|
523
550
|
};
|
|
524
551
|
|
|
552
|
+
const createResizePresets = (resizeHandle, container, updateWidth) => {
|
|
553
|
+
const presets = document.createElement("div");
|
|
554
|
+
presets.className = "resize-presets";
|
|
555
|
+
RESIZE_PRESETS.forEach((preset) => {
|
|
556
|
+
const button = document.createElement("button");
|
|
557
|
+
button.textContent = preset.name;
|
|
558
|
+
button.className = "resize-preset-button";
|
|
559
|
+
button.addEventListener("click", () => {
|
|
560
|
+
updateWidth(container, preset.rawValue);
|
|
561
|
+
});
|
|
562
|
+
presets.appendChild(button);
|
|
563
|
+
});
|
|
564
|
+
resizeHandle.appendChild(presets);
|
|
565
|
+
return presets;
|
|
566
|
+
};
|
|
567
|
+
|
|
525
568
|
const calcConstrainedWidth = (startWidth, deltaX) => {
|
|
526
569
|
const newWidth = startWidth + Math.round(deltaX);
|
|
527
570
|
return Math.max(RESIZE_CONFIG.minWidth, Math.min(RESIZE_CONFIG.maxWidth, newWidth));
|
|
@@ -534,14 +577,38 @@
|
|
|
534
577
|
return width;
|
|
535
578
|
};
|
|
536
579
|
|
|
580
|
+
const updateActivePreset = (container, width) => {
|
|
581
|
+
console.log("updateActivePreset", width);
|
|
582
|
+
const presetButtons = container.querySelectorAll(".resize-preset-button");
|
|
583
|
+
console.log("presetButtons", presetButtons);
|
|
584
|
+
presetButtons.forEach((button, index) => {
|
|
585
|
+
if (index < RESIZE_PRESETS.length) {
|
|
586
|
+
const preset = RESIZE_PRESETS[index];
|
|
587
|
+
if (preset.rawValue === width) {
|
|
588
|
+
button.classList.add("is-active");
|
|
589
|
+
}
|
|
590
|
+
else {
|
|
591
|
+
button.classList.remove("is-active");
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
});
|
|
595
|
+
};
|
|
596
|
+
|
|
537
597
|
const updateWidth = (container, width) => {
|
|
538
598
|
container.style.setProperty("--container-width", `${width}px`);
|
|
599
|
+
updateActivePreset(container, width);
|
|
539
600
|
};
|
|
540
601
|
|
|
541
602
|
const createViewport = (container) => {
|
|
542
603
|
const canvas = getCanvasContainer();
|
|
604
|
+
// Remove any existing resize handle to prevent duplicates
|
|
605
|
+
const existingHandle = container.querySelector(".resize-handle");
|
|
606
|
+
if (existingHandle) {
|
|
607
|
+
existingHandle.remove();
|
|
608
|
+
}
|
|
543
609
|
const resizeHandle = createResizeHandle(container);
|
|
544
610
|
container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
|
|
611
|
+
createResizePresets(resizeHandle, container, updateWidth);
|
|
545
612
|
let isDragging = false;
|
|
546
613
|
let startX = 0;
|
|
547
614
|
let startWidth = 0;
|
|
@@ -578,6 +645,7 @@
|
|
|
578
645
|
isDragging = false;
|
|
579
646
|
throttledHandleResize?.cleanup();
|
|
580
647
|
removeListeners();
|
|
648
|
+
resizeHandle.remove();
|
|
581
649
|
};
|
|
582
650
|
return {
|
|
583
651
|
setWidth: (width) => {
|
|
@@ -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
|
|
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 l=(e,t)=>{let n=null;const o=e.clientX,r=e.clientY,l=e.metaKey||e.ctrlKey,d=((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()));if(t&&d.includes(t))return t;if(l)return a=[],n=d[0],n;var c,u;u=d,(c=a).length===u.length&&c.every((e,t)=>e===u[t])?i<=d.length&&i++:i=0;return n=d[d.length-1-i],a=d,n},d=(e,t,n,o)=>{const s=e=>{(e=>{if("markup-canvas"===e.data.source&&"canvas"===e.data.canvasName&&"zoom"===e.data.action){const t=e.data.data;console.log("zoom",t)}})(e)},a=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(t),void o(null);o(l(e,n))})(n,e,o(),t)},i=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),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 l=document.createElement("div");l.classList.add("highlight-frame"),l.style.setProperty("--frame-top",`${o}px`),l.style.setProperty("--frame-left",`${r}px`),l.style.setProperty("--frame-width",`${s}px`),l.style.setProperty("--frame-height",`${a}px`);const d=document.createElementNS("http://www.w3.org/2000/svg","svg");d.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"),d.appendChild(u),l.appendChild(d),l})(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:l,height:d}=c(e,t);r.style.setProperty("--frame-top",`${a}px`),r.style.setProperty("--frame-left",`${i}px`),r.style.setProperty("--frame-width",`${l}px`),r.style.setProperty("--frame-height",`${d}px`)},v=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)}},h=(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","enable"]);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","disable"]);e?.()})(),o=((e,t,n)=>{if(!t)return()=>{};e.addEventListener("blur",n);const o=v(e),r=h(e,t);return()=>{e.removeEventListener("blur",n),o(),r?.()}})(t,s,r))},blurEditMode:r,getEditableNode:()=>e,isEditing:()=>null!==e}},y=320,g=1680,b=[{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"}],w=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(y,Math.min(g,n))})(n,(e.clientX-t)/o);return r},E=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{console.log("updateActivePreset",t);const n=e.querySelectorAll(".resize-preset-button");console.log("presetButtons",n),n.forEach((e,n)=>{n<b.length&&(b[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;const a=f(),i=t(p),l=e=>{if(a.isEditing()){const t=a.getEditableNode();t&&t!==e&&a.blurEditMode()}var t,r;o?.disconnect(),e&&n&&(a.enableEditMode(e,n),o=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(n,()=>{i(e,n)})),s=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)},c=d(n,l,()=>{a.isEditing()&&a.blurEditMode(),s&&n&&(r(n),s=null,o?.disconnect())},a.getEditableNode),u={selectNode:l,getSelectedNode:()=>s,refreshHighlightFrame:()=>{i(s,n)},clearSelectedNode:()=>{r(n),s=null,o?.disconnect()},getEditableNode:()=>a.getEditableNode(),cleanup:()=>{c(),o?.disconnect(),a.blurEditMode(),i.cleanup()}};var v,h;return v="nodeTools",h=u,"undefined"!=typeof window&&(window[v]=h),u},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",b.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,E);let s=!1,a=0,i=0;const l=t(t=>{if(!s)return;n&&(n.style.cursor="ew-resize");const o=w(t,a,i);E(e,o)}),d=((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},l,e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),s=!1},()=>{s=!1});return{setWidth:t=>{E(e,t)},cleanup:()=>{s=!1,l?.cleanup(),d(),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:.25rem;justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:.25rem;position:absolute;transform:scale(calc(1/var(--zoom))) translate3d(0,100%,0);transform-origin:bottom left;transition:
|
|
1
|
+
.node-provider{::selection{background:transparent}::-moz-selection{background:transparent}::-webkit-selection{background:transparent}}.node-tools{bottom:0;display:flex;gap:.25rem;justify-content:center;left:0;opacity:var(--tool-opacity);padding-top:.25rem;position:absolute;transform:scale(calc(1/var(--zoom))) translate3d(0,100%,0);transform-origin:bottom left;transition:opacity .1s ease;z-index:10000}.highlight-frame{height:var(--frame-height);left:var(--frame-left);pointer-events:none;position:absolute;top:var(--frame-top);width:var(--frame-width);z-index:1000}.highlight-frame-svg{height:100%;left:0;overflow:visible;position:absolute;top:0;width:100%}.highlight-frame-rect{fill:none;stroke:oklch(62.7% .265 303.9);stroke-width:var(--stroke-width)}.tag-label{align-items:center;background-color:oklch(62.7% .265 303.9);border-radius:.375rem;color:#fff;display:flex;font-size:.575rem;font-weight:500;height:1.25rem;justify-content:center;line-height:1;padding:0 .375rem;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:1.25rem;transform:scale(calc(1/var(--zoom)));transform-origin:top left;transition:opacity .1s ease-in-out,visibility .1s ease-in-out;visibility:hidden;width:12px;z-index:10000}.viewport:hover .resize-handle{opacity:1;visibility:visible}.resize-handle:before{background:oklch(55.2% .016 285.938);border-radius:4px;content:"";height:32px;position:relative;right:0;top:0;transition:transform .2s ease-in-out;width:3px}.resize-handle:hover:before{transform:scaleY(1.3)}.resize-presets{display:flex;flex-direction:column;gap:.25rem;left:0;opacity:0;padding-left:1.25rem;position:absolute;top:0;transition:visibility .1s ease-in-out,opacity .1s ease-in-out;visibility:hidden}.resize-handle:hover .resize-presets{opacity:1;visibility:visible}.resize-preset-button{text-wrap:nowrap;backdrop-filter:blur(8px);background:oklch(55.2% .016 285.938/.7);border:none;border-radius:.5rem;color:#fff;cursor:pointer;font-size:.6875rem;padding:.25rem .5rem;text-align:left;transition:background .1s ease-in-out;width:fit-content;&:hover{background:oklch(55.2% .016 285.938/1)}&.is-active{background:oklch(45.7% .24 277.023)}}.is-editable{outline:none;user-select:text;&::selection{background:oklch(62.7% .265 303.9/.3)}&::-moz-selection{background:oklch(62.7% .265 303.9/.3)}&::-webkit-selection{background:oklch(62.7% .265 303.9/.3)}}
|
package/package.json
CHANGED
|
@@ -52,7 +52,7 @@ export const createNodeTools = (element: HTMLElement | null): NodeTools => {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
selectedNode = node;
|
|
55
|
-
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-
|
|
55
|
+
sendPostMessage("selectedNodeChanged", node?.getAttribute("data-node-id") ?? null);
|
|
56
56
|
highlightNode(node, nodeProvider as HTMLElement) ?? null;
|
|
57
57
|
};
|
|
58
58
|
|
|
@@ -20,9 +20,7 @@
|
|
|
20
20
|
z-index: 10000;
|
|
21
21
|
transform: scale(calc(1 / var(--zoom))) translate3d(0, 100%, 0);
|
|
22
22
|
transform-origin: bottom left;
|
|
23
|
-
transition:
|
|
24
|
-
transform 0.025s ease,
|
|
25
|
-
opacity 0.1s ease;
|
|
23
|
+
transition: opacity 0.1s ease;
|
|
26
24
|
opacity: var(--tool-opacity);
|
|
27
25
|
padding-top: 0.25rem;
|
|
28
26
|
display: flex;
|
|
@@ -85,18 +83,20 @@
|
|
|
85
83
|
z-index: 10000;
|
|
86
84
|
display: flex;
|
|
87
85
|
opacity: 0;
|
|
86
|
+
visibility: hidden;
|
|
88
87
|
align-items: center;
|
|
89
88
|
justify-content: center;
|
|
90
89
|
pointer-events: auto;
|
|
91
90
|
transform: scale(calc(1 / var(--zoom)));
|
|
92
91
|
transform-origin: top left;
|
|
93
92
|
transition:
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
opacity 0.1s ease-in-out,
|
|
94
|
+
visibility 0.1s ease-in-out;
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
.viewport:hover .resize-handle {
|
|
99
98
|
opacity: 1;
|
|
99
|
+
visibility: visible;
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
.resize-handle::before {
|
|
@@ -115,6 +115,49 @@
|
|
|
115
115
|
transform: scaleY(1.3);
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
.resize-presets {
|
|
119
|
+
position: absolute;
|
|
120
|
+
top: 0;
|
|
121
|
+
left: 0;
|
|
122
|
+
padding-left: 1.25rem;
|
|
123
|
+
display: flex;
|
|
124
|
+
flex-direction: column;
|
|
125
|
+
gap: 0.25rem;
|
|
126
|
+
visibility: hidden;
|
|
127
|
+
opacity: 0;
|
|
128
|
+
transition:
|
|
129
|
+
visibility 0.1s ease-in-out,
|
|
130
|
+
opacity 0.1s ease-in-out;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.resize-handle:hover .resize-presets {
|
|
134
|
+
visibility: visible;
|
|
135
|
+
opacity: 1;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.resize-preset-button {
|
|
139
|
+
background: oklch(55.2% 0.016 285.938 / 0.7);
|
|
140
|
+
border: none;
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
padding: 0.25rem 0.5rem;
|
|
143
|
+
border-radius: 0.5rem;
|
|
144
|
+
font-size: 0.6875rem;
|
|
145
|
+
text-wrap: nowrap;
|
|
146
|
+
text-align: left;
|
|
147
|
+
backdrop-filter: blur(8px);
|
|
148
|
+
transition: background 0.1s ease-in-out;
|
|
149
|
+
color: white;
|
|
150
|
+
width: fit-content;
|
|
151
|
+
|
|
152
|
+
&:hover {
|
|
153
|
+
background: oklch(55.2% 0.016 285.938 / 1);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
&.is-active {
|
|
157
|
+
background: oklch(45.7% 0.24 277.023);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
118
161
|
.is-editable {
|
|
119
162
|
outline: none;
|
|
120
163
|
user-select: text;
|
|
@@ -4,3 +4,31 @@ export const RESIZE_CONFIG = {
|
|
|
4
4
|
minWidth: 320,
|
|
5
5
|
maxWidth: 1680,
|
|
6
6
|
} as const;
|
|
7
|
+
|
|
8
|
+
export const RESIZE_PRESETS = [
|
|
9
|
+
{
|
|
10
|
+
name: "Mobile",
|
|
11
|
+
rawValue: 390,
|
|
12
|
+
value: "320px",
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
name: "Tablet Portrait",
|
|
16
|
+
rawValue: 768,
|
|
17
|
+
value: "768px",
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: "Tablet Landscape",
|
|
21
|
+
rawValue: 1024,
|
|
22
|
+
value: "1024px",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: "Notebook",
|
|
26
|
+
rawValue: 1280,
|
|
27
|
+
value: "1280px",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "Desktop",
|
|
31
|
+
rawValue: 1680,
|
|
32
|
+
value: "1680px",
|
|
33
|
+
},
|
|
34
|
+
] as const;
|
|
@@ -3,15 +3,25 @@ import { withRAFThrottle } from "../helpers";
|
|
|
3
3
|
import { DEFAULT_WIDTH } from "./constants";
|
|
4
4
|
import { setupEventListener } from "./events/setupEventListener";
|
|
5
5
|
import { createResizeHandle } from "./resize/createResizeHandle";
|
|
6
|
+
import { createResizePresets } from "./resize/createResizePresets";
|
|
6
7
|
import type { Viewport } from "./types";
|
|
7
8
|
import { calcWidth } from "./width/calcWidth";
|
|
8
9
|
import { updateWidth } from "./width/updateWidth";
|
|
9
10
|
|
|
10
11
|
export const createViewport = (container: HTMLElement): Viewport => {
|
|
11
12
|
const canvas: HTMLElement | null = getCanvasContainer();
|
|
13
|
+
|
|
14
|
+
// Remove any existing resize handle to prevent duplicates
|
|
15
|
+
const existingHandle = container.querySelector(".resize-handle");
|
|
16
|
+
if (existingHandle) {
|
|
17
|
+
existingHandle.remove();
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
const resizeHandle = createResizeHandle(container);
|
|
13
21
|
container.style.setProperty("--container-width", `${DEFAULT_WIDTH}px`);
|
|
14
22
|
|
|
23
|
+
createResizePresets(resizeHandle, container, updateWidth);
|
|
24
|
+
|
|
15
25
|
let isDragging: boolean = false;
|
|
16
26
|
let startX: number = 0;
|
|
17
27
|
let startWidth: number = 0;
|
|
@@ -59,6 +69,7 @@ export const createViewport = (container: HTMLElement): Viewport => {
|
|
|
59
69
|
isDragging = false;
|
|
60
70
|
throttledHandleResize?.cleanup();
|
|
61
71
|
removeListeners();
|
|
72
|
+
resizeHandle.remove();
|
|
62
73
|
};
|
|
63
74
|
|
|
64
75
|
return {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { RESIZE_PRESETS } from "../constants";
|
|
2
|
+
|
|
3
|
+
export const createResizePresets = (
|
|
4
|
+
resizeHandle: HTMLElement,
|
|
5
|
+
container: HTMLElement,
|
|
6
|
+
updateWidth: (container: HTMLElement, width: number) => void
|
|
7
|
+
): HTMLElement => {
|
|
8
|
+
const presets = document.createElement("div");
|
|
9
|
+
presets.className = "resize-presets";
|
|
10
|
+
|
|
11
|
+
RESIZE_PRESETS.forEach((preset) => {
|
|
12
|
+
const button = document.createElement("button");
|
|
13
|
+
button.textContent = preset.name;
|
|
14
|
+
button.className = "resize-preset-button";
|
|
15
|
+
button.addEventListener("click", () => {
|
|
16
|
+
updateWidth(container, preset.rawValue);
|
|
17
|
+
});
|
|
18
|
+
presets.appendChild(button);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
resizeHandle.appendChild(presets);
|
|
22
|
+
|
|
23
|
+
return presets;
|
|
24
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { RESIZE_PRESETS } from "../constants";
|
|
2
|
+
|
|
3
|
+
export const updateActivePreset = (container: HTMLElement, width: number): void => {
|
|
4
|
+
console.log("updateActivePreset", width);
|
|
5
|
+
const presetButtons = container.querySelectorAll<HTMLButtonElement>(".resize-preset-button");
|
|
6
|
+
console.log("presetButtons", presetButtons);
|
|
7
|
+
presetButtons.forEach((button, index) => {
|
|
8
|
+
if (index < RESIZE_PRESETS.length) {
|
|
9
|
+
const preset = RESIZE_PRESETS[index];
|
|
10
|
+
if (preset.rawValue === width) {
|
|
11
|
+
button.classList.add("is-active");
|
|
12
|
+
} else {
|
|
13
|
+
button.classList.remove("is-active");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
};
|