@xyflow/react 12.4.2 → 12.4.4

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 (230) hide show
  1. package/dist/esm/additional-components/Background/Background.d.ts +53 -0
  2. package/dist/esm/additional-components/Background/Background.d.ts.map +1 -1
  3. package/dist/esm/additional-components/Background/types.d.ts +11 -1
  4. package/dist/esm/additional-components/Background/types.d.ts.map +1 -1
  5. package/dist/esm/additional-components/Controls/ControlButton.d.ts +23 -0
  6. package/dist/esm/additional-components/Controls/ControlButton.d.ts.map +1 -1
  7. package/dist/esm/additional-components/Controls/Controls.d.ts +21 -0
  8. package/dist/esm/additional-components/Controls/Controls.d.ts.map +1 -1
  9. package/dist/esm/additional-components/Controls/types.d.ts +8 -1
  10. package/dist/esm/additional-components/Controls/types.d.ts.map +1 -1
  11. package/dist/esm/additional-components/MiniMap/MiniMap.d.ts +20 -0
  12. package/dist/esm/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  13. package/dist/esm/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  14. package/dist/esm/additional-components/MiniMap/types.d.ts +11 -1
  15. package/dist/esm/additional-components/MiniMap/types.d.ts.map +1 -1
  16. package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts +5 -0
  17. package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  18. package/dist/esm/additional-components/NodeResizer/NodeResizer.d.ts +24 -0
  19. package/dist/esm/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -1
  20. package/dist/esm/additional-components/NodeResizer/types.d.ts +15 -3
  21. package/dist/esm/additional-components/NodeResizer/types.d.ts.map +1 -1
  22. package/dist/esm/additional-components/NodeToolbar/NodeToolbar.d.ts +35 -0
  23. package/dist/esm/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -1
  24. package/dist/esm/additional-components/NodeToolbar/types.d.ts +7 -2
  25. package/dist/esm/additional-components/NodeToolbar/types.d.ts.map +1 -1
  26. package/dist/esm/components/BatchProvider/index.d.ts.map +1 -1
  27. package/dist/esm/components/BatchProvider/useQueue.d.ts.map +1 -1
  28. package/dist/esm/components/ConnectionLine/index.d.ts +4 -4
  29. package/dist/esm/components/ConnectionLine/index.d.ts.map +1 -1
  30. package/dist/esm/components/EdgeLabelRenderer/index.d.ts +40 -0
  31. package/dist/esm/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  32. package/dist/esm/components/EdgeWrapper/index.d.ts +1 -1
  33. package/dist/esm/components/EdgeWrapper/index.d.ts.map +1 -1
  34. package/dist/esm/components/Edges/BaseEdge.d.ts +27 -0
  35. package/dist/esm/components/Edges/BaseEdge.d.ts.map +1 -1
  36. package/dist/esm/components/Edges/EdgeText.d.ts +26 -0
  37. package/dist/esm/components/Edges/EdgeText.d.ts.map +1 -1
  38. package/dist/esm/components/Edges/SimpleBezierEdge.d.ts +5 -0
  39. package/dist/esm/components/Edges/SimpleBezierEdge.d.ts.map +1 -1
  40. package/dist/esm/components/Edges/index.d.ts.map +1 -1
  41. package/dist/esm/components/Handle/index.d.ts +32 -4
  42. package/dist/esm/components/Handle/index.d.ts.map +1 -1
  43. package/dist/esm/components/NodeWrapper/index.d.ts +1 -1
  44. package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
  45. package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts.map +1 -1
  46. package/dist/esm/components/NodeWrapper/utils.d.ts.map +1 -1
  47. package/dist/esm/components/Nodes/utils.d.ts.map +1 -1
  48. package/dist/esm/components/Panel/index.d.ts +32 -4
  49. package/dist/esm/components/Panel/index.d.ts.map +1 -1
  50. package/dist/esm/components/ReactFlowProvider/index.d.ts +34 -0
  51. package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -1
  52. package/dist/esm/components/SelectionListener/index.d.ts +4 -4
  53. package/dist/esm/components/SelectionListener/index.d.ts.map +1 -1
  54. package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
  55. package/dist/esm/components/ViewportPortal/index.d.ts +25 -0
  56. package/dist/esm/components/ViewportPortal/index.d.ts.map +1 -1
  57. package/dist/esm/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
  58. package/dist/esm/container/NodeRenderer/index.d.ts.map +1 -1
  59. package/dist/esm/container/Pane/index.d.ts.map +1 -1
  60. package/dist/esm/container/ReactFlow/Wrapper.d.ts.map +1 -1
  61. package/dist/esm/container/ReactFlow/index.d.ts +21 -1
  62. package/dist/esm/container/ReactFlow/index.d.ts.map +1 -1
  63. package/dist/esm/contexts/NodeIdContext.d.ts +28 -0
  64. package/dist/esm/contexts/NodeIdContext.d.ts.map +1 -1
  65. package/dist/esm/hooks/useConnection.d.ts +20 -1
  66. package/dist/esm/hooks/useConnection.d.ts.map +1 -1
  67. package/dist/esm/hooks/useEdges.d.ts +13 -1
  68. package/dist/esm/hooks/useEdges.d.ts.map +1 -1
  69. package/dist/esm/hooks/useInternalNode.d.ts +21 -1
  70. package/dist/esm/hooks/useInternalNode.d.ts.map +1 -1
  71. package/dist/esm/hooks/useKeyPress.d.ts +19 -1
  72. package/dist/esm/hooks/useKeyPress.d.ts.map +1 -1
  73. package/dist/esm/hooks/useMoveSelectedNodes.d.ts.map +1 -1
  74. package/dist/esm/hooks/useNodeConnections.d.ts +17 -1
  75. package/dist/esm/hooks/useNodeConnections.d.ts.map +1 -1
  76. package/dist/esm/hooks/useNodes.d.ts +14 -1
  77. package/dist/esm/hooks/useNodes.d.ts.map +1 -1
  78. package/dist/esm/hooks/useNodesData.d.ts +14 -2
  79. package/dist/esm/hooks/useNodesData.d.ts.map +1 -1
  80. package/dist/esm/hooks/useNodesEdgesState.d.ts +62 -2
  81. package/dist/esm/hooks/useNodesEdgesState.d.ts.map +1 -1
  82. package/dist/esm/hooks/useNodesInitialized.d.ts +27 -1
  83. package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -1
  84. package/dist/esm/hooks/useOnSelectionChange.d.ts +37 -5
  85. package/dist/esm/hooks/useOnSelectionChange.d.ts.map +1 -1
  86. package/dist/esm/hooks/useOnViewportChange.d.ts +19 -1
  87. package/dist/esm/hooks/useOnViewportChange.d.ts.map +1 -1
  88. package/dist/esm/hooks/useReactFlow.d.ts +24 -1
  89. package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
  90. package/dist/esm/hooks/useStore.d.ts +23 -2
  91. package/dist/esm/hooks/useStore.d.ts.map +1 -1
  92. package/dist/esm/hooks/useUpdateNodeInternals.d.ts +39 -1
  93. package/dist/esm/hooks/useUpdateNodeInternals.d.ts.map +1 -1
  94. package/dist/esm/hooks/useViewport.d.ts +24 -1
  95. package/dist/esm/hooks/useViewport.d.ts.map +1 -1
  96. package/dist/esm/hooks/useViewportHelper.d.ts.map +1 -1
  97. package/dist/esm/index.js +1107 -198
  98. package/dist/esm/index.mjs +1107 -198
  99. package/dist/esm/store/index.d.ts.map +1 -1
  100. package/dist/esm/types/component-props.d.ts +112 -57
  101. package/dist/esm/types/component-props.d.ts.map +1 -1
  102. package/dist/esm/types/edges.d.ts +27 -2
  103. package/dist/esm/types/edges.d.ts.map +1 -1
  104. package/dist/esm/types/general.d.ts +48 -7
  105. package/dist/esm/types/general.d.ts.map +1 -1
  106. package/dist/esm/types/instance.d.ts +11 -1
  107. package/dist/esm/types/instance.d.ts.map +1 -1
  108. package/dist/esm/types/nodes.d.ts +46 -4
  109. package/dist/esm/types/nodes.d.ts.map +1 -1
  110. package/dist/esm/types/store.d.ts +2 -2
  111. package/dist/esm/types/store.d.ts.map +1 -1
  112. package/dist/esm/utils/changes.d.ts +45 -23
  113. package/dist/esm/utils/changes.d.ts.map +1 -1
  114. package/dist/esm/utils/general.d.ts +27 -3
  115. package/dist/esm/utils/general.d.ts.map +1 -1
  116. package/dist/umd/additional-components/Background/Background.d.ts +53 -0
  117. package/dist/umd/additional-components/Background/Background.d.ts.map +1 -1
  118. package/dist/umd/additional-components/Background/types.d.ts +11 -1
  119. package/dist/umd/additional-components/Background/types.d.ts.map +1 -1
  120. package/dist/umd/additional-components/Controls/ControlButton.d.ts +23 -0
  121. package/dist/umd/additional-components/Controls/ControlButton.d.ts.map +1 -1
  122. package/dist/umd/additional-components/Controls/Controls.d.ts +21 -0
  123. package/dist/umd/additional-components/Controls/Controls.d.ts.map +1 -1
  124. package/dist/umd/additional-components/Controls/types.d.ts +8 -1
  125. package/dist/umd/additional-components/Controls/types.d.ts.map +1 -1
  126. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts +20 -0
  127. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  128. package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  129. package/dist/umd/additional-components/MiniMap/types.d.ts +11 -1
  130. package/dist/umd/additional-components/MiniMap/types.d.ts.map +1 -1
  131. package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts +5 -0
  132. package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  133. package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts +24 -0
  134. package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -1
  135. package/dist/umd/additional-components/NodeResizer/types.d.ts +15 -3
  136. package/dist/umd/additional-components/NodeResizer/types.d.ts.map +1 -1
  137. package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts +35 -0
  138. package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -1
  139. package/dist/umd/additional-components/NodeToolbar/types.d.ts +7 -2
  140. package/dist/umd/additional-components/NodeToolbar/types.d.ts.map +1 -1
  141. package/dist/umd/components/BatchProvider/index.d.ts.map +1 -1
  142. package/dist/umd/components/BatchProvider/useQueue.d.ts.map +1 -1
  143. package/dist/umd/components/ConnectionLine/index.d.ts +4 -4
  144. package/dist/umd/components/ConnectionLine/index.d.ts.map +1 -1
  145. package/dist/umd/components/EdgeLabelRenderer/index.d.ts +40 -0
  146. package/dist/umd/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  147. package/dist/umd/components/EdgeWrapper/index.d.ts +1 -1
  148. package/dist/umd/components/EdgeWrapper/index.d.ts.map +1 -1
  149. package/dist/umd/components/Edges/BaseEdge.d.ts +27 -0
  150. package/dist/umd/components/Edges/BaseEdge.d.ts.map +1 -1
  151. package/dist/umd/components/Edges/EdgeText.d.ts +26 -0
  152. package/dist/umd/components/Edges/EdgeText.d.ts.map +1 -1
  153. package/dist/umd/components/Edges/SimpleBezierEdge.d.ts +5 -0
  154. package/dist/umd/components/Edges/SimpleBezierEdge.d.ts.map +1 -1
  155. package/dist/umd/components/Edges/index.d.ts.map +1 -1
  156. package/dist/umd/components/Handle/index.d.ts +32 -4
  157. package/dist/umd/components/Handle/index.d.ts.map +1 -1
  158. package/dist/umd/components/NodeWrapper/index.d.ts +1 -1
  159. package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
  160. package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts.map +1 -1
  161. package/dist/umd/components/NodeWrapper/utils.d.ts.map +1 -1
  162. package/dist/umd/components/Nodes/utils.d.ts.map +1 -1
  163. package/dist/umd/components/Panel/index.d.ts +32 -4
  164. package/dist/umd/components/Panel/index.d.ts.map +1 -1
  165. package/dist/umd/components/ReactFlowProvider/index.d.ts +34 -0
  166. package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -1
  167. package/dist/umd/components/SelectionListener/index.d.ts +4 -4
  168. package/dist/umd/components/SelectionListener/index.d.ts.map +1 -1
  169. package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
  170. package/dist/umd/components/ViewportPortal/index.d.ts +25 -0
  171. package/dist/umd/components/ViewportPortal/index.d.ts.map +1 -1
  172. package/dist/umd/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
  173. package/dist/umd/container/NodeRenderer/index.d.ts.map +1 -1
  174. package/dist/umd/container/Pane/index.d.ts.map +1 -1
  175. package/dist/umd/container/ReactFlow/Wrapper.d.ts.map +1 -1
  176. package/dist/umd/container/ReactFlow/index.d.ts +21 -1
  177. package/dist/umd/container/ReactFlow/index.d.ts.map +1 -1
  178. package/dist/umd/contexts/NodeIdContext.d.ts +28 -0
  179. package/dist/umd/contexts/NodeIdContext.d.ts.map +1 -1
  180. package/dist/umd/hooks/useConnection.d.ts +20 -1
  181. package/dist/umd/hooks/useConnection.d.ts.map +1 -1
  182. package/dist/umd/hooks/useEdges.d.ts +13 -1
  183. package/dist/umd/hooks/useEdges.d.ts.map +1 -1
  184. package/dist/umd/hooks/useInternalNode.d.ts +21 -1
  185. package/dist/umd/hooks/useInternalNode.d.ts.map +1 -1
  186. package/dist/umd/hooks/useKeyPress.d.ts +19 -1
  187. package/dist/umd/hooks/useKeyPress.d.ts.map +1 -1
  188. package/dist/umd/hooks/useMoveSelectedNodes.d.ts.map +1 -1
  189. package/dist/umd/hooks/useNodeConnections.d.ts +17 -1
  190. package/dist/umd/hooks/useNodeConnections.d.ts.map +1 -1
  191. package/dist/umd/hooks/useNodes.d.ts +14 -1
  192. package/dist/umd/hooks/useNodes.d.ts.map +1 -1
  193. package/dist/umd/hooks/useNodesData.d.ts +14 -2
  194. package/dist/umd/hooks/useNodesData.d.ts.map +1 -1
  195. package/dist/umd/hooks/useNodesEdgesState.d.ts +62 -2
  196. package/dist/umd/hooks/useNodesEdgesState.d.ts.map +1 -1
  197. package/dist/umd/hooks/useNodesInitialized.d.ts +27 -1
  198. package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
  199. package/dist/umd/hooks/useOnSelectionChange.d.ts +37 -5
  200. package/dist/umd/hooks/useOnSelectionChange.d.ts.map +1 -1
  201. package/dist/umd/hooks/useOnViewportChange.d.ts +19 -1
  202. package/dist/umd/hooks/useOnViewportChange.d.ts.map +1 -1
  203. package/dist/umd/hooks/useReactFlow.d.ts +24 -1
  204. package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
  205. package/dist/umd/hooks/useStore.d.ts +23 -2
  206. package/dist/umd/hooks/useStore.d.ts.map +1 -1
  207. package/dist/umd/hooks/useUpdateNodeInternals.d.ts +39 -1
  208. package/dist/umd/hooks/useUpdateNodeInternals.d.ts.map +1 -1
  209. package/dist/umd/hooks/useViewport.d.ts +24 -1
  210. package/dist/umd/hooks/useViewport.d.ts.map +1 -1
  211. package/dist/umd/hooks/useViewportHelper.d.ts.map +1 -1
  212. package/dist/umd/index.js +2 -2
  213. package/dist/umd/store/index.d.ts.map +1 -1
  214. package/dist/umd/types/component-props.d.ts +112 -57
  215. package/dist/umd/types/component-props.d.ts.map +1 -1
  216. package/dist/umd/types/edges.d.ts +27 -2
  217. package/dist/umd/types/edges.d.ts.map +1 -1
  218. package/dist/umd/types/general.d.ts +48 -7
  219. package/dist/umd/types/general.d.ts.map +1 -1
  220. package/dist/umd/types/instance.d.ts +11 -1
  221. package/dist/umd/types/instance.d.ts.map +1 -1
  222. package/dist/umd/types/nodes.d.ts +46 -4
  223. package/dist/umd/types/nodes.d.ts.map +1 -1
  224. package/dist/umd/types/store.d.ts +2 -2
  225. package/dist/umd/types/store.d.ts.map +1 -1
  226. package/dist/umd/utils/changes.d.ts +45 -23
  227. package/dist/umd/utils/changes.d.ts.map +1 -1
  228. package/dist/umd/utils/general.d.ts +27 -3
  229. package/dist/umd/utils/general.d.ts.map +1 -1
  230. package/package.json +4 -3
package/dist/esm/index.js CHANGED
@@ -1,9 +1,9 @@
1
1
  "use client"
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
+ import { createContext, useContext, useMemo, forwardRef, useEffect, useRef, useState, useLayoutEffect, useCallback, memo } from 'react';
3
4
  import cc from 'classcat';
4
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
6
  export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, ResizeControlVariant, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, reconnectEdge } from '@xyflow/system';
6
- import { createContext, useContext, useMemo, forwardRef, useEffect, useRef, useState, useLayoutEffect, useCallback, memo } from 'react';
7
7
  import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
8
8
  import { shallow } from 'zustand/shallow';
9
9
  import { createPortal } from 'react-dom';
@@ -13,7 +13,9 @@ const Provider$1 = StoreContext.Provider;
13
13
 
14
14
  const zustandErrorMessage = errorMessages['error001']();
15
15
  /**
16
- * Hook for accessing the internal store. Should only be used in rare cases.
16
+ * This hook can be used to subscribe to internal state changes of the React Flow
17
+ * component. The `useStore` hook is re-exported from the [Zustand](https://github.com/pmndrs/zustand)
18
+ * state management library, so you should check out their docs for more details.
17
19
  *
18
20
  * @public
19
21
  * @param selector
@@ -21,8 +23,13 @@ const zustandErrorMessage = errorMessages['error001']();
21
23
  * @returns The selected state slice
22
24
  *
23
25
  * @example
24
- * const nodes = useStore((state: ReactFlowState<MyNodeType>) => state.nodes);
26
+ * ```ts
27
+ * const nodes = useStore((state) => state.nodes);
28
+ * ```
25
29
  *
30
+ * @remarks This hook should only be used if there is no other way to access the internal
31
+ * state. For many of the common use cases, there are dedicated hooks available
32
+ * such as {@link useReactFlow}, {@link useViewport}, etc.
26
33
  */
27
34
  function useStore(selector, equalityFn) {
28
35
  const store = useContext(StoreContext);
@@ -31,6 +38,20 @@ function useStore(selector, equalityFn) {
31
38
  }
32
39
  return useStoreWithEqualityFn(store, selector, equalityFn);
33
40
  }
41
+ /**
42
+ * In some cases, you might need to access the store directly. This hook returns the store object which can be used on demand to access the state or dispatch actions.
43
+ *
44
+ * @returns The store object
45
+ *
46
+ * @example
47
+ * ```ts
48
+ * const store = useStoreApi();
49
+ * ```
50
+ *
51
+ * @remarks This hook should only be used if there is no other way to access the internal
52
+ * state. For many of the common use cases, there are dedicated hooks available
53
+ * such as {@link useReactFlow}, {@link useViewport}, etc.
54
+ */
34
55
  function useStoreApi() {
35
56
  const store = useContext(StoreContext);
36
57
  if (store === null) {
@@ -68,11 +89,37 @@ function A11yDescriptions({ rfId, disableKeyboardA11y }) {
68
89
  }
69
90
 
70
91
  const selector$n = (s) => (s.userSelectionActive ? 'none' : 'all');
92
+ /**
93
+ * The `<Panel />` component helps you position content above the viewport.
94
+ * It is used internally by the [`<MiniMap />`](/api-reference/components/minimap)
95
+ * and [`<Controls />`](/api-reference/components/controls) components.
96
+ *
97
+ * @public
98
+ *
99
+ * @example
100
+ * ```jsx
101
+ *import { ReactFlow, Background, Panel } from '@xyflow/react';
102
+ *
103
+ *export default function Flow() {
104
+ * return (
105
+ * <ReactFlow nodes={[]} fitView>
106
+ * <Panel position="top-left">top-left</Panel>
107
+ * <Panel position="top-center">top-center</Panel>
108
+ * <Panel position="top-right">top-right</Panel>
109
+ * <Panel position="bottom-left">bottom-left</Panel>
110
+ * <Panel position="bottom-center">bottom-center</Panel>
111
+ * <Panel position="bottom-right">bottom-right</Panel>
112
+ * </ReactFlow>
113
+ * );
114
+ *}
115
+ *```
116
+ */
71
117
  const Panel = forwardRef(({ position = 'top-left', children, className, style, ...rest }, ref) => {
72
118
  const pointerEvents = useStore(selector$n);
73
119
  const positionClasses = `${position}`.split('-');
74
120
  return (jsx("div", { className: cc(['react-flow__panel', className, ...positionClasses]), style: { ...style, pointerEvents }, ref: ref, ...rest, children: children }));
75
121
  });
122
+ Panel.displayName = 'Panel';
76
123
 
77
124
  function Attribution({ proOptions, position = 'bottom-right' }) {
78
125
  if (proOptions?.hideAttribution) {
@@ -101,7 +148,7 @@ function areEqual(a, b) {
101
148
  return (shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
102
149
  shallow(a.selectedEdges.map(selectId), b.selectedEdges.map(selectId)));
103
150
  }
104
- function SelectionListenerInner({ onSelectionChange }) {
151
+ function SelectionListenerInner({ onSelectionChange, }) {
105
152
  const store = useStoreApi();
106
153
  const { selectedNodes, selectedEdges } = useStore(selector$m, areEqual);
107
154
  useEffect(() => {
@@ -112,7 +159,7 @@ function SelectionListenerInner({ onSelectionChange }) {
112
159
  return null;
113
160
  }
114
161
  const changeSelector = (s) => !!s.onSelectionChangeHandlers;
115
- function SelectionListener({ onSelectionChange }) {
162
+ function SelectionListener({ onSelectionChange, }) {
116
163
  const storeHasSelectionChangeHandlers = useStore(changeSelector);
117
164
  if (onSelectionChange || storeHasSelectionChangeHandlers) {
118
165
  return jsx(SelectionListenerInner, { onSelectionChange: onSelectionChange });
@@ -200,9 +247,11 @@ const selector$l = (s) => ({
200
247
  setPaneClickDistance: s.setPaneClickDistance,
201
248
  });
202
249
  const initPrevValues = {
203
- // these are values that are also passed directly to other components
204
- // than the StoreUpdater. We can reduce the number of setStore calls
205
- // by setting the same values here as prev fields.
250
+ /*
251
+ * these are values that are also passed directly to other components
252
+ * than the StoreUpdater. We can reduce the number of setStore calls
253
+ * by setting the same values here as prev fields.
254
+ */
206
255
  translateExtent: infiniteExtent,
207
256
  nodeOrigin: defaultNodeOrigin,
208
257
  minZoom: 0.5,
@@ -295,38 +344,62 @@ function useColorModeClass(colorMode) {
295
344
 
296
345
  const defaultDoc = typeof document !== 'undefined' ? document : null;
297
346
  /**
298
- * Hook for handling key events.
347
+ * This hook lets you listen for specific key codes and tells you whether they are
348
+ * currently pressed or not.
299
349
  *
300
350
  * @public
301
351
  * @param param.keyCode - The key code (string or array of strings) to use
302
352
  * @param param.options - Options
303
353
  * @returns boolean
354
+ *
355
+ * @example
356
+ * ```tsx
357
+ *import { useKeyPress } from '@xyflow/react';
358
+ *
359
+ *export default function () {
360
+ * const spacePressed = useKeyPress('Space');
361
+ * const cmdAndSPressed = useKeyPress(['Meta+s', 'Strg+s']);
362
+ *
363
+ * return (
364
+ * <div>
365
+ * {spacePressed && <p>Space pressed!</p>}
366
+ * {cmdAndSPressed && <p>Cmd + S pressed!</p>}
367
+ * </div>
368
+ * );
369
+ *}
370
+ *```
304
371
  */
305
372
  function useKeyPress(
306
- // the keycode can be a string 'a' or an array of strings ['a', 'a+d']
307
- // a string means a single key 'a' or a combination when '+' is used 'a+d'
308
- // an array means different possibilites. Explainer: ['a', 'd+s'] here the
309
- // user can use the single key 'a' or the combination 'd' + 's'
373
+ /*
374
+ * the keycode can be a string 'a' or an array of strings ['a', 'a+d']
375
+ * a string means a single key 'a' or a combination when '+' is used 'a+d'
376
+ * an array means different possibilites. Explainer: ['a', 'd+s'] here the
377
+ * user can use the single key 'a' or the combination 'd' + 's'
378
+ */
310
379
  keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true }) {
311
380
  const [keyPressed, setKeyPressed] = useState(false);
312
381
  // we need to remember if a modifier key is pressed in order to track it
313
382
  const modifierPressed = useRef(false);
314
383
  // we need to remember the pressed keys in order to support combinations
315
384
  const pressedKeys = useRef(new Set([]));
316
- // keyCodes = array with single keys [['a']] or key combinations [['a', 's']]
317
- // keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft']
318
- // used to check if we store event.code or event.key. When the code is in the list of keysToWatch
319
- // we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft"
320
- // and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when
321
- // we can't find it in the list of keysToWatch.
385
+ /*
386
+ * keyCodes = array with single keys [['a']] or key combinations [['a', 's']]
387
+ * keysToWatch = array with all keys flattened ['a', 'd', 'ShiftLeft']
388
+ * used to check if we store event.code or event.key. When the code is in the list of keysToWatch
389
+ * we use the code otherwise the key. Explainer: When you press the left "command" key, the code is "MetaLeft"
390
+ * and the key is "Meta". We want users to be able to pass keys and codes so we assume that the key is meant when
391
+ * we can't find it in the list of keysToWatch.
392
+ */
322
393
  const [keyCodes, keysToWatch] = useMemo(() => {
323
394
  if (keyCode !== null) {
324
395
  const keyCodeArr = Array.isArray(keyCode) ? keyCode : [keyCode];
325
396
  const keys = keyCodeArr
326
397
  .filter((kc) => typeof kc === 'string')
327
- // we first replace all '+' with '\n' which we will use to split the keys on
328
- // then we replace '\n\n' with '\n+', this way we can also support the combination 'key++'
329
- // in the end we simply split on '\n' to get the key array
398
+ /*
399
+ * we first replace all '+' with '\n' which we will use to split the keys on
400
+ * then we replace '\n\n' with '\n+', this way we can also support the combination 'key++'
401
+ * in the end we simply split on '\n' to get the key array
402
+ */
330
403
  .map((kc) => kc.replace('+', '\n').replace('\n\n', '\n+').split('\n'));
331
404
  const keysFlat = keys.reduce((res, item) => res.concat(...item), []);
332
405
  return [keys, keysFlat];
@@ -391,12 +464,16 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
391
464
  // utils
392
465
  function isMatchingKey(keyCodes, pressedKeys, isUp) {
393
466
  return (keyCodes
394
- // we only want to compare same sizes of keyCode definitions
395
- // and pressed keys. When the user specified 'Meta' as a key somewhere
396
- // this would also be truthy without this filter when user presses 'Meta' + 'r'
467
+ /*
468
+ * we only want to compare same sizes of keyCode definitions
469
+ * and pressed keys. When the user specified 'Meta' as a key somewhere
470
+ * this would also be truthy without this filter when user presses 'Meta' + 'r'
471
+ */
397
472
  .filter((keys) => isUp || keys.length === pressedKeys.size)
398
- // since we want to support multiple possibilities only one of the
399
- // combinations need to be part of the pressed keys
473
+ /*
474
+ * since we want to support multiple possibilities only one of the
475
+ * combinations need to be part of the pressed keys
476
+ */
400
477
  .some((keys) => keys.every((k) => pressedKeys.has(k))));
401
478
  }
402
479
  function useKeyOrCode(eventCode, keysToWatch) {
@@ -482,8 +559,8 @@ const useViewportHelper = () => {
482
559
  await panZoom.setViewport(viewport, { duration: options?.duration });
483
560
  return Promise.resolve(true);
484
561
  },
485
- screenToFlowPosition: (clientPosition, options = { snapToGrid: true }) => {
486
- const { transform, snapGrid, domNode } = store.getState();
562
+ screenToFlowPosition: (clientPosition, options = {}) => {
563
+ const { transform, snapGrid, snapToGrid, domNode } = store.getState();
487
564
  if (!domNode) {
488
565
  return clientPosition;
489
566
  }
@@ -492,7 +569,9 @@ const useViewportHelper = () => {
492
569
  x: clientPosition.x - domX,
493
570
  y: clientPosition.y - domY,
494
571
  };
495
- return pointToRendererPoint(correctedPosition, transform, options.snapToGrid, snapGrid);
572
+ const _snapGrid = options.snapGrid ?? snapGrid;
573
+ const _snapToGrid = options.snapToGrid ?? snapToGrid;
574
+ return pointToRendererPoint(correctedPosition, transform, _snapToGrid, _snapGrid);
496
575
  },
497
576
  flowToScreenPosition: (flowPosition) => {
498
577
  const { transform, domNode } = store.getState();
@@ -510,13 +589,17 @@ const useViewportHelper = () => {
510
589
  }, []);
511
590
  };
512
591
 
513
- // This function applies changes to nodes or edges that are triggered by React Flow internally.
514
- // When you drag a node for example, React Flow will send a position change update.
515
- // This function then applies the changes and returns the updated elements.
592
+ /*
593
+ * This function applies changes to nodes or edges that are triggered by React Flow internally.
594
+ * When you drag a node for example, React Flow will send a position change update.
595
+ * This function then applies the changes and returns the updated elements.
596
+ */
516
597
  function applyChanges(changes, elements) {
517
598
  const updatedElements = [];
518
- // By storing a map of changes for each element, we can a quick lookup as we
519
- // iterate over the elements array!
599
+ /*
600
+ * By storing a map of changes for each element, we can a quick lookup as we
601
+ * iterate over the elements array!
602
+ */
520
603
  const changesMap = new Map();
521
604
  const addItemChanges = [];
522
605
  for (const change of changes) {
@@ -525,15 +608,19 @@ function applyChanges(changes, elements) {
525
608
  continue;
526
609
  }
527
610
  else if (change.type === 'remove' || change.type === 'replace') {
528
- // For a 'remove' change we can safely ignore any other changes queued for
529
- // the same element, it's going to be removed anyway!
611
+ /*
612
+ * For a 'remove' change we can safely ignore any other changes queued for
613
+ * the same element, it's going to be removed anyway!
614
+ */
530
615
  changesMap.set(change.id, [change]);
531
616
  }
532
617
  else {
533
618
  const elementChanges = changesMap.get(change.id);
534
619
  if (elementChanges) {
535
- // If we have some changes queued already, we can do a mutable update of
536
- // that array and save ourselves some copying.
620
+ /*
621
+ * If we have some changes queued already, we can do a mutable update of
622
+ * that array and save ourselves some copying.
623
+ */
537
624
  elementChanges.push(change);
538
625
  }
539
626
  else {
@@ -543,8 +630,10 @@ function applyChanges(changes, elements) {
543
630
  }
544
631
  for (const element of elements) {
545
632
  const changes = changesMap.get(element.id);
546
- // When there are no changes for an element we can just push it unmodified,
547
- // no need to copy it.
633
+ /*
634
+ * When there are no changes for an element we can just push it unmodified,
635
+ * no need to copy it.
636
+ */
548
637
  if (!changes) {
549
638
  updatedElements.push(element);
550
639
  continue;
@@ -557,17 +646,21 @@ function applyChanges(changes, elements) {
557
646
  updatedElements.push({ ...changes[0].item });
558
647
  continue;
559
648
  }
560
- // For other types of changes, we want to start with a shallow copy of the
561
- // object so React knows this element has changed. Sequential changes will
562
- /// each _mutate_ this object, so there's only ever one copy.
649
+ /**
650
+ * For other types of changes, we want to start with a shallow copy of the
651
+ * object so React knows this element has changed. Sequential changes will
652
+ * each _mutate_ this object, so there's only ever one copy.
653
+ */
563
654
  const updatedElement = { ...element };
564
655
  for (const change of changes) {
565
656
  applyChange(change, updatedElement);
566
657
  }
567
658
  updatedElements.push(updatedElement);
568
659
  }
569
- // we need to wait for all changes to be applied before adding new items
570
- // to be able to add them at the correct index
660
+ /*
661
+ * we need to wait for all changes to be applied before adding new items
662
+ * to be able to add them at the correct index
663
+ */
571
664
  if (addItemChanges.length) {
572
665
  addItemChanges.forEach((change) => {
573
666
  if (change.index !== undefined) {
@@ -616,22 +709,33 @@ function applyChange(change, element) {
616
709
  /**
617
710
  * Drop in function that applies node changes to an array of nodes.
618
711
  * @public
619
- * @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange} that describes how to update the edges of your flow in some way.
620
- If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
621
712
  * @param changes - Array of changes to apply
622
713
  * @param nodes - Array of nodes to apply the changes to
623
714
  * @returns Array of updated nodes
624
715
  * @example
625
- * const onNodesChange = useCallback(
626
- (changes) => {
627
- setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
628
- },
629
- [setNodes],
630
- );
631
-
632
- return (
633
- <ReactFLow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
634
- );
716
+ *```tsx
717
+ *import { useState, useCallback } from 'react';
718
+ *import { ReactFlow, applyNodeChanges, type Node, type Edge, type OnNodesChange } from '@xyflow/react';
719
+ *
720
+ *export default function Flow() {
721
+ * const [nodes, setNodes] = useState<Node[]>([]);
722
+ * const [edges, setEdges] = useState<Edge[]>([]);
723
+ * const onNodesChange: OnNodesChange = useCallback(
724
+ * (changes) => {
725
+ * setNodes((oldNodes) => applyNodeChanges(changes, oldNodes));
726
+ * },
727
+ * [setNodes],
728
+ * );
729
+ *
730
+ * return (
731
+ * <ReactFlow nodes={nodes} edges={edges} onNodesChange={onNodesChange} />
732
+ * );
733
+ *}
734
+ *```
735
+ * @remarks Various events on the <ReactFlow /> component can produce an {@link NodeChange}
736
+ * that describes how to update the edges of your flow in some way.
737
+ * If you don't need any custom behaviour, this util can be used to take an array
738
+ * of these changes and apply them to your edges.
635
739
  */
636
740
  function applyNodeChanges(changes, nodes) {
637
741
  return applyChanges(changes, nodes);
@@ -639,22 +743,33 @@ function applyNodeChanges(changes, nodes) {
639
743
  /**
640
744
  * Drop in function that applies edge changes to an array of edges.
641
745
  * @public
642
- * @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange} that describes how to update the edges of your flow in some way.
643
- If you don't need any custom behaviour, this util can be used to take an array of these changes and apply them to your edges.
644
746
  * @param changes - Array of changes to apply
645
747
  * @param edges - Array of edge to apply the changes to
646
748
  * @returns Array of updated edges
647
749
  * @example
750
+ * ```tsx
751
+ *import { useState, useCallback } from 'react';
752
+ *import { ReactFlow, applyEdgeChanges } from '@xyflow/react';
753
+ *
754
+ *export default function Flow() {
755
+ * const [nodes, setNodes] = useState([]);
756
+ * const [edges, setEdges] = useState([]);
648
757
  * const onEdgesChange = useCallback(
649
- (changes) => {
650
- setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
651
- },
652
- [setEdges],
653
- );
654
-
655
- return (
656
- <ReactFlow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
657
- );
758
+ * (changes) => {
759
+ * setEdges((oldEdges) => applyEdgeChanges(changes, oldEdges));
760
+ * },
761
+ * [setEdges],
762
+ * );
763
+ *
764
+ * return (
765
+ * <ReactFlow nodes={nodes} edges={edges} onEdgesChange={onEdgesChange} />
766
+ * );
767
+ *}
768
+ *```
769
+ * @remarks Various events on the <ReactFlow /> component can produce an {@link EdgeChange}
770
+ * that describes how to update the edges of your flow in some way.
771
+ * If you don't need any custom behaviour, this util can be used to take an array
772
+ * of these changes and apply them to your edges.
658
773
  */
659
774
  function applyEdgeChanges(changes, edges) {
660
775
  return applyChanges(changes, edges);
@@ -673,9 +788,11 @@ function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false)
673
788
  // we don't want to set all items to selected=false on the first selection
674
789
  if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
675
790
  if (mutateItem) {
676
- // this hack is needed for nodes. When the user dragged a node, it's selected.
677
- // When another node gets dragged, we need to deselect the previous one,
678
- // in order to have only one selected node at a time - the onNodesChange callback comes too late here :/
791
+ /*
792
+ * this hack is needed for nodes. When the user dragged a node, it's selected.
793
+ * When another node gets dragged, we need to deselect the previous one,
794
+ * in order to have only one selected node at a time - the onNodesChange callback comes too late here :/
795
+ */
679
796
  item.selected = willBeSelected;
680
797
  }
681
798
  changes.push(createSelectionChange(item.id, willBeSelected));
@@ -712,22 +829,46 @@ function elementToRemoveChange(item) {
712
829
  }
713
830
 
714
831
  /**
715
- * Test whether an object is useable as a Node
832
+ * Test whether an object is useable as an [`Node`](/api-reference/types/node).
833
+ * In TypeScript this is a type guard that will narrow the type of whatever you pass in to
834
+ * [`Node`](/api-reference/types/node) if it returns `true`.
835
+ *
716
836
  * @public
717
837
  * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Node if it returns true
718
838
  * @param element - The element to test
719
839
  * @returns A boolean indicating whether the element is an Node
840
+ *
841
+ * @example
842
+ * ```js
843
+ *import { isNode } from '@xyflow/react';
844
+ *
845
+ *if (isNode(node)) {
846
+ * // ..
847
+ *}
848
+ *```
720
849
  */
721
850
  const isNode = (element) => isNodeBase(element);
722
851
  /**
723
- * Test whether an object is useable as an Edge
852
+ * Test whether an object is useable as an [`Edge`](/api-reference/types/edge).
853
+ * In TypeScript this is a type guard that will narrow the type of whatever you pass in to
854
+ * [`Edge`](/api-reference/types/edge) if it returns `true`.
855
+ *
724
856
  * @public
725
857
  * @remarks In TypeScript this is a type guard that will narrow the type of whatever you pass in to Edge if it returns true
726
858
  * @param element - The element to test
727
859
  * @returns A boolean indicating whether the element is an Edge
860
+ *
861
+ * @example
862
+ * ```js
863
+ *import { isEdge } from '@xyflow/react';
864
+ *
865
+ *if (isEdge(edge)) {
866
+ * // ..
867
+ *}
868
+ *```
728
869
  */
729
870
  const isEdge = (element) => isEdgeBase(element);
730
- // eslint-disable-next-line @typescript-eslint/ban-types
871
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
731
872
  function fixedForwardRef(render) {
732
873
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
733
874
  return forwardRef(render);
@@ -745,19 +886,25 @@ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffec
745
886
  * @returns a Queue object
746
887
  */
747
888
  function useQueue(runQueue) {
748
- // Because we're using a ref above, we need some way to let React know when to
749
- // actually process the queue. We increment this number any time we mutate the
750
- // queue, creating a new state to trigger the layout effect below.
751
- // Using a boolean dirty flag here instead would lead to issues related to
752
- // automatic batching. (https://github.com/xyflow/xyflow/issues/4779)
889
+ /*
890
+ * Because we're using a ref above, we need some way to let React know when to
891
+ * actually process the queue. We increment this number any time we mutate the
892
+ * queue, creating a new state to trigger the layout effect below.
893
+ * Using a boolean dirty flag here instead would lead to issues related to
894
+ * automatic batching. (https://github.com/xyflow/xyflow/issues/4779)
895
+ */
753
896
  const [serial, setSerial] = useState(BigInt(0));
754
- // A reference of all the batched updates to process before the next render. We
755
- // want a reference here so multiple synchronous calls to `setNodes` etc can be
756
- // batched together.
897
+ /*
898
+ * A reference of all the batched updates to process before the next render. We
899
+ * want a reference here so multiple synchronous calls to `setNodes` etc can be
900
+ * batched together.
901
+ */
757
902
  const [queue] = useState(() => createQueue(() => setSerial(n => n + BigInt(1))));
758
- // Layout effects are guaranteed to run before the next render which means we
759
- // shouldn't run into any issues with stale state or weird issues that come from
760
- // rendering things one frame later than expected (we used to use `setTimeout`).
903
+ /*
904
+ * Layout effects are guaranteed to run before the next render which means we
905
+ * shouldn't run into any issues with stale state or weird issues that come from
906
+ * rendering things one frame later than expected (we used to use `setTimeout`).
907
+ */
761
908
  useIsomorphicLayoutEffect(() => {
762
909
  const queueItems = queue.get();
763
910
  if (queueItems.length) {
@@ -792,9 +939,11 @@ function BatchProvider({ children, }) {
792
939
  const store = useStoreApi();
793
940
  const nodeQueueHandler = useCallback((queueItems) => {
794
941
  const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState();
795
- // This is essentially an `Array.reduce` in imperative clothing. Processing
796
- // this queue is a relatively hot path so we'd like to avoid the overhead of
797
- // array methods where we can.
942
+ /*
943
+ * This is essentially an `Array.reduce` in imperative clothing. Processing
944
+ * this queue is a relatively hot path so we'd like to avoid the overhead of
945
+ * array methods where we can.
946
+ */
798
947
  let next = nodes;
799
948
  for (const payload of queueItems) {
800
949
  next = typeof payload === 'function' ? payload(next) : payload;
@@ -840,10 +989,33 @@ function useBatchContext() {
840
989
 
841
990
  const selector$k = (s) => !!s.panZoom;
842
991
  /**
843
- * Hook for accessing the ReactFlow instance.
992
+ * This hook returns a ReactFlowInstance that can be used to update nodes and edges, manipulate the viewport, or query the current state of the flow.
844
993
  *
845
994
  * @public
846
995
  * @returns ReactFlowInstance
996
+ *
997
+ * @example
998
+ * ```jsx
999
+ *import { useCallback, useState } from 'react';
1000
+ *import { useReactFlow } from '@xyflow/react';
1001
+ *
1002
+ *export function NodeCounter() {
1003
+ * const reactFlow = useReactFlow();
1004
+ * const [count, setCount] = useState(0);
1005
+ * const countNodes = useCallback(() => {
1006
+ * setCount(reactFlow.getNodes().length);
1007
+ * // you need to pass it as a dependency if you are using it with useEffect or useCallback
1008
+ * // because at the first render, it's not initialized yet and some functions might not work.
1009
+ * }, [reactFlow]);
1010
+ *
1011
+ * return (
1012
+ * <div>
1013
+ * <button onClick={countNodes}>Update count</button>
1014
+ * <p>There are {count} nodes in the flow.</p>
1015
+ * </div>
1016
+ * );
1017
+ *}
1018
+ *```
847
1019
  */
848
1020
  function useReactFlow() {
849
1021
  const viewportHelper = useViewportHelper();
@@ -1310,8 +1482,10 @@ function Pane({ isSelecting, selectionKeyPressed, selectionMode = SelectionMode.
1310
1482
  }
1311
1483
  event.target?.releasePointerCapture?.(event.pointerId);
1312
1484
  const { userSelectionRect } = store.getState();
1313
- // We only want to trigger click functions when in selection mode if
1314
- // the user did not move the mouse.
1485
+ /*
1486
+ * We only want to trigger click functions when in selection mode if
1487
+ * the user did not move the mouse.
1488
+ */
1315
1489
  if (!userSelectionActive && userSelectionRect && event.target === container.current) {
1316
1490
  onClick?.(event);
1317
1491
  }
@@ -1321,8 +1495,10 @@ function Pane({ isSelecting, selectionKeyPressed, selectionMode = SelectionMode.
1321
1495
  nodesSelectionActive: selectedNodeIds.current.size > 0,
1322
1496
  });
1323
1497
  onSelectionEnd?.(event);
1324
- // If the user kept holding the selectionKey during the selection,
1325
- // we need to reset the selectionInProgress, so the next click event is not prevented
1498
+ /*
1499
+ * If the user kept holding the selectionKey during the selection,
1500
+ * we need to reset the selectionInProgress, so the next click event is not prevented
1501
+ */
1326
1502
  if (selectionKeyPressed || selectionOnDrag) {
1327
1503
  selectionInProgress.current = false;
1328
1504
  }
@@ -1332,10 +1508,12 @@ function Pane({ isSelecting, selectionKeyPressed, selectionMode = SelectionMode.
1332
1508
  return (jsxs("div", { className: cc(['react-flow__pane', { draggable, dragging, selection: isSelecting }]), onClick: hasActiveSelection ? undefined : wrapHandler(onClick, container), onContextMenu: wrapHandler(onContextMenu, container), onWheel: wrapHandler(onWheel, container), onPointerEnter: hasActiveSelection ? undefined : onPaneMouseEnter, onPointerDown: hasActiveSelection ? onPointerDown : onPaneMouseMove, onPointerMove: hasActiveSelection ? onPointerMove : onPaneMouseMove, onPointerUp: hasActiveSelection ? onPointerUp : undefined, onPointerLeave: onPaneMouseLeave, ref: container, style: containerStyle, children: [children, jsx(UserSelection, {})] }));
1333
1509
  }
1334
1510
 
1335
- // this handler is called by
1336
- // 1. the click handler when node is not draggable or selectNodesOnDrag = false
1337
- // or
1338
- // 2. the on drag start handler when node is draggable and selectNodesOnDrag = true
1511
+ /*
1512
+ * this handler is called by
1513
+ * 1. the click handler when node is not draggable or selectNodesOnDrag = false
1514
+ * or
1515
+ * 2. the on drag start handler when node is draggable and selectNodesOnDrag = true
1516
+ */
1339
1517
  function handleNodeClick({ id, store, unselect = false, nodeRef, }) {
1340
1518
  const { addSelectedNodes, unselectNodesAndEdges, multiSelectionActive, nodeLookup, onError } = store.getState();
1341
1519
  const node = nodeLookup.get(id);
@@ -1414,8 +1592,10 @@ function useMoveSelectedNodes() {
1414
1592
  const { nodeExtent, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin } = store.getState();
1415
1593
  const nodeUpdates = new Map();
1416
1594
  const isSelected = selectedAndDraggable(nodesDraggable);
1417
- // by default a node moves 5px on each key press
1418
- // if snap grid is enabled, we use that for the velocity
1595
+ /*
1596
+ * by default a node moves 5px on each key press
1597
+ * if snap grid is enabled, we use that for the velocity
1598
+ */
1419
1599
  const xVelo = snapToGrid ? snapGrid[0] : 5;
1420
1600
  const yVelo = snapToGrid ? snapGrid[1] : 5;
1421
1601
  const xDiff = params.direction.x * xVelo * params.factor;
@@ -1451,6 +1631,34 @@ function useMoveSelectedNodes() {
1451
1631
  const NodeIdContext = createContext(null);
1452
1632
  const Provider = NodeIdContext.Provider;
1453
1633
  NodeIdContext.Consumer;
1634
+ /**
1635
+ * You can use this hook to get the id of the node it is used inside. It is useful
1636
+ * if you need the node's id deeper in the render tree but don't want to manually
1637
+ * drill down the id as a prop.
1638
+ *
1639
+ * @public
1640
+ * @returns id of the node
1641
+ *
1642
+ * @example
1643
+ *```jsx
1644
+ *import { useNodeId } from '@xyflow/react';
1645
+ *
1646
+ *export default function CustomNode() {
1647
+ * return (
1648
+ * <div>
1649
+ * <span>This node has an id of </span>
1650
+ * <NodeIdDisplay />
1651
+ * </div>
1652
+ * );
1653
+ *}
1654
+ *
1655
+ *function NodeIdDisplay() {
1656
+ * const nodeId = useNodeId();
1657
+ *
1658
+ * return <span>{nodeId}</span>;
1659
+ *}
1660
+ *```
1661
+ */
1454
1662
  const useNodeId = () => {
1455
1663
  const nodeId = useContext(NodeIdContext);
1456
1664
  return nodeId;
@@ -1473,6 +1681,7 @@ const connectingSelector = (nodeId, handleId, type) => (state) => {
1473
1681
  ? fromHandle?.type !== type
1474
1682
  : nodeId !== fromHandle?.nodeId || handleId !== fromHandle?.id,
1475
1683
  connectionInProcess: !!fromHandle,
1684
+ clickConnectionInProcess: !!clickHandle,
1476
1685
  valid: connectingTo && isValid,
1477
1686
  };
1478
1687
  };
@@ -1482,7 +1691,7 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1482
1691
  const store = useStoreApi();
1483
1692
  const nodeId = useNodeId();
1484
1693
  const { connectOnClick, noPanClassName, rfId } = useStore(selector$g, shallow);
1485
- const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, valid } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1694
+ const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, clickConnectionInProcess, valid, } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1486
1695
  if (!nodeId) {
1487
1696
  store.getState().onError?.('010', errorMessages['error010']());
1488
1697
  }
@@ -1590,16 +1799,40 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1590
1799
  connectingfrom: connectingFrom,
1591
1800
  connectingto: connectingTo,
1592
1801
  valid,
1593
- // shows where you can start a connection from
1594
- // and where you can end it while connecting
1802
+ /*
1803
+ * shows where you can start a connection from
1804
+ * and where you can end it while connecting
1805
+ */
1595
1806
  connectionindicator: isConnectable &&
1596
1807
  (!connectionInProcess || isPossibleEndHandle) &&
1597
- (connectionInProcess ? isConnectableEnd : isConnectableStart),
1808
+ (connectionInProcess || clickConnectionInProcess ? isConnectableEnd : isConnectableStart),
1598
1809
  },
1599
1810
  ]), onMouseDown: onPointerDown, onTouchStart: onPointerDown, onClick: connectOnClick ? onClick : undefined, ref: ref, ...rest, children: children }));
1600
1811
  }
1601
1812
  /**
1602
- * The Handle component is a UI element that is used to connect nodes.
1813
+ * The `<Handle />` component is used in your [custom nodes](/learn/customization/custom-nodes)
1814
+ * to define connection points.
1815
+ *
1816
+ *@public
1817
+ *
1818
+ *@example
1819
+ *
1820
+ *```jsx
1821
+ *import { Handle, Position } from '@xyflow/react';
1822
+ *
1823
+ *export function CustomNode({ data }) {
1824
+ * return (
1825
+ * <>
1826
+ * <div style={{ padding: '10px 20px' }}>
1827
+ * {data.label}
1828
+ * </div>
1829
+ *
1830
+ * <Handle type="target" position={Position.Left} />
1831
+ * <Handle type="source" position={Position.Right} />
1832
+ * </>
1833
+ * );
1834
+ *};
1835
+ *```
1603
1836
  */
1604
1837
  const Handle = memo(fixedForwardRef(HandleComponent));
1605
1838
 
@@ -1792,8 +2025,10 @@ function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
1792
2025
  }, []);
1793
2026
  useEffect(() => {
1794
2027
  if (nodeRef.current) {
1795
- // when the user programmatically changes the source or handle position, we need to update the internals
1796
- // to make sure the edges are updated correctly
2028
+ /*
2029
+ * when the user programmatically changes the source or handle position, we need to update the internals
2030
+ * to make sure the edges are updated correctly
2031
+ */
1797
2032
  const typeChanged = prevType.current !== nodeType;
1798
2033
  const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
1799
2034
  const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
@@ -1810,7 +2045,7 @@ function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
1810
2045
  return nodeRef;
1811
2046
  }
1812
2047
 
1813
- function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, nodeClickDistance, onError, }) {
2048
+ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeClickDistance, onError, }) {
1814
2049
  const { node, internals, isParent } = useStore((s) => {
1815
2050
  const node = s.nodeLookup.get(id);
1816
2051
  const isParent = s.parentLookup.has(id);
@@ -1868,8 +2103,10 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1868
2103
  const onSelectNodeHandler = (event) => {
1869
2104
  const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
1870
2105
  if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
1871
- // this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
1872
- // here we only need to call it when selectNodesOnDrag=false
2106
+ /*
2107
+ * this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
2108
+ * here we only need to call it when selectNodesOnDrag=false
2109
+ */
1873
2110
  handleNodeClick({
1874
2111
  id,
1875
2112
  store,
@@ -1945,29 +2182,31 @@ function NodeRendererComponent(props) {
1945
2182
  const resizeObserver = useResizeObserver();
1946
2183
  return (jsx("div", { className: "react-flow__nodes", style: containerStyle, children: nodeIds.map((nodeId) => {
1947
2184
  return (
1948
- // The split of responsibilities between NodeRenderer and
1949
- // NodeComponentWrapper may appear weird. However, it’s designed to
1950
- // minimize the cost of updates when individual nodes change.
1951
- //
1952
- // For example, when you’re dragging a single node, that node gets
1953
- // updated multiple times per second. If `NodeRenderer` were to update
1954
- // every time, it would have to re-run the `nodes.map()` loop every
1955
- // time. This gets pricey with hundreds of nodes, especially if every
1956
- // loop cycle does more than just rendering a JSX element!
1957
- //
1958
- // As a result of this choice, we took the following implementation
1959
- // decisions:
1960
- // - NodeRenderer subscribes *only* to node IDs – and therefore
1961
- // rerender *only* when visible nodes are added or removed.
1962
- // - NodeRenderer performs all operations the result of which can be
1963
- // shared between nodes (such as creating the `ResizeObserver`
1964
- // instance, or subscribing to `selector`). This means extra prop
1965
- // drilling into `NodeComponentWrapper`, but it means we need to run
1966
- // these operations only once instead of once per node.
1967
- // - Any operations that you’d normally write inside `nodes.map` are
1968
- // moved into `NodeComponentWrapper`. This ensures they are
1969
- // memorized so if `NodeRenderer` *has* to rerender, it only
1970
- // needs to regenerate the list of nodes, nothing else.
2185
+ /*
2186
+ * The split of responsibilities between NodeRenderer and
2187
+ * NodeComponentWrapper may appear weird. However, it’s designed to
2188
+ * minimize the cost of updates when individual nodes change.
2189
+ *
2190
+ * For example, when you’re dragging a single node, that node gets
2191
+ * updated multiple times per second. If `NodeRenderer` were to update
2192
+ * every time, it would have to re-run the `nodes.map()` loop every
2193
+ * time. This gets pricey with hundreds of nodes, especially if every
2194
+ * loop cycle does more than just rendering a JSX element!
2195
+ *
2196
+ * As a result of this choice, we took the following implementation
2197
+ * decisions:
2198
+ * - NodeRenderer subscribes *only* to node IDs and therefore
2199
+ * rerender *only* when visible nodes are added or removed.
2200
+ * - NodeRenderer performs all operations the result of which can be
2201
+ * shared between nodes (such as creating the `ResizeObserver`
2202
+ * instance, or subscribing to `selector`). This means extra prop
2203
+ * drilling into `NodeComponentWrapper`, but it means we need to run
2204
+ * these operations only once instead of once per node.
2205
+ * - Any operations that you’d normally write inside `nodes.map` are
2206
+ * moved into `NodeComponentWrapper`. This ensures they are
2207
+ * memorized so if `NodeRenderer` *has* to rerender, it only
2208
+ * needs to regenerate the list of nodes, nothing else.
2209
+ */
1971
2210
  jsx(NodeWrapper, { id: nodeId, nodeTypes: props.nodeTypes, nodeExtent: props.nodeExtent, onClick: props.onNodeClick, onMouseEnter: props.onNodeMouseEnter, onMouseMove: props.onNodeMouseMove, onMouseLeave: props.onNodeMouseLeave, onContextMenu: props.onNodeContextMenu, onDoubleClick: props.onNodeDoubleClick, noDragClassName: props.noDragClassName, noPanClassName: props.noPanClassName, rfId: props.rfId, disableKeyboardA11y: props.disableKeyboardA11y, resizeObserver: resizeObserver, nodesDraggable: nodesDraggable, nodesConnectable: nodesConnectable, nodesFocusable: nodesFocusable, elementsSelectable: elementsSelectable, nodeClickDistance: props.nodeClickDistance, onError: onError }, nodeId));
1972
2211
  }) }));
1973
2212
  }
@@ -2046,9 +2285,11 @@ const Marker = ({ id, type, color, width = 12.5, height = 12.5, markerUnits = 's
2046
2285
  }
2047
2286
  return (jsx("marker", { className: "react-flow__arrowhead", id: id, markerWidth: `${width}`, markerHeight: `${height}`, viewBox: "-10 -10 20 20", markerUnits: markerUnits, orient: orient, refX: "0", refY: "0", children: jsx(Symbol, { color: color, strokeWidth: strokeWidth }) }));
2048
2287
  };
2049
- // when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
2050
- // when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
2051
- // that we can then use for creating our unique marker ids
2288
+ /*
2289
+ * when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
2290
+ * when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
2291
+ * that we can then use for creating our unique marker ids
2292
+ */
2052
2293
  const MarkerDefinitions = ({ defaultColor, rfId }) => {
2053
2294
  const edges = useStore((s) => s.edges);
2054
2295
  const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions);
@@ -2090,8 +2331,61 @@ function EdgeTextComponent({ x, y, label, labelStyle = {}, labelShowBg = true, l
2090
2331
  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] }));
2091
2332
  }
2092
2333
  EdgeTextComponent.displayName = 'EdgeText';
2334
+ /**
2335
+ * You can use the `<EdgeText />` component as a helper component to display text
2336
+ * within your custom edges.
2337
+ *
2338
+ *@public
2339
+ *
2340
+ *@example
2341
+ *```jsx
2342
+ *import { EdgeText } from '@xyflow/react';
2343
+ *
2344
+ *export function CustomEdgeLabel({ label }) {
2345
+ * return (
2346
+ * <EdgeText
2347
+ * x={100}
2348
+ * y={100}
2349
+ * label={label}
2350
+ * labelStyle={{ fill: 'white' }}
2351
+ * labelShowBg
2352
+ * labelBgStyle={{ fill: 'red' }}
2353
+ * labelBgPadding={[2, 4]}
2354
+ * labelBgBorderRadius={2}
2355
+ * />
2356
+ * );
2357
+ *}
2358
+ *```
2359
+ */
2093
2360
  const EdgeText = memo(EdgeTextComponent);
2094
2361
 
2362
+ /**
2363
+ * The `<BaseEdge />` component gets used internally for all the edges. It can be
2364
+ * used inside a custom edge and handles the invisible helper edge and the edge label
2365
+ * for you.
2366
+ *
2367
+ * @public
2368
+ * @example
2369
+ * ```jsx
2370
+ *import { BaseEdge } from '@xyflow/react';
2371
+ *
2372
+ *export function CustomEdge({ sourceX, sourceY, targetX, targetY, ...props }) {
2373
+ * const [edgePath] = getStraightPath({
2374
+ * sourceX,
2375
+ * sourceY,
2376
+ * targetX,
2377
+ * targetY,
2378
+ * });
2379
+ *
2380
+ * return <BaseEdge path={edgePath} {...props} />;
2381
+ *}
2382
+ *```
2383
+ *
2384
+ * @remarks If you want to use an edge marker with the [`<BaseEdge />`](/api-reference/components/base-edge) component,
2385
+ * you can pass the `markerStart` or `markerEnd` props passed to your custom edge
2386
+ * through to the [`<BaseEdge />`](/api-reference/components/base-edge) component.
2387
+ * You can see all the props passed to a custom edge by looking at the [`EdgeProps`](/api-reference/types/edge-props) type.
2388
+ */
2095
2389
  function BaseEdge({ path, labelX, labelY, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, interactionWidth = 20, ...props }) {
2096
2390
  return (jsxs(Fragment, { children: [jsx("path", { ...props, d: path, fill: "none", className: cc(['react-flow__edge-path', props.className]) }), interactionWidth && (jsx("path", { d: path, fill: "none", strokeOpacity: 0, strokeWidth: interactionWidth, className: "react-flow__edge-interaction" })), label && isNumeric(labelX) && isNumeric(labelY) ? (jsx(EdgeText, { x: labelX, y: labelY, label: label, labelStyle: labelStyle, labelShowBg: labelShowBg, labelBgStyle: labelBgStyle, labelBgPadding: labelBgPadding, labelBgBorderRadius: labelBgBorderRadius })) : null] }));
2097
2391
  }
@@ -2102,6 +2396,11 @@ function getControl({ pos, x1, y1, x2, y2 }) {
2102
2396
  }
2103
2397
  return [x1, 0.5 * (y1 + y2)];
2104
2398
  }
2399
+ /**
2400
+ * The `getSimpleBezierPath` util returns everything you need to render a simple
2401
+ * bezier edge between two nodes.
2402
+ * @public
2403
+ */
2105
2404
  function getSimpleBezierPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, }) {
2106
2405
  const [sourceControlX, sourceControlY] = getControl({
2107
2406
  pos: sourcePosition,
@@ -2502,9 +2801,28 @@ function getSelector(connectionSelector) {
2502
2801
  return storeSelector$1;
2503
2802
  }
2504
2803
  /**
2505
- * Hook for accessing the connection state.
2804
+ * The `useConnection` hook returns the current connection when there is an active
2805
+ * connection interaction. If no connection interaction is active, it returns null
2806
+ * for every property. A typical use case for this hook is to colorize handles
2807
+ * based on a certain condition (e.g. if the connection is valid or not).
2506
2808
  *
2507
2809
  * @public
2810
+ * @example
2811
+ *
2812
+ * ```tsx
2813
+ *import { useConnection } from '@xyflow/react';
2814
+ *
2815
+ *function App() {
2816
+ * const connection = useConnection();
2817
+ *
2818
+ * return (
2819
+ * <div> {connection ? `Someone is trying to make a connection from ${connection.fromNode} to this one.` : 'There are currently no incoming connections!'}
2820
+ *
2821
+ * </div>
2822
+ * );
2823
+ * }
2824
+ * ```
2825
+ *
2508
2826
  * @returns ConnectionState
2509
2827
  */
2510
2828
  function useConnection(connectionSelector) {
@@ -2519,7 +2837,7 @@ const selector$7 = (s) => ({
2519
2837
  width: s.width,
2520
2838
  height: s.height,
2521
2839
  });
2522
- function ConnectionLineWrapper({ containerStyle, style, type, component }) {
2840
+ function ConnectionLineWrapper({ containerStyle, style, type, component, }) {
2523
2841
  const { nodesConnectable, width, height, isValid, inProgress } = useStore(selector$7, shallow);
2524
2842
  const renderConnection = !!(width && nodesConnectable && inProgress);
2525
2843
  if (!renderConnection) {
@@ -2527,7 +2845,7 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }) {
2527
2845
  }
2528
2846
  return (jsx("svg", { style: containerStyle, width: width, height: height, className: "react-flow__connectionline react-flow__container", children: jsx("g", { className: cc(['react-flow__connection', getConnectionStatus(isValid)]), children: jsx(ConnectionLine, { style: style, type: type, CustomComponent: component, isValid: isValid }) }) }));
2529
2847
  }
2530
- const ConnectionLine = ({ style, type = ConnectionLineType.Bezier, CustomComponent, isValid }) => {
2848
+ const ConnectionLine = ({ style, type = ConnectionLineType.Bezier, CustomComponent, isValid, }) => {
2531
2849
  const { inProgress, from, fromNode, fromHandle, fromPosition, to, toNode, toHandle, toPosition } = useConnection();
2532
2850
  if (!inProgress) {
2533
2851
  return;
@@ -2700,12 +3018,14 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2700
3018
  ...getInitialState({ nodes, edges, width, height, fitView: fitView$1, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
2701
3019
  setNodes: (nodes) => {
2702
3020
  const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
2703
- // setNodes() is called exclusively in response to user actions:
2704
- // - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
2705
- // - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
2706
- //
2707
- // When this happens, we take the note objects passed by the user and extend them with fields
2708
- // relevant for internal React Flow operations.
3021
+ /*
3022
+ * setNodes() is called exclusively in response to user actions:
3023
+ * - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
3024
+ * - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
3025
+ *
3026
+ * When this happens, we take the note objects passed by the user and extend them with fields
3027
+ * relevant for internal React Flow operations.
3028
+ */
2709
3029
  adoptUserNodes(nodes, nodeLookup, parentLookup, {
2710
3030
  nodeOrigin,
2711
3031
  nodeExtent,
@@ -2731,9 +3051,11 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2731
3051
  set({ hasDefaultEdges: true });
2732
3052
  }
2733
3053
  },
2734
- // Every node gets registerd at a ResizeObserver. Whenever a node
2735
- // changes its dimensions, this function is called to measure the
2736
- // new dimensions and update the nodes.
3054
+ /*
3055
+ * Every node gets registerd at a ResizeObserver. Whenever a node
3056
+ * changes its dimensions, this function is called to measure the
3057
+ * new dimensions and update the nodes.
3058
+ */
2737
3059
  updateNodeInternals: (updates, params = { triggerFitView: true }) => {
2738
3060
  const { triggerNodeChanges, nodeLookup, parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, nodeExtent, debug, fitViewSync, } = get();
2739
3061
  const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent);
@@ -2750,11 +3072,13 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2750
3072
  nodes: fitViewOnInitOptions?.nodes,
2751
3073
  });
2752
3074
  }
2753
- // here we are cirmumventing the onNodesChange handler
2754
- // in order to be able to display nodes even if the user
2755
- // has not provided an onNodesChange handler.
2756
- // Nodes are only rendered if they have a width and height
2757
- // attribute which they get from this handler.
3075
+ /*
3076
+ * here we are cirmumventing the onNodesChange handler
3077
+ * in order to be able to display nodes even if the user
3078
+ * has not provided an onNodesChange handler.
3079
+ * Nodes are only rendered if they have a width and height
3080
+ * attribute which they get from this handler.
3081
+ */
2758
3082
  set({ fitViewDone: nextFitViewDone });
2759
3083
  }
2760
3084
  else {
@@ -2771,8 +3095,11 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2771
3095
  updateNodePositions: (nodeDragItems, dragging = false) => {
2772
3096
  const parentExpandChildren = [];
2773
3097
  const changes = [];
3098
+ const { nodeLookup, triggerNodeChanges } = get();
2774
3099
  for (const [id, dragItem] of nodeDragItems) {
2775
- const expandParent = !!(dragItem?.expandParent && dragItem?.parentId && dragItem?.position);
3100
+ // we are using the nodelookup to be sure to use the current expandParent and parentId value
3101
+ const node = nodeLookup.get(id);
3102
+ const expandParent = !!(node?.expandParent && node?.parentId && dragItem?.position);
2776
3103
  const change = {
2777
3104
  id,
2778
3105
  type: 'position',
@@ -2784,25 +3111,25 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2784
3111
  : dragItem.position,
2785
3112
  dragging,
2786
3113
  };
2787
- if (expandParent) {
3114
+ if (expandParent && node.parentId) {
2788
3115
  parentExpandChildren.push({
2789
3116
  id,
2790
- parentId: dragItem.parentId,
3117
+ parentId: node.parentId,
2791
3118
  rect: {
2792
3119
  ...dragItem.internals.positionAbsolute,
2793
- width: dragItem.measured.width,
2794
- height: dragItem.measured.height,
3120
+ width: dragItem.measured.width ?? 0,
3121
+ height: dragItem.measured.height ?? 0,
2795
3122
  },
2796
3123
  });
2797
3124
  }
2798
3125
  changes.push(change);
2799
3126
  }
2800
3127
  if (parentExpandChildren.length > 0) {
2801
- const { nodeLookup, parentLookup, nodeOrigin } = get();
3128
+ const { parentLookup, nodeOrigin } = get();
2802
3129
  const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup, nodeOrigin);
2803
3130
  changes.push(...parentExpandChanges);
2804
3131
  }
2805
- get().triggerNodeChanges(changes);
3132
+ triggerNodeChanges(changes);
2806
3133
  },
2807
3134
  triggerNodeChanges: (changes) => {
2808
3135
  const { onNodesChange, setNodes, nodes, hasDefaultNodes, debug } = get();
@@ -2857,8 +3184,10 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2857
3184
  const nodeChanges = nodesToUnselect.map((n) => {
2858
3185
  const internalNode = nodeLookup.get(n.id);
2859
3186
  if (internalNode) {
2860
- // we need to unselect the internal node that was selected previously before we
2861
- // send the change to the user to prevent it to be selected while dragging the new node
3187
+ /*
3188
+ * we need to unselect the internal node that was selected previously before we
3189
+ * send the change to the user to prevent it to be selected while dragging the new node
3190
+ */
2862
3191
  internalNode.selected = false;
2863
3192
  }
2864
3193
  return createSelectionChange(n.id, false);
@@ -2926,8 +3255,10 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2926
3255
  maxZoom,
2927
3256
  }, options);
2928
3257
  },
2929
- // we can't call an asnychronous function in updateNodeInternals
2930
- // for that we created this sync version of fitView
3258
+ /*
3259
+ * we can't call an asnychronous function in updateNodeInternals
3260
+ * for that we created this sync version of fitView
3261
+ */
2931
3262
  fitViewSync: (options) => {
2932
3263
  const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
2933
3264
  if (!panZoom) {
@@ -2955,6 +3286,40 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2955
3286
  reset: () => set({ ...getInitialState() }),
2956
3287
  }), Object.is);
2957
3288
 
3289
+ /**
3290
+ * The `<ReactFlowProvider />` component is a [context provider](https://react.dev/learn/passing-data-deeply-with-context#)
3291
+ * that makes it possible to access a flow's internal state outside of the
3292
+ * [`<ReactFlow />`](/api-reference/react-flow) component. Many of the hooks we
3293
+ * provide rely on this component to work.
3294
+ * @public
3295
+ *
3296
+ * @example
3297
+ * ```tsx
3298
+ *import { ReactFlow, ReactFlowProvider, useNodes } from '@xyflow/react'
3299
+ *
3300
+ *export default function Flow() {
3301
+ * return (
3302
+ * <ReactFlowProvider>
3303
+ * <ReactFlow nodes={...} edges={...} />
3304
+ * <Sidebar />
3305
+ * </ReactFlowProvider>
3306
+ * );
3307
+ *}
3308
+ *
3309
+ *function Sidebar() {
3310
+ * // This hook will only work if the component it's used in is a child of a
3311
+ * // <ReactFlowProvider />.
3312
+ * const nodes = useNodes()
3313
+ *
3314
+ * return <aside>do something with nodes</aside>;
3315
+ *}
3316
+ *```
3317
+ *
3318
+ * @remarks If you're using a router and want your flow's state to persist across routes,
3319
+ * it's vital that you place the `<ReactFlowProvider />` component _outside_ of
3320
+ * your router. If you have multiple flows on the same page you will need to use a separate
3321
+ * `<ReactFlowProvider />` for each flow.
3322
+ */
2958
3323
  function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNodes, defaultEdges, initialWidth: width, initialHeight: height, fitView, nodeOrigin, nodeExtent, children, }) {
2959
3324
  const [store] = useState(() => createStore({
2960
3325
  nodes,
@@ -2973,8 +3338,10 @@ function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNo
2973
3338
  function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, nodeOrigin, nodeExtent, }) {
2974
3339
  const isWrapped = useContext(StoreContext);
2975
3340
  if (isWrapped) {
2976
- // we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
2977
- // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
3341
+ /*
3342
+ * we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
3343
+ * https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
3344
+ */
2978
3345
  return jsx(Fragment, { children: children });
2979
3346
  }
2980
3347
  return (jsx(ReactFlowProvider, { initialNodes: nodes, initialEdges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, initialWidth: width, initialHeight: height, fitView: fitView, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, children: children }));
@@ -2987,14 +3354,79 @@ const wrapperStyle = {
2987
3354
  position: 'relative',
2988
3355
  zIndex: 0,
2989
3356
  };
2990
- function ReactFlow({ nodes, edges, defaultNodes, defaultEdges, className, nodeTypes, edgeTypes, onNodeClick, onEdgeClick, onInit, onMove, onMoveStart, onMoveEnd, onConnect, onConnectStart, onConnectEnd, onClickConnectStart, onClickConnectEnd, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onNodeDoubleClick, onNodeDragStart, onNodeDrag, onNodeDragStop, onNodesDelete, onEdgesDelete, onDelete, onSelectionChange, onSelectionDragStart, onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, onSelectionStart, onSelectionEnd, onBeforeDelete, connectionMode, connectionLineType = ConnectionLineType.Bezier, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, deleteKeyCode = 'Backspace', selectionKeyCode = 'Shift', selectionOnDrag = false, selectionMode = SelectionMode.Full, panActivationKeyCode = 'Space', multiSelectionKeyCode = isMacOs() ? 'Meta' : 'Control', zoomActivationKeyCode = isMacOs() ? 'Meta' : 'Control', snapToGrid, snapGrid, onlyRenderVisibleElements = false, selectNodesOnDrag, nodesDraggable, nodesConnectable, nodesFocusable, nodeOrigin = defaultNodeOrigin, edgesFocusable, edgesReconnectable, elementsSelectable = true, defaultViewport: defaultViewport$1 = defaultViewport, minZoom = 0.5, maxZoom = 2, translateExtent = infiniteExtent, preventScrolling = true, nodeExtent, defaultMarkerColor = '#b1b1b7', zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, panOnScrollSpeed = 0.5, panOnScrollMode = PanOnScrollMode.Free, zoomOnDoubleClick = true, panOnDrag = true, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneScroll, onPaneContextMenu, paneClickDistance = 0, nodeClickDistance = 0, children, onReconnect, onReconnectStart, onReconnectEnd, onEdgeContextMenu, onEdgeDoubleClick, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, reconnectRadius = 10, onNodesChange, onEdgesChange, noDragClassName = 'nodrag', noWheelClassName = 'nowheel', noPanClassName = 'nopan', fitView, fitViewOptions, connectOnClick, attributionPosition, proOptions, defaultEdgeOptions, elevateNodesOnSelect, elevateEdgesOnSelect, disableKeyboardA11y = false, autoPanOnConnect, autoPanOnNodeDrag, autoPanSpeed, connectionRadius, isValidConnection, onError, style, id, nodeDragThreshold, viewport, onViewportChange, width, height, colorMode = 'light', debug, ...rest }, ref) {
3357
+ function ReactFlow({ nodes, edges, defaultNodes, defaultEdges, className, nodeTypes, edgeTypes, onNodeClick, onEdgeClick, onInit, onMove, onMoveStart, onMoveEnd, onConnect, onConnectStart, onConnectEnd, onClickConnectStart, onClickConnectEnd, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onNodeDoubleClick, onNodeDragStart, onNodeDrag, onNodeDragStop, onNodesDelete, onEdgesDelete, onDelete, onSelectionChange, onSelectionDragStart, onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, onSelectionStart, onSelectionEnd, onBeforeDelete, connectionMode, connectionLineType = ConnectionLineType.Bezier, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, deleteKeyCode = 'Backspace', selectionKeyCode = 'Shift', selectionOnDrag = false, selectionMode = SelectionMode.Full, panActivationKeyCode = 'Space', multiSelectionKeyCode = isMacOs() ? 'Meta' : 'Control', zoomActivationKeyCode = isMacOs() ? 'Meta' : 'Control', snapToGrid, snapGrid, onlyRenderVisibleElements = false, selectNodesOnDrag, nodesDraggable, nodesConnectable, nodesFocusable, nodeOrigin = defaultNodeOrigin, edgesFocusable, edgesReconnectable, elementsSelectable = true, defaultViewport: defaultViewport$1 = defaultViewport, minZoom = 0.5, maxZoom = 2, translateExtent = infiniteExtent, preventScrolling = true, nodeExtent, defaultMarkerColor = '#b1b1b7', zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, panOnScrollSpeed = 0.5, panOnScrollMode = PanOnScrollMode.Free, zoomOnDoubleClick = true, panOnDrag = true, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneScroll, onPaneContextMenu, paneClickDistance = 0, nodeClickDistance = 0, children, onReconnect, onReconnectStart, onReconnectEnd, onEdgeContextMenu, onEdgeDoubleClick, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, reconnectRadius = 10, onNodesChange, onEdgesChange, noDragClassName = 'nodrag', noWheelClassName = 'nowheel', noPanClassName = 'nopan', fitView, fitViewOptions, connectOnClick, attributionPosition, proOptions, defaultEdgeOptions, elevateNodesOnSelect, elevateEdgesOnSelect, disableKeyboardA11y = false, autoPanOnConnect, autoPanOnNodeDrag, autoPanSpeed, connectionRadius, isValidConnection, onError, style, id, nodeDragThreshold, viewport, onViewportChange, width, height, colorMode = 'light', debug, onScroll, ...rest }, ref) {
2991
3358
  const rfId = id || '1';
2992
3359
  const colorModeClassName = useColorModeClass(colorMode);
2993
- return (jsx("div", { "data-testid": "rf__wrapper", ...rest, style: { ...style, ...wrapperStyle }, ref: ref, className: cc(['react-flow', className, colorModeClassName]), id: id, children: jsxs(Wrapper, { nodes: nodes, edges: edges, width: width, height: height, fitView: fitView, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, children: [jsx(GraphView, { onInit: onInit, onNodeClick: onNodeClick, onEdgeClick: onEdgeClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onNodeDoubleClick: onNodeDoubleClick, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineType: connectionLineType, connectionLineStyle: connectionLineStyle, connectionLineComponent: connectionLineComponent, connectionLineContainerStyle: connectionLineContainerStyle, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, deleteKeyCode: deleteKeyCode, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, onlyRenderVisibleElements: onlyRenderVisibleElements, defaultViewport: defaultViewport$1, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, preventScrolling: preventScrolling, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneScroll: onPaneScroll, onPaneContextMenu: onPaneContextMenu, paneClickDistance: paneClickDistance, nodeClickDistance: nodeClickDistance, onSelectionContextMenu: onSelectionContextMenu, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onReconnect: onReconnect, onReconnectStart: onReconnectStart, onReconnectEnd: onReconnectEnd, onEdgeContextMenu: onEdgeContextMenu, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, reconnectRadius: reconnectRadius, defaultMarkerColor: defaultMarkerColor, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, rfId: rfId, disableKeyboardA11y: disableKeyboardA11y, nodeExtent: nodeExtent, viewport: viewport, onViewportChange: onViewportChange }), jsx(StoreUpdater, { nodes: nodes, edges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, onConnect: onConnect, onConnectStart: onConnectStart, onConnectEnd: onConnectEnd, onClickConnectStart: onClickConnectStart, onClickConnectEnd: onClickConnectEnd, nodesDraggable: nodesDraggable, nodesConnectable: nodesConnectable, nodesFocusable: nodesFocusable, edgesFocusable: edgesFocusable, edgesReconnectable: edgesReconnectable, elementsSelectable: elementsSelectable, elevateNodesOnSelect: elevateNodesOnSelect, elevateEdgesOnSelect: elevateEdgesOnSelect, minZoom: minZoom, maxZoom: maxZoom, nodeExtent: nodeExtent, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, snapToGrid: snapToGrid, snapGrid: snapGrid, connectionMode: connectionMode, translateExtent: translateExtent, connectOnClick: connectOnClick, defaultEdgeOptions: defaultEdgeOptions, fitView: fitView, fitViewOptions: fitViewOptions, onNodesDelete: onNodesDelete, onEdgesDelete: onEdgesDelete, onDelete: onDelete, onNodeDragStart: onNodeDragStart, onNodeDrag: onNodeDrag, onNodeDragStop: onNodeDragStop, onSelectionDrag: onSelectionDrag, onSelectionDragStart: onSelectionDragStart, onSelectionDragStop: onSelectionDragStop, onMove: onMove, onMoveStart: onMoveStart, onMoveEnd: onMoveEnd, noPanClassName: noPanClassName, nodeOrigin: nodeOrigin, rfId: rfId, autoPanOnConnect: autoPanOnConnect, autoPanOnNodeDrag: autoPanOnNodeDrag, autoPanSpeed: autoPanSpeed, onError: onError, connectionRadius: connectionRadius, isValidConnection: isValidConnection, selectNodesOnDrag: selectNodesOnDrag, nodeDragThreshold: nodeDragThreshold, onBeforeDelete: onBeforeDelete, paneClickDistance: paneClickDistance, debug: debug }), jsx(SelectionListener, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
3360
+ // Undo scroll events, preventing viewport from shifting when nodes outside of it are focused
3361
+ const wrapperOnScroll = useCallback((e) => {
3362
+ e.currentTarget.scrollTo({ top: 0, left: 0, behavior: 'instant' });
3363
+ onScroll?.(e);
3364
+ }, [onScroll]);
3365
+ return (jsx("div", { "data-testid": "rf__wrapper", ...rest, onScroll: wrapperOnScroll, style: { ...style, ...wrapperStyle }, ref: ref, className: cc(['react-flow', className, colorModeClassName]), id: id, children: jsxs(Wrapper, { nodes: nodes, edges: edges, width: width, height: height, fitView: fitView, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, children: [jsx(GraphView, { onInit: onInit, onNodeClick: onNodeClick, onEdgeClick: onEdgeClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onNodeDoubleClick: onNodeDoubleClick, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionLineType: connectionLineType, connectionLineStyle: connectionLineStyle, connectionLineComponent: connectionLineComponent, connectionLineContainerStyle: connectionLineContainerStyle, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, deleteKeyCode: deleteKeyCode, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, onlyRenderVisibleElements: onlyRenderVisibleElements, defaultViewport: defaultViewport$1, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, preventScrolling: preventScrolling, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneScroll: onPaneScroll, onPaneContextMenu: onPaneContextMenu, paneClickDistance: paneClickDistance, nodeClickDistance: nodeClickDistance, onSelectionContextMenu: onSelectionContextMenu, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onReconnect: onReconnect, onReconnectStart: onReconnectStart, onReconnectEnd: onReconnectEnd, onEdgeContextMenu: onEdgeContextMenu, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, reconnectRadius: reconnectRadius, defaultMarkerColor: defaultMarkerColor, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, rfId: rfId, disableKeyboardA11y: disableKeyboardA11y, nodeExtent: nodeExtent, viewport: viewport, onViewportChange: onViewportChange }), jsx(StoreUpdater, { nodes: nodes, edges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, onConnect: onConnect, onConnectStart: onConnectStart, onConnectEnd: onConnectEnd, onClickConnectStart: onClickConnectStart, onClickConnectEnd: onClickConnectEnd, nodesDraggable: nodesDraggable, nodesConnectable: nodesConnectable, nodesFocusable: nodesFocusable, edgesFocusable: edgesFocusable, edgesReconnectable: edgesReconnectable, elementsSelectable: elementsSelectable, elevateNodesOnSelect: elevateNodesOnSelect, elevateEdgesOnSelect: elevateEdgesOnSelect, minZoom: minZoom, maxZoom: maxZoom, nodeExtent: nodeExtent, onNodesChange: onNodesChange, onEdgesChange: onEdgesChange, snapToGrid: snapToGrid, snapGrid: snapGrid, connectionMode: connectionMode, translateExtent: translateExtent, connectOnClick: connectOnClick, defaultEdgeOptions: defaultEdgeOptions, fitView: fitView, fitViewOptions: fitViewOptions, onNodesDelete: onNodesDelete, onEdgesDelete: onEdgesDelete, onDelete: onDelete, onNodeDragStart: onNodeDragStart, onNodeDrag: onNodeDrag, onNodeDragStop: onNodeDragStop, onSelectionDrag: onSelectionDrag, onSelectionDragStart: onSelectionDragStart, onSelectionDragStop: onSelectionDragStop, onMove: onMove, onMoveStart: onMoveStart, onMoveEnd: onMoveEnd, noPanClassName: noPanClassName, nodeOrigin: nodeOrigin, rfId: rfId, autoPanOnConnect: autoPanOnConnect, autoPanOnNodeDrag: autoPanOnNodeDrag, autoPanSpeed: autoPanSpeed, onError: onError, connectionRadius: connectionRadius, isValidConnection: isValidConnection, selectNodesOnDrag: selectNodesOnDrag, nodeDragThreshold: nodeDragThreshold, onBeforeDelete: onBeforeDelete, paneClickDistance: paneClickDistance, debug: debug }), jsx(SelectionListener, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
2994
3366
  }
3367
+ /**
3368
+ * The `<ReactFlow />` component is the heart of your React Flow application.
3369
+ * It renders your nodes and edges and handles user interaction
3370
+ *
3371
+ * @public
3372
+ *
3373
+ * @example
3374
+ * ```tsx
3375
+ *import { ReactFlow } from '@xyflow/react'
3376
+ *
3377
+ *export default function Flow() {
3378
+ * return (<ReactFlow
3379
+ * nodes={...}
3380
+ * edges={...}
3381
+ * onNodesChange={...}
3382
+ * ...
3383
+ * />);
3384
+ *}
3385
+ *```
3386
+ */
2995
3387
  var index = fixedForwardRef(ReactFlow);
2996
3388
 
2997
3389
  const selector$6 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
3390
+ /**
3391
+ * Edges are SVG-based. If you want to render more complex labels you can use the
3392
+ * `<EdgeLabelRenderer />` component to access a div based renderer. This component
3393
+ * is a portal that renders the label in a `<div />` that is positioned on top of
3394
+ * the edges. You can see an example usage of the component in the [edge label renderer](/examples/edges/edge-label-renderer) example.
3395
+ * @public
3396
+ *
3397
+ * @example
3398
+ *```jsx
3399
+ *import React from 'react';
3400
+ *import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';
3401
+ *
3402
+ *export function CustomEdge({ id, data, ...props }) {
3403
+ * const [edgePath, labelX, labelY] = getBezierPath(props);
3404
+ *
3405
+ * return (
3406
+ * <>
3407
+ * <BaseEdge id={id} path={edgePath} />
3408
+ * <EdgeLabelRenderer>
3409
+ * <div
3410
+ * style={{
3411
+ * position: 'absolute',
3412
+ * transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
3413
+ * background: '#ffcc00',
3414
+ * padding: 10,
3415
+ * }}
3416
+ * className="nodrag nopan"
3417
+ * >
3418
+ * {data.label}
3419
+ * </div>
3420
+ * </EdgeLabelRenderer>
3421
+ * </>
3422
+ * );
3423
+ *};
3424
+ *```
3425
+ *
3426
+ * @remarks The `<EdgeLabelRenderer />` has no pointer events by default. If you want to
3427
+ * add mouse interactions you need to set the style `pointerEvents: all` and add
3428
+ * the `nopan` class on the label or the element you want to interact with.
3429
+ */
2998
3430
  function EdgeLabelRenderer({ children }) {
2999
3431
  const edgeLabelRenderer = useStore(selector$6);
3000
3432
  if (!edgeLabelRenderer) {
@@ -3004,6 +3436,31 @@ function EdgeLabelRenderer({ children }) {
3004
3436
  }
3005
3437
 
3006
3438
  const selector$5 = (s) => s.domNode?.querySelector('.react-flow__viewport-portal');
3439
+ /**
3440
+ * The `<ViewportPortal />` component can be used to add components to the same viewport
3441
+ * of the flow where nodes and edges are rendered. This is useful when you want to render
3442
+ * your own components that are adhere to the same coordinate system as the nodes & edges
3443
+ * and are also affected by zooming and panning
3444
+ * @public
3445
+ * @example
3446
+ *
3447
+ * ```jsx
3448
+ *import React from 'react';
3449
+ *import { ViewportPortal } from '@xyflow/react';
3450
+ *
3451
+ *export default function () {
3452
+ * return (
3453
+ * <ViewportPortal>
3454
+ * <div
3455
+ * style={{ transform: 'translate(100px, 100px)', position: 'absolute' }}
3456
+ * >
3457
+ * This div is positioned at [100, 100] on the flow.
3458
+ * </div>
3459
+ * </ViewportPortal>
3460
+ * );
3461
+ *}
3462
+ *```
3463
+ */
3007
3464
  function ViewportPortal({ children }) {
3008
3465
  const viewPortalDiv = useStore(selector$5);
3009
3466
  if (!viewPortalDiv) {
@@ -3013,10 +3470,48 @@ function ViewportPortal({ children }) {
3013
3470
  }
3014
3471
 
3015
3472
  /**
3016
- * Hook for updating node internals.
3473
+ * When you programmatically add or remove handles to a node or update a node's
3474
+ *handle position, you need to let React Flow know about it using this hook. This
3475
+ *will update the internal dimensions of the node and properly reposition handles
3476
+ *on the canvas if necessary.
3017
3477
  *
3018
3478
  * @public
3019
3479
  * @returns function for updating node internals
3480
+ *
3481
+ * @example
3482
+ * ```jsx
3483
+ *import { useCallback, useState } from 'react';
3484
+ *import { Handle, useUpdateNodeInternals } from '@xyflow/react';
3485
+ *
3486
+ *export default function RandomHandleNode({ id }) {
3487
+ * const updateNodeInternals = useUpdateNodeInternals();
3488
+ * const [handleCount, setHandleCount] = useState(0);
3489
+ * const randomizeHandleCount = useCallback(() => {
3490
+ * setHandleCount(Math.floor(Math.random() * 10));
3491
+ * updateNodeInternals(id);
3492
+ * }, [id, updateNodeInternals]);
3493
+ *
3494
+ * return (
3495
+ * <>
3496
+ * {Array.from({ length: handleCount }).map((_, index) => (
3497
+ * <Handle
3498
+ * key={index}
3499
+ * type="target"
3500
+ * position="left"
3501
+ * id={`handle-${index}`}
3502
+ * />
3503
+ * ))}
3504
+ *
3505
+ * <div>
3506
+ * <button onClick={randomizeHandleCount}>Randomize handle count</button>
3507
+ * <p>There are {handleCount} handles on this node.</p>
3508
+ * </div>
3509
+ * </>
3510
+ * );
3511
+ *}
3512
+ *```
3513
+ * @remarks This hook can only be used in a component that is a child of a
3514
+ *{@link ReactFlowProvider} or a {@link ReactFlow} component.
3020
3515
  */
3021
3516
  function useUpdateNodeInternals() {
3022
3517
  const store = useStoreApi();
@@ -3036,10 +3531,23 @@ function useUpdateNodeInternals() {
3036
3531
 
3037
3532
  const nodesSelector = (state) => state.nodes;
3038
3533
  /**
3039
- * Hook for getting the current nodes from the store.
3534
+ * This hook returns an array of the current nodes. Components that use this hook
3535
+ * will re-render **whenever any node changes**, including when a node is selected
3536
+ * or moved.
3040
3537
  *
3041
3538
  * @public
3042
3539
  * @returns An array of nodes
3540
+ *
3541
+ * @example
3542
+ * ```jsx
3543
+ *import { useNodes } from '@xyflow/react';
3544
+ *
3545
+ *export default function() {
3546
+ * const nodes = useNodes();
3547
+ *
3548
+ * return <div>There are currently {nodes.length} nodes!</div>;
3549
+ *}
3550
+ *```
3043
3551
  */
3044
3552
  function useNodes() {
3045
3553
  const nodes = useStore(nodesSelector, shallow);
@@ -3048,10 +3556,22 @@ function useNodes() {
3048
3556
 
3049
3557
  const edgesSelector = (state) => state.edges;
3050
3558
  /**
3051
- * Hook for getting the current edges from the store.
3559
+ * This hook returns an array of the current edges. Components that use this hook
3560
+ * will re-render **whenever any edge changes**.
3052
3561
  *
3053
3562
  * @public
3054
3563
  * @returns An array of edges
3564
+ *
3565
+ * @example
3566
+ * ```tsx
3567
+ *import { useEdges } from '@xyflow/react';
3568
+ *
3569
+ *export default function () {
3570
+ * const edges = useEdges();
3571
+ *
3572
+ * return <div>There are currently {edges.length} edges!</div>;
3573
+ *}
3574
+ *```
3055
3575
  */
3056
3576
  function useEdges() {
3057
3577
  const edges = useStore(edgesSelector, shallow);
@@ -3064,10 +3584,33 @@ const viewportSelector = (state) => ({
3064
3584
  zoom: state.transform[2],
3065
3585
  });
3066
3586
  /**
3067
- * Hook for getting the current viewport from the store.
3587
+ * The `useViewport` hook is a convenient way to read the current state of the
3588
+ *{@link Viewport} in a component. Components that use this hook
3589
+ *will re-render **whenever the viewport changes**.
3068
3590
  *
3069
3591
  * @public
3070
3592
  * @returns The current viewport
3593
+ *
3594
+ * @example
3595
+ *
3596
+ *```jsx
3597
+ *import { useViewport } from '@xyflow/react';
3598
+ *
3599
+ *export default function ViewportDisplay() {
3600
+ * const { x, y, zoom } = useViewport();
3601
+ *
3602
+ * return (
3603
+ * <div>
3604
+ * <p>
3605
+ * The viewport is currently at ({x}, {y}) and zoomed to {zoom}.
3606
+ * </p>
3607
+ * </div>
3608
+ * );
3609
+ *}
3610
+ *```
3611
+ *
3612
+ * @remarks This hook can only be used in a component that is a child of a
3613
+ *{@link ReactFlowProvider} or a {@link ReactFlow} component.
3071
3614
  */
3072
3615
  function useViewport() {
3073
3616
  const viewport = useStore(viewportSelector, shallow);
@@ -3075,11 +3618,41 @@ function useViewport() {
3075
3618
  }
3076
3619
 
3077
3620
  /**
3078
- * Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
3621
+ * This hook makes it easy to prototype a controlled flow where you manage the
3622
+ * state of nodes and edges outside the `ReactFlowInstance`. You can think of it
3623
+ * like React's `useState` hook with an additional helper callback.
3079
3624
  *
3080
3625
  * @public
3081
3626
  * @param initialNodes
3082
3627
  * @returns an array [nodes, setNodes, onNodesChange]
3628
+ * @example
3629
+ *
3630
+ *```tsx
3631
+ *import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
3632
+ *
3633
+ *const initialNodes = [];
3634
+ *const initialEdges = [];
3635
+ *
3636
+ *export default function () {
3637
+ * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
3638
+ * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
3639
+ *
3640
+ * return (
3641
+ * <ReactFlow
3642
+ * nodes={nodes}
3643
+ * edges={edges}
3644
+ * onNodesChange={onNodesChange}
3645
+ * onEdgesChange={onEdgesChange}
3646
+ * />
3647
+ * );
3648
+ *}
3649
+ *```
3650
+ *
3651
+ * @remarks This hook was created to make prototyping easier and our documentation
3652
+ * examples clearer. Although it is OK to use this hook in production, in
3653
+ * practice you may want to use a more sophisticated state management solution
3654
+ * like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead.
3655
+ *
3083
3656
  */
3084
3657
  function useNodesState(initialNodes) {
3085
3658
  const [nodes, setNodes] = useState(initialNodes);
@@ -3087,11 +3660,41 @@ function useNodesState(initialNodes) {
3087
3660
  return [nodes, setNodes, onNodesChange];
3088
3661
  }
3089
3662
  /**
3090
- * Hook for managing the state of edges - should only be used for prototyping / simple use cases.
3663
+ * This hook makes it easy to prototype a controlled flow where you manage the
3664
+ * state of nodes and edges outside the `ReactFlowInstance`. You can think of it
3665
+ * like React's `useState` hook with an additional helper callback.
3091
3666
  *
3092
3667
  * @public
3093
3668
  * @param initialEdges
3094
3669
  * @returns an array [edges, setEdges, onEdgesChange]
3670
+ * @example
3671
+ *
3672
+ *```tsx
3673
+ *import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
3674
+ *
3675
+ *const initialNodes = [];
3676
+ *const initialEdges = [];
3677
+ *
3678
+ *export default function () {
3679
+ * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
3680
+ * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
3681
+ *
3682
+ * return (
3683
+ * <ReactFlow
3684
+ * nodes={nodes}
3685
+ * edges={edges}
3686
+ * onNodesChange={onNodesChange}
3687
+ * onEdgesChange={onEdgesChange}
3688
+ * />
3689
+ * );
3690
+ *}
3691
+ *```
3692
+ *
3693
+ * @remarks This hook was created to make prototyping easier and our documentation
3694
+ * examples clearer. Although it is OK to use this hook in production, in
3695
+ * practice you may want to use a more sophisticated state management solution
3696
+ * like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead.
3697
+ *
3095
3698
  */
3096
3699
  function useEdgesState(initialEdges) {
3097
3700
  const [edges, setEdges] = useState(initialEdges);
@@ -3100,12 +3703,30 @@ function useEdgesState(initialEdges) {
3100
3703
  }
3101
3704
 
3102
3705
  /**
3103
- * Hook for registering an onViewportChange handler.
3706
+ * The `useOnViewportChange` hook lets you listen for changes to the viewport such
3707
+ *as panning and zooming. You can provide a callback for each phase of a viewport
3708
+ *change: `onStart`, `onChange`, and `onEnd`.
3104
3709
  *
3105
3710
  * @public
3106
3711
  * @param params.onStart - gets called when the viewport starts changing
3107
3712
  * @param params.onChange - gets called when the viewport changes
3108
3713
  * @param params.onEnd - gets called when the viewport stops changing
3714
+ *
3715
+ * @example
3716
+ * ```jsx
3717
+ *import { useCallback } from 'react';
3718
+ *import { useOnViewportChange } from '@xyflow/react';
3719
+ *
3720
+ *function ViewportChangeLogger() {
3721
+ * useOnViewportChange({
3722
+ * onStart: (viewport: Viewport) => console.log('start', viewport),
3723
+ * onChange: (viewport: Viewport) => console.log('change', viewport),
3724
+ * onEnd: (viewport: Viewport) => console.log('end', viewport),
3725
+ * });
3726
+ *
3727
+ * return null;
3728
+ *}
3729
+ *```
3109
3730
  */
3110
3731
  function useOnViewportChange({ onStart, onChange, onEnd }) {
3111
3732
  const store = useStoreApi();
@@ -3121,12 +3742,44 @@ function useOnViewportChange({ onStart, onChange, onEnd }) {
3121
3742
  }
3122
3743
 
3123
3744
  /**
3124
- * Hook for registering an onSelectionChange handler.
3745
+ * This hook lets you listen for changes to both node and edge selection. As the
3746
+ *name implies, the callback you provide will be called whenever the selection of
3747
+ *_either_ nodes or edges changes.
3125
3748
  *
3126
3749
  * @public
3127
3750
  * @param params.onChange - The handler to register
3751
+ *
3752
+ * @example
3753
+ * ```jsx
3754
+ *import { useState } from 'react';
3755
+ *import { ReactFlow, useOnSelectionChange } from '@xyflow/react';
3756
+ *
3757
+ *function SelectionDisplay() {
3758
+ * const [selectedNodes, setSelectedNodes] = useState([]);
3759
+ * const [selectedEdges, setSelectedEdges] = useState([]);
3760
+ *
3761
+ * // the passed handler has to be memoized, otherwise the hook will not work correctly
3762
+ * const onChange = useCallback(({ nodes, edges }) => {
3763
+ * setSelectedNodes(nodes.map((node) => node.id));
3764
+ * setSelectedEdges(edges.map((edge) => edge.id));
3765
+ * }, []);
3766
+ *
3767
+ * useOnSelectionChange({
3768
+ * onChange,
3769
+ * });
3770
+ *
3771
+ * return (
3772
+ * <div>
3773
+ * <p>Selected nodes: {selectedNodes.join(', ')}</p>
3774
+ * <p>Selected edges: {selectedEdges.join(', ')}</p>
3775
+ * </div>
3776
+ * );
3777
+ *}
3778
+ *```
3779
+ *
3780
+ * @remarks You need to memoize the passed `onChange` handler, otherwise the hook will not work correctly.
3128
3781
  */
3129
- function useOnSelectionChange({ onChange }) {
3782
+ function useOnSelectionChange({ onChange, }) {
3130
3783
  const store = useStoreApi();
3131
3784
  useEffect(() => {
3132
3785
  const nextOnSelectionChangeHandlers = [...store.getState().onSelectionChangeHandlers, onChange];
@@ -3151,17 +3804,42 @@ const selector$4 = (options) => (s) => {
3151
3804
  }
3152
3805
  return true;
3153
3806
  };
3154
- const defaultOptions = {
3155
- includeHiddenNodes: false,
3156
- };
3157
3807
  /**
3158
- * Hook which returns true when all nodes are initialized.
3808
+ * This hook tells you whether all the nodes in a flow have been measured and given
3809
+ *a width and height. When you add a node to the flow, this hook will return
3810
+ *`false` and then `true` again once the node has been measured.
3159
3811
  *
3160
3812
  * @public
3161
3813
  * @param options.includeHiddenNodes - defaults to false
3162
3814
  * @returns boolean indicating whether all nodes are initialized
3815
+ *
3816
+ * @example
3817
+ * ```jsx
3818
+ *import { useReactFlow, useNodesInitialized } from '@xyflow/react';
3819
+ *import { useEffect, useState } from 'react';
3820
+ *
3821
+ *const options = {
3822
+ * includeHiddenNodes: false,
3823
+ *};
3824
+ *
3825
+ *export default function useLayout() {
3826
+ * const { getNodes } = useReactFlow();
3827
+ * const nodesInitialized = useNodesInitialized(options);
3828
+ * const [layoutedNodes, setLayoutedNodes] = useState(getNodes());
3829
+ *
3830
+ * useEffect(() => {
3831
+ * if (nodesInitialized) {
3832
+ * setLayoutedNodes(yourLayoutingFunction(getNodes()));
3833
+ * }
3834
+ * }, [nodesInitialized]);
3835
+ *
3836
+ * return layoutedNodes;
3837
+ *}
3838
+ *```
3163
3839
  */
3164
- function useNodesInitialized(options = defaultOptions) {
3840
+ function useNodesInitialized(options = {
3841
+ includeHiddenNodes: false,
3842
+ }) {
3165
3843
  const initialized = useStore(selector$4(options));
3166
3844
  return initialized;
3167
3845
  }
@@ -3198,7 +3876,7 @@ function useHandleConnections({ type, id, nodeId, onConnect, onDisconnect, }) {
3198
3876
 
3199
3877
  const error014 = errorMessages['error014']();
3200
3878
  /**
3201
- * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
3879
+ * This hook returns an array of connections on a specific node, handle type ('source', 'target') or handle ID.
3202
3880
  *
3203
3881
  * @public
3204
3882
  * @param param.id - node id - optional if called inside a custom node
@@ -3207,6 +3885,22 @@ const error014 = errorMessages['error014']();
3207
3885
  * @param param.onConnect - gets called when a connection is established
3208
3886
  * @param param.onDisconnect - gets called when a connection is removed
3209
3887
  * @returns an array with connections
3888
+ *
3889
+ * @example
3890
+ * ```jsx
3891
+ *import { useNodeConnections } from '@xyflow/react';
3892
+ *
3893
+ *export default function () {
3894
+ * const connections = useNodeConnections({
3895
+ * handleType: 'target',
3896
+ * handleId: 'my-handle',
3897
+ * });
3898
+ *
3899
+ * return (
3900
+ * <div>There are currently {connections.length} incoming connections!</div>
3901
+ * );
3902
+ *}
3903
+ *```
3210
3904
  */
3211
3905
  function useNodeConnections({ id, handleType, handleId, onConnect, onDisconnect, } = {}) {
3212
3906
  const nodeId = useNodeId();
@@ -3250,11 +3944,31 @@ function useNodesData(nodeIds) {
3250
3944
  }
3251
3945
 
3252
3946
  /**
3253
- * Hook for getting an internal node by id
3947
+ * This hook returns the internal representation of a specific node.
3948
+ * Components that use this hook will re-render **whenever the node changes**,
3949
+ * including when a node is selected or moved.
3254
3950
  *
3255
3951
  * @public
3256
3952
  * @param id - id of the node
3257
3953
  * @returns array with visible node ids
3954
+ *
3955
+ * @example
3956
+ * ```tsx
3957
+ *import { useInternalNode } from '@xyflow/react';
3958
+ *
3959
+ *export default function () {
3960
+ * const internalNode = useInternalNode('node-1');
3961
+ * const absolutePosition = internalNode.internals.positionAbsolute;
3962
+ *
3963
+ * return (
3964
+ * <div>
3965
+ * The absolute position of the node is at:
3966
+ * <p>x: {absolutePosition.x}</p>
3967
+ * <p>y: {absolutePosition.y}</p>
3968
+ * </div>
3969
+ * );
3970
+ *}
3971
+ *```
3258
3972
  */
3259
3973
  function useInternalNode(id) {
3260
3974
  const node = useStore(useCallback((s) => s.nodeLookup.get(id), [id]), shallow);
@@ -3268,6 +3982,12 @@ function DotPattern({ radius, className }) {
3268
3982
  return (jsx("circle", { cx: radius, cy: radius, r: radius, className: cc(['react-flow__background-pattern', 'dots', className]) }));
3269
3983
  }
3270
3984
 
3985
+ /**
3986
+ * The three variants are exported as an enum for convenience. You can either import
3987
+ * the enum and use it like `BackgroundVariant.Lines` or you can use the raw string
3988
+ * value directly.
3989
+ * @public
3990
+ */
3271
3991
  var BackgroundVariant;
3272
3992
  (function (BackgroundVariant) {
3273
3993
  BackgroundVariant["Lines"] = "lines";
@@ -3309,6 +4029,59 @@ size, lineWidth = 1, offset = 0, color, bgColor, style, className, patternClassN
3309
4029
  }, ref: ref, "data-testid": "rf__background", children: [jsx("pattern", { id: _patternId, x: transform[0] % scaledGap[0], y: transform[1] % scaledGap[1], width: scaledGap[0], height: scaledGap[1], patternUnits: "userSpaceOnUse", patternTransform: `translate(-${scaledOffset[0]},-${scaledOffset[1]})`, children: isDots ? (jsx(DotPattern, { radius: scaledSize / 2, className: patternClassName })) : (jsx(LinePattern, { dimensions: patternDimensions, lineWidth: lineWidth, variant: variant, className: patternClassName })) }), jsx("rect", { x: "0", y: "0", width: "100%", height: "100%", fill: `url(#${_patternId})` })] }));
3310
4030
  }
3311
4031
  BackgroundComponent.displayName = 'Background';
4032
+ /**
4033
+ * The `<Background />` component makes it convenient to render different types of backgrounds common in node-based UIs. It comes with three variants: lines, dots and cross.
4034
+ *
4035
+ * @example
4036
+ *
4037
+ * A simple example of how to use the Background component.
4038
+ *
4039
+ * ```tsx
4040
+ * import { useState } from 'react';
4041
+ * import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
4042
+ *
4043
+ * export default function Flow() {
4044
+ * return (
4045
+ * <ReactFlow defaultNodes={[...]} defaultEdges={[...]}>
4046
+ * <Background color="#ccc" variant={BackgroundVariant.Dots} />
4047
+ * </ReactFlow>
4048
+ * );
4049
+ * }
4050
+ * ```
4051
+ *
4052
+ * @example
4053
+ *
4054
+ * In this example you can see how to combine multiple backgrounds
4055
+ *
4056
+ * ```tsx
4057
+ * import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
4058
+ * import '@xyflow/react/dist/style.css';
4059
+ *
4060
+ * export default function Flow() {
4061
+ * return (
4062
+ * <ReactFlow defaultNodes={[...]} defaultEdges={[...]}>
4063
+ * <Background
4064
+ * id="1"
4065
+ * gap={10}
4066
+ * color="#f1f1f1"
4067
+ * variant={BackgroundVariant.Lines}
4068
+ * />
4069
+ * <Background
4070
+ * id="2"
4071
+ * gap={100}
4072
+ * color="#ccc"
4073
+ * variant={BackgroundVariant.Lines}
4074
+ * />
4075
+ * </ReactFlow>
4076
+ * );
4077
+ * }
4078
+ * ```
4079
+ *
4080
+ * @remarks
4081
+ *
4082
+ * When combining multiple <Background /> components it’s important to give each of them a unique id prop!
4083
+ *
4084
+ */
3312
4085
  const Background = memo(BackgroundComponent);
3313
4086
 
3314
4087
  function PlusIcon() {
@@ -3331,6 +4104,29 @@ function UnlockIcon() {
3331
4104
  return (jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 25 32", children: jsx("path", { d: "M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z" }) }));
3332
4105
  }
3333
4106
 
4107
+ /**
4108
+ * You can add buttons to the control panel by using the `<ControlButton />` component
4109
+ * and pass it as a child to the [`<Controls />`](/api-reference/components/controls) component.
4110
+ *
4111
+ * @public
4112
+ * @example
4113
+ *```jsx
4114
+ *import { MagicWand } from '@radix-ui/react-icons'
4115
+ *import { ReactFlow, Controls, ControlButton } from '@xyflow/react'
4116
+ *
4117
+ *export default function Flow() {
4118
+ * return (
4119
+ * <ReactFlow nodes={[...]} edges={[...]}>
4120
+ * <Controls>
4121
+ * <ControlButton onClick={() => alert('Something magical just happened. ✨')}>
4122
+ * <MagicWand />
4123
+ * </ControlButton>
4124
+ * </Controls>
4125
+ * </ReactFlow>
4126
+ * )
4127
+ *}
4128
+ *```
4129
+ */
3334
4130
  function ControlButton({ children, className, ...rest }) {
3335
4131
  return (jsx("button", { type: "button", className: cc(['react-flow__controls-button', className]), ...rest, children: children }));
3336
4132
  }
@@ -3368,6 +4164,27 @@ function ControlsComponent({ style, showZoom = true, showFitView = true, showInt
3368
4164
  return (jsxs(Panel, { className: cc(['react-flow__controls', orientationClass, className]), position: position, style: style, "data-testid": "rf__controls", "aria-label": ariaLabel, children: [showZoom && (jsxs(Fragment, { children: [jsx(ControlButton, { onClick: onZoomInHandler, className: "react-flow__controls-zoomin", title: "zoom in", "aria-label": "zoom in", disabled: maxZoomReached, children: jsx(PlusIcon, {}) }), jsx(ControlButton, { onClick: onZoomOutHandler, className: "react-flow__controls-zoomout", title: "zoom out", "aria-label": "zoom out", disabled: minZoomReached, children: jsx(MinusIcon, {}) })] })), showFitView && (jsx(ControlButton, { className: "react-flow__controls-fitview", onClick: onFitViewHandler, title: "fit view", "aria-label": "fit view", children: jsx(FitViewIcon, {}) })), showInteractive && (jsx(ControlButton, { className: "react-flow__controls-interactive", onClick: onToggleInteractivity, title: "toggle interactivity", "aria-label": "toggle interactivity", children: isInteractive ? jsx(UnlockIcon, {}) : jsx(LockIcon, {}) })), children] }));
3369
4165
  }
3370
4166
  ControlsComponent.displayName = 'Controls';
4167
+ /**
4168
+ * The `<Controls />` component renders a small panel that contains convenient
4169
+ * buttons to zoom in, zoom out, fit the view, and lock the viewport.
4170
+ *
4171
+ * @public
4172
+ * @example
4173
+ *```tsx
4174
+ *import { ReactFlow, Controls } from '@xyflow/react'
4175
+ *
4176
+ *export default function Flow() {
4177
+ * return (
4178
+ * <ReactFlow nodes={[...]} edges={[...]}>
4179
+ * <Controls />
4180
+ * </ReactFlow>
4181
+ * )
4182
+ *}
4183
+ *```
4184
+ *
4185
+ * @remarks To extend or customise the controls, you can use the [`<ControlButton />`](/api-reference/components/control-button) component
4186
+ *
4187
+ */
3371
4188
  const Controls = memo(ControlsComponent);
3372
4189
 
3373
4190
  function MiniMapNodeComponent({ id, x, y, width, height, style, color, strokeColor, strokeWidth, className, borderRadius, shapeRendering, selected, onClick, }) {
@@ -3384,8 +4201,10 @@ const MiniMapNode = memo(MiniMapNodeComponent);
3384
4201
  const selectorNodeIds = (s) => s.nodes.map((node) => node.id);
3385
4202
  const getAttrFunction = (func) => func instanceof Function ? func : () => func;
3386
4203
  function MiniMapNodes({ nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
3387
- // We need to rename the prop to be `CapitalCase` so that JSX will render it as
3388
- // a component properly.
4204
+ /*
4205
+ * We need to rename the prop to be `CapitalCase` so that JSX will render it as
4206
+ * a component properly.
4207
+ */
3389
4208
  nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
3390
4209
  const nodeIds = useStore(selectorNodeIds, shallow);
3391
4210
  const nodeColorFunc = getAttrFunction(nodeColor);
@@ -3393,11 +4212,13 @@ nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
3393
4212
  const nodeClassNameFunc = getAttrFunction(nodeClassName);
3394
4213
  const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
3395
4214
  return (jsx(Fragment, { children: nodeIds.map((nodeId) => (
3396
- // The split of responsibilities between MiniMapNodes and
3397
- // NodeComponentWrapper may appear weird. However, it’s designed to
3398
- // minimize the cost of updates when individual nodes change.
3399
- //
3400
- // For more details, see a similar commit in `NodeRenderer/index.tsx`.
4215
+ /*
4216
+ * The split of responsibilities between MiniMapNodes and
4217
+ * NodeComponentWrapper may appear weird. However, it’s designed to
4218
+ * minimize the cost of updates when individual nodes change.
4219
+ *
4220
+ * For more details, see a similar commit in `NodeRenderer/index.tsx`.
4221
+ */
3401
4222
  jsx(NodeComponentWrapper, { id: nodeId, nodeColorFunc: nodeColorFunc, nodeStrokeColorFunc: nodeStrokeColorFunc, nodeClassNameFunc: nodeClassNameFunc, nodeBorderRadius: nodeBorderRadius, nodeStrokeWidth: nodeStrokeWidth, NodeComponent: NodeComponent, onClick: onClick, shapeRendering: shapeRendering }, nodeId))) }));
3402
4223
  }
3403
4224
  function NodeComponentWrapperInner({ id, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
@@ -3442,8 +4263,10 @@ const selector$1 = (s) => {
3442
4263
  };
3443
4264
  const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
3444
4265
  function MiniMapComponent({ style, className, nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
3445
- // We need to rename the prop to be `CapitalCase` so that JSX will render it as
3446
- // a component properly.
4266
+ /*
4267
+ * We need to rename the prop to be `CapitalCase` so that JSX will render it as
4268
+ * a component properly.
4269
+ */
3447
4270
  nodeComponent, bgColor, maskColor, maskStrokeColor, maskStrokeWidth, position = 'bottom-right', onClick, onNodeClick, pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', inversePan, zoomStep = 10, offsetScale = 5, }) {
3448
4271
  const store = useStoreApi();
3449
4272
  const svg = useRef(null);
@@ -3513,6 +4336,26 @@ nodeComponent, bgColor, maskColor, maskStrokeColor, maskStrokeWidth, position =
3513
4336
  M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd", pointerEvents: "none" })] }) }));
3514
4337
  }
3515
4338
  MiniMapComponent.displayName = 'MiniMap';
4339
+ /**
4340
+ * The `<MiniMap />` component can be used to render an overview of your flow. It
4341
+ * renders each node as an SVG element and visualizes where the current viewport is
4342
+ * in relation to the rest of the flow.
4343
+ *
4344
+ * @public
4345
+ * @example
4346
+ *
4347
+ * ```jsx
4348
+ *import { ReactFlow, MiniMap } from '@xyflow/react';
4349
+ *
4350
+ *export default function Flow() {
4351
+ * return (
4352
+ * <ReactFlow nodes={[...]]} edges={[...]]}>
4353
+ * <MiniMap nodeStrokeWidth={3} />
4354
+ * </ReactFlow>
4355
+ * );
4356
+ *}
4357
+ *```
4358
+ */
3516
4359
  const MiniMap = memo(MiniMapComponent);
3517
4360
 
3518
4361
  function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle, className, style = {}, children, color, minWidth = 10, minHeight = 10, maxWidth = Number.MAX_VALUE, maxHeight = Number.MAX_VALUE, keepAspectRatio = false, shouldResize, onResizeStart, onResize, onResizeEnd, }) {
@@ -3549,8 +4392,8 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3549
4392
  const node = nodeLookup.get(id);
3550
4393
  if (node && node.expandParent && node.parentId) {
3551
4394
  const origin = node.origin ?? nodeOrigin;
3552
- const width = change.width ?? node.measured.width;
3553
- const height = change.height ?? node.measured.height;
4395
+ const width = change.width ?? node.measured.width ?? 0;
4396
+ const height = change.height ?? node.measured.height ?? 0;
3554
4397
  const child = {
3555
4398
  id: node.id,
3556
4399
  parentId: node.parentId,
@@ -3565,8 +4408,10 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3565
4408
  };
3566
4409
  const parentExpandChanges = handleExpandParent([child], nodeLookup, parentLookup, nodeOrigin);
3567
4410
  changes.push(...parentExpandChanges);
3568
- // when the parent was expanded by the child node, its position will be clamped at
3569
- // 0,0 when node origin is 0,0 and to width, height if it's 1,1
4411
+ /*
4412
+ * when the parent was expanded by the child node, its position will be clamped at
4413
+ * 0,0 when node origin is 0,0 and to width, height if it's 1,1
4414
+ */
3570
4415
  nextPosition.x = change.x ? Math.max(origin[0] * width, change.x) : undefined;
3571
4416
  nextPosition.y = change.y ? Math.max(origin[1] * height, change.y) : undefined;
3572
4417
  }
@@ -3644,8 +4489,37 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3644
4489
  const controlStyle = color ? { ...style, [colorStyleProp]: color } : style;
3645
4490
  return (jsx("div", { className: cc(['react-flow__resize-control', 'nodrag', ...positionClassNames, variant, className]), ref: resizeControlRef, style: controlStyle, children: children }));
3646
4491
  }
4492
+ /**
4493
+ * To create your own resizing UI, you can use the `NodeResizeControl` component where you can pass children (such as icons).
4494
+ * @public
4495
+ *
4496
+ */
3647
4497
  const NodeResizeControl = memo(ResizeControl);
3648
4498
 
4499
+ /**
4500
+ * The `<NodeResizer />` component can be used to add a resize functionality to your
4501
+ * nodes. It renders draggable controls around the node to resize in all directions.
4502
+ * @public
4503
+ *
4504
+ * @example
4505
+ *```jsx
4506
+ *import { memo } from 'react';
4507
+ *import { Handle, Position, NodeResizer } from '@xyflow/react';
4508
+ *
4509
+ *function ResizableNode({ data }) {
4510
+ * return (
4511
+ * <>
4512
+ * <NodeResizer minWidth={100} minHeight={30} />
4513
+ * <Handle type="target" position={Position.Left} />
4514
+ * <div style={{ padding: 10 }}>{data.label}</div>
4515
+ * <Handle type="source" position={Position.Right} />
4516
+ * </>
4517
+ * );
4518
+ *};
4519
+ *
4520
+ *export default memo(ResizableNode);
4521
+ *```
4522
+ */
3649
4523
  function NodeResizer({ nodeId, isVisible = true, handleClassName, handleStyle, lineClassName, lineStyle, color, minWidth = 10, minHeight = 10, maxWidth = Number.MAX_VALUE, maxHeight = Number.MAX_VALUE, keepAspectRatio = false, shouldResize, onResizeStart, onResize, onResizeEnd, }) {
3650
4524
  if (!isVisible) {
3651
4525
  return null;
@@ -3685,6 +4559,41 @@ const storeSelector = (state) => ({
3685
4559
  zoom: state.transform[2],
3686
4560
  selectedNodesCount: state.nodes.filter((node) => node.selected).length,
3687
4561
  });
4562
+ /**
4563
+ * This component can render a toolbar or tooltip to one side of a custom node. This
4564
+ * toolbar doesn't scale with the viewport so that the content is always visible.
4565
+ *
4566
+ * @public
4567
+ * @example
4568
+ * ```jsx
4569
+ *import { memo } from 'react';
4570
+ *import { Handle, Position, NodeToolbar } from '@xyflow/react';
4571
+ *
4572
+ *function CustomNode({ data }) {
4573
+ * return (
4574
+ * <>
4575
+ * <NodeToolbar isVisible={data.toolbarVisible} position={data.toolbarPosition}>
4576
+ * <button>delete</button>
4577
+ * <button>copy</button>
4578
+ * <button>expand</button>
4579
+ * </NodeToolbar>
4580
+ *
4581
+ * <div style={{ padding: '10px 20px' }}>
4582
+ * {data.label}
4583
+ * </div>
4584
+ *
4585
+ * <Handle type="target" position={Position.Left} />
4586
+ * <Handle type="source" position={Position.Right} />
4587
+ * </>
4588
+ * );
4589
+ *};
4590
+ *
4591
+ *export default memo(CustomNode);
4592
+ *```
4593
+ * @remarks By default, the toolbar is only visible when a node is selected. If multiple
4594
+ * nodes are selected it will not be visible to prevent overlapping toolbars or
4595
+ * clutter. You can override this behavior by setting the `isVisible` prop to `true`.
4596
+ */
3688
4597
  function NodeToolbar({ nodeId, children, className, style, isVisible, position = Position.Top, offset = 10, align = 'center', ...rest }) {
3689
4598
  const contextNodeId = useNodeId();
3690
4599
  const nodesSelector = useCallback((state) => {
@@ -3703,7 +4612,7 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
3703
4612
  // if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
3704
4613
  const isActive = typeof isVisible === 'boolean'
3705
4614
  ? isVisible
3706
- : nodes.size === 1 && nodes.values().next().value.selected && selectedNodesCount === 1;
4615
+ : nodes.size === 1 && nodes.values().next().value?.selected && selectedNodesCount === 1;
3707
4616
  if (!isActive || !nodes.size) {
3708
4617
  return null;
3709
4618
  }