@inditextech/weave-sdk 0.50.0 → 0.52.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.
package/dist/sdk.cjs CHANGED
@@ -15645,7 +15645,7 @@ const WEAVE_NODES_SELECTION_LAYER_ID = "selectionLayer";
15645
15645
 
15646
15646
  //#endregion
15647
15647
  //#region src/plugins/context-menu/constants.ts
15648
- const WEAVE_CONTEXT_MENU_KEY = "contextMenu";
15648
+ const WEAVE_CONTEXT_MENU_PLUGIN_KEY = "contextMenu";
15649
15649
  const WEAVE_CONTEXT_MENU_X_OFFSET_DEFAULT = 4;
15650
15650
  const WEAVE_CONTEXT_MENU_Y_OFFSET_DEFAULT = 4;
15651
15651
  const WEAVE_CONTEXT_MENU_TAP_HOLD_TIMEOUT = 500;
@@ -15665,10 +15665,10 @@ function clearContainerTargets(instance) {
15665
15665
  const containers = instance.getContainerNodes();
15666
15666
  for (const container of containers) container.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetLeave, { bubbles: true });
15667
15667
  }
15668
- function containerOverCursor(instance, ignoreNodes) {
15668
+ function containerOverCursor(instance, ignoreNodes, definedCursorPosition) {
15669
15669
  konva.default.hitOnDragEnabled = true;
15670
15670
  const stage = instance.getStage();
15671
- const cursorPosition = stage.getRelativePointerPosition();
15671
+ const cursorPosition = definedCursorPosition ?? stage.getRelativePointerPosition();
15672
15672
  if (!cursorPosition) return void 0;
15673
15673
  const containerUnderPointer = new Set();
15674
15674
  stage.find(".containerCapable").reverse().forEach((node) => {
@@ -15775,7 +15775,10 @@ function getBoundingBox(stage, nodes) {
15775
15775
  let maxX = -Infinity;
15776
15776
  let maxY = -Infinity;
15777
15777
  for (const node of nodes) {
15778
- const box = node.getRealClientRect({ skipTransform: false });
15778
+ const box = node.getRealClientRect({
15779
+ relativeTo: stage,
15780
+ skipTransform: false
15781
+ });
15779
15782
  minX = Math.min(minX, box.x);
15780
15783
  minY = Math.min(minY, box.y);
15781
15784
  maxX = Math.max(maxX, box.x + box.width);
@@ -15829,6 +15832,87 @@ function containsNodeDeep(nodes, target) {
15829
15832
  }
15830
15833
  return false;
15831
15834
  }
15835
+ function getSelectedNodesMetadata(transformer) {
15836
+ const firstNode = transformer.getNodes()[0];
15837
+ const firstNodeClientRect = firstNode.getClientRect();
15838
+ const rectCoordsMin = {
15839
+ x: firstNodeClientRect.x,
15840
+ y: firstNodeClientRect.y
15841
+ };
15842
+ const rectCoordsMax = {
15843
+ x: firstNodeClientRect.x + firstNodeClientRect.width,
15844
+ y: firstNodeClientRect.y + firstNodeClientRect.height
15845
+ };
15846
+ const nodes = [];
15847
+ for (const node of transformer.getNodes()) {
15848
+ const clientRect = node.getClientRect();
15849
+ if (clientRect.x < rectCoordsMin.x) rectCoordsMin.x = clientRect.x;
15850
+ if (clientRect.y < rectCoordsMin.y) rectCoordsMin.y = clientRect.y;
15851
+ if (clientRect.x + clientRect.width > rectCoordsMax.x) rectCoordsMax.x = clientRect.x + clientRect.width;
15852
+ if (clientRect.y + clientRect.height > rectCoordsMax.y) rectCoordsMax.y = clientRect.y + clientRect.height;
15853
+ nodes.push(node.getAttrs().id);
15854
+ }
15855
+ return {
15856
+ width: rectCoordsMax.x - rectCoordsMin.x,
15857
+ height: rectCoordsMax.y - rectCoordsMin.y,
15858
+ nodes
15859
+ };
15860
+ }
15861
+ function getTargetAndSkipNodes(instance, e) {
15862
+ const nodesSelectionPlugin = instance.getPlugin("nodesSelection");
15863
+ let skipNodes = [];
15864
+ let node = void 0;
15865
+ if (e.type === "dragmove" && nodesSelectionPlugin && nodesSelectionPlugin.getTransformer().nodes().length === 1) {
15866
+ node = nodesSelectionPlugin.getTransformer().nodes()[0];
15867
+ skipNodes.push(node.getAttrs().id ?? "");
15868
+ }
15869
+ if (e.type === "dragmove" && nodesSelectionPlugin && nodesSelectionPlugin.getTransformer().nodes().length > 1) {
15870
+ const { nodes } = getSelectedNodesMetadata(nodesSelectionPlugin.getTransformer());
15871
+ node = nodesSelectionPlugin.getTransformer();
15872
+ skipNodes = [...nodes];
15873
+ }
15874
+ if (e.type === "transform") {
15875
+ node = e.target;
15876
+ skipNodes.push(node.getAttrs().id ?? "");
15877
+ }
15878
+ return {
15879
+ targetNode: node,
15880
+ skipNodes
15881
+ };
15882
+ }
15883
+ function getVisibleNodesInViewport(stage, referenceLayer) {
15884
+ const scale = stage.scaleX();
15885
+ const stagePos = stage.position();
15886
+ const stageSize = {
15887
+ width: stage.width(),
15888
+ height: stage.height()
15889
+ };
15890
+ const viewRect = {
15891
+ x: -stagePos.x / scale,
15892
+ y: -stagePos.y / scale,
15893
+ width: stageSize.width / scale,
15894
+ height: stageSize.height / scale
15895
+ };
15896
+ const visibleNodes = [];
15897
+ referenceLayer?.find(".node").forEach((node) => {
15898
+ if (!node.isVisible()) return;
15899
+ const box = node.getClientRect({
15900
+ relativeTo: stage,
15901
+ skipStroke: true,
15902
+ skipShadow: true
15903
+ });
15904
+ const intersects = box.x + box.width > viewRect.x && box.x < viewRect.x + viewRect.width && box.y + box.height > viewRect.y && box.y < viewRect.y + viewRect.height;
15905
+ if (intersects) visibleNodes.push(node);
15906
+ });
15907
+ return visibleNodes;
15908
+ }
15909
+ function getClosestParentWithId(el) {
15910
+ while (el) {
15911
+ if (el.id) return el;
15912
+ el = el.parentElement;
15913
+ }
15914
+ return null;
15915
+ }
15832
15916
 
15833
15917
  //#endregion
15834
15918
  //#region src/actions/selection-tool/constants.ts
@@ -15863,7 +15947,7 @@ var WeaveContextMenuPlugin = class extends WeavePlugin {
15863
15947
  this.pointers = {};
15864
15948
  }
15865
15949
  getName() {
15866
- return WEAVE_CONTEXT_MENU_KEY;
15950
+ return WEAVE_CONTEXT_MENU_PLUGIN_KEY;
15867
15951
  }
15868
15952
  onInit() {
15869
15953
  this.initEvents();
@@ -15905,14 +15989,16 @@ var WeaveContextMenuPlugin = class extends WeavePlugin {
15905
15989
  const containerRect = stage.container().getBoundingClientRect();
15906
15990
  const pointerPos = stage.getPointerPosition();
15907
15991
  if (containerRect && pointerPos) {
15908
- const point = {
15992
+ const contextMenuPoint = {
15909
15993
  x: containerRect.left + pointerPos.x + (this.config.xOffset ?? 4),
15910
15994
  y: containerRect.top + pointerPos.y + (this.config.yOffset ?? 4)
15911
15995
  };
15996
+ const clickPoint = pointerPos;
15912
15997
  this.contextMenuVisible = true;
15913
15998
  this.instance.emitEvent("onNodeContextMenu", {
15914
15999
  selection: nodes,
15915
- point,
16000
+ contextMenuPoint,
16001
+ clickPoint,
15916
16002
  visible: true
15917
16003
  });
15918
16004
  }
@@ -15921,7 +16007,11 @@ var WeaveContextMenuPlugin = class extends WeavePlugin {
15921
16007
  this.contextMenuVisible = false;
15922
16008
  this.instance.emitEvent("onNodeContextMenu", {
15923
16009
  selection: [],
15924
- point: {
16010
+ contextMenuPoint: {
16011
+ x: 0,
16012
+ y: 0
16013
+ },
16014
+ clickPoint: {
15925
16015
  x: 0,
15926
16016
  y: 0
15927
16017
  },
@@ -15966,9 +16056,6 @@ var WeaveContextMenuPlugin = class extends WeavePlugin {
15966
16056
  this.tapHold = false;
15967
16057
  }
15968
16058
  });
15969
- window.addEventListener("contextmenu", (e) => {
15970
- e.preventDefault();
15971
- });
15972
16059
  stage.on("contextmenu", (e) => {
15973
16060
  e.evt.preventDefault();
15974
16061
  if (!this.enabled) return;
@@ -15980,13 +16067,15 @@ var WeaveContextMenuPlugin = class extends WeavePlugin {
15980
16067
  const containerRect = stage.container().getBoundingClientRect();
15981
16068
  const pointerPos = stage.getPointerPosition();
15982
16069
  if (containerRect && pointerPos) {
15983
- const point = {
16070
+ const contextMenuPoint = {
15984
16071
  x: containerRect.left + pointerPos.x + (this.config.xOffset ?? 4),
15985
16072
  y: containerRect.top + pointerPos.y + (this.config.yOffset ?? 4)
15986
16073
  };
16074
+ const clickPoint = pointerPos;
15987
16075
  this.instance.emitEvent("onNodeContextMenu", {
15988
16076
  selection: [],
15989
- point,
16077
+ contextMenuPoint,
16078
+ clickPoint,
15990
16079
  visible: false
15991
16080
  });
15992
16081
  }
@@ -16016,6 +16105,45 @@ const WEAVE_USER_SELECTION_KEY = "userSelection";
16016
16105
  const WEAVE_STAGE_NODE_TYPE = "stage";
16017
16106
  const WEAVE_STAGE_DEFAULT_MODE = "default";
16018
16107
 
16108
+ //#endregion
16109
+ //#region src/plugins/nodes-edge-snapping/constants.ts
16110
+ const WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY = "nodesEdgeSnapping";
16111
+ const GUIDE_LINE_NAME = "guide-edge-snapping-line";
16112
+ const GUIDE_LINE_DEFAULT_CONFIG = {
16113
+ stroke: "#ff0000",
16114
+ strokeWidth: 1,
16115
+ dash: []
16116
+ };
16117
+ const GUIDE_LINE_DRAG_SNAPPING_THRESHOLD = 3;
16118
+ const GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD = 3;
16119
+ const GUIDE_ORIENTATION = {
16120
+ ["HORIZONTAL"]: "H",
16121
+ ["VERTICAL"]: "V"
16122
+ };
16123
+ const NODE_SNAP = {
16124
+ ["START"]: "start",
16125
+ ["CENTER"]: "center",
16126
+ ["END"]: "end"
16127
+ };
16128
+
16129
+ //#endregion
16130
+ //#region src/plugins/nodes-distance-snapping/constants.ts
16131
+ const WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY = "nodesDistanceSnapping";
16132
+ const GUIDE_HORIZONTAL_LINE_NAME = "guide-distance-snapping-horizontal-line";
16133
+ const GUIDE_VERTICAL_LINE_NAME = "guide-distance-snapping-vertical-line";
16134
+ const GUIDE_ENTER_SNAPPING_TOLERANCE = 3;
16135
+ const GUIDE_EXIT_SNAPPING_TOLERANCE = 5;
16136
+ const NODE_SNAP_HORIZONTAL = {
16137
+ ["LEFT"]: "left",
16138
+ ["CENTER"]: "center",
16139
+ ["RIGHT"]: "right"
16140
+ };
16141
+ const NODE_SNAP_VERTICAL = {
16142
+ ["TOP"]: "top",
16143
+ ["MIDDLE"]: "middle",
16144
+ ["BOTTOM"]: "bottom"
16145
+ };
16146
+
16019
16147
  //#endregion
16020
16148
  //#region src/plugins/nodes-selection/nodes-selection.ts
16021
16149
  var WeaveNodesSelectionPlugin = class extends WeavePlugin {
@@ -16156,7 +16284,8 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16156
16284
  selectionLayer?.add(selectionRectangle);
16157
16285
  const tr = new konva.default.Transformer({
16158
16286
  id: "selectionTransformer",
16159
- ...this.selectionOriginalConfig
16287
+ ...this.selectionOriginalConfig,
16288
+ listening: true
16160
16289
  });
16161
16290
  selectionLayer?.add(tr);
16162
16291
  const trHover = new konva.default.Transformer({
@@ -16168,6 +16297,29 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16168
16297
  listening: false
16169
16298
  });
16170
16299
  selectionLayer?.add(trHover);
16300
+ stage.on("pointermove", () => {
16301
+ if (tr.nodes().length === 1 && tr.nodes()[0].getAttrs().isContainerPrincipal) {
16302
+ const pos = stage.getPointerPosition();
16303
+ if (!pos) return;
16304
+ const shapeUnder = stage.getIntersection(pos);
16305
+ if (!shapeUnder) {
16306
+ tr.setAttrs({ listening: true });
16307
+ tr.forceUpdate();
16308
+ }
16309
+ if (shapeUnder && tr.getChildren().includes(shapeUnder) && shapeUnder.name() === "back") {
16310
+ tr.setAttrs({ listening: false });
16311
+ tr.forceUpdate();
16312
+ }
16313
+ if (shapeUnder && tr.nodes()[0].getChildren().includes(shapeUnder)) {
16314
+ tr.setAttrs({ listening: false });
16315
+ tr.forceUpdate();
16316
+ }
16317
+ if (shapeUnder && !tr.getChildren().includes(shapeUnder) && tr.nodes()[0].getChildren().includes(shapeUnder)) {
16318
+ tr.setAttrs({ listening: true });
16319
+ tr.forceUpdate();
16320
+ }
16321
+ }
16322
+ });
16171
16323
  tr.on("transformstart", () => {
16172
16324
  this.triggerSelectedNodesEvent();
16173
16325
  });
@@ -16257,12 +16409,10 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16257
16409
  const nodeHandler = this.instance.getNodeHandler(node.getAttrs().nodeType);
16258
16410
  if (nodeHandler) this.instance.updateNode(nodeHandler.serialize(node));
16259
16411
  let containerToMove = this.instance.getMainLayer();
16260
- if (layerToMove) {
16261
- containerToMove = layerToMove;
16262
- containerToMove.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetLeave, { bubbles: true });
16263
- }
16412
+ if (layerToMove) containerToMove = layerToMove;
16264
16413
  let moved = false;
16265
16414
  if (containerToMove && !selectionContainsFrames) moved = moveNodeToContainer(this.instance, node, containerToMove);
16415
+ if (containerToMove) containerToMove.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetLeave, { bubbles: true });
16266
16416
  if (!nodeHandler) return resolve();
16267
16417
  toSelect.push(node.getAttrs().id ?? "");
16268
16418
  if (!moved) toUpdate.push(nodeHandler.serialize(node));
@@ -16479,10 +16629,14 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16479
16629
  };
16480
16630
  stage.on("pointermove", handleMouseMove);
16481
16631
  stage.on("pointerup", (e) => {
16632
+ this.tr.setAttrs({ listening: true });
16482
16633
  const moved = this.checkMoved(e);
16483
16634
  this.checkDoubleTap(e);
16484
16635
  delete this.pointers[e.evt.pointerId];
16485
- if (stage.mode() === WEAVE_STAGE_DEFAULT_MODE) this.getSnappingPlugin()?.cleanupEvaluateGuidelines();
16636
+ if (stage.mode() === WEAVE_STAGE_DEFAULT_MODE) {
16637
+ this.getNodesEdgeSnappingPlugin()?.cleanupGuidelines();
16638
+ this.getNodesDistanceSnappingPlugin()?.cleanupGuidelines();
16639
+ }
16486
16640
  const contextMenuPlugin = this.getContextMenuPlugin();
16487
16641
  if (!this.initialized) {
16488
16642
  this.hideSelectorArea();
@@ -16723,11 +16877,15 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16723
16877
  this.enabled = false;
16724
16878
  }
16725
16879
  getContextMenuPlugin() {
16726
- const contextMenuPlugin = this.instance.getPlugin("contextMenu");
16880
+ const contextMenuPlugin = this.instance.getPlugin(WEAVE_CONTEXT_MENU_PLUGIN_KEY);
16727
16881
  return contextMenuPlugin;
16728
16882
  }
16729
- getSnappingPlugin() {
16730
- const snappingPlugin = this.instance.getPlugin("nodesSnapping");
16883
+ getNodesEdgeSnappingPlugin() {
16884
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY);
16885
+ return snappingPlugin;
16886
+ }
16887
+ getNodesDistanceSnappingPlugin() {
16888
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY);
16731
16889
  return snappingPlugin;
16732
16890
  }
16733
16891
  getSelectorConfig() {
@@ -16738,6 +16896,7 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16738
16896
  //#endregion
16739
16897
  //#region src/plugins/copy-paste-nodes/constants.ts
16740
16898
  const WEAVE_COPY_PASTE_NODES_KEY = "copyPasteNodes";
16899
+ const WEAVE_COPY_PASTE_PASTE_CATCHER_ID = "weave-paste-catcher";
16741
16900
  const WEAVE_COPY_PASTE_PASTE_MODES = {
16742
16901
  ["INTERNAL"]: "internal",
16743
16902
  ["EXTERNAL"]: "external",
@@ -16763,18 +16922,6 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16763
16922
  onInit() {
16764
16923
  this.initEvents();
16765
16924
  }
16766
- readClipboardData() {
16767
- return new Promise((resolve, reject) => {
16768
- setTimeout(async () => {
16769
- if (typeof navigator.clipboard === "undefined") return reject(new Error("Clipboard API not supported"));
16770
- navigator.clipboard.readText().then((text) => {
16771
- resolve(this.isWeaveData(text));
16772
- }).catch((error) => {
16773
- reject(error);
16774
- });
16775
- });
16776
- });
16777
- }
16778
16925
  writeClipboardData(data) {
16779
16926
  return new Promise((resolve, reject) => {
16780
16927
  setTimeout(async () => {
@@ -16787,8 +16934,36 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16787
16934
  });
16788
16935
  });
16789
16936
  }
16937
+ existsPasteCatcher() {
16938
+ return document.getElementById(WEAVE_COPY_PASTE_PASTE_CATCHER_ID) !== null;
16939
+ }
16940
+ createPasteCatcher() {
16941
+ const stage = this.instance.getStage();
16942
+ if (!this.existsPasteCatcher()) {
16943
+ const catcher = document.createElement("div");
16944
+ catcher.id = WEAVE_COPY_PASTE_PASTE_CATCHER_ID;
16945
+ catcher.contentEditable = "true";
16946
+ catcher.style.position = "absolute";
16947
+ catcher.style.top = "-1px";
16948
+ catcher.style.left = "-1px";
16949
+ catcher.style.width = "1px";
16950
+ catcher.style.height = "1px";
16951
+ catcher.style.zIndex = "-1";
16952
+ catcher.style.outline = "none";
16953
+ catcher.style.opacity = "0";
16954
+ catcher.tabIndex = 0;
16955
+ const stageContainer = stage.container();
16956
+ if (stageContainer?.parentNode) stageContainer.parentNode.appendChild(catcher);
16957
+ }
16958
+ }
16959
+ focusPasteCatcher() {
16960
+ const catcher = document.getElementById(WEAVE_COPY_PASTE_PASTE_CATCHER_ID);
16961
+ catcher?.focus();
16962
+ }
16790
16963
  initEvents() {
16791
16964
  const stage = this.instance.getStage();
16965
+ this.createPasteCatcher();
16966
+ const catcher = document.getElementById(WEAVE_COPY_PASTE_PASTE_CATCHER_ID);
16792
16967
  window.addEventListener("keydown", async (e) => {
16793
16968
  if (stage.isFocused() && e.key === "c" && (e.ctrlKey || e.metaKey)) {
16794
16969
  e.preventDefault();
@@ -16796,49 +16971,49 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16796
16971
  return;
16797
16972
  }
16798
16973
  if (stage.isFocused() && e.key === "v" && (e.ctrlKey || e.metaKey)) {
16799
- const catcher = document.getElementById("paste-catcher");
16800
- catcher?.focus();
16974
+ this.focusPasteCatcher();
16801
16975
  if (!this.enabled) return;
16802
- try {
16803
- if (this.isClipboardAPIAvailable()) {
16804
- const continueToPaste = await this.readClipboardData();
16805
- if (continueToPaste) {
16806
- this.state = COPY_PASTE_NODES_PLUGIN_STATE.PASTING;
16807
- this.handlePaste();
16808
- e.preventDefault();
16809
- return;
16810
- }
16811
- }
16812
- } catch (ex) {
16813
- this.instance.emitEvent("onPaste", ex);
16814
- }
16815
- e.preventDefault();
16816
- stage.container().focus();
16817
- return;
16818
16976
  }
16819
16977
  });
16820
- document.addEventListener("paste", async (e) => {
16978
+ if (catcher) catcher.addEventListener("paste", async (e) => {
16821
16979
  e.preventDefault();
16822
- const items = e.clipboardData?.items;
16823
- if (!items) return;
16980
+ const dataList = e.clipboardData?.items;
16981
+ let items = void 0;
16982
+ if (!items) {
16983
+ if (this.isClipboardAPIAvailable()) items = await navigator.clipboard.read();
16984
+ }
16985
+ if (!items && !dataList) return;
16824
16986
  let hasWeaveData = false;
16825
16987
  if (this.isClipboardAPIAvailable()) {
16826
- for (const item of items) if (item.type === "text/plain") {
16827
- const text = await this.getTextFromClipboard(item);
16828
- if (this.isWeaveData(text)) {
16829
- hasWeaveData = true;
16830
- this.toPaste = JSON.parse(text);
16831
- }
16988
+ const readText = await navigator.clipboard.readText();
16989
+ const continueToPaste = this.isWeaveData(readText);
16990
+ if (continueToPaste) hasWeaveData = true;
16991
+ } else for (const item of dataList ?? []) if (item.type === "text/plain") {
16992
+ const text = await this.getTextFromClipboard(item);
16993
+ if (this.isWeaveData(text)) {
16994
+ hasWeaveData = true;
16995
+ this.toPaste = JSON.parse(text);
16832
16996
  }
16833
16997
  }
16834
16998
  if (hasWeaveData) {
16835
16999
  this.handlePaste();
16836
17000
  return;
16837
17001
  }
16838
- const position = this.instance.getStage().getRelativePointerPosition();
16839
- if (position) this.instance.emitEvent("onPasteExternal", {
16840
- position,
16841
- dataList: items
17002
+ const container = stage.container();
17003
+ const scale = stage.scale();
17004
+ const position = stage.position();
17005
+ const width = container.clientWidth;
17006
+ const height = container.clientHeight;
17007
+ const centerX = (width / 2 - position.x) / scale.x;
17008
+ const centerY = (height / 2 - position.y) / scale.y;
17009
+ const pastePosition = {
17010
+ x: centerX,
17011
+ y: centerY
17012
+ };
17013
+ this.instance.emitEvent("onPasteExternal", {
17014
+ position: pastePosition,
17015
+ dataList,
17016
+ items
16842
17017
  });
16843
17018
  });
16844
17019
  }
@@ -16864,8 +17039,8 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16864
17039
  }
16865
17040
  mapToPasteNodes() {
16866
17041
  const nodesSelectionPlugin = this.getNodesSelectionPlugin();
16867
- const selectedNodes = nodesSelectionPlugin.getSelectedNodes();
16868
- return selectedNodes.map((node) => ({
17042
+ const selectedNodes = nodesSelectionPlugin?.getSelectedNodes();
17043
+ return (selectedNodes ?? []).map((node) => ({
16869
17044
  konvaNode: node,
16870
17045
  node: node.getAttrs()
16871
17046
  }));
@@ -16881,22 +17056,50 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16881
17056
  if (child.props.children) this.recursivelyUpdateKeys(child.props.children);
16882
17057
  }
16883
17058
  }
16884
- handlePaste() {
17059
+ handlePaste(position) {
17060
+ const stage = this.instance.getStage();
16885
17061
  if (this.toPaste) {
16886
- const { mousePoint, container } = this.instance.getMousePointer();
17062
+ const nodesToSelect = [];
16887
17063
  for (const element of Object.keys(this.toPaste.weave)) {
16888
- const node = this.toPaste.weave[element];
17064
+ const node = this.toPaste.weave[element].element;
17065
+ const posRelativeToSelection = this.toPaste.weave[element].posRelativeToSelection;
17066
+ let containerId = this.toPaste.weave[element].containerId;
16889
17067
  if (node.props.children) this.recursivelyUpdateKeys(node.props.children);
16890
17068
  const newNodeId = v4_default();
16891
17069
  delete node.props.containerId;
16892
17070
  node.key = newNodeId;
16893
17071
  node.props.id = newNodeId;
16894
- node.props.x = mousePoint.x + (node.props.x - this.toPaste.weaveMinPoint.x);
16895
- node.props.y = mousePoint.y + (node.props.y - this.toPaste.weaveMinPoint.y);
16896
- this.instance.addNode(node, container?.getAttrs().id);
17072
+ if (position) {
17073
+ const container = containerOverCursor(this.instance, [], position);
17074
+ let localPos = position;
17075
+ if (!container) {
17076
+ containerId = this.instance.getMainLayer()?.getAttrs().id ?? "";
17077
+ const scale = stage.scaleX();
17078
+ const stagePos = stage.position();
17079
+ localPos = {
17080
+ x: (localPos.x - stagePos.x) / scale,
17081
+ y: (localPos.y - stagePos.y) / scale
17082
+ };
17083
+ }
17084
+ if (container && container.getAttrs().nodeType === "frame") {
17085
+ containerId = container.getAttrs().id ?? "";
17086
+ localPos = container.getAbsoluteTransform().copy().invert().point(position);
17087
+ }
17088
+ const nodeHandler = this.instance.getNodeHandler(node.props.nodeType ?? "");
17089
+ if (nodeHandler) {
17090
+ const realOffset = nodeHandler.realOffset(node);
17091
+ node.props.x = localPos.x + realOffset.x + posRelativeToSelection.x;
17092
+ node.props.y = localPos.y + realOffset.y + posRelativeToSelection.y;
17093
+ }
17094
+ }
17095
+ this.instance.addNode(node, containerId);
17096
+ const realNode = this.instance.getStage().findOne(`#${newNodeId}`);
17097
+ if (realNode) nodesToSelect.push(realNode);
16897
17098
  this.instance.emitEvent("onPaste");
16898
- this.instance.emitEvent("onPaste", void 0);
16899
17099
  }
17100
+ this.instance.emitEvent("onPaste", void 0);
17101
+ const nodesSelectionPlugin = this.getNodesSelectionPlugin();
17102
+ nodesSelectionPlugin?.setSelectedNodes(nodesToSelect);
16900
17103
  this.toPaste = void 0;
16901
17104
  }
16902
17105
  this.cancel();
@@ -16908,8 +17111,9 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16908
17111
  stage.container().focus();
16909
17112
  this.setState(COPY_PASTE_NODES_PLUGIN_STATE.IDLE);
16910
17113
  const nodesSelectionPlugin = this.getNodesSelectionPlugin();
16911
- const selectedNodes = nodesSelectionPlugin.getSelectedNodes();
16912
- if (selectedNodes.length === 0) return;
17114
+ const selectedNodes = nodesSelectionPlugin?.getSelectedNodes();
17115
+ if (!selectedNodes || selectedNodes.length === 0) return;
17116
+ const box = getBoundingBox(stage, selectedNodes);
16913
17117
  const copyClipboard = {
16914
17118
  weaveInstanceId: this.instance.getId(),
16915
17119
  weave: {},
@@ -16918,33 +17122,67 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16918
17122
  y: 0
16919
17123
  }
16920
17124
  };
16921
- const result = this.instance.nodesToGroupSerialized(selectedNodes);
16922
- if (result && result.serializedNodes && result.serializedNodes.length > 0) {
16923
- copyClipboard.weaveMinPoint = result.minPoint;
16924
- for (const serializedNode of result.serializedNodes) copyClipboard.weave[serializedNode.key ?? ""] = serializedNode;
16925
- try {
16926
- await this.writeClipboardData(JSON.stringify(copyClipboard));
16927
- this.instance.emitEvent("onCopy");
16928
- } catch (ex) {
16929
- this.instance.emitEvent("onCopy", ex);
16930
- }
17125
+ for (const node of selectedNodes) {
17126
+ const nodeHandler = this.instance.getNodeHandler(node.getAttrs().nodeType);
17127
+ if (!nodeHandler) continue;
17128
+ const parentNode = node.getParent();
17129
+ let parentId = parentNode?.getAttrs().id;
17130
+ if (parentNode?.getAttrs().nodeId) {
17131
+ const realParent = this.instance.getStage().findOne(`#${parentNode.getAttrs().nodeId}`);
17132
+ if (realParent) parentId = realParent.getAttrs().id;
17133
+ }
17134
+ if (!parentId) continue;
17135
+ const serializedNode = nodeHandler.serialize(node);
17136
+ const nodeBox = node.getClientRect({ relativeTo: stage });
17137
+ copyClipboard.weave[serializedNode.key ?? ""] = {
17138
+ element: serializedNode,
17139
+ posRelativeToSelection: {
17140
+ x: nodeBox.x - (box?.x ?? 0),
17141
+ y: nodeBox.y - (box?.y ?? 0)
17142
+ },
17143
+ containerId: parentId
17144
+ };
17145
+ }
17146
+ try {
17147
+ await this.writeClipboardData(JSON.stringify(copyClipboard));
17148
+ this.instance.emitEvent("onCopy");
17149
+ } catch (ex) {
17150
+ this.instance.emitEvent("onCopy", ex);
16931
17151
  }
16932
17152
  }
16933
17153
  async copy() {
16934
17154
  await this.performCopy();
16935
17155
  }
16936
- async paste() {
17156
+ async paste(position) {
17157
+ const stage = this.instance.getStage();
16937
17158
  try {
16938
- const continueToPaste = await this.readClipboardData();
16939
- if (continueToPaste) this.handlePaste();
17159
+ const readText = await navigator.clipboard.readText();
17160
+ const continueToPaste = this.isWeaveData(readText);
17161
+ if (continueToPaste) {
17162
+ this.handlePaste(position);
17163
+ return;
17164
+ }
16940
17165
  } catch (ex) {
16941
17166
  this.instance.emitEvent("onPaste", ex);
16942
17167
  }
16943
17168
  try {
16944
17169
  const items = await navigator.clipboard.read();
16945
- const position = this.instance.getStage().getRelativePointerPosition();
16946
- if (position) this.instance.emitEvent("onPasteExternal", {
16947
- position,
17170
+ let pastePosition = position;
17171
+ if (typeof pastePosition === "undefined") {
17172
+ const container = stage.container();
17173
+ const scale = stage.scale();
17174
+ const position$1 = stage.position();
17175
+ const centerClientX = container.clientWidth / 2;
17176
+ const centerClientY = container.clientHeight / 2;
17177
+ const centerX = (centerClientX - position$1.x) / scale.x;
17178
+ const centerY = (centerClientY - position$1.y) / scale.y;
17179
+ pastePosition = {
17180
+ x: centerX,
17181
+ y: centerY
17182
+ };
17183
+ }
17184
+ this.instance.emitEvent("onPasteExternal", {
17185
+ position: pastePosition,
16948
17186
  items
16949
17187
  });
16950
17188
  } catch (ex) {
@@ -16966,7 +17204,6 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16966
17204
  }
16967
17205
  getNodesSelectionPlugin() {
16968
17206
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
16969
- if (!nodesSelectionPlugin) throw new Error("WeaveNodesSelectionPlugin plugin not found");
16970
17207
  return nodesSelectionPlugin;
16971
17208
  }
16972
17209
  isClipboardApiEnabled() {
@@ -16975,7 +17212,8 @@ var WeaveCopyPasteNodesPlugin = class extends WeavePlugin {
16975
17212
  async getAvailablePasteMode(canHandleExternal) {
16976
17213
  if (!this.isClipboardApiEnabled()) return WEAVE_COPY_PASTE_PASTE_MODES.CLIPBOARD_API_NOT_SUPPORTED;
16977
17214
  try {
16978
- const allowPaste = await this.readClipboardData();
17215
+ const readText = await navigator.clipboard.readText();
17216
+ const allowPaste = this.isWeaveData(readText);
16979
17217
  if (allowPaste) return WEAVE_COPY_PASTE_PASTE_MODES.INTERNAL;
16980
17218
  const items = await navigator.clipboard.read();
16981
17219
  if (await canHandleExternal(items)) return WEAVE_COPY_PASTE_PASTE_MODES.EXTERNAL;
@@ -17139,9 +17377,9 @@ var WeaveNode = class {
17139
17377
  const handleTransform = (e) => {
17140
17378
  const node$1 = e.target;
17141
17379
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
17142
- const nodesSnappingPlugin = this.instance.getPlugin("nodesSnapping");
17380
+ const nodesEdgeSnappingPlugin = this.getNodesEdgeSnappingPlugin();
17143
17381
  if (nodesSelectionPlugin && this.isSelecting() && this.isNodeSelected(node$1)) nodesSelectionPlugin.getTransformer().forceUpdate();
17144
- if (nodesSnappingPlugin && transforming && this.isSelecting() && this.isNodeSelected(node$1)) nodesSnappingPlugin.evaluateGuidelines(e);
17382
+ if (nodesEdgeSnappingPlugin && transforming && this.isSelecting() && this.isNodeSelected(node$1)) nodesEdgeSnappingPlugin.evaluateGuidelines(e);
17145
17383
  };
17146
17384
  node.on("transform", (0, import_lodash.throttle)(handleTransform, 100));
17147
17385
  node.on("transformend", (e) => {
@@ -17149,8 +17387,8 @@ var WeaveNode = class {
17149
17387
  this.instance.emitEvent("onTransform", null);
17150
17388
  transforming = false;
17151
17389
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
17152
- const nodesSnappingPlugin = this.instance.getPlugin("nodesSnapping");
17153
- if (nodesSnappingPlugin) nodesSnappingPlugin.cleanupEvaluateGuidelines();
17390
+ const nodesSnappingPlugin = this.getNodesEdgeSnappingPlugin();
17391
+ if (nodesSnappingPlugin) nodesSnappingPlugin.cleanupGuidelines();
17154
17392
  if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
17155
17393
  this.scaleReset(node$1);
17156
17394
  const nodeHandler = this.instance.getNodeHandler(node$1.getAttrs().nodeType);
@@ -17194,7 +17432,7 @@ var WeaveNode = class {
17194
17432
  if (this.isSelecting() && this.isNodeSelected(node) && this.getSelectionPlugin()?.getSelectedNodes().length === 1) {
17195
17433
  clearContainerTargets(this.instance);
17196
17434
  const layerToMove = containerOverCursor(this.instance, [node]);
17197
- if (layerToMove && !hasFrames(node)) layerToMove.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetEnter, { bubbles: true });
17435
+ if (layerToMove && !hasFrames(node) && node.isDragging()) layerToMove.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetEnter, { bubbles: true });
17198
17436
  }
17199
17437
  };
17200
17438
  node.on("dragmove", (0, import_lodash.throttle)(handleDragMove, 100));
@@ -17208,16 +17446,16 @@ var WeaveNode = class {
17208
17446
  this.instance.emitEvent("onDrag", null);
17209
17447
  if (this.isSelecting() && this.isNodeSelected(node) && this.getSelectionPlugin()?.getSelectedNodes().length === 1) {
17210
17448
  clearContainerTargets(this.instance);
17211
- const nodesSnappingPlugin = this.instance.getPlugin("nodesSnapping");
17212
- if (nodesSnappingPlugin) nodesSnappingPlugin.cleanupEvaluateGuidelines();
17449
+ const nodesEdgeSnappingPlugin = this.getNodesEdgeSnappingPlugin();
17450
+ const nodesDistanceSnappingPlugin = this.getNodesDistanceSnappingPlugin();
17451
+ if (nodesEdgeSnappingPlugin) nodesEdgeSnappingPlugin.cleanupGuidelines();
17452
+ if (nodesDistanceSnappingPlugin) nodesDistanceSnappingPlugin.cleanupGuidelines();
17213
17453
  const layerToMove = containerOverCursor(this.instance, [node]);
17214
17454
  let containerToMove = this.instance.getMainLayer();
17215
- if (layerToMove) {
17216
- containerToMove = layerToMove;
17217
- containerToMove.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetLeave, { bubbles: true });
17218
- }
17455
+ if (layerToMove) containerToMove = layerToMove;
17219
17456
  let moved = false;
17220
17457
  if (containerToMove && !hasFrames(node)) moved = moveNodeToContainer(this.instance, e.target, containerToMove);
17458
+ if (containerToMove) containerToMove.fire(__inditextech_weave_types.WEAVE_NODE_CUSTOM_EVENTS.onTargetLeave, { bubbles: true });
17221
17459
  if (!moved) this.instance.updateNode(this.serialize(node));
17222
17460
  }
17223
17461
  });
@@ -17349,6 +17587,20 @@ var WeaveNode = class {
17349
17587
  ...nodeTransformConfig
17350
17588
  };
17351
17589
  }
17590
+ getNodesEdgeSnappingPlugin() {
17591
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY);
17592
+ return snappingPlugin;
17593
+ }
17594
+ getNodesDistanceSnappingPlugin() {
17595
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY);
17596
+ return snappingPlugin;
17597
+ }
17598
+ realOffset(instance) {
17599
+ return {
17600
+ x: 0,
17601
+ y: 0
17602
+ };
17603
+ }
17352
17604
  };
17353
17605
 
17354
17606
  //#endregion
@@ -18062,30 +18314,10 @@ var WeaveTargetingManager = class {
18062
18314
  }
18063
18315
  isNodesBoundingBoxIntersecting(nodes, nodeB) {
18064
18316
  const stage = this.instance.getStage();
18065
- const a = this.getBoundingBoxOfNodes(nodes);
18317
+ const a = getBoundingBox(stage, nodes);
18066
18318
  const b = nodeB.getClientRect({ relativeTo: stage });
18067
18319
  return !(a.x + a.width < b.x || a.x > b.x + b.width || a.y + a.height < b.y || a.y > b.y + b.height);
18068
18320
  }
18069
- getBoundingBoxOfNodes(nodes) {
18070
- const stage = this.instance.getStage();
18071
- if (!nodes || nodes.length === 0) return {
18072
- x: 0,
18073
- y: 0,
18074
- width: 0,
18075
- height: 0
18076
- };
18077
- const rects = nodes.map((node) => node.getClientRect({ relativeTo: stage }));
18078
- const minX = Math.min(...rects.map((r) => r.x));
18079
- const minY = Math.min(...rects.map((r) => r.y));
18080
- const maxX = Math.max(...rects.map((r) => r.x + r.width));
18081
- const maxY = Math.max(...rects.map((r) => r.y + r.height));
18082
- return {
18083
- x: minX,
18084
- y: minY,
18085
- width: maxX - minX,
18086
- height: maxY - minY
18087
- };
18088
- }
18089
18321
  nodeIntersectsContainerElement(node, actualLayer) {
18090
18322
  const stage = this.instance.getStage();
18091
18323
  const containers = stage.find(".containerCapable");
@@ -18105,7 +18337,7 @@ var WeaveTargetingManager = class {
18105
18337
  }
18106
18338
  } else {
18107
18339
  let nodeActualContainer = node.getParent();
18108
- if (nodeActualContainer.getAttrs().nodeId) {
18340
+ if (nodeActualContainer?.getAttrs().nodeId) {
18109
18341
  const realParent = stage.findOne(`#${nodeActualContainer.getAttrs().nodeId}`);
18110
18342
  if (realParent) nodeActualContainer = realParent;
18111
18343
  }
@@ -18134,7 +18366,7 @@ var WeaveTargetingManager = class {
18134
18366
  this.logger.debug({ point }, "getMousePointer");
18135
18367
  const stage = this.instance.getStage();
18136
18368
  const mainLayer = this.instance.getMainLayer();
18137
- let relativeMousePointer = point ? point : mainLayer?.getRelativePointerPosition() ?? {
18369
+ let relativeMousePointer = typeof point !== "undefined" ? point : mainLayer?.getRelativePointerPosition() ?? {
18138
18370
  x: 0,
18139
18371
  y: 0
18140
18372
  };
@@ -18157,8 +18389,8 @@ var WeaveTargetingManager = class {
18157
18389
  measureContainer = containerOfNode;
18158
18390
  }
18159
18391
  }
18160
- if (container?.getAttrs().nodeType !== "layer") relativeMousePointer = measureContainer?.getRelativePointerPosition() ?? relativeMousePointer;
18161
- if (container?.getAttrs().nodeType === "layer") relativeMousePointer = measureContainer?.getRelativePointerPosition() ?? {
18392
+ if (typeof point === "undefined" && container?.getAttrs().nodeType !== "layer") relativeMousePointer = measureContainer?.getRelativePointerPosition() ?? relativeMousePointer;
18393
+ if (typeof point === "undefined" && container?.getAttrs().nodeType === "layer") relativeMousePointer = measureContainer?.getRelativePointerPosition() ?? {
18162
18394
  x: 0,
18163
18395
  y: 0
18164
18396
  };
@@ -18916,7 +19148,7 @@ var WeaveRegisterManager = class {
18916
19148
 
18917
19149
  //#endregion
18918
19150
  //#region package.json
18919
- var version = "0.50.0";
19151
+ var version = "0.52.0";
18920
19152
 
18921
19153
  //#endregion
18922
19154
  //#region src/managers/setup.ts
@@ -19959,6 +20191,12 @@ var WeaveEllipseNode = class extends WeaveNode {
19959
20191
  y: 1
19960
20192
  });
19961
20193
  }
20194
+ realOffset(element) {
20195
+ return {
20196
+ x: element.props.radiusX,
20197
+ y: element.props.radiusY
20198
+ };
20199
+ }
19962
20200
  };
19963
20201
 
19964
20202
  //#endregion
@@ -20593,10 +20831,8 @@ var WeaveImageToolAction = class extends WeaveAction {
20593
20831
  crossOrigin: "anonymous",
20594
20832
  ...options
20595
20833
  };
20596
- if (!position) {
20597
- stage.container().style.cursor = "crosshair";
20598
- stage.container().focus();
20599
- }
20834
+ stage.container().style.cursor = "crosshair";
20835
+ stage.container().focus();
20600
20836
  this.imageId = v4_default();
20601
20837
  this.imageURL = imageURL;
20602
20838
  this.preloadImgs[this.imageId] = new Image();
@@ -20623,10 +20859,8 @@ var WeaveImageToolAction = class extends WeaveAction {
20623
20859
  }
20624
20860
  addImageNode(position) {
20625
20861
  const stage = this.instance.getStage();
20626
- if (!position) {
20627
- stage.container().style.cursor = "crosshair";
20628
- stage.container().focus();
20629
- }
20862
+ stage.container().style.cursor = "crosshair";
20863
+ stage.container().focus();
20630
20864
  if (position) {
20631
20865
  this.setState(IMAGE_TOOL_STATE.SELECTED_POSITION);
20632
20866
  this.handleAdding(position);
@@ -20687,7 +20921,10 @@ var WeaveImageToolAction = class extends WeaveAction {
20687
20921
  }
20688
20922
  });
20689
20923
  this.instance.addNode(node, this.container?.getAttrs().id);
20690
- this.instance.emitEvent("onAddedImage", { imageURL: this.props.imageURL });
20924
+ this.instance.emitEvent("onAddedImage", {
20925
+ imageURL: this.props.imageURL,
20926
+ nodeId: this.imageId
20927
+ });
20691
20928
  }
20692
20929
  this.setState(IMAGE_TOOL_STATE.FINISHED);
20693
20930
  }
@@ -21556,6 +21793,12 @@ var WeaveStarNode = class extends WeaveNode {
21556
21793
  y: 1
21557
21794
  });
21558
21795
  }
21796
+ realOffset(element) {
21797
+ return {
21798
+ x: element.props.outerRadius,
21799
+ y: element.props.outerRadius
21800
+ };
21801
+ }
21559
21802
  };
21560
21803
 
21561
21804
  //#endregion
@@ -21673,6 +21916,12 @@ var WeaveRegularPolygonNode = class extends WeaveNode {
21673
21916
  y: 1
21674
21917
  });
21675
21918
  }
21919
+ realOffset(element) {
21920
+ return {
21921
+ x: element.props.radius,
21922
+ y: element.props.radius
21923
+ };
21924
+ }
21676
21925
  };
21677
21926
 
21678
21927
  //#endregion
@@ -22341,20 +22590,39 @@ var WeaveStageZoomPlugin = class extends WeavePlugin {
22341
22590
  });
22342
22591
  this.setZoom(scale, false);
22343
22592
  }
22344
- fitToSelection() {
22593
+ fitToSelection(smartZoom = false) {
22345
22594
  if (!this.enabled) return;
22346
22595
  const stage = this.instance.getStage();
22347
22596
  const selectionPlugin = this.getNodesSelectionPlugin();
22348
22597
  if (!selectionPlugin) return;
22349
22598
  const nodes = selectionPlugin.getTransformer().getNodes();
22350
22599
  if (nodes.length === 0) return;
22600
+ const box = getBoundingBox(stage, selectionPlugin.getTransformer().getNodes());
22601
+ if (box.width === 0 || box.height === 0) return;
22602
+ const container = stage.container();
22603
+ const scale = stage.scale();
22604
+ const viewportWidth = container.clientWidth;
22605
+ const viewportHeight = container.clientHeight;
22606
+ const visibleStageWidth = viewportWidth / scale.x;
22607
+ const visibleStageHeight = viewportHeight / scale.y;
22608
+ const fitsInView = box.width + this.config.fitToSelection.padding * 2 <= visibleStageWidth && box.height + this.config.fitToSelection.padding * 2 <= visibleStageHeight;
22609
+ const selectionCenter = {
22610
+ x: box.x + box.width / 2,
22611
+ y: box.y + box.height / 2
22612
+ };
22613
+ if (smartZoom && fitsInView) {
22614
+ const newPosition = {
22615
+ x: viewportWidth / 2 - selectionCenter.x * scale.x,
22616
+ y: viewportHeight / 2 - selectionCenter.y * scale.y
22617
+ };
22618
+ stage.position(newPosition);
22619
+ return;
22620
+ }
22351
22621
  this.setZoom(1, false);
22352
22622
  stage.setAttrs({
22353
22623
  x: 0,
22354
22624
  y: 0
22355
22625
  });
22356
- const box = getBoundingBox(stage, selectionPlugin.getTransformer().getNodes());
22357
- if (box.width === 0 || box.height === 0) return;
22358
22626
  const stageBox = {
22359
22627
  width: stage.width(),
22360
22628
  height: stage.height()
@@ -22363,22 +22631,22 @@ var WeaveStageZoomPlugin = class extends WeavePlugin {
22363
22631
  const availableScreenHeight = stageBox.height - 2 * this.config.fitToSelection.padding;
22364
22632
  const scaleX = availableScreenWidth / box.width;
22365
22633
  const scaleY = availableScreenHeight / box.height;
22366
- const scale = Math.min(scaleX, scaleY);
22634
+ const finalScale = Math.min(scaleX, scaleY);
22367
22635
  stage.scale({
22368
- x: scale,
22369
- y: scale
22636
+ x: finalScale,
22637
+ y: finalScale
22370
22638
  });
22371
22639
  const selectionCenterX = box.x + box.width / 2;
22372
22640
  const selectionCenterY = box.y + box.height / 2;
22373
- const canvasCenterX = stage.width() / (2 * scale);
22374
- const canvasCenterY = stage.height() / (2 * scale);
22375
- const stageX = (canvasCenterX - selectionCenterX) * scale;
22376
- const stageY = (canvasCenterY - selectionCenterY) * scale;
22641
+ const canvasCenterX = stage.width() / (2 * finalScale);
22642
+ const canvasCenterY = stage.height() / (2 * finalScale);
22643
+ const stageX = (canvasCenterX - selectionCenterX) * finalScale;
22644
+ const stageY = (canvasCenterY - selectionCenterY) * finalScale;
22377
22645
  stage.position({
22378
22646
  x: stageX,
22379
22647
  y: stageY
22380
22648
  });
22381
- this.setZoom(scale, false);
22649
+ this.setZoom(finalScale, false);
22382
22650
  }
22383
22651
  enable() {
22384
22652
  this.enabled = true;
@@ -22473,20 +22741,21 @@ var WeaveStageZoomPlugin = class extends WeavePlugin {
22473
22741
  lastCenter = null;
22474
22742
  }, { passive: false });
22475
22743
  const handleWheel = (e) => {
22476
- e.evt.preventDefault();
22477
- const stage$1 = this.instance.getStage();
22478
- const performZoom = this.isCtrlOrMetaPressed || !this.isCtrlOrMetaPressed && e.evt.ctrlKey && e.evt.deltaMode === 0;
22479
- if (!this.enabled || !stage$1.isFocused() || !performZoom) return;
22480
- const delta = e.evt.deltaY > 0 ? 1 : -1;
22744
+ const performZoom = this.isCtrlOrMetaPressed || !this.isCtrlOrMetaPressed && e.ctrlKey && e.deltaMode === 0;
22745
+ const mouseX = e.clientX;
22746
+ const mouseY = e.clientY;
22747
+ const elementUnderMouse = document.elementFromPoint(mouseX, mouseY);
22748
+ if (!this.enabled || !performZoom || getClosestParentWithId(elementUnderMouse) !== stage.container()) return;
22749
+ const delta = e.deltaY > 0 ? 1 : -1;
22481
22750
  this.zoomVelocity += delta;
22482
- this.isTrackpad = Math.abs(e.evt.deltaY) < 15 && e.evt.deltaMode === 0;
22751
+ this.isTrackpad = Math.abs(e.deltaY) < 15 && e.deltaMode === 0;
22483
22752
  if (!this.zooming) {
22484
22753
  this.zooming = true;
22485
22754
  this.zoomInertiaType = WEAVE_STAGE_ZOOM_TYPE.MOUSE_WHEEL;
22486
22755
  requestAnimationFrame(this.zoomTick.bind(this));
22487
22756
  }
22488
22757
  };
22489
- stage.on("wheel", handleWheel);
22758
+ window.addEventListener("wheel", handleWheel, { passive: false });
22490
22759
  }
22491
22760
  getInertiaScale() {
22492
22761
  const stage = this.instance.getStage();
@@ -22659,7 +22928,7 @@ var WeaveFitToSelectionToolAction = class extends WeaveAction {
22659
22928
  }
22660
22929
  trigger(cancelAction, params) {
22661
22930
  const stageZoomPlugin = this.getStageZoomPlugin();
22662
- stageZoomPlugin.fitToSelection();
22931
+ stageZoomPlugin.fitToSelection(params?.smartZoom ?? false);
22663
22932
  this.previousAction = params.previousAction;
22664
22933
  this.cancelAction = cancelAction;
22665
22934
  this.cancelAction();
@@ -24981,7 +25250,7 @@ const WEAVE_GRID_DEFAULT_MAJOR_LINE_RATIO = 4;
24981
25250
  const WEAVE_GRID_DEFAULT_RADIUS = 1;
24982
25251
  const WEAVE_GRID_DEFAULT_MAJOR_DOT_RATIO = 2;
24983
25252
  const WEAVE_GRID_DEFAULT_MAJOR_EVERY = 10;
24984
- const WEAVE_GRID_DEFAULT_DOT_MAX_DOTS_PER_AXIS = 300;
25253
+ const WEAVE_GRID_DEFAULT_DOT_MAX_DOTS_PER_AXIS = 250;
24985
25254
  const WEAVE_GRID_LAYER_ID = "gridLayer";
24986
25255
 
24987
25256
  //#endregion
@@ -25356,16 +25625,17 @@ var WeaveStagePanningPlugin = class extends WeavePlugin {
25356
25625
  this.panning = false;
25357
25626
  });
25358
25627
  const handleWheel = (e) => {
25359
- e.evt.preventDefault();
25360
- const stage$1 = this.instance.getStage();
25361
- const performPanning = !this.isCtrlOrMetaPressed && !e.evt.ctrlKey;
25362
- if (!this.enabled || !stage$1.isFocused() || this.isCtrlOrMetaPressed || e.evt.buttons === 4 || !performPanning) return;
25628
+ const performPanning = !this.isCtrlOrMetaPressed && !e.ctrlKey;
25629
+ const mouseX = e.clientX;
25630
+ const mouseY = e.clientY;
25631
+ const elementUnderMouse = document.elementFromPoint(mouseX, mouseY);
25632
+ if (!this.enabled || this.isCtrlOrMetaPressed || e.buttons === 4 || !performPanning || getClosestParentWithId(elementUnderMouse) !== stage.container()) return;
25363
25633
  this.getContextMenuPlugin()?.cancelLongPressTimer();
25364
- stage$1.x(stage$1.x() - e.evt.deltaX);
25365
- stage$1.y(stage$1.y() - e.evt.deltaY);
25634
+ stage.x(stage.x() - e.deltaX);
25635
+ stage.y(stage.y() - e.deltaY);
25366
25636
  this.instance.emitEvent("onStageMove");
25367
25637
  };
25368
- stage.on("wheel", handleWheel);
25638
+ window.addEventListener("wheel", handleWheel, { passive: false });
25369
25639
  stage.container().style.touchAction = "none";
25370
25640
  stage.container().style.userSelect = "none";
25371
25641
  stage.container().style.setProperty("-webkit-user-drag", "none");
@@ -25877,29 +26147,8 @@ var WeaveStageDropAreaPlugin = class extends WeavePlugin {
25877
26147
  };
25878
26148
 
25879
26149
  //#endregion
25880
- //#region src/plugins/nodes-snapping/constants.ts
25881
- const WEAVE_NODES_SNAPPING_KEY = "nodesSnapping";
25882
- const GUIDE_LINE_NAME = "guide-line";
25883
- const GUIDE_LINE_DEFAULT_CONFIG = {
25884
- stroke: "rgb(0, 161, 255)",
25885
- strokeWidth: 1,
25886
- dash: [4, 6]
25887
- };
25888
- const GUIDE_LINE_DRAG_SNAPPING_THRESHOLD = 10;
25889
- const GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD = 10;
25890
- const GUIDE_ORIENTATION = {
25891
- ["HORIZONTAL"]: "H",
25892
- ["VERTICAL"]: "V"
25893
- };
25894
- const NODE_SNAP = {
25895
- ["START"]: "start",
25896
- ["CENTER"]: "center",
25897
- ["END"]: "end"
25898
- };
25899
-
25900
- //#endregion
25901
- //#region src/plugins/nodes-snapping/nodes-snapping.ts
25902
- var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26150
+ //#region src/plugins/nodes-edge-snapping/nodes-edge-snapping.ts
26151
+ var WeaveNodesEdgeSnappingPlugin = class extends WeavePlugin {
25903
26152
  constructor(params) {
25904
26153
  super();
25905
26154
  const { config } = params ?? {};
@@ -25909,7 +26158,7 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
25909
26158
  this.enabled = true;
25910
26159
  }
25911
26160
  getName() {
25912
- return WEAVE_NODES_SNAPPING_KEY;
26161
+ return WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY;
25913
26162
  }
25914
26163
  onInit() {
25915
26164
  this.initEvents();
@@ -25917,32 +26166,6 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
25917
26166
  setEnabled(enabled) {
25918
26167
  this.enabled = enabled;
25919
26168
  }
25920
- getSelectedNodesMetadata(transformer) {
25921
- const firstNode = transformer.getNodes()[0];
25922
- const firstNodeClientRect = firstNode.getClientRect();
25923
- const rectCoordsMin = {
25924
- x: firstNodeClientRect.x,
25925
- y: firstNodeClientRect.y
25926
- };
25927
- const rectCoordsMax = {
25928
- x: firstNodeClientRect.x + firstNodeClientRect.width,
25929
- y: firstNodeClientRect.y + firstNodeClientRect.height
25930
- };
25931
- const nodes = [];
25932
- for (const node of transformer.getNodes()) {
25933
- const clientRect = node.getClientRect();
25934
- if (clientRect.x < rectCoordsMin.x) rectCoordsMin.x = clientRect.x;
25935
- if (clientRect.y < rectCoordsMin.y) rectCoordsMin.y = clientRect.y;
25936
- if (clientRect.x + clientRect.width > rectCoordsMax.x) rectCoordsMax.x = clientRect.x + clientRect.width;
25937
- if (clientRect.y + clientRect.height > rectCoordsMax.y) rectCoordsMax.y = clientRect.y + clientRect.height;
25938
- nodes.push(node.getAttrs().id);
25939
- }
25940
- return {
25941
- width: rectCoordsMax.x - rectCoordsMin.x,
25942
- height: rectCoordsMax.y - rectCoordsMin.y,
25943
- nodes
25944
- };
25945
- }
25946
26169
  deleteGuides() {
25947
26170
  const utilityLayer = this.instance.getUtilityLayer();
25948
26171
  if (utilityLayer) utilityLayer.find(`.${GUIDE_LINE_NAME}`).forEach((l) => l.destroy());
@@ -25951,87 +26174,73 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
25951
26174
  const utilityLayer = this.instance.getUtilityLayer();
25952
26175
  if (!this.enabled) return;
25953
26176
  if (!utilityLayer) return;
25954
- const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
25955
- let skipNodes = [];
25956
- let node = void 0;
25957
- if (e.type === "dragmove" && nodesSelectionPlugin && nodesSelectionPlugin.getTransformer().nodes().length === 1) {
25958
- node = nodesSelectionPlugin.getTransformer().nodes()[0];
25959
- skipNodes.push(node.getAttrs().id ?? "");
25960
- }
25961
- if (e.type === "dragmove" && nodesSelectionPlugin && nodesSelectionPlugin.getTransformer().nodes().length > 1) {
25962
- const { nodes } = this.getSelectedNodesMetadata(nodesSelectionPlugin.getTransformer());
25963
- node = nodesSelectionPlugin.getTransformer();
25964
- skipNodes = [...nodes];
25965
- }
25966
- if (e.type === "transform") {
25967
- node = e.target;
25968
- skipNodes.push(node.getAttrs().id ?? "");
25969
- }
26177
+ const { targetNode: node, skipNodes } = getTargetAndSkipNodes(this.instance, e);
25970
26178
  if (typeof node === "undefined") return;
25971
- const lineGuideStops = this.getLineGuideStops(skipNodes);
26179
+ const visibleNodes = this.getVisibleNodes(skipNodes);
26180
+ const lineGuideStops = this.getLineGuideStops(visibleNodes);
25972
26181
  const itemBounds = this.getObjectSnappingEdges(node);
25973
26182
  const guides = this.getGuides(lineGuideStops, itemBounds, e.type);
25974
- utilityLayer.destroyChildren();
25975
- if (!guides.length) return;
25976
- utilityLayer.find(`.${GUIDE_LINE_NAME}`).forEach((l) => l.destroy());
25977
- this.drawGuides(guides);
25978
- if (e.type === "dragmove") {
25979
- const orgAbsPos = node.absolutePosition();
25980
- const absPos = node.absolutePosition();
25981
- guides.forEach((lg) => {
25982
- switch (lg.orientation) {
25983
- case GUIDE_ORIENTATION.VERTICAL: {
25984
- absPos.x = lg.lineGuide + lg.offset;
25985
- break;
25986
- }
25987
- case GUIDE_ORIENTATION.HORIZONTAL: {
25988
- absPos.y = lg.lineGuide + lg.offset;
25989
- break;
25990
- }
25991
- }
25992
- });
25993
- const vecDiff = {
25994
- x: orgAbsPos.x - absPos.x,
25995
- y: orgAbsPos.y - absPos.y
25996
- };
25997
- if (node instanceof konva.default.Transformer) node.getNodes().forEach((n) => {
25998
- const nodeAbsPos = n.getAbsolutePosition();
25999
- const newPos = {
26000
- x: nodeAbsPos.x - vecDiff.x,
26001
- y: nodeAbsPos.y - vecDiff.y
26002
- };
26003
- n.setAbsolutePosition(newPos);
26004
- });
26005
- else node.absolutePosition(absPos);
26006
- }
26007
- if (e.type === "transform") {
26008
- const nodesSelectionPlugin$1 = this.instance.getPlugin("nodesSelection");
26009
- if (nodesSelectionPlugin$1) {
26010
- const transformer = nodesSelectionPlugin$1.getTransformer();
26011
- transformer.anchorDragBoundFunc((_, newAbsPos) => {
26012
- const finalPos = { ...newAbsPos };
26013
- for (const lg of guides) switch (lg.orientation) {
26183
+ this.cleanupGuidelines();
26184
+ if (guides.length > 0) {
26185
+ this.drawGuides(guides);
26186
+ if (e.type === "dragmove") {
26187
+ const orgAbsPos = node.absolutePosition();
26188
+ const absPos = node.absolutePosition();
26189
+ guides.forEach((lg) => {
26190
+ switch (lg.orientation) {
26014
26191
  case GUIDE_ORIENTATION.VERTICAL: {
26015
- const distX = Math.sqrt(Math.pow(newAbsPos.x - lg.lineGuide, 2));
26016
- if (distX < this.transformSnappingThreshold) finalPos.x = lg.lineGuide;
26192
+ absPos.x = lg.lineGuide + lg.offset;
26017
26193
  break;
26018
26194
  }
26019
26195
  case GUIDE_ORIENTATION.HORIZONTAL: {
26020
- const distY = Math.sqrt(Math.pow(newAbsPos.y - lg.lineGuide, 2));
26021
- if (distY < this.transformSnappingThreshold) finalPos.y = lg.lineGuide;
26196
+ absPos.y = lg.lineGuide + lg.offset;
26022
26197
  break;
26023
26198
  }
26024
26199
  }
26025
- return finalPos;
26026
26200
  });
26201
+ const vecDiff = {
26202
+ x: orgAbsPos.x - absPos.x,
26203
+ y: orgAbsPos.y - absPos.y
26204
+ };
26205
+ if (node instanceof konva.default.Transformer) node.getNodes().forEach((n) => {
26206
+ const nodeAbsPos = n.getAbsolutePosition();
26207
+ const newPos = {
26208
+ x: nodeAbsPos.x - vecDiff.x,
26209
+ y: nodeAbsPos.y - vecDiff.y
26210
+ };
26211
+ n.setAbsolutePosition(newPos);
26212
+ });
26213
+ else node.absolutePosition(absPos);
26214
+ }
26215
+ if (e.type === "transform") {
26216
+ const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
26217
+ if (nodesSelectionPlugin) {
26218
+ const transformer = nodesSelectionPlugin.getTransformer();
26219
+ transformer.anchorDragBoundFunc((_, newAbsPos) => {
26220
+ const finalPos = { ...newAbsPos };
26221
+ for (const lg of guides) switch (lg.orientation) {
26222
+ case GUIDE_ORIENTATION.VERTICAL: {
26223
+ const distX = Math.sqrt(Math.pow(newAbsPos.x - lg.lineGuide, 2));
26224
+ if (distX < this.transformSnappingThreshold) finalPos.x = lg.lineGuide;
26225
+ break;
26226
+ }
26227
+ case GUIDE_ORIENTATION.HORIZONTAL: {
26228
+ const distY = Math.sqrt(Math.pow(newAbsPos.y - lg.lineGuide, 2));
26229
+ if (distY < this.transformSnappingThreshold) finalPos.y = lg.lineGuide;
26230
+ break;
26231
+ }
26232
+ }
26233
+ return finalPos;
26234
+ });
26235
+ }
26027
26236
  }
26028
26237
  }
26029
26238
  }
26030
- cleanupEvaluateGuidelines() {
26239
+ cleanupGuidelines() {
26031
26240
  const utilityLayer = this.instance.getUtilityLayer();
26032
26241
  if (!this.enabled) return;
26033
26242
  if (!utilityLayer) return;
26034
- utilityLayer.destroyChildren();
26243
+ this.deleteGuides();
26035
26244
  }
26036
26245
  initEvents() {
26037
26246
  const stage = this.instance.getStage();
@@ -26041,45 +26250,29 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26041
26250
  this.evaluateGuidelines(e);
26042
26251
  });
26043
26252
  stage.on("dragend", () => {
26044
- this.cleanupEvaluateGuidelines();
26253
+ this.cleanupGuidelines();
26045
26254
  });
26046
26255
  }
26047
26256
  }
26048
- nodeIntersectsViewport(node) {
26049
- const stage = this.instance.getStage();
26050
- const scale = stage.scaleX();
26051
- const stageWidth = stage.width() / scale;
26052
- const stageHeight = stage.height() / scale;
26053
- const viewportRect = {
26054
- x: -stage.x() / scale,
26055
- y: -stage.y() / scale,
26056
- width: stageWidth,
26057
- height: stageHeight
26058
- };
26059
- if (!node.isVisible()) return false;
26060
- const box = node.getClientRect({ relativeTo: stage });
26061
- const intersects = box.x + box.width > viewportRect.x && box.x < viewportRect.x + viewportRect.width && box.y + box.height > viewportRect.y && box.y < viewportRect.y + viewportRect.height;
26062
- return intersects;
26063
- }
26064
- getLineGuideStops(skipNodes) {
26257
+ getVisibleNodes(skipNodes) {
26065
26258
  const stage = this.instance.getStage();
26066
26259
  const nodesSelection = this.instance.getPlugin("nodesSelection");
26067
26260
  if (nodesSelection) nodesSelection.getTransformer().hide();
26068
- const vertical = [
26069
- 0,
26070
- stage.width() / 2,
26071
- stage.width()
26072
- ];
26073
- const horizontal = [
26074
- 0,
26075
- stage.height() / 2,
26076
- stage.height()
26077
- ];
26078
- stage.find(".node").forEach((guideItem) => {
26079
- if (guideItem.getParent()?.getAttrs().nodeType === "group") return;
26080
- if (skipNodes.includes(guideItem.getParent()?.getAttrs().nodeId)) return;
26081
- if (skipNodes.includes(guideItem.getAttrs().id ?? "")) return;
26082
- if (!this.nodeIntersectsViewport(guideItem)) return;
26261
+ const nodes = getVisibleNodesInViewport(stage, this.instance.getMainLayer());
26262
+ const finalVisibleNodes = [];
26263
+ nodes.forEach((node) => {
26264
+ if (node.getParent()?.getAttrs().nodeType === "group") return;
26265
+ if (skipNodes.includes(node.getParent()?.getAttrs().nodeId)) return;
26266
+ if (skipNodes.includes(node.getAttrs().id ?? "")) return;
26267
+ finalVisibleNodes.push(node);
26268
+ });
26269
+ if (nodesSelection) nodesSelection.getTransformer().show();
26270
+ return finalVisibleNodes;
26271
+ }
26272
+ getLineGuideStops(nodes) {
26273
+ const vertical = [];
26274
+ const horizontal = [];
26275
+ nodes.forEach((guideItem) => {
26083
26276
  const box = guideItem.getClientRect({ skipStroke: true });
26084
26277
  vertical.push([
26085
26278
  box.x,
@@ -26092,7 +26285,6 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26092
26285
  box.y + box.height / 2
26093
26286
  ]);
26094
26287
  });
26095
- if (nodesSelection) nodesSelection.getTransformer().show();
26096
26288
  return {
26097
26289
  vertical: vertical.flat(),
26098
26290
  horizontal: horizontal.flat()
@@ -26110,16 +26302,19 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26110
26302
  const snappingEdges = {
26111
26303
  vertical: [
26112
26304
  {
26305
+ nodeId: node.getAttrs().id ?? "",
26113
26306
  guide: box.x,
26114
26307
  offset: Math.round(absPos.x - box.x),
26115
26308
  snap: NODE_SNAP.START
26116
26309
  },
26117
26310
  {
26311
+ nodeId: node.getAttrs().id ?? "",
26118
26312
  guide: box.x + box.width / 2,
26119
26313
  offset: Math.round(absPos.x - box.x - box.width / 2),
26120
26314
  snap: NODE_SNAP.CENTER
26121
26315
  },
26122
26316
  {
26317
+ nodeId: node.getAttrs().id ?? "",
26123
26318
  guide: box.x + box.width,
26124
26319
  offset: Math.round(absPos.x - box.x - box.width),
26125
26320
  snap: NODE_SNAP.END
@@ -26127,16 +26322,19 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26127
26322
  ],
26128
26323
  horizontal: [
26129
26324
  {
26325
+ nodeId: node.getAttrs().id ?? "",
26130
26326
  guide: Math.round(box.y),
26131
26327
  offset: Math.round(absPos.y - box.y),
26132
26328
  snap: NODE_SNAP.START
26133
26329
  },
26134
26330
  {
26331
+ nodeId: node.getAttrs().id ?? "",
26135
26332
  guide: Math.round(box.y + box.height / 2),
26136
26333
  offset: Math.round(absPos.y - box.y - box.height / 2),
26137
26334
  snap: NODE_SNAP.CENTER
26138
26335
  },
26139
26336
  {
26337
+ nodeId: node.getAttrs().id ?? "",
26140
26338
  guide: Math.round(box.y + box.height),
26141
26339
  offset: Math.round(absPos.y - box.y - box.height),
26142
26340
  snap: NODE_SNAP.END
@@ -26146,34 +26344,45 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26146
26344
  return snappingEdges;
26147
26345
  }
26148
26346
  getGuides(lineGuideStops, itemBounds, type) {
26347
+ const resultMapV = new Map();
26348
+ const resultMapH = new Map();
26149
26349
  const resultV = [];
26150
26350
  const resultH = [];
26151
26351
  lineGuideStops.vertical.forEach((lineGuide) => {
26152
26352
  itemBounds.vertical.forEach((itemBound) => {
26153
26353
  const diff = Math.abs(lineGuide - itemBound.guide);
26154
- if (diff < this.dragSnappingThreshold) resultV.push({
26155
- lineGuide,
26156
- diff,
26157
- snap: itemBound.snap,
26158
- offset: itemBound.offset
26159
- });
26354
+ if (diff < this.dragSnappingThreshold && !resultMapV.has(itemBound.nodeId)) {
26355
+ const guide = {
26356
+ nodeId: itemBound.nodeId,
26357
+ lineGuide,
26358
+ diff,
26359
+ snap: itemBound.snap,
26360
+ offset: itemBound.offset
26361
+ };
26362
+ resultMapV.set(itemBound.nodeId, guide);
26363
+ resultV.push(guide);
26364
+ }
26160
26365
  });
26161
26366
  });
26162
26367
  lineGuideStops.horizontal.forEach((lineGuide) => {
26163
26368
  itemBounds.horizontal.forEach((itemBound) => {
26164
26369
  const diff = Math.abs(lineGuide - itemBound.guide);
26165
- if (diff < this.dragSnappingThreshold) resultH.push({
26166
- lineGuide,
26167
- diff,
26168
- snap: itemBound.snap,
26169
- offset: itemBound.offset
26170
- });
26370
+ if (diff < this.dragSnappingThreshold && !resultMapH.has(itemBound.nodeId)) {
26371
+ const guide = {
26372
+ nodeId: itemBound.nodeId,
26373
+ lineGuide,
26374
+ diff,
26375
+ snap: itemBound.snap,
26376
+ offset: itemBound.offset
26377
+ };
26378
+ resultMapH.set(itemBound.nodeId, guide);
26379
+ resultH.push(guide);
26380
+ }
26171
26381
  });
26172
26382
  });
26173
26383
  const guides = [];
26174
26384
  if (type === "dragmove") {
26175
- const minV = resultV.sort((a, b) => a.diff - b.diff)[0];
26176
- const minH = resultH.sort((a, b) => a.diff - b.diff)[0];
26385
+ const { minH, minV } = this.sortedGuides(resultH, resultV);
26177
26386
  if (minV) guides.push({
26178
26387
  lineGuide: minV.lineGuide,
26179
26388
  offset: minV.offset,
@@ -26207,6 +26416,14 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26207
26416
  }
26208
26417
  return guides;
26209
26418
  }
26419
+ sortedGuides(resultH, resultV) {
26420
+ const minV = resultV.toSorted((a, b) => a.diff - b.diff)[0];
26421
+ const minH = resultH.toSorted((a, b) => a.diff - b.diff)[0];
26422
+ return {
26423
+ minH,
26424
+ minV
26425
+ };
26426
+ }
26210
26427
  drawGuides(guides) {
26211
26428
  const stage = this.instance.getStage();
26212
26429
  const utilityLayer = this.instance.getUtilityLayer();
@@ -26259,6 +26476,518 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26259
26476
  }
26260
26477
  };
26261
26478
 
26479
+ //#endregion
26480
+ //#region src/plugins/nodes-distance-snapping/nodes-distance-snapping.ts
26481
+ var WeaveNodesDistanceSnappingPlugin = class extends WeavePlugin {
26482
+ peerDistanceX = null;
26483
+ peerDistanceY = null;
26484
+ snapPositionX = null;
26485
+ snapPositionY = null;
26486
+ currentSizeSnapHorizontal = null;
26487
+ currentSizeSnapVertical = null;
26488
+ constructor(params) {
26489
+ super();
26490
+ const { config } = params ?? {};
26491
+ this.enterSnappingTolerance = config?.enterSnappingTolerance ?? GUIDE_ENTER_SNAPPING_TOLERANCE;
26492
+ this.exitSnappingTolerance = config?.exitSnappingTolerance ?? GUIDE_EXIT_SNAPPING_TOLERANCE;
26493
+ this.enabled = true;
26494
+ }
26495
+ getName() {
26496
+ return WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY;
26497
+ }
26498
+ onInit() {
26499
+ this.initEvents();
26500
+ }
26501
+ setEnabled(enabled) {
26502
+ this.enabled = enabled;
26503
+ }
26504
+ deleteGuides() {
26505
+ const utilityLayer = this.instance.getUtilityLayer();
26506
+ if (utilityLayer) {
26507
+ utilityLayer.find(`.${GUIDE_HORIZONTAL_LINE_NAME}`).forEach((l) => l.destroy());
26508
+ utilityLayer.find(`.${GUIDE_VERTICAL_LINE_NAME}`).forEach((l) => l.destroy());
26509
+ }
26510
+ }
26511
+ evaluateGuidelines(e) {
26512
+ const stage = this.instance.getStage();
26513
+ const mainLayer = this.instance.getMainLayer();
26514
+ const utilityLayer = this.instance.getUtilityLayer();
26515
+ if (!this.enabled) return;
26516
+ if (!utilityLayer) return;
26517
+ const { targetNode: node, skipNodes } = getTargetAndSkipNodes(this.instance, e);
26518
+ if (typeof node === "undefined") return;
26519
+ if (node.getParent() === mainLayer) this.referenceLayer = mainLayer;
26520
+ if (node.getParent()?.getAttrs().nodeId) {
26521
+ const realNode = stage.findOne(`#${node.getParent()?.getAttrs().nodeId}`);
26522
+ if (realNode) this.referenceLayer = realNode;
26523
+ }
26524
+ const visibleNodes = this.getVisibleNodes(skipNodes);
26525
+ const { intersectedNodes: sortedHorizontalIntersectedNodes, intersectedNodesWithDistances: horizontalIntersectedNodes } = this.getHorizontallyIntersectingNodes(node, visibleNodes);
26526
+ const { intersectedNodes: sortedVerticalIntersectedNodes, intersectedNodesWithDistances: verticalIntersectedNodes } = this.getVerticallyIntersectingNodes(node, visibleNodes);
26527
+ this.cleanupGuidelines();
26528
+ if (horizontalIntersectedNodes.length > 0 || verticalIntersectedNodes.length > 0) {
26529
+ if (e.type === "dragmove") {
26530
+ this.validateHorizontalSnapping(node, visibleNodes, sortedHorizontalIntersectedNodes, horizontalIntersectedNodes);
26531
+ this.validateVerticalSnapping(node, visibleNodes, sortedVerticalIntersectedNodes, verticalIntersectedNodes);
26532
+ }
26533
+ }
26534
+ }
26535
+ getBoxClientRect(node) {
26536
+ const stage = this.instance.getStage();
26537
+ return node.getClientRect({
26538
+ relativeTo: stage,
26539
+ skipStroke: true,
26540
+ skipShadow: true
26541
+ });
26542
+ }
26543
+ getPeers(intersectedNodes, targetNode, prev, next) {
26544
+ const peers = intersectedNodes.filter((int) => {
26545
+ if (prev && next) return int.to.getAttrs().id !== targetNode.getAttrs().id && int.from.getAttrs().id !== targetNode.getAttrs().id;
26546
+ if (!prev && next) return int.from.getAttrs().id !== targetNode.getAttrs().id;
26547
+ return int.to.getAttrs().id !== targetNode.getAttrs().id;
26548
+ });
26549
+ let prevBox = null;
26550
+ if (prev) prevBox = this.getBoxClientRect(prev);
26551
+ let nextBox = null;
26552
+ if (next) nextBox = this.getBoxClientRect(next);
26553
+ return {
26554
+ prevBox,
26555
+ nextBox,
26556
+ peers
26557
+ };
26558
+ }
26559
+ validateHorizontalSnapping(node, visibleNodes, sortedHorizontalIntersectedNodes, horizontalIntersectedNodes) {
26560
+ const box = this.getBoxClientRect(node);
26561
+ const targetIndex = sortedHorizontalIntersectedNodes.findIndex((actNode) => actNode.getAttrs().id === node.getAttrs().id);
26562
+ const prev = sortedHorizontalIntersectedNodes[targetIndex - 1];
26563
+ const next = sortedHorizontalIntersectedNodes[targetIndex + 1];
26564
+ const { prevBox, nextBox, peers } = this.getPeers(horizontalIntersectedNodes, node, prev, next);
26565
+ if (this.currentSizeSnapHorizontal === NODE_SNAP_HORIZONTAL.LEFT && prev && prevBox) {
26566
+ const dist = Math.round(box.x - (prevBox.x + prevBox.width));
26567
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26568
+ if (!match) this.currentSizeSnapHorizontal = null;
26569
+ }
26570
+ if (this.currentSizeSnapHorizontal === NODE_SNAP_HORIZONTAL.RIGHT && next && nextBox) {
26571
+ const dist = Math.round(nextBox.x - (box.x + box.width));
26572
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26573
+ if (!match) this.currentSizeSnapHorizontal = null;
26574
+ }
26575
+ if (prev && prevBox && next && nextBox && prevBox.x + prevBox.width <= box.x && box.x + box.width <= nextBox.x) {
26576
+ const distanceToPrev = box.x - (prevBox.x + prevBox.width);
26577
+ const distanceToNext = nextBox.x - (box.x + box.width);
26578
+ const delta = Math.abs(distanceToPrev - distanceToNext);
26579
+ if (delta <= this.enterSnappingTolerance) {
26580
+ const center = (prevBox.x + prevBox.width + nextBox.x) / 2;
26581
+ const newX = center - box.width / 2;
26582
+ this.setNodeClientRectX(node, newX);
26583
+ this.snapPositionX = node.x();
26584
+ this.currentSizeSnapHorizontal = NODE_SNAP_HORIZONTAL.CENTER;
26585
+ const newBox = this.getBoxClientRect(node);
26586
+ this.peerDistanceX = Math.round(newBox.x - (prevBox.x + prevBox.width));
26587
+ }
26588
+ if (this.currentSizeSnapHorizontal === NODE_SNAP_HORIZONTAL.CENTER && delta > this.exitSnappingTolerance) this.currentSizeSnapHorizontal = null;
26589
+ }
26590
+ if (this.currentSizeSnapHorizontal && this.peerDistanceX && this.snapPositionX) {
26591
+ node.x(this.snapPositionX);
26592
+ const { intersectedNodesWithDistances: newHorizontalIntersectedNodes } = this.getHorizontallyIntersectingNodes(node, visibleNodes);
26593
+ this.drawSizeGuidesHorizontally(newHorizontalIntersectedNodes, this.peerDistanceX);
26594
+ return;
26595
+ }
26596
+ const canSnapLeft = prev && prevBox && (() => {
26597
+ const dist = Math.round(box.x - (prevBox.x + prevBox.width));
26598
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26599
+ if (match) {
26600
+ const newX = prevBox.x + prevBox.width + match.distance;
26601
+ this.setNodeClientRectX(node, newX);
26602
+ this.snapPositionX = node.x();
26603
+ this.currentSizeSnapHorizontal = NODE_SNAP_HORIZONTAL.LEFT;
26604
+ const newBox = this.getBoxClientRect(node);
26605
+ this.peerDistanceX = Math.round(newBox.x - (prevBox.x + prevBox.width));
26606
+ return true;
26607
+ }
26608
+ return false;
26609
+ })();
26610
+ if (!canSnapLeft && next && nextBox) {
26611
+ const dist = Math.round(nextBox.x - (box.x + box.width));
26612
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26613
+ if (match) {
26614
+ const newX = nextBox.x - match.distance - box.width;
26615
+ this.setNodeClientRectX(node, newX);
26616
+ this.snapPositionX = node.x();
26617
+ const newBox = this.getBoxClientRect(node);
26618
+ this.peerDistanceX = Math.round(nextBox.x - (newBox.x + newBox.width));
26619
+ this.currentSizeSnapHorizontal = NODE_SNAP_HORIZONTAL.RIGHT;
26620
+ }
26621
+ }
26622
+ }
26623
+ validateVerticalSnapping(node, visibleNodes, sortedVerticalIntersectedNodes, verticalIntersectedNodes) {
26624
+ const box = this.getBoxClientRect(node);
26625
+ const targetIndex = sortedVerticalIntersectedNodes.findIndex((actNode) => actNode.getAttrs().id === node.getAttrs().id);
26626
+ const prev = sortedVerticalIntersectedNodes[targetIndex - 1];
26627
+ const next = sortedVerticalIntersectedNodes[targetIndex + 1];
26628
+ const { prevBox, nextBox, peers } = this.getPeers(verticalIntersectedNodes, node, prev, next);
26629
+ if (this.currentSizeSnapVertical === NODE_SNAP_VERTICAL.TOP && prev && prevBox) {
26630
+ const dist = Math.round(box.y - (prevBox.y + prevBox.height));
26631
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26632
+ if (!match) this.currentSizeSnapVertical = null;
26633
+ }
26634
+ if (this.currentSizeSnapVertical === NODE_SNAP_VERTICAL.BOTTOM && next && nextBox) {
26635
+ const dist = Math.round(nextBox.y - (box.y + box.height));
26636
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26637
+ if (!match) this.currentSizeSnapVertical = null;
26638
+ }
26639
+ if (prev && prevBox && next && nextBox && prevBox.y + prevBox.height <= box.y && box.y + box.height <= nextBox.y) {
26640
+ const distanceToPrev = box.y - (prevBox.y + prevBox.height);
26641
+ const distanceToNext = nextBox.y - (box.y + box.height);
26642
+ const delta = Math.abs(distanceToPrev - distanceToNext);
26643
+ if (delta <= this.enterSnappingTolerance) {
26644
+ const center = (prevBox.y + prevBox.height + nextBox.y) / 2;
26645
+ const newY = center - box.height / 2;
26646
+ this.setNodeClientRectY(node, newY);
26647
+ this.snapPositionY = node.y();
26648
+ this.currentSizeSnapVertical = NODE_SNAP_VERTICAL.MIDDLE;
26649
+ const newBox = this.getBoxClientRect(node);
26650
+ this.peerDistanceY = Math.round(newBox.y - (prevBox.y + prevBox.height));
26651
+ }
26652
+ if (this.currentSizeSnapVertical === NODE_SNAP_VERTICAL.MIDDLE && delta > this.exitSnappingTolerance) this.currentSizeSnapVertical = null;
26653
+ }
26654
+ if (this.currentSizeSnapVertical && this.peerDistanceY && this.snapPositionY) {
26655
+ node.y(this.snapPositionY);
26656
+ const { intersectedNodesWithDistances: newVerticalIntersectedNodes } = this.getVerticallyIntersectingNodes(node, visibleNodes);
26657
+ this.drawSizeGuidesVertically(newVerticalIntersectedNodes, this.peerDistanceY);
26658
+ return;
26659
+ }
26660
+ const canSnapTop = prev && prevBox && (() => {
26661
+ const dist = Math.round(box.y - (prevBox.y + prevBox.height));
26662
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26663
+ if (match) {
26664
+ const newY = prevBox.y + prevBox.height + match.distance;
26665
+ this.setNodeClientRectY(node, newY);
26666
+ this.snapPositionY = node.y();
26667
+ this.currentSizeSnapVertical = NODE_SNAP_VERTICAL.TOP;
26668
+ const newBox = this.getBoxClientRect(node);
26669
+ this.peerDistanceY = Math.round(newBox.y - (prevBox.y + prevBox.height));
26670
+ return true;
26671
+ }
26672
+ return false;
26673
+ })();
26674
+ if (!canSnapTop && next && nextBox) {
26675
+ const dist = Math.round(nextBox.y - (box.y + box.height));
26676
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26677
+ if (match) {
26678
+ const newY = nextBox.y - match.distance - box.height;
26679
+ this.setNodeClientRectY(node, newY);
26680
+ this.snapPositionY = node.y();
26681
+ const newBox = this.getBoxClientRect(node);
26682
+ this.peerDistanceY = Math.round(nextBox.y - (newBox.y + newBox.height));
26683
+ this.currentSizeSnapVertical = NODE_SNAP_VERTICAL.BOTTOM;
26684
+ }
26685
+ }
26686
+ }
26687
+ setNodeClientRectX(node, snappedClientX) {
26688
+ if (node.getParent()?.getType() === "Layer") {
26689
+ node.x(snappedClientX);
26690
+ return;
26691
+ }
26692
+ const box = this.getBoxClientRect(node);
26693
+ const absolutePos = node.getAbsolutePosition();
26694
+ const offsetX = absolutePos.x - box.x;
26695
+ const newAbsX = snappedClientX + offsetX;
26696
+ const parent = node.getParent();
26697
+ if (!parent) {
26698
+ console.warn("Node has no parent to set position");
26699
+ return;
26700
+ }
26701
+ const local = parent.getAbsoluteTransform().copy().invert().point({
26702
+ x: newAbsX,
26703
+ y: absolutePos.y
26704
+ });
26705
+ node.position({
26706
+ x: local.x,
26707
+ y: node.y()
26708
+ });
26709
+ }
26710
+ setNodeClientRectY(node, snappedClientY) {
26711
+ if (node.getParent()?.getType() === "Layer") {
26712
+ node.y(snappedClientY);
26713
+ return;
26714
+ }
26715
+ const box = this.getBoxClientRect(node);
26716
+ const absolutePos = node.getAbsolutePosition();
26717
+ const offsetY = absolutePos.y - box.y;
26718
+ const newAbsY = snappedClientY + offsetY;
26719
+ const parent = node.getParent();
26720
+ if (!parent) {
26721
+ console.warn("Node has no parent to set position");
26722
+ return;
26723
+ }
26724
+ const local = parent.getAbsoluteTransform().copy().invert().point({
26725
+ x: absolutePos.x,
26726
+ y: newAbsY
26727
+ });
26728
+ node.position({
26729
+ x: node.x(),
26730
+ y: local.y
26731
+ });
26732
+ }
26733
+ cleanupGuidelines() {
26734
+ const utilityLayer = this.instance.getUtilityLayer();
26735
+ if (!this.enabled) return;
26736
+ if (!utilityLayer) return;
26737
+ this.deleteGuides();
26738
+ }
26739
+ initEvents() {
26740
+ const stage = this.instance.getStage();
26741
+ const utilityLayer = this.instance.getUtilityLayer();
26742
+ if (utilityLayer) {
26743
+ stage.on("dragmove", (e) => {
26744
+ this.evaluateGuidelines(e);
26745
+ });
26746
+ stage.on("dragend", () => {
26747
+ this.peerDistanceX = null;
26748
+ this.peerDistanceY = null;
26749
+ this.currentSizeSnapVertical = null;
26750
+ this.currentSizeSnapHorizontal = null;
26751
+ this.cleanupGuidelines();
26752
+ });
26753
+ }
26754
+ }
26755
+ getVerticallyIntersectingNodes(targetNode, nodes) {
26756
+ const targetBox = this.getBoxClientRect(targetNode);
26757
+ const intersectedNodes = [];
26758
+ nodes.forEach((node) => {
26759
+ if (node === targetNode || !node.isVisible()) return false;
26760
+ const box = this.getBoxClientRect(node);
26761
+ const horizontalOverlap = box.x + box.width > targetBox.x && box.x < targetBox.x + targetBox.width;
26762
+ if (horizontalOverlap) intersectedNodes.push(node);
26763
+ });
26764
+ intersectedNodes.push(targetNode);
26765
+ intersectedNodes.sort((a, b) => {
26766
+ const ay = this.getBoxClientRect(a).y;
26767
+ const by = this.getBoxClientRect(b).y;
26768
+ return ay - by;
26769
+ });
26770
+ const intersectedNodesWithDistances = [];
26771
+ for (let i = 0; i < intersectedNodes.length - 1; i++) {
26772
+ const a = intersectedNodes[i];
26773
+ const b = intersectedNodes[i + 1];
26774
+ const boxA = this.getBoxClientRect(a);
26775
+ const boxB = this.getBoxClientRect(b);
26776
+ const aBottom = boxA.y + boxA.height;
26777
+ const bTop = boxB.y;
26778
+ const distance = Math.abs(aBottom - bTop);
26779
+ const left = Math.max(boxA.x, boxB.x);
26780
+ const right = Math.min(boxA.x + boxA.width, boxB.x + boxB.width);
26781
+ let midX;
26782
+ if (right > left) midX = left + (right - left) / 2;
26783
+ else {
26784
+ const aCenterX = boxA.x + boxA.width / 2;
26785
+ const bCenterX = boxB.x + boxB.width / 2;
26786
+ midX = (aCenterX + bCenterX) / 2;
26787
+ }
26788
+ intersectedNodesWithDistances.push({
26789
+ index: i,
26790
+ from: a,
26791
+ to: b,
26792
+ midX,
26793
+ distance: Math.round(distance)
26794
+ });
26795
+ }
26796
+ return {
26797
+ intersectedNodes,
26798
+ intersectedNodesWithDistances
26799
+ };
26800
+ }
26801
+ getHorizontallyIntersectingNodes(targetNode, nodes) {
26802
+ const targetBox = this.getBoxClientRect(targetNode);
26803
+ const intersectedNodes = [];
26804
+ nodes.forEach((node) => {
26805
+ if (node === targetNode || !node.isVisible()) return false;
26806
+ const box = this.getBoxClientRect(node);
26807
+ const verticalOverlap = box.y + box.height > targetBox.y && box.y < targetBox.y + targetBox.height;
26808
+ if (verticalOverlap) intersectedNodes.push(node);
26809
+ });
26810
+ intersectedNodes.push(targetNode);
26811
+ intersectedNodes.sort((a, b) => {
26812
+ const ax = this.getBoxClientRect(a).x;
26813
+ const bx = this.getBoxClientRect(b).x;
26814
+ return ax - bx;
26815
+ });
26816
+ const intersectedNodesWithDistances = [];
26817
+ for (let i = 0; i < intersectedNodes.length - 1; i++) {
26818
+ const a = intersectedNodes[i];
26819
+ const b = intersectedNodes[i + 1];
26820
+ const boxA = this.getBoxClientRect(a);
26821
+ const boxB = this.getBoxClientRect(b);
26822
+ const aRight = boxA.x + boxA.width;
26823
+ const bLeft = boxB.x;
26824
+ const distance = Math.abs(Math.round(aRight - bLeft));
26825
+ const top = Math.max(boxA.y, boxB.y);
26826
+ const bottom = Math.min(boxA.y + boxA.height, boxB.y + boxB.height);
26827
+ let midY;
26828
+ if (bottom > top) midY = top + (bottom - top) / 2;
26829
+ else {
26830
+ const aCenterY = boxA.y + boxA.height / 2;
26831
+ const bCenterY = boxB.y + boxB.height / 2;
26832
+ midY = (aCenterY + bCenterY) / 2;
26833
+ }
26834
+ intersectedNodesWithDistances.push({
26835
+ index: i,
26836
+ from: a,
26837
+ to: b,
26838
+ midY,
26839
+ distance
26840
+ });
26841
+ }
26842
+ return {
26843
+ intersectedNodes,
26844
+ intersectedNodesWithDistances
26845
+ };
26846
+ }
26847
+ getVisibleNodes(skipNodes) {
26848
+ const stage = this.instance.getStage();
26849
+ const nodesSelection = this.instance.getPlugin("nodesSelection");
26850
+ if (nodesSelection) nodesSelection.getTransformer().hide();
26851
+ const nodes = getVisibleNodesInViewport(stage, this.referenceLayer);
26852
+ const finalVisibleNodes = [];
26853
+ nodes.forEach((node) => {
26854
+ if (node.getParent()?.getAttrs().nodeType === "group") return;
26855
+ if (skipNodes.includes(node.getParent()?.getAttrs().nodeId)) return;
26856
+ if (skipNodes.includes(node.getAttrs().id ?? "")) return;
26857
+ if (node.getParent() !== this.referenceLayer && !node.getParent()?.getAttrs().nodeId) return;
26858
+ if (node.getParent() !== this.referenceLayer && node.getParent()?.getAttrs().nodeId) {
26859
+ const realNode = stage.findOne(`#${node.getParent()?.getAttrs().nodeId}`);
26860
+ if (realNode && realNode !== this.referenceLayer) return;
26861
+ }
26862
+ finalVisibleNodes.push(node);
26863
+ });
26864
+ if (nodesSelection) nodesSelection.getTransformer().show();
26865
+ return finalVisibleNodes;
26866
+ }
26867
+ drawSizeGuidesHorizontally(intersectionsH, peerDistance) {
26868
+ const utilityLayer = this.instance.getUtilityLayer();
26869
+ if (utilityLayer) intersectionsH.forEach((pairInfo) => {
26870
+ const from = this.getBoxClientRect(pairInfo.from);
26871
+ const to = this.getBoxClientRect(pairInfo.to);
26872
+ if (pairInfo.distance === peerDistance) this.renderHorizontalLineWithDistanceBetweenNodes(from, to, pairInfo.midY, `${pairInfo.distance}`);
26873
+ });
26874
+ }
26875
+ drawSizeGuidesVertically(intersectionsV, peerDistance) {
26876
+ const utilityLayer = this.instance.getUtilityLayer();
26877
+ if (utilityLayer) intersectionsV.forEach((pairInfo) => {
26878
+ const from = this.getBoxClientRect(pairInfo.from);
26879
+ const to = this.getBoxClientRect(pairInfo.to);
26880
+ if (pairInfo.distance === peerDistance) this.renderVerticalLineWithDistanceBetweenNodes(from, to, pairInfo.midX, `${pairInfo.distance}`);
26881
+ });
26882
+ }
26883
+ renderDistanceLabel(ctx, stage, labelText, { canvasMidX, canvasMidY }) {
26884
+ const scaleX = stage?.scaleX() || 1;
26885
+ const scaleY = stage?.scaleY() || 1;
26886
+ const fontSize = 12;
26887
+ const fontFamily = "Arial";
26888
+ const padding = 6;
26889
+ const tempText = new konva.default.Text({
26890
+ text: labelText,
26891
+ fontSize,
26892
+ fontFamily,
26893
+ visible: false
26894
+ });
26895
+ const textWidth = tempText.width();
26896
+ const textHeight = tempText.height();
26897
+ const labelWidth = textWidth + padding * 2;
26898
+ const labelHeight = textHeight + padding * 2;
26899
+ ctx.save();
26900
+ ctx.scale(1 / scaleX, 1 / scaleY);
26901
+ const labelX = canvasMidX - labelWidth / 2;
26902
+ const labelY = canvasMidY - labelHeight / 2;
26903
+ ctx.beginPath();
26904
+ ctx.rect(labelX, labelY, labelWidth, labelHeight);
26905
+ ctx.fillStyle = "#ff0000";
26906
+ ctx.fill();
26907
+ ctx.font = `bold ${fontSize}px ${fontFamily}`;
26908
+ ctx.fillStyle = "white";
26909
+ ctx.textAlign = "center";
26910
+ ctx.textBaseline = "middle";
26911
+ ctx.fillText(labelText, canvasMidX, labelY + labelHeight / 2);
26912
+ ctx.restore();
26913
+ }
26914
+ renderHorizontalLineWithDistanceBetweenNodes(from, to, midY, labelText) {
26915
+ const utilityLayer = this.instance.getUtilityLayer();
26916
+ const renderLabel = this.renderDistanceLabel;
26917
+ const lineWithLabel = new konva.default.Shape({
26918
+ name: GUIDE_HORIZONTAL_LINE_NAME,
26919
+ sceneFunc: function(ctx, shape) {
26920
+ const stage = shape.getStage();
26921
+ const scaleX = stage?.scaleX() || 1;
26922
+ const scaleY = stage?.scaleY() || 1;
26923
+ const x1 = from.x + from.width;
26924
+ const x2 = to.x;
26925
+ const y = midY;
26926
+ ctx.beginPath();
26927
+ ctx.moveTo(x1, y);
26928
+ ctx.lineTo(x2, y);
26929
+ ctx.closePath();
26930
+ ctx.strokeStyle = "#ff0000";
26931
+ ctx.lineWidth = 1;
26932
+ ctx.setLineDash([]);
26933
+ ctx.stroke();
26934
+ ctx.closePath();
26935
+ const worldMidX = (x1 + x2) / 2;
26936
+ const worldMidY = y;
26937
+ const canvasMidX = worldMidX * scaleX;
26938
+ const canvasMidY = worldMidY * scaleY;
26939
+ renderLabel(ctx, stage, labelText, {
26940
+ canvasMidX,
26941
+ canvasMidY
26942
+ });
26943
+ ctx.fillStrokeShape(shape);
26944
+ }
26945
+ });
26946
+ lineWithLabel.moveToBottom();
26947
+ utilityLayer?.add(lineWithLabel);
26948
+ }
26949
+ renderVerticalLineWithDistanceBetweenNodes(from, to, midX, labelText) {
26950
+ const utilityLayer = this.instance.getUtilityLayer();
26951
+ const renderLabel = this.renderDistanceLabel;
26952
+ const lineWithLabel = new konva.default.Shape({
26953
+ name: GUIDE_VERTICAL_LINE_NAME,
26954
+ sceneFunc: function(ctx, shape) {
26955
+ const stage = shape.getStage();
26956
+ const scaleX = stage?.scaleX() || 1;
26957
+ const scaleY = stage?.scaleY() || 1;
26958
+ const x = midX;
26959
+ const y1 = from.y + from.height;
26960
+ const y2 = to.y;
26961
+ ctx.beginPath();
26962
+ ctx.setLineDash([]);
26963
+ ctx.moveTo(x, y1);
26964
+ ctx.lineTo(x, y2);
26965
+ ctx.strokeStyle = "#ff0000";
26966
+ ctx.lineWidth = 1;
26967
+ ctx.stroke();
26968
+ ctx.closePath();
26969
+ const worldMidX = x;
26970
+ const worldMidY = (y1 + y2) / 2;
26971
+ const canvasMidX = worldMidX * scaleX;
26972
+ const canvasMidY = worldMidY * scaleY;
26973
+ renderLabel(ctx, stage, labelText, {
26974
+ canvasMidX,
26975
+ canvasMidY
26976
+ });
26977
+ ctx.fillStrokeShape(shape);
26978
+ }
26979
+ });
26980
+ lineWithLabel.moveToBottom();
26981
+ utilityLayer?.add(lineWithLabel);
26982
+ }
26983
+ enable() {
26984
+ this.enabled = true;
26985
+ }
26986
+ disable() {
26987
+ this.enabled = false;
26988
+ }
26989
+ };
26990
+
26262
26991
  //#endregion
26263
26992
  exports.ALIGN_NODES_ALIGN_TO = ALIGN_NODES_ALIGN_TO
26264
26993
  exports.ALIGN_NODES_TOOL_ACTION_NAME = ALIGN_NODES_TOOL_ACTION_NAME
@@ -26274,16 +27003,22 @@ exports.ERASER_TOOL_ACTION_NAME = ERASER_TOOL_ACTION_NAME
26274
27003
  exports.ERASER_TOOL_STATE = ERASER_TOOL_STATE
26275
27004
  exports.FRAME_TOOL_ACTION_NAME = FRAME_TOOL_ACTION_NAME
26276
27005
  exports.FRAME_TOOL_STATE = FRAME_TOOL_STATE
27006
+ exports.GUIDE_ENTER_SNAPPING_TOLERANCE = GUIDE_ENTER_SNAPPING_TOLERANCE
27007
+ exports.GUIDE_EXIT_SNAPPING_TOLERANCE = GUIDE_EXIT_SNAPPING_TOLERANCE
27008
+ exports.GUIDE_HORIZONTAL_LINE_NAME = GUIDE_HORIZONTAL_LINE_NAME
26277
27009
  exports.GUIDE_LINE_DEFAULT_CONFIG = GUIDE_LINE_DEFAULT_CONFIG
26278
27010
  exports.GUIDE_LINE_DRAG_SNAPPING_THRESHOLD = GUIDE_LINE_DRAG_SNAPPING_THRESHOLD
26279
27011
  exports.GUIDE_LINE_NAME = GUIDE_LINE_NAME
26280
27012
  exports.GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD = GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD
26281
27013
  exports.GUIDE_ORIENTATION = GUIDE_ORIENTATION
27014
+ exports.GUIDE_VERTICAL_LINE_NAME = GUIDE_VERTICAL_LINE_NAME
26282
27015
  exports.IMAGE_TOOL_ACTION_NAME = IMAGE_TOOL_ACTION_NAME
26283
27016
  exports.IMAGE_TOOL_STATE = IMAGE_TOOL_STATE
26284
27017
  exports.MOVE_TOOL_ACTION_NAME = MOVE_TOOL_ACTION_NAME
26285
27018
  exports.MOVE_TOOL_STATE = MOVE_TOOL_STATE
26286
27019
  exports.NODE_SNAP = NODE_SNAP
27020
+ exports.NODE_SNAP_HORIZONTAL = NODE_SNAP_HORIZONTAL
27021
+ exports.NODE_SNAP_VERTICAL = NODE_SNAP_VERTICAL
26287
27022
  exports.PEN_TOOL_ACTION_NAME = PEN_TOOL_ACTION_NAME
26288
27023
  exports.PEN_TOOL_STATE = PEN_TOOL_STATE
26289
27024
  exports.RECTANGLE_TOOL_ACTION_NAME = RECTANGLE_TOOL_ACTION_NAME
@@ -26299,6 +27034,7 @@ exports.TEXT_TOOL_ACTION_NAME = TEXT_TOOL_ACTION_NAME
26299
27034
  exports.TEXT_TOOL_STATE = TEXT_TOOL_STATE
26300
27035
  exports.WEAVE_ARROW_NODE_TYPE = WEAVE_ARROW_NODE_TYPE
26301
27036
  exports.WEAVE_COPY_PASTE_NODES_KEY = WEAVE_COPY_PASTE_NODES_KEY
27037
+ exports.WEAVE_COPY_PASTE_PASTE_CATCHER_ID = WEAVE_COPY_PASTE_PASTE_CATCHER_ID
26302
27038
  exports.WEAVE_COPY_PASTE_PASTE_MODES = WEAVE_COPY_PASTE_PASTE_MODES
26303
27039
  exports.WEAVE_DEFAULT_USER_INFO_FUNCTION = WEAVE_DEFAULT_USER_INFO_FUNCTION
26304
27040
  exports.WEAVE_ELLIPSE_NODE_TYPE = WEAVE_ELLIPSE_NODE_TYPE
@@ -26322,9 +27058,10 @@ exports.WEAVE_IMAGE_CROP_END_TYPE = WEAVE_IMAGE_CROP_END_TYPE
26322
27058
  exports.WEAVE_IMAGE_NODE_TYPE = WEAVE_IMAGE_NODE_TYPE
26323
27059
  exports.WEAVE_LAYER_NODE_TYPE = WEAVE_LAYER_NODE_TYPE
26324
27060
  exports.WEAVE_LINE_NODE_TYPE = WEAVE_LINE_NODE_TYPE
27061
+ exports.WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY = WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY
27062
+ exports.WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY = WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY
26325
27063
  exports.WEAVE_NODES_SELECTION_KEY = WEAVE_NODES_SELECTION_KEY
26326
27064
  exports.WEAVE_NODES_SELECTION_LAYER_ID = WEAVE_NODES_SELECTION_LAYER_ID
26327
- exports.WEAVE_NODES_SNAPPING_KEY = WEAVE_NODES_SNAPPING_KEY
26328
27065
  exports.WEAVE_RECTANGLE_NODE_TYPE = WEAVE_RECTANGLE_NODE_TYPE
26329
27066
  exports.WEAVE_REGULAR_POLYGON_NODE_TYPE = WEAVE_REGULAR_POLYGON_NODE_TYPE
26330
27067
  exports.WEAVE_STAGE_DEFAULT_MODE = WEAVE_STAGE_DEFAULT_MODE
@@ -26363,8 +27100,9 @@ exports.WeaveLayerNode = WeaveLayerNode
26363
27100
  exports.WeaveLineNode = WeaveLineNode
26364
27101
  exports.WeaveMoveToolAction = WeaveMoveToolAction
26365
27102
  exports.WeaveNode = WeaveNode
27103
+ exports.WeaveNodesDistanceSnappingPlugin = WeaveNodesDistanceSnappingPlugin
27104
+ exports.WeaveNodesEdgeSnappingPlugin = WeaveNodesEdgeSnappingPlugin
26366
27105
  exports.WeaveNodesSelectionPlugin = WeaveNodesSelectionPlugin
26367
- exports.WeaveNodesSnappingPlugin = WeaveNodesSnappingPlugin
26368
27106
  exports.WeavePenToolAction = WeavePenToolAction
26369
27107
  exports.WeavePlugin = WeavePlugin
26370
27108
  exports.WeaveRectangleNode = WeaveRectangleNode
@@ -26392,9 +27130,13 @@ exports.clearContainerTargets = clearContainerTargets
26392
27130
  exports.containerOverCursor = containerOverCursor
26393
27131
  exports.containsNodeDeep = containsNodeDeep
26394
27132
  exports.getBoundingBox = getBoundingBox
27133
+ exports.getClosestParentWithId = getClosestParentWithId
26395
27134
  exports.getContrastTextColor = getContrastTextColor
26396
27135
  exports.getExportBoundingBox = getExportBoundingBox
27136
+ exports.getSelectedNodesMetadata = getSelectedNodesMetadata
27137
+ exports.getTargetAndSkipNodes = getTargetAndSkipNodes
26397
27138
  exports.getTargetedNode = getTargetedNode
27139
+ exports.getVisibleNodesInViewport = getVisibleNodesInViewport
26398
27140
  exports.hasFrames = hasFrames
26399
27141
  exports.hasImages = hasImages
26400
27142
  exports.intersectArrays = intersectArrays