@xyflow/system 0.0.30 → 0.0.32
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/index.js +278 -232
- package/dist/esm/index.mjs +278 -232
- package/dist/esm/types/general.d.ts +30 -11
- package/dist/esm/types/general.d.ts.map +1 -1
- package/dist/esm/types/handles.d.ts +4 -16
- package/dist/esm/types/handles.d.ts.map +1 -1
- package/dist/esm/types/nodes.d.ts +6 -5
- package/dist/esm/types/nodes.d.ts.map +1 -1
- package/dist/esm/types/utils.d.ts +6 -0
- package/dist/esm/types/utils.d.ts.map +1 -1
- package/dist/esm/utils/connections.d.ts +1 -0
- package/dist/esm/utils/connections.d.ts.map +1 -1
- package/dist/esm/utils/dom.d.ts +2 -2
- package/dist/esm/utils/dom.d.ts.map +1 -1
- package/dist/esm/utils/edges/positions.d.ts +3 -3
- package/dist/esm/utils/edges/positions.d.ts.map +1 -1
- package/dist/esm/utils/general.d.ts +5 -9
- package/dist/esm/utils/general.d.ts.map +1 -1
- package/dist/esm/utils/graph.d.ts +3 -7
- package/dist/esm/utils/graph.d.ts.map +1 -1
- package/dist/esm/utils/store.d.ts +2 -2
- package/dist/esm/utils/store.d.ts.map +1 -1
- package/dist/esm/xydrag/XYDrag.d.ts +2 -0
- package/dist/esm/xydrag/XYDrag.d.ts.map +1 -1
- package/dist/esm/xyhandle/XYHandle.d.ts +1 -46
- package/dist/esm/xyhandle/XYHandle.d.ts.map +1 -1
- package/dist/esm/xyhandle/types.d.ts +48 -0
- package/dist/esm/xyhandle/types.d.ts.map +1 -0
- package/dist/esm/xyhandle/utils.d.ts +5 -6
- package/dist/esm/xyhandle/utils.d.ts.map +1 -1
- package/dist/esm/xyresizer/XYResizer.d.ts.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/types/general.d.ts +30 -11
- package/dist/umd/types/general.d.ts.map +1 -1
- package/dist/umd/types/handles.d.ts +4 -16
- package/dist/umd/types/handles.d.ts.map +1 -1
- package/dist/umd/types/nodes.d.ts +6 -5
- package/dist/umd/types/nodes.d.ts.map +1 -1
- package/dist/umd/types/utils.d.ts +6 -0
- package/dist/umd/types/utils.d.ts.map +1 -1
- package/dist/umd/utils/connections.d.ts +1 -0
- package/dist/umd/utils/connections.d.ts.map +1 -1
- package/dist/umd/utils/dom.d.ts +2 -2
- package/dist/umd/utils/dom.d.ts.map +1 -1
- package/dist/umd/utils/edges/positions.d.ts +3 -3
- package/dist/umd/utils/edges/positions.d.ts.map +1 -1
- package/dist/umd/utils/general.d.ts +5 -9
- package/dist/umd/utils/general.d.ts.map +1 -1
- package/dist/umd/utils/graph.d.ts +3 -7
- package/dist/umd/utils/graph.d.ts.map +1 -1
- package/dist/umd/utils/store.d.ts +2 -2
- package/dist/umd/utils/store.d.ts.map +1 -1
- package/dist/umd/xydrag/XYDrag.d.ts +2 -0
- package/dist/umd/xydrag/XYDrag.d.ts.map +1 -1
- package/dist/umd/xyhandle/XYHandle.d.ts +1 -46
- package/dist/umd/xyhandle/XYHandle.d.ts.map +1 -1
- package/dist/umd/xyhandle/types.d.ts +48 -0
- package/dist/umd/xyhandle/types.d.ts.map +1 -0
- package/dist/umd/xyhandle/utils.d.ts +5 -6
- package/dist/umd/xyhandle/utils.d.ts.map +1 -1
- package/dist/umd/xyresizer/XYResizer.d.ts.map +1 -1
- package/package.json +3 -3
package/dist/esm/index.mjs
CHANGED
|
@@ -39,6 +39,18 @@ var SelectionMode;
|
|
|
39
39
|
SelectionMode["Partial"] = "partial";
|
|
40
40
|
SelectionMode["Full"] = "full";
|
|
41
41
|
})(SelectionMode || (SelectionMode = {}));
|
|
42
|
+
const initialConnection = {
|
|
43
|
+
inProgress: false,
|
|
44
|
+
isValid: null,
|
|
45
|
+
from: null,
|
|
46
|
+
fromHandle: null,
|
|
47
|
+
fromPosition: null,
|
|
48
|
+
fromNode: null,
|
|
49
|
+
to: null,
|
|
50
|
+
toHandle: null,
|
|
51
|
+
toPosition: null,
|
|
52
|
+
toNode: null,
|
|
53
|
+
};
|
|
42
54
|
|
|
43
55
|
var ConnectionLineType;
|
|
44
56
|
(function (ConnectionLineType) {
|
|
@@ -61,6 +73,12 @@ var Position;
|
|
|
61
73
|
Position["Right"] = "right";
|
|
62
74
|
Position["Bottom"] = "bottom";
|
|
63
75
|
})(Position || (Position = {}));
|
|
76
|
+
const oppositePosition = {
|
|
77
|
+
[Position.Left]: Position.Right,
|
|
78
|
+
[Position.Right]: Position.Left,
|
|
79
|
+
[Position.Top]: Position.Bottom,
|
|
80
|
+
[Position.Bottom]: Position.Top,
|
|
81
|
+
};
|
|
64
82
|
|
|
65
83
|
/**
|
|
66
84
|
* @internal
|
|
@@ -101,6 +119,9 @@ function handleConnectionChange(a, b, cb) {
|
|
|
101
119
|
cb(diff);
|
|
102
120
|
}
|
|
103
121
|
}
|
|
122
|
+
function getConnectionStatus(isValid) {
|
|
123
|
+
return isValid === null ? null : isValid ? 'valid' : 'invalid';
|
|
124
|
+
}
|
|
104
125
|
|
|
105
126
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
106
127
|
/**
|
|
@@ -162,19 +183,12 @@ const getIncomers = (node, nodes, edges) => {
|
|
|
162
183
|
};
|
|
163
184
|
const getNodePositionWithOrigin = (node, nodeOrigin = [0, 0]) => {
|
|
164
185
|
const { width, height } = getNodeDimensions(node);
|
|
165
|
-
const
|
|
166
|
-
const origin = node.origin || nodeOrigin;
|
|
186
|
+
const origin = node.origin ?? nodeOrigin;
|
|
167
187
|
const offsetX = width * origin[0];
|
|
168
188
|
const offsetY = height * origin[1];
|
|
169
189
|
return {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
y: node.position.y - offsetY,
|
|
173
|
-
},
|
|
174
|
-
positionAbsolute: {
|
|
175
|
-
x: positionAbsolute.x - offsetX,
|
|
176
|
-
y: positionAbsolute.y - offsetY,
|
|
177
|
-
},
|
|
190
|
+
x: node.position.x - offsetX,
|
|
191
|
+
y: node.position.y - offsetY,
|
|
178
192
|
};
|
|
179
193
|
};
|
|
180
194
|
/**
|
|
@@ -199,16 +213,14 @@ const getNodesBounds = (nodes, params = { nodeOrigin: [0, 0] }) => {
|
|
|
199
213
|
* Determines a bounding box that contains all given nodes in an array
|
|
200
214
|
* @internal
|
|
201
215
|
*/
|
|
202
|
-
const getInternalNodesBounds = (nodeLookup, params = {
|
|
203
|
-
nodeOrigin: [0, 0],
|
|
204
|
-
}) => {
|
|
216
|
+
const getInternalNodesBounds = (nodeLookup, params = {}) => {
|
|
205
217
|
if (nodeLookup.size === 0) {
|
|
206
218
|
return { x: 0, y: 0, width: 0, height: 0 };
|
|
207
219
|
}
|
|
208
220
|
let box = { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity };
|
|
209
221
|
nodeLookup.forEach((node) => {
|
|
210
|
-
if (params.filter
|
|
211
|
-
const nodeBox = nodeToBox(node
|
|
222
|
+
if (params.filter === undefined || params.filter(node)) {
|
|
223
|
+
const nodeBox = nodeToBox(node);
|
|
212
224
|
box = getBoundsOfBoxes(box, nodeBox);
|
|
213
225
|
}
|
|
214
226
|
});
|
|
@@ -216,7 +228,7 @@ const getInternalNodesBounds = (nodeLookup, params = {
|
|
|
216
228
|
};
|
|
217
229
|
const getNodesInside = (nodes, rect, [tx, ty, tScale] = [0, 0, 1], partially = false,
|
|
218
230
|
// set excludeNonSelectableNodes if you want to pay attention to the nodes "selectable" attribute
|
|
219
|
-
excludeNonSelectableNodes = false
|
|
231
|
+
excludeNonSelectableNodes = false) => {
|
|
220
232
|
const paneRect = {
|
|
221
233
|
...pointToRendererPoint(rect, [tx, ty, tScale]),
|
|
222
234
|
width: rect.width / tScale,
|
|
@@ -230,7 +242,7 @@ excludeNonSelectableNodes = false, nodeOrigin = [0, 0]) => {
|
|
|
230
242
|
if ((excludeNonSelectableNodes && !selectable) || hidden) {
|
|
231
243
|
continue;
|
|
232
244
|
}
|
|
233
|
-
const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node
|
|
245
|
+
const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node));
|
|
234
246
|
const notInitialized = width === null || height === null;
|
|
235
247
|
const partiallyVisible = partially && overlappingArea > 0;
|
|
236
248
|
const area = (width ?? 0) * (height ?? 0);
|
|
@@ -254,17 +266,17 @@ const getConnectedEdges = (nodes, edges) => {
|
|
|
254
266
|
});
|
|
255
267
|
return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target));
|
|
256
268
|
};
|
|
257
|
-
function fitView({ nodeLookup, width, height, panZoom, minZoom, maxZoom
|
|
258
|
-
const filteredNodes =
|
|
269
|
+
function fitView({ nodeLookup, width, height, panZoom, minZoom, maxZoom }, options) {
|
|
270
|
+
const filteredNodes = new Map();
|
|
259
271
|
const optionNodeIds = options?.nodes ? new Set(options.nodes.map((node) => node.id)) : null;
|
|
260
272
|
nodeLookup.forEach((n) => {
|
|
261
273
|
const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden);
|
|
262
274
|
if (isVisible && (!optionNodeIds || optionNodeIds.has(n.id))) {
|
|
263
|
-
filteredNodes.
|
|
275
|
+
filteredNodes.set(n.id, n);
|
|
264
276
|
}
|
|
265
277
|
});
|
|
266
|
-
if (filteredNodes.
|
|
267
|
-
const bounds =
|
|
278
|
+
if (filteredNodes.size > 0) {
|
|
279
|
+
const bounds = getInternalNodesBounds(filteredNodes);
|
|
268
280
|
const viewport = getViewportForBounds(bounds, width, height, options?.minZoom ?? minZoom, options?.maxZoom ?? maxZoom, options?.padding ?? 0.1);
|
|
269
281
|
panZoom.setViewport(viewport, { duration: options?.duration });
|
|
270
282
|
return true;
|
|
@@ -294,9 +306,8 @@ function clampNodeExtent(node, extent) {
|
|
|
294
306
|
function calculateNodePosition({ nodeId, nextPosition, nodeLookup, nodeOrigin = [0, 0], nodeExtent, onError, }) {
|
|
295
307
|
const node = nodeLookup.get(nodeId);
|
|
296
308
|
const parentNode = node.parentId ? nodeLookup.get(node.parentId) : undefined;
|
|
297
|
-
const { x: parentX, y: parentY } = parentNode
|
|
298
|
-
|
|
299
|
-
: { x: 0, y: 0 };
|
|
309
|
+
const { x: parentX, y: parentY } = parentNode ? parentNode.internals.positionAbsolute : { x: 0, y: 0 };
|
|
310
|
+
const origin = node.origin ?? nodeOrigin;
|
|
300
311
|
let currentExtent = clampNodeExtent(node, node.extent || nodeExtent);
|
|
301
312
|
if (node.extent === 'parent' && !node.expandParent) {
|
|
302
313
|
if (!parentNode) {
|
|
@@ -308,12 +319,9 @@ function calculateNodePosition({ nodeId, nextPosition, nodeLookup, nodeOrigin =
|
|
|
308
319
|
const parentWidth = parentNode.measured.width;
|
|
309
320
|
const parentHeight = parentNode.measured.height;
|
|
310
321
|
if (nodeWidth && nodeHeight && parentWidth && parentHeight) {
|
|
311
|
-
const currNodeOrigin = node.origin || nodeOrigin;
|
|
312
|
-
const extentX = parentX + nodeWidth * currNodeOrigin[0];
|
|
313
|
-
const extentY = parentY + nodeHeight * currNodeOrigin[1];
|
|
314
322
|
currentExtent = [
|
|
315
|
-
[
|
|
316
|
-
[
|
|
323
|
+
[parentX, parentY],
|
|
324
|
+
[parentX + parentWidth - nodeWidth, parentY + parentHeight - nodeHeight],
|
|
317
325
|
];
|
|
318
326
|
}
|
|
319
327
|
}
|
|
@@ -329,8 +337,9 @@ function calculateNodePosition({ nodeId, nextPosition, nodeLookup, nodeOrigin =
|
|
|
329
337
|
: nextPosition;
|
|
330
338
|
return {
|
|
331
339
|
position: {
|
|
332
|
-
|
|
333
|
-
|
|
340
|
+
// TODO: is there a better way to do this?
|
|
341
|
+
x: positionAbsolute.x - parentX + node.measured.width * origin[0],
|
|
342
|
+
y: positionAbsolute.y - parentY + node.measured.height * origin[1],
|
|
334
343
|
},
|
|
335
344
|
positionAbsolute,
|
|
336
345
|
};
|
|
@@ -399,16 +408,16 @@ const clampPosition = (position = { x: 0, y: 0 }, extent) => ({
|
|
|
399
408
|
*/
|
|
400
409
|
const calcAutoPanVelocity = (value, min, max) => {
|
|
401
410
|
if (value < min) {
|
|
402
|
-
return clamp(Math.abs(value - min), 1,
|
|
411
|
+
return clamp(Math.abs(value - min), 1, min) / min;
|
|
403
412
|
}
|
|
404
413
|
else if (value > max) {
|
|
405
|
-
return -clamp(Math.abs(value - max), 1,
|
|
414
|
+
return -clamp(Math.abs(value - max), 1, min) / min;
|
|
406
415
|
}
|
|
407
416
|
return 0;
|
|
408
417
|
};
|
|
409
|
-
const calcAutoPan = (pos, bounds) => {
|
|
410
|
-
const xMovement = calcAutoPanVelocity(pos.x,
|
|
411
|
-
const yMovement = calcAutoPanVelocity(pos.y,
|
|
418
|
+
const calcAutoPan = (pos, bounds, speed = 15, distance = 40) => {
|
|
419
|
+
const xMovement = calcAutoPanVelocity(pos.x, distance, bounds.width - distance) * speed;
|
|
420
|
+
const yMovement = calcAutoPanVelocity(pos.y, distance, bounds.height - distance) * speed;
|
|
412
421
|
return [xMovement, yMovement];
|
|
413
422
|
};
|
|
414
423
|
const getBoundsOfBoxes = (box1, box2) => ({
|
|
@@ -430,21 +439,25 @@ const boxToRect = ({ x, y, x2, y2 }) => ({
|
|
|
430
439
|
height: y2 - y,
|
|
431
440
|
});
|
|
432
441
|
const nodeToRect = (node, nodeOrigin = [0, 0]) => {
|
|
433
|
-
const { x, y } =
|
|
442
|
+
const { x, y } = isInternalNodeBase(node)
|
|
443
|
+
? node.internals.positionAbsolute
|
|
444
|
+
: getNodePositionWithOrigin(node, nodeOrigin);
|
|
434
445
|
return {
|
|
435
446
|
x,
|
|
436
447
|
y,
|
|
437
|
-
width: node.measured?.width ?? node.width ?? 0,
|
|
438
|
-
height: node.measured?.height ?? node.height ?? 0,
|
|
448
|
+
width: node.measured?.width ?? node.width ?? node.initialWidth ?? 0,
|
|
449
|
+
height: node.measured?.height ?? node.height ?? node.initialHeight ?? 0,
|
|
439
450
|
};
|
|
440
451
|
};
|
|
441
452
|
const nodeToBox = (node, nodeOrigin = [0, 0]) => {
|
|
442
|
-
const { x, y } =
|
|
453
|
+
const { x, y } = isInternalNodeBase(node)
|
|
454
|
+
? node.internals.positionAbsolute
|
|
455
|
+
: getNodePositionWithOrigin(node, nodeOrigin);
|
|
443
456
|
return {
|
|
444
457
|
x,
|
|
445
458
|
y,
|
|
446
|
-
x2: x + (node.measured?.width ?? node.width ?? 0),
|
|
447
|
-
y2: y + (node.measured?.height ?? node.height ?? 0),
|
|
459
|
+
x2: x + (node.measured?.width ?? node.width ?? node.initialWidth ?? 0),
|
|
460
|
+
y2: y + (node.measured?.height ?? node.height ?? node.initialHeight ?? 0),
|
|
448
461
|
};
|
|
449
462
|
};
|
|
450
463
|
const getBoundsOfRects = (rect1, rect2) => boxToRect(getBoundsOfBoxes(rectToBox(rect1), rectToBox(rect2)));
|
|
@@ -463,15 +476,6 @@ const devWarn = (id, message) => {
|
|
|
463
476
|
console.warn(`[React Flow]: ${message} Help: https://reactflow.dev/error#${id}`);
|
|
464
477
|
}
|
|
465
478
|
};
|
|
466
|
-
const getPositionWithOrigin = ({ x, y, width, height, origin = [0, 0], }) => {
|
|
467
|
-
if (!width || !height || origin[0] < 0 || origin[1] < 0 || origin[0] > 1 || origin[1] > 1) {
|
|
468
|
-
return { x, y };
|
|
469
|
-
}
|
|
470
|
-
return {
|
|
471
|
-
x: x - width * origin[0],
|
|
472
|
-
y: y - height * origin[1],
|
|
473
|
-
};
|
|
474
|
-
};
|
|
475
479
|
const snapPosition = (position, snapGrid = [1, 1]) => {
|
|
476
480
|
return {
|
|
477
481
|
x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
|
|
@@ -542,7 +546,7 @@ function nodeHasDimensions(node) {
|
|
|
542
546
|
* @param nodeOrigin
|
|
543
547
|
* @returns an internal node with an absolute position
|
|
544
548
|
*/
|
|
545
|
-
function evaluateAbsolutePosition(position,
|
|
549
|
+
function evaluateAbsolutePosition(position, dimensions = { width: 0, height: 0 }, parentId, nodeLookup, nodeOrigin) {
|
|
546
550
|
let nextParentId = parentId;
|
|
547
551
|
const positionAbsolute = { ...position };
|
|
548
552
|
while (nextParentId) {
|
|
@@ -550,10 +554,8 @@ function evaluateAbsolutePosition(position, parentId, nodeLookup, nodeOrigin = [
|
|
|
550
554
|
nextParentId = parent?.parentId;
|
|
551
555
|
if (parent) {
|
|
552
556
|
const origin = parent.origin || nodeOrigin;
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
positionAbsolute.x += parent.position.x - xOffset;
|
|
556
|
-
positionAbsolute.y += parent.position.y - yOffset;
|
|
557
|
+
positionAbsolute.x += parent.internals.positionAbsolute.x - (dimensions.width ?? 0) * origin[0];
|
|
558
|
+
positionAbsolute.y += parent.internals.positionAbsolute.y - (dimensions.height ?? 0) * origin[1];
|
|
557
559
|
}
|
|
558
560
|
}
|
|
559
561
|
return positionAbsolute;
|
|
@@ -596,23 +598,20 @@ const getEventPosition = (event, bounds) => {
|
|
|
596
598
|
// The handle bounds are calculated relative to the node element.
|
|
597
599
|
// We store them in the internals object of the node in order to avoid
|
|
598
600
|
// unnecessary recalculations.
|
|
599
|
-
const getHandleBounds = (
|
|
600
|
-
const handles = nodeElement.querySelectorAll(
|
|
601
|
+
const getHandleBounds = (type, nodeElement, nodeBounds, zoom, nodeId) => {
|
|
602
|
+
const handles = nodeElement.querySelectorAll(`.${type}`);
|
|
601
603
|
if (!handles || !handles.length) {
|
|
602
604
|
return null;
|
|
603
605
|
}
|
|
604
|
-
|
|
605
|
-
const nodeOffset = {
|
|
606
|
-
x: nodeBounds.left + nodeBounds.width * nodeOrigin[0],
|
|
607
|
-
y: nodeBounds.top + nodeBounds.height * nodeOrigin[1],
|
|
608
|
-
};
|
|
609
|
-
return handlesArray.map((handle) => {
|
|
606
|
+
return Array.from(handles).map((handle) => {
|
|
610
607
|
const handleBounds = handle.getBoundingClientRect();
|
|
611
608
|
return {
|
|
612
609
|
id: handle.getAttribute('data-handleid'),
|
|
610
|
+
type,
|
|
611
|
+
nodeId,
|
|
613
612
|
position: handle.getAttribute('data-handlepos'),
|
|
614
|
-
x: (handleBounds.left -
|
|
615
|
-
y: (handleBounds.top -
|
|
613
|
+
x: (handleBounds.left - nodeBounds.left) / zoom,
|
|
614
|
+
y: (handleBounds.top - nodeBounds.top) / zoom,
|
|
616
615
|
...getDimensions(handle),
|
|
617
616
|
};
|
|
618
617
|
});
|
|
@@ -1049,13 +1048,13 @@ function getEdgePosition(params) {
|
|
|
1049
1048
|
}
|
|
1050
1049
|
const sourcePosition = sourceHandle?.position || Position.Bottom;
|
|
1051
1050
|
const targetPosition = targetHandle?.position || Position.Top;
|
|
1052
|
-
const
|
|
1053
|
-
const
|
|
1051
|
+
const source = getHandlePosition(sourceNode, sourceHandle, sourcePosition);
|
|
1052
|
+
const target = getHandlePosition(targetNode, targetHandle, targetPosition);
|
|
1054
1053
|
return {
|
|
1055
|
-
sourceX,
|
|
1056
|
-
sourceY,
|
|
1057
|
-
targetX,
|
|
1058
|
-
targetY,
|
|
1054
|
+
sourceX: source.x,
|
|
1055
|
+
sourceY: source.y,
|
|
1056
|
+
targetX: target.x,
|
|
1057
|
+
targetY: target.y,
|
|
1059
1058
|
sourcePosition,
|
|
1060
1059
|
targetPosition,
|
|
1061
1060
|
};
|
|
@@ -1081,20 +1080,23 @@ function toHandleBounds(handles) {
|
|
|
1081
1080
|
target,
|
|
1082
1081
|
};
|
|
1083
1082
|
}
|
|
1084
|
-
function getHandlePosition(node, handle, fallbackPosition = Position.Left) {
|
|
1083
|
+
function getHandlePosition(node, handle, fallbackPosition = Position.Left, center = false) {
|
|
1085
1084
|
const x = (handle?.x ?? 0) + node.internals.positionAbsolute.x;
|
|
1086
1085
|
const y = (handle?.y ?? 0) + node.internals.positionAbsolute.y;
|
|
1087
1086
|
const { width, height } = handle ?? getNodeDimensions(node);
|
|
1087
|
+
if (center) {
|
|
1088
|
+
return { x: x + width / 2, y: y + height / 2 };
|
|
1089
|
+
}
|
|
1088
1090
|
const position = handle?.position ?? fallbackPosition;
|
|
1089
1091
|
switch (position) {
|
|
1090
1092
|
case Position.Top:
|
|
1091
|
-
return
|
|
1093
|
+
return { x: x + width / 2, y };
|
|
1092
1094
|
case Position.Right:
|
|
1093
|
-
return
|
|
1095
|
+
return { x: x + width, y: y + height / 2 };
|
|
1094
1096
|
case Position.Bottom:
|
|
1095
|
-
return
|
|
1097
|
+
return { x: x + width / 2, y: y + height };
|
|
1096
1098
|
case Position.Left:
|
|
1097
|
-
return
|
|
1099
|
+
return { x, y: y + height / 2 };
|
|
1098
1100
|
}
|
|
1099
1101
|
}
|
|
1100
1102
|
function getHandle(bounds, handleId) {
|
|
@@ -1175,95 +1177,98 @@ function getNodeToolbarTransform(nodeRect, viewport, position, offset, align) {
|
|
|
1175
1177
|
return `translate(${pos[0]}px, ${pos[1]}px) translate(${shift[0]}%, ${shift[1]}%)`;
|
|
1176
1178
|
}
|
|
1177
1179
|
|
|
1178
|
-
|
|
1180
|
+
const defaultOptions = {
|
|
1179
1181
|
nodeOrigin: [0, 0],
|
|
1180
1182
|
elevateNodesOnSelect: true,
|
|
1181
1183
|
defaults: {},
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1184
|
+
};
|
|
1185
|
+
const adoptUserNodesDefaultOptions = {
|
|
1186
|
+
...defaultOptions,
|
|
1187
|
+
checkEquality: true,
|
|
1188
|
+
};
|
|
1189
|
+
function updateAbsolutePositions(nodeLookup, parentLookup, options) {
|
|
1190
|
+
const _options = { ...defaultOptions, ...options };
|
|
1191
|
+
for (const node of nodeLookup.values()) {
|
|
1192
|
+
if (!node.parentId) {
|
|
1187
1193
|
continue;
|
|
1188
1194
|
}
|
|
1189
|
-
|
|
1190
|
-
throw new Error(`Parent node ${parentId} not found`);
|
|
1191
|
-
}
|
|
1192
|
-
const parentNode = nodeLookup.get(parentId);
|
|
1193
|
-
const { x, y, z } = calculateXYZPosition(node, nodeLookup, {
|
|
1194
|
-
...node.position,
|
|
1195
|
-
z: (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0),
|
|
1196
|
-
}, parentNode?.origin ?? options.nodeOrigin);
|
|
1197
|
-
const currPosition = node.internals.positionAbsolute;
|
|
1198
|
-
const positionChanged = x !== currPosition.x || y !== currPosition.y;
|
|
1199
|
-
if (positionChanged || z !== node.internals.z) {
|
|
1200
|
-
node.internals = {
|
|
1201
|
-
...node.internals,
|
|
1202
|
-
positionAbsolute: positionChanged ? { x, y } : currPosition,
|
|
1203
|
-
z,
|
|
1204
|
-
};
|
|
1205
|
-
}
|
|
1195
|
+
updateChildPosition(node, nodeLookup, parentLookup, _options);
|
|
1206
1196
|
}
|
|
1207
1197
|
}
|
|
1208
|
-
function adoptUserNodes(nodes, nodeLookup, parentLookup, options
|
|
1209
|
-
|
|
1210
|
-
elevateNodesOnSelect: true,
|
|
1211
|
-
defaults: {},
|
|
1212
|
-
checkEquality: true,
|
|
1213
|
-
}) {
|
|
1198
|
+
function adoptUserNodes(nodes, nodeLookup, parentLookup, options) {
|
|
1199
|
+
const _options = { ...adoptUserNodesDefaultOptions, ...options };
|
|
1214
1200
|
const tmpLookup = new Map(nodeLookup);
|
|
1215
1201
|
nodeLookup.clear();
|
|
1216
1202
|
parentLookup.clear();
|
|
1217
1203
|
const selectedNodeZ = options?.elevateNodesOnSelect ? 1000 : 0;
|
|
1218
|
-
|
|
1204
|
+
for (const userNode of nodes) {
|
|
1219
1205
|
let internalNode = tmpLookup.get(userNode.id);
|
|
1220
|
-
if (
|
|
1206
|
+
if (_options.checkEquality && userNode === internalNode?.internals.userNode) {
|
|
1221
1207
|
nodeLookup.set(userNode.id, internalNode);
|
|
1222
1208
|
}
|
|
1223
1209
|
else {
|
|
1224
1210
|
internalNode = {
|
|
1225
|
-
...
|
|
1211
|
+
..._options.defaults,
|
|
1226
1212
|
...userNode,
|
|
1227
1213
|
measured: {
|
|
1228
1214
|
width: userNode.measured?.width,
|
|
1229
1215
|
height: userNode.measured?.height,
|
|
1230
1216
|
},
|
|
1231
1217
|
internals: {
|
|
1232
|
-
positionAbsolute: userNode.
|
|
1218
|
+
positionAbsolute: getNodePositionWithOrigin(userNode, _options.nodeOrigin),
|
|
1233
1219
|
handleBounds: internalNode?.internals.handleBounds,
|
|
1234
|
-
z: (
|
|
1220
|
+
z: calculateZ(userNode, selectedNodeZ),
|
|
1235
1221
|
userNode,
|
|
1236
1222
|
},
|
|
1237
1223
|
};
|
|
1238
1224
|
nodeLookup.set(userNode.id, internalNode);
|
|
1239
1225
|
}
|
|
1240
1226
|
if (userNode.parentId) {
|
|
1241
|
-
|
|
1242
|
-
if (childNodes) {
|
|
1243
|
-
childNodes.push(internalNode);
|
|
1244
|
-
}
|
|
1245
|
-
else {
|
|
1246
|
-
parentLookup.set(userNode.parentId, [internalNode]);
|
|
1247
|
-
}
|
|
1227
|
+
updateChildPosition(internalNode, nodeLookup, parentLookup, options);
|
|
1248
1228
|
}
|
|
1249
|
-
});
|
|
1250
|
-
if (parentLookup.size > 0) {
|
|
1251
|
-
updateAbsolutePositions(nodeLookup, options);
|
|
1252
1229
|
}
|
|
1253
1230
|
}
|
|
1254
|
-
function
|
|
1255
|
-
|
|
1256
|
-
|
|
1231
|
+
function updateChildPosition(node, nodeLookup, parentLookup, options) {
|
|
1232
|
+
const _options = { ...defaultOptions, ...options };
|
|
1233
|
+
const parentId = node.parentId;
|
|
1234
|
+
const parentNode = nodeLookup.get(parentId);
|
|
1235
|
+
if (!parentNode) {
|
|
1236
|
+
throw new Error(`Parent node ${parentId} not found`);
|
|
1237
|
+
}
|
|
1238
|
+
// update the parentLookup
|
|
1239
|
+
const childNodes = parentLookup.get(parentId);
|
|
1240
|
+
if (childNodes) {
|
|
1241
|
+
childNodes.set(node.id, node);
|
|
1242
|
+
}
|
|
1243
|
+
else {
|
|
1244
|
+
parentLookup.set(parentId, new Map([[node.id, node]]));
|
|
1245
|
+
}
|
|
1246
|
+
const selectedNodeZ = options?.elevateNodesOnSelect ? 1000 : 0;
|
|
1247
|
+
const { x, y, z } = calculateChildXYZ(node, parentNode, _options.nodeOrigin, selectedNodeZ);
|
|
1248
|
+
const currPosition = node.internals.positionAbsolute;
|
|
1249
|
+
const positionChanged = x !== currPosition.x || y !== currPosition.y;
|
|
1250
|
+
if (positionChanged || z !== node.internals.z) {
|
|
1251
|
+
node.internals = {
|
|
1252
|
+
...node.internals,
|
|
1253
|
+
positionAbsolute: positionChanged ? { x, y } : currPosition,
|
|
1254
|
+
z,
|
|
1255
|
+
};
|
|
1257
1256
|
}
|
|
1258
|
-
const parent = nodeLookup.get(node.parentId);
|
|
1259
|
-
const parentPosition = getNodePositionWithOrigin(parent, nodeOrigin).position;
|
|
1260
|
-
return calculateXYZPosition(parent, nodeLookup, {
|
|
1261
|
-
x: (result.x ?? 0) + parentPosition.x,
|
|
1262
|
-
y: (result.y ?? 0) + parentPosition.y,
|
|
1263
|
-
z: (parent.internals.z ?? 0) > (result.z ?? 0) ? parent.internals.z ?? 0 : result.z ?? 0,
|
|
1264
|
-
}, parent.origin || nodeOrigin);
|
|
1265
1257
|
}
|
|
1266
|
-
function
|
|
1258
|
+
function calculateZ(node, selectedNodeZ) {
|
|
1259
|
+
return (isNumeric(node.zIndex) ? node.zIndex : 0) + (node.selected ? selectedNodeZ : 0);
|
|
1260
|
+
}
|
|
1261
|
+
function calculateChildXYZ(childNode, parentNode, nodeOrigin, selectedNodeZ) {
|
|
1262
|
+
const position = getNodePositionWithOrigin(childNode, nodeOrigin);
|
|
1263
|
+
const childZ = calculateZ(childNode, selectedNodeZ);
|
|
1264
|
+
const parentZ = parentNode.internals.z ?? 0;
|
|
1265
|
+
return {
|
|
1266
|
+
x: parentNode.internals.positionAbsolute.x + position.x,
|
|
1267
|
+
y: parentNode.internals.positionAbsolute.y + position.y,
|
|
1268
|
+
z: parentZ > childZ ? parentZ : childZ,
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
function handleExpandParent(children, nodeLookup, parentLookup, nodeOrigin = [0, 0]) {
|
|
1267
1272
|
const changes = [];
|
|
1268
1273
|
const parentExpansions = new Map();
|
|
1269
1274
|
// determine the expanded rectangle the child nodes would take for each parent
|
|
@@ -1272,31 +1277,36 @@ function handleExpandParent(children, nodeLookup, parentLookup, nodeOrigin) {
|
|
|
1272
1277
|
if (!parent) {
|
|
1273
1278
|
continue;
|
|
1274
1279
|
}
|
|
1275
|
-
const parentRect = parentExpansions.get(child.parentId)?.expandedRect ?? nodeToRect(parent
|
|
1280
|
+
const parentRect = parentExpansions.get(child.parentId)?.expandedRect ?? nodeToRect(parent);
|
|
1276
1281
|
const expandedRect = getBoundsOfRects(parentRect, child.rect);
|
|
1277
1282
|
parentExpansions.set(child.parentId, { expandedRect, parent });
|
|
1278
1283
|
}
|
|
1279
1284
|
if (parentExpansions.size > 0) {
|
|
1280
1285
|
parentExpansions.forEach(({ expandedRect, parent }, parentId) => {
|
|
1281
1286
|
// determine the position & dimensions of the parent
|
|
1282
|
-
const
|
|
1287
|
+
const positionAbsolute = parent.internals.positionAbsolute;
|
|
1283
1288
|
const dimensions = getNodeDimensions(parent);
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
const
|
|
1287
|
-
|
|
1289
|
+
const origin = parent.origin ?? nodeOrigin;
|
|
1290
|
+
// determine how much the parent expands in width and position
|
|
1291
|
+
const xChange = expandedRect.x < positionAbsolute.x ? Math.round(Math.abs(positionAbsolute.x - expandedRect.x)) : 0;
|
|
1292
|
+
const yChange = expandedRect.y < positionAbsolute.y ? Math.round(Math.abs(positionAbsolute.y - expandedRect.y)) : 0;
|
|
1293
|
+
const newWidth = Math.max(dimensions.width, Math.round(expandedRect.width));
|
|
1294
|
+
const newHeight = Math.max(dimensions.height, Math.round(expandedRect.height));
|
|
1295
|
+
const widthChange = (newWidth - dimensions.width) * origin[0];
|
|
1296
|
+
const heightChange = (newHeight - dimensions.height) * origin[1];
|
|
1297
|
+
// We need to correct the position of the parent node if the origin is not [0,0]
|
|
1298
|
+
if (xChange > 0 || yChange > 0 || widthChange || heightChange) {
|
|
1288
1299
|
changes.push({
|
|
1289
1300
|
id: parentId,
|
|
1290
1301
|
type: 'position',
|
|
1291
1302
|
position: {
|
|
1292
|
-
x: position.x - xChange,
|
|
1293
|
-
y: position.y - yChange,
|
|
1303
|
+
x: parent.position.x - xChange + widthChange,
|
|
1304
|
+
y: parent.position.y - yChange + heightChange,
|
|
1294
1305
|
},
|
|
1295
1306
|
});
|
|
1296
1307
|
// We move all child nodes in the oppsite direction
|
|
1297
1308
|
// so the x,y changes of the parent do not move the children
|
|
1298
|
-
|
|
1299
|
-
childNodes?.forEach((childNode) => {
|
|
1309
|
+
parentLookup.get(parentId)?.forEach((childNode) => {
|
|
1300
1310
|
if (!children.some((child) => child.id === childNode.id)) {
|
|
1301
1311
|
changes.push({
|
|
1302
1312
|
id: childNode.id,
|
|
@@ -1309,14 +1319,15 @@ function handleExpandParent(children, nodeLookup, parentLookup, nodeOrigin) {
|
|
|
1309
1319
|
}
|
|
1310
1320
|
});
|
|
1311
1321
|
}
|
|
1312
|
-
|
|
1322
|
+
// We need to correct the dimensions of the parent node if the origin is not [0,0]
|
|
1323
|
+
if (dimensions.width < expandedRect.width || dimensions.height < expandedRect.height || xChange || yChange) {
|
|
1313
1324
|
changes.push({
|
|
1314
1325
|
id: parentId,
|
|
1315
1326
|
type: 'dimensions',
|
|
1316
1327
|
setAttributes: true,
|
|
1317
1328
|
dimensions: {
|
|
1318
|
-
width:
|
|
1319
|
-
height:
|
|
1329
|
+
width: newWidth + (xChange ? origin[0] * xChange - widthChange : 0),
|
|
1330
|
+
height: newHeight + (yChange ? origin[1] * yChange - heightChange : 0),
|
|
1320
1331
|
},
|
|
1321
1332
|
});
|
|
1322
1333
|
}
|
|
@@ -1335,16 +1346,19 @@ function updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOri
|
|
|
1335
1346
|
const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform);
|
|
1336
1347
|
// in this array we collect nodes, that might trigger changes (like expanding parent)
|
|
1337
1348
|
const parentExpandChildren = [];
|
|
1338
|
-
updates.
|
|
1349
|
+
for (const update of updates.values()) {
|
|
1339
1350
|
const node = nodeLookup.get(update.id);
|
|
1340
|
-
if (node
|
|
1351
|
+
if (!node) {
|
|
1352
|
+
continue;
|
|
1353
|
+
}
|
|
1354
|
+
if (node.hidden) {
|
|
1341
1355
|
node.internals = {
|
|
1342
1356
|
...node.internals,
|
|
1343
1357
|
handleBounds: undefined,
|
|
1344
1358
|
};
|
|
1345
1359
|
updatedInternals = true;
|
|
1346
1360
|
}
|
|
1347
|
-
else
|
|
1361
|
+
else {
|
|
1348
1362
|
const dimensions = getDimensions(update.nodeElement);
|
|
1349
1363
|
const dimensionChanged = node.measured.width !== dimensions.width || node.measured.height !== dimensions.height;
|
|
1350
1364
|
const doUpdate = !!(dimensions.width &&
|
|
@@ -1355,11 +1369,15 @@ function updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOri
|
|
|
1355
1369
|
node.measured = dimensions;
|
|
1356
1370
|
node.internals = {
|
|
1357
1371
|
...node.internals,
|
|
1372
|
+
positionAbsolute: getNodePositionWithOrigin(node, nodeOrigin),
|
|
1358
1373
|
handleBounds: {
|
|
1359
|
-
source: getHandleBounds('
|
|
1360
|
-
target: getHandleBounds('
|
|
1374
|
+
source: getHandleBounds('source', update.nodeElement, nodeBounds, zoom, node.id),
|
|
1375
|
+
target: getHandleBounds('target', update.nodeElement, nodeBounds, zoom, node.id),
|
|
1361
1376
|
},
|
|
1362
1377
|
};
|
|
1378
|
+
if (node.parentId) {
|
|
1379
|
+
updateChildPosition(node, nodeLookup, parentLookup, { nodeOrigin });
|
|
1380
|
+
}
|
|
1363
1381
|
updatedInternals = true;
|
|
1364
1382
|
if (dimensionChanged) {
|
|
1365
1383
|
changes.push({
|
|
@@ -1377,7 +1395,7 @@ function updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOri
|
|
|
1377
1395
|
}
|
|
1378
1396
|
}
|
|
1379
1397
|
}
|
|
1380
|
-
}
|
|
1398
|
+
}
|
|
1381
1399
|
if (parentExpandChildren.length > 0) {
|
|
1382
1400
|
const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup, nodeOrigin);
|
|
1383
1401
|
changes.push(...parentExpandChanges);
|
|
@@ -1537,7 +1555,7 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
|
|
|
1537
1555
|
let hasChange = false;
|
|
1538
1556
|
let nodesBox = { x: 0, y: 0, x2: 0, y2: 0 };
|
|
1539
1557
|
if (dragItems.size > 1 && nodeExtent) {
|
|
1540
|
-
const rect = getInternalNodesBounds(dragItems
|
|
1558
|
+
const rect = getInternalNodesBounds(dragItems);
|
|
1541
1559
|
nodesBox = rectToBox(rect);
|
|
1542
1560
|
}
|
|
1543
1561
|
for (const [id, dragItem] of dragItems) {
|
|
@@ -1596,9 +1614,9 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
|
|
|
1596
1614
|
if (!containerBounds) {
|
|
1597
1615
|
return;
|
|
1598
1616
|
}
|
|
1599
|
-
const
|
|
1617
|
+
const { transform, panBy, autoPanSpeed } = getStoreItems();
|
|
1618
|
+
const [xMovement, yMovement] = calcAutoPan(mousePosition, containerBounds, autoPanSpeed);
|
|
1600
1619
|
if (xMovement !== 0 || yMovement !== 0) {
|
|
1601
|
-
const { transform, panBy } = getStoreItems();
|
|
1602
1620
|
lastPos.x = (lastPos.x ?? 0) - xMovement / transform[2];
|
|
1603
1621
|
lastPos.y = (lastPos.y ?? 0) - yMovement / transform[2];
|
|
1604
1622
|
if (panBy({ x: xMovement, y: yMovement })) {
|
|
@@ -1720,19 +1738,18 @@ function XYDrag({ onNodeMouseDown, getStoreItems, onDragStart, onDrag, onDragSto
|
|
|
1720
1738
|
// this functions collects all handles and adds an absolute position
|
|
1721
1739
|
// so that we can later find the closest handle to the mouse position
|
|
1722
1740
|
function getHandles(node, handleBounds, type, currentHandle) {
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
y,
|
|
1732
|
-
});
|
|
1741
|
+
let excludedHandle = null;
|
|
1742
|
+
const handles = (handleBounds[type] || []).reduce((res, handle) => {
|
|
1743
|
+
if (node.id === currentHandle.nodeId && type === currentHandle.handleType && handle.id === currentHandle.handleId) {
|
|
1744
|
+
excludedHandle = handle;
|
|
1745
|
+
}
|
|
1746
|
+
else {
|
|
1747
|
+
const handleXY = getHandlePosition(node, handle, handle.position, true);
|
|
1748
|
+
res.push({ ...handle, ...handleXY });
|
|
1733
1749
|
}
|
|
1734
1750
|
return res;
|
|
1735
1751
|
}, []);
|
|
1752
|
+
return [handles, excludedHandle];
|
|
1736
1753
|
}
|
|
1737
1754
|
function getClosestHandle(pos, connectionRadius, handles) {
|
|
1738
1755
|
let closestHandles = [];
|
|
@@ -1760,15 +1777,17 @@ function getClosestHandle(pos, connectionRadius, handles) {
|
|
|
1760
1777
|
}
|
|
1761
1778
|
function getHandleLookup({ nodeLookup, nodeId, handleId, handleType, }) {
|
|
1762
1779
|
const connectionHandles = [];
|
|
1763
|
-
|
|
1780
|
+
const currentHandle = { nodeId, handleId, handleType };
|
|
1781
|
+
let excludedHandle = null;
|
|
1782
|
+
for (const node of nodeLookup.values()) {
|
|
1764
1783
|
if (node.internals.handleBounds) {
|
|
1765
|
-
const
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1784
|
+
const [sourceHandles, excludedSource] = getHandles(node, node.internals.handleBounds, 'source', currentHandle);
|
|
1785
|
+
const [targetHandles, excludedTarget] = getHandles(node, node.internals.handleBounds, 'target', currentHandle);
|
|
1786
|
+
excludedHandle = excludedHandle ? excludedHandle : excludedSource ?? excludedTarget;
|
|
1768
1787
|
connectionHandles.push(...sourceHandles, ...targetHandles);
|
|
1769
1788
|
}
|
|
1770
1789
|
}
|
|
1771
|
-
return connectionHandles;
|
|
1790
|
+
return [connectionHandles, excludedHandle];
|
|
1772
1791
|
}
|
|
1773
1792
|
function getHandleType(edgeUpdaterType, handleDomNode) {
|
|
1774
1793
|
if (edgeUpdaterType) {
|
|
@@ -1782,20 +1801,19 @@ function getHandleType(edgeUpdaterType, handleDomNode) {
|
|
|
1782
1801
|
}
|
|
1783
1802
|
return null;
|
|
1784
1803
|
}
|
|
1785
|
-
function
|
|
1786
|
-
let
|
|
1804
|
+
function isConnectionValid(isInsideConnectionRadius, isHandleValid) {
|
|
1805
|
+
let isValid = null;
|
|
1787
1806
|
if (isHandleValid) {
|
|
1788
|
-
|
|
1807
|
+
isValid = true;
|
|
1789
1808
|
}
|
|
1790
1809
|
else if (isInsideConnectionRadius && !isHandleValid) {
|
|
1791
|
-
|
|
1810
|
+
isValid = false;
|
|
1792
1811
|
}
|
|
1793
|
-
return
|
|
1812
|
+
return isValid;
|
|
1794
1813
|
}
|
|
1795
1814
|
|
|
1796
1815
|
const alwaysValid = () => true;
|
|
1797
|
-
|
|
1798
|
-
function onPointerDown(event, { connectionMode, connectionRadius, handleId, nodeId, edgeUpdaterType, isTarget, domNode, nodeLookup, lib, autoPanOnConnect, flowId, panBy, cancelConnection, onConnectStart, onConnect, onConnectEnd, isValidConnection = alwaysValid, onReconnectEnd, updateConnection, getTransform, getConnectionStartHandle, }) {
|
|
1816
|
+
function onPointerDown(event, { connectionMode, connectionRadius, handleId, nodeId, edgeUpdaterType, isTarget, domNode, nodeLookup, lib, autoPanOnConnect, flowId, panBy, cancelConnection, onConnectStart, onConnect, onConnectEnd, isValidConnection = alwaysValid, onReconnectEnd, updateConnection, getTransform, getFromHandle, autoPanSpeed, }) {
|
|
1799
1817
|
// when xyflow is used inside a shadow root we can't use document
|
|
1800
1818
|
const doc = getHostForElement(event.target);
|
|
1801
1819
|
let autoPanId = 0;
|
|
@@ -1807,12 +1825,12 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
1807
1825
|
if (!containerBounds || !handleType) {
|
|
1808
1826
|
return;
|
|
1809
1827
|
}
|
|
1810
|
-
let
|
|
1828
|
+
let position = getEventPosition(event, containerBounds);
|
|
1811
1829
|
let autoPanStarted = false;
|
|
1812
1830
|
let connection = null;
|
|
1813
1831
|
let isValid = false;
|
|
1814
1832
|
let handleDomNode = null;
|
|
1815
|
-
const handleLookup = getHandleLookup({
|
|
1833
|
+
const [handleLookup, fromHandleInternal] = getHandleLookup({
|
|
1816
1834
|
nodeLookup,
|
|
1817
1835
|
nodeId,
|
|
1818
1836
|
handleId,
|
|
@@ -1823,31 +1841,42 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
1823
1841
|
if (!autoPanOnConnect || !containerBounds) {
|
|
1824
1842
|
return;
|
|
1825
1843
|
}
|
|
1826
|
-
const [x, y] = calcAutoPan(
|
|
1844
|
+
const [x, y] = calcAutoPan(position, containerBounds, autoPanSpeed);
|
|
1827
1845
|
panBy({ x, y });
|
|
1828
1846
|
autoPanId = requestAnimationFrame(autoPan);
|
|
1829
1847
|
}
|
|
1830
1848
|
// Stays the same for all consecutive pointermove events
|
|
1831
|
-
|
|
1849
|
+
const fromHandle = {
|
|
1850
|
+
...fromHandleInternal,
|
|
1832
1851
|
nodeId,
|
|
1833
|
-
handleId,
|
|
1834
1852
|
type: handleType,
|
|
1835
|
-
position:
|
|
1853
|
+
position: fromHandleInternal.position,
|
|
1836
1854
|
};
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1855
|
+
const fromNodeInternal = nodeLookup.get(nodeId);
|
|
1856
|
+
const from = getHandlePosition(fromNodeInternal, fromHandle, Position.Left, true);
|
|
1857
|
+
const newConnection = {
|
|
1858
|
+
inProgress: true,
|
|
1859
|
+
isValid: null,
|
|
1860
|
+
from,
|
|
1861
|
+
fromHandle,
|
|
1862
|
+
fromPosition: fromHandle.position,
|
|
1863
|
+
fromNode: fromNodeInternal.internals.userNode,
|
|
1864
|
+
to: position,
|
|
1865
|
+
toHandle: null,
|
|
1866
|
+
toPosition: oppositePosition[fromHandle.position],
|
|
1867
|
+
toNode: null,
|
|
1868
|
+
};
|
|
1869
|
+
updateConnection(newConnection);
|
|
1870
|
+
let previousConnection = newConnection;
|
|
1843
1871
|
onConnectStart?.(event, { nodeId, handleId, handleType });
|
|
1844
1872
|
function onPointerMove(event) {
|
|
1845
|
-
if (!
|
|
1873
|
+
if (!getFromHandle() || !fromHandle) {
|
|
1846
1874
|
onPointerUp(event);
|
|
1875
|
+
return;
|
|
1847
1876
|
}
|
|
1848
1877
|
const transform = getTransform();
|
|
1849
|
-
|
|
1850
|
-
closestHandle = getClosestHandle(pointToRendererPoint(
|
|
1878
|
+
position = getEventPosition(event, containerBounds);
|
|
1879
|
+
closestHandle = getClosestHandle(pointToRendererPoint(position, transform, false, [1, 1]), connectionRadius, handleLookup);
|
|
1851
1880
|
if (!autoPanStarted) {
|
|
1852
1881
|
autoPan();
|
|
1853
1882
|
autoPanStarted = true;
|
|
@@ -1862,21 +1891,35 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
1862
1891
|
doc,
|
|
1863
1892
|
lib,
|
|
1864
1893
|
flowId,
|
|
1894
|
+
handleLookup,
|
|
1865
1895
|
});
|
|
1866
1896
|
handleDomNode = result.handleDomNode;
|
|
1867
1897
|
connection = result.connection;
|
|
1868
|
-
isValid = result.isValid;
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
}
|
|
1898
|
+
isValid = isConnectionValid(!!closestHandle, result.isValid);
|
|
1899
|
+
const newConnection = {
|
|
1900
|
+
// from stays the same
|
|
1901
|
+
...previousConnection,
|
|
1902
|
+
isValid,
|
|
1903
|
+
to: closestHandle && isValid
|
|
1904
|
+
? rendererPointToPoint({ x: closestHandle.x, y: closestHandle.y }, transform)
|
|
1905
|
+
: position,
|
|
1906
|
+
toHandle: result.toHandle,
|
|
1907
|
+
toPosition: isValid && result.toHandle ? result.toHandle.position : oppositePosition[fromHandle.position],
|
|
1908
|
+
toNode: result.toHandle ? nodeLookup.get(result.toHandle.nodeId).internals.userNode : null,
|
|
1909
|
+
};
|
|
1910
|
+
// we don't want to trigger an update when the connection
|
|
1911
|
+
// is snapped to the same handle as before
|
|
1912
|
+
if (isValid &&
|
|
1913
|
+
closestHandle &&
|
|
1914
|
+
previousConnection.toHandle &&
|
|
1915
|
+
newConnection.toHandle &&
|
|
1916
|
+
previousConnection.toHandle.type === newConnection.toHandle.type &&
|
|
1917
|
+
previousConnection.toHandle.nodeId === newConnection.toHandle.nodeId &&
|
|
1918
|
+
previousConnection.toHandle.id === newConnection.toHandle.id) {
|
|
1919
|
+
return;
|
|
1920
|
+
}
|
|
1921
|
+
updateConnection(newConnection);
|
|
1922
|
+
previousConnection = newConnection;
|
|
1880
1923
|
}
|
|
1881
1924
|
function onPointerUp(event) {
|
|
1882
1925
|
if ((closestHandle || handleDomNode) && connection && isValid) {
|
|
@@ -1894,7 +1937,6 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
1894
1937
|
isValid = false;
|
|
1895
1938
|
connection = null;
|
|
1896
1939
|
handleDomNode = null;
|
|
1897
|
-
connectionStartHandle = null;
|
|
1898
1940
|
doc.removeEventListener('mousemove', onPointerMove);
|
|
1899
1941
|
doc.removeEventListener('mouseup', onPointerUp);
|
|
1900
1942
|
doc.removeEventListener('touchmove', onPointerMove);
|
|
@@ -1906,7 +1948,7 @@ function onPointerDown(event, { connectionMode, connectionRadius, handleId, node
|
|
|
1906
1948
|
doc.addEventListener('touchend', onPointerUp);
|
|
1907
1949
|
}
|
|
1908
1950
|
// checks if and returns connection in fom of an object { source: 123, target: 312 }
|
|
1909
|
-
function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId, fromType, doc, lib, flowId, isValidConnection = alwaysValid, }) {
|
|
1951
|
+
function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId, fromType, doc, lib, flowId, isValidConnection = alwaysValid, handleLookup, }) {
|
|
1910
1952
|
const isTarget = fromType === 'target';
|
|
1911
1953
|
const handleDomNode = handle
|
|
1912
1954
|
? doc.querySelector(`.${lib}-flow__handle[data-id="${flowId}-${handle?.nodeId}-${handle?.id}-${handle?.type}"]`)
|
|
@@ -1920,7 +1962,7 @@ function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId
|
|
|
1920
1962
|
handleDomNode: handleToCheck,
|
|
1921
1963
|
isValid: false,
|
|
1922
1964
|
connection: null,
|
|
1923
|
-
|
|
1965
|
+
toHandle: null,
|
|
1924
1966
|
};
|
|
1925
1967
|
if (handleToCheck) {
|
|
1926
1968
|
const handleType = getHandleType(undefined, handleToCheck);
|
|
@@ -1945,12 +1987,14 @@ function isValidHandle(event, { handle, connectionMode, fromNodeId, fromHandleId
|
|
|
1945
1987
|
? (isTarget && handleType === 'source') || (!isTarget && handleType === 'target')
|
|
1946
1988
|
: handleNodeId !== fromNodeId || handleId !== fromHandleId);
|
|
1947
1989
|
result.isValid = isValid && isValidConnection(connection);
|
|
1948
|
-
|
|
1949
|
-
nodeId
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1990
|
+
if (handleLookup) {
|
|
1991
|
+
const toHandle = handleLookup.find((h) => h.id === handleId && h.nodeId === handleNodeId && h.type === handleType);
|
|
1992
|
+
if (toHandle) {
|
|
1993
|
+
result.toHandle = {
|
|
1994
|
+
...toHandle,
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1954
1998
|
}
|
|
1955
1999
|
return result;
|
|
1956
2000
|
}
|
|
@@ -2655,8 +2699,8 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2655
2699
|
}
|
|
2656
2700
|
const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid });
|
|
2657
2701
|
prevValues = {
|
|
2658
|
-
width: node.measured
|
|
2659
|
-
height: node.measured
|
|
2702
|
+
width: node.measured.width ?? 0,
|
|
2703
|
+
height: node.measured.height ?? 0,
|
|
2660
2704
|
x: node.position.x ?? 0,
|
|
2661
2705
|
y: node.position.y ?? 0,
|
|
2662
2706
|
};
|
|
@@ -2667,11 +2711,9 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2667
2711
|
aspectRatio: prevValues.width / prevValues.height,
|
|
2668
2712
|
};
|
|
2669
2713
|
parentNode = undefined;
|
|
2670
|
-
if (node.extent === 'parent' || node.expandParent) {
|
|
2714
|
+
if (node.parentId && (node.extent === 'parent' || node.expandParent)) {
|
|
2671
2715
|
parentNode = nodeLookup.get(node.parentId);
|
|
2672
|
-
|
|
2673
|
-
parentExtent = nodeToParentExtent(parentNode);
|
|
2674
|
-
}
|
|
2716
|
+
parentExtent = parentNode && node.extent === 'parent' ? nodeToParentExtent(parentNode) : undefined;
|
|
2675
2717
|
}
|
|
2676
2718
|
// Collect all child nodes to correct their relative positions when top/left changes
|
|
2677
2719
|
// Determine largest minimal extent the parent node is allowed to resize to
|
|
@@ -2718,22 +2760,13 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2718
2760
|
if (!isXPosChange && !isYPosChange && !isWidthChange && !isHeightChange) {
|
|
2719
2761
|
return;
|
|
2720
2762
|
}
|
|
2721
|
-
if (isXPosChange || isYPosChange || nodeOrigin[0] === 1 || nodeOrigin[1]
|
|
2763
|
+
if (isXPosChange || isYPosChange || nodeOrigin[0] === 1 || nodeOrigin[1] === 1) {
|
|
2722
2764
|
change.x = isXPosChange ? x : prevValues.x;
|
|
2723
2765
|
change.y = isYPosChange ? y : prevValues.y;
|
|
2724
2766
|
prevValues.x = change.x;
|
|
2725
2767
|
prevValues.y = change.y;
|
|
2726
|
-
//
|
|
2727
|
-
|
|
2728
|
-
if (change.x && change.x < 0) {
|
|
2729
|
-
prevValues.x = 0;
|
|
2730
|
-
startValues.x = startValues.x - change.x;
|
|
2731
|
-
}
|
|
2732
|
-
if (change.y && change.y < 0) {
|
|
2733
|
-
prevValues.y = 0;
|
|
2734
|
-
startValues.y = startValues.y - change.y;
|
|
2735
|
-
}
|
|
2736
|
-
}
|
|
2768
|
+
// when top/left changes, correct the relative positions of child nodes
|
|
2769
|
+
// so that they stay in the same position
|
|
2737
2770
|
if (childNodes.length > 0) {
|
|
2738
2771
|
const xChange = x - prevX;
|
|
2739
2772
|
const yChange = y - prevY;
|
|
@@ -2752,6 +2785,19 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2752
2785
|
prevValues.width = change.width;
|
|
2753
2786
|
prevValues.height = change.height;
|
|
2754
2787
|
}
|
|
2788
|
+
// Fix expandParent when resizing from top/left
|
|
2789
|
+
if (parentNode && node.expandParent) {
|
|
2790
|
+
const xLimit = nodeOrigin[0] * (change.width ?? 0);
|
|
2791
|
+
if (change.x && change.x < xLimit) {
|
|
2792
|
+
prevValues.x = xLimit;
|
|
2793
|
+
startValues.x = startValues.x - (change.x - xLimit);
|
|
2794
|
+
}
|
|
2795
|
+
const yLimit = nodeOrigin[1] * (change.height ?? 0);
|
|
2796
|
+
if (change.y && change.y < yLimit) {
|
|
2797
|
+
prevValues.y = yLimit;
|
|
2798
|
+
startValues.y = startValues.y - (change.y - yLimit);
|
|
2799
|
+
}
|
|
2800
|
+
}
|
|
2755
2801
|
const direction = getResizeDirection({
|
|
2756
2802
|
width: prevValues.width,
|
|
2757
2803
|
prevWidth,
|
|
@@ -2783,4 +2829,4 @@ function XYResizer({ domNode, nodeId, getStoreItems, onChange, onEnd }) {
|
|
|
2783
2829
|
};
|
|
2784
2830
|
}
|
|
2785
2831
|
|
|
2786
|
-
export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, adoptUserNodes, areConnectionMapsEqual, boxToRect, calcAutoPan, calculateNodePosition, clamp, clampPosition, createMarkerIds, devWarn, elementSelectionKeys, errorMessages, evaluateAbsolutePosition, fitView, getBezierEdgeCenter, getBezierPath, getBoundsOfBoxes, getBoundsOfRects, getConnectedEdges, getDimensions, getEdgeCenter, getEdgePosition, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHandlePosition, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesInside, getOutgoers, getOverlappingArea, getPointerPosition,
|
|
2832
|
+
export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, XYDrag, XYHandle, XYMinimap, XYPanZoom, XYResizer, XY_RESIZER_HANDLE_POSITIONS, XY_RESIZER_LINE_POSITIONS, addEdge, adoptUserNodes, areConnectionMapsEqual, boxToRect, calcAutoPan, calculateNodePosition, clamp, clampPosition, createMarkerIds, devWarn, elementSelectionKeys, errorMessages, evaluateAbsolutePosition, fitView, getBezierEdgeCenter, getBezierPath, getBoundsOfBoxes, getBoundsOfRects, getConnectedEdges, getConnectionStatus, getDimensions, getEdgeCenter, getEdgePosition, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHandlePosition, getHostForElement, getIncomers, getInternalNodesBounds, getMarkerId, getNodeDimensions, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesInside, getOutgoers, getOverlappingArea, getPointerPosition, getSmoothStepPath, getStraightPath, getViewportForBounds, handleConnectionChange, handleExpandParent, infiniteExtent, initialConnection, isCoordinateExtent, isEdgeBase, isEdgeVisible, isInputDOMNode, isInternalNodeBase, isMacOs, isMouseEvent, isNodeBase, isNumeric, isRectObject, nodeHasDimensions, nodeToBox, nodeToRect, oppositePosition, panBy, pointToRendererPoint, reconnectEdge, rectToBox, rendererPointToPoint, shallowNodeData, snapPosition, updateAbsolutePositions, updateConnectionLookup, updateNodeInternals };
|