@inditextech/weave-sdk 0.50.0 → 0.51.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;
@@ -15829,6 +15829,80 @@ function containsNodeDeep(nodes, target) {
15829
15829
  }
15830
15830
  return false;
15831
15831
  }
15832
+ function getSelectedNodesMetadata(transformer) {
15833
+ const firstNode = transformer.getNodes()[0];
15834
+ const firstNodeClientRect = firstNode.getClientRect();
15835
+ const rectCoordsMin = {
15836
+ x: firstNodeClientRect.x,
15837
+ y: firstNodeClientRect.y
15838
+ };
15839
+ const rectCoordsMax = {
15840
+ x: firstNodeClientRect.x + firstNodeClientRect.width,
15841
+ y: firstNodeClientRect.y + firstNodeClientRect.height
15842
+ };
15843
+ const nodes = [];
15844
+ for (const node of transformer.getNodes()) {
15845
+ const clientRect = node.getClientRect();
15846
+ if (clientRect.x < rectCoordsMin.x) rectCoordsMin.x = clientRect.x;
15847
+ if (clientRect.y < rectCoordsMin.y) rectCoordsMin.y = clientRect.y;
15848
+ if (clientRect.x + clientRect.width > rectCoordsMax.x) rectCoordsMax.x = clientRect.x + clientRect.width;
15849
+ if (clientRect.y + clientRect.height > rectCoordsMax.y) rectCoordsMax.y = clientRect.y + clientRect.height;
15850
+ nodes.push(node.getAttrs().id);
15851
+ }
15852
+ return {
15853
+ width: rectCoordsMax.x - rectCoordsMin.x,
15854
+ height: rectCoordsMax.y - rectCoordsMin.y,
15855
+ nodes
15856
+ };
15857
+ }
15858
+ function getTargetAndSkipNodes(instance, e) {
15859
+ const nodesSelectionPlugin = instance.getPlugin("nodesSelection");
15860
+ let skipNodes = [];
15861
+ let node = void 0;
15862
+ if (e.type === "dragmove" && nodesSelectionPlugin && nodesSelectionPlugin.getTransformer().nodes().length === 1) {
15863
+ node = nodesSelectionPlugin.getTransformer().nodes()[0];
15864
+ skipNodes.push(node.getAttrs().id ?? "");
15865
+ }
15866
+ if (e.type === "dragmove" && nodesSelectionPlugin && nodesSelectionPlugin.getTransformer().nodes().length > 1) {
15867
+ const { nodes } = getSelectedNodesMetadata(nodesSelectionPlugin.getTransformer());
15868
+ node = nodesSelectionPlugin.getTransformer();
15869
+ skipNodes = [...nodes];
15870
+ }
15871
+ if (e.type === "transform") {
15872
+ node = e.target;
15873
+ skipNodes.push(node.getAttrs().id ?? "");
15874
+ }
15875
+ return {
15876
+ targetNode: node,
15877
+ skipNodes
15878
+ };
15879
+ }
15880
+ function getVisibleNodesInViewport(stage, referenceLayer) {
15881
+ const scale = stage.scaleX();
15882
+ const stagePos = stage.position();
15883
+ const stageSize = {
15884
+ width: stage.width(),
15885
+ height: stage.height()
15886
+ };
15887
+ const viewRect = {
15888
+ x: -stagePos.x / scale,
15889
+ y: -stagePos.y / scale,
15890
+ width: stageSize.width / scale,
15891
+ height: stageSize.height / scale
15892
+ };
15893
+ const visibleNodes = [];
15894
+ referenceLayer?.find(".node").forEach((node) => {
15895
+ if (!node.isVisible()) return;
15896
+ const box = node.getClientRect({
15897
+ relativeTo: stage,
15898
+ skipStroke: true,
15899
+ skipShadow: true
15900
+ });
15901
+ 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;
15902
+ if (intersects) visibleNodes.push(node);
15903
+ });
15904
+ return visibleNodes;
15905
+ }
15832
15906
 
15833
15907
  //#endregion
15834
15908
  //#region src/actions/selection-tool/constants.ts
@@ -15863,7 +15937,7 @@ var WeaveContextMenuPlugin = class extends WeavePlugin {
15863
15937
  this.pointers = {};
15864
15938
  }
15865
15939
  getName() {
15866
- return WEAVE_CONTEXT_MENU_KEY;
15940
+ return WEAVE_CONTEXT_MENU_PLUGIN_KEY;
15867
15941
  }
15868
15942
  onInit() {
15869
15943
  this.initEvents();
@@ -16016,6 +16090,45 @@ const WEAVE_USER_SELECTION_KEY = "userSelection";
16016
16090
  const WEAVE_STAGE_NODE_TYPE = "stage";
16017
16091
  const WEAVE_STAGE_DEFAULT_MODE = "default";
16018
16092
 
16093
+ //#endregion
16094
+ //#region src/plugins/nodes-edge-snapping/constants.ts
16095
+ const WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY = "nodesEdgeSnapping";
16096
+ const GUIDE_LINE_NAME = "guide-edge-snapping-line";
16097
+ const GUIDE_LINE_DEFAULT_CONFIG = {
16098
+ stroke: "#ff0000",
16099
+ strokeWidth: 1,
16100
+ dash: []
16101
+ };
16102
+ const GUIDE_LINE_DRAG_SNAPPING_THRESHOLD = 3;
16103
+ const GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD = 3;
16104
+ const GUIDE_ORIENTATION = {
16105
+ ["HORIZONTAL"]: "H",
16106
+ ["VERTICAL"]: "V"
16107
+ };
16108
+ const NODE_SNAP = {
16109
+ ["START"]: "start",
16110
+ ["CENTER"]: "center",
16111
+ ["END"]: "end"
16112
+ };
16113
+
16114
+ //#endregion
16115
+ //#region src/plugins/nodes-distance-snapping/constants.ts
16116
+ const WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY = "nodesDistanceSnapping";
16117
+ const GUIDE_HORIZONTAL_LINE_NAME = "guide-distance-snapping-horizontal-line";
16118
+ const GUIDE_VERTICAL_LINE_NAME = "guide-distance-snapping-vertical-line";
16119
+ const GUIDE_ENTER_SNAPPING_TOLERANCE = 3;
16120
+ const GUIDE_EXIT_SNAPPING_TOLERANCE = 5;
16121
+ const NODE_SNAP_HORIZONTAL = {
16122
+ ["LEFT"]: "left",
16123
+ ["CENTER"]: "center",
16124
+ ["RIGHT"]: "right"
16125
+ };
16126
+ const NODE_SNAP_VERTICAL = {
16127
+ ["TOP"]: "top",
16128
+ ["MIDDLE"]: "middle",
16129
+ ["BOTTOM"]: "bottom"
16130
+ };
16131
+
16019
16132
  //#endregion
16020
16133
  //#region src/plugins/nodes-selection/nodes-selection.ts
16021
16134
  var WeaveNodesSelectionPlugin = class extends WeavePlugin {
@@ -16482,7 +16595,10 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16482
16595
  const moved = this.checkMoved(e);
16483
16596
  this.checkDoubleTap(e);
16484
16597
  delete this.pointers[e.evt.pointerId];
16485
- if (stage.mode() === WEAVE_STAGE_DEFAULT_MODE) this.getSnappingPlugin()?.cleanupEvaluateGuidelines();
16598
+ if (stage.mode() === WEAVE_STAGE_DEFAULT_MODE) {
16599
+ this.getNodesEdgeSnappingPlugin()?.cleanupGuidelines();
16600
+ this.getNodesDistanceSnappingPlugin()?.cleanupGuidelines();
16601
+ }
16486
16602
  const contextMenuPlugin = this.getContextMenuPlugin();
16487
16603
  if (!this.initialized) {
16488
16604
  this.hideSelectorArea();
@@ -16723,11 +16839,15 @@ var WeaveNodesSelectionPlugin = class extends WeavePlugin {
16723
16839
  this.enabled = false;
16724
16840
  }
16725
16841
  getContextMenuPlugin() {
16726
- const contextMenuPlugin = this.instance.getPlugin("contextMenu");
16842
+ const contextMenuPlugin = this.instance.getPlugin(WEAVE_CONTEXT_MENU_PLUGIN_KEY);
16727
16843
  return contextMenuPlugin;
16728
16844
  }
16729
- getSnappingPlugin() {
16730
- const snappingPlugin = this.instance.getPlugin("nodesSnapping");
16845
+ getNodesEdgeSnappingPlugin() {
16846
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY);
16847
+ return snappingPlugin;
16848
+ }
16849
+ getNodesDistanceSnappingPlugin() {
16850
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY);
16731
16851
  return snappingPlugin;
16732
16852
  }
16733
16853
  getSelectorConfig() {
@@ -17139,9 +17259,9 @@ var WeaveNode = class {
17139
17259
  const handleTransform = (e) => {
17140
17260
  const node$1 = e.target;
17141
17261
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
17142
- const nodesSnappingPlugin = this.instance.getPlugin("nodesSnapping");
17262
+ const nodesEdgeSnappingPlugin = this.getNodesEdgeSnappingPlugin();
17143
17263
  if (nodesSelectionPlugin && this.isSelecting() && this.isNodeSelected(node$1)) nodesSelectionPlugin.getTransformer().forceUpdate();
17144
- if (nodesSnappingPlugin && transforming && this.isSelecting() && this.isNodeSelected(node$1)) nodesSnappingPlugin.evaluateGuidelines(e);
17264
+ if (nodesEdgeSnappingPlugin && transforming && this.isSelecting() && this.isNodeSelected(node$1)) nodesEdgeSnappingPlugin.evaluateGuidelines(e);
17145
17265
  };
17146
17266
  node.on("transform", (0, import_lodash.throttle)(handleTransform, 100));
17147
17267
  node.on("transformend", (e) => {
@@ -17149,8 +17269,8 @@ var WeaveNode = class {
17149
17269
  this.instance.emitEvent("onTransform", null);
17150
17270
  transforming = false;
17151
17271
  const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
17152
- const nodesSnappingPlugin = this.instance.getPlugin("nodesSnapping");
17153
- if (nodesSnappingPlugin) nodesSnappingPlugin.cleanupEvaluateGuidelines();
17272
+ const nodesSnappingPlugin = this.getNodesEdgeSnappingPlugin();
17273
+ if (nodesSnappingPlugin) nodesSnappingPlugin.cleanupGuidelines();
17154
17274
  if (nodesSelectionPlugin) nodesSelectionPlugin.getTransformer().forceUpdate();
17155
17275
  this.scaleReset(node$1);
17156
17276
  const nodeHandler = this.instance.getNodeHandler(node$1.getAttrs().nodeType);
@@ -17208,8 +17328,10 @@ var WeaveNode = class {
17208
17328
  this.instance.emitEvent("onDrag", null);
17209
17329
  if (this.isSelecting() && this.isNodeSelected(node) && this.getSelectionPlugin()?.getSelectedNodes().length === 1) {
17210
17330
  clearContainerTargets(this.instance);
17211
- const nodesSnappingPlugin = this.instance.getPlugin("nodesSnapping");
17212
- if (nodesSnappingPlugin) nodesSnappingPlugin.cleanupEvaluateGuidelines();
17331
+ const nodesEdgeSnappingPlugin = this.getNodesEdgeSnappingPlugin();
17332
+ const nodesDistanceSnappingPlugin = this.getNodesDistanceSnappingPlugin();
17333
+ if (nodesEdgeSnappingPlugin) nodesEdgeSnappingPlugin.cleanupGuidelines();
17334
+ if (nodesDistanceSnappingPlugin) nodesDistanceSnappingPlugin.cleanupGuidelines();
17213
17335
  const layerToMove = containerOverCursor(this.instance, [node]);
17214
17336
  let containerToMove = this.instance.getMainLayer();
17215
17337
  if (layerToMove) {
@@ -17349,6 +17471,14 @@ var WeaveNode = class {
17349
17471
  ...nodeTransformConfig
17350
17472
  };
17351
17473
  }
17474
+ getNodesEdgeSnappingPlugin() {
17475
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY);
17476
+ return snappingPlugin;
17477
+ }
17478
+ getNodesDistanceSnappingPlugin() {
17479
+ const snappingPlugin = this.instance.getPlugin(WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY);
17480
+ return snappingPlugin;
17481
+ }
17352
17482
  };
17353
17483
 
17354
17484
  //#endregion
@@ -18916,7 +19046,7 @@ var WeaveRegisterManager = class {
18916
19046
 
18917
19047
  //#endregion
18918
19048
  //#region package.json
18919
- var version = "0.50.0";
19049
+ var version = "0.51.0";
18920
19050
 
18921
19051
  //#endregion
18922
19052
  //#region src/managers/setup.ts
@@ -25877,29 +26007,8 @@ var WeaveStageDropAreaPlugin = class extends WeavePlugin {
25877
26007
  };
25878
26008
 
25879
26009
  //#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 {
26010
+ //#region src/plugins/nodes-edge-snapping/nodes-edge-snapping.ts
26011
+ var WeaveNodesEdgeSnappingPlugin = class extends WeavePlugin {
25903
26012
  constructor(params) {
25904
26013
  super();
25905
26014
  const { config } = params ?? {};
@@ -25909,7 +26018,7 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
25909
26018
  this.enabled = true;
25910
26019
  }
25911
26020
  getName() {
25912
- return WEAVE_NODES_SNAPPING_KEY;
26021
+ return WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY;
25913
26022
  }
25914
26023
  onInit() {
25915
26024
  this.initEvents();
@@ -25917,32 +26026,6 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
25917
26026
  setEnabled(enabled) {
25918
26027
  this.enabled = enabled;
25919
26028
  }
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
26029
  deleteGuides() {
25947
26030
  const utilityLayer = this.instance.getUtilityLayer();
25948
26031
  if (utilityLayer) utilityLayer.find(`.${GUIDE_LINE_NAME}`).forEach((l) => l.destroy());
@@ -25951,87 +26034,73 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
25951
26034
  const utilityLayer = this.instance.getUtilityLayer();
25952
26035
  if (!this.enabled) return;
25953
26036
  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
- }
26037
+ const { targetNode: node, skipNodes } = getTargetAndSkipNodes(this.instance, e);
25970
26038
  if (typeof node === "undefined") return;
25971
- const lineGuideStops = this.getLineGuideStops(skipNodes);
26039
+ const visibleNodes = this.getVisibleNodes(skipNodes);
26040
+ const lineGuideStops = this.getLineGuideStops(visibleNodes);
25972
26041
  const itemBounds = this.getObjectSnappingEdges(node);
25973
26042
  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) {
26043
+ this.cleanupGuidelines();
26044
+ if (guides.length > 0) {
26045
+ this.drawGuides(guides);
26046
+ if (e.type === "dragmove") {
26047
+ const orgAbsPos = node.absolutePosition();
26048
+ const absPos = node.absolutePosition();
26049
+ guides.forEach((lg) => {
26050
+ switch (lg.orientation) {
26014
26051
  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;
26052
+ absPos.x = lg.lineGuide + lg.offset;
26017
26053
  break;
26018
26054
  }
26019
26055
  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;
26056
+ absPos.y = lg.lineGuide + lg.offset;
26022
26057
  break;
26023
26058
  }
26024
26059
  }
26025
- return finalPos;
26026
26060
  });
26061
+ const vecDiff = {
26062
+ x: orgAbsPos.x - absPos.x,
26063
+ y: orgAbsPos.y - absPos.y
26064
+ };
26065
+ if (node instanceof konva.default.Transformer) node.getNodes().forEach((n) => {
26066
+ const nodeAbsPos = n.getAbsolutePosition();
26067
+ const newPos = {
26068
+ x: nodeAbsPos.x - vecDiff.x,
26069
+ y: nodeAbsPos.y - vecDiff.y
26070
+ };
26071
+ n.setAbsolutePosition(newPos);
26072
+ });
26073
+ else node.absolutePosition(absPos);
26074
+ }
26075
+ if (e.type === "transform") {
26076
+ const nodesSelectionPlugin = this.instance.getPlugin("nodesSelection");
26077
+ if (nodesSelectionPlugin) {
26078
+ const transformer = nodesSelectionPlugin.getTransformer();
26079
+ transformer.anchorDragBoundFunc((_, newAbsPos) => {
26080
+ const finalPos = { ...newAbsPos };
26081
+ for (const lg of guides) switch (lg.orientation) {
26082
+ case GUIDE_ORIENTATION.VERTICAL: {
26083
+ const distX = Math.sqrt(Math.pow(newAbsPos.x - lg.lineGuide, 2));
26084
+ if (distX < this.transformSnappingThreshold) finalPos.x = lg.lineGuide;
26085
+ break;
26086
+ }
26087
+ case GUIDE_ORIENTATION.HORIZONTAL: {
26088
+ const distY = Math.sqrt(Math.pow(newAbsPos.y - lg.lineGuide, 2));
26089
+ if (distY < this.transformSnappingThreshold) finalPos.y = lg.lineGuide;
26090
+ break;
26091
+ }
26092
+ }
26093
+ return finalPos;
26094
+ });
26095
+ }
26027
26096
  }
26028
26097
  }
26029
26098
  }
26030
- cleanupEvaluateGuidelines() {
26099
+ cleanupGuidelines() {
26031
26100
  const utilityLayer = this.instance.getUtilityLayer();
26032
26101
  if (!this.enabled) return;
26033
26102
  if (!utilityLayer) return;
26034
- utilityLayer.destroyChildren();
26103
+ this.deleteGuides();
26035
26104
  }
26036
26105
  initEvents() {
26037
26106
  const stage = this.instance.getStage();
@@ -26041,45 +26110,29 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26041
26110
  this.evaluateGuidelines(e);
26042
26111
  });
26043
26112
  stage.on("dragend", () => {
26044
- this.cleanupEvaluateGuidelines();
26113
+ this.cleanupGuidelines();
26045
26114
  });
26046
26115
  }
26047
26116
  }
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) {
26117
+ getVisibleNodes(skipNodes) {
26065
26118
  const stage = this.instance.getStage();
26066
26119
  const nodesSelection = this.instance.getPlugin("nodesSelection");
26067
26120
  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;
26121
+ const nodes = getVisibleNodesInViewport(stage, this.instance.getMainLayer());
26122
+ const finalVisibleNodes = [];
26123
+ nodes.forEach((node) => {
26124
+ if (node.getParent()?.getAttrs().nodeType === "group") return;
26125
+ if (skipNodes.includes(node.getParent()?.getAttrs().nodeId)) return;
26126
+ if (skipNodes.includes(node.getAttrs().id ?? "")) return;
26127
+ finalVisibleNodes.push(node);
26128
+ });
26129
+ if (nodesSelection) nodesSelection.getTransformer().show();
26130
+ return finalVisibleNodes;
26131
+ }
26132
+ getLineGuideStops(nodes) {
26133
+ const vertical = [];
26134
+ const horizontal = [];
26135
+ nodes.forEach((guideItem) => {
26083
26136
  const box = guideItem.getClientRect({ skipStroke: true });
26084
26137
  vertical.push([
26085
26138
  box.x,
@@ -26092,7 +26145,6 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26092
26145
  box.y + box.height / 2
26093
26146
  ]);
26094
26147
  });
26095
- if (nodesSelection) nodesSelection.getTransformer().show();
26096
26148
  return {
26097
26149
  vertical: vertical.flat(),
26098
26150
  horizontal: horizontal.flat()
@@ -26110,16 +26162,19 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26110
26162
  const snappingEdges = {
26111
26163
  vertical: [
26112
26164
  {
26165
+ nodeId: node.getAttrs().id ?? "",
26113
26166
  guide: box.x,
26114
26167
  offset: Math.round(absPos.x - box.x),
26115
26168
  snap: NODE_SNAP.START
26116
26169
  },
26117
26170
  {
26171
+ nodeId: node.getAttrs().id ?? "",
26118
26172
  guide: box.x + box.width / 2,
26119
26173
  offset: Math.round(absPos.x - box.x - box.width / 2),
26120
26174
  snap: NODE_SNAP.CENTER
26121
26175
  },
26122
26176
  {
26177
+ nodeId: node.getAttrs().id ?? "",
26123
26178
  guide: box.x + box.width,
26124
26179
  offset: Math.round(absPos.x - box.x - box.width),
26125
26180
  snap: NODE_SNAP.END
@@ -26127,16 +26182,19 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26127
26182
  ],
26128
26183
  horizontal: [
26129
26184
  {
26185
+ nodeId: node.getAttrs().id ?? "",
26130
26186
  guide: Math.round(box.y),
26131
26187
  offset: Math.round(absPos.y - box.y),
26132
26188
  snap: NODE_SNAP.START
26133
26189
  },
26134
26190
  {
26191
+ nodeId: node.getAttrs().id ?? "",
26135
26192
  guide: Math.round(box.y + box.height / 2),
26136
26193
  offset: Math.round(absPos.y - box.y - box.height / 2),
26137
26194
  snap: NODE_SNAP.CENTER
26138
26195
  },
26139
26196
  {
26197
+ nodeId: node.getAttrs().id ?? "",
26140
26198
  guide: Math.round(box.y + box.height),
26141
26199
  offset: Math.round(absPos.y - box.y - box.height),
26142
26200
  snap: NODE_SNAP.END
@@ -26146,34 +26204,45 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26146
26204
  return snappingEdges;
26147
26205
  }
26148
26206
  getGuides(lineGuideStops, itemBounds, type) {
26207
+ const resultMapV = new Map();
26208
+ const resultMapH = new Map();
26149
26209
  const resultV = [];
26150
26210
  const resultH = [];
26151
26211
  lineGuideStops.vertical.forEach((lineGuide) => {
26152
26212
  itemBounds.vertical.forEach((itemBound) => {
26153
26213
  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
- });
26214
+ if (diff < this.dragSnappingThreshold && !resultMapV.has(itemBound.nodeId)) {
26215
+ const guide = {
26216
+ nodeId: itemBound.nodeId,
26217
+ lineGuide,
26218
+ diff,
26219
+ snap: itemBound.snap,
26220
+ offset: itemBound.offset
26221
+ };
26222
+ resultMapV.set(itemBound.nodeId, guide);
26223
+ resultV.push(guide);
26224
+ }
26160
26225
  });
26161
26226
  });
26162
26227
  lineGuideStops.horizontal.forEach((lineGuide) => {
26163
26228
  itemBounds.horizontal.forEach((itemBound) => {
26164
26229
  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
- });
26230
+ if (diff < this.dragSnappingThreshold && !resultMapH.has(itemBound.nodeId)) {
26231
+ const guide = {
26232
+ nodeId: itemBound.nodeId,
26233
+ lineGuide,
26234
+ diff,
26235
+ snap: itemBound.snap,
26236
+ offset: itemBound.offset
26237
+ };
26238
+ resultMapH.set(itemBound.nodeId, guide);
26239
+ resultH.push(guide);
26240
+ }
26171
26241
  });
26172
26242
  });
26173
26243
  const guides = [];
26174
26244
  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];
26245
+ const { minH, minV } = this.sortedGuides(resultH, resultV);
26177
26246
  if (minV) guides.push({
26178
26247
  lineGuide: minV.lineGuide,
26179
26248
  offset: minV.offset,
@@ -26207,6 +26276,14 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26207
26276
  }
26208
26277
  return guides;
26209
26278
  }
26279
+ sortedGuides(resultH, resultV) {
26280
+ const minV = resultV.toSorted((a, b) => a.diff - b.diff)[0];
26281
+ const minH = resultH.toSorted((a, b) => a.diff - b.diff)[0];
26282
+ return {
26283
+ minH,
26284
+ minV
26285
+ };
26286
+ }
26210
26287
  drawGuides(guides) {
26211
26288
  const stage = this.instance.getStage();
26212
26289
  const utilityLayer = this.instance.getUtilityLayer();
@@ -26259,6 +26336,518 @@ var WeaveNodesSnappingPlugin = class extends WeavePlugin {
26259
26336
  }
26260
26337
  };
26261
26338
 
26339
+ //#endregion
26340
+ //#region src/plugins/nodes-distance-snapping/nodes-distance-snapping.ts
26341
+ var WeaveNodesDistanceSnappingPlugin = class extends WeavePlugin {
26342
+ peerDistanceX = null;
26343
+ peerDistanceY = null;
26344
+ snapPositionX = null;
26345
+ snapPositionY = null;
26346
+ currentSizeSnapHorizontal = null;
26347
+ currentSizeSnapVertical = null;
26348
+ constructor(params) {
26349
+ super();
26350
+ const { config } = params ?? {};
26351
+ this.enterSnappingTolerance = config?.enterSnappingTolerance ?? GUIDE_ENTER_SNAPPING_TOLERANCE;
26352
+ this.exitSnappingTolerance = config?.exitSnappingTolerance ?? GUIDE_EXIT_SNAPPING_TOLERANCE;
26353
+ this.enabled = true;
26354
+ }
26355
+ getName() {
26356
+ return WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY;
26357
+ }
26358
+ onInit() {
26359
+ this.initEvents();
26360
+ }
26361
+ setEnabled(enabled) {
26362
+ this.enabled = enabled;
26363
+ }
26364
+ deleteGuides() {
26365
+ const utilityLayer = this.instance.getUtilityLayer();
26366
+ if (utilityLayer) {
26367
+ utilityLayer.find(`.${GUIDE_HORIZONTAL_LINE_NAME}`).forEach((l) => l.destroy());
26368
+ utilityLayer.find(`.${GUIDE_VERTICAL_LINE_NAME}`).forEach((l) => l.destroy());
26369
+ }
26370
+ }
26371
+ evaluateGuidelines(e) {
26372
+ const stage = this.instance.getStage();
26373
+ const mainLayer = this.instance.getMainLayer();
26374
+ const utilityLayer = this.instance.getUtilityLayer();
26375
+ if (!this.enabled) return;
26376
+ if (!utilityLayer) return;
26377
+ const { targetNode: node, skipNodes } = getTargetAndSkipNodes(this.instance, e);
26378
+ if (typeof node === "undefined") return;
26379
+ if (node.getParent() === mainLayer) this.referenceLayer = mainLayer;
26380
+ if (node.getParent()?.getAttrs().nodeId) {
26381
+ const realNode = stage.findOne(`#${node.getParent()?.getAttrs().nodeId}`);
26382
+ if (realNode) this.referenceLayer = realNode;
26383
+ }
26384
+ const visibleNodes = this.getVisibleNodes(skipNodes);
26385
+ const { intersectedNodes: sortedHorizontalIntersectedNodes, intersectedNodesWithDistances: horizontalIntersectedNodes } = this.getHorizontallyIntersectingNodes(node, visibleNodes);
26386
+ const { intersectedNodes: sortedVerticalIntersectedNodes, intersectedNodesWithDistances: verticalIntersectedNodes } = this.getVerticallyIntersectingNodes(node, visibleNodes);
26387
+ this.cleanupGuidelines();
26388
+ if (horizontalIntersectedNodes.length > 0 || verticalIntersectedNodes.length > 0) {
26389
+ if (e.type === "dragmove") {
26390
+ this.validateHorizontalSnapping(node, visibleNodes, sortedHorizontalIntersectedNodes, horizontalIntersectedNodes);
26391
+ this.validateVerticalSnapping(node, visibleNodes, sortedVerticalIntersectedNodes, verticalIntersectedNodes);
26392
+ }
26393
+ }
26394
+ }
26395
+ getBoxClientRect(node) {
26396
+ const stage = this.instance.getStage();
26397
+ return node.getClientRect({
26398
+ relativeTo: stage,
26399
+ skipStroke: true,
26400
+ skipShadow: true
26401
+ });
26402
+ }
26403
+ getPeers(intersectedNodes, targetNode, prev, next) {
26404
+ const peers = intersectedNodes.filter((int) => {
26405
+ if (prev && next) return int.to.getAttrs().id !== targetNode.getAttrs().id && int.from.getAttrs().id !== targetNode.getAttrs().id;
26406
+ if (!prev && next) return int.from.getAttrs().id !== targetNode.getAttrs().id;
26407
+ return int.to.getAttrs().id !== targetNode.getAttrs().id;
26408
+ });
26409
+ let prevBox = null;
26410
+ if (prev) prevBox = this.getBoxClientRect(prev);
26411
+ let nextBox = null;
26412
+ if (next) nextBox = this.getBoxClientRect(next);
26413
+ return {
26414
+ prevBox,
26415
+ nextBox,
26416
+ peers
26417
+ };
26418
+ }
26419
+ validateHorizontalSnapping(node, visibleNodes, sortedHorizontalIntersectedNodes, horizontalIntersectedNodes) {
26420
+ const box = this.getBoxClientRect(node);
26421
+ const targetIndex = sortedHorizontalIntersectedNodes.findIndex((actNode) => actNode.getAttrs().id === node.getAttrs().id);
26422
+ const prev = sortedHorizontalIntersectedNodes[targetIndex - 1];
26423
+ const next = sortedHorizontalIntersectedNodes[targetIndex + 1];
26424
+ const { prevBox, nextBox, peers } = this.getPeers(horizontalIntersectedNodes, node, prev, next);
26425
+ if (this.currentSizeSnapHorizontal === NODE_SNAP_HORIZONTAL.LEFT && prev && prevBox) {
26426
+ const dist = Math.round(box.x - (prevBox.x + prevBox.width));
26427
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26428
+ if (!match) this.currentSizeSnapHorizontal = null;
26429
+ }
26430
+ if (this.currentSizeSnapHorizontal === NODE_SNAP_HORIZONTAL.RIGHT && next && nextBox) {
26431
+ const dist = Math.round(nextBox.x - (box.x + box.width));
26432
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26433
+ if (!match) this.currentSizeSnapHorizontal = null;
26434
+ }
26435
+ if (prev && prevBox && next && nextBox && prevBox.x + prevBox.width <= box.x && box.x + box.width <= nextBox.x) {
26436
+ const distanceToPrev = box.x - (prevBox.x + prevBox.width);
26437
+ const distanceToNext = nextBox.x - (box.x + box.width);
26438
+ const delta = Math.abs(distanceToPrev - distanceToNext);
26439
+ if (delta <= this.enterSnappingTolerance) {
26440
+ const center = (prevBox.x + prevBox.width + nextBox.x) / 2;
26441
+ const newX = center - box.width / 2;
26442
+ this.setNodeClientRectX(node, newX);
26443
+ this.snapPositionX = node.x();
26444
+ this.currentSizeSnapHorizontal = NODE_SNAP_HORIZONTAL.CENTER;
26445
+ const newBox = this.getBoxClientRect(node);
26446
+ this.peerDistanceX = Math.round(newBox.x - (prevBox.x + prevBox.width));
26447
+ }
26448
+ if (this.currentSizeSnapHorizontal === NODE_SNAP_HORIZONTAL.CENTER && delta > this.exitSnappingTolerance) this.currentSizeSnapHorizontal = null;
26449
+ }
26450
+ if (this.currentSizeSnapHorizontal && this.peerDistanceX && this.snapPositionX) {
26451
+ node.x(this.snapPositionX);
26452
+ const { intersectedNodesWithDistances: newHorizontalIntersectedNodes } = this.getHorizontallyIntersectingNodes(node, visibleNodes);
26453
+ this.drawSizeGuidesHorizontally(newHorizontalIntersectedNodes, this.peerDistanceX);
26454
+ return;
26455
+ }
26456
+ const canSnapLeft = prev && prevBox && (() => {
26457
+ const dist = Math.round(box.x - (prevBox.x + prevBox.width));
26458
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26459
+ if (match) {
26460
+ const newX = prevBox.x + prevBox.width + match.distance;
26461
+ this.setNodeClientRectX(node, newX);
26462
+ this.snapPositionX = node.x();
26463
+ this.currentSizeSnapHorizontal = NODE_SNAP_HORIZONTAL.LEFT;
26464
+ const newBox = this.getBoxClientRect(node);
26465
+ this.peerDistanceX = Math.round(newBox.x - (prevBox.x + prevBox.width));
26466
+ return true;
26467
+ }
26468
+ return false;
26469
+ })();
26470
+ if (!canSnapLeft && next && nextBox) {
26471
+ const dist = Math.round(nextBox.x - (box.x + box.width));
26472
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26473
+ if (match) {
26474
+ const newX = nextBox.x - match.distance - box.width;
26475
+ this.setNodeClientRectX(node, newX);
26476
+ this.snapPositionX = node.x();
26477
+ const newBox = this.getBoxClientRect(node);
26478
+ this.peerDistanceX = Math.round(nextBox.x - (newBox.x + newBox.width));
26479
+ this.currentSizeSnapHorizontal = NODE_SNAP_HORIZONTAL.RIGHT;
26480
+ }
26481
+ }
26482
+ }
26483
+ validateVerticalSnapping(node, visibleNodes, sortedVerticalIntersectedNodes, verticalIntersectedNodes) {
26484
+ const box = this.getBoxClientRect(node);
26485
+ const targetIndex = sortedVerticalIntersectedNodes.findIndex((actNode) => actNode.getAttrs().id === node.getAttrs().id);
26486
+ const prev = sortedVerticalIntersectedNodes[targetIndex - 1];
26487
+ const next = sortedVerticalIntersectedNodes[targetIndex + 1];
26488
+ const { prevBox, nextBox, peers } = this.getPeers(verticalIntersectedNodes, node, prev, next);
26489
+ if (this.currentSizeSnapVertical === NODE_SNAP_VERTICAL.TOP && prev && prevBox) {
26490
+ const dist = Math.round(box.y - (prevBox.y + prevBox.height));
26491
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26492
+ if (!match) this.currentSizeSnapVertical = null;
26493
+ }
26494
+ if (this.currentSizeSnapVertical === NODE_SNAP_VERTICAL.BOTTOM && next && nextBox) {
26495
+ const dist = Math.round(nextBox.y - (box.y + box.height));
26496
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.exitSnappingTolerance);
26497
+ if (!match) this.currentSizeSnapVertical = null;
26498
+ }
26499
+ if (prev && prevBox && next && nextBox && prevBox.y + prevBox.height <= box.y && box.y + box.height <= nextBox.y) {
26500
+ const distanceToPrev = box.y - (prevBox.y + prevBox.height);
26501
+ const distanceToNext = nextBox.y - (box.y + box.height);
26502
+ const delta = Math.abs(distanceToPrev - distanceToNext);
26503
+ if (delta <= this.enterSnappingTolerance) {
26504
+ const center = (prevBox.y + prevBox.height + nextBox.y) / 2;
26505
+ const newY = center - box.height / 2;
26506
+ this.setNodeClientRectY(node, newY);
26507
+ this.snapPositionY = node.y();
26508
+ this.currentSizeSnapVertical = NODE_SNAP_VERTICAL.MIDDLE;
26509
+ const newBox = this.getBoxClientRect(node);
26510
+ this.peerDistanceY = Math.round(newBox.y - (prevBox.y + prevBox.height));
26511
+ }
26512
+ if (this.currentSizeSnapVertical === NODE_SNAP_VERTICAL.MIDDLE && delta > this.exitSnappingTolerance) this.currentSizeSnapVertical = null;
26513
+ }
26514
+ if (this.currentSizeSnapVertical && this.peerDistanceY && this.snapPositionY) {
26515
+ node.y(this.snapPositionY);
26516
+ const { intersectedNodesWithDistances: newVerticalIntersectedNodes } = this.getVerticallyIntersectingNodes(node, visibleNodes);
26517
+ this.drawSizeGuidesVertically(newVerticalIntersectedNodes, this.peerDistanceY);
26518
+ return;
26519
+ }
26520
+ const canSnapTop = prev && prevBox && (() => {
26521
+ const dist = Math.round(box.y - (prevBox.y + prevBox.height));
26522
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26523
+ if (match) {
26524
+ const newY = prevBox.y + prevBox.height + match.distance;
26525
+ this.setNodeClientRectY(node, newY);
26526
+ this.snapPositionY = node.y();
26527
+ this.currentSizeSnapVertical = NODE_SNAP_VERTICAL.TOP;
26528
+ const newBox = this.getBoxClientRect(node);
26529
+ this.peerDistanceY = Math.round(newBox.y - (prevBox.y + prevBox.height));
26530
+ return true;
26531
+ }
26532
+ return false;
26533
+ })();
26534
+ if (!canSnapTop && next && nextBox) {
26535
+ const dist = Math.round(nextBox.y - (box.y + box.height));
26536
+ const match = peers.find((d) => Math.abs(d.distance - dist) <= this.enterSnappingTolerance);
26537
+ if (match) {
26538
+ const newY = nextBox.y - match.distance - box.height;
26539
+ this.setNodeClientRectY(node, newY);
26540
+ this.snapPositionY = node.y();
26541
+ const newBox = this.getBoxClientRect(node);
26542
+ this.peerDistanceY = Math.round(nextBox.y - (newBox.y + newBox.height));
26543
+ this.currentSizeSnapVertical = NODE_SNAP_VERTICAL.BOTTOM;
26544
+ }
26545
+ }
26546
+ }
26547
+ setNodeClientRectX(node, snappedClientX) {
26548
+ if (node.getParent()?.getType() === "Layer") {
26549
+ node.x(snappedClientX);
26550
+ return;
26551
+ }
26552
+ const box = this.getBoxClientRect(node);
26553
+ const absolutePos = node.getAbsolutePosition();
26554
+ const offsetX = absolutePos.x - box.x;
26555
+ const newAbsX = snappedClientX + offsetX;
26556
+ const parent = node.getParent();
26557
+ if (!parent) {
26558
+ console.warn("Node has no parent to set position");
26559
+ return;
26560
+ }
26561
+ const local = parent.getAbsoluteTransform().copy().invert().point({
26562
+ x: newAbsX,
26563
+ y: absolutePos.y
26564
+ });
26565
+ node.position({
26566
+ x: local.x,
26567
+ y: node.y()
26568
+ });
26569
+ }
26570
+ setNodeClientRectY(node, snappedClientY) {
26571
+ if (node.getParent()?.getType() === "Layer") {
26572
+ node.y(snappedClientY);
26573
+ return;
26574
+ }
26575
+ const box = this.getBoxClientRect(node);
26576
+ const absolutePos = node.getAbsolutePosition();
26577
+ const offsetY = absolutePos.y - box.y;
26578
+ const newAbsY = snappedClientY + offsetY;
26579
+ const parent = node.getParent();
26580
+ if (!parent) {
26581
+ console.warn("Node has no parent to set position");
26582
+ return;
26583
+ }
26584
+ const local = parent.getAbsoluteTransform().copy().invert().point({
26585
+ x: absolutePos.x,
26586
+ y: newAbsY
26587
+ });
26588
+ node.position({
26589
+ x: node.x(),
26590
+ y: local.y
26591
+ });
26592
+ }
26593
+ cleanupGuidelines() {
26594
+ const utilityLayer = this.instance.getUtilityLayer();
26595
+ if (!this.enabled) return;
26596
+ if (!utilityLayer) return;
26597
+ this.deleteGuides();
26598
+ }
26599
+ initEvents() {
26600
+ const stage = this.instance.getStage();
26601
+ const utilityLayer = this.instance.getUtilityLayer();
26602
+ if (utilityLayer) {
26603
+ stage.on("dragmove", (e) => {
26604
+ this.evaluateGuidelines(e);
26605
+ });
26606
+ stage.on("dragend", () => {
26607
+ this.peerDistanceX = null;
26608
+ this.peerDistanceY = null;
26609
+ this.currentSizeSnapVertical = null;
26610
+ this.currentSizeSnapHorizontal = null;
26611
+ this.cleanupGuidelines();
26612
+ });
26613
+ }
26614
+ }
26615
+ getVerticallyIntersectingNodes(targetNode, nodes) {
26616
+ const targetBox = this.getBoxClientRect(targetNode);
26617
+ const intersectedNodes = [];
26618
+ nodes.forEach((node) => {
26619
+ if (node === targetNode || !node.isVisible()) return false;
26620
+ const box = this.getBoxClientRect(node);
26621
+ const horizontalOverlap = box.x + box.width > targetBox.x && box.x < targetBox.x + targetBox.width;
26622
+ if (horizontalOverlap) intersectedNodes.push(node);
26623
+ });
26624
+ intersectedNodes.push(targetNode);
26625
+ intersectedNodes.sort((a, b) => {
26626
+ const ay = this.getBoxClientRect(a).y;
26627
+ const by = this.getBoxClientRect(b).y;
26628
+ return ay - by;
26629
+ });
26630
+ const intersectedNodesWithDistances = [];
26631
+ for (let i = 0; i < intersectedNodes.length - 1; i++) {
26632
+ const a = intersectedNodes[i];
26633
+ const b = intersectedNodes[i + 1];
26634
+ const boxA = this.getBoxClientRect(a);
26635
+ const boxB = this.getBoxClientRect(b);
26636
+ const aBottom = boxA.y + boxA.height;
26637
+ const bTop = boxB.y;
26638
+ const distance = Math.abs(aBottom - bTop);
26639
+ const left = Math.max(boxA.x, boxB.x);
26640
+ const right = Math.min(boxA.x + boxA.width, boxB.x + boxB.width);
26641
+ let midX;
26642
+ if (right > left) midX = left + (right - left) / 2;
26643
+ else {
26644
+ const aCenterX = boxA.x + boxA.width / 2;
26645
+ const bCenterX = boxB.x + boxB.width / 2;
26646
+ midX = (aCenterX + bCenterX) / 2;
26647
+ }
26648
+ intersectedNodesWithDistances.push({
26649
+ index: i,
26650
+ from: a,
26651
+ to: b,
26652
+ midX,
26653
+ distance: Math.round(distance)
26654
+ });
26655
+ }
26656
+ return {
26657
+ intersectedNodes,
26658
+ intersectedNodesWithDistances
26659
+ };
26660
+ }
26661
+ getHorizontallyIntersectingNodes(targetNode, nodes) {
26662
+ const targetBox = this.getBoxClientRect(targetNode);
26663
+ const intersectedNodes = [];
26664
+ nodes.forEach((node) => {
26665
+ if (node === targetNode || !node.isVisible()) return false;
26666
+ const box = this.getBoxClientRect(node);
26667
+ const verticalOverlap = box.y + box.height > targetBox.y && box.y < targetBox.y + targetBox.height;
26668
+ if (verticalOverlap) intersectedNodes.push(node);
26669
+ });
26670
+ intersectedNodes.push(targetNode);
26671
+ intersectedNodes.sort((a, b) => {
26672
+ const ax = this.getBoxClientRect(a).x;
26673
+ const bx = this.getBoxClientRect(b).x;
26674
+ return ax - bx;
26675
+ });
26676
+ const intersectedNodesWithDistances = [];
26677
+ for (let i = 0; i < intersectedNodes.length - 1; i++) {
26678
+ const a = intersectedNodes[i];
26679
+ const b = intersectedNodes[i + 1];
26680
+ const boxA = this.getBoxClientRect(a);
26681
+ const boxB = this.getBoxClientRect(b);
26682
+ const aRight = boxA.x + boxA.width;
26683
+ const bLeft = boxB.x;
26684
+ const distance = Math.abs(Math.round(aRight - bLeft));
26685
+ const top = Math.max(boxA.y, boxB.y);
26686
+ const bottom = Math.min(boxA.y + boxA.height, boxB.y + boxB.height);
26687
+ let midY;
26688
+ if (bottom > top) midY = top + (bottom - top) / 2;
26689
+ else {
26690
+ const aCenterY = boxA.y + boxA.height / 2;
26691
+ const bCenterY = boxB.y + boxB.height / 2;
26692
+ midY = (aCenterY + bCenterY) / 2;
26693
+ }
26694
+ intersectedNodesWithDistances.push({
26695
+ index: i,
26696
+ from: a,
26697
+ to: b,
26698
+ midY,
26699
+ distance
26700
+ });
26701
+ }
26702
+ return {
26703
+ intersectedNodes,
26704
+ intersectedNodesWithDistances
26705
+ };
26706
+ }
26707
+ getVisibleNodes(skipNodes) {
26708
+ const stage = this.instance.getStage();
26709
+ const nodesSelection = this.instance.getPlugin("nodesSelection");
26710
+ if (nodesSelection) nodesSelection.getTransformer().hide();
26711
+ const nodes = getVisibleNodesInViewport(stage, this.referenceLayer);
26712
+ const finalVisibleNodes = [];
26713
+ nodes.forEach((node) => {
26714
+ if (node.getParent()?.getAttrs().nodeType === "group") return;
26715
+ if (skipNodes.includes(node.getParent()?.getAttrs().nodeId)) return;
26716
+ if (skipNodes.includes(node.getAttrs().id ?? "")) return;
26717
+ if (node.getParent() !== this.referenceLayer && !node.getParent()?.getAttrs().nodeId) return;
26718
+ if (node.getParent() !== this.referenceLayer && node.getParent()?.getAttrs().nodeId) {
26719
+ const realNode = stage.findOne(`#${node.getParent()?.getAttrs().nodeId}`);
26720
+ if (realNode && realNode !== this.referenceLayer) return;
26721
+ }
26722
+ finalVisibleNodes.push(node);
26723
+ });
26724
+ if (nodesSelection) nodesSelection.getTransformer().show();
26725
+ return finalVisibleNodes;
26726
+ }
26727
+ drawSizeGuidesHorizontally(intersectionsH, peerDistance) {
26728
+ const utilityLayer = this.instance.getUtilityLayer();
26729
+ if (utilityLayer) intersectionsH.forEach((pairInfo) => {
26730
+ const from = this.getBoxClientRect(pairInfo.from);
26731
+ const to = this.getBoxClientRect(pairInfo.to);
26732
+ if (pairInfo.distance === peerDistance) this.renderHorizontalLineWithDistanceBetweenNodes(from, to, pairInfo.midY, `${pairInfo.distance}`);
26733
+ });
26734
+ }
26735
+ drawSizeGuidesVertically(intersectionsV, peerDistance) {
26736
+ const utilityLayer = this.instance.getUtilityLayer();
26737
+ if (utilityLayer) intersectionsV.forEach((pairInfo) => {
26738
+ const from = this.getBoxClientRect(pairInfo.from);
26739
+ const to = this.getBoxClientRect(pairInfo.to);
26740
+ if (pairInfo.distance === peerDistance) this.renderVerticalLineWithDistanceBetweenNodes(from, to, pairInfo.midX, `${pairInfo.distance}`);
26741
+ });
26742
+ }
26743
+ renderDistanceLabel(ctx, stage, labelText, { canvasMidX, canvasMidY }) {
26744
+ const scaleX = stage?.scaleX() || 1;
26745
+ const scaleY = stage?.scaleY() || 1;
26746
+ const fontSize = 12;
26747
+ const fontFamily = "Arial";
26748
+ const padding = 6;
26749
+ const tempText = new konva.default.Text({
26750
+ text: labelText,
26751
+ fontSize,
26752
+ fontFamily,
26753
+ visible: false
26754
+ });
26755
+ const textWidth = tempText.width();
26756
+ const textHeight = tempText.height();
26757
+ const labelWidth = textWidth + padding * 2;
26758
+ const labelHeight = textHeight + padding * 2;
26759
+ ctx.save();
26760
+ ctx.scale(1 / scaleX, 1 / scaleY);
26761
+ const labelX = canvasMidX - labelWidth / 2;
26762
+ const labelY = canvasMidY - labelHeight / 2;
26763
+ ctx.beginPath();
26764
+ ctx.rect(labelX, labelY, labelWidth, labelHeight);
26765
+ ctx.fillStyle = "#ff0000";
26766
+ ctx.fill();
26767
+ ctx.font = `bold ${fontSize}px ${fontFamily}`;
26768
+ ctx.fillStyle = "white";
26769
+ ctx.textAlign = "center";
26770
+ ctx.textBaseline = "middle";
26771
+ ctx.fillText(labelText, canvasMidX, labelY + labelHeight / 2);
26772
+ ctx.restore();
26773
+ }
26774
+ renderHorizontalLineWithDistanceBetweenNodes(from, to, midY, labelText) {
26775
+ const utilityLayer = this.instance.getUtilityLayer();
26776
+ const renderLabel = this.renderDistanceLabel;
26777
+ const lineWithLabel = new konva.default.Shape({
26778
+ name: GUIDE_HORIZONTAL_LINE_NAME,
26779
+ sceneFunc: function(ctx, shape) {
26780
+ const stage = shape.getStage();
26781
+ const scaleX = stage?.scaleX() || 1;
26782
+ const scaleY = stage?.scaleY() || 1;
26783
+ const x1 = from.x + from.width;
26784
+ const x2 = to.x;
26785
+ const y = midY;
26786
+ ctx.beginPath();
26787
+ ctx.moveTo(x1, y);
26788
+ ctx.lineTo(x2, y);
26789
+ ctx.closePath();
26790
+ ctx.strokeStyle = "#ff0000";
26791
+ ctx.lineWidth = 1;
26792
+ ctx.setLineDash([]);
26793
+ ctx.stroke();
26794
+ ctx.closePath();
26795
+ const worldMidX = (x1 + x2) / 2;
26796
+ const worldMidY = y;
26797
+ const canvasMidX = worldMidX * scaleX;
26798
+ const canvasMidY = worldMidY * scaleY;
26799
+ renderLabel(ctx, stage, labelText, {
26800
+ canvasMidX,
26801
+ canvasMidY
26802
+ });
26803
+ ctx.fillStrokeShape(shape);
26804
+ }
26805
+ });
26806
+ lineWithLabel.moveToBottom();
26807
+ utilityLayer?.add(lineWithLabel);
26808
+ }
26809
+ renderVerticalLineWithDistanceBetweenNodes(from, to, midX, labelText) {
26810
+ const utilityLayer = this.instance.getUtilityLayer();
26811
+ const renderLabel = this.renderDistanceLabel;
26812
+ const lineWithLabel = new konva.default.Shape({
26813
+ name: GUIDE_VERTICAL_LINE_NAME,
26814
+ sceneFunc: function(ctx, shape) {
26815
+ const stage = shape.getStage();
26816
+ const scaleX = stage?.scaleX() || 1;
26817
+ const scaleY = stage?.scaleY() || 1;
26818
+ const x = midX;
26819
+ const y1 = from.y + from.height;
26820
+ const y2 = to.y;
26821
+ ctx.beginPath();
26822
+ ctx.setLineDash([]);
26823
+ ctx.moveTo(x, y1);
26824
+ ctx.lineTo(x, y2);
26825
+ ctx.strokeStyle = "#ff0000";
26826
+ ctx.lineWidth = 1;
26827
+ ctx.stroke();
26828
+ ctx.closePath();
26829
+ const worldMidX = x;
26830
+ const worldMidY = (y1 + y2) / 2;
26831
+ const canvasMidX = worldMidX * scaleX;
26832
+ const canvasMidY = worldMidY * scaleY;
26833
+ renderLabel(ctx, stage, labelText, {
26834
+ canvasMidX,
26835
+ canvasMidY
26836
+ });
26837
+ ctx.fillStrokeShape(shape);
26838
+ }
26839
+ });
26840
+ lineWithLabel.moveToBottom();
26841
+ utilityLayer?.add(lineWithLabel);
26842
+ }
26843
+ enable() {
26844
+ this.enabled = true;
26845
+ }
26846
+ disable() {
26847
+ this.enabled = false;
26848
+ }
26849
+ };
26850
+
26262
26851
  //#endregion
26263
26852
  exports.ALIGN_NODES_ALIGN_TO = ALIGN_NODES_ALIGN_TO
26264
26853
  exports.ALIGN_NODES_TOOL_ACTION_NAME = ALIGN_NODES_TOOL_ACTION_NAME
@@ -26274,16 +26863,22 @@ exports.ERASER_TOOL_ACTION_NAME = ERASER_TOOL_ACTION_NAME
26274
26863
  exports.ERASER_TOOL_STATE = ERASER_TOOL_STATE
26275
26864
  exports.FRAME_TOOL_ACTION_NAME = FRAME_TOOL_ACTION_NAME
26276
26865
  exports.FRAME_TOOL_STATE = FRAME_TOOL_STATE
26866
+ exports.GUIDE_ENTER_SNAPPING_TOLERANCE = GUIDE_ENTER_SNAPPING_TOLERANCE
26867
+ exports.GUIDE_EXIT_SNAPPING_TOLERANCE = GUIDE_EXIT_SNAPPING_TOLERANCE
26868
+ exports.GUIDE_HORIZONTAL_LINE_NAME = GUIDE_HORIZONTAL_LINE_NAME
26277
26869
  exports.GUIDE_LINE_DEFAULT_CONFIG = GUIDE_LINE_DEFAULT_CONFIG
26278
26870
  exports.GUIDE_LINE_DRAG_SNAPPING_THRESHOLD = GUIDE_LINE_DRAG_SNAPPING_THRESHOLD
26279
26871
  exports.GUIDE_LINE_NAME = GUIDE_LINE_NAME
26280
26872
  exports.GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD = GUIDE_LINE_TRANSFORM_SNAPPING_THRESHOLD
26281
26873
  exports.GUIDE_ORIENTATION = GUIDE_ORIENTATION
26874
+ exports.GUIDE_VERTICAL_LINE_NAME = GUIDE_VERTICAL_LINE_NAME
26282
26875
  exports.IMAGE_TOOL_ACTION_NAME = IMAGE_TOOL_ACTION_NAME
26283
26876
  exports.IMAGE_TOOL_STATE = IMAGE_TOOL_STATE
26284
26877
  exports.MOVE_TOOL_ACTION_NAME = MOVE_TOOL_ACTION_NAME
26285
26878
  exports.MOVE_TOOL_STATE = MOVE_TOOL_STATE
26286
26879
  exports.NODE_SNAP = NODE_SNAP
26880
+ exports.NODE_SNAP_HORIZONTAL = NODE_SNAP_HORIZONTAL
26881
+ exports.NODE_SNAP_VERTICAL = NODE_SNAP_VERTICAL
26287
26882
  exports.PEN_TOOL_ACTION_NAME = PEN_TOOL_ACTION_NAME
26288
26883
  exports.PEN_TOOL_STATE = PEN_TOOL_STATE
26289
26884
  exports.RECTANGLE_TOOL_ACTION_NAME = RECTANGLE_TOOL_ACTION_NAME
@@ -26322,9 +26917,10 @@ exports.WEAVE_IMAGE_CROP_END_TYPE = WEAVE_IMAGE_CROP_END_TYPE
26322
26917
  exports.WEAVE_IMAGE_NODE_TYPE = WEAVE_IMAGE_NODE_TYPE
26323
26918
  exports.WEAVE_LAYER_NODE_TYPE = WEAVE_LAYER_NODE_TYPE
26324
26919
  exports.WEAVE_LINE_NODE_TYPE = WEAVE_LINE_NODE_TYPE
26920
+ exports.WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY = WEAVE_NODES_DISTANCE_SNAPPING_PLUGIN_KEY
26921
+ exports.WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY = WEAVE_NODES_EDGE_SNAPPING_PLUGIN_KEY
26325
26922
  exports.WEAVE_NODES_SELECTION_KEY = WEAVE_NODES_SELECTION_KEY
26326
26923
  exports.WEAVE_NODES_SELECTION_LAYER_ID = WEAVE_NODES_SELECTION_LAYER_ID
26327
- exports.WEAVE_NODES_SNAPPING_KEY = WEAVE_NODES_SNAPPING_KEY
26328
26924
  exports.WEAVE_RECTANGLE_NODE_TYPE = WEAVE_RECTANGLE_NODE_TYPE
26329
26925
  exports.WEAVE_REGULAR_POLYGON_NODE_TYPE = WEAVE_REGULAR_POLYGON_NODE_TYPE
26330
26926
  exports.WEAVE_STAGE_DEFAULT_MODE = WEAVE_STAGE_DEFAULT_MODE
@@ -26363,8 +26959,9 @@ exports.WeaveLayerNode = WeaveLayerNode
26363
26959
  exports.WeaveLineNode = WeaveLineNode
26364
26960
  exports.WeaveMoveToolAction = WeaveMoveToolAction
26365
26961
  exports.WeaveNode = WeaveNode
26962
+ exports.WeaveNodesDistanceSnappingPlugin = WeaveNodesDistanceSnappingPlugin
26963
+ exports.WeaveNodesEdgeSnappingPlugin = WeaveNodesEdgeSnappingPlugin
26366
26964
  exports.WeaveNodesSelectionPlugin = WeaveNodesSelectionPlugin
26367
- exports.WeaveNodesSnappingPlugin = WeaveNodesSnappingPlugin
26368
26965
  exports.WeavePenToolAction = WeavePenToolAction
26369
26966
  exports.WeavePlugin = WeavePlugin
26370
26967
  exports.WeaveRectangleNode = WeaveRectangleNode
@@ -26394,7 +26991,10 @@ exports.containsNodeDeep = containsNodeDeep
26394
26991
  exports.getBoundingBox = getBoundingBox
26395
26992
  exports.getContrastTextColor = getContrastTextColor
26396
26993
  exports.getExportBoundingBox = getExportBoundingBox
26994
+ exports.getSelectedNodesMetadata = getSelectedNodesMetadata
26995
+ exports.getTargetAndSkipNodes = getTargetAndSkipNodes
26397
26996
  exports.getTargetedNode = getTargetedNode
26997
+ exports.getVisibleNodesInViewport = getVisibleNodesInViewport
26398
26998
  exports.hasFrames = hasFrames
26399
26999
  exports.hasImages = hasImages
26400
27000
  exports.intersectArrays = intersectArrays