@xyflow/react 12.4.3 → 12.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. package/dist/base.css +8 -4
  2. package/dist/esm/additional-components/Background/types.d.ts +20 -4
  3. package/dist/esm/additional-components/Background/types.d.ts.map +1 -1
  4. package/dist/esm/additional-components/Controls/types.d.ts +33 -9
  5. package/dist/esm/additional-components/Controls/types.d.ts.map +1 -1
  6. package/dist/esm/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  7. package/dist/esm/additional-components/NodeToolbar/types.d.ts +8 -2
  8. package/dist/esm/additional-components/NodeToolbar/types.d.ts.map +1 -1
  9. package/dist/esm/components/EdgeLabelRenderer/index.d.ts +31 -29
  10. package/dist/esm/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  11. package/dist/esm/components/EdgeWrapper/index.d.ts +1 -1
  12. package/dist/esm/components/EdgeWrapper/index.d.ts.map +1 -1
  13. package/dist/esm/components/Edges/BezierEdge.d.ts +26 -0
  14. package/dist/esm/components/Edges/BezierEdge.d.ts.map +1 -1
  15. package/dist/esm/components/Edges/EdgeAnchor.d.ts +3 -0
  16. package/dist/esm/components/Edges/EdgeAnchor.d.ts.map +1 -1
  17. package/dist/esm/components/Edges/EdgeText.d.ts +18 -18
  18. package/dist/esm/components/Edges/EdgeText.d.ts.map +1 -1
  19. package/dist/esm/components/Edges/SmoothStepEdge.d.ts +26 -0
  20. package/dist/esm/components/Edges/SmoothStepEdge.d.ts.map +1 -1
  21. package/dist/esm/components/Edges/StepEdge.d.ts +26 -0
  22. package/dist/esm/components/Edges/StepEdge.d.ts.map +1 -1
  23. package/dist/esm/components/Edges/StraightEdge.d.ts +24 -0
  24. package/dist/esm/components/Edges/StraightEdge.d.ts.map +1 -1
  25. package/dist/esm/components/Handle/index.d.ts +1 -1
  26. package/dist/esm/components/Handle/index.d.ts.map +1 -1
  27. package/dist/esm/components/SelectionListener/index.d.ts +4 -4
  28. package/dist/esm/components/SelectionListener/index.d.ts.map +1 -1
  29. package/dist/esm/container/Pane/index.d.ts.map +1 -1
  30. package/dist/esm/container/ReactFlow/index.d.ts +1 -1
  31. package/dist/esm/hooks/useKeyPress.d.ts +1 -0
  32. package/dist/esm/hooks/useKeyPress.d.ts.map +1 -1
  33. package/dist/esm/hooks/useNodeConnections.d.ts +1 -1
  34. package/dist/esm/hooks/useOnSelectionChange.d.ts +4 -4
  35. package/dist/esm/hooks/useOnSelectionChange.d.ts.map +1 -1
  36. package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
  37. package/dist/esm/hooks/useViewportHelper.d.ts.map +1 -1
  38. package/dist/esm/index.d.ts +1 -1
  39. package/dist/esm/index.d.ts.map +1 -1
  40. package/dist/esm/index.js +427 -341
  41. package/dist/esm/index.mjs +427 -341
  42. package/dist/esm/store/index.d.ts +9 -9
  43. package/dist/esm/store/index.d.ts.map +1 -1
  44. package/dist/esm/store/initialState.d.ts +9 -9
  45. package/dist/esm/store/initialState.d.ts.map +1 -1
  46. package/dist/esm/types/component-props.d.ts +1 -1
  47. package/dist/esm/types/component-props.d.ts.map +1 -1
  48. package/dist/esm/types/edges.d.ts +23 -3
  49. package/dist/esm/types/edges.d.ts.map +1 -1
  50. package/dist/esm/types/general.d.ts +8 -19
  51. package/dist/esm/types/general.d.ts.map +1 -1
  52. package/dist/esm/types/instance.d.ts +2 -2
  53. package/dist/esm/types/instance.d.ts.map +1 -1
  54. package/dist/esm/types/store.d.ts +5 -7
  55. package/dist/esm/types/store.d.ts.map +1 -1
  56. package/dist/esm/utils/general.d.ts +1 -1
  57. package/dist/esm/utils/general.d.ts.map +1 -1
  58. package/dist/style.css +8 -4
  59. package/dist/umd/additional-components/Background/types.d.ts +20 -4
  60. package/dist/umd/additional-components/Background/types.d.ts.map +1 -1
  61. package/dist/umd/additional-components/Controls/types.d.ts +33 -9
  62. package/dist/umd/additional-components/Controls/types.d.ts.map +1 -1
  63. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  64. package/dist/umd/additional-components/NodeToolbar/types.d.ts +8 -2
  65. package/dist/umd/additional-components/NodeToolbar/types.d.ts.map +1 -1
  66. package/dist/umd/components/EdgeLabelRenderer/index.d.ts +31 -29
  67. package/dist/umd/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  68. package/dist/umd/components/EdgeWrapper/index.d.ts +1 -1
  69. package/dist/umd/components/EdgeWrapper/index.d.ts.map +1 -1
  70. package/dist/umd/components/Edges/BezierEdge.d.ts +26 -0
  71. package/dist/umd/components/Edges/BezierEdge.d.ts.map +1 -1
  72. package/dist/umd/components/Edges/EdgeAnchor.d.ts +3 -0
  73. package/dist/umd/components/Edges/EdgeAnchor.d.ts.map +1 -1
  74. package/dist/umd/components/Edges/EdgeText.d.ts +18 -18
  75. package/dist/umd/components/Edges/EdgeText.d.ts.map +1 -1
  76. package/dist/umd/components/Edges/SmoothStepEdge.d.ts +26 -0
  77. package/dist/umd/components/Edges/SmoothStepEdge.d.ts.map +1 -1
  78. package/dist/umd/components/Edges/StepEdge.d.ts +26 -0
  79. package/dist/umd/components/Edges/StepEdge.d.ts.map +1 -1
  80. package/dist/umd/components/Edges/StraightEdge.d.ts +24 -0
  81. package/dist/umd/components/Edges/StraightEdge.d.ts.map +1 -1
  82. package/dist/umd/components/Handle/index.d.ts +1 -1
  83. package/dist/umd/components/Handle/index.d.ts.map +1 -1
  84. package/dist/umd/components/SelectionListener/index.d.ts +4 -4
  85. package/dist/umd/components/SelectionListener/index.d.ts.map +1 -1
  86. package/dist/umd/container/Pane/index.d.ts.map +1 -1
  87. package/dist/umd/container/ReactFlow/index.d.ts +1 -1
  88. package/dist/umd/hooks/useKeyPress.d.ts +1 -0
  89. package/dist/umd/hooks/useKeyPress.d.ts.map +1 -1
  90. package/dist/umd/hooks/useNodeConnections.d.ts +1 -1
  91. package/dist/umd/hooks/useOnSelectionChange.d.ts +4 -4
  92. package/dist/umd/hooks/useOnSelectionChange.d.ts.map +1 -1
  93. package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
  94. package/dist/umd/hooks/useViewportHelper.d.ts.map +1 -1
  95. package/dist/umd/index.d.ts +1 -1
  96. package/dist/umd/index.d.ts.map +1 -1
  97. package/dist/umd/index.js +2 -2
  98. package/dist/umd/store/index.d.ts +9 -9
  99. package/dist/umd/store/index.d.ts.map +1 -1
  100. package/dist/umd/store/initialState.d.ts +9 -9
  101. package/dist/umd/store/initialState.d.ts.map +1 -1
  102. package/dist/umd/types/component-props.d.ts +1 -1
  103. package/dist/umd/types/component-props.d.ts.map +1 -1
  104. package/dist/umd/types/edges.d.ts +23 -3
  105. package/dist/umd/types/edges.d.ts.map +1 -1
  106. package/dist/umd/types/general.d.ts +8 -19
  107. package/dist/umd/types/general.d.ts.map +1 -1
  108. package/dist/umd/types/instance.d.ts +2 -2
  109. package/dist/umd/types/instance.d.ts.map +1 -1
  110. package/dist/umd/types/store.d.ts +5 -7
  111. package/dist/umd/types/store.d.ts.map +1 -1
  112. package/dist/umd/utils/general.d.ts +1 -1
  113. package/dist/umd/utils/general.d.ts.map +1 -1
  114. package/package.json +3 -3
@@ -2,7 +2,7 @@
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
  import { createContext, useContext, useMemo, forwardRef, useEffect, useRef, useState, useLayoutEffect, useCallback, memo } from 'react';
4
4
  import cc from 'classcat';
5
- import { errorMessages, infiniteExtent, isInputDOMNode, getFitViewNodes, getDimensions, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getNodesBounds, evaluateAbsolutePosition, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, areSetsEqual, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getInternalNodesBounds, isNumeric, nodeHasDimensions, getNodeDimensions, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, getConnectionStatus, ConnectionLineType, updateConnectionLookup, adoptUserNodes, initialConnection, devWarn, updateNodeInternals, updateAbsolutePositions, handleExpandParent, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, shallowNodeData, XYMinimap, getBoundsOfRects, ResizeControlVariant, XYResizer, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, getNodeToolbarTransform } from '@xyflow/system';
5
+ import { errorMessages, infiniteExtent, isInputDOMNode, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getNodesBounds, evaluateAbsolutePosition, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, areSetsEqual, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getInternalNodesBounds, isNumeric, nodeHasDimensions, getNodeDimensions, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, getConnectionStatus, ConnectionLineType, updateConnectionLookup, adoptUserNodes, initialConnection, devWarn, updateNodeInternals, updateAbsolutePositions, handleExpandParent, panBy, fitViewport, isMacOs, areConnectionMapsEqual, handleConnectionChange, shallowNodeData, XYMinimap, getBoundsOfRects, ResizeControlVariant, XYResizer, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, getNodeToolbarTransform } from '@xyflow/system';
6
6
  export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, reconnectEdge } from '@xyflow/system';
7
7
  import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
8
8
  import { shallow } from 'zustand/shallow';
@@ -148,7 +148,7 @@ function areEqual(a, b) {
148
148
  return (shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
149
149
  shallow(a.selectedEdges.map(selectId), b.selectedEdges.map(selectId)));
150
150
  }
151
- function SelectionListenerInner({ onSelectionChange }) {
151
+ function SelectionListenerInner({ onSelectionChange, }) {
152
152
  const store = useStoreApi();
153
153
  const { selectedNodes, selectedEdges } = useStore(selector$m, areEqual);
154
154
  useEffect(() => {
@@ -159,7 +159,7 @@ function SelectionListenerInner({ onSelectionChange }) {
159
159
  return null;
160
160
  }
161
161
  const changeSelector = (s) => !!s.onSelectionChangeHandlers;
162
- function SelectionListener({ onSelectionChange }) {
162
+ function SelectionListener({ onSelectionChange, }) {
163
163
  const storeHasSelectionChangeHandlers = useStore(changeSelector);
164
164
  if (onSelectionChange || storeHasSelectionChangeHandlers) {
165
165
  return jsx(SelectionListenerInner, { onSelectionChange: onSelectionChange });
@@ -298,9 +298,9 @@ function StoreUpdater(props) {
298
298
  setPaneClickDistance(fieldValue);
299
299
  // Renamed fields
300
300
  else if (fieldName === 'fitView')
301
- store.setState({ fitViewOnInit: fieldValue });
301
+ store.setState({ fitViewQueued: fieldValue });
302
302
  else if (fieldName === 'fitViewOptions')
303
- store.setState({ fitViewOnInitOptions: fieldValue });
303
+ store.setState({ fitViewOptions: fieldValue });
304
304
  // General case
305
305
  else
306
306
  store.setState({ [fieldName]: fieldValue });
@@ -410,7 +410,7 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
410
410
  const target = options?.target || defaultDoc;
411
411
  if (keyCode !== null) {
412
412
  const downHandler = (event) => {
413
- modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey;
413
+ modifierPressed.current = event.ctrlKey || event.metaKey || event.shiftKey || event.altKey;
414
414
  const preventAction = (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) &&
415
415
  isInputDOMNode(event);
416
416
  if (preventAction) {
@@ -419,16 +419,15 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
419
419
  const keyOrCode = useKeyOrCode(event.code, keysToWatch);
420
420
  pressedKeys.current.add(event[keyOrCode]);
421
421
  if (isMatchingKey(keyCodes, pressedKeys.current, false)) {
422
- event.preventDefault();
422
+ const target = (event.composedPath?.()?.[0] || event.target);
423
+ const isInteractiveElement = target?.nodeName === 'BUTTON' || target?.nodeName === 'A';
424
+ if (options.preventDefault !== false && (modifierPressed.current || !isInteractiveElement)) {
425
+ event.preventDefault();
426
+ }
423
427
  setKeyPressed(true);
424
428
  }
425
429
  };
426
430
  const upHandler = (event) => {
427
- const preventAction = (!modifierPressed.current || (modifierPressed.current && !options.actInsideInputWithModifier)) &&
428
- isInputDOMNode(event);
429
- if (preventAction) {
430
- return false;
431
- }
432
431
  const keyOrCode = useKeyOrCode(event.code, keysToWatch);
433
432
  if (isMatchingKey(keyCodes, pressedKeys.current, true)) {
434
433
  setKeyPressed(false);
@@ -519,22 +518,6 @@ const useViewportHelper = () => {
519
518
  const [x, y, zoom] = store.getState().transform;
520
519
  return { x, y, zoom };
521
520
  },
522
- fitView: (options) => {
523
- const { nodeLookup, minZoom, maxZoom, panZoom, domNode } = store.getState();
524
- if (!panZoom || !domNode) {
525
- return Promise.resolve(false);
526
- }
527
- const fitViewNodes = getFitViewNodes(nodeLookup, options);
528
- const { width, height } = getDimensions(domNode);
529
- return fitView({
530
- nodes: fitViewNodes,
531
- width,
532
- height,
533
- minZoom,
534
- maxZoom,
535
- panZoom,
536
- }, options);
537
- },
538
521
  setCenter: async (x, y, options) => {
539
522
  const { width, height, maxZoom, panZoom } = store.getState();
540
523
  const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
@@ -1174,6 +1157,15 @@ function useReactFlow() {
1174
1157
  .getState()
1175
1158
  .connectionLookup.get(`${nodeId}${type ? (handleId ? `-${type}-${handleId}` : `-${type}`) : ''}`)
1176
1159
  ?.values() ?? []),
1160
+ fitView: async (options) => {
1161
+ // We either create a new Promise or reuse the existing one
1162
+ // Even if fitView is called multiple times in a row, we only end up with a single Promise
1163
+ const fitViewResolver = store.getState().fitViewResolver ?? Promise.withResolvers();
1164
+ // We schedule a fitView by setting fitViewQueued and triggering a setNodes
1165
+ store.setState({ fitViewQueued: true, fitViewOptions: options, fitViewResolver });
1166
+ batchContext.nodeQueue.push((nodes) => [...nodes]);
1167
+ return fitViewResolver.promise;
1168
+ },
1177
1169
  };
1178
1170
  }, []);
1179
1171
  return useMemo(() => {
@@ -1371,11 +1363,12 @@ const wrapHandler = (handler, containerRef) => {
1371
1363
  const selector$h = (s) => ({
1372
1364
  userSelectionActive: s.userSelectionActive,
1373
1365
  elementsSelectable: s.elementsSelectable,
1366
+ connectionInProgress: s.connection.inProgress,
1374
1367
  dragging: s.paneDragging,
1375
1368
  });
1376
1369
  function Pane({ isSelecting, selectionKeyPressed, selectionMode = SelectionMode.Full, panOnDrag, selectionOnDrag, onSelectionStart, onSelectionEnd, onPaneClick, onPaneContextMenu, onPaneScroll, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, children, }) {
1377
1370
  const store = useStoreApi();
1378
- const { userSelectionActive, elementsSelectable, dragging } = useStore(selector$h, shallow);
1371
+ const { userSelectionActive, elementsSelectable, dragging, connectionInProgress } = useStore(selector$h, shallow);
1379
1372
  const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
1380
1373
  const container = useRef(null);
1381
1374
  const containerBounds = useRef();
@@ -1386,7 +1379,8 @@ function Pane({ isSelecting, selectionKeyPressed, selectionMode = SelectionMode.
1386
1379
  const selectionStarted = useRef(false);
1387
1380
  const onClick = (event) => {
1388
1381
  // We prevent click events when the user let go of the selectionKey during a selection
1389
- if (selectionInProgress.current) {
1382
+ // We also prevent click events when a connection is in progress
1383
+ if (selectionInProgress.current || connectionInProgress) {
1390
1384
  selectionInProgress.current = false;
1391
1385
  return;
1392
1386
  }
@@ -1681,6 +1675,7 @@ const connectingSelector = (nodeId, handleId, type) => (state) => {
1681
1675
  ? fromHandle?.type !== type
1682
1676
  : nodeId !== fromHandle?.nodeId || handleId !== fromHandle?.id,
1683
1677
  connectionInProcess: !!fromHandle,
1678
+ clickConnectionInProcess: !!clickHandle,
1684
1679
  valid: connectingTo && isValid,
1685
1680
  };
1686
1681
  };
@@ -1690,7 +1685,7 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1690
1685
  const store = useStoreApi();
1691
1686
  const nodeId = useNodeId();
1692
1687
  const { connectOnClick, noPanClassName, rfId } = useStore(selector$g, shallow);
1693
- const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, valid } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1688
+ const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, clickConnectionInProcess, valid, } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1694
1689
  if (!nodeId) {
1695
1690
  store.getState().onError?.('010', errorMessages['error010']());
1696
1691
  }
@@ -1804,7 +1799,7 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1804
1799
  */
1805
1800
  connectionindicator: isConnectable &&
1806
1801
  (!connectionInProcess || isPossibleEndHandle) &&
1807
- (connectionInProcess ? isConnectableEnd : isConnectableStart),
1802
+ (connectionInProcess || clickConnectionInProcess ? isConnectableEnd : isConnectableStart),
1808
1803
  },
1809
1804
  ]), onMouseDown: onPointerDown, onTouchStart: onPointerDown, onClick: connectOnClick ? onClick : undefined, ref: ref, ...rest, children: children }));
1810
1805
  }
@@ -2309,7 +2304,7 @@ const MarkerDefinitions = ({ defaultColor, rfId }) => {
2309
2304
  MarkerDefinitions.displayName = 'MarkerDefinitions';
2310
2305
  var MarkerDefinitions$1 = memo(MarkerDefinitions);
2311
2306
 
2312
- function EdgeTextComponent({ x, y, label, labelStyle = {}, labelShowBg = true, labelBgStyle = {}, labelBgPadding = [2, 4], labelBgBorderRadius = 2, children, className, ...rest }) {
2307
+ function EdgeTextComponent({ x, y, label, labelStyle, labelShowBg = true, labelBgStyle, labelBgPadding = [2, 4], labelBgBorderRadius = 2, children, className, ...rest }) {
2313
2308
  const [edgeTextBbox, setEdgeTextBbox] = useState({ x: 1, y: 0, width: 0, height: 0 });
2314
2309
  const edgeTextClasses = cc(['react-flow__edge-textwrapper', className]);
2315
2310
  const edgeTextRef = useRef(null);
@@ -2324,7 +2319,7 @@ function EdgeTextComponent({ x, y, label, labelStyle = {}, labelShowBg = true, l
2324
2319
  });
2325
2320
  }
2326
2321
  }, [label]);
2327
- if (typeof label === 'undefined' || !label) {
2322
+ if (!label) {
2328
2323
  return null;
2329
2324
  }
2330
2325
  return (jsxs("g", { transform: `translate(${x - edgeTextBbox.width / 2} ${y - edgeTextBbox.height / 2})`, className: edgeTextClasses, visibility: edgeTextBbox.width ? 'visible' : 'hidden', ...rest, children: [labelShowBg && (jsx("rect", { width: edgeTextBbox.width + 2 * labelBgPadding[0], x: -labelBgPadding[0], y: -labelBgPadding[1], height: edgeTextBbox.height + 2 * labelBgPadding[1], className: "react-flow__edge-textbg", style: labelBgStyle, rx: labelBgBorderRadius, ry: labelBgBorderRadius })), jsx("text", { className: "react-flow__edge-text", y: edgeTextBbox.height / 2, dy: "0.3em", ref: edgeTextRef, style: labelStyle, children: label }), children] }));
@@ -2334,26 +2329,26 @@ EdgeTextComponent.displayName = 'EdgeText';
2334
2329
  * You can use the `<EdgeText />` component as a helper component to display text
2335
2330
  * within your custom edges.
2336
2331
  *
2337
- *@public
2332
+ * @public
2338
2333
  *
2339
- *@example
2340
- *```jsx
2341
- *import { EdgeText } from '@xyflow/react';
2334
+ * @example
2335
+ * ```jsx
2336
+ * import { EdgeText } from '@xyflow/react';
2342
2337
  *
2343
- *export function CustomEdgeLabel({ label }) {
2344
- * return (
2345
- * <EdgeText
2346
- * x={100}
2347
- * y={100}
2348
- * label={label}
2349
- * labelStyle={{ fill: 'white' }}
2350
- * labelShowBg
2351
- * labelBgStyle={{ fill: 'red' }}
2352
- * labelBgPadding={[2, 4]}
2353
- * labelBgBorderRadius={2}
2354
- * />
2355
- * );
2356
- *}
2338
+ * export function CustomEdgeLabel({ label }) {
2339
+ * return (
2340
+ * <EdgeText
2341
+ * x={100}
2342
+ * y={100}
2343
+ * label={label}
2344
+ * labelStyle={{ fill: 'white' }}
2345
+ * labelShowBg
2346
+ * labelBgStyle={{ fill: 'red' }}
2347
+ * labelBgPadding={[2, 4]}
2348
+ * labelBgBorderRadius={2}
2349
+ * />
2350
+ * );
2351
+ * }
2357
2352
  *```
2358
2353
  */
2359
2354
  const EdgeText = memo(EdgeTextComponent);
@@ -2470,7 +2465,33 @@ function createSmoothStepEdge(params) {
2470
2465
  return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
2471
2466
  });
2472
2467
  }
2468
+ /**
2469
+ * Component that can be used inside a custom edge to render a smooth step edge.
2470
+ *
2471
+ * @public
2472
+ * @example
2473
+ *
2474
+ * ```tsx
2475
+ * import { SmoothStepEdge } from '@xyflow/react';
2476
+ *
2477
+ * function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) {
2478
+ * return (
2479
+ * <SmoothStepEdge
2480
+ * sourceX={sourceX}
2481
+ * sourceY={sourceY}
2482
+ * targetX={targetX}
2483
+ * targetY={targetY}
2484
+ * sourcePosition={sourcePosition}
2485
+ * targetPosition={targetPosition}
2486
+ * />
2487
+ * );
2488
+ * }
2489
+ * ```
2490
+ */
2473
2491
  const SmoothStepEdge = createSmoothStepEdge({ isInternal: false });
2492
+ /**
2493
+ * @internal
2494
+ */
2474
2495
  const SmoothStepEdgeInternal = createSmoothStepEdge({ isInternal: true });
2475
2496
  SmoothStepEdge.displayName = 'SmoothStepEdge';
2476
2497
  SmoothStepEdgeInternal.displayName = 'SmoothStepEdgeInternal';
@@ -2482,7 +2503,33 @@ function createStepEdge(params) {
2482
2503
  return (jsx(SmoothStepEdge, { ...props, id: _id, pathOptions: useMemo(() => ({ borderRadius: 0, offset: props.pathOptions?.offset }), [props.pathOptions?.offset]) }));
2483
2504
  });
2484
2505
  }
2506
+ /**
2507
+ * Component that can be used inside a custom edge to render a step edge.
2508
+ *
2509
+ * @public
2510
+ * @example
2511
+ *
2512
+ * ```tsx
2513
+ * import { StepEdge } from '@xyflow/react';
2514
+ *
2515
+ * function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) {
2516
+ * return (
2517
+ * <StepEdge
2518
+ * sourceX={sourceX}
2519
+ * sourceY={sourceY}
2520
+ * targetX={targetX}
2521
+ * targetY={targetY}
2522
+ * sourcePosition={sourcePosition}
2523
+ * targetPosition={targetPosition}
2524
+ * />
2525
+ * );
2526
+ * }
2527
+ * ```
2528
+ */
2485
2529
  const StepEdge = createStepEdge({ isInternal: false });
2530
+ /**
2531
+ * @internal
2532
+ */
2486
2533
  const StepEdgeInternal = createStepEdge({ isInternal: true });
2487
2534
  StepEdge.displayName = 'StepEdge';
2488
2535
  StepEdgeInternal.displayName = 'StepEdgeInternal';
@@ -2495,7 +2542,31 @@ function createStraightEdge(params) {
2495
2542
  return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
2496
2543
  });
2497
2544
  }
2545
+ /**
2546
+ * Component that can be used inside a custom edge to render a straight line.
2547
+ *
2548
+ * @public
2549
+ * @example
2550
+ *
2551
+ * ```tsx
2552
+ * import { StraightEdge } from '@xyflow/react';
2553
+ *
2554
+ * function CustomEdge({ sourceX, sourceY, targetX, targetY }) {
2555
+ * return (
2556
+ * <StraightEdge
2557
+ * sourceX={sourceX}
2558
+ * sourceY={sourceY}
2559
+ * targetX={targetX}
2560
+ * targetY={targetY}
2561
+ * />
2562
+ * );
2563
+ * }
2564
+ * ```
2565
+ */
2498
2566
  const StraightEdge = createStraightEdge({ isInternal: false });
2567
+ /**
2568
+ * @internal
2569
+ */
2499
2570
  const StraightEdgeInternal = createStraightEdge({ isInternal: true });
2500
2571
  StraightEdge.displayName = 'StraightEdge';
2501
2572
  StraightEdgeInternal.displayName = 'StraightEdgeInternal';
@@ -2516,7 +2587,33 @@ function createBezierEdge(params) {
2516
2587
  return (jsx(BaseEdge, { id: _id, path: path, labelX: labelX, labelY: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius, style: style, markerEnd: markerEnd, markerStart: markerStart, interactionWidth: interactionWidth }));
2517
2588
  });
2518
2589
  }
2590
+ /**
2591
+ * Component that can be used inside a custom edge to render a bezier curve.
2592
+ *
2593
+ * @public
2594
+ * @example
2595
+ *
2596
+ * ```tsx
2597
+ * import { BezierEdge } from '@xyflow/react';
2598
+ *
2599
+ * function CustomEdge({ sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition }) {
2600
+ * return (
2601
+ * <BezierEdge
2602
+ * sourceX={sourceX}
2603
+ * sourceY={sourceY}
2604
+ * targetX={targetX}
2605
+ * targetY={targetY}
2606
+ * sourcePosition={sourcePosition}
2607
+ * targetPosition={targetPosition}
2608
+ * />
2609
+ * );
2610
+ * }
2611
+ * ```
2612
+ */
2519
2613
  const BezierEdge = createBezierEdge({ isInternal: false });
2614
+ /**
2615
+ * @internal
2616
+ */
2520
2617
  const BezierEdgeInternal = createBezierEdge({ isInternal: true });
2521
2618
  BezierEdge.displayName = 'BezierEdge';
2522
2619
  BezierEdgeInternal.displayName = 'BezierEdgeInternal';
@@ -2552,6 +2649,9 @@ const shiftY = (y, shift, position) => {
2552
2649
  return y;
2553
2650
  };
2554
2651
  const EdgeUpdaterClassName = 'react-flow__edgeupdater';
2652
+ /**
2653
+ * @internal
2654
+ */
2555
2655
  function EdgeAnchor({ position, centerX, centerY, radius = 10, onMouseDown, onMouseEnter, onMouseOut, type, }) {
2556
2656
  return (jsx("circle", { onMouseDown: onMouseDown, onMouseEnter: onMouseEnter, onMouseOut: onMouseOut, className: cc([EdgeUpdaterClassName, `${EdgeUpdaterClassName}-${type}`]), cx: shiftX(centerX, radius, position), cy: shiftY(centerY, radius, position), r: radius, stroke: "transparent", fill: "transparent" }));
2557
2657
  }
@@ -2992,11 +3092,11 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
2992
3092
  elementsSelectable: true,
2993
3093
  elevateNodesOnSelect: true,
2994
3094
  elevateEdgesOnSelect: false,
2995
- fitViewOnInit: false,
2996
- fitViewDone: false,
2997
- fitViewOnInitOptions: undefined,
2998
3095
  selectNodesOnDrag: true,
2999
3096
  multiSelectionActive: false,
3097
+ fitViewQueued: fitView ?? false,
3098
+ fitViewOptions: undefined,
3099
+ fitViewResolver: null,
3000
3100
  connection: { ...initialConnection },
3001
3101
  connectionClickStartHandle: null,
3002
3102
  connectOnClick: true,
@@ -3013,274 +3113,256 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
3013
3113
  };
3014
3114
  };
3015
3115
 
3016
- const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView: fitView$1, nodeOrigin, nodeExtent, }) => createWithEqualityFn((set, get) => ({
3017
- ...getInitialState({ nodes, edges, width, height, fitView: fitView$1, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
3018
- setNodes: (nodes) => {
3019
- const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
3020
- /*
3021
- * setNodes() is called exclusively in response to user actions:
3022
- * - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
3023
- * - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
3024
- *
3025
- * When this happens, we take the note objects passed by the user and extend them with fields
3026
- * relevant for internal React Flow operations.
3027
- */
3028
- adoptUserNodes(nodes, nodeLookup, parentLookup, {
3029
- nodeOrigin,
3030
- nodeExtent,
3031
- elevateNodesOnSelect,
3032
- checkEquality: true,
3033
- });
3034
- set({ nodes });
3035
- },
3036
- setEdges: (edges) => {
3037
- const { connectionLookup, edgeLookup } = get();
3038
- updateConnectionLookup(connectionLookup, edgeLookup, edges);
3039
- set({ edges });
3040
- },
3041
- setDefaultNodesAndEdges: (nodes, edges) => {
3042
- if (nodes) {
3043
- const { setNodes } = get();
3044
- setNodes(nodes);
3045
- set({ hasDefaultNodes: true });
3046
- }
3047
- if (edges) {
3048
- const { setEdges } = get();
3049
- setEdges(edges);
3050
- set({ hasDefaultEdges: true });
3051
- }
3052
- },
3053
- /*
3054
- * Every node gets registerd at a ResizeObserver. Whenever a node
3055
- * changes its dimensions, this function is called to measure the
3056
- * new dimensions and update the nodes.
3057
- */
3058
- updateNodeInternals: (updates, params = { triggerFitView: true }) => {
3059
- const { triggerNodeChanges, nodeLookup, parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, nodeExtent, debug, fitViewSync, } = get();
3060
- const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent);
3061
- if (!updatedInternals) {
3116
+ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView, nodeOrigin, nodeExtent, }) => createWithEqualityFn((set, get) => {
3117
+ async function resolveFitView() {
3118
+ const { nodeLookup, panZoom, fitViewOptions, fitViewResolver, width, height, minZoom, maxZoom } = get();
3119
+ if (!panZoom || !fitViewResolver) {
3062
3120
  return;
3063
3121
  }
3064
- updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
3065
- if (params.triggerFitView) {
3066
- // we call fitView once initially after all dimensions are set
3067
- let nextFitViewDone = fitViewDone;
3068
- if (!fitViewDone && fitViewOnInit) {
3069
- nextFitViewDone = fitViewSync({
3070
- ...fitViewOnInitOptions,
3071
- nodes: fitViewOnInitOptions?.nodes,
3072
- });
3073
- }
3122
+ await fitViewport({
3123
+ nodes: nodeLookup,
3124
+ width,
3125
+ height,
3126
+ panZoom,
3127
+ minZoom,
3128
+ maxZoom,
3129
+ }, fitViewOptions);
3130
+ fitViewResolver.resolve(true);
3131
+ /**
3132
+ * wait for the fitViewport to resolve before deleting the resolver,
3133
+ * we want to reuse the old resolver if the user calls fitView again in the mean time
3134
+ */
3135
+ set({ fitViewResolver: null });
3136
+ }
3137
+ return {
3138
+ ...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
3139
+ setNodes: (nodes) => {
3140
+ const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued } = get();
3074
3141
  /*
3075
- * here we are cirmumventing the onNodesChange handler
3076
- * in order to be able to display nodes even if the user
3077
- * has not provided an onNodesChange handler.
3078
- * Nodes are only rendered if they have a width and height
3079
- * attribute which they get from this handler.
3142
+ * setNodes() is called exclusively in response to user actions:
3143
+ * - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
3144
+ * - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
3145
+ *
3146
+ * When this happens, we take the note objects passed by the user and extend them with fields
3147
+ * relevant for internal React Flow operations.
3080
3148
  */
3081
- set({ fitViewDone: nextFitViewDone });
3082
- }
3083
- else {
3084
- // we always want to trigger useStore calls whenever updateNodeInternals is called
3085
- set({});
3086
- }
3087
- if (changes?.length > 0) {
3088
- if (debug) {
3089
- console.log('React Flow: trigger node changes', changes);
3149
+ const nodesInitialized = adoptUserNodes(nodes, nodeLookup, parentLookup, {
3150
+ nodeOrigin,
3151
+ nodeExtent,
3152
+ elevateNodesOnSelect,
3153
+ checkEquality: true,
3154
+ });
3155
+ if (fitViewQueued && nodesInitialized) {
3156
+ resolveFitView();
3157
+ set({ nodes, fitViewQueued: false, fitViewOptions: undefined });
3090
3158
  }
3091
- triggerNodeChanges?.(changes);
3092
- }
3093
- },
3094
- updateNodePositions: (nodeDragItems, dragging = false) => {
3095
- const parentExpandChildren = [];
3096
- const changes = [];
3097
- for (const [id, dragItem] of nodeDragItems) {
3098
- const expandParent = !!(dragItem?.expandParent && dragItem?.parentId && dragItem?.position);
3099
- const change = {
3100
- id,
3101
- type: 'position',
3102
- position: expandParent
3103
- ? {
3104
- x: Math.max(0, dragItem.position.x),
3105
- y: Math.max(0, dragItem.position.y),
3106
- }
3107
- : dragItem.position,
3108
- dragging,
3109
- };
3110
- if (expandParent) {
3111
- parentExpandChildren.push({
3159
+ else {
3160
+ set({ nodes });
3161
+ }
3162
+ },
3163
+ setEdges: (edges) => {
3164
+ const { connectionLookup, edgeLookup } = get();
3165
+ updateConnectionLookup(connectionLookup, edgeLookup, edges);
3166
+ set({ edges });
3167
+ },
3168
+ setDefaultNodesAndEdges: (nodes, edges) => {
3169
+ if (nodes) {
3170
+ const { setNodes } = get();
3171
+ setNodes(nodes);
3172
+ set({ hasDefaultNodes: true });
3173
+ }
3174
+ if (edges) {
3175
+ const { setEdges } = get();
3176
+ setEdges(edges);
3177
+ set({ hasDefaultEdges: true });
3178
+ }
3179
+ },
3180
+ /*
3181
+ * Every node gets registerd at a ResizeObserver. Whenever a node
3182
+ * changes its dimensions, this function is called to measure the
3183
+ * new dimensions and update the nodes.
3184
+ */
3185
+ updateNodeInternals: (updates) => {
3186
+ const { triggerNodeChanges, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent, debug, fitViewQueued } = get();
3187
+ const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent);
3188
+ if (!updatedInternals) {
3189
+ return;
3190
+ }
3191
+ updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
3192
+ if (fitViewQueued) {
3193
+ resolveFitView();
3194
+ set({ fitViewQueued: false, fitViewOptions: undefined });
3195
+ }
3196
+ else {
3197
+ // we always want to trigger useStore calls whenever updateNodeInternals is called
3198
+ set({});
3199
+ }
3200
+ if (changes?.length > 0) {
3201
+ if (debug) {
3202
+ console.log('React Flow: trigger node changes', changes);
3203
+ }
3204
+ triggerNodeChanges?.(changes);
3205
+ }
3206
+ },
3207
+ updateNodePositions: (nodeDragItems, dragging = false) => {
3208
+ const parentExpandChildren = [];
3209
+ const changes = [];
3210
+ const { nodeLookup, triggerNodeChanges } = get();
3211
+ for (const [id, dragItem] of nodeDragItems) {
3212
+ // we are using the nodelookup to be sure to use the current expandParent and parentId value
3213
+ const node = nodeLookup.get(id);
3214
+ const expandParent = !!(node?.expandParent && node?.parentId && dragItem?.position);
3215
+ const change = {
3112
3216
  id,
3113
- parentId: dragItem.parentId,
3114
- rect: {
3115
- ...dragItem.internals.positionAbsolute,
3116
- width: dragItem.measured.width,
3117
- height: dragItem.measured.height,
3118
- },
3119
- });
3217
+ type: 'position',
3218
+ position: expandParent
3219
+ ? {
3220
+ x: Math.max(0, dragItem.position.x),
3221
+ y: Math.max(0, dragItem.position.y),
3222
+ }
3223
+ : dragItem.position,
3224
+ dragging,
3225
+ };
3226
+ if (expandParent && node.parentId) {
3227
+ parentExpandChildren.push({
3228
+ id,
3229
+ parentId: node.parentId,
3230
+ rect: {
3231
+ ...dragItem.internals.positionAbsolute,
3232
+ width: dragItem.measured.width ?? 0,
3233
+ height: dragItem.measured.height ?? 0,
3234
+ },
3235
+ });
3236
+ }
3237
+ changes.push(change);
3120
3238
  }
3121
- changes.push(change);
3122
- }
3123
- if (parentExpandChildren.length > 0) {
3124
- const { nodeLookup, parentLookup, nodeOrigin } = get();
3125
- const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup, nodeOrigin);
3126
- changes.push(...parentExpandChanges);
3127
- }
3128
- get().triggerNodeChanges(changes);
3129
- },
3130
- triggerNodeChanges: (changes) => {
3131
- const { onNodesChange, setNodes, nodes, hasDefaultNodes, debug } = get();
3132
- if (changes?.length) {
3133
- if (hasDefaultNodes) {
3134
- const updatedNodes = applyNodeChanges(changes, nodes);
3135
- setNodes(updatedNodes);
3239
+ if (parentExpandChildren.length > 0) {
3240
+ const { parentLookup, nodeOrigin } = get();
3241
+ const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup, nodeOrigin);
3242
+ changes.push(...parentExpandChanges);
3243
+ }
3244
+ triggerNodeChanges(changes);
3245
+ },
3246
+ triggerNodeChanges: (changes) => {
3247
+ const { onNodesChange, setNodes, nodes, hasDefaultNodes, debug } = get();
3248
+ if (changes?.length) {
3249
+ if (hasDefaultNodes) {
3250
+ const updatedNodes = applyNodeChanges(changes, nodes);
3251
+ setNodes(updatedNodes);
3252
+ }
3253
+ if (debug) {
3254
+ console.log('React Flow: trigger node changes', changes);
3255
+ }
3256
+ onNodesChange?.(changes);
3136
3257
  }
3137
- if (debug) {
3138
- console.log('React Flow: trigger node changes', changes);
3258
+ },
3259
+ triggerEdgeChanges: (changes) => {
3260
+ const { onEdgesChange, setEdges, edges, hasDefaultEdges, debug } = get();
3261
+ if (changes?.length) {
3262
+ if (hasDefaultEdges) {
3263
+ const updatedEdges = applyEdgeChanges(changes, edges);
3264
+ setEdges(updatedEdges);
3265
+ }
3266
+ if (debug) {
3267
+ console.log('React Flow: trigger edge changes', changes);
3268
+ }
3269
+ onEdgesChange?.(changes);
3139
3270
  }
3140
- onNodesChange?.(changes);
3141
- }
3142
- },
3143
- triggerEdgeChanges: (changes) => {
3144
- const { onEdgesChange, setEdges, edges, hasDefaultEdges, debug } = get();
3145
- if (changes?.length) {
3146
- if (hasDefaultEdges) {
3147
- const updatedEdges = applyEdgeChanges(changes, edges);
3148
- setEdges(updatedEdges);
3271
+ },
3272
+ addSelectedNodes: (selectedNodeIds) => {
3273
+ const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
3274
+ if (multiSelectionActive) {
3275
+ const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
3276
+ triggerNodeChanges(nodeChanges);
3277
+ return;
3149
3278
  }
3150
- if (debug) {
3151
- console.log('React Flow: trigger edge changes', changes);
3279
+ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set([...selectedNodeIds]), true));
3280
+ triggerEdgeChanges(getSelectionChanges(edgeLookup));
3281
+ },
3282
+ addSelectedEdges: (selectedEdgeIds) => {
3283
+ const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
3284
+ if (multiSelectionActive) {
3285
+ const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
3286
+ triggerEdgeChanges(changedEdges);
3287
+ return;
3152
3288
  }
3153
- onEdgesChange?.(changes);
3154
- }
3155
- },
3156
- addSelectedNodes: (selectedNodeIds) => {
3157
- const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
3158
- if (multiSelectionActive) {
3159
- const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
3289
+ triggerEdgeChanges(getSelectionChanges(edgeLookup, new Set([...selectedEdgeIds])));
3290
+ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set(), true));
3291
+ },
3292
+ unselectNodesAndEdges: ({ nodes, edges } = {}) => {
3293
+ const { edges: storeEdges, nodes: storeNodes, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
3294
+ const nodesToUnselect = nodes ? nodes : storeNodes;
3295
+ const edgesToUnselect = edges ? edges : storeEdges;
3296
+ const nodeChanges = nodesToUnselect.map((n) => {
3297
+ const internalNode = nodeLookup.get(n.id);
3298
+ if (internalNode) {
3299
+ /*
3300
+ * we need to unselect the internal node that was selected previously before we
3301
+ * send the change to the user to prevent it to be selected while dragging the new node
3302
+ */
3303
+ internalNode.selected = false;
3304
+ }
3305
+ return createSelectionChange(n.id, false);
3306
+ });
3307
+ const edgeChanges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
3160
3308
  triggerNodeChanges(nodeChanges);
3161
- return;
3162
- }
3163
- triggerNodeChanges(getSelectionChanges(nodeLookup, new Set([...selectedNodeIds]), true));
3164
- triggerEdgeChanges(getSelectionChanges(edgeLookup));
3165
- },
3166
- addSelectedEdges: (selectedEdgeIds) => {
3167
- const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
3168
- if (multiSelectionActive) {
3169
- const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
3170
- triggerEdgeChanges(changedEdges);
3171
- return;
3172
- }
3173
- triggerEdgeChanges(getSelectionChanges(edgeLookup, new Set([...selectedEdgeIds])));
3174
- triggerNodeChanges(getSelectionChanges(nodeLookup, new Set(), true));
3175
- },
3176
- unselectNodesAndEdges: ({ nodes, edges } = {}) => {
3177
- const { edges: storeEdges, nodes: storeNodes, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
3178
- const nodesToUnselect = nodes ? nodes : storeNodes;
3179
- const edgesToUnselect = edges ? edges : storeEdges;
3180
- const nodeChanges = nodesToUnselect.map((n) => {
3181
- const internalNode = nodeLookup.get(n.id);
3182
- if (internalNode) {
3183
- /*
3184
- * we need to unselect the internal node that was selected previously before we
3185
- * send the change to the user to prevent it to be selected while dragging the new node
3186
- */
3187
- internalNode.selected = false;
3309
+ triggerEdgeChanges(edgeChanges);
3310
+ },
3311
+ setMinZoom: (minZoom) => {
3312
+ const { panZoom, maxZoom } = get();
3313
+ panZoom?.setScaleExtent([minZoom, maxZoom]);
3314
+ set({ minZoom });
3315
+ },
3316
+ setMaxZoom: (maxZoom) => {
3317
+ const { panZoom, minZoom } = get();
3318
+ panZoom?.setScaleExtent([minZoom, maxZoom]);
3319
+ set({ maxZoom });
3320
+ },
3321
+ setTranslateExtent: (translateExtent) => {
3322
+ get().panZoom?.setTranslateExtent(translateExtent);
3323
+ set({ translateExtent });
3324
+ },
3325
+ setPaneClickDistance: (clickDistance) => {
3326
+ get().panZoom?.setClickDistance(clickDistance);
3327
+ },
3328
+ resetSelectedElements: () => {
3329
+ const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
3330
+ const nodeChanges = nodes.reduce((res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res), []);
3331
+ const edgeChanges = edges.reduce((res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false)] : res), []);
3332
+ triggerNodeChanges(nodeChanges);
3333
+ triggerEdgeChanges(edgeChanges);
3334
+ },
3335
+ setNodeExtent: (nextNodeExtent) => {
3336
+ const { nodes, nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, nodeExtent } = get();
3337
+ if (nextNodeExtent[0][0] === nodeExtent[0][0] &&
3338
+ nextNodeExtent[0][1] === nodeExtent[0][1] &&
3339
+ nextNodeExtent[1][0] === nodeExtent[1][0] &&
3340
+ nextNodeExtent[1][1] === nodeExtent[1][1]) {
3341
+ return;
3188
3342
  }
3189
- return createSelectionChange(n.id, false);
3190
- });
3191
- const edgeChanges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
3192
- triggerNodeChanges(nodeChanges);
3193
- triggerEdgeChanges(edgeChanges);
3194
- },
3195
- setMinZoom: (minZoom) => {
3196
- const { panZoom, maxZoom } = get();
3197
- panZoom?.setScaleExtent([minZoom, maxZoom]);
3198
- set({ minZoom });
3199
- },
3200
- setMaxZoom: (maxZoom) => {
3201
- const { panZoom, minZoom } = get();
3202
- panZoom?.setScaleExtent([minZoom, maxZoom]);
3203
- set({ maxZoom });
3204
- },
3205
- setTranslateExtent: (translateExtent) => {
3206
- get().panZoom?.setTranslateExtent(translateExtent);
3207
- set({ translateExtent });
3208
- },
3209
- setPaneClickDistance: (clickDistance) => {
3210
- get().panZoom?.setClickDistance(clickDistance);
3211
- },
3212
- resetSelectedElements: () => {
3213
- const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
3214
- const nodeChanges = nodes.reduce((res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res), []);
3215
- const edgeChanges = edges.reduce((res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false)] : res), []);
3216
- triggerNodeChanges(nodeChanges);
3217
- triggerEdgeChanges(edgeChanges);
3218
- },
3219
- setNodeExtent: (nextNodeExtent) => {
3220
- const { nodes, nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, nodeExtent } = get();
3221
- if (nextNodeExtent[0][0] === nodeExtent[0][0] &&
3222
- nextNodeExtent[0][1] === nodeExtent[0][1] &&
3223
- nextNodeExtent[1][0] === nodeExtent[1][0] &&
3224
- nextNodeExtent[1][1] === nodeExtent[1][1]) {
3225
- return;
3226
- }
3227
- adoptUserNodes(nodes, nodeLookup, parentLookup, {
3228
- nodeOrigin,
3229
- nodeExtent: nextNodeExtent,
3230
- elevateNodesOnSelect,
3231
- checkEquality: false,
3232
- });
3233
- set({ nodeExtent: nextNodeExtent });
3234
- },
3235
- panBy: (delta) => {
3236
- const { transform, width, height, panZoom, translateExtent } = get();
3237
- return panBy({ delta, panZoom, transform, translateExtent, width, height });
3238
- },
3239
- fitView: (options) => {
3240
- const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
3241
- if (!panZoom) {
3242
- return Promise.resolve(false);
3243
- }
3244
- const fitViewNodes = getFitViewNodes(nodeLookup, options);
3245
- return fitView({
3246
- nodes: fitViewNodes,
3247
- width,
3248
- height,
3249
- panZoom,
3250
- minZoom,
3251
- maxZoom,
3252
- }, options);
3253
- },
3254
- /*
3255
- * we can't call an asnychronous function in updateNodeInternals
3256
- * for that we created this sync version of fitView
3257
- */
3258
- fitViewSync: (options) => {
3259
- const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
3260
- if (!panZoom) {
3261
- return false;
3262
- }
3263
- const fitViewNodes = getFitViewNodes(nodeLookup, options);
3264
- fitView({
3265
- nodes: fitViewNodes,
3266
- width,
3267
- height,
3268
- panZoom,
3269
- minZoom,
3270
- maxZoom,
3271
- }, options);
3272
- return fitViewNodes.size > 0;
3273
- },
3274
- cancelConnection: () => {
3275
- set({
3276
- connection: { ...initialConnection },
3277
- });
3278
- },
3279
- updateConnection: (connection) => {
3280
- set({ connection });
3281
- },
3282
- reset: () => set({ ...getInitialState() }),
3283
- }), Object.is);
3343
+ adoptUserNodes(nodes, nodeLookup, parentLookup, {
3344
+ nodeOrigin,
3345
+ nodeExtent: nextNodeExtent,
3346
+ elevateNodesOnSelect,
3347
+ checkEquality: false,
3348
+ });
3349
+ set({ nodeExtent: nextNodeExtent });
3350
+ },
3351
+ panBy: (delta) => {
3352
+ const { transform, width, height, panZoom, translateExtent } = get();
3353
+ return panBy({ delta, panZoom, transform, translateExtent, width, height });
3354
+ },
3355
+ cancelConnection: () => {
3356
+ set({
3357
+ connection: { ...initialConnection },
3358
+ });
3359
+ },
3360
+ updateConnection: (connection) => {
3361
+ set({ connection });
3362
+ },
3363
+ reset: () => set({ ...getInitialState() }),
3364
+ };
3365
+ }, Object.is);
3284
3366
 
3285
3367
  /**
3286
3368
  * The `<ReactFlowProvider />` component is a [context provider](https://react.dev/learn/passing-data-deeply-with-context#)
@@ -3387,37 +3469,38 @@ const selector$6 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-rende
3387
3469
  * Edges are SVG-based. If you want to render more complex labels you can use the
3388
3470
  * `<EdgeLabelRenderer />` component to access a div based renderer. This component
3389
3471
  * is a portal that renders the label in a `<div />` that is positioned on top of
3390
- * the edges. You can see an example usage of the component in the [edge label renderer](/examples/edges/edge-label-renderer) example.
3472
+ * the edges. You can see an example usage of the component in the
3473
+ * [edge label renderer example](/examples/edges/edge-label-renderer).
3391
3474
  * @public
3392
3475
  *
3393
3476
  * @example
3394
- *```jsx
3395
- *import React from 'react';
3396
- *import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';
3477
+ * ```jsx
3478
+ * import React from 'react';
3479
+ * import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';
3397
3480
  *
3398
- *export function CustomEdge({ id, data, ...props }) {
3399
- * const [edgePath, labelX, labelY] = getBezierPath(props);
3481
+ * export function CustomEdge({ id, data, ...props }) {
3482
+ * const [edgePath, labelX, labelY] = getBezierPath(props);
3400
3483
  *
3401
- * return (
3402
- * <>
3403
- * <BaseEdge id={id} path={edgePath} />
3404
- * <EdgeLabelRenderer>
3405
- * <div
3406
- * style={{
3407
- * position: 'absolute',
3408
- * transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
3409
- * background: '#ffcc00',
3410
- * padding: 10,
3411
- * }}
3412
- * className="nodrag nopan"
3413
- * >
3414
- * {data.label}
3415
- * </div>
3416
- * </EdgeLabelRenderer>
3417
- * </>
3418
- * );
3419
- *};
3420
- *```
3484
+ * return (
3485
+ * <>
3486
+ * <BaseEdge id={id} path={edgePath} />
3487
+ * <EdgeLabelRenderer>
3488
+ * <div
3489
+ * style={{
3490
+ * position: 'absolute',
3491
+ * transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
3492
+ * background: '#ffcc00',
3493
+ * padding: 10,
3494
+ * }}
3495
+ * className="nodrag nopan"
3496
+ * >
3497
+ * {data.label}
3498
+ * </div>
3499
+ * </EdgeLabelRenderer>
3500
+ * </>
3501
+ * );
3502
+ * };
3503
+ * ```
3421
3504
  *
3422
3505
  * @remarks The `<EdgeLabelRenderer />` has no pointer events by default. If you want to
3423
3506
  * add mouse interactions you need to set the style `pointerEvents: all` and add
@@ -3775,7 +3858,7 @@ function useOnViewportChange({ onStart, onChange, onEnd }) {
3775
3858
  *
3776
3859
  * @remarks You need to memoize the passed `onChange` handler, otherwise the hook will not work correctly.
3777
3860
  */
3778
- function useOnSelectionChange({ onChange }) {
3861
+ function useOnSelectionChange({ onChange, }) {
3779
3862
  const store = useStoreApi();
3780
3863
  useEffect(() => {
3781
3864
  const nextOnSelectionChangeHandlers = [...store.getState().onSelectionChangeHandlers, onChange];
@@ -3888,7 +3971,7 @@ const error014 = errorMessages['error014']();
3888
3971
  *
3889
3972
  *export default function () {
3890
3973
  * const connections = useNodeConnections({
3891
- * type: 'target',
3974
+ * handleType: 'target',
3892
3975
  * handleId: 'my-handle',
3893
3976
  * });
3894
3977
  *
@@ -4240,6 +4323,7 @@ var MiniMapNodes$1 = memo(MiniMapNodes);
4240
4323
 
4241
4324
  const defaultWidth = 200;
4242
4325
  const defaultHeight = 150;
4326
+ const filterHidden = (node) => !node.hidden;
4243
4327
  const selector$1 = (s) => {
4244
4328
  const viewBB = {
4245
4329
  x: -s.transform[0] / s.transform[2],
@@ -4249,7 +4333,9 @@ const selector$1 = (s) => {
4249
4333
  };
4250
4334
  return {
4251
4335
  viewBB,
4252
- boundingRect: s.nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup), viewBB) : viewBB,
4336
+ boundingRect: s.nodeLookup.size > 0
4337
+ ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup, { filter: filterHidden }), viewBB)
4338
+ : viewBB,
4253
4339
  rfId: s.rfId,
4254
4340
  panZoom: s.panZoom,
4255
4341
  translateExtent: s.translateExtent,
@@ -4388,8 +4474,8 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
4388
4474
  const node = nodeLookup.get(id);
4389
4475
  if (node && node.expandParent && node.parentId) {
4390
4476
  const origin = node.origin ?? nodeOrigin;
4391
- const width = change.width ?? node.measured.width;
4392
- const height = change.height ?? node.measured.height;
4477
+ const width = change.width ?? node.measured.width ?? 0;
4478
+ const height = change.height ?? node.measured.height ?? 0;
4393
4479
  const child = {
4394
4480
  id: node.id,
4395
4481
  parentId: node.parentId,