@node-edit-utils/core 2.0.8 → 2.1.0

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.
@@ -0,0 +1 @@
1
+ export declare const isLocked: (node: HTMLElement | null) => boolean;
@@ -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.8
4
+ * @version 2.1.0
5
5
  */
6
6
  'use strict';
7
7
 
@@ -97,16 +97,24 @@ const bindToWindow = (key, value) => {
97
97
  }
98
98
  };
99
99
 
100
+ const isLocked = (node) => {
101
+ return node?.classList.contains("select-none") ?? false;
102
+ };
103
+
100
104
  const processPostMessage = (event, onNodeSelected) => {
101
- if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
102
- if (event.data.action === "zoom") {
103
- event.data.data;
104
- }
105
- }
105
+ // if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
106
+ // if (event.data.action === "zoom") {
107
+ // // Zoom handling can be implemented here if needed
108
+ // }
109
+ // }
106
110
  if (event.data.source === "application") {
107
111
  if (event.data.action === "selectedNodeChanged") {
108
112
  const nodeId = event.data.data;
109
113
  const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
114
+ if (isLocked(selectedNode)) {
115
+ onNodeSelected?.(null);
116
+ return;
117
+ }
110
118
  if (selectedNode) {
111
119
  onNodeSelected?.(selectedNode);
112
120
  }
@@ -153,13 +161,19 @@ const selectNode = (event, editableNode) => {
153
161
  const clickX = event.clientX;
154
162
  const clickY = event.clientY;
155
163
  const clickThrough = event.metaKey || event.ctrlKey;
156
- const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
164
+ const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
157
165
  if (editableNode && candidates.includes(editableNode)) {
166
+ if (isLocked(editableNode)) {
167
+ return null;
168
+ }
158
169
  return editableNode;
159
170
  }
160
171
  if (clickThrough) {
161
172
  candidateCache = [];
162
173
  selectedNode = candidates[0];
174
+ if (isLocked(selectedNode)) {
175
+ return null;
176
+ }
163
177
  return selectedNode;
164
178
  }
165
179
  if (targetSameCandidates(candidateCache, candidates)) {
@@ -171,6 +185,9 @@ const selectNode = (event, editableNode) => {
171
185
  const nodeIndex = candidates.length - 1 - attempt;
172
186
  selectedNode = candidates[nodeIndex];
173
187
  candidateCache = candidates;
188
+ if (isLocked(selectedNode)) {
189
+ return null;
190
+ }
174
191
  return selectedNode;
175
192
  };
176
193
 
@@ -306,20 +323,11 @@ const updateHighlightFrameVisibility = (node, nodeProvider) => {
306
323
  const frame = getHighlightFrameElement(nodeProvider);
307
324
  if (!frame)
308
325
  return;
309
- const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
326
+ const className = node.className;
327
+ const hasHiddenClass = /\bhidden\b/.test(className) || node.classList.contains("select-none");
310
328
  frame.style.display = hasHiddenClass ? "none" : "";
311
329
  };
312
330
 
313
- const disableCanvasTextMode = () => {
314
- const disableTextEditMode = getCanvasWindowValue(["keyboard", "disableTextEditMode"]);
315
- disableTextEditMode?.();
316
- };
317
-
318
- const enableCanvasTextMode = () => {
319
- const enableTextEditMode = getCanvasWindowValue(["keyboard", "enableTextEditMode"]);
320
- enableTextEditMode?.();
321
- };
322
-
323
331
  const insertLineBreak = () => {
324
332
  const selection = window.getSelection();
325
333
  if (selection && selection.rangeCount > 0) {
@@ -413,7 +421,7 @@ const nodeText = () => {
413
421
  if (editable) {
414
422
  editableNode = node;
415
423
  makeNodeEditable(node);
416
- enableCanvasTextMode();
424
+ //enableCanvasTextMode();
417
425
  cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
418
426
  }
419
427
  };
@@ -430,7 +438,7 @@ const nodeText = () => {
430
438
  blurInProgress = true;
431
439
  const nodeToCleanup = editableNode;
432
440
  makeNodeNonEditable(nodeToCleanup);
433
- disableCanvasTextMode();
441
+ //disableCanvasTextMode();
434
442
  cleanup?.();
435
443
  editableNode = null;
436
444
  blurInProgress = false;
@@ -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.8
4
+ * @version 2.1.0
5
5
  */
6
6
  // biome-ignore lint/suspicious/noExplicitAny: generic constraint requires flexibility
7
7
  function withRAFThrottle(func) {
@@ -95,16 +95,24 @@ const bindToWindow = (key, value) => {
95
95
  }
96
96
  };
97
97
 
98
+ const isLocked = (node) => {
99
+ return node?.classList.contains("select-none") ?? false;
100
+ };
101
+
98
102
  const processPostMessage = (event, onNodeSelected) => {
99
- if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
100
- if (event.data.action === "zoom") {
101
- event.data.data;
102
- }
103
- }
103
+ // if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
104
+ // if (event.data.action === "zoom") {
105
+ // // Zoom handling can be implemented here if needed
106
+ // }
107
+ // }
104
108
  if (event.data.source === "application") {
105
109
  if (event.data.action === "selectedNodeChanged") {
106
110
  const nodeId = event.data.data;
107
111
  const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
112
+ if (isLocked(selectedNode)) {
113
+ onNodeSelected?.(null);
114
+ return;
115
+ }
108
116
  if (selectedNode) {
109
117
  onNodeSelected?.(selectedNode);
110
118
  }
@@ -151,13 +159,19 @@ const selectNode = (event, editableNode) => {
151
159
  const clickX = event.clientX;
152
160
  const clickY = event.clientY;
153
161
  const clickThrough = event.metaKey || event.ctrlKey;
154
- const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
162
+ const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
155
163
  if (editableNode && candidates.includes(editableNode)) {
164
+ if (isLocked(editableNode)) {
165
+ return null;
166
+ }
156
167
  return editableNode;
157
168
  }
158
169
  if (clickThrough) {
159
170
  candidateCache = [];
160
171
  selectedNode = candidates[0];
172
+ if (isLocked(selectedNode)) {
173
+ return null;
174
+ }
161
175
  return selectedNode;
162
176
  }
163
177
  if (targetSameCandidates(candidateCache, candidates)) {
@@ -169,6 +183,9 @@ const selectNode = (event, editableNode) => {
169
183
  const nodeIndex = candidates.length - 1 - attempt;
170
184
  selectedNode = candidates[nodeIndex];
171
185
  candidateCache = candidates;
186
+ if (isLocked(selectedNode)) {
187
+ return null;
188
+ }
172
189
  return selectedNode;
173
190
  };
174
191
 
@@ -304,20 +321,11 @@ const updateHighlightFrameVisibility = (node, nodeProvider) => {
304
321
  const frame = getHighlightFrameElement(nodeProvider);
305
322
  if (!frame)
306
323
  return;
307
- const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
324
+ const className = node.className;
325
+ const hasHiddenClass = /\bhidden\b/.test(className) || node.classList.contains("select-none");
308
326
  frame.style.display = hasHiddenClass ? "none" : "";
309
327
  };
310
328
 
311
- const disableCanvasTextMode = () => {
312
- const disableTextEditMode = getCanvasWindowValue(["keyboard", "disableTextEditMode"]);
313
- disableTextEditMode?.();
314
- };
315
-
316
- const enableCanvasTextMode = () => {
317
- const enableTextEditMode = getCanvasWindowValue(["keyboard", "enableTextEditMode"]);
318
- enableTextEditMode?.();
319
- };
320
-
321
329
  const insertLineBreak = () => {
322
330
  const selection = window.getSelection();
323
331
  if (selection && selection.rangeCount > 0) {
@@ -411,7 +419,7 @@ const nodeText = () => {
411
419
  if (editable) {
412
420
  editableNode = node;
413
421
  makeNodeEditable(node);
414
- enableCanvasTextMode();
422
+ //enableCanvasTextMode();
415
423
  cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
416
424
  }
417
425
  };
@@ -428,7 +436,7 @@ const nodeText = () => {
428
436
  blurInProgress = true;
429
437
  const nodeToCleanup = editableNode;
430
438
  makeNodeNonEditable(nodeToCleanup);
431
- disableCanvasTextMode();
439
+ //disableCanvasTextMode();
432
440
  cleanup?.();
433
441
  editableNode = null;
434
442
  blurInProgress = false;
@@ -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.8
4
+ * @version 2.1.0
5
5
  */
6
6
  (function (global, factory) {
7
7
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
@@ -101,16 +101,24 @@
101
101
  }
102
102
  };
103
103
 
104
+ const isLocked = (node) => {
105
+ return node?.classList.contains("select-none") ?? false;
106
+ };
107
+
104
108
  const processPostMessage = (event, onNodeSelected) => {
105
- if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
106
- if (event.data.action === "zoom") {
107
- event.data.data;
108
- }
109
- }
109
+ // if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
110
+ // if (event.data.action === "zoom") {
111
+ // // Zoom handling can be implemented here if needed
112
+ // }
113
+ // }
110
114
  if (event.data.source === "application") {
111
115
  if (event.data.action === "selectedNodeChanged") {
112
116
  const nodeId = event.data.data;
113
117
  const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
118
+ if (isLocked(selectedNode)) {
119
+ onNodeSelected?.(null);
120
+ return;
121
+ }
114
122
  if (selectedNode) {
115
123
  onNodeSelected?.(selectedNode);
116
124
  }
@@ -157,13 +165,19 @@
157
165
  const clickX = event.clientX;
158
166
  const clickY = event.clientY;
159
167
  const clickThrough = event.metaKey || event.ctrlKey;
160
- const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none"));
168
+ const candidates = getElementsFromPoint(clickX, clickY).filter((element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()));
161
169
  if (editableNode && candidates.includes(editableNode)) {
170
+ if (isLocked(editableNode)) {
171
+ return null;
172
+ }
162
173
  return editableNode;
163
174
  }
164
175
  if (clickThrough) {
165
176
  candidateCache = [];
166
177
  selectedNode = candidates[0];
178
+ if (isLocked(selectedNode)) {
179
+ return null;
180
+ }
167
181
  return selectedNode;
168
182
  }
169
183
  if (targetSameCandidates(candidateCache, candidates)) {
@@ -175,6 +189,9 @@
175
189
  const nodeIndex = candidates.length - 1 - attempt;
176
190
  selectedNode = candidates[nodeIndex];
177
191
  candidateCache = candidates;
192
+ if (isLocked(selectedNode)) {
193
+ return null;
194
+ }
178
195
  return selectedNode;
179
196
  };
180
197
 
@@ -310,20 +327,11 @@
310
327
  const frame = getHighlightFrameElement(nodeProvider);
311
328
  if (!frame)
312
329
  return;
313
- const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
330
+ const className = node.className;
331
+ const hasHiddenClass = /\bhidden\b/.test(className) || node.classList.contains("select-none");
314
332
  frame.style.display = hasHiddenClass ? "none" : "";
315
333
  };
316
334
 
317
- const disableCanvasTextMode = () => {
318
- const disableTextEditMode = getCanvasWindowValue(["keyboard", "disableTextEditMode"]);
319
- disableTextEditMode?.();
320
- };
321
-
322
- const enableCanvasTextMode = () => {
323
- const enableTextEditMode = getCanvasWindowValue(["keyboard", "enableTextEditMode"]);
324
- enableTextEditMode?.();
325
- };
326
-
327
335
  const insertLineBreak = () => {
328
336
  const selection = window.getSelection();
329
337
  if (selection && selection.rangeCount > 0) {
@@ -417,7 +425,7 @@
417
425
  if (editable) {
418
426
  editableNode = node;
419
427
  makeNodeEditable(node);
420
- enableCanvasTextMode();
428
+ //enableCanvasTextMode();
421
429
  cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
422
430
  }
423
431
  };
@@ -434,7 +442,7 @@
434
442
  blurInProgress = true;
435
443
  const nodeToCleanup = editableNode;
436
444
  makeNodeNonEditable(nodeToCleanup);
437
- disableCanvasTextMode();
445
+ //disableCanvasTextMode();
438
446
  cleanup?.();
439
447
  editableNode = null;
440
448
  blurInProgress = false;
@@ -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=[],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&&(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 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)}},f=(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()},y=()=>{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=f(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=y(),d=t(p),c=e=>{if(a!==e){if(a=e,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"]})),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 f,g;return f="nodeTools",g=h,"undefined"!=typeof window&&(window[f]=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()}}}});
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)};const o=e=>e?.classList.contains("select-none")??!1;function r(e){return e.querySelector(".highlight-frame")}const s=e=>{if(!e)return;const t=r(e);t&&t.remove()},a=["path","rect","circle","ellipse","polygon","line","polyline","text","text-noci"];let i=[],l=0;const d=(e,t)=>{let n=null;const r=e.clientX,s=e.clientY,d=e.metaKey||e.ctrlKey,c=((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})(r,s).filter(e=>!a.includes(e.tagName.toLowerCase()));if(t&&c.includes(t))return o(t)?null:t;if(d)return i=[],n=c[0],o(n)?null:n;var u,m;m=c,(u=i).length===m.length&&u.every((e,t)=>e===m[t])?l<=c.length&&l++:l=0;return n=c[c.length-1-l],i=c,o(n)?null:n},c=(e,t,n,r)=>{const a=e=>{((e,t)=>{if("application"===e.data.source&&"selectedNodeChanged"===e.data.action){const n=e.data.data,r=document.querySelector(`[data-node-id="${n}"]`);if(o(r))return void t?.(null);r&&t?.(r)}})(e,t)},i=n=>{((e,t,n,o)=>{if(e.preventDefault(),e.stopPropagation(),t&&!t.contains(e.target))return s(t),void o(null);o(d(e,n))})(n,e,r(),t)},l=e=>{console.log("Esacpe Handler",e),"Escape"===e.key&&(e.preventDefault(),e.stopPropagation(),n?.())};return window.addEventListener("message",a),document.addEventListener("click",i),document.addEventListener("keydown",l),()=>{window.removeEventListener("message",a),document.removeEventListener("click",i),document.removeEventListener("keydown",l)}};function u(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 m=(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)},p=(e,t)=>{if(!e)return;const o=r(t);o&&o.remove();const s=((e,t)=>{const{top:o,left:r,width:s,height:a}=u(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 c=document.createElementNS("http://www.w3.org/2000/svg","rect");return c.setAttribute("x","0"),c.setAttribute("y","0"),c.setAttribute("width","100%"),c.setAttribute("height","100%"),c.classList.add("highlight-frame-rect"),d.appendChild(c),l.appendChild(d),l})(e,t);"true"===e.contentEditable&&s.classList.add("is-editable"),m(e,s),t.appendChild(s)},v=(e,t)=>{const o=r(t),s=n(["zoom","current"])??1;if(!o)return;s>=.3?t.style.setProperty("--tool-opacity","1"):t.style.setProperty("--tool-opacity","0");const{top:a,left:i,width:l,height:d}=u(e,t);o.style.setProperty("--frame-top",`${a}px`),o.style.setProperty("--frame-left",`${i}px`),o.style.setProperty("--frame-width",`${l}px`),o.style.setProperty("--frame-height",`${d}px`)},h=(e,t)=>{const n=r(t);if(!n)return;const o=e.className,s=/\bhidden\b/.test(o)||e.classList.contains("select-none");n.style.display=s?"none":""},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,()=>{v(e,t)});return()=>n.disconnect()},g=()=>{let e=null,t=!1,n=null;const o=()=>{if(t||!e)return;t=!0;var o;(o=e).contentEditable="false",o.classList.remove("is-editable"),o.style.outline="none",n?.(),e=null,t=!1};return{enableEditMode:(t,r)=>{if(e===t)return;e&&e!==t&&o();const s=(e=>Array.from(e.childNodes).some(e=>e.nodeType===Node.TEXT_NODE&&e.textContent?.trim()))(t);s&&(e=t,(e=>{e.contentEditable="true",e.classList.add("is-editable"),e.style.outline="none"})(t),n=((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,r,o))},blurEditMode:o,getEditableNode:()=>e,isEditing:()=>null!==e}},b=320,w=1680,E=[{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"}],x=(e,t,n)=>{const o=parseFloat(document.body.dataset.zoom||"1"),r=((e,t)=>{const n=e+Math.round(t);return Math.max(b,Math.min(w,n))})(n,(e.clientX-t)/o);return r},L=(e,t)=>{e.style.setProperty("--container-width",`${t}px`),((e,t)=>{e.querySelectorAll(".resize-preset-button").forEach((e,n)=>{n<E.length&&(E[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,r=null,a=null;const i=g(),l=t(v),d=e=>{if(a!==e){if(a=e,i.isEditing()){const t=i.getEditableNode();t&&t!==e&&i.blurEditMode()}var t,s;o?.disconnect(),r?.disconnect(),e&&n&&(i.enableEditMode(e,n),o=((e,t)=>{const n=new ResizeObserver(e=>{t(e)});return n.observe(e),n})(n,()=>{l(e,n)}),r=new MutationObserver(()=>{l(e,n),h(e,n)}),r.observe(e,{attributes:!0,attributeFilter:["class"]})),t="selectedNodeChanged",s=e?.getAttribute("data-node-id")??null,window.parent.postMessage({source:"node-edit-utils",action:t,data:s,timestamp:Date.now()},"*"),p(e,n),e&&n&&h(e,n)}},u=c(n,d,()=>{i.isEditing()&&i.blurEditMode(),a&&n&&(s(n),a=null,o?.disconnect(),r?.disconnect())},i.getEditableNode),m={selectNode:d,getSelectedNode:()=>a,refreshHighlightFrame:()=>{l(a,n)},clearSelectedNode:()=>{s(n),a=null,o?.disconnect(),r?.disconnect()},getEditableNode:()=>i.getEditableNode(),cleanup:()=>{u(),o?.disconnect(),r?.disconnect(),i.blurEditMode(),l.cleanup()}};var f,y;return f="nodeTools",y=m,"undefined"!=typeof window&&(window[f]=y),m},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",E.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,L);let s=!1,a=0,i=0;const l=t(t=>{if(!s)return;n&&(n.style.cursor="ew-resize");const o=x(t,a,i);L(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=>{L(e,t)},cleanup:()=>{s=!1,l?.cleanup(),d(),r.remove()}}}});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@node-edit-utils/core",
3
- "version": "2.0.8",
3
+ "version": "2.1.0",
4
4
  "description": "Utilities for editing nodes in a dom tree.",
5
5
  "type": "module",
6
6
  "main": "dist/node-edit-utils.cjs.js",
@@ -4,7 +4,7 @@ export const updateHighlightFrameVisibility = (node: HTMLElement, nodeProvider:
4
4
  const frame = getHighlightFrameElement(nodeProvider);
5
5
  if (!frame) return;
6
6
 
7
- const hasHiddenClass = node.classList.contains("hidden") || node.classList.contains("select-none");
7
+ const className = node.className;
8
+ const hasHiddenClass = /\bhidden\b/.test(className) || node.classList.contains("select-none");
8
9
  frame.style.display = hasHiddenClass ? "none" : "";
9
10
  };
10
-
@@ -0,0 +1,4 @@
1
+ export const isLocked = (node: HTMLElement | null): boolean => {
2
+ return node?.classList.contains("select-none") ?? false;
3
+ };
4
+
@@ -1,5 +1,6 @@
1
1
  import { IGNORED_DOM_ELEMENTS } from "./constants";
2
2
  import { getElementsFromPoint } from "./helpers/getElementsFromPoint";
3
+ import { isLocked } from "./helpers/isLocked";
3
4
  import { targetSameCandidates } from "./helpers/targetSameCandidates";
4
5
 
5
6
  let candidateCache: Element[] = [];
@@ -14,10 +15,13 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
14
15
  const clickThrough = event.metaKey || event.ctrlKey;
15
16
 
16
17
  const candidates = getElementsFromPoint(clickX, clickY).filter(
17
- (element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase()) && !element.classList.contains("select-none")
18
+ (element) => !IGNORED_DOM_ELEMENTS.includes(element.tagName.toLowerCase())
18
19
  );
19
20
 
20
21
  if (editableNode && candidates.includes(editableNode)) {
22
+ if (isLocked(editableNode)) {
23
+ return null;
24
+ }
21
25
  return editableNode;
22
26
  }
23
27
 
@@ -25,6 +29,9 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
25
29
  candidateCache = [];
26
30
 
27
31
  selectedNode = candidates[0] as HTMLElement;
32
+ if (isLocked(selectedNode)) {
33
+ return null;
34
+ }
28
35
  return selectedNode;
29
36
  }
30
37
 
@@ -40,5 +47,9 @@ export const selectNode = (event: MouseEvent, editableNode: HTMLElement | null):
40
47
 
41
48
  candidateCache = candidates;
42
49
 
50
+ if (isLocked(selectedNode)) {
51
+ return null;
52
+ }
53
+
43
54
  return selectedNode;
44
55
  };
@@ -1,5 +1,3 @@
1
- import { disableCanvasTextMode } from "../../canvas/disableCanvasTextMode";
2
- import { enableCanvasTextMode } from "../../canvas/enableCanvasTextMode";
3
1
  import { setupNodeListeners } from "./events/setupNodeListeners";
4
2
  import { hasTextContent } from "./helpers/hasTextContent";
5
3
  import { makeNodeEditable } from "./helpers/makeNodeEditable";
@@ -26,7 +24,7 @@ export const nodeText = (): NodeText => {
26
24
  editableNode = node;
27
25
 
28
26
  makeNodeEditable(node);
29
- enableCanvasTextMode();
27
+ //enableCanvasTextMode();
30
28
 
31
29
  cleanup = setupNodeListeners(node, nodeProvider, blurEditMode);
32
30
  }
@@ -50,7 +48,7 @@ export const nodeText = (): NodeText => {
50
48
  const nodeToCleanup = editableNode;
51
49
 
52
50
  makeNodeNonEditable(nodeToCleanup);
53
- disableCanvasTextMode();
51
+ //disableCanvasTextMode();
54
52
  cleanup?.();
55
53
 
56
54
  editableNode = null;
@@ -1,17 +1,24 @@
1
+ import { isLocked } from "../node-tools/select/helpers/isLocked";
2
+
1
3
  export const processPostMessage = (event: MessageEvent, onNodeSelected?: (node: HTMLElement | null) => void) => {
2
- if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
3
- if (event.data.action === "zoom") {
4
- const zoom = event.data.data;
5
- }
6
- }
4
+ // if (event.data.source === "markup-canvas" && event.data.canvasName === "canvas") {
5
+ // if (event.data.action === "zoom") {
6
+ // // Zoom handling can be implemented here if needed
7
+ // }
8
+ // }
7
9
 
8
10
  if (event.data.source === "application") {
9
11
  if (event.data.action === "selectedNodeChanged") {
10
12
  const nodeId = event.data.data;
11
- const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`);
13
+ const selectedNode = document.querySelector(`[data-node-id="${nodeId}"]`) as HTMLElement | null;
14
+
15
+ if (isLocked(selectedNode)) {
16
+ onNodeSelected?.(null);
17
+ return;
18
+ }
12
19
 
13
20
  if (selectedNode) {
14
- onNodeSelected?.(selectedNode as HTMLElement);
21
+ onNodeSelected?.(selectedNode);
15
22
  }
16
23
  }
17
24
  }