@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.
@@ -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.2
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-layer-id") ?? null);
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 = "component-resize-handle resize-handle";
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.2
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-layer-id") ?? null);
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 = "component-resize-handle resize-handle";
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.2
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-layer-id") ?? null);
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 = "component-resize-handle resize-handle";
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 i=[],d=0;const a=(e,t)=>{let n=null;const o=e.clientX,r=e.clientY,a=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()));if(t&&l.includes(t))return t;if(a)return i=[],n=l[0],n;var c,u;u=l,(c=i).length===u.length&&c.every((e,t)=>e===u[t])?d<=l.length&&d++:d=0;return n=l[l.length-1-d],i=l,n},l=(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)},i=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return r(t),void o(null);o(a(e,n))})(n,e,o(),t)},d=e=>{"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",s),document.addEventListener("click",i),document.addEventListener("keydown",d),()=>{window.removeEventListener("message",s),document.removeEventListener("click",i),document.removeEventListener("keydown",d)}};function c(e,t){const o=e.getBoundingClientRect(),r=t.getBoundingClientRect(),s=o.top-r.top,i=o.left-r.left,d=n(["zoom","current"])??1;return{top:parseFloat((s/d).toFixed(5)),left:parseFloat((i/d).toFixed(5)),width:Math.max(4,parseFloat((o.width/d).toFixed(5))),height:parseFloat((o.height/d).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:i}=c(e,t),d=n(["zoom","current"])??1;document.body.style.setProperty("--zoom",d.toString()),document.body.style.setProperty("--stroke-width",(2/d).toFixed(3));const a=document.createElement("div");a.classList.add("highlight-frame"),a.style.setProperty("--frame-top",`${o}px`),a.style.setProperty("--frame-left",`${r}px`),a.style.setProperty("--frame-width",`${s}px`),a.style.setProperty("--frame-height",`${i}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),a.appendChild(l),a})(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:i,left:d,width:a,height:l}=c(e,t);r.style.setProperty("--frame-top",`${i}px`),r.style.setProperty("--frame-left",`${d}px`),r.style.setProperty("--frame-width",`${a}px`),r.style.setProperty("--frame-height",`${l}px`)},f=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()},h=()=>{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 i=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(t);i&&(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=f(e),r=y(e,t);return()=>{e.removeEventListener("blur",n),o(),r?.()}})(t,s,r))},blurEditMode:r,getEditableNode:()=>e,isEditing:()=>null!==e}},v=320,g=1680,b=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(v,Math.min(g,n))})(n,(e.clientX-t)/o);return r},w=(e,t)=>{e.style.setProperty("--container-width",`${t}px`)};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 i=h(),d=t(p),a=e=>{if(i.isEditing()){const t=i.getEditableNode();t&&t!==e&&i.blurEditMode()}var t,r;o?.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=e,t="selectedNodeChanged",r=e?.getAttribute("data-layer-id")??null,window.parent.postMessage({source:"node-edit-utils",action:t,data:r,timestamp:Date.now()},"*"),m(e,n)},c=l(n,a,()=>{i.isEditing()&&i.blurEditMode(),s&&n&&(r(n),s=null,o?.disconnect())},i.getEditableNode),u={selectNode:a,getSelectedNode:()=>s,refreshHighlightFrame:()=>{d(s,n)},clearSelectedNode:()=>{r(n),s=null,o?.disconnect()},getEditableNode:()=>i.getEditableNode(),cleanup:()=>{c(),o?.disconnect(),i.blurEditMode(),d.cleanup()}};var f,y;return f="nodeTools",y=u,"undefined"!=typeof window&&(window[f]=y),u},e.createViewport=e=>{const n=document.querySelector(".canvas-container"),o=(e=>{const t=document.createElement("div");return t.className="component-resize-handle resize-handle",e.appendChild(t),t})(e);e.style.setProperty("--container-width","400px");let r=!1,s=0,i=0;const d=t(t=>{if(!r)return;n&&(n.style.cursor="ew-resize");const o=b(t,s,i);w(e,o)}),a=((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)}))(o,t=>{t.preventDefault(),t.stopPropagation(),r=!0,s=t.clientX,i=e.offsetWidth},d,e=>{e.preventDefault(),e.stopPropagation(),n&&(n.style.cursor="default"),r=!1},()=>{r=!1});return{setWidth:t=>{w(e,t)},cleanup:()=>{r=!1,d?.cleanup(),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 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:transform 25ms ease,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:transform 25ms ease,opacity .2s ease-in-out;width:12px;z-index:10000}.viewport:hover .resize-handle{opacity:1}.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)}.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)}}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-edit-utils/core",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "Utilities for editing nodes in a dom tree.",
5
5
  "type": "module",
6
6
  "main": "dist/node-edit-utils.cjs.js",
@@ -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-layer-id") ?? null);
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
- transform 0.025s ease,
95
- opacity 0.2s ease-in-out;
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 {
@@ -1,7 +1,7 @@
1
1
  export const createResizeHandle = (container: HTMLElement): HTMLElement => {
2
2
  const handle = document.createElement("div");
3
3
 
4
- handle.className = "component-resize-handle resize-handle";
4
+ handle.className = "resize-handle";
5
5
 
6
6
  container.appendChild(handle);
7
7
 
@@ -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
+ };
@@ -1,3 +1,6 @@
1
+ import { updateActivePreset } from "../resize/updateActivePreset";
2
+
1
3
  export const updateWidth = (container: HTMLElement, width: number): void => {
2
4
  container.style.setProperty("--container-width", `${width}px`);
5
+ updateActivePreset(container, width);
3
6
  };