@xyflow/system 0.0.12 → 0.0.13

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 (48) hide show
  1. package/dist/esm/index.js +2176 -0
  2. package/dist/esm/index.mjs +266 -146
  3. package/dist/esm/types/edges.d.ts +2 -2
  4. package/dist/esm/types/edges.d.ts.map +1 -1
  5. package/dist/esm/types/general.d.ts +7 -0
  6. package/dist/esm/types/general.d.ts.map +1 -1
  7. package/dist/esm/types/nodes.d.ts +7 -2
  8. package/dist/esm/types/nodes.d.ts.map +1 -1
  9. package/dist/esm/utils/edges/bezier-edge.d.ts +23 -0
  10. package/dist/esm/utils/edges/bezier-edge.d.ts.map +1 -1
  11. package/dist/esm/utils/edges/general.d.ts +23 -5
  12. package/dist/esm/utils/edges/general.d.ts.map +1 -1
  13. package/dist/esm/utils/edges/positions.d.ts.map +1 -1
  14. package/dist/esm/utils/edges/smoothstep-edge.d.ts +22 -0
  15. package/dist/esm/utils/edges/smoothstep-edge.d.ts.map +1 -1
  16. package/dist/esm/utils/edges/straight-edge.d.ts +20 -0
  17. package/dist/esm/utils/edges/straight-edge.d.ts.map +1 -1
  18. package/dist/esm/utils/general.d.ts +19 -2
  19. package/dist/esm/utils/general.d.ts.map +1 -1
  20. package/dist/esm/utils/graph.d.ts +61 -6
  21. package/dist/esm/utils/graph.d.ts.map +1 -1
  22. package/dist/esm/utils/store.d.ts +4 -4
  23. package/dist/esm/utils/store.d.ts.map +1 -1
  24. package/dist/esm/xyminimap/index.d.ts.map +1 -1
  25. package/dist/umd/index.js +1 -1
  26. package/dist/umd/types/edges.d.ts +2 -2
  27. package/dist/umd/types/edges.d.ts.map +1 -1
  28. package/dist/umd/types/general.d.ts +7 -0
  29. package/dist/umd/types/general.d.ts.map +1 -1
  30. package/dist/umd/types/nodes.d.ts +7 -2
  31. package/dist/umd/types/nodes.d.ts.map +1 -1
  32. package/dist/umd/utils/edges/bezier-edge.d.ts +23 -0
  33. package/dist/umd/utils/edges/bezier-edge.d.ts.map +1 -1
  34. package/dist/umd/utils/edges/general.d.ts +23 -5
  35. package/dist/umd/utils/edges/general.d.ts.map +1 -1
  36. package/dist/umd/utils/edges/positions.d.ts.map +1 -1
  37. package/dist/umd/utils/edges/smoothstep-edge.d.ts +22 -0
  38. package/dist/umd/utils/edges/smoothstep-edge.d.ts.map +1 -1
  39. package/dist/umd/utils/edges/straight-edge.d.ts +20 -0
  40. package/dist/umd/utils/edges/straight-edge.d.ts.map +1 -1
  41. package/dist/umd/utils/general.d.ts +19 -2
  42. package/dist/umd/utils/general.d.ts.map +1 -1
  43. package/dist/umd/utils/graph.d.ts +61 -6
  44. package/dist/umd/utils/graph.d.ts.map +1 -1
  45. package/dist/umd/utils/store.d.ts +4 -4
  46. package/dist/umd/utils/store.d.ts.map +1 -1
  47. package/dist/umd/xyminimap/index.d.ts.map +1 -1
  48. package/package.json +4 -4
@@ -1,4 +1,3 @@
1
- "use client"
2
1
  import { drag } from 'd3-drag';
3
2
  import { select, pointer } from 'd3-selection';
4
3
  import { zoom, zoomIdentity, zoomTransform } from 'd3-zoom';
@@ -105,8 +104,30 @@ function handleConnectionChange(a, b, cb) {
105
104
  }
106
105
 
107
106
  /* eslint-disable @typescript-eslint/no-explicit-any */
107
+ /**
108
+ * Test whether an object is useable as an Edge
109
+ * @public
110
+ * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true
111
+ * @param element - The element to test
112
+ * @returns A boolean indicating whether the element is an Edge
113
+ */
108
114
  const isEdgeBase = (element) => 'id' in element && 'source' in element && 'target' in element;
115
+ /**
116
+ * Test whether an object is useable as a Node
117
+ * @public
118
+ * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true
119
+ * @param element - The element to test
120
+ * @returns A boolean indicating whether the element is an Node
121
+ */
109
122
  const isNodeBase = (element) => 'id' in element && !('source' in element) && !('target' in element);
123
+ /**
124
+ * Pass in a node, and get connected nodes where edge.source === node.id
125
+ * @public
126
+ * @param node - The node to get the connected nodes from
127
+ * @param nodes - The array of all nodes
128
+ * @param edges - The array of all edges
129
+ * @returns An array of nodes that are connected over eges where the source is the given node
130
+ */
110
131
  const getOutgoersBase = (node, nodes, edges) => {
111
132
  if (!node.id) {
112
133
  return [];
@@ -119,6 +140,14 @@ const getOutgoersBase = (node, nodes, edges) => {
119
140
  });
120
141
  return nodes.filter((n) => outgoerIds.has(n.id));
121
142
  };
143
+ /**
144
+ * Pass in a node, and get connected nodes where edge.target === node.id
145
+ * @public
146
+ * @param node - The node to get the connected nodes from
147
+ * @param nodes - The array of all nodes
148
+ * @param edges - The array of all edges
149
+ * @returns An array of nodes that are connected over eges where the target is the given node
150
+ */
122
151
  const getIncomersBase = (node, nodes, edges) => {
123
152
  if (!node.id) {
124
153
  return [];
@@ -158,6 +187,14 @@ const getNodePositionWithOrigin = (node, nodeOrigin = [0, 0]) => {
158
187
  : position,
159
188
  };
160
189
  };
190
+ /**
191
+ * Determines a bounding box that contains all given nodes in an array
192
+ * @public
193
+ * @remarks Useful when combined with {@link getViewportForBounds} to calculate the correct transform to fit the given nodes in a viewport.
194
+ * @param nodes - Nodes to calculate the bounds for
195
+ * @param nodeOrigin - Origin of the nodes: [0, 0] - top left, [0.5, 0.5] - center
196
+ * @returns Bounding box enclosing all nodes
197
+ */
161
198
  const getNodesBounds = (nodes, nodeOrigin = [0, 0]) => {
162
199
  if (nodes.length === 0) {
163
200
  return { x: 0, y: 0, width: 0, height: 0 };
@@ -200,6 +237,12 @@ excludeNonSelectableNodes = false, nodeOrigin = [0, 0]) => {
200
237
  }, []);
201
238
  return visibleNodes;
202
239
  };
240
+ /**
241
+ * Get all connecting edges for a given set of nodes
242
+ * @param nodes - Nodes you want to get the connected edges for
243
+ * @param edges - All edges
244
+ * @returns Array of edges that connect any of the given nodes with each other
245
+ */
203
246
  const getConnectedEdgesBase = (nodes, edges) => {
204
247
  const nodeIds = new Set();
205
248
  nodes.forEach((node) => {
@@ -278,33 +321,53 @@ function calcNextPosition(node, nextPosition, nodes, nodeExtent, nodeOrigin = [0
278
321
  positionAbsolute,
279
322
  };
280
323
  }
281
- // helper function to get arrays of nodes and edges that can be deleted
282
- // you can pass in a list of nodes and edges that should be deleted
283
- // and the function only returns elements that are deletable and also handles connected nodes and child nodes
284
- function getElementsToRemove({ nodesToRemove, edgesToRemove, nodes, edges, }) {
324
+ /**
325
+ * Pass in nodes & edges to delete, get arrays of nodes and edges that actually can be deleted
326
+ * @internal
327
+ * @param param.nodesToRemove - The nodes to remove
328
+ * @param param.edgesToRemove - The edges to remove
329
+ * @param param.nodes - All nodes
330
+ * @param param.edges - All edges
331
+ * @param param.onBeforeDelete - Callback to check which nodes and edges can be deleted
332
+ * @returns nodes: nodes that can be deleted, edges: edges that can be deleted
333
+ */
334
+ async function getElementsToRemove({ nodesToRemove = [], edgesToRemove = [], nodes, edges, onBeforeDelete, }) {
285
335
  const nodeIds = nodesToRemove.map((node) => node.id);
286
- const edgeIds = edgesToRemove.map((edge) => edge.id);
287
- const matchingNodes = nodes.reduce((res, node) => {
288
- const parentHit = !nodeIds.includes(node.id) && node.parentNode && res.find((n) => n.id === node.parentNode);
289
- const deletable = typeof node.deletable === 'boolean' ? node.deletable : true;
290
- if (deletable && (nodeIds.includes(node.id) || parentHit)) {
291
- res.push(node);
336
+ const matchingNodes = [];
337
+ for (const node of nodes) {
338
+ if (node.deletable === false) {
339
+ continue;
292
340
  }
293
- return res;
294
- }, []);
295
- const deletableEdges = edges.filter((e) => (typeof e.deletable === 'boolean' ? e.deletable : true));
296
- const initialHitEdges = deletableEdges.filter((e) => edgeIds.includes(e.id));
341
+ const isIncluded = nodeIds.includes(node.id);
342
+ const parentHit = !isIncluded && node.parentNode && matchingNodes.find((n) => n.id === node.parentNode);
343
+ if (isIncluded || parentHit) {
344
+ matchingNodes.push(node);
345
+ }
346
+ }
347
+ const edgeIds = edgesToRemove.map((edge) => edge.id);
348
+ const deletableEdges = edges.filter((edge) => edge.deletable !== false);
297
349
  const connectedEdges = getConnectedEdgesBase(matchingNodes, deletableEdges);
298
- const matchingEdges = connectedEdges.reduce((res, edge) => {
299
- if (!res.find((e) => e.id === edge.id)) {
300
- res.push(edge);
350
+ const matchingEdges = connectedEdges;
351
+ for (const edge of deletableEdges) {
352
+ const isIncluded = edgeIds.includes(edge.id);
353
+ if (isIncluded && !matchingEdges.find((e) => e.id === edge.id)) {
354
+ matchingEdges.push(edge);
301
355
  }
302
- return res;
303
- }, initialHitEdges);
304
- return {
305
- matchingEdges,
306
- matchingNodes,
307
- };
356
+ }
357
+ if (!onBeforeDelete) {
358
+ return {
359
+ edges: matchingEdges,
360
+ nodes: matchingNodes,
361
+ };
362
+ }
363
+ const onBeforeDeleteResult = await onBeforeDelete({
364
+ nodes: matchingNodes,
365
+ edges: matchingEdges,
366
+ });
367
+ if (typeof onBeforeDeleteResult === 'boolean') {
368
+ return onBeforeDeleteResult ? { edges: matchingEdges, nodes: matchingNodes } : { edges: [], nodes: [] };
369
+ }
370
+ return onBeforeDeleteResult;
308
371
  }
309
372
 
310
373
  const clamp = (val, min = 0, max = 1) => Math.min(Math.max(val, min), max);
@@ -312,8 +375,14 @@ const clampPosition = (position = { x: 0, y: 0 }, extent) => ({
312
375
  x: clamp(position.x, extent[0][0], extent[1][0]),
313
376
  y: clamp(position.y, extent[0][1], extent[1][1]),
314
377
  });
315
- // returns a number between 0 and 1 that represents the velocity of the movement
316
- // when the mouse is close to the edge of the canvas
378
+ /**
379
+ * Calculates the velocity of panning when the mouse is close to the edge of the canvas
380
+ * @internal
381
+ * @param value - One dimensional poition of the mouse (x or y)
382
+ * @param min - Minimal position on canvas before panning starts
383
+ * @param max - Maximal position on canvas before panning starts
384
+ * @returns - A number between 0 and 1 that represents the velocity of panning
385
+ */
317
386
  const calcAutoPanVelocity = (value, min, max) => {
318
387
  if (value < min) {
319
388
  return clamp(Math.abs(value - min), 1, 50) / 50;
@@ -387,12 +456,12 @@ const getPositionWithOrigin = ({ x, y, width, height, origin = [0, 0], }) => {
387
456
  y: y - height * origin[1],
388
457
  };
389
458
  };
390
- function snapPosition(position, snapGrid = [1, 1]) {
459
+ const snapPosition = (position, snapGrid = [1, 1]) => {
391
460
  return {
392
461
  x: snapGrid[0] * Math.round(position.x / snapGrid[0]),
393
462
  y: snapGrid[1] * Math.round(position.y / snapGrid[1]),
394
463
  };
395
- }
464
+ };
396
465
  const pointToRendererPoint = ({ x, y }, [tx, ty, tScale], snapToGrid = false, snapGrid = [1, 1]) => {
397
466
  const position = {
398
467
  x: (x - tx) / tScale,
@@ -406,6 +475,22 @@ const rendererPointToPoint = ({ x, y }, [tx, ty, tScale]) => {
406
475
  y: y * tScale + ty,
407
476
  };
408
477
  };
478
+ /**
479
+ * Returns a viewport that encloses the given bounds with optional padding.
480
+ * @public
481
+ * @remarks You can determine bounds of nodes with {@link getNodesBounds} and {@link getBoundsOfRects}
482
+ * @param bounds - Bounds to fit inside viewport
483
+ * @param width - Width of the viewport
484
+ * @param height - Height of the viewport
485
+ * @param minZoom - Minimum zoom level of the resulting viewport
486
+ * @param maxZoom - Maximum zoom level of the resulting viewport
487
+ * @param padding - Optional padding around the bounds
488
+ * @returns A transforned {@link Viewport} that encloses the given bounds which you can pass to e.g. {@link setViewport}
489
+ * @example
490
+ * const { x, y, zoom } = getViewportForBounds(
491
+ { x: 0, y: 0, width: 100, height: 100},
492
+ 1200, 800, 0.5, 2);
493
+ */
409
494
  const getViewportForBounds = (bounds, width, height, minZoom, maxZoom, padding) => {
410
495
  const xZoom = width / (bounds.width * (1 + padding));
411
496
  const yZoom = height / (bounds.height * (1 + padding));
@@ -509,6 +594,29 @@ function getControlWithCurvature({ pos, x1, y1, x2, y2, c }) {
509
594
  return [x1, y1 + calculateControlOffset(y2 - y1, c)];
510
595
  }
511
596
  }
597
+ /**
598
+ * Get a bezier path from source to target handle
599
+ * @param params.sourceX - The x position of the source handle
600
+ * @param params.sourceY - The y position of the source handle
601
+ * @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
602
+ * @param params.targetX - The x position of the target handle
603
+ * @param params.targetY - The y position of the target handle
604
+ * @param params.targetPosition - The position of the target handle (default: Position.Top)
605
+ * @param params.curvature - The curvature of the bezier edge
606
+ * @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
607
+ * @example
608
+ * const source = { x: 0, y: 20 };
609
+ const target = { x: 150, y: 100 };
610
+
611
+ const [path, labelX, labelY, offsetX, offsetY] = getBezierPath({
612
+ sourceX: source.x,
613
+ sourceY: source.y,
614
+ sourcePosition: Position.Right,
615
+ targetX: target.x,
616
+ targetY: target.y,
617
+ targetPosition: Position.Left,
618
+ });
619
+ */
512
620
  function getBezierPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, curvature = 0.25, }) {
513
621
  const [sourceControlX, sourceControlY] = getControlWithCurvature({
514
622
  pos: sourcePosition,
@@ -553,40 +661,13 @@ function getEdgeCenter({ sourceX, sourceY, targetX, targetY, }) {
553
661
  const centerY = targetY < sourceY ? targetY + yOffset : targetY - yOffset;
554
662
  return [centerX, centerY, xOffset, yOffset];
555
663
  }
556
- const defaultEdgeTree = [{ level: 0, isMaxLevel: true, edges: [] }];
557
- function groupEdgesByZLevel(edges, nodeLookup, elevateEdgesOnSelect = false) {
558
- let maxLevel = -1;
559
- const levelLookup = edges.reduce((tree, edge) => {
560
- const hasZIndex = isNumeric(edge.zIndex);
561
- let z = hasZIndex ? edge.zIndex : 0;
562
- if (elevateEdgesOnSelect) {
563
- const targetNode = nodeLookup.get(edge.target);
564
- const sourceNode = nodeLookup.get(edge.source);
565
- const edgeOrConnectedNodeSelected = edge.selected || targetNode?.selected || sourceNode?.selected;
566
- const selectedZIndex = Math.max(sourceNode?.[internalsSymbol]?.z || 0, targetNode?.[internalsSymbol]?.z || 0, 1000);
567
- z = (hasZIndex ? edge.zIndex : 0) + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
568
- }
569
- if (tree[z]) {
570
- tree[z].push(edge);
571
- }
572
- else {
573
- tree[z] = [edge];
574
- }
575
- maxLevel = z > maxLevel ? z : maxLevel;
576
- return tree;
577
- }, {});
578
- const edgeTree = Object.entries(levelLookup).map(([key, edges]) => {
579
- const level = +key;
580
- return {
581
- edges,
582
- level,
583
- isMaxLevel: level === maxLevel,
584
- };
585
- });
586
- if (edgeTree.length === 0) {
587
- return defaultEdgeTree;
664
+ function getElevatedEdgeZIndex({ sourceNode, targetNode, selected = false, zIndex = 0, elevateOnSelect = false, }) {
665
+ if (!elevateOnSelect) {
666
+ return zIndex;
588
667
  }
589
- return edgeTree;
668
+ const edgeOrConnectedNodeSelected = selected || targetNode.selected || sourceNode.selected;
669
+ const selectedZIndex = Math.max(sourceNode[internalsSymbol]?.z || 0, targetNode[internalsSymbol]?.z || 0, 1000);
670
+ return zIndex + (edgeOrConnectedNodeSelected ? selectedZIndex : 0);
590
671
  }
591
672
  function isEdgeVisible({ sourceNode, targetNode, width, height, transform }) {
592
673
  const edgeBox = getBoundsOfBoxes(nodeToBox(sourceNode), nodeToBox(targetNode));
@@ -611,6 +692,14 @@ const connectionExists = (edge, edges) => {
611
692
  (el.sourceHandle === edge.sourceHandle || (!el.sourceHandle && !edge.sourceHandle)) &&
612
693
  (el.targetHandle === edge.targetHandle || (!el.targetHandle && !edge.targetHandle)));
613
694
  };
695
+ /**
696
+ * This util is a convenience function to add a new Edge to an array of edges
697
+ * @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
698
+ * @public
699
+ * @param edgeParams - Either an Edge or a Connection you want to add
700
+ * @param edges - The array of all current edges
701
+ * @returns A new array of edges with the new edge added
702
+ */
614
703
  const addEdgeBase = (edgeParams, edges) => {
615
704
  if (!edgeParams.source || !edgeParams.target) {
616
705
  devWarn('006', errorMessages['error006']());
@@ -637,6 +726,14 @@ const addEdgeBase = (edgeParams, edges) => {
637
726
  }
638
727
  return edges.concat(edge);
639
728
  };
729
+ /**
730
+ * A handy utility to update an existing Edge with new properties
731
+ * @param oldEdge - The edge you want to update
732
+ * @param newConnection - The new connection you want to update the edge with
733
+ * @param edges - The array of all current edges
734
+ * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id
735
+ * @returns the updated edges array
736
+ */
640
737
  const updateEdgeBase = (oldEdge, newConnection, edges, options = { shouldReplaceId: true }) => {
641
738
  const { id: oldEdgeId, ...rest } = oldEdge;
642
739
  if (!newConnection.source || !newConnection.target) {
@@ -660,6 +757,26 @@ const updateEdgeBase = (oldEdge, newConnection, edges, options = { shouldReplace
660
757
  return edges.filter((e) => e.id !== oldEdgeId).concat(edge);
661
758
  };
662
759
 
760
+ /**
761
+ * Get a straight path from source to target handle
762
+ * @param params.sourceX - The x position of the source handle
763
+ * @param params.sourceY - The y position of the source handle
764
+ * @param params.targetX - The x position of the target handle
765
+ * @param params.targetY - The y position of the target handle
766
+ * @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
767
+ * @example
768
+ * const source = { x: 0, y: 20 };
769
+ const target = { x: 150, y: 100 };
770
+
771
+ const [path, labelX, labelY, offsetX, offsetY] = getStraightPath({
772
+ sourceX: source.x,
773
+ sourceY: source.y,
774
+ sourcePosition: Position.Right,
775
+ targetX: target.x,
776
+ targetY: target.y,
777
+ targetPosition: Position.Left,
778
+ });
779
+ */
663
780
  function getStraightPath({ sourceX, sourceY, targetX, targetY, }) {
664
781
  const [labelX, labelY, offsetX, offsetY] = getEdgeCenter({
665
782
  sourceX,
@@ -808,6 +925,28 @@ function getBend(a, b, c, size) {
808
925
  const yDir = a.y < c.y ? -1 : 1;
809
926
  return `L ${x},${y + bendSize * yDir}Q ${x},${y} ${x + bendSize * xDir},${y}`;
810
927
  }
928
+ /**
929
+ * Get a smooth step path from source to target handle
930
+ * @param params.sourceX - The x position of the source handle
931
+ * @param params.sourceY - The y position of the source handle
932
+ * @param params.sourcePosition - The position of the source handle (default: Position.Bottom)
933
+ * @param params.targetX - The x position of the target handle
934
+ * @param params.targetY - The y position of the target handle
935
+ * @param params.targetPosition - The position of the target handle (default: Position.Top)
936
+ * @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
937
+ * @example
938
+ * const source = { x: 0, y: 20 };
939
+ const target = { x: 150, y: 100 };
940
+
941
+ const [path, labelX, labelY, offsetX, offsetY] = getSmoothStepPath({
942
+ sourceX: source.x,
943
+ sourceY: source.y,
944
+ sourcePosition: Position.Right,
945
+ targetX: target.x,
946
+ targetY: target.y,
947
+ targetPosition: Position.Left,
948
+ });
949
+ */
811
950
  function getSmoothStepPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, borderRadius = 5, centerX, centerY, offset = 20, }) {
812
951
  const [points, labelX, labelY, offsetX, offsetY] = getPoints({
813
952
  source: { x: sourceX, y: sourceY },
@@ -831,18 +970,22 @@ function getSmoothStepPath({ sourceX, sourceY, sourcePosition = Position.Bottom,
831
970
  return [path, labelX, labelY, offsetX, offsetY];
832
971
  }
833
972
 
973
+ function isNodeInitialized(node) {
974
+ return !!(node?.[internalsSymbol]?.handleBounds || node?.handles?.length) && !!(node?.computed?.width || node?.width);
975
+ }
834
976
  function getEdgePosition(params) {
835
- const [sourceNodeRect, sourceHandleBounds, isSourceValid] = getHandleDataByNode(params.sourceNode);
836
- const [targetNodeRect, targetHandleBounds, isTargetValid] = getHandleDataByNode(params.targetNode);
837
- if (!isSourceValid || !isTargetValid) {
977
+ const { sourceNode, targetNode } = params;
978
+ if (!isNodeInitialized(sourceNode) || !isNodeInitialized(targetNode)) {
838
979
  return null;
839
980
  }
981
+ const sourceHandleBounds = sourceNode[internalsSymbol]?.handleBounds || toHandleBounds(sourceNode.handles);
982
+ const targetHandleBounds = targetNode[internalsSymbol]?.handleBounds || toHandleBounds(targetNode.handles);
983
+ const sourceHandle = getHandle(sourceHandleBounds?.source ?? [], params.sourceHandle);
984
+ const targetHandle = getHandle(
840
985
  // when connection type is loose we can define all handles as sources and connect source -> source
841
- const targetNodeHandles = params.connectionMode === ConnectionMode.Strict
842
- ? targetHandleBounds.target
843
- : (targetHandleBounds.target ?? []).concat(targetHandleBounds.source ?? []);
844
- const sourceHandle = getHandle(sourceHandleBounds.source, params.sourceHandle);
845
- const targetHandle = getHandle(targetNodeHandles, params.targetHandle);
986
+ params.connectionMode === ConnectionMode.Strict
987
+ ? targetHandleBounds?.target ?? []
988
+ : (targetHandleBounds?.target ?? []).concat(targetHandleBounds?.source ?? []), params.targetHandle);
846
989
  const sourcePosition = sourceHandle?.position || Position.Bottom;
847
990
  const targetPosition = targetHandle?.position || Position.Top;
848
991
  if (!sourceHandle || !targetHandle) {
@@ -853,8 +996,8 @@ function getEdgePosition(params) {
853
996
  }));
854
997
  return null;
855
998
  }
856
- const { x: sourceX, y: sourceY } = getHandlePosition(sourcePosition, sourceNodeRect, sourceHandle);
857
- const { x: targetX, y: targetY } = getHandlePosition(targetPosition, targetNodeRect, targetHandle);
999
+ const [sourceX, sourceY] = getHandlePosition(sourcePosition, sourceNode, sourceHandle);
1000
+ const [targetX, targetY] = getHandlePosition(targetPosition, targetNode, targetHandle);
858
1001
  return {
859
1002
  sourceX,
860
1003
  sourceY,
@@ -868,67 +1011,37 @@ function toHandleBounds(handles) {
868
1011
  if (!handles) {
869
1012
  return null;
870
1013
  }
871
- return handles.reduce((res, item) => {
872
- item.width = item.width || 1;
873
- item.height = item.height || 1;
874
- if (item.type === 'source') {
875
- res.source?.push(item);
1014
+ const source = [];
1015
+ const target = [];
1016
+ for (const handle of handles) {
1017
+ handle.width = handle.width || 1;
1018
+ handle.height = handle.height || 1;
1019
+ if (handle.type === 'source') {
1020
+ source.push(handle);
876
1021
  }
877
- if (item.type === 'target') {
878
- res.target?.push(item);
1022
+ else if (handle.type === 'target') {
1023
+ target.push(handle);
879
1024
  }
880
- return res;
881
- }, {
882
- source: [],
883
- target: [],
884
- });
885
- }
886
- function getHandleDataByNode(node) {
887
- const handleBounds = node?.[internalsSymbol]?.handleBounds || toHandleBounds(node?.handles) || null;
888
- const nodeWidth = node?.computed?.width || node?.width;
889
- const nodeHeight = node?.computed?.height || node?.height;
890
- const isValid = handleBounds &&
891
- nodeWidth &&
892
- nodeHeight &&
893
- typeof node?.computed?.positionAbsolute?.x !== 'undefined' &&
894
- typeof node?.computed?.positionAbsolute?.y !== 'undefined';
895
- return [
896
- {
897
- x: node?.computed?.positionAbsolute?.x || 0,
898
- y: node?.computed?.positionAbsolute?.y || 0,
899
- width: nodeWidth || 0,
900
- height: nodeHeight || 0,
901
- },
902
- handleBounds,
903
- !!isValid,
904
- ];
1025
+ }
1026
+ return {
1027
+ source,
1028
+ target,
1029
+ };
905
1030
  }
906
- function getHandlePosition(position, nodeRect, handle = null) {
907
- const x = (handle?.x || 0) + nodeRect.x;
908
- const y = (handle?.y || 0) + nodeRect.y;
909
- const width = handle?.width || nodeRect.width;
910
- const height = handle?.height || nodeRect.height;
1031
+ function getHandlePosition(position, node, handle = null) {
1032
+ const x = (handle?.x ?? 0) + (node.computed?.positionAbsolute?.x ?? 0);
1033
+ const y = (handle?.y ?? 0) + (node.computed?.positionAbsolute?.y ?? 0);
1034
+ const width = handle?.width || (node?.computed?.width ?? node?.width ?? 0);
1035
+ const height = handle?.height || (node?.computed?.height ?? node?.height ?? 0);
911
1036
  switch (position) {
912
1037
  case Position.Top:
913
- return {
914
- x: x + width / 2,
915
- y,
916
- };
1038
+ return [x + width / 2, y];
917
1039
  case Position.Right:
918
- return {
919
- x: x + width,
920
- y: y + height / 2,
921
- };
1040
+ return [x + width, y + height / 2];
922
1041
  case Position.Bottom:
923
- return {
924
- x: x + width / 2,
925
- y: y + height,
926
- };
1042
+ return [x + width / 2, y + height];
927
1043
  case Position.Left:
928
- return {
929
- x,
930
- y: y + height / 2,
931
- };
1044
+ return [x, y + height / 2];
932
1045
  }
933
1046
  }
934
1047
  function getHandle(bounds, handleId) {
@@ -1025,10 +1138,13 @@ function updateAbsolutePositions(nodes, nodeLookup, nodeOrigin = [0, 0], parentN
1025
1138
  ...node.position,
1026
1139
  z: node[internalsSymbol]?.z ?? 0,
1027
1140
  }, parentNode?.origin || nodeOrigin);
1028
- node.computed.positionAbsolute = {
1029
- x,
1030
- y,
1031
- };
1141
+ const positionChanged = x !== node.computed?.positionAbsolute?.x || y !== node.computed?.positionAbsolute?.y;
1142
+ node.computed.positionAbsolute = positionChanged
1143
+ ? {
1144
+ x,
1145
+ y,
1146
+ }
1147
+ : node.computed?.positionAbsolute;
1032
1148
  node[internalsSymbol].z = z;
1033
1149
  if (parentNodes?.[node.id]) {
1034
1150
  node[internalsSymbol].isParent = true;
@@ -1037,7 +1153,7 @@ function updateAbsolutePositions(nodes, nodeLookup, nodeOrigin = [0, 0], parentN
1037
1153
  return node;
1038
1154
  });
1039
1155
  }
1040
- function updateNodes(nodes, nodeLookup, options = {
1156
+ function adoptUserProvidedNodes(nodes, nodeLookup, options = {
1041
1157
  nodeOrigin: [0, 0],
1042
1158
  elevateNodesOnSelect: true,
1043
1159
  defaults: {},
@@ -1048,6 +1164,10 @@ function updateNodes(nodes, nodeLookup, options = {
1048
1164
  const selectedNodeZ = options?.elevateNodesOnSelect ? 1000 : 0;
1049
1165
  const nextNodes = nodes.map((n) => {
1050
1166
  const currentStoreNode = tmpLookup.get(n.id);
1167
+ if (n === currentStoreNode?.[internalsSymbol]?.userProvidedNode) {
1168
+ nodeLookup.set(n.id, currentStoreNode);
1169
+ return currentStoreNode;
1170
+ }
1051
1171
  const node = {
1052
1172
  ...options.defaults,
1053
1173
  ...n,
@@ -1067,6 +1187,7 @@ function updateNodes(nodes, nodeLookup, options = {
1067
1187
  value: {
1068
1188
  handleBounds: currInternals?.handleBounds,
1069
1189
  z,
1190
+ userProvidedNode: n,
1070
1191
  },
1071
1192
  });
1072
1193
  nodeLookup.set(node.id, node);
@@ -1141,20 +1262,20 @@ function panBy({ delta, panZoom, transform, translateExtent, width, height, }) {
1141
1262
  (nextViewport.x !== transform[0] || nextViewport.y !== transform[1] || nextViewport.k !== transform[2]);
1142
1263
  return transformChanged;
1143
1264
  }
1144
- function updateConnectionLookup(lookup, edges) {
1145
- lookup.clear();
1146
- edges.forEach(({ source, target, sourceHandle = null, targetHandle = null }) => {
1147
- if (source && target) {
1148
- const sourceKey = `${source}-source-${sourceHandle}`;
1149
- const targetKey = `${target}-target-${targetHandle}`;
1150
- const prevSource = lookup.get(sourceKey) || new Map();
1151
- const prevTarget = lookup.get(targetKey) || new Map();
1152
- const connection = { source, target, sourceHandle, targetHandle };
1153
- lookup.set(sourceKey, prevSource.set(`${target}-${targetHandle}`, connection));
1154
- lookup.set(targetKey, prevTarget.set(`${source}-${sourceHandle}`, connection));
1155
- }
1156
- });
1157
- return lookup;
1265
+ function updateConnectionLookup(connectionLookup, edgeLookup, edges) {
1266
+ connectionLookup.clear();
1267
+ edgeLookup.clear();
1268
+ for (const edge of edges) {
1269
+ const { source, target, sourceHandle = null, targetHandle = null } = edge;
1270
+ const sourceKey = `${source}-source-${sourceHandle}`;
1271
+ const targetKey = `${target}-target-${targetHandle}`;
1272
+ const prevSource = connectionLookup.get(sourceKey) || new Map();
1273
+ const prevTarget = connectionLookup.get(targetKey) || new Map();
1274
+ const connection = { source, target, sourceHandle, targetHandle };
1275
+ edgeLookup.set(edge.id, edge);
1276
+ connectionLookup.set(sourceKey, prevSource.set(`${target}-${targetHandle}`, connection));
1277
+ connectionLookup.set(targetKey, prevTarget.set(`${source}-${sourceHandle}`, connection));
1278
+ }
1158
1279
  }
1159
1280
 
1160
1281
  function wrapSelectionDragFunc(selectionFunc) {
@@ -1679,8 +1800,7 @@ function XYMinimap({ domNode, panZoom, getTransform, getViewScale }) {
1679
1800
  if (event.sourceEvent.type !== 'mousemove' || !panZoom) {
1680
1801
  return;
1681
1802
  }
1682
- // @TODO: how to calculate the correct next position? Math.max(1, transform[2]) is a workaround.
1683
- const moveScale = getViewScale() * Math.max(1, transform[2]) * (inversePan ? -1 : 1);
1803
+ const moveScale = getViewScale() * Math.max(transform[2], Math.log(transform[2])) * (inversePan ? -1 : 1);
1684
1804
  const position = {
1685
1805
  x: transform[0] - event.sourceEvent.movementX * moveScale,
1686
1806
  y: transform[1] - event.sourceEvent.movementY * moveScale,
@@ -2053,4 +2173,4 @@ function XYPanZoom({ domNode, minZoom, maxZoom, translateExtent, viewport, onPan
2053
2173
  };
2054
2174
  }
2055
2175
 
2056
- export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, XYDrag, XYHandle, XYMinimap, XYPanZoom, addEdgeBase, areConnectionMapsEqual, boxToRect, calcAutoPan, calcNextPosition, clamp, clampPosition, createMarkerIds, devWarn, elementSelectionKeys, errorMessages, fitView, getBezierEdgeCenter, getBezierPath, getBoundsOfBoxes, getBoundsOfRects, getConnectedEdgesBase, getDimensions, getEdgeCenter, getEdgePosition, getElementsToRemove, getEventPosition, getHandleBounds, getHostForElement, getIncomersBase, getMarkerId, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesInside, getOutgoersBase, getOverlappingArea, getPointerPosition, getPositionWithOrigin, getSmoothStepPath, getStraightPath, getViewportForBounds, groupEdgesByZLevel, handleConnectionChange, infiniteExtent, internalsSymbol, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isMouseEvent, isNodeBase, isNumeric, isRectObject, nodeToBox, nodeToRect, panBy, pointToRendererPoint, rectToBox, rendererPointToPoint, snapPosition, updateAbsolutePositions, updateConnectionLookup, updateEdgeBase, updateNodeDimensions, updateNodes };
2176
+ export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, XYDrag, XYHandle, XYMinimap, XYPanZoom, addEdgeBase, adoptUserProvidedNodes, areConnectionMapsEqual, boxToRect, calcAutoPan, calcNextPosition, clamp, clampPosition, createMarkerIds, devWarn, elementSelectionKeys, errorMessages, fitView, getBezierEdgeCenter, getBezierPath, getBoundsOfBoxes, getBoundsOfRects, getConnectedEdgesBase, getDimensions, getEdgeCenter, getEdgePosition, getElementsToRemove, getElevatedEdgeZIndex, getEventPosition, getHandleBounds, getHostForElement, getIncomersBase, getMarkerId, getNodePositionWithOrigin, getNodeToolbarTransform, getNodesBounds, getNodesInside, getOutgoersBase, getOverlappingArea, getPointerPosition, getPositionWithOrigin, getSmoothStepPath, getStraightPath, getViewportForBounds, handleConnectionChange, infiniteExtent, internalsSymbol, isEdgeBase, isEdgeVisible, isInputDOMNode, isMacOs, isMouseEvent, isNodeBase, isNumeric, isRectObject, nodeToBox, nodeToRect, panBy, pointToRendererPoint, rectToBox, rendererPointToPoint, snapPosition, updateAbsolutePositions, updateConnectionLookup, updateEdgeBase, updateNodeDimensions };
@@ -17,7 +17,6 @@ export type EdgeBase<EdgeData = any> = {
17
17
  zIndex?: number;
18
18
  ariaLabel?: string;
19
19
  interactionWidth?: number;
20
- focusable?: boolean;
21
20
  };
22
21
  export type SmoothStepPathOptions = {
23
22
  offset?: number;
@@ -29,7 +28,7 @@ export type StepPathOptions = {
29
28
  export type BezierPathOptions = {
30
29
  curvature?: number;
31
30
  };
32
- export type DefaultEdgeOptionsBase<EdgeType extends EdgeBase> = Omit<EdgeType, 'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'sourceNode' | 'targetNode'>;
31
+ export type DefaultEdgeOptionsBase<EdgeType extends EdgeBase> = Omit<EdgeType, 'id' | 'source' | 'target' | 'sourceHandle' | 'targetHandle' | 'selected'>;
33
32
  export declare enum ConnectionLineType {
34
33
  Bezier = "default",
35
34
  Straight = "straight",
@@ -62,4 +61,5 @@ export type EdgePosition = {
62
61
  sourcePosition: Position;
63
62
  targetPosition: Position;
64
63
  };
64
+ export type EdgeLookup<EdgeType extends EdgeBase = EdgeBase> = Map<string, EdgeType>;
65
65
  //# sourceMappingURL=edges.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../src/types/edges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,MAAM,MAAM,QAAQ,CAAC,QAAQ,GAAG,GAAG,IAAI;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,QAAQ,IAAI,IAAI,CAClE,QAAQ,EACR,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,YAAY,GAAG,YAAY,CAC3F,CAAC;AAEF,oBAAY,kBAAkB;IAC5B,MAAM,YAAY;IAClB,QAAQ,aAAa;IACrB,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,YAAY,iBAAiB;CAC9B;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjD,oBAAY,UAAU;IACpB,KAAK,UAAU;IACf,WAAW,gBAAgB;CAC5B;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,QAAQ,CAAC;IACzB,cAAc,EAAE,QAAQ,CAAC;CAC1B,CAAC"}
1
+ {"version":3,"file":"edges.d.ts","sourceRoot":"","sources":["../../src/types/edges.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAGnC,MAAM,MAAM,QAAQ,CAAC,QAAQ,GAAG,GAAG,IAAI;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,QAAQ,SAAS,QAAQ,IAAI,IAAI,CAClE,QAAQ,EACR,IAAI,GAAG,QAAQ,GAAG,QAAQ,GAAG,cAAc,GAAG,cAAc,GAAG,UAAU,CAC1E,CAAC;AAEF,oBAAY,kBAAkB;IAC5B,MAAM,YAAY;IAClB,QAAQ,aAAa;IACrB,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,YAAY,iBAAiB;CAC9B;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,UAAU,CAAC;AAEjD,oBAAY,UAAU;IACpB,KAAK,UAAU;IACf,WAAW,gBAAgB;CAC5B;AAED,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG;IACrC,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,QAAQ,CAAC;IACzB,cAAc,EAAE,QAAQ,CAAC;CAC1B,CAAC;AAEF,MAAM,MAAM,UAAU,CAAC,QAAQ,SAAS,QAAQ,GAAG,QAAQ,IAAI,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC"}
@@ -106,4 +106,11 @@ export type UpdateConnection = (params: {
106
106
  export type ColorModeClass = 'light' | 'dark';
107
107
  export type ColorMode = ColorModeClass | 'system';
108
108
  export type ConnectionLookup = Map<string, Map<string, Connection>>;
109
+ export type OnBeforeDelete = <NodeType extends NodeBase = NodeBase, EdgeType extends EdgeBase = EdgeBase>({ nodes, edges, }: {
110
+ nodes: NodeType[];
111
+ edges: EdgeType[];
112
+ }) => Promise<boolean | {
113
+ nodes: NodeType[];
114
+ edges: EdgeType[];
115
+ }>;
109
116
  //# sourceMappingURL=general.d.ts.map