@xyflow/system 0.0.50 → 0.0.52
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/esm/constants.d.ts +1 -0
- package/dist/esm/constants.d.ts.map +1 -1
- package/dist/esm/index.js +292 -92
- package/dist/esm/index.mjs +292 -92
- package/dist/esm/types/changes.d.ts +12 -1
- package/dist/esm/types/changes.d.ts.map +1 -1
- package/dist/esm/types/edges.d.ts +37 -4
- package/dist/esm/types/edges.d.ts.map +1 -1
- package/dist/esm/types/general.d.ts +63 -0
- package/dist/esm/types/general.d.ts.map +1 -1
- package/dist/esm/types/handles.d.ts +8 -4
- package/dist/esm/types/handles.d.ts.map +1 -1
- package/dist/esm/types/nodes.d.ts +28 -8
- package/dist/esm/types/nodes.d.ts.map +1 -1
- package/dist/esm/types/utils.d.ts +22 -0
- package/dist/esm/types/utils.d.ts.map +1 -1
- package/dist/esm/utils/dom.d.ts.map +1 -1
- package/dist/esm/utils/edges/bezier-edge.d.ts +18 -11
- package/dist/esm/utils/edges/bezier-edge.d.ts.map +1 -1
- package/dist/esm/utils/edges/general.d.ts +16 -3
- package/dist/esm/utils/edges/general.d.ts.map +1 -1
- package/dist/esm/utils/edges/smoothstep-edge.d.ts +17 -11
- package/dist/esm/utils/edges/smoothstep-edge.d.ts.map +1 -1
- package/dist/esm/utils/edges/straight-edge.d.ts +15 -11
- package/dist/esm/utils/edges/straight-edge.d.ts.map +1 -1
- package/dist/esm/utils/general.d.ts +2 -2
- package/dist/esm/utils/graph.d.ts +82 -4
- package/dist/esm/utils/graph.d.ts.map +1 -1
- package/dist/esm/utils/node-toolbar.d.ts.map +1 -1
- package/dist/esm/utils/store.d.ts.map +1 -1
- package/dist/esm/xydrag/XYDrag.d.ts.map +1 -1
- package/dist/esm/xydrag/utils.d.ts.map +1 -1
- package/dist/esm/xyhandle/XYHandle.d.ts.map +1 -1
- package/dist/esm/xyhandle/types.d.ts.map +1 -1
- package/dist/esm/xyhandle/utils.d.ts.map +1 -1
- package/dist/esm/xypanzoom/XYPanZoom.d.ts.map +1 -1
- package/dist/esm/xypanzoom/eventhandler.d.ts.map +1 -1
- package/dist/esm/xyresizer/XYResizer.d.ts.map +1 -1
- package/dist/esm/xyresizer/types.d.ts +15 -0
- package/dist/esm/xyresizer/types.d.ts.map +1 -1
- package/dist/umd/constants.d.ts +1 -0
- package/dist/umd/constants.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/types/changes.d.ts +12 -1
- package/dist/umd/types/changes.d.ts.map +1 -1
- package/dist/umd/types/edges.d.ts +37 -4
- package/dist/umd/types/edges.d.ts.map +1 -1
- package/dist/umd/types/general.d.ts +63 -0
- package/dist/umd/types/general.d.ts.map +1 -1
- package/dist/umd/types/handles.d.ts +8 -4
- package/dist/umd/types/handles.d.ts.map +1 -1
- package/dist/umd/types/nodes.d.ts +28 -8
- package/dist/umd/types/nodes.d.ts.map +1 -1
- package/dist/umd/types/utils.d.ts +22 -0
- package/dist/umd/types/utils.d.ts.map +1 -1
- package/dist/umd/utils/dom.d.ts.map +1 -1
- package/dist/umd/utils/edges/bezier-edge.d.ts +18 -11
- package/dist/umd/utils/edges/bezier-edge.d.ts.map +1 -1
- package/dist/umd/utils/edges/general.d.ts +16 -3
- package/dist/umd/utils/edges/general.d.ts.map +1 -1
- package/dist/umd/utils/edges/smoothstep-edge.d.ts +17 -11
- package/dist/umd/utils/edges/smoothstep-edge.d.ts.map +1 -1
- package/dist/umd/utils/edges/straight-edge.d.ts +15 -11
- package/dist/umd/utils/edges/straight-edge.d.ts.map +1 -1
- package/dist/umd/utils/general.d.ts +2 -2
- package/dist/umd/utils/graph.d.ts +82 -4
- package/dist/umd/utils/graph.d.ts.map +1 -1
- package/dist/umd/utils/node-toolbar.d.ts.map +1 -1
- package/dist/umd/utils/store.d.ts.map +1 -1
- package/dist/umd/xydrag/XYDrag.d.ts.map +1 -1
- package/dist/umd/xydrag/utils.d.ts.map +1 -1
- package/dist/umd/xyhandle/XYHandle.d.ts.map +1 -1
- package/dist/umd/xyhandle/types.d.ts.map +1 -1
- package/dist/umd/xyhandle/utils.d.ts.map +1 -1
- package/dist/umd/xypanzoom/XYPanZoom.d.ts.map +1 -1
- package/dist/umd/xypanzoom/eventhandler.d.ts.map +1 -1
- package/dist/umd/xyresizer/XYResizer.d.ts.map +1 -1
- package/dist/umd/xyresizer/types.d.ts +15 -0
- package/dist/umd/xyresizer/types.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/esm/index.js
CHANGED
|
@@ -17,6 +17,7 @@ const errorMessages = {
|
|
|
17
17
|
error012: (id) => `Node with id "${id}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,
|
|
18
18
|
error013: (lib = 'react') => `It seems that you haven't loaded the styles. Please import '@xyflow/${lib}/dist/style.css' or base.css to make sure everything is working properly.`,
|
|
19
19
|
error014: () => 'useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.',
|
|
20
|
+
error015: () => 'It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs.',
|
|
20
21
|
};
|
|
21
22
|
const infiniteExtent = [
|
|
22
23
|
[Number.NEGATIVE_INFINITY, Number.NEGATIVE_INFINITY],
|
|
@@ -24,11 +25,26 @@ const infiniteExtent = [
|
|
|
24
25
|
];
|
|
25
26
|
const elementSelectionKeys = ['Enter', ' ', 'Escape'];
|
|
26
27
|
|
|
28
|
+
/**
|
|
29
|
+
* The `ConnectionMode` is used to set the mode of connection between nodes.
|
|
30
|
+
* The `Strict` mode is the default one and only allows source to target edges.
|
|
31
|
+
* `Loose` mode allows source to source and target to target edges as well.
|
|
32
|
+
*
|
|
33
|
+
* @public
|
|
34
|
+
*/
|
|
27
35
|
var ConnectionMode;
|
|
28
36
|
(function (ConnectionMode) {
|
|
29
37
|
ConnectionMode["Strict"] = "strict";
|
|
30
38
|
ConnectionMode["Loose"] = "loose";
|
|
31
39
|
})(ConnectionMode || (ConnectionMode = {}));
|
|
40
|
+
/**
|
|
41
|
+
* This enum is used to set the different modes of panning the viewport when the
|
|
42
|
+
* user scrolls. The `Free` mode allows the user to pan in any direction by scrolling
|
|
43
|
+
* with a device like a trackpad. The `Vertical` and `Horizontal` modes restrict
|
|
44
|
+
* scroll panning to only the vertical or horizontal axis, respectively.
|
|
45
|
+
*
|
|
46
|
+
* @public
|
|
47
|
+
*/
|
|
32
48
|
var PanOnScrollMode;
|
|
33
49
|
(function (PanOnScrollMode) {
|
|
34
50
|
PanOnScrollMode["Free"] = "free";
|
|
@@ -53,6 +69,16 @@ const initialConnection = {
|
|
|
53
69
|
toNode: null,
|
|
54
70
|
};
|
|
55
71
|
|
|
72
|
+
/**
|
|
73
|
+
* If you set the `connectionLineType` prop on your [`<ReactFlow />`](/api-reference/react-flow#connection-connectionLineType)
|
|
74
|
+
*component, it will dictate the style of connection line rendered when creating
|
|
75
|
+
*new edges.
|
|
76
|
+
*
|
|
77
|
+
* @public
|
|
78
|
+
*
|
|
79
|
+
* @remarks If you choose to render a custom connection line component, this value will be
|
|
80
|
+
*passed to your component as part of its [`ConnectionLineComponentProps`](/api-reference/types/connection-line-component-props).
|
|
81
|
+
*/
|
|
56
82
|
var ConnectionLineType;
|
|
57
83
|
(function (ConnectionLineType) {
|
|
58
84
|
ConnectionLineType["Bezier"] = "default";
|
|
@@ -61,12 +87,25 @@ var ConnectionLineType;
|
|
|
61
87
|
ConnectionLineType["SmoothStep"] = "smoothstep";
|
|
62
88
|
ConnectionLineType["SimpleBezier"] = "simplebezier";
|
|
63
89
|
})(ConnectionLineType || (ConnectionLineType = {}));
|
|
90
|
+
/**
|
|
91
|
+
* Edges may optionally have a marker on either end. The MarkerType type enumerates
|
|
92
|
+
* the options available to you when configuring a given marker.
|
|
93
|
+
*
|
|
94
|
+
* @public
|
|
95
|
+
*/
|
|
64
96
|
var MarkerType;
|
|
65
97
|
(function (MarkerType) {
|
|
66
98
|
MarkerType["Arrow"] = "arrow";
|
|
67
99
|
MarkerType["ArrowClosed"] = "arrowclosed";
|
|
68
100
|
})(MarkerType || (MarkerType = {}));
|
|
69
101
|
|
|
102
|
+
/**
|
|
103
|
+
* While [`PanelPosition`](/api-reference/types/panel-position) can be used to place a
|
|
104
|
+
* component in the corners of a container, the `Position` enum is less precise and used
|
|
105
|
+
* primarily in relation to edges and handles.
|
|
106
|
+
*
|
|
107
|
+
* @public
|
|
108
|
+
*/
|
|
70
109
|
var Position;
|
|
71
110
|
(function (Position) {
|
|
72
111
|
Position["Left"] = "left";
|
|
@@ -143,12 +182,27 @@ const isEdgeBase = (element) => 'id' in element && 'source' in element && 'targe
|
|
|
143
182
|
const isNodeBase = (element) => 'id' in element && 'position' in element && !('source' in element) && !('target' in element);
|
|
144
183
|
const isInternalNodeBase = (element) => 'id' in element && 'internals' in element && !('source' in element) && !('target' in element);
|
|
145
184
|
/**
|
|
146
|
-
*
|
|
185
|
+
* This util is used to tell you what nodes, if any, are connected to the given node
|
|
186
|
+
* as the _target_ of an edge.
|
|
147
187
|
* @public
|
|
148
188
|
* @param node - The node to get the connected nodes from
|
|
149
189
|
* @param nodes - The array of all nodes
|
|
150
190
|
* @param edges - The array of all edges
|
|
151
191
|
* @returns An array of nodes that are connected over eges where the source is the given node
|
|
192
|
+
*
|
|
193
|
+
* @example
|
|
194
|
+
* ```ts
|
|
195
|
+
*import { getOutgoers } from '@xyflow/react';
|
|
196
|
+
*
|
|
197
|
+
*const nodes = [];
|
|
198
|
+
*const edges = [];
|
|
199
|
+
*
|
|
200
|
+
*const outgoers = getOutgoers(
|
|
201
|
+
* { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
|
|
202
|
+
* nodes,
|
|
203
|
+
* edges,
|
|
204
|
+
*);
|
|
205
|
+
*```
|
|
152
206
|
*/
|
|
153
207
|
const getOutgoers = (node, nodes, edges) => {
|
|
154
208
|
if (!node.id) {
|
|
@@ -163,12 +217,27 @@ const getOutgoers = (node, nodes, edges) => {
|
|
|
163
217
|
return nodes.filter((n) => outgoerIds.has(n.id));
|
|
164
218
|
};
|
|
165
219
|
/**
|
|
166
|
-
*
|
|
220
|
+
* This util is used to tell you what nodes, if any, are connected to the given node
|
|
221
|
+
* as the _source_ of an edge.
|
|
167
222
|
* @public
|
|
168
223
|
* @param node - The node to get the connected nodes from
|
|
169
224
|
* @param nodes - The array of all nodes
|
|
170
225
|
* @param edges - The array of all edges
|
|
171
226
|
* @returns An array of nodes that are connected over eges where the target is the given node
|
|
227
|
+
*
|
|
228
|
+
* @example
|
|
229
|
+
* ```ts
|
|
230
|
+
*import { getIncomers } from '@xyflow/react';
|
|
231
|
+
*
|
|
232
|
+
*const nodes = [];
|
|
233
|
+
*const edges = [];
|
|
234
|
+
*
|
|
235
|
+
*const incomers = getIncomers(
|
|
236
|
+
* { id: '1', position: { x: 0, y: 0 }, data: { label: 'node' } },
|
|
237
|
+
* nodes,
|
|
238
|
+
* edges,
|
|
239
|
+
*);
|
|
240
|
+
*```
|
|
172
241
|
*/
|
|
173
242
|
const getIncomers = (node, nodes, edges) => {
|
|
174
243
|
if (!node.id) {
|
|
@@ -193,12 +262,40 @@ const getNodePositionWithOrigin = (node, nodeOrigin = [0, 0]) => {
|
|
|
193
262
|
};
|
|
194
263
|
};
|
|
195
264
|
/**
|
|
196
|
-
*
|
|
265
|
+
* Returns the bounding box that contains all the given nodes in an array. This can
|
|
266
|
+
* be useful when combined with [`getViewportForBounds`](/api-reference/utils/get-viewport-for-bounds)
|
|
267
|
+
* to calculate the correct transform to fit the given nodes in a viewport.
|
|
197
268
|
* @public
|
|
198
269
|
* @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport.
|
|
199
270
|
* @param nodes - Nodes to calculate the bounds for
|
|
200
271
|
* @param params.nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center
|
|
201
272
|
* @returns Bounding box enclosing all nodes
|
|
273
|
+
*
|
|
274
|
+
* @remarks This function was previously called `getRectOfNodes`
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```js
|
|
278
|
+
*import { getNodesBounds } from '@xyflow/react';
|
|
279
|
+
*
|
|
280
|
+
*const nodes = [
|
|
281
|
+
* {
|
|
282
|
+
* id: 'a',
|
|
283
|
+
* position: { x: 0, y: 0 },
|
|
284
|
+
* data: { label: 'a' },
|
|
285
|
+
* width: 50,
|
|
286
|
+
* height: 25,
|
|
287
|
+
* },
|
|
288
|
+
* {
|
|
289
|
+
* id: 'b',
|
|
290
|
+
* position: { x: 100, y: 100 },
|
|
291
|
+
* data: { label: 'b' },
|
|
292
|
+
* width: 50,
|
|
293
|
+
* height: 25,
|
|
294
|
+
* },
|
|
295
|
+
*];
|
|
296
|
+
*
|
|
297
|
+
*const bounds = getNodesBounds(nodes);
|
|
298
|
+
*```
|
|
202
299
|
*/
|
|
203
300
|
const getNodesBounds = (nodes, params = { nodeOrigin: [0, 0], nodeLookup: undefined }) => {
|
|
204
301
|
if (process.env.NODE_ENV === 'development' && !params.nodeLookup) {
|
|
@@ -267,10 +364,30 @@ excludeNonSelectableNodes = false) => {
|
|
|
267
364
|
return visibleNodes;
|
|
268
365
|
};
|
|
269
366
|
/**
|
|
270
|
-
*
|
|
367
|
+
* This utility filters an array of edges, keeping only those where either the source or target
|
|
368
|
+
* node is present in the given array of nodes.
|
|
369
|
+
* @public
|
|
271
370
|
* @param nodes - Nodes you want to get the connected edges for
|
|
272
371
|
* @param edges - All edges
|
|
273
372
|
* @returns Array of edges that connect any of the given nodes with each other
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```js
|
|
376
|
+
*import { getConnectedEdges } from '@xyflow/react';
|
|
377
|
+
*
|
|
378
|
+
*const nodes = [
|
|
379
|
+
* { id: 'a', position: { x: 0, y: 0 } },
|
|
380
|
+
* { id: 'b', position: { x: 100, y: 0 } },
|
|
381
|
+
*];
|
|
382
|
+
*
|
|
383
|
+
*const edges = [
|
|
384
|
+
* { id: 'a->c', source: 'a', target: 'c' },
|
|
385
|
+
* { id: 'c->d', source: 'c', target: 'd' },
|
|
386
|
+
*];
|
|
387
|
+
*
|
|
388
|
+
*const connectedEdges = getConnectedEdges(nodes, edges);
|
|
389
|
+
* // => [{ id: 'a->c', source: 'a', target: 'c' }]
|
|
390
|
+
*```
|
|
274
391
|
*/
|
|
275
392
|
const getConnectedEdges = (nodes, edges) => {
|
|
276
393
|
const nodeIds = new Set();
|
|
@@ -335,10 +452,13 @@ function calculateNodePosition({ nodeId, nextPosition, nodeLookup, nodeOrigin =
|
|
|
335
452
|
const positionAbsolute = isCoordinateExtent(extent)
|
|
336
453
|
? clampPosition(nextPosition, extent, node.measured)
|
|
337
454
|
: nextPosition;
|
|
455
|
+
if (node.measured.width === undefined || node.measured.height === undefined) {
|
|
456
|
+
onError?.('015', errorMessages['error015']());
|
|
457
|
+
}
|
|
338
458
|
return {
|
|
339
459
|
position: {
|
|
340
|
-
x: positionAbsolute.x - parentX + node.measured.width * origin[0],
|
|
341
|
-
y: positionAbsolute.y - parentY + node.measured.height * origin[1],
|
|
460
|
+
x: positionAbsolute.x - parentX + (node.measured.width ?? 0) * origin[0],
|
|
461
|
+
y: positionAbsolute.y - parentY + (node.measured.height ?? 0) * origin[1],
|
|
342
462
|
},
|
|
343
463
|
positionAbsolute,
|
|
344
464
|
};
|
|
@@ -515,8 +635,8 @@ const rendererPointToPoint = ({ x, y }, [tx, ty, tScale]) => {
|
|
|
515
635
|
* @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport}
|
|
516
636
|
* @example
|
|
517
637
|
* const { x, y, zoom } = getViewportForBounds(
|
|
518
|
-
|
|
519
|
-
|
|
638
|
+
*{ x: 0, y: 0, width: 100, height: 100},
|
|
639
|
+
*1200, 800, 0.5, 2);
|
|
520
640
|
*/
|
|
521
641
|
const getViewportForBounds = (bounds, width, height, minZoom, maxZoom, padding) => {
|
|
522
642
|
const xZoom = width / (bounds.width * (1 + padding));
|
|
@@ -611,9 +731,11 @@ const getEventPosition = (event, bounds) => {
|
|
|
611
731
|
y: evtY - (bounds?.top ?? 0),
|
|
612
732
|
};
|
|
613
733
|
};
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
734
|
+
/*
|
|
735
|
+
* The handle bounds are calculated relative to the node element.
|
|
736
|
+
* We store them in the internals object of the node in order to avoid
|
|
737
|
+
* unnecessary recalculations.
|
|
738
|
+
*/
|
|
617
739
|
const getHandleBounds = (type, nodeElement, nodeBounds, zoom, nodeId) => {
|
|
618
740
|
const handles = nodeElement.querySelectorAll(`.${type}`);
|
|
619
741
|
if (!handles || !handles.length) {
|
|
@@ -634,8 +756,10 @@ const getHandleBounds = (type, nodeElement, nodeBounds, zoom, nodeId) => {
|
|
|
634
756
|
};
|
|
635
757
|
|
|
636
758
|
function getBezierEdgeCenter({ sourceX, sourceY, targetX, targetY, sourceControlX, sourceControlY, targetControlX, targetControlY, }) {
|
|
637
|
-
|
|
638
|
-
|
|
759
|
+
/*
|
|
760
|
+
* cubic bezier t=0.5 mid point, not the actual mid point, but easy to calculate
|
|
761
|
+
* https://stackoverflow.com/questions/67516101/how-to-find-distance-mid-point-of-bezier-curve
|
|
762
|
+
*/
|
|
639
763
|
const centerX = sourceX * 0.125 + sourceControlX * 0.375 + targetControlX * 0.375 + targetX * 0.125;
|
|
640
764
|
const centerY = sourceY * 0.125 + sourceControlY * 0.375 + targetControlY * 0.375 + targetY * 0.125;
|
|
641
765
|
const offsetX = Math.abs(centerX - sourceX);
|
|
@@ -661,7 +785,9 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }) {
|
|
|
661
785
|
}
|
|
662
786
|
}
|
|
663
787
|
/**
|
|
664
|
-
*
|
|
788
|
+
* The `getBezierPath` util returns everything you need to render a bezier edge
|
|
789
|
+
*between two nodes.
|
|
790
|
+
* @public
|
|
665
791
|
* @param params.sourceX - The x position of the source handle
|
|
666
792
|
* @param params.sourceY - The y position of the source handle
|
|
667
793
|
* @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
|
|
@@ -671,17 +797,22 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }) {
|
|
|
671
797
|
* @param params.curvature - The curvature of the bezier edge
|
|
672
798
|
* @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
799
|
* @example
|
|
800
|
+
* ```js
|
|
674
801
|
* const source = { x: 0, y: 20 };
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
});
|
|
802
|
+
* const target = { x: 150, y: 100 };
|
|
803
|
+
*
|
|
804
|
+
* const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
|
|
805
|
+
* sourceX: source.x,
|
|
806
|
+
* sourceY: source.y,
|
|
807
|
+
* sourcePosition: Position.Right,
|
|
808
|
+
* targetX: target.x,
|
|
809
|
+
* targetY: target.y,
|
|
810
|
+
* targetPosition: Position.Left,
|
|
811
|
+
*});
|
|
812
|
+
*```
|
|
813
|
+
*
|
|
814
|
+
* @remarks This function returns a tuple (aka a fixed-size array) to make it easier to
|
|
815
|
+
*work with multiple edge paths at once.
|
|
685
816
|
*/
|
|
686
817
|
function getBezierPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, curvature = 0.25, }) {
|
|
687
818
|
const [sourceControlX, sourceControlY] = getControlWithCurvature({
|
|
@@ -759,12 +890,16 @@ const connectionExists = (edge, edges) => {
|
|
|
759
890
|
(el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle)));
|
|
760
891
|
};
|
|
761
892
|
/**
|
|
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.
|
|
893
|
+
* 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
894
|
* @public
|
|
765
895
|
* @param edgeParams - Either an Edge or a Connection you want to add
|
|
766
896
|
* @param edges - The array of all current edges
|
|
767
897
|
* @returns A new array of edges with the new edge added
|
|
898
|
+
*
|
|
899
|
+
* @remarks If an edge with the same `target` and `source` already exists (and the same
|
|
900
|
+
*`targetHandle` and `sourceHandle` if those are set), then this util won't add
|
|
901
|
+
*a new edge even if the `id` property is different.
|
|
902
|
+
*
|
|
768
903
|
*/
|
|
769
904
|
const addEdge = (edgeParams, edges) => {
|
|
770
905
|
if (!edgeParams.source || !edgeParams.target) {
|
|
@@ -793,12 +928,21 @@ const addEdge = (edgeParams, edges) => {
|
|
|
793
928
|
return edges.concat(edge);
|
|
794
929
|
};
|
|
795
930
|
/**
|
|
796
|
-
* A handy utility to
|
|
931
|
+
* A handy utility to update an existing [`Edge`](/api-reference/types/edge) with new properties.
|
|
932
|
+
*This searches your edge array for an edge with a matching `id` and updates its
|
|
933
|
+
*properties with the connection you provide.
|
|
934
|
+
* @public
|
|
797
935
|
* @param oldEdge - The edge you want to update
|
|
798
936
|
* @param newConnection - The new connection you want to update the edge with
|
|
799
937
|
* @param edges - The array of all current edges
|
|
800
938
|
* @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id
|
|
801
939
|
* @returns the updated edges array
|
|
940
|
+
*
|
|
941
|
+
* @example
|
|
942
|
+
* ```js
|
|
943
|
+
*const onReconnect = useCallback(
|
|
944
|
+
* (oldEdge: Edge, newConnection: Connection) => setEdges((els) => reconnectEdge(oldEdge, newConnection, els)),[]);
|
|
945
|
+
*```
|
|
802
946
|
*/
|
|
803
947
|
const reconnectEdge = (oldEdge, newConnection, edges, options = { shouldReplaceId: true }) => {
|
|
804
948
|
const { id: oldEdgeId, ...rest } = oldEdge;
|
|
@@ -824,24 +968,28 @@ const reconnectEdge = (oldEdge, newConnection, edges, options = { shouldReplaceI
|
|
|
824
968
|
};
|
|
825
969
|
|
|
826
970
|
/**
|
|
827
|
-
*
|
|
971
|
+
* Calculates the straight line path between two points.
|
|
972
|
+
* @public
|
|
828
973
|
* @param params.sourceX - The x position of the source handle
|
|
829
974
|
* @param params.sourceY - The y position of the source handle
|
|
830
975
|
* @param params.targetX - The x position of the target handle
|
|
831
976
|
* @param params.targetY - The y position of the target handle
|
|
832
977
|
* @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
978
|
* @example
|
|
979
|
+
* ```js
|
|
834
980
|
* const source = { x: 0, y: 20 };
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
981
|
+
* const target = { x: 150, y: 100 };
|
|
982
|
+
*
|
|
983
|
+
* const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
|
|
984
|
+
* sourceX: source.x,
|
|
985
|
+
* sourceY: source.y,
|
|
986
|
+
* sourcePosition: Position.Right,
|
|
987
|
+
* targetX: target.x,
|
|
988
|
+
* targetY: target.y,
|
|
989
|
+
* targetPosition: Position.Left,
|
|
990
|
+
* });
|
|
991
|
+
* ```
|
|
992
|
+
* @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
|
|
845
993
|
*/
|
|
846
994
|
function getStraightPath({ sourceX, sourceY, targetX, targetY, }) {
|
|
847
995
|
const [labelX, labelY, offsetX, offsetY] = getEdgeCenter({
|
|
@@ -866,8 +1014,10 @@ const getDirection = ({ source, sourcePosition = Position.Bottom, target, }) =>
|
|
|
866
1014
|
return source.y < target.y ? { x: 0, y: 1 } : { x: 0, y: -1 };
|
|
867
1015
|
};
|
|
868
1016
|
const distance = (a, b) => Math.sqrt(Math.pow(b.x - a.x, 2) + Math.pow(b.y - a.y, 2));
|
|
869
|
-
|
|
870
|
-
|
|
1017
|
+
/*
|
|
1018
|
+
* ith this function we try to mimic a orthogonal edge routing behaviour
|
|
1019
|
+
* 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
|
|
1020
|
+
*/
|
|
871
1021
|
function getPoints({ source, sourcePosition = Position.Bottom, target, targetPosition = Position.Top, center, offset, }) {
|
|
872
1022
|
const sourceDir = handleDirections[sourcePosition];
|
|
873
1023
|
const targetDir = handleDirections[targetPosition];
|
|
@@ -894,16 +1044,20 @@ function getPoints({ source, sourcePosition = Position.Bottom, target, targetPos
|
|
|
894
1044
|
if (sourceDir[dirAccessor] * targetDir[dirAccessor] === -1) {
|
|
895
1045
|
centerX = center.x ?? defaultCenterX;
|
|
896
1046
|
centerY = center.y ?? defaultCenterY;
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
1047
|
+
/*
|
|
1048
|
+
* --->
|
|
1049
|
+
* |
|
|
1050
|
+
* >---
|
|
1051
|
+
*/
|
|
900
1052
|
const verticalSplit = [
|
|
901
1053
|
{ x: centerX, y: sourceGapped.y },
|
|
902
1054
|
{ x: centerX, y: targetGapped.y },
|
|
903
1055
|
];
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
1056
|
+
/*
|
|
1057
|
+
* |
|
|
1058
|
+
* ---
|
|
1059
|
+
* |
|
|
1060
|
+
*/
|
|
907
1061
|
const horizontalSplit = [
|
|
908
1062
|
{ x: sourceGapped.x, y: centerY },
|
|
909
1063
|
{ x: targetGapped.x, y: centerY },
|
|
@@ -992,7 +1146,10 @@ function getBend(a, b, c, size) {
|
|
|
992
1146
|
return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
|
|
993
1147
|
}
|
|
994
1148
|
/**
|
|
995
|
-
*
|
|
1149
|
+
* The `getSmoothStepPath` util returns everything you need to render a stepped path
|
|
1150
|
+
*between two nodes. The `borderRadius` property can be used to choose how rounded
|
|
1151
|
+
*the corners of those steps are.
|
|
1152
|
+
* @public
|
|
996
1153
|
* @param params.sourceX - The x position of the source handle
|
|
997
1154
|
* @param params.sourceY - The y position of the source handle
|
|
998
1155
|
* @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
|
|
@@ -1001,17 +1158,20 @@ function getBend(a, b, c, size) {
|
|
|
1001
1158
|
* @param params.targetPosition - The position of the target handle (default: Position.Top)
|
|
1002
1159
|
* @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
1160
|
* @example
|
|
1161
|
+
* ```js
|
|
1004
1162
|
* const source = { x: 0, y: 20 };
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1163
|
+
* const target = { x: 150, y: 100 };
|
|
1164
|
+
*
|
|
1165
|
+
* const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
|
|
1166
|
+
* sourceX: source.x,
|
|
1167
|
+
* sourceY: source.y,
|
|
1168
|
+
* sourcePosition: Position.Right,
|
|
1169
|
+
* targetX: target.x,
|
|
1170
|
+
* targetY: target.y,
|
|
1171
|
+
* targetPosition: Position.Left,
|
|
1172
|
+
* });
|
|
1173
|
+
* ```
|
|
1174
|
+
* @remarks This function returns a tuple (aka a fixed-size array) to make it easier to work with multiple edge paths at once.
|
|
1015
1175
|
*/
|
|
1016
1176
|
function getSmoothStepPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, borderRadius = 5, centerX, centerY, offset = 20, }) {
|
|
1017
1177
|
const [points, labelX, labelY, offsetX, offsetY] = getPoints({
|
|
@@ -1162,8 +1322,10 @@ function getNodeToolbarTransform(nodeRect, viewport, position, offset, align) {
|
|
|
1162
1322
|
else if (align === 'end') {
|
|
1163
1323
|
alignmentOffset = 1;
|
|
1164
1324
|
}
|
|
1165
|
-
|
|
1166
|
-
|
|
1325
|
+
/*
|
|
1326
|
+
* position === Position.Top
|
|
1327
|
+
* we set the x any y position of the toolbar based on the nodes position
|
|
1328
|
+
*/
|
|
1167
1329
|
let pos = [
|
|
1168
1330
|
(nodeRect.x + nodeRect.width * alignmentOffset) * viewport.zoom + viewport.x,
|
|
1169
1331
|
nodeRect.y * viewport.zoom + viewport.y - offset,
|
|
@@ -1362,8 +1524,10 @@ function handleExpandParent(children, nodeLookup, parentLookup, nodeOrigin = [0,
|
|
|
1362
1524
|
y: parent.position.y - yChange + heightChange,
|
|
1363
1525
|
},
|
|
1364
1526
|
});
|
|
1365
|
-
|
|
1366
|
-
|
|
1527
|
+
/*
|
|
1528
|
+
* We move all child nodes in the oppsite direction
|
|
1529
|
+
* so the x,y changes of the parent do not move the children
|
|
1530
|
+
*/
|
|
1367
1531
|
parentLookup.get(parentId)?.forEach((childNode) => {
|
|
1368
1532
|
if (!children.some((child) => child.id === childNode.id)) {
|
|
1369
1533
|
changes.push({
|
|
@@ -1501,9 +1665,11 @@ async function panBy({ delta, panZoom, transform, translateExtent, width, height
|
|
|
1501
1665
|
* @param handleId handleId of the conneciton
|
|
1502
1666
|
*/
|
|
1503
1667
|
function addConnectionToLookup(type, connection, connectionKey, connectionLookup, nodeId, handleId) {
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1668
|
+
/*
|
|
1669
|
+
* We add the connection to the connectionLookup at the following keys
|
|
1670
|
+
* 1. nodeId, 2. nodeId-type, 3. nodeId-type-handleId
|
|
1671
|
+
* If the key already exists, we add the connection to the existing map
|
|
1672
|
+
*/
|
|
1507
1673
|
let key = nodeId;
|
|
1508
1674
|
const nodeMap = connectionLookup.get(key) || new Map();
|
|
1509
1675
|
connectionLookup.set(key, nodeMap.set(connectionKey, connection));
|
|
@@ -1604,9 +1770,11 @@ function getDragItems(nodeLookup, nodesDraggable, mousePos, nodeId) {
|
|
|
1604
1770
|
}
|
|
1605
1771
|
return dragItems;
|
|
1606
1772
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1773
|
+
/*
|
|
1774
|
+
* returns two params:
|
|
1775
|
+
* 1. the dragged node (or the first of the list, if we are dragging a node selection)
|
|
1776
|
+
* 2. array of selected nodes (for multi selections)
|
|
1777
|
+
*/
|
|
1610
1778
|
function getEventHandlerParams({ nodeId, dragItems, nodeLookup, dragging = true, }) {
|
|
1611
1779
|
const nodesFromDragItems = [];
|
|
1612
1780
|
for (const [id, dragItem] of dragItems) {
|
|
@@ -1660,16 +1828,20 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
|
|
|
1660
1828
|
}
|
|
1661
1829
|
for (const [id, dragItem] of dragItems) {
|
|
1662
1830
|
if (!nodeLookup.has(id)) {
|
|
1663
|
-
|
|
1664
|
-
|
|
1831
|
+
/*
|
|
1832
|
+
* if the node is not in the nodeLookup anymore, it was probably deleted while dragging
|
|
1833
|
+
* and we don't need to update it anymore
|
|
1834
|
+
*/
|
|
1665
1835
|
continue;
|
|
1666
1836
|
}
|
|
1667
1837
|
let nextPosition = { x: x - dragItem.distance.x, y: y - dragItem.distance.y };
|
|
1668
1838
|
if (snapToGrid) {
|
|
1669
1839
|
nextPosition = snapPosition(nextPosition, snapGrid);
|
|
1670
1840
|
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1841
|
+
/*
|
|
1842
|
+
* if there is selection with multiple nodes and a node extent is set, we need to adjust the node extent for each node
|
|
1843
|
+
* based on its position so that the node stays at it's position relative to the selection.
|
|
1844
|
+
*/
|
|
1673
1845
|
let adjustedNodeExtent = [
|
|
1674
1846
|
[nodeExtent[0][0], nodeExtent[0][1]],
|
|
1675
1847
|
[nodeExtent[1][0], nodeExtent[1][1]],
|
|
@@ -1719,7 +1891,12 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
|
|
|
1719
1891
|
if (!containerBounds) {
|
|
1720
1892
|
return;
|
|
1721
1893
|
}
|
|
1722
|
-
const { transform, panBy, autoPanSpeed } = getStoreItems();
|
|
1894
|
+
const { transform, panBy, autoPanSpeed, autoPanOnNodeDrag } = getStoreItems();
|
|
1895
|
+
if (!autoPanOnNodeDrag) {
|
|
1896
|
+
autoPanStarted = false;
|
|
1897
|
+
cancelAnimationFrame(autoPanId);
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1723
1900
|
const [xMovement, yMovement] = calcAutoPan(mousePosition, containerBounds, autoPanSpeed);
|
|
1724
1901
|
if (xMovement !== 0 || yMovement !== 0) {
|
|
1725
1902
|
lastPos.x = (lastPos.x ?? 0) - xMovement / transform[2];
|
|
@@ -1859,8 +2036,10 @@ function getNodesWithinDistance(position, nodeLookup, distance) {
|
|
|
1859
2036
|
}
|
|
1860
2037
|
return nodes;
|
|
1861
2038
|
}
|
|
1862
|
-
|
|
1863
|
-
|
|
2039
|
+
/*
|
|
2040
|
+
* this distance is used for the area around the user pointer
|
|
2041
|
+
* while doing a connection for finding the closest nodes
|
|
2042
|
+
*/
|
|
1864
2043
|
const ADDITIONAL_DISTANCE = 250;
|
|
1865
2044
|
function getClosestHandle(position, connectionRadius, nodeLookup, fromHandle) {
|
|
1866
2045
|
let closestHandles = [];
|
|
@@ -2028,8 +2207,10 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
2028
2207
|
toPosition: isValid && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position],
|
|
2029
2208
|
toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId) : null,
|
|
2030
2209
|
};
|
|
2031
|
-
|
|
2032
|
-
|
|
2210
|
+
/*
|
|
2211
|
+
* we don't want to trigger an update when the connection
|
|
2212
|
+
* is snapped to the same handle as before
|
|
2213
|
+
*/
|
|
2033
2214
|
if (isValid &&
|
|
2034
2215
|
closestHandle &&
|
|
2035
2216
|
previousConnection.toHandle &&
|
|
@@ -2048,8 +2229,10 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
2048
2229
|
if ((closestHandle || handleDomNode) && connection && isValid) {
|
|
2049
2230
|
onConnect?.(connection);
|
|
2050
2231
|
}
|
|
2051
|
-
|
|
2052
|
-
|
|
2232
|
+
/*
|
|
2233
|
+
* it's important to get a fresh reference from the store here
|
|
2234
|
+
* in order to get the latest state of onConnectEnd
|
|
2235
|
+
*/
|
|
2053
2236
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
2054
2237
|
const { inProgress, ...connectionState } = previousConnection;
|
|
2055
2238
|
const finalConnectionState = {
|
|
@@ -2084,8 +2267,10 @@ function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId
|
|
|
2084
2267
|
: null;
|
|
2085
2268
|
const { x, y } = getEventPosition(event);
|
|
2086
2269
|
const handleBelow = doc.elementFromPoint(x, y);
|
|
2087
|
-
|
|
2088
|
-
|
|
2270
|
+
/*
|
|
2271
|
+
* we always want to prioritize the handle below the mouse cursor over the closest distance handle,
|
|
2272
|
+
* because it could be that the center of another handle is closer to the mouse pointer than the handle below the cursor
|
|
2273
|
+
*/
|
|
2089
2274
|
const handleToCheck = handleBelow?.classList.contains(`${lib}-flow__handle`) ? handleBelow : handleDomNode;
|
|
2090
2275
|
const result = {
|
|
2091
2276
|
handleDomNode: handleToCheck,
|
|
@@ -2236,8 +2421,10 @@ function createPanOnScrollHandler({ zoomPanValues, noWheelClassName, d3Selection
|
|
|
2236
2421
|
d3Zoom.scaleTo(d3Selection, zoom, point, event);
|
|
2237
2422
|
return;
|
|
2238
2423
|
}
|
|
2239
|
-
|
|
2240
|
-
|
|
2424
|
+
/*
|
|
2425
|
+
* increase scroll speed in firefox
|
|
2426
|
+
* firefox: deltaMode === 1; chrome: deltaMode === 0
|
|
2427
|
+
*/
|
|
2241
2428
|
const deltaNormalize = event.deltaMode === 1 ? 20 : 1;
|
|
2242
2429
|
let deltaX = panOnScrollMode === PanOnScrollMode.Vertical ? 0 : event.deltaX * deltaNormalize;
|
|
2243
2430
|
let deltaY = panOnScrollMode === PanOnScrollMode.Horizontal ? 0 : event.deltaY * deltaNormalize;
|
|
@@ -2251,9 +2438,11 @@ function createPanOnScrollHandler({ zoomPanValues, noWheelClassName, d3Selection
|
|
|
2251
2438
|
{ internal: true });
|
|
2252
2439
|
const nextViewport = transformToViewport(d3Selection.property('__zoom'));
|
|
2253
2440
|
clearTimeout(zoomPanValues.panScrollTimeout);
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2441
|
+
/*
|
|
2442
|
+
* for pan on scroll we need to handle the event calls on our own
|
|
2443
|
+
* we can't use the start, zoom and end events from d3-zoom
|
|
2444
|
+
* because start and move gets called on every scroll event and not once at the beginning
|
|
2445
|
+
*/
|
|
2257
2446
|
if (!zoomPanValues.isPanScrolling) {
|
|
2258
2447
|
zoomPanValues.isPanScrolling = true;
|
|
2259
2448
|
onPanZoomStart?.(event, nextViewport);
|
|
@@ -2488,9 +2677,11 @@ function XYPanZoom({ domNode, minZoom, maxZoom, paneClickDistance, translateExte
|
|
|
2488
2677
|
lib,
|
|
2489
2678
|
});
|
|
2490
2679
|
d3ZoomInstance.filter(filter);
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
2680
|
+
/*
|
|
2681
|
+
* We cannot add zoomOnDoubleClick to the filter above because
|
|
2682
|
+
* double tapping on touch screens circumvents the filter and
|
|
2683
|
+
* dblclick.zoom is fired on the selection directly
|
|
2684
|
+
*/
|
|
2494
2685
|
if (zoomOnDoubleClick) {
|
|
2495
2686
|
d3Selection.on('dblclick.zoom', d3DblClickZoomHandler);
|
|
2496
2687
|
}
|
|
@@ -2572,6 +2763,11 @@ function XYPanZoom({ domNode, minZoom, maxZoom, paneClickDistance, translateExte
|
|
|
2572
2763
|
};
|
|
2573
2764
|
}
|
|
2574
2765
|
|
|
2766
|
+
/**
|
|
2767
|
+
* Used to determine the variant of the resize control
|
|
2768
|
+
*
|
|
2769
|
+
* @public
|
|
2770
|
+
*/
|
|
2575
2771
|
var ResizeControlVariant;
|
|
2576
2772
|
(function (ResizeControlVariant) {
|
|
2577
2773
|
ResizeControlVariant["Line"] = "line";
|
|
@@ -2867,8 +3063,10 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2867
3063
|
parentNode = nodeLookup.get(node.parentId);
|
|
2868
3064
|
parentExtent = parentNode && node.extent === 'parent' ? nodeToParentExtent(parentNode) : undefined;
|
|
2869
3065
|
}
|
|
2870
|
-
|
|
2871
|
-
|
|
3066
|
+
/*
|
|
3067
|
+
* Collect all child nodes to correct their relative positions when top/left changes
|
|
3068
|
+
* Determine largest minimal extent the parent node is allowed to resize to
|
|
3069
|
+
*/
|
|
2872
3070
|
childNodes = [];
|
|
2873
3071
|
childExtent = undefined;
|
|
2874
3072
|
for (const [childId, child] of nodeLookup) {
|
|
@@ -2922,8 +3120,10 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2922
3120
|
change.y = isYPosChange ? y : prevValues.y;
|
|
2923
3121
|
prevValues.x = change.x;
|
|
2924
3122
|
prevValues.y = change.y;
|
|
2925
|
-
|
|
2926
|
-
|
|
3123
|
+
/*
|
|
3124
|
+
* when top/left changes, correct the relative positions of child nodes
|
|
3125
|
+
* so that they stay in the same position
|
|
3126
|
+
*/
|
|
2927
3127
|
if (childNodes.length > 0) {
|
|
2928
3128
|
const xChange = x - prevX;
|
|
2929
3129
|
const yChange = y - prevY;
|