@xyflow/system 0.0.49 → 0.0.51

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.
Files changed (76) hide show
  1. package/dist/esm/index.js +339 -133
  2. package/dist/esm/index.mjs +339 -133
  3. package/dist/esm/types/changes.d.ts +12 -1
  4. package/dist/esm/types/changes.d.ts.map +1 -1
  5. package/dist/esm/types/edges.d.ts +37 -4
  6. package/dist/esm/types/edges.d.ts.map +1 -1
  7. package/dist/esm/types/general.d.ts +63 -0
  8. package/dist/esm/types/general.d.ts.map +1 -1
  9. package/dist/esm/types/handles.d.ts +9 -5
  10. package/dist/esm/types/handles.d.ts.map +1 -1
  11. package/dist/esm/types/nodes.d.ts +29 -9
  12. package/dist/esm/types/nodes.d.ts.map +1 -1
  13. package/dist/esm/types/utils.d.ts +22 -0
  14. package/dist/esm/types/utils.d.ts.map +1 -1
  15. package/dist/esm/utils/dom.d.ts.map +1 -1
  16. package/dist/esm/utils/edges/bezier-edge.d.ts +18 -11
  17. package/dist/esm/utils/edges/bezier-edge.d.ts.map +1 -1
  18. package/dist/esm/utils/edges/general.d.ts +16 -3
  19. package/dist/esm/utils/edges/general.d.ts.map +1 -1
  20. package/dist/esm/utils/edges/smoothstep-edge.d.ts +17 -11
  21. package/dist/esm/utils/edges/smoothstep-edge.d.ts.map +1 -1
  22. package/dist/esm/utils/edges/straight-edge.d.ts +15 -11
  23. package/dist/esm/utils/edges/straight-edge.d.ts.map +1 -1
  24. package/dist/esm/utils/general.d.ts +2 -2
  25. package/dist/esm/utils/graph.d.ts +82 -4
  26. package/dist/esm/utils/graph.d.ts.map +1 -1
  27. package/dist/esm/utils/node-toolbar.d.ts.map +1 -1
  28. package/dist/esm/utils/store.d.ts.map +1 -1
  29. package/dist/esm/xydrag/XYDrag.d.ts.map +1 -1
  30. package/dist/esm/xydrag/utils.d.ts.map +1 -1
  31. package/dist/esm/xyhandle/XYHandle.d.ts.map +1 -1
  32. package/dist/esm/xyhandle/types.d.ts.map +1 -1
  33. package/dist/esm/xyhandle/utils.d.ts.map +1 -1
  34. package/dist/esm/xypanzoom/XYPanZoom.d.ts.map +1 -1
  35. package/dist/esm/xypanzoom/eventhandler.d.ts.map +1 -1
  36. package/dist/esm/xyresizer/XYResizer.d.ts.map +1 -1
  37. package/dist/esm/xyresizer/types.d.ts +15 -0
  38. package/dist/esm/xyresizer/types.d.ts.map +1 -1
  39. package/dist/umd/index.js +1 -1
  40. package/dist/umd/types/changes.d.ts +12 -1
  41. package/dist/umd/types/changes.d.ts.map +1 -1
  42. package/dist/umd/types/edges.d.ts +37 -4
  43. package/dist/umd/types/edges.d.ts.map +1 -1
  44. package/dist/umd/types/general.d.ts +63 -0
  45. package/dist/umd/types/general.d.ts.map +1 -1
  46. package/dist/umd/types/handles.d.ts +9 -5
  47. package/dist/umd/types/handles.d.ts.map +1 -1
  48. package/dist/umd/types/nodes.d.ts +29 -9
  49. package/dist/umd/types/nodes.d.ts.map +1 -1
  50. package/dist/umd/types/utils.d.ts +22 -0
  51. package/dist/umd/types/utils.d.ts.map +1 -1
  52. package/dist/umd/utils/dom.d.ts.map +1 -1
  53. package/dist/umd/utils/edges/bezier-edge.d.ts +18 -11
  54. package/dist/umd/utils/edges/bezier-edge.d.ts.map +1 -1
  55. package/dist/umd/utils/edges/general.d.ts +16 -3
  56. package/dist/umd/utils/edges/general.d.ts.map +1 -1
  57. package/dist/umd/utils/edges/smoothstep-edge.d.ts +17 -11
  58. package/dist/umd/utils/edges/smoothstep-edge.d.ts.map +1 -1
  59. package/dist/umd/utils/edges/straight-edge.d.ts +15 -11
  60. package/dist/umd/utils/edges/straight-edge.d.ts.map +1 -1
  61. package/dist/umd/utils/general.d.ts +2 -2
  62. package/dist/umd/utils/graph.d.ts +82 -4
  63. package/dist/umd/utils/graph.d.ts.map +1 -1
  64. package/dist/umd/utils/node-toolbar.d.ts.map +1 -1
  65. package/dist/umd/utils/store.d.ts.map +1 -1
  66. package/dist/umd/xydrag/XYDrag.d.ts.map +1 -1
  67. package/dist/umd/xydrag/utils.d.ts.map +1 -1
  68. package/dist/umd/xyhandle/XYHandle.d.ts.map +1 -1
  69. package/dist/umd/xyhandle/types.d.ts.map +1 -1
  70. package/dist/umd/xyhandle/utils.d.ts.map +1 -1
  71. package/dist/umd/xypanzoom/XYPanZoom.d.ts.map +1 -1
  72. package/dist/umd/xypanzoom/eventhandler.d.ts.map +1 -1
  73. package/dist/umd/xyresizer/XYResizer.d.ts.map +1 -1
  74. package/dist/umd/xyresizer/types.d.ts +15 -0
  75. package/dist/umd/xyresizer/types.d.ts.map +1 -1
  76. package/package.json +4 -4
@@ -24,11 +24,26 @@ const infiniteExtent = [
24
24
  ];
25
25
  const elementSelectionKeys = ['Enter', ' ', 'Escape'];
26
26
 
27
+ /**
28
+ * The `ConnectionMode` is used to set the mode of connection between nodes.
29
+ * The `Strict` mode is the default one and only allows source to target edges.
30
+ * `Loose` mode allows source to source and target to target edges as well.
31
+ *
32
+ * @public
33
+ */
27
34
  var ConnectionMode;
28
35
  (function (ConnectionMode) {
29
36
  ConnectionMode["Strict"] = "strict";
30
37
  ConnectionMode["Loose"] = "loose";
31
38
  })(ConnectionMode || (ConnectionMode = {}));
39
+ /**
40
+ * This enum is used to set the different modes of panning the viewport when the
41
+ * user scrolls. The `Free` mode allows the user to pan in any direction by scrolling
42
+ * with a device like a trackpad. The `Vertical` and `Horizontal` modes restrict
43
+ * scroll panning to only the vertical or horizontal axis, respectively.
44
+ *
45
+ * @public
46
+ */
32
47
  var PanOnScrollMode;
33
48
  (function (PanOnScrollMode) {
34
49
  PanOnScrollMode["Free"] = "free";
@@ -53,6 +68,16 @@ const initialConnection = {
53
68
  toNode: null,
54
69
  };
55
70
 
71
+ /**
72
+ * If you set the `connectionLineType` prop on your [`<ReactFlow />`](/api-reference/react-flow#connection-connectionLineType)
73
+ *component, it will dictate the style of connection line rendered when creating
74
+ *new edges.
75
+ *
76
+ * @public
77
+ *
78
+ * @remarks If you choose to render a custom connection line component, this value will be
79
+ *passed to your component as part of its [`ConnectionLineComponentProps`](/api-reference/types/connection-line-component-props).
80
+ */
56
81
  var ConnectionLineType;
57
82
  (function (ConnectionLineType) {
58
83
  ConnectionLineType["Bezier"] = "default";
@@ -61,12 +86,25 @@ var ConnectionLineType;
61
86
  ConnectionLineType["SmoothStep"] = "smoothstep";
62
87
  ConnectionLineType["SimpleBezier"] = "simplebezier";
63
88
  })(ConnectionLineType || (ConnectionLineType = {}));
89
+ /**
90
+ * Edges may optionally have a marker on either end. The MarkerType type enumerates
91
+ * the options available to you when configuring a given marker.
92
+ *
93
+ * @public
94
+ */
64
95
  var MarkerType;
65
96
  (function (MarkerType) {
66
97
  MarkerType["Arrow"] = "arrow";
67
98
  MarkerType["ArrowClosed"] = "arrowclosed";
68
99
  })(MarkerType || (MarkerType = {}));
69
100
 
101
+ /**
102
+ * While [`PanelPosition`](/api-reference/types/panel-position) can be used to place a
103
+ * component in the corners of a container, the `Position` enum is less precise and used
104
+ * primarily in relation to edges and handles.
105
+ *
106
+ * @public
107
+ */
70
108
  var Position;
71
109
  (function (Position) {
72
110
  Position["Left"] = "left";
@@ -143,12 +181,27 @@ const isEdgeBase = (element) => 'id' in element && 'source' in element && 'targe
143
181
  const isNodeBase = (element) => 'id' in element && 'position' in element && !('source' in element) && !('target' in element);
144
182
  const isInternalNodeBase = (element) => 'id' in element && 'internals' in element && !('source' in element) && !('target' in element);
145
183
  /**
146
- * Pass in a node, and get connected nodes where edge.source === node.id
184
+ * This util is used to tell you what nodes, if any, are connected to the given node
185
+ * as the _target_ of an edge.
147
186
  * @public
148
187
  * @param node - The node to get the connected nodes from
149
188
  * @param nodes - The array of all nodes
150
189
  * @param edges - The array of all edges
151
190
  * @returns An array of nodes that are connected over eges where the source is the given node
191
+ *
192
+ * @example
193
+ * ```ts
194
+ *import { getOutgoers } from '@xyflow/react';
195
+ *
196
+ *const nodes = [];
197
+ *const edges = [];
198
+ *
199
+ *const outgoers = getOutgoers(
200
+ * { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
201
+ * nodes,
202
+ * edges,
203
+ *);
204
+ *```
152
205
  */
153
206
  const getOutgoers = (node, nodes, edges) => {
154
207
  if (!node.id) {
@@ -163,12 +216,27 @@ const getOutgoers = (node, nodes, edges) => {
163
216
  return nodes.filter((n) => outgoerIds.has(n.id));
164
217
  };
165
218
  /**
166
- * Pass in a node, and get connected nodes where edge.target === node.id
219
+ * This util is used to tell you what nodes, if any, are connected to the given node
220
+ * as the _source_ of an edge.
167
221
  * @public
168
222
  * @param node - The node to get the connected nodes from
169
223
  * @param nodes - The array of all nodes
170
224
  * @param edges - The array of all edges
171
225
  * @returns An array of nodes that are connected over eges where the target is the given node
226
+ *
227
+ * @example
228
+ * ```ts
229
+ *import { getIncomers } from '@xyflow/react';
230
+ *
231
+ *const nodes = [];
232
+ *const edges = [];
233
+ *
234
+ *const incomers = getIncomers(
235
+ * { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
236
+ * nodes,
237
+ * edges,
238
+ *);
239
+ *```
172
240
  */
173
241
  const getIncomers = (node, nodes, edges) => {
174
242
  if (!node.id) {
@@ -193,12 +261,40 @@ const getNodePositionWithOrigin = (node, nodeOrigin = [0, 0]) => {
193
261
  };
194
262
  };
195
263
  /**
196
- * Internal function for determining a bounding box that contains all given nodes in an array.
264
+ * Returns the bounding box that contains all the given nodes in an array. This can
265
+ * be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds)
266
+ * to calculate the correct transform to fit the given nodes in a viewport.
197
267
  * @public
198
268
  * @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport.
199
269
  * @param nodes - Nodes to calculate the bounds for
200
270
  * @param params.nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center
201
271
  * @returns Bounding box enclosing all nodes
272
+ *
273
+ * @remarks This function was previously called `getRectOfNodes`
274
+ *
275
+ * @example
276
+ * ```js
277
+ *import { getNodesBounds } from '@xyflow/react';
278
+ *
279
+ *const nodes = [
280
+ * {
281
+ * id: 'a',
282
+ * position: { x: 0, y: 0 },
283
+ * data: { label: 'a' },
284
+ * width: 50,
285
+ * height: 25,
286
+ * },
287
+ * {
288
+ * id: 'b',
289
+ * position: { x: 100, y: 100 },
290
+ * data: { label: 'b' },
291
+ * width: 50,
292
+ * height: 25,
293
+ * },
294
+ *];
295
+ *
296
+ *const bounds = getNodesBounds(nodes);
297
+ *```
202
298
  */
203
299
  const getNodesBounds = (nodes, params = { nodeOrigin: [0, 0], nodeLookup: undefined }) => {
204
300
  if (process.env.NODE_ENV === 'development' && !params.nodeLookup) {
@@ -267,10 +363,30 @@ excludeNonSelectableNodes = false) => {
267
363
  return visibleNodes;
268
364
  };
269
365
  /**
270
- * Get all connecting edges for a given set of nodes
366
+ * This utility filters an array of edges, keeping only those where either the source or target
367
+ * node is present in the given array of nodes.
368
+ * @public
271
369
  * @param nodes - Nodes you want to get the connected edges for
272
370
  * @param edges - All edges
273
371
  * @returns Array of edges that connect any of the given nodes with each other
372
+ *
373
+ * @example
374
+ * ```js
375
+ *import { getConnectedEdges } from '@xyflow/react';
376
+ *
377
+ *const nodes = [
378
+ * { id: 'a', position: { x: 0, y: 0 } },
379
+ * { id: 'b', position: { x: 100, y: 0 } },
380
+ *];
381
+ *
382
+ *const edges = [
383
+ * { id: 'a->c', source: 'a', target: 'c' },
384
+ * { id: 'c->d', source: 'c', target: 'd' },
385
+ *];
386
+ *
387
+ *const connectedEdges = getConnectedEdges(nodes, edges);
388
+ * // => [{ id: 'a->c', source: 'a', target: 'c' }]
389
+ *```
274
390
  */
275
391
  const getConnectedEdges = (nodes, edges) => {
276
392
  const nodeIds = new Set();
@@ -515,8 +631,8 @@ const rendererPointToPoint = ({ x, y }, [tx, ty, tScale]) => {
515
631
  * @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport}
516
632
  * @example
517
633
  * const { x, y, zoom } = getViewportForBounds(
518
- { x: 0, y: 0, width: 100, height: 100},
519
- 1200, 800, 0.5, 2);
634
+ *{ x: 0, y: 0, width: 100, height: 100},
635
+ *1200, 800, 0.5, 2);
520
636
  */
521
637
  const getViewportForBounds = (bounds, width, height, minZoom, maxZoom, padding) => {
522
638
  const xZoom = width / (bounds.width * (1 + padding));
@@ -611,9 +727,11 @@ const getEventPosition = (event, bounds) => {
611
727
  y: evtY - (bounds?.top ?? 0),
612
728
  };
613
729
  };
614
- // The handle bounds are calculated relative to the node element.
615
- // We store them in the internals object of the node in order to avoid
616
- // unnecessary recalculations.
730
+ /*
731
+ * The handle bounds are calculated relative to the node element.
732
+ * We store them in the internals object of the node in order to avoid
733
+ * unnecessary recalculations.
734
+ */
617
735
  const getHandleBounds = (type, nodeElement, nodeBounds, zoom, nodeId) => {
618
736
  const handles = nodeElement.querySelectorAll(`.${type}`);
619
737
  if (!handles || !handles.length) {
@@ -634,8 +752,10 @@ const getHandleBounds = (type, nodeElement, nodeBounds, zoom, nodeId) => {
634
752
  };
635
753
 
636
754
  function getBezierEdgeCenter({ sourceX, sourceY, targetX, targetY, sourceControlX, sourceControlY, targetControlX, targetControlY, }) {
637
- // cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
638
- // https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
755
+ /*
756
+ * cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
757
+ * https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
758
+ */
639
759
  const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125;
640
760
  const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125;
641
761
  const offsetX = Math.abs(centerX - sourceX);
@@ -661,7 +781,9 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }) {
661
781
  }
662
782
  }
663
783
  /**
664
- * Get a bezier path from source to target handle
784
+ * The `getBezierPath` util returns everything you need to render a bezier edge
785
+ *between two nodes.
786
+ * @public
665
787
  * @param params.sourceX - The x position of the source handle
666
788
  * @param params.sourceY - The y position of the source handle
667
789
  * @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
@@ -671,17 +793,22 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }) {
671
793
  * @param params.curvature - The curvature of the bezier edge
672
794
  * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label
673
795
  * @example
796
+ * ```js
674
797
  * const source = { x: 0, y: 20 };
675
- const target = { x: 150, y: 100 };
676
-
677
- const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
678
- sourceX: source.x,
679
- sourceY: source.y,
680
- sourcePosition: Position.Right,
681
- targetX: target.x,
682
- targetY: target.y,
683
- targetPosition: Position.Left,
684
- });
798
+ * const target = { x: 150, y: 100 };
799
+ *
800
+ * const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
801
+ * sourceX: source.x,
802
+ * sourceY: source.y,
803
+ * sourcePosition: Position.Right,
804
+ * targetX: target.x,
805
+ * targetY: target.y,
806
+ * targetPosition: Position.Left,
807
+ *});
808
+ *```
809
+ *
810
+ * @remarks This function returns a tuple (aka a fixed-size array) to make it easier to
811
+ *work with multiple edge paths at once.
685
812
  */
686
813
  function getBezierPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, curvature = 0.25, }) {
687
814
  const [sourceControlX, sourceControlY] = getControlWithCurvature({
@@ -759,12 +886,16 @@ const connectionExists = (edge, edges) => {
759
886
  (el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle)));
760
887
  };
761
888
  /**
762
- * This util is a convenience function to add a new Edge to an array of edges
763
- * @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
889
+ * This util is a convenience function to add a new Edge to an array of edges. It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
764
890
  * @public
765
891
  * @param edgeParams - Either an Edge or a Connection you want to add
766
892
  * @param edges - The array of all current edges
767
893
  * @returns A new array of edges with the new edge added
894
+ *
895
+ * @remarks If an edge with the same `target` and `source` already exists (and the same
896
+ *`targetHandle` and `sourceHandle` if those are set), then this util won't add
897
+ *a new edge even if the `id` property is different.
898
+ *
768
899
  */
769
900
  const addEdge = (edgeParams, edges) => {
770
901
  if (!edgeParams.source || !edgeParams.target) {
@@ -793,12 +924,21 @@ const addEdge = (edgeParams, edges) => {
793
924
  return edges.concat(edge);
794
925
  };
795
926
  /**
796
- * A handy utility to reconnect an existing edge with new properties
927
+ * A handy utility to update an existing [`Edge`](/api-reference/types/edge) with new properties.
928
+ *This searches your edge array for an edge with a matching `id` and updates its
929
+ *properties with the connection you provide.
930
+ * @public
797
931
  * @param oldEdge - The edge you want to update
798
932
  * @param newConnection - The new connection you want to update the edge with
799
933
  * @param edges - The array of all current edges
800
934
  * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id
801
935
  * @returns the updated edges array
936
+ *
937
+ * @example
938
+ * ```js
939
+ *const onReconnect = useCallback(
940
+ * (oldEdge: Edge, newConnection: Connection) => setEdges((els) => reconnectEdge(oldEdge, newConnection, els)),[]);
941
+ *```
802
942
  */
803
943
  const reconnectEdge = (oldEdge, newConnection, edges, options = { shouldReplaceId: true }) => {
804
944
  const { id: oldEdgeId, ...rest } = oldEdge;
@@ -824,24 +964,28 @@ const reconnectEdge = (oldEdge, newConnection, edges, options = { shouldReplaceI
824
964
  };
825
965
 
826
966
  /**
827
- * Get a straight path from source to target handle
967
+ * Calculates the straight line path between two points.
968
+ * @public
828
969
  * @param params.sourceX - The x position of the source handle
829
970
  * @param params.sourceY - The y position of the source handle
830
971
  * @param params.targetX - The x position of the target handle
831
972
  * @param params.targetY - The y position of the target handle
832
973
  * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label
833
974
  * @example
975
+ * ```js
834
976
  * const source = { x: 0, y: 20 };
835
- const target = { x: 150, y: 100 };
836
-
837
- const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
838
- sourceX: source.x,
839
- sourceY: source.y,
840
- sourcePosition: Position.Right,
841
- targetX: target.x,
842
- targetY: target.y,
843
- targetPosition: Position.Left,
844
- });
977
+ * const target = { x: 150, y: 100 };
978
+ *
979
+ * const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
980
+ * sourceX: source.x,
981
+ * sourceY: source.y,
982
+ * sourcePosition: Position.Right,
983
+ * targetX: target.x,
984
+ * targetY: target.y,
985
+ * targetPosition: Position.Left,
986
+ * });
987
+ * ```
988
+ * @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
845
989
  */
846
990
  function getStraightPath({ sourceX, sourceY, targetX, targetY, }) {
847
991
  const [labelX, labelY, offsetX, offsetY] = getEdgeCenter({
@@ -866,8 +1010,10 @@ const getDirection = ({ source, sourcePosition = Position.Bottom, target, }) =>
866
1010
  return source.y < target.y ? { x: 0, y: 1 } : { x: 0, y: -1 };
867
1011
  };
868
1012
  const distance = (a, b) => Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
869
- // ith this function we try to mimic a orthogonal edge routing behaviour
870
- // It's not as good as a real orthogonal edge routing but it's faster and good enough as a default for step and smooth step edges
1013
+ /*
1014
+ * ith this function we try to mimic a orthogonal edge routing behaviour
1015
+ * It's not as good as a real orthogonal edge routing but it's faster and good enough as a default for step and smooth step edges
1016
+ */
871
1017
  function getPoints({ source, sourcePosition = Position.Bottom, target, targetPosition = Position.Top, center, offset, }) {
872
1018
  const sourceDir = handleDirections[sourcePosition];
873
1019
  const targetDir = handleDirections[targetPosition];
@@ -894,16 +1040,20 @@ function getPoints({ source, sourcePosition = Position.Bottom, target, targetPos
894
1040
  if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
895
1041
  centerX = center.x ?? defaultCenterX;
896
1042
  centerY = center.y ?? defaultCenterY;
897
- // --->
898
- // |
899
- // >---
1043
+ /*
1044
+ * --->
1045
+ * |
1046
+ * >---
1047
+ */
900
1048
  const verticalSplit = [
901
1049
  { x: centerX, y: sourceGapped.y },
902
1050
  { x: centerX, y: targetGapped.y },
903
1051
  ];
904
- // |
905
- // ---
906
- // |
1052
+ /*
1053
+ * |
1054
+ * ---
1055
+ * |
1056
+ */
907
1057
  const horizontalSplit = [
908
1058
  { x: sourceGapped.x, y: centerY },
909
1059
  { x: targetGapped.x, y: centerY },
@@ -992,7 +1142,10 @@ function getBend(a, b, c, size) {
992
1142
  return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
993
1143
  }
994
1144
  /**
995
- * Get a smooth step path from source to target handle
1145
+ * The `getSmoothStepPath` util returns everything you need to render a stepped path
1146
+ *between two nodes. The `borderRadius` property can be used to choose how rounded
1147
+ *the corners of those steps are.
1148
+ * @public
996
1149
  * @param params.sourceX - The x position of the source handle
997
1150
  * @param params.sourceY - The y position of the source handle
998
1151
  * @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
@@ -1001,17 +1154,20 @@ function getBend(a, b, c, size) {
1001
1154
  * @param params.targetPosition - The position of the target handle (default: Position.Top)
1002
1155
  * @returns A path string you can use in an SVG, the labelX and labelY position (center of path) and offsetX, offsetY between source handle and label
1003
1156
  * @example
1157
+ * ```js
1004
1158
  * const source = { x: 0, y: 20 };
1005
- const target = { x: 150, y: 100 };
1006
-
1007
- const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
1008
- sourceX: source.x,
1009
- sourceY: source.y,
1010
- sourcePosition: Position.Right,
1011
- targetX: target.x,
1012
- targetY: target.y,
1013
- targetPosition: Position.Left,
1014
- });
1159
+ * const target = { x: 150, y: 100 };
1160
+ *
1161
+ * const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
1162
+ * sourceX: source.x,
1163
+ * sourceY: source.y,
1164
+ * sourcePosition: Position.Right,
1165
+ * targetX: target.x,
1166
+ * targetY: target.y,
1167
+ * targetPosition: Position.Left,
1168
+ * });
1169
+ * ```
1170
+ * @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
1015
1171
  */
1016
1172
  function getSmoothStepPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, borderRadius = 5, centerX, centerY, offset = 20, }) {
1017
1173
  const [points, labelX, labelY, offsetX, offsetY] = getPoints({
@@ -1162,8 +1318,10 @@ function getNodeToolbarTransform(nodeRect, viewport, position, offset, align) {
1162
1318
  else if (align === 'end') {
1163
1319
  alignmentOffset = 1;
1164
1320
  }
1165
- // position === Position.Top
1166
- // we set the x any y position of the toolbar based on the nodes position
1321
+ /*
1322
+ * position === Position.Top
1323
+ * we set the x any y position of the toolbar based on the nodes position
1324
+ */
1167
1325
  let pos = [
1168
1326
  (nodeRect.x + nodeRect.width * alignmentOffset) * viewport.zoom + viewport.x,
1169
1327
  nodeRect.y * viewport.zoom + viewport.y - offset,
@@ -1293,11 +1451,15 @@ function updateChildNode(node, nodeLookup, parentLookup, options) {
1293
1451
  const { positionAbsolute } = node.internals;
1294
1452
  const positionChanged = x !== positionAbsolute.x || y !== positionAbsolute.y;
1295
1453
  if (positionChanged || z !== node.internals.z) {
1296
- node.internals = {
1297
- ...node.internals,
1298
- positionAbsolute: positionChanged ? { x, y } : positionAbsolute,
1299
- z,
1300
- };
1454
+ // we create a new object to mark the node as updated
1455
+ nodeLookup.set(node.id, {
1456
+ ...node,
1457
+ internals: {
1458
+ ...node.internals,
1459
+ positionAbsolute: positionChanged ? { x, y } : positionAbsolute,
1460
+ z,
1461
+ },
1462
+ });
1301
1463
  }
1302
1464
  }
1303
1465
  function calculateZ(node, selectedNodeZ) {
@@ -1358,8 +1520,10 @@ function handleExpandParent(children, nodeLookup, parentLookup, nodeOrigin = [0,
1358
1520
  y: parent.position.y - yChange + heightChange,
1359
1521
  },
1360
1522
  });
1361
- // We move all child nodes in the oppsite direction
1362
- // so the x,y changes of the parent do not move the children
1523
+ /*
1524
+ * We move all child nodes in the oppsite direction
1525
+ * so the x,y changes of the parent do not move the children
1526
+ */
1363
1527
  parentLookup.get(parentId)?.forEach((childNode) => {
1364
1528
  if (!children.some((child) => child.id === childNode.id)) {
1365
1529
  changes.push({
@@ -1406,54 +1570,60 @@ function updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOri
1406
1570
  continue;
1407
1571
  }
1408
1572
  if (node.hidden) {
1409
- node.internals = {
1410
- ...node.internals,
1411
- handleBounds: undefined,
1412
- };
1573
+ nodeLookup.set(node.id, {
1574
+ ...node,
1575
+ internals: {
1576
+ ...node.internals,
1577
+ handleBounds: undefined,
1578
+ },
1579
+ });
1413
1580
  updatedInternals = true;
1581
+ continue;
1414
1582
  }
1415
- else {
1416
- const dimensions = getDimensions(update.nodeElement);
1417
- const dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height;
1418
- const doUpdate = !!(dimensions.width &&
1419
- dimensions.height &&
1420
- (dimensionChanged || !node.internals.handleBounds || update.force));
1421
- if (doUpdate) {
1422
- const nodeBounds = update.nodeElement.getBoundingClientRect();
1423
- const extent = isCoordinateExtent(node.extent) ? node.extent : nodeExtent;
1424
- let { positionAbsolute } = node.internals;
1425
- if (node.parentId && node.extent === 'parent') {
1426
- positionAbsolute = clampPositionToParent(positionAbsolute, dimensions, nodeLookup.get(node.parentId));
1427
- }
1428
- else if (extent) {
1429
- positionAbsolute = clampPosition(positionAbsolute, extent, dimensions);
1430
- }
1431
- node.measured = dimensions;
1432
- node.internals = {
1583
+ const dimensions = getDimensions(update.nodeElement);
1584
+ const dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height;
1585
+ const doUpdate = !!(dimensions.width &&
1586
+ dimensions.height &&
1587
+ (dimensionChanged || !node.internals.handleBounds || update.force));
1588
+ if (doUpdate) {
1589
+ const nodeBounds = update.nodeElement.getBoundingClientRect();
1590
+ const extent = isCoordinateExtent(node.extent) ? node.extent : nodeExtent;
1591
+ let { positionAbsolute } = node.internals;
1592
+ if (node.parentId && node.extent === 'parent') {
1593
+ positionAbsolute = clampPositionToParent(positionAbsolute, dimensions, nodeLookup.get(node.parentId));
1594
+ }
1595
+ else if (extent) {
1596
+ positionAbsolute = clampPosition(positionAbsolute, extent, dimensions);
1597
+ }
1598
+ const newNode = {
1599
+ ...node,
1600
+ measured: dimensions,
1601
+ internals: {
1433
1602
  ...node.internals,
1434
1603
  positionAbsolute,
1435
1604
  handleBounds: {
1436
1605
  source: getHandleBounds('source', update.nodeElement, nodeBounds, zoom, node.id),
1437
1606
  target: getHandleBounds('target', update.nodeElement, nodeBounds, zoom, node.id),
1438
1607
  },
1439
- };
1440
- if (node.parentId) {
1441
- updateChildNode(node, nodeLookup, parentLookup, { nodeOrigin });
1442
- }
1443
- updatedInternals = true;
1444
- if (dimensionChanged) {
1445
- changes.push({
1608
+ },
1609
+ };
1610
+ nodeLookup.set(node.id, newNode);
1611
+ if (node.parentId) {
1612
+ updateChildNode(newNode, nodeLookup, parentLookup, { nodeOrigin });
1613
+ }
1614
+ updatedInternals = true;
1615
+ if (dimensionChanged) {
1616
+ changes.push({
1617
+ id: node.id,
1618
+ type: 'dimensions',
1619
+ dimensions,
1620
+ });
1621
+ if (node.expandParent && node.parentId) {
1622
+ parentExpandChildren.push({
1446
1623
  id: node.id,
1447
- type: 'dimensions',
1448
- dimensions,
1624
+ parentId: node.parentId,
1625
+ rect: nodeToRect(newNode, nodeOrigin),
1449
1626
  });
1450
- if (node.expandParent && node.parentId) {
1451
- parentExpandChildren.push({
1452
- id: node.id,
1453
- parentId: node.parentId,
1454
- rect: nodeToRect(node, nodeOrigin),
1455
- });
1456
- }
1457
1627
  }
1458
1628
  }
1459
1629
  }
@@ -1491,9 +1661,11 @@ async function panBy({ delta, panZoom, transform, translateExtent, width, height
1491
1661
  * @param handleId handleId of the conneciton
1492
1662
  */
1493
1663
  function addConnectionToLookup(type, connection, connectionKey, connectionLookup, nodeId, handleId) {
1494
- // We add the connection to the connectionLookup at the following keys
1495
- // 1. nodeId, 2. nodeId-type, 3. nodeId-type-handleId
1496
- // If the key already exists, we add the connection to the existing map
1664
+ /*
1665
+ * We add the connection to the connectionLookup at the following keys
1666
+ * 1. nodeId, 2. nodeId-type, 3. nodeId-type-handleId
1667
+ * If the key already exists, we add the connection to the existing map
1668
+ */
1497
1669
  let key = nodeId;
1498
1670
  const nodeMap = connectionLookup.get(key) || new Map();
1499
1671
  connectionLookup.set(key, nodeMap.set(connectionKey, connection));
@@ -1594,9 +1766,11 @@ function getDragItems(nodeLookup, nodesDraggable, mousePos, nodeId) {
1594
1766
  }
1595
1767
  return dragItems;
1596
1768
  }
1597
- // returns two params:
1598
- // 1. the dragged node (or the first of the list, if we are dragging a node selection)
1599
- // 2. array of selected nodes (for multi selections)
1769
+ /*
1770
+ * returns two params:
1771
+ * 1. the dragged node (or the first of the list, if we are dragging a node selection)
1772
+ * 2. array of selected nodes (for multi selections)
1773
+ */
1600
1774
  function getEventHandlerParams({ nodeId, dragItems, nodeLookup, dragging = true, }) {
1601
1775
  const nodesFromDragItems = [];
1602
1776
  for (const [id, dragItem] of dragItems) {
@@ -1650,16 +1824,20 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
1650
1824
  }
1651
1825
  for (const [id, dragItem] of dragItems) {
1652
1826
  if (!nodeLookup.has(id)) {
1653
- // if the node is not in the nodeLookup anymore, it was probably deleted while dragging
1654
- // and we don't need to update it anymore
1827
+ /*
1828
+ * if the node is not in the nodeLookup anymore, it was probably deleted while dragging
1829
+ * and we don't need to update it anymore
1830
+ */
1655
1831
  continue;
1656
1832
  }
1657
1833
  let nextPosition = { x: x - dragItem.distance.x, y: y - dragItem.distance.y };
1658
1834
  if (snapToGrid) {
1659
1835
  nextPosition = snapPosition(nextPosition, snapGrid);
1660
1836
  }
1661
- // if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node
1662
- // based on its position so that the node stays at it's position relative to the selection.
1837
+ /*
1838
+ * if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node
1839
+ * based on its position so that the node stays at it's position relative to the selection.
1840
+ */
1663
1841
  let adjustedNodeExtent = [
1664
1842
  [nodeExtent[0][0], nodeExtent[0][1]],
1665
1843
  [nodeExtent[1][0], nodeExtent[1][1]],
@@ -1709,7 +1887,12 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
1709
1887
  if (!containerBounds) {
1710
1888
  return;
1711
1889
  }
1712
- const { transform, panBy, autoPanSpeed } = getStoreItems();
1890
+ const { transform, panBy, autoPanSpeed, autoPanOnNodeDrag } = getStoreItems();
1891
+ if (!autoPanOnNodeDrag) {
1892
+ autoPanStarted = false;
1893
+ cancelAnimationFrame(autoPanId);
1894
+ return;
1895
+ }
1713
1896
  const [xMovement, yMovement] = calcAutoPan(mousePosition, containerBounds, autoPanSpeed);
1714
1897
  if (xMovement !== 0 || yMovement !== 0) {
1715
1898
  lastPos.x = (lastPos.x ?? 0) - xMovement / transform[2];
@@ -1849,8 +2032,10 @@ function getNodesWithinDistance(position, nodeLookup, distance) {
1849
2032
  }
1850
2033
  return nodes;
1851
2034
  }
1852
- // this distance is used for the area around the user pointer
1853
- // while doing a connection for finding the closest nodes
2035
+ /*
2036
+ * this distance is used for the area around the user pointer
2037
+ * while doing a connection for finding the closest nodes
2038
+ */
1854
2039
  const ADDITIONAL_DISTANCE = 250;
1855
2040
  function getClosestHandle(position, connectionRadius, nodeLookup, fromHandle) {
1856
2041
  let closestHandles = [];
@@ -2018,8 +2203,10 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
2018
2203
  toPosition: isValid && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position],
2019
2204
  toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId) : null,
2020
2205
  };
2021
- // we don't want to trigger an update when the connection
2022
- // is snapped to the same handle as before
2206
+ /*
2207
+ * we don't want to trigger an update when the connection
2208
+ * is snapped to the same handle as before
2209
+ */
2023
2210
  if (isValid &&
2024
2211
  closestHandle &&
2025
2212
  previousConnection.toHandle &&
@@ -2038,8 +2225,10 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
2038
2225
  if ((closestHandle || handleDomNode) && connection && isValid) {
2039
2226
  onConnect?.(connection);
2040
2227
  }
2041
- // it's important to get a fresh reference from the store here
2042
- // in order to get the latest state of onConnectEnd
2228
+ /*
2229
+ * it's important to get a fresh reference from the store here
2230
+ * in order to get the latest state of onConnectEnd
2231
+ */
2043
2232
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
2044
2233
  const { inProgress, ...connectionState } = previousConnection;
2045
2234
  const finalConnectionState = {
@@ -2074,8 +2263,10 @@ function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId
2074
2263
  : null;
2075
2264
  const { x, y } = getEventPosition(event);
2076
2265
  const handleBelow = doc.elementFromPoint(x, y);
2077
- // we always want to prioritize the handle below the mouse cursor over the closest distance handle,
2078
- // because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
2266
+ /*
2267
+ * we always want to prioritize the handle below the mouse cursor over the closest distance handle,
2268
+ * because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
2269
+ */
2079
2270
  const handleToCheck = handleBelow?.classList.contains(`${lib}-flow__handle`) ? handleBelow : handleDomNode;
2080
2271
  const result = {
2081
2272
  handleDomNode: handleToCheck,
@@ -2226,8 +2417,10 @@ function createPanOnScrollHandler({ zoomPanValues, noWheelClassName, d3Selection
2226
2417
  d3Zoom.scaleTo(d3Selection, zoom, point, event);
2227
2418
  return;
2228
2419
  }
2229
- // increase scroll speed in firefox
2230
- // firefox: deltaMode === 1; chrome: deltaMode === 0
2420
+ /*
2421
+ * increase scroll speed in firefox
2422
+ * firefox: deltaMode === 1; chrome: deltaMode === 0
2423
+ */
2231
2424
  const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
2232
2425
  let deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
2233
2426
  let deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
@@ -2241,9 +2434,11 @@ function createPanOnScrollHandler({ zoomPanValues, noWheelClassName, d3Selection
2241
2434
  { internal: true });
2242
2435
  const nextViewport = transformToViewport(d3Selection.property('__zoom'));
2243
2436
  clearTimeout(zoomPanValues.panScrollTimeout);
2244
- // for pan on scroll we need to handle the event calls on our own
2245
- // we can't use the start, zoom and end events from d3-zoom
2246
- // because start and move gets called on every scroll event and not once at the beginning
2437
+ /*
2438
+ * for pan on scroll we need to handle the event calls on our own
2439
+ * we can't use the start, zoom and end events from d3-zoom
2440
+ * because start and move gets called on every scroll event and not once at the beginning
2441
+ */
2247
2442
  if (!zoomPanValues.isPanScrolling) {
2248
2443
  zoomPanValues.isPanScrolling = true;
2249
2444
  onPanZoomStart?.(event, nextViewport);
@@ -2478,9 +2673,11 @@ function XYPanZoom({ domNode, minZoom, maxZoom, paneClickDistance, translateExte
2478
2673
  lib,
2479
2674
  });
2480
2675
  d3ZoomInstance.filter(filter);
2481
- // We cannot add zoomOnDoubleClick to the filter above because
2482
- // double tapping on touch screens circumvents the filter and
2483
- // dblclick.zoom is fired on the selection directly
2676
+ /*
2677
+ * We cannot add zoomOnDoubleClick to the filter above because
2678
+ * double tapping on touch screens circumvents the filter and
2679
+ * dblclick.zoom is fired on the selection directly
2680
+ */
2484
2681
  if (zoomOnDoubleClick) {
2485
2682
  d3Selection.on('dblclick.zoom', d3DblClickZoomHandler);
2486
2683
  }
@@ -2562,6 +2759,11 @@ function XYPanZoom({ domNode, minZoom, maxZoom, paneClickDistance, translateExte
2562
2759
  };
2563
2760
  }
2564
2761
 
2762
+ /**
2763
+ * Used to determine the variant of the resize control
2764
+ *
2765
+ * @public
2766
+ */
2565
2767
  var ResizeControlVariant;
2566
2768
  (function (ResizeControlVariant) {
2567
2769
  ResizeControlVariant["Line"] = "line";
@@ -2857,8 +3059,10 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
2857
3059
  parentNode = nodeLookup.get(node.parentId);
2858
3060
  parentExtent = parentNode && node.extent === 'parent' ? nodeToParentExtent(parentNode) : undefined;
2859
3061
  }
2860
- // Collect all child nodes to correct their relative positions when top/left changes
2861
- // Determine largest minimal extent the parent node is allowed to resize to
3062
+ /*
3063
+ * Collect all child nodes to correct their relative positions when top/left changes
3064
+ * Determine largest minimal extent the parent node is allowed to resize to
3065
+ */
2862
3066
  childNodes = [];
2863
3067
  childExtent = undefined;
2864
3068
  for (const [childId, child] of nodeLookup) {
@@ -2912,8 +3116,10 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
2912
3116
  change.y = isYPosChange ? y : prevValues.y;
2913
3117
  prevValues.x = change.x;
2914
3118
  prevValues.y = change.y;
2915
- // when top/left changes, correct the relative positions of child nodes
2916
- // so that they stay in the same position
3119
+ /*
3120
+ * when top/left changes, correct the relative positions of child nodes
3121
+ * so that they stay in the same position
3122
+ */
2917
3123
  if (childNodes.length > 0) {
2918
3124
  const xChange = x - prevX;
2919
3125
  const yChange = y - prevY;