@xyflow/react 12.4.1 → 12.4.3

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 (218) 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/Edges/BaseEdge.d.ts +27 -0
  33. package/dist/esm/components/Edges/BaseEdge.d.ts.map +1 -1
  34. package/dist/esm/components/Edges/EdgeText.d.ts +26 -0
  35. package/dist/esm/components/Edges/EdgeText.d.ts.map +1 -1
  36. package/dist/esm/components/Edges/SimpleBezierEdge.d.ts +5 -0
  37. package/dist/esm/components/Edges/SimpleBezierEdge.d.ts.map +1 -1
  38. package/dist/esm/components/Edges/index.d.ts.map +1 -1
  39. package/dist/esm/components/Handle/index.d.ts +32 -4
  40. package/dist/esm/components/Handle/index.d.ts.map +1 -1
  41. package/dist/esm/components/NodeWrapper/index.d.ts +1 -1
  42. package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
  43. package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts.map +1 -1
  44. package/dist/esm/components/NodeWrapper/utils.d.ts.map +1 -1
  45. package/dist/esm/components/Nodes/utils.d.ts.map +1 -1
  46. package/dist/esm/components/Panel/index.d.ts +32 -4
  47. package/dist/esm/components/Panel/index.d.ts.map +1 -1
  48. package/dist/esm/components/ReactFlowProvider/index.d.ts +34 -0
  49. package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -1
  50. package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
  51. package/dist/esm/components/ViewportPortal/index.d.ts +25 -0
  52. package/dist/esm/components/ViewportPortal/index.d.ts.map +1 -1
  53. package/dist/esm/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
  54. package/dist/esm/container/NodeRenderer/index.d.ts.map +1 -1
  55. package/dist/esm/container/Pane/index.d.ts.map +1 -1
  56. package/dist/esm/container/ReactFlow/Wrapper.d.ts.map +1 -1
  57. package/dist/esm/container/ReactFlow/index.d.ts +20 -0
  58. package/dist/esm/container/ReactFlow/index.d.ts.map +1 -1
  59. package/dist/esm/contexts/NodeIdContext.d.ts +28 -0
  60. package/dist/esm/contexts/NodeIdContext.d.ts.map +1 -1
  61. package/dist/esm/hooks/useConnection.d.ts +20 -1
  62. package/dist/esm/hooks/useConnection.d.ts.map +1 -1
  63. package/dist/esm/hooks/useEdges.d.ts +13 -1
  64. package/dist/esm/hooks/useEdges.d.ts.map +1 -1
  65. package/dist/esm/hooks/useInternalNode.d.ts +21 -1
  66. package/dist/esm/hooks/useInternalNode.d.ts.map +1 -1
  67. package/dist/esm/hooks/useKeyPress.d.ts +19 -1
  68. package/dist/esm/hooks/useKeyPress.d.ts.map +1 -1
  69. package/dist/esm/hooks/useMoveSelectedNodes.d.ts.map +1 -1
  70. package/dist/esm/hooks/useNodeConnections.d.ts +17 -1
  71. package/dist/esm/hooks/useNodeConnections.d.ts.map +1 -1
  72. package/dist/esm/hooks/useNodes.d.ts +14 -1
  73. package/dist/esm/hooks/useNodes.d.ts.map +1 -1
  74. package/dist/esm/hooks/useNodesData.d.ts +14 -2
  75. package/dist/esm/hooks/useNodesData.d.ts.map +1 -1
  76. package/dist/esm/hooks/useNodesEdgesState.d.ts +62 -2
  77. package/dist/esm/hooks/useNodesEdgesState.d.ts.map +1 -1
  78. package/dist/esm/hooks/useNodesInitialized.d.ts +27 -1
  79. package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -1
  80. package/dist/esm/hooks/useOnSelectionChange.d.ts +33 -1
  81. package/dist/esm/hooks/useOnSelectionChange.d.ts.map +1 -1
  82. package/dist/esm/hooks/useOnViewportChange.d.ts +19 -1
  83. package/dist/esm/hooks/useOnViewportChange.d.ts.map +1 -1
  84. package/dist/esm/hooks/useReactFlow.d.ts +24 -1
  85. package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
  86. package/dist/esm/hooks/useStore.d.ts +23 -2
  87. package/dist/esm/hooks/useStore.d.ts.map +1 -1
  88. package/dist/esm/hooks/useUpdateNodeInternals.d.ts +39 -1
  89. package/dist/esm/hooks/useUpdateNodeInternals.d.ts.map +1 -1
  90. package/dist/esm/hooks/useViewport.d.ts +24 -1
  91. package/dist/esm/hooks/useViewport.d.ts.map +1 -1
  92. package/dist/esm/hooks/useViewportHelper.d.ts.map +1 -1
  93. package/dist/esm/index.js +1090 -185
  94. package/dist/esm/index.mjs +1090 -185
  95. package/dist/esm/store/index.d.ts.map +1 -1
  96. package/dist/esm/types/component-props.d.ts +111 -56
  97. package/dist/esm/types/component-props.d.ts.map +1 -1
  98. package/dist/esm/types/edges.d.ts +27 -2
  99. package/dist/esm/types/edges.d.ts.map +1 -1
  100. package/dist/esm/types/general.d.ts +41 -0
  101. package/dist/esm/types/general.d.ts.map +1 -1
  102. package/dist/esm/types/instance.d.ts +11 -0
  103. package/dist/esm/types/instance.d.ts.map +1 -1
  104. package/dist/esm/types/nodes.d.ts +46 -4
  105. package/dist/esm/types/nodes.d.ts.map +1 -1
  106. package/dist/esm/utils/changes.d.ts +45 -23
  107. package/dist/esm/utils/changes.d.ts.map +1 -1
  108. package/dist/esm/utils/general.d.ts +26 -2
  109. package/dist/esm/utils/general.d.ts.map +1 -1
  110. package/dist/umd/additional-components/Background/Background.d.ts +53 -0
  111. package/dist/umd/additional-components/Background/Background.d.ts.map +1 -1
  112. package/dist/umd/additional-components/Background/types.d.ts +11 -1
  113. package/dist/umd/additional-components/Background/types.d.ts.map +1 -1
  114. package/dist/umd/additional-components/Controls/ControlButton.d.ts +23 -0
  115. package/dist/umd/additional-components/Controls/ControlButton.d.ts.map +1 -1
  116. package/dist/umd/additional-components/Controls/Controls.d.ts +21 -0
  117. package/dist/umd/additional-components/Controls/Controls.d.ts.map +1 -1
  118. package/dist/umd/additional-components/Controls/types.d.ts +8 -1
  119. package/dist/umd/additional-components/Controls/types.d.ts.map +1 -1
  120. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts +20 -0
  121. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  122. package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  123. package/dist/umd/additional-components/MiniMap/types.d.ts +11 -1
  124. package/dist/umd/additional-components/MiniMap/types.d.ts.map +1 -1
  125. package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts +5 -0
  126. package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  127. package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts +24 -0
  128. package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -1
  129. package/dist/umd/additional-components/NodeResizer/types.d.ts +15 -3
  130. package/dist/umd/additional-components/NodeResizer/types.d.ts.map +1 -1
  131. package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts +35 -0
  132. package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -1
  133. package/dist/umd/additional-components/NodeToolbar/types.d.ts +7 -2
  134. package/dist/umd/additional-components/NodeToolbar/types.d.ts.map +1 -1
  135. package/dist/umd/components/BatchProvider/index.d.ts.map +1 -1
  136. package/dist/umd/components/BatchProvider/useQueue.d.ts.map +1 -1
  137. package/dist/umd/components/ConnectionLine/index.d.ts +4 -4
  138. package/dist/umd/components/ConnectionLine/index.d.ts.map +1 -1
  139. package/dist/umd/components/EdgeLabelRenderer/index.d.ts +40 -0
  140. package/dist/umd/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  141. package/dist/umd/components/Edges/BaseEdge.d.ts +27 -0
  142. package/dist/umd/components/Edges/BaseEdge.d.ts.map +1 -1
  143. package/dist/umd/components/Edges/EdgeText.d.ts +26 -0
  144. package/dist/umd/components/Edges/EdgeText.d.ts.map +1 -1
  145. package/dist/umd/components/Edges/SimpleBezierEdge.d.ts +5 -0
  146. package/dist/umd/components/Edges/SimpleBezierEdge.d.ts.map +1 -1
  147. package/dist/umd/components/Edges/index.d.ts.map +1 -1
  148. package/dist/umd/components/Handle/index.d.ts +32 -4
  149. package/dist/umd/components/Handle/index.d.ts.map +1 -1
  150. package/dist/umd/components/NodeWrapper/index.d.ts +1 -1
  151. package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
  152. package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts.map +1 -1
  153. package/dist/umd/components/NodeWrapper/utils.d.ts.map +1 -1
  154. package/dist/umd/components/Nodes/utils.d.ts.map +1 -1
  155. package/dist/umd/components/Panel/index.d.ts +32 -4
  156. package/dist/umd/components/Panel/index.d.ts.map +1 -1
  157. package/dist/umd/components/ReactFlowProvider/index.d.ts +34 -0
  158. package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -1
  159. package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
  160. package/dist/umd/components/ViewportPortal/index.d.ts +25 -0
  161. package/dist/umd/components/ViewportPortal/index.d.ts.map +1 -1
  162. package/dist/umd/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
  163. package/dist/umd/container/NodeRenderer/index.d.ts.map +1 -1
  164. package/dist/umd/container/Pane/index.d.ts.map +1 -1
  165. package/dist/umd/container/ReactFlow/Wrapper.d.ts.map +1 -1
  166. package/dist/umd/container/ReactFlow/index.d.ts +20 -0
  167. package/dist/umd/container/ReactFlow/index.d.ts.map +1 -1
  168. package/dist/umd/contexts/NodeIdContext.d.ts +28 -0
  169. package/dist/umd/contexts/NodeIdContext.d.ts.map +1 -1
  170. package/dist/umd/hooks/useConnection.d.ts +20 -1
  171. package/dist/umd/hooks/useConnection.d.ts.map +1 -1
  172. package/dist/umd/hooks/useEdges.d.ts +13 -1
  173. package/dist/umd/hooks/useEdges.d.ts.map +1 -1
  174. package/dist/umd/hooks/useInternalNode.d.ts +21 -1
  175. package/dist/umd/hooks/useInternalNode.d.ts.map +1 -1
  176. package/dist/umd/hooks/useKeyPress.d.ts +19 -1
  177. package/dist/umd/hooks/useKeyPress.d.ts.map +1 -1
  178. package/dist/umd/hooks/useMoveSelectedNodes.d.ts.map +1 -1
  179. package/dist/umd/hooks/useNodeConnections.d.ts +17 -1
  180. package/dist/umd/hooks/useNodeConnections.d.ts.map +1 -1
  181. package/dist/umd/hooks/useNodes.d.ts +14 -1
  182. package/dist/umd/hooks/useNodes.d.ts.map +1 -1
  183. package/dist/umd/hooks/useNodesData.d.ts +14 -2
  184. package/dist/umd/hooks/useNodesData.d.ts.map +1 -1
  185. package/dist/umd/hooks/useNodesEdgesState.d.ts +62 -2
  186. package/dist/umd/hooks/useNodesEdgesState.d.ts.map +1 -1
  187. package/dist/umd/hooks/useNodesInitialized.d.ts +27 -1
  188. package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
  189. package/dist/umd/hooks/useOnSelectionChange.d.ts +33 -1
  190. package/dist/umd/hooks/useOnSelectionChange.d.ts.map +1 -1
  191. package/dist/umd/hooks/useOnViewportChange.d.ts +19 -1
  192. package/dist/umd/hooks/useOnViewportChange.d.ts.map +1 -1
  193. package/dist/umd/hooks/useReactFlow.d.ts +24 -1
  194. package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
  195. package/dist/umd/hooks/useStore.d.ts +23 -2
  196. package/dist/umd/hooks/useStore.d.ts.map +1 -1
  197. package/dist/umd/hooks/useUpdateNodeInternals.d.ts +39 -1
  198. package/dist/umd/hooks/useUpdateNodeInternals.d.ts.map +1 -1
  199. package/dist/umd/hooks/useViewport.d.ts +24 -1
  200. package/dist/umd/hooks/useViewport.d.ts.map +1 -1
  201. package/dist/umd/hooks/useViewportHelper.d.ts.map +1 -1
  202. package/dist/umd/index.js +2 -2
  203. package/dist/umd/store/index.d.ts.map +1 -1
  204. package/dist/umd/types/component-props.d.ts +111 -56
  205. package/dist/umd/types/component-props.d.ts.map +1 -1
  206. package/dist/umd/types/edges.d.ts +27 -2
  207. package/dist/umd/types/edges.d.ts.map +1 -1
  208. package/dist/umd/types/general.d.ts +41 -0
  209. package/dist/umd/types/general.d.ts.map +1 -1
  210. package/dist/umd/types/instance.d.ts +11 -0
  211. package/dist/umd/types/instance.d.ts.map +1 -1
  212. package/dist/umd/types/nodes.d.ts +46 -4
  213. package/dist/umd/types/nodes.d.ts.map +1 -1
  214. package/dist/umd/utils/changes.d.ts +45 -23
  215. package/dist/umd/utils/changes.d.ts.map +1 -1
  216. package/dist/umd/utils/general.d.ts +26 -2
  217. package/dist/umd/utils/general.d.ts.map +1 -1
  218. package/package.json +4 -3
@@ -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) {
@@ -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;
@@ -1590,8 +1798,10 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1590
1798
  connectingfrom: connectingFrom,
1591
1799
  connectingto: connectingTo,
1592
1800
  valid,
1593
- // shows where you can start a connection from
1594
- // and where you can end it while connecting
1801
+ /*
1802
+ * shows where you can start a connection from
1803
+ * and where you can end it while connecting
1804
+ */
1595
1805
  connectionindicator: isConnectable &&
1596
1806
  (!connectionInProcess || isPossibleEndHandle) &&
1597
1807
  (connectionInProcess ? isConnectableEnd : isConnectableStart),
@@ -1599,7 +1809,29 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1599
1809
  ]), onMouseDown: onPointerDown, onTouchStart: onPointerDown, onClick: connectOnClick ? onClick : undefined, ref: ref, ...rest, children: children }));
1600
1810
  }
1601
1811
  /**
1602
- * The Handle component is a UI element that is used to connect nodes.
1812
+ * The `<Handle />` component is used in your [custom nodes](/learn/customization/custom-nodes)
1813
+ * to define connection points.
1814
+ *
1815
+ *@public
1816
+ *
1817
+ *@example
1818
+ *
1819
+ *```jsx
1820
+ *import { Handle, Position } from '@xyflow/react';
1821
+ *
1822
+ *export function CustomNode({ data }) {
1823
+ * return (
1824
+ * <>
1825
+ * <div style={{ padding: '10px 20px' }}>
1826
+ * {data.label}
1827
+ * </div>
1828
+ *
1829
+ * <Handle type="target" position={Position.Left} />
1830
+ * <Handle type="source" position={Position.Right} />
1831
+ * </>
1832
+ * );
1833
+ *};
1834
+ *```
1603
1835
  */
1604
1836
  const Handle = memo(fixedForwardRef(HandleComponent));
1605
1837
 
@@ -1792,8 +2024,10 @@ function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
1792
2024
  }, []);
1793
2025
  useEffect(() => {
1794
2026
  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
2027
+ /*
2028
+ * when the user programmatically changes the source or handle position, we need to update the internals
2029
+ * to make sure the edges are updated correctly
2030
+ */
1797
2031
  const typeChanged = prevType.current !== nodeType;
1798
2032
  const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
1799
2033
  const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
@@ -1810,7 +2044,7 @@ function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
1810
2044
  return nodeRef;
1811
2045
  }
1812
2046
 
1813
- function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, nodeClickDistance, onError, }) {
2047
+ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeClickDistance, onError, }) {
1814
2048
  const { node, internals, isParent } = useStore((s) => {
1815
2049
  const node = s.nodeLookup.get(id);
1816
2050
  const isParent = s.parentLookup.has(id);
@@ -1868,8 +2102,10 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1868
2102
  const onSelectNodeHandler = (event) => {
1869
2103
  const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
1870
2104
  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
2105
+ /*
2106
+ * this handler gets called by XYDrag on drag start when selectNodesOnDrag=true
2107
+ * here we only need to call it when selectNodesOnDrag=false
2108
+ */
1873
2109
  handleNodeClick({
1874
2110
  id,
1875
2111
  store,
@@ -1929,7 +2165,7 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1929
2165
  visibility: hasDimensions ? 'visible' : 'hidden',
1930
2166
  ...node.style,
1931
2167
  ...inlineDimensions,
1932
- }, "data-id": id, "data-testid": `rf__node-${id}`, onMouseEnter: onMouseEnterHandler, onMouseMove: onMouseMoveHandler, onMouseLeave: onMouseLeaveHandler, onContextMenu: onContextMenuHandler, onClick: onSelectNodeHandler, onDoubleClick: onDoubleClickHandler, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : undefined, "aria-describedby": disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`, "aria-label": node.ariaLabel, children: jsx(Provider, { value: id, children: jsx(NodeComponent, { id: id, data: node.data, type: nodeType, positionAbsoluteX: internals.positionAbsolute.x, positionAbsoluteY: internals.positionAbsolute.y, selected: node.selected, selectable: isSelectable, draggable: isDraggable, deletable: node.deletable ?? true, isConnectable: isConnectable, sourcePosition: node.sourcePosition, targetPosition: node.targetPosition, dragging: dragging, dragHandle: node.dragHandle, zIndex: internals.z, parentId: node.parentId, ...nodeDimensions }) }) }));
2168
+ }, "data-id": id, "data-testid": `rf__node-${id}`, onMouseEnter: onMouseEnterHandler, onMouseMove: onMouseMoveHandler, onMouseLeave: onMouseLeaveHandler, onContextMenu: onContextMenuHandler, onClick: onSelectNodeHandler, onDoubleClick: onDoubleClickHandler, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : undefined, "aria-describedby": disableKeyboardA11y ? undefined : `${ARIA_NODE_DESC_KEY}-${rfId}`, "aria-label": node.ariaLabel, children: jsx(Provider, { value: id, children: jsx(NodeComponent, { id: id, data: node.data, type: nodeType, positionAbsoluteX: internals.positionAbsolute.x, positionAbsoluteY: internals.positionAbsolute.y, selected: node.selected ?? false, selectable: isSelectable, draggable: isDraggable, deletable: node.deletable ?? true, isConnectable: isConnectable, sourcePosition: node.sourcePosition, targetPosition: node.targetPosition, dragging: dragging, dragHandle: node.dragHandle, zIndex: internals.z, parentId: node.parentId, ...nodeDimensions }) }) }));
1933
2169
  }
1934
2170
 
1935
2171
  const selector$b = (s) => ({
@@ -1945,29 +2181,31 @@ function NodeRendererComponent(props) {
1945
2181
  const resizeObserver = useResizeObserver();
1946
2182
  return (jsx("div", { className: "react-flow__nodes", style: containerStyle, children: nodeIds.map((nodeId) => {
1947
2183
  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.
2184
+ /*
2185
+ * The split of responsibilities between NodeRenderer and
2186
+ * NodeComponentWrapper may appear weird. However, it’s designed to
2187
+ * minimize the cost of updates when individual nodes change.
2188
+ *
2189
+ * For example, when you’re dragging a single node, that node gets
2190
+ * updated multiple times per second. If `NodeRenderer` were to update
2191
+ * every time, it would have to re-run the `nodes.map()` loop every
2192
+ * time. This gets pricey with hundreds of nodes, especially if every
2193
+ * loop cycle does more than just rendering a JSX element!
2194
+ *
2195
+ * As a result of this choice, we took the following implementation
2196
+ * decisions:
2197
+ * - NodeRenderer subscribes *only* to node IDs and therefore
2198
+ * rerender *only* when visible nodes are added or removed.
2199
+ * - NodeRenderer performs all operations the result of which can be
2200
+ * shared between nodes (such as creating the `ResizeObserver`
2201
+ * instance, or subscribing to `selector`). This means extra prop
2202
+ * drilling into `NodeComponentWrapper`, but it means we need to run
2203
+ * these operations only once instead of once per node.
2204
+ * - Any operations that you’d normally write inside `nodes.map` are
2205
+ * moved into `NodeComponentWrapper`. This ensures they are
2206
+ * memorized so if `NodeRenderer` *has* to rerender, it only
2207
+ * needs to regenerate the list of nodes, nothing else.
2208
+ */
1971
2209
  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
2210
  }) }));
1973
2211
  }
@@ -2046,9 +2284,11 @@ const Marker = ({ id, type, color, width = 12.5, height = 12.5, markerUnits = 's
2046
2284
  }
2047
2285
  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
2286
  };
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
2287
+ /*
2288
+ * when you have multiple flows on a page and you hide the first one, the other ones have no markers anymore
2289
+ * when they do have markers with the same ids. To prevent this the user can pass a unique id to the react flow wrapper
2290
+ * that we can then use for creating our unique marker ids
2291
+ */
2052
2292
  const MarkerDefinitions = ({ defaultColor, rfId }) => {
2053
2293
  const edges = useStore((s) => s.edges);
2054
2294
  const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions);
@@ -2090,8 +2330,61 @@ function EdgeTextComponent({ x, y, label, labelStyle = {}, labelShowBg = true, l
2090
2330
  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
2331
  }
2092
2332
  EdgeTextComponent.displayName = 'EdgeText';
2333
+ /**
2334
+ * You can use the `<EdgeText />` component as a helper component to display text
2335
+ * within your custom edges.
2336
+ *
2337
+ *@public
2338
+ *
2339
+ *@example
2340
+ *```jsx
2341
+ *import { EdgeText } from '@xyflow/react';
2342
+ *
2343
+ *export function CustomEdgeLabel({ label }) {
2344
+ * return (
2345
+ * <EdgeText
2346
+ * x={100}
2347
+ * y={100}
2348
+ * label={label}
2349
+ * labelStyle={{ fill: 'white' }}
2350
+ * labelShowBg
2351
+ * labelBgStyle={{ fill: 'red' }}
2352
+ * labelBgPadding={[2, 4]}
2353
+ * labelBgBorderRadius={2}
2354
+ * />
2355
+ * );
2356
+ *}
2357
+ *```
2358
+ */
2093
2359
  const EdgeText = memo(EdgeTextComponent);
2094
2360
 
2361
+ /**
2362
+ * The `<BaseEdge />` component gets used internally for all the edges. It can be
2363
+ * used inside a custom edge and handles the invisible helper edge and the edge label
2364
+ * for you.
2365
+ *
2366
+ * @public
2367
+ * @example
2368
+ * ```jsx
2369
+ *import { BaseEdge } from '@xyflow/react';
2370
+ *
2371
+ *export function CustomEdge({ sourceX, sourceY, targetX, targetY, ...props }) {
2372
+ * const [edgePath] = getStraightPath({
2373
+ * sourceX,
2374
+ * sourceY,
2375
+ * targetX,
2376
+ * targetY,
2377
+ * });
2378
+ *
2379
+ * return <BaseEdge path={edgePath} {...props} />;
2380
+ *}
2381
+ *```
2382
+ *
2383
+ * @remarks If you want to use an edge marker with the [`<BaseEdge />`](/api-reference/components/base-edge) component,
2384
+ * you can pass the `markerStart` or `markerEnd` props passed to your custom edge
2385
+ * through to the [`<BaseEdge />`](/api-reference/components/base-edge) component.
2386
+ * You can see all the props passed to a custom edge by looking at the [`EdgeProps`](/api-reference/types/edge-props) type.
2387
+ */
2095
2388
  function BaseEdge({ path, labelX, labelY, label, labelStyle, labelShowBg, labelBgStyle, labelBgPadding, labelBgBorderRadius, interactionWidth = 20, ...props }) {
2096
2389
  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
2390
  }
@@ -2102,6 +2395,11 @@ function getControl({ pos, x1, y1, x2, y2 }) {
2102
2395
  }
2103
2396
  return [x1, 0.5 * (y1 + y2)];
2104
2397
  }
2398
+ /**
2399
+ * The `getSimpleBezierPath` util returns everything you need to render a simple
2400
+ * bezier edge between two nodes.
2401
+ * @public
2402
+ */
2105
2403
  function getSimpleBezierPath({ sourceX, sourceY, sourcePosition = Position.Bottom, targetX, targetY, targetPosition = Position.Top, }) {
2106
2404
  const [sourceControlX, sourceControlY] = getControl({
2107
2405
  pos: sourcePosition,
@@ -2502,9 +2800,28 @@ function getSelector(connectionSelector) {
2502
2800
  return storeSelector$1;
2503
2801
  }
2504
2802
  /**
2505
- * Hook for accessing the connection state.
2803
+ * The `useConnection` hook returns the current connection when there is an active
2804
+ * connection interaction. If no connection interaction is active, it returns null
2805
+ * for every property. A typical use case for this hook is to colorize handles
2806
+ * based on a certain condition (e.g. if the connection is valid or not).
2506
2807
  *
2507
2808
  * @public
2809
+ * @example
2810
+ *
2811
+ * ```tsx
2812
+ *import { useConnection } from '@xyflow/react';
2813
+ *
2814
+ *function App() {
2815
+ * const connection = useConnection();
2816
+ *
2817
+ * return (
2818
+ * <div> {connection ? `Someone is trying to make a connection from ${connection.fromNode} to this one.` : 'There are currently no incoming connections!'}
2819
+ *
2820
+ * </div>
2821
+ * );
2822
+ * }
2823
+ * ```
2824
+ *
2508
2825
  * @returns ConnectionState
2509
2826
  */
2510
2827
  function useConnection(connectionSelector) {
@@ -2519,7 +2836,7 @@ const selector$7 = (s) => ({
2519
2836
  width: s.width,
2520
2837
  height: s.height,
2521
2838
  });
2522
- function ConnectionLineWrapper({ containerStyle, style, type, component }) {
2839
+ function ConnectionLineWrapper({ containerStyle, style, type, component, }) {
2523
2840
  const { nodesConnectable, width, height, isValid, inProgress } = useStore(selector$7, shallow);
2524
2841
  const renderConnection = !!(width && nodesConnectable && inProgress);
2525
2842
  if (!renderConnection) {
@@ -2527,7 +2844,7 @@ function ConnectionLineWrapper({ containerStyle, style, type, component }) {
2527
2844
  }
2528
2845
  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
2846
  }
2530
- const ConnectionLine = ({ style, type = ConnectionLineType.Bezier, CustomComponent, isValid }) => {
2847
+ const ConnectionLine = ({ style, type = ConnectionLineType.Bezier, CustomComponent, isValid, }) => {
2531
2848
  const { inProgress, from, fromNode, fromHandle, fromPosition, to, toNode, toHandle, toPosition } = useConnection();
2532
2849
  if (!inProgress) {
2533
2850
  return;
@@ -2700,12 +3017,14 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2700
3017
  ...getInitialState({ nodes, edges, width, height, fitView: fitView$1, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
2701
3018
  setNodes: (nodes) => {
2702
3019
  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.
3020
+ /*
3021
+ * setNodes() is called exclusively in response to user actions:
3022
+ * - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
3023
+ * - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
3024
+ *
3025
+ * When this happens, we take the note objects passed by the user and extend them with fields
3026
+ * relevant for internal React Flow operations.
3027
+ */
2709
3028
  adoptUserNodes(nodes, nodeLookup, parentLookup, {
2710
3029
  nodeOrigin,
2711
3030
  nodeExtent,
@@ -2731,9 +3050,11 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2731
3050
  set({ hasDefaultEdges: true });
2732
3051
  }
2733
3052
  },
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.
3053
+ /*
3054
+ * Every node gets registerd at a ResizeObserver. Whenever a node
3055
+ * changes its dimensions, this function is called to measure the
3056
+ * new dimensions and update the nodes.
3057
+ */
2737
3058
  updateNodeInternals: (updates, params = { triggerFitView: true }) => {
2738
3059
  const { triggerNodeChanges, nodeLookup, parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, nodeExtent, debug, fitViewSync, } = get();
2739
3060
  const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent);
@@ -2750,11 +3071,13 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2750
3071
  nodes: fitViewOnInitOptions?.nodes,
2751
3072
  });
2752
3073
  }
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.
3074
+ /*
3075
+ * here we are cirmumventing the onNodesChange handler
3076
+ * in order to be able to display nodes even if the user
3077
+ * has not provided an onNodesChange handler.
3078
+ * Nodes are only rendered if they have a width and height
3079
+ * attribute which they get from this handler.
3080
+ */
2758
3081
  set({ fitViewDone: nextFitViewDone });
2759
3082
  }
2760
3083
  else {
@@ -2857,8 +3180,10 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2857
3180
  const nodeChanges = nodesToUnselect.map((n) => {
2858
3181
  const internalNode = nodeLookup.get(n.id);
2859
3182
  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
3183
+ /*
3184
+ * we need to unselect the internal node that was selected previously before we
3185
+ * send the change to the user to prevent it to be selected while dragging the new node
3186
+ */
2862
3187
  internalNode.selected = false;
2863
3188
  }
2864
3189
  return createSelectionChange(n.id, false);
@@ -2926,8 +3251,10 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2926
3251
  maxZoom,
2927
3252
  }, options);
2928
3253
  },
2929
- // we can't call an asnychronous function in updateNodeInternals
2930
- // for that we created this sync version of fitView
3254
+ /*
3255
+ * we can't call an asnychronous function in updateNodeInternals
3256
+ * for that we created this sync version of fitView
3257
+ */
2931
3258
  fitViewSync: (options) => {
2932
3259
  const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
2933
3260
  if (!panZoom) {
@@ -2955,6 +3282,40 @@ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height,
2955
3282
  reset: () => set({ ...getInitialState() }),
2956
3283
  }), Object.is);
2957
3284
 
3285
+ /**
3286
+ * The `<ReactFlowProvider />` component is a [context provider](https://react.dev/learn/passing-data-deeply-with-context#)
3287
+ * that makes it possible to access a flow's internal state outside of the
3288
+ * [`<ReactFlow />`](/api-reference/react-flow) component. Many of the hooks we
3289
+ * provide rely on this component to work.
3290
+ * @public
3291
+ *
3292
+ * @example
3293
+ * ```tsx
3294
+ *import { ReactFlow, ReactFlowProvider, useNodes } from '@xyflow/react'
3295
+ *
3296
+ *export default function Flow() {
3297
+ * return (
3298
+ * <ReactFlowProvider>
3299
+ * <ReactFlow nodes={...} edges={...} />
3300
+ * <Sidebar />
3301
+ * </ReactFlowProvider>
3302
+ * );
3303
+ *}
3304
+ *
3305
+ *function Sidebar() {
3306
+ * // This hook will only work if the component it's used in is a child of a
3307
+ * // <ReactFlowProvider />.
3308
+ * const nodes = useNodes()
3309
+ *
3310
+ * return <aside>do something with nodes</aside>;
3311
+ *}
3312
+ *```
3313
+ *
3314
+ * @remarks If you're using a router and want your flow's state to persist across routes,
3315
+ * it's vital that you place the `<ReactFlowProvider />` component _outside_ of
3316
+ * your router. If you have multiple flows on the same page you will need to use a separate
3317
+ * `<ReactFlowProvider />` for each flow.
3318
+ */
2958
3319
  function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNodes, defaultEdges, initialWidth: width, initialHeight: height, fitView, nodeOrigin, nodeExtent, children, }) {
2959
3320
  const [store] = useState(() => createStore({
2960
3321
  nodes,
@@ -2973,8 +3334,10 @@ function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNo
2973
3334
  function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, nodeOrigin, nodeExtent, }) {
2974
3335
  const isWrapped = useContext(StoreContext);
2975
3336
  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
3337
+ /*
3338
+ * we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
3339
+ * https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
3340
+ */
2978
3341
  return jsx(Fragment, { children: children });
2979
3342
  }
2980
3343
  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 +3350,79 @@ const wrapperStyle = {
2987
3350
  position: 'relative',
2988
3351
  zIndex: 0,
2989
3352
  };
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) {
3353
+ 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
3354
  const rfId = id || '1';
2992
3355
  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 })] }) }));
3356
+ // Undo scroll events, preventing viewport from shifting when nodes outside of it are focused
3357
+ const wrapperOnScroll = useCallback((e) => {
3358
+ e.currentTarget.scrollTo({ top: 0, left: 0, behavior: 'instant' });
3359
+ onScroll?.(e);
3360
+ }, [onScroll]);
3361
+ 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
3362
  }
3363
+ /**
3364
+ * The `<ReactFlow />` component is the heart of your React Flow application.
3365
+ * It renders your nodes and edges and handles user interaction
3366
+ *
3367
+ * @public
3368
+ *
3369
+ * @example
3370
+ * ```tsx
3371
+ *import { ReactFlow } from '@xyflow/react'
3372
+ *
3373
+ *export default function Flow() {
3374
+ * return (<ReactFlow
3375
+ * nodes={...}
3376
+ * edges={...}
3377
+ * onNodesChange={...}
3378
+ * ...
3379
+ * />);
3380
+ *}
3381
+ *```
3382
+ */
2995
3383
  var index = fixedForwardRef(ReactFlow);
2996
3384
 
2997
3385
  const selector$6 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
3386
+ /**
3387
+ * Edges are SVG-based. If you want to render more complex labels you can use the
3388
+ * `<EdgeLabelRenderer />` component to access a div based renderer. This component
3389
+ * is a portal that renders the label in a `<div />` that is positioned on top of
3390
+ * the edges. You can see an example usage of the component in the [edge label renderer](/examples/edges/edge-label-renderer) example.
3391
+ * @public
3392
+ *
3393
+ * @example
3394
+ *```jsx
3395
+ *import React from 'react';
3396
+ *import { getBezierPath, EdgeLabelRenderer, BaseEdge } from '@xyflow/react';
3397
+ *
3398
+ *export function CustomEdge({ id, data, ...props }) {
3399
+ * const [edgePath, labelX, labelY] = getBezierPath(props);
3400
+ *
3401
+ * return (
3402
+ * <>
3403
+ * <BaseEdge id={id} path={edgePath} />
3404
+ * <EdgeLabelRenderer>
3405
+ * <div
3406
+ * style={{
3407
+ * position: 'absolute',
3408
+ * transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,
3409
+ * background: '#ffcc00',
3410
+ * padding: 10,
3411
+ * }}
3412
+ * className="nodrag nopan"
3413
+ * >
3414
+ * {data.label}
3415
+ * </div>
3416
+ * </EdgeLabelRenderer>
3417
+ * </>
3418
+ * );
3419
+ *};
3420
+ *```
3421
+ *
3422
+ * @remarks The `<EdgeLabelRenderer />` has no pointer events by default. If you want to
3423
+ * add mouse interactions you need to set the style `pointerEvents: all` and add
3424
+ * the `nopan` class on the label or the element you want to interact with.
3425
+ */
2998
3426
  function EdgeLabelRenderer({ children }) {
2999
3427
  const edgeLabelRenderer = useStore(selector$6);
3000
3428
  if (!edgeLabelRenderer) {
@@ -3004,6 +3432,31 @@ function EdgeLabelRenderer({ children }) {
3004
3432
  }
3005
3433
 
3006
3434
  const selector$5 = (s) => s.domNode?.querySelector('.react-flow__viewport-portal');
3435
+ /**
3436
+ * The `<ViewportPortal />` component can be used to add components to the same viewport
3437
+ * of the flow where nodes and edges are rendered. This is useful when you want to render
3438
+ * your own components that are adhere to the same coordinate system as the nodes & edges
3439
+ * and are also affected by zooming and panning
3440
+ * @public
3441
+ * @example
3442
+ *
3443
+ * ```jsx
3444
+ *import React from 'react';
3445
+ *import { ViewportPortal } from '@xyflow/react';
3446
+ *
3447
+ *export default function () {
3448
+ * return (
3449
+ * <ViewportPortal>
3450
+ * <div
3451
+ * style={{ transform: 'translate(100px, 100px)', position: 'absolute' }}
3452
+ * >
3453
+ * This div is positioned at [100, 100] on the flow.
3454
+ * </div>
3455
+ * </ViewportPortal>
3456
+ * );
3457
+ *}
3458
+ *```
3459
+ */
3007
3460
  function ViewportPortal({ children }) {
3008
3461
  const viewPortalDiv = useStore(selector$5);
3009
3462
  if (!viewPortalDiv) {
@@ -3013,10 +3466,48 @@ function ViewportPortal({ children }) {
3013
3466
  }
3014
3467
 
3015
3468
  /**
3016
- * Hook for updating node internals.
3469
+ * When you programmatically add or remove handles to a node or update a node's
3470
+ *handle position, you need to let React Flow know about it using this hook. This
3471
+ *will update the internal dimensions of the node and properly reposition handles
3472
+ *on the canvas if necessary.
3017
3473
  *
3018
3474
  * @public
3019
3475
  * @returns function for updating node internals
3476
+ *
3477
+ * @example
3478
+ * ```jsx
3479
+ *import { useCallback, useState } from 'react';
3480
+ *import { Handle, useUpdateNodeInternals } from '@xyflow/react';
3481
+ *
3482
+ *export default function RandomHandleNode({ id }) {
3483
+ * const updateNodeInternals = useUpdateNodeInternals();
3484
+ * const [handleCount, setHandleCount] = useState(0);
3485
+ * const randomizeHandleCount = useCallback(() => {
3486
+ * setHandleCount(Math.floor(Math.random() * 10));
3487
+ * updateNodeInternals(id);
3488
+ * }, [id, updateNodeInternals]);
3489
+ *
3490
+ * return (
3491
+ * <>
3492
+ * {Array.from({ length: handleCount }).map((_, index) => (
3493
+ * <Handle
3494
+ * key={index}
3495
+ * type="target"
3496
+ * position="left"
3497
+ * id={`handle-${index}`}
3498
+ * />
3499
+ * ))}
3500
+ *
3501
+ * <div>
3502
+ * <button onClick={randomizeHandleCount}>Randomize handle count</button>
3503
+ * <p>There are {handleCount} handles on this node.</p>
3504
+ * </div>
3505
+ * </>
3506
+ * );
3507
+ *}
3508
+ *```
3509
+ * @remarks This hook can only be used in a component that is a child of a
3510
+ *{@link ReactFlowProvider} or a {@link ReactFlow} component.
3020
3511
  */
3021
3512
  function useUpdateNodeInternals() {
3022
3513
  const store = useStoreApi();
@@ -3036,10 +3527,23 @@ function useUpdateNodeInternals() {
3036
3527
 
3037
3528
  const nodesSelector = (state) => state.nodes;
3038
3529
  /**
3039
- * Hook for getting the current nodes from the store.
3530
+ * This hook returns an array of the current nodes. Components that use this hook
3531
+ * will re-render **whenever any node changes**, including when a node is selected
3532
+ * or moved.
3040
3533
  *
3041
3534
  * @public
3042
3535
  * @returns An array of nodes
3536
+ *
3537
+ * @example
3538
+ * ```jsx
3539
+ *import { useNodes } from '@xyflow/react';
3540
+ *
3541
+ *export default function() {
3542
+ * const nodes = useNodes();
3543
+ *
3544
+ * return <div>There are currently {nodes.length} nodes!</div>;
3545
+ *}
3546
+ *```
3043
3547
  */
3044
3548
  function useNodes() {
3045
3549
  const nodes = useStore(nodesSelector, shallow);
@@ -3048,10 +3552,22 @@ function useNodes() {
3048
3552
 
3049
3553
  const edgesSelector = (state) => state.edges;
3050
3554
  /**
3051
- * Hook for getting the current edges from the store.
3555
+ * This hook returns an array of the current edges. Components that use this hook
3556
+ * will re-render **whenever any edge changes**.
3052
3557
  *
3053
3558
  * @public
3054
3559
  * @returns An array of edges
3560
+ *
3561
+ * @example
3562
+ * ```tsx
3563
+ *import { useEdges } from '@xyflow/react';
3564
+ *
3565
+ *export default function () {
3566
+ * const edges = useEdges();
3567
+ *
3568
+ * return <div>There are currently {edges.length} edges!</div>;
3569
+ *}
3570
+ *```
3055
3571
  */
3056
3572
  function useEdges() {
3057
3573
  const edges = useStore(edgesSelector, shallow);
@@ -3064,10 +3580,33 @@ const viewportSelector = (state) => ({
3064
3580
  zoom: state.transform[2],
3065
3581
  });
3066
3582
  /**
3067
- * Hook for getting the current viewport from the store.
3583
+ * The `useViewport` hook is a convenient way to read the current state of the
3584
+ *{@link Viewport} in a component. Components that use this hook
3585
+ *will re-render **whenever the viewport changes**.
3068
3586
  *
3069
3587
  * @public
3070
3588
  * @returns The current viewport
3589
+ *
3590
+ * @example
3591
+ *
3592
+ *```jsx
3593
+ *import { useViewport } from '@xyflow/react';
3594
+ *
3595
+ *export default function ViewportDisplay() {
3596
+ * const { x, y, zoom } = useViewport();
3597
+ *
3598
+ * return (
3599
+ * <div>
3600
+ * <p>
3601
+ * The viewport is currently at ({x}, {y}) and zoomed to {zoom}.
3602
+ * </p>
3603
+ * </div>
3604
+ * );
3605
+ *}
3606
+ *```
3607
+ *
3608
+ * @remarks This hook can only be used in a component that is a child of a
3609
+ *{@link ReactFlowProvider} or a {@link ReactFlow} component.
3071
3610
  */
3072
3611
  function useViewport() {
3073
3612
  const viewport = useStore(viewportSelector, shallow);
@@ -3075,11 +3614,41 @@ function useViewport() {
3075
3614
  }
3076
3615
 
3077
3616
  /**
3078
- * Hook for managing the state of nodes - should only be used for prototyping / simple use cases.
3617
+ * This hook makes it easy to prototype a controlled flow where you manage the
3618
+ * state of nodes and edges outside the `ReactFlowInstance`. You can think of it
3619
+ * like React's `useState` hook with an additional helper callback.
3079
3620
  *
3080
3621
  * @public
3081
3622
  * @param initialNodes
3082
3623
  * @returns an array [nodes, setNodes, onNodesChange]
3624
+ * @example
3625
+ *
3626
+ *```tsx
3627
+ *import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
3628
+ *
3629
+ *const initialNodes = [];
3630
+ *const initialEdges = [];
3631
+ *
3632
+ *export default function () {
3633
+ * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
3634
+ * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
3635
+ *
3636
+ * return (
3637
+ * <ReactFlow
3638
+ * nodes={nodes}
3639
+ * edges={edges}
3640
+ * onNodesChange={onNodesChange}
3641
+ * onEdgesChange={onEdgesChange}
3642
+ * />
3643
+ * );
3644
+ *}
3645
+ *```
3646
+ *
3647
+ * @remarks This hook was created to make prototyping easier and our documentation
3648
+ * examples clearer. Although it is OK to use this hook in production, in
3649
+ * practice you may want to use a more sophisticated state management solution
3650
+ * like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead.
3651
+ *
3083
3652
  */
3084
3653
  function useNodesState(initialNodes) {
3085
3654
  const [nodes, setNodes] = useState(initialNodes);
@@ -3087,11 +3656,41 @@ function useNodesState(initialNodes) {
3087
3656
  return [nodes, setNodes, onNodesChange];
3088
3657
  }
3089
3658
  /**
3090
- * Hook for managing the state of edges - should only be used for prototyping / simple use cases.
3659
+ * This hook makes it easy to prototype a controlled flow where you manage the
3660
+ * state of nodes and edges outside the `ReactFlowInstance`. You can think of it
3661
+ * like React's `useState` hook with an additional helper callback.
3091
3662
  *
3092
3663
  * @public
3093
3664
  * @param initialEdges
3094
3665
  * @returns an array [edges, setEdges, onEdgesChange]
3666
+ * @example
3667
+ *
3668
+ *```tsx
3669
+ *import { ReactFlow, useNodesState, useEdgesState } from '@xyflow/react';
3670
+ *
3671
+ *const initialNodes = [];
3672
+ *const initialEdges = [];
3673
+ *
3674
+ *export default function () {
3675
+ * const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
3676
+ * const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
3677
+ *
3678
+ * return (
3679
+ * <ReactFlow
3680
+ * nodes={nodes}
3681
+ * edges={edges}
3682
+ * onNodesChange={onNodesChange}
3683
+ * onEdgesChange={onEdgesChange}
3684
+ * />
3685
+ * );
3686
+ *}
3687
+ *```
3688
+ *
3689
+ * @remarks This hook was created to make prototyping easier and our documentation
3690
+ * examples clearer. Although it is OK to use this hook in production, in
3691
+ * practice you may want to use a more sophisticated state management solution
3692
+ * like Zustand {@link https://reactflow.dev/docs/guides/state-management/} instead.
3693
+ *
3095
3694
  */
3096
3695
  function useEdgesState(initialEdges) {
3097
3696
  const [edges, setEdges] = useState(initialEdges);
@@ -3100,12 +3699,30 @@ function useEdgesState(initialEdges) {
3100
3699
  }
3101
3700
 
3102
3701
  /**
3103
- * Hook for registering an onViewportChange handler.
3702
+ * The `useOnViewportChange` hook lets you listen for changes to the viewport such
3703
+ *as panning and zooming. You can provide a callback for each phase of a viewport
3704
+ *change: `onStart`, `onChange`, and `onEnd`.
3104
3705
  *
3105
3706
  * @public
3106
3707
  * @param params.onStart - gets called when the viewport starts changing
3107
3708
  * @param params.onChange - gets called when the viewport changes
3108
3709
  * @param params.onEnd - gets called when the viewport stops changing
3710
+ *
3711
+ * @example
3712
+ * ```jsx
3713
+ *import { useCallback } from 'react';
3714
+ *import { useOnViewportChange } from '@xyflow/react';
3715
+ *
3716
+ *function ViewportChangeLogger() {
3717
+ * useOnViewportChange({
3718
+ * onStart: (viewport: Viewport) => console.log('start', viewport),
3719
+ * onChange: (viewport: Viewport) => console.log('change', viewport),
3720
+ * onEnd: (viewport: Viewport) => console.log('end', viewport),
3721
+ * });
3722
+ *
3723
+ * return null;
3724
+ *}
3725
+ *```
3109
3726
  */
3110
3727
  function useOnViewportChange({ onStart, onChange, onEnd }) {
3111
3728
  const store = useStoreApi();
@@ -3121,10 +3738,42 @@ function useOnViewportChange({ onStart, onChange, onEnd }) {
3121
3738
  }
3122
3739
 
3123
3740
  /**
3124
- * Hook for registering an onSelectionChange handler.
3741
+ * This hook lets you listen for changes to both node and edge selection. As the
3742
+ *name implies, the callback you provide will be called whenever the selection of
3743
+ *_either_ nodes or edges changes.
3125
3744
  *
3126
3745
  * @public
3127
3746
  * @param params.onChange - The handler to register
3747
+ *
3748
+ * @example
3749
+ * ```jsx
3750
+ *import { useState } from 'react';
3751
+ *import { ReactFlow, useOnSelectionChange } from '@xyflow/react';
3752
+ *
3753
+ *function SelectionDisplay() {
3754
+ * const [selectedNodes, setSelectedNodes] = useState([]);
3755
+ * const [selectedEdges, setSelectedEdges] = useState([]);
3756
+ *
3757
+ * // the passed handler has to be memoized, otherwise the hook will not work correctly
3758
+ * const onChange = useCallback(({ nodes, edges }) => {
3759
+ * setSelectedNodes(nodes.map((node) => node.id));
3760
+ * setSelectedEdges(edges.map((edge) => edge.id));
3761
+ * }, []);
3762
+ *
3763
+ * useOnSelectionChange({
3764
+ * onChange,
3765
+ * });
3766
+ *
3767
+ * return (
3768
+ * <div>
3769
+ * <p>Selected nodes: {selectedNodes.join(', ')}</p>
3770
+ * <p>Selected edges: {selectedEdges.join(', ')}</p>
3771
+ * </div>
3772
+ * );
3773
+ *}
3774
+ *```
3775
+ *
3776
+ * @remarks You need to memoize the passed `onChange` handler, otherwise the hook will not work correctly.
3128
3777
  */
3129
3778
  function useOnSelectionChange({ onChange }) {
3130
3779
  const store = useStoreApi();
@@ -3151,17 +3800,42 @@ const selector$4 = (options) => (s) => {
3151
3800
  }
3152
3801
  return true;
3153
3802
  };
3154
- const defaultOptions = {
3155
- includeHiddenNodes: false,
3156
- };
3157
3803
  /**
3158
- * Hook which returns true when all nodes are initialized.
3804
+ * This hook tells you whether all the nodes in a flow have been measured and given
3805
+ *a width and height. When you add a node to the flow, this hook will return
3806
+ *`false` and then `true` again once the node has been measured.
3159
3807
  *
3160
3808
  * @public
3161
3809
  * @param options.includeHiddenNodes - defaults to false
3162
3810
  * @returns boolean indicating whether all nodes are initialized
3811
+ *
3812
+ * @example
3813
+ * ```jsx
3814
+ *import { useReactFlow, useNodesInitialized } from '@xyflow/react';
3815
+ *import { useEffect, useState } from 'react';
3816
+ *
3817
+ *const options = {
3818
+ * includeHiddenNodes: false,
3819
+ *};
3820
+ *
3821
+ *export default function useLayout() {
3822
+ * const { getNodes } = useReactFlow();
3823
+ * const nodesInitialized = useNodesInitialized(options);
3824
+ * const [layoutedNodes, setLayoutedNodes] = useState(getNodes());
3825
+ *
3826
+ * useEffect(() => {
3827
+ * if (nodesInitialized) {
3828
+ * setLayoutedNodes(yourLayoutingFunction(getNodes()));
3829
+ * }
3830
+ * }, [nodesInitialized]);
3831
+ *
3832
+ * return layoutedNodes;
3833
+ *}
3834
+ *```
3163
3835
  */
3164
- function useNodesInitialized(options = defaultOptions) {
3836
+ function useNodesInitialized(options = {
3837
+ includeHiddenNodes: false,
3838
+ }) {
3165
3839
  const initialized = useStore(selector$4(options));
3166
3840
  return initialized;
3167
3841
  }
@@ -3198,7 +3872,7 @@ function useHandleConnections({ type, id, nodeId, onConnect, onDisconnect, }) {
3198
3872
 
3199
3873
  const error014 = errorMessages['error014']();
3200
3874
  /**
3201
- * Hook to retrieve all edges connected to a node. Can be filtered by handle type and id.
3875
+ * This hook returns an array of connections on a specific node, handle type ('source', 'target') or handle ID.
3202
3876
  *
3203
3877
  * @public
3204
3878
  * @param param.id - node id - optional if called inside a custom node
@@ -3207,6 +3881,22 @@ const error014 = errorMessages['error014']();
3207
3881
  * @param param.onConnect - gets called when a connection is established
3208
3882
  * @param param.onDisconnect - gets called when a connection is removed
3209
3883
  * @returns an array with connections
3884
+ *
3885
+ * @example
3886
+ * ```jsx
3887
+ *import { useNodeConnections } from '@xyflow/react';
3888
+ *
3889
+ *export default function () {
3890
+ * const connections = useNodeConnections({
3891
+ * type: 'target',
3892
+ * handleId: 'my-handle',
3893
+ * });
3894
+ *
3895
+ * return (
3896
+ * <div>There are currently {connections.length} incoming connections!</div>
3897
+ * );
3898
+ *}
3899
+ *```
3210
3900
  */
3211
3901
  function useNodeConnections({ id, handleType, handleId, onConnect, onDisconnect, } = {}) {
3212
3902
  const nodeId = useNodeId();
@@ -3250,11 +3940,31 @@ function useNodesData(nodeIds) {
3250
3940
  }
3251
3941
 
3252
3942
  /**
3253
- * Hook for getting an internal node by id
3943
+ * This hook returns the internal representation of a specific node.
3944
+ * Components that use this hook will re-render **whenever the node changes**,
3945
+ * including when a node is selected or moved.
3254
3946
  *
3255
3947
  * @public
3256
3948
  * @param id - id of the node
3257
3949
  * @returns array with visible node ids
3950
+ *
3951
+ * @example
3952
+ * ```tsx
3953
+ *import { useInternalNode } from '@xyflow/react';
3954
+ *
3955
+ *export default function () {
3956
+ * const internalNode = useInternalNode('node-1');
3957
+ * const absolutePosition = internalNode.internals.positionAbsolute;
3958
+ *
3959
+ * return (
3960
+ * <div>
3961
+ * The absolute position of the node is at:
3962
+ * <p>x: {absolutePosition.x}</p>
3963
+ * <p>y: {absolutePosition.y}</p>
3964
+ * </div>
3965
+ * );
3966
+ *}
3967
+ *```
3258
3968
  */
3259
3969
  function useInternalNode(id) {
3260
3970
  const node = useStore(useCallback((s) => s.nodeLookup.get(id), [id]), shallow);
@@ -3268,6 +3978,12 @@ function DotPattern({ radius, className }) {
3268
3978
  return (jsx("circle", { cx: radius, cy: radius, r: radius, className: cc(['react-flow__background-pattern', 'dots', className]) }));
3269
3979
  }
3270
3980
 
3981
+ /**
3982
+ * The three variants are exported as an enum for convenience. You can either import
3983
+ * the enum and use it like `BackgroundVariant.Lines` or you can use the raw string
3984
+ * value directly.
3985
+ * @public
3986
+ */
3271
3987
  var BackgroundVariant;
3272
3988
  (function (BackgroundVariant) {
3273
3989
  BackgroundVariant["Lines"] = "lines";
@@ -3309,6 +4025,59 @@ size, lineWidth = 1, offset = 0, color, bgColor, style, className, patternClassN
3309
4025
  }, 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
4026
  }
3311
4027
  BackgroundComponent.displayName = 'Background';
4028
+ /**
4029
+ * 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.
4030
+ *
4031
+ * @example
4032
+ *
4033
+ * A simple example of how to use the Background component.
4034
+ *
4035
+ * ```tsx
4036
+ * import { useState } from 'react';
4037
+ * import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
4038
+ *
4039
+ * export default function Flow() {
4040
+ * return (
4041
+ * <ReactFlow defaultNodes={[...]} defaultEdges={[...]}>
4042
+ * <Background color="#ccc" variant={BackgroundVariant.Dots} />
4043
+ * </ReactFlow>
4044
+ * );
4045
+ * }
4046
+ * ```
4047
+ *
4048
+ * @example
4049
+ *
4050
+ * In this example you can see how to combine multiple backgrounds
4051
+ *
4052
+ * ```tsx
4053
+ * import { ReactFlow, Background, BackgroundVariant } from '@xyflow/react';
4054
+ * import '@xyflow/react/dist/style.css';
4055
+ *
4056
+ * export default function Flow() {
4057
+ * return (
4058
+ * <ReactFlow defaultNodes={[...]} defaultEdges={[...]}>
4059
+ * <Background
4060
+ * id="1"
4061
+ * gap={10}
4062
+ * color="#f1f1f1"
4063
+ * variant={BackgroundVariant.Lines}
4064
+ * />
4065
+ * <Background
4066
+ * id="2"
4067
+ * gap={100}
4068
+ * color="#ccc"
4069
+ * variant={BackgroundVariant.Lines}
4070
+ * />
4071
+ * </ReactFlow>
4072
+ * );
4073
+ * }
4074
+ * ```
4075
+ *
4076
+ * @remarks
4077
+ *
4078
+ * When combining multiple <Background /> components it’s important to give each of them a unique id prop!
4079
+ *
4080
+ */
3312
4081
  const Background = memo(BackgroundComponent);
3313
4082
 
3314
4083
  function PlusIcon() {
@@ -3331,6 +4100,29 @@ function UnlockIcon() {
3331
4100
  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
4101
  }
3333
4102
 
4103
+ /**
4104
+ * You can add buttons to the control panel by using the `<ControlButton />` component
4105
+ * and pass it as a child to the [`<Controls />`](/api-reference/components/controls) component.
4106
+ *
4107
+ * @public
4108
+ * @example
4109
+ *```jsx
4110
+ *import { MagicWand } from '@radix-ui/react-icons'
4111
+ *import { ReactFlow, Controls, ControlButton } from '@xyflow/react'
4112
+ *
4113
+ *export default function Flow() {
4114
+ * return (
4115
+ * <ReactFlow nodes={[...]} edges={[...]}>
4116
+ * <Controls>
4117
+ * <ControlButton onClick={() => alert('Something magical just happened. ✨')}>
4118
+ * <MagicWand />
4119
+ * </ControlButton>
4120
+ * </Controls>
4121
+ * </ReactFlow>
4122
+ * )
4123
+ *}
4124
+ *```
4125
+ */
3334
4126
  function ControlButton({ children, className, ...rest }) {
3335
4127
  return (jsx("button", { type: "button", className: cc(['react-flow__controls-button', className]), ...rest, children: children }));
3336
4128
  }
@@ -3368,6 +4160,27 @@ function ControlsComponent({ style, showZoom = true, showFitView = true, showInt
3368
4160
  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
4161
  }
3370
4162
  ControlsComponent.displayName = 'Controls';
4163
+ /**
4164
+ * The `<Controls />` component renders a small panel that contains convenient
4165
+ * buttons to zoom in, zoom out, fit the view, and lock the viewport.
4166
+ *
4167
+ * @public
4168
+ * @example
4169
+ *```tsx
4170
+ *import { ReactFlow, Controls } from '@xyflow/react'
4171
+ *
4172
+ *export default function Flow() {
4173
+ * return (
4174
+ * <ReactFlow nodes={[...]} edges={[...]}>
4175
+ * <Controls />
4176
+ * </ReactFlow>
4177
+ * )
4178
+ *}
4179
+ *```
4180
+ *
4181
+ * @remarks To extend or customise the controls, you can use the [`<ControlButton />`](/api-reference/components/control-button) component
4182
+ *
4183
+ */
3371
4184
  const Controls = memo(ControlsComponent);
3372
4185
 
3373
4186
  function MiniMapNodeComponent({ id, x, y, width, height, style, color, strokeColor, strokeWidth, className, borderRadius, shapeRendering, selected, onClick, }) {
@@ -3384,8 +4197,10 @@ const MiniMapNode = memo(MiniMapNodeComponent);
3384
4197
  const selectorNodeIds = (s) => s.nodes.map((node) => node.id);
3385
4198
  const getAttrFunction = (func) => func instanceof Function ? func : () => func;
3386
4199
  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.
4200
+ /*
4201
+ * We need to rename the prop to be `CapitalCase` so that JSX will render it as
4202
+ * a component properly.
4203
+ */
3389
4204
  nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
3390
4205
  const nodeIds = useStore(selectorNodeIds, shallow);
3391
4206
  const nodeColorFunc = getAttrFunction(nodeColor);
@@ -3393,11 +4208,13 @@ nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
3393
4208
  const nodeClassNameFunc = getAttrFunction(nodeClassName);
3394
4209
  const shapeRendering = typeof window === 'undefined' || !!window.chrome ? 'crispEdges' : 'geometricPrecision';
3395
4210
  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`.
4211
+ /*
4212
+ * The split of responsibilities between MiniMapNodes and
4213
+ * NodeComponentWrapper may appear weird. However, it’s designed to
4214
+ * minimize the cost of updates when individual nodes change.
4215
+ *
4216
+ * For more details, see a similar commit in `NodeRenderer/index.tsx`.
4217
+ */
3401
4218
  jsx(NodeComponentWrapper, { id: nodeId, nodeColorFunc: nodeColorFunc, nodeStrokeColorFunc: nodeStrokeColorFunc, nodeClassNameFunc: nodeClassNameFunc, nodeBorderRadius: nodeBorderRadius, nodeStrokeWidth: nodeStrokeWidth, NodeComponent: NodeComponent, onClick: onClick, shapeRendering: shapeRendering }, nodeId))) }));
3402
4219
  }
3403
4220
  function NodeComponentWrapperInner({ id, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
@@ -3442,8 +4259,10 @@ const selector$1 = (s) => {
3442
4259
  };
3443
4260
  const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
3444
4261
  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.
4262
+ /*
4263
+ * We need to rename the prop to be `CapitalCase` so that JSX will render it as
4264
+ * a component properly.
4265
+ */
3447
4266
  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
4267
  const store = useStoreApi();
3449
4268
  const svg = useRef(null);
@@ -3513,6 +4332,26 @@ nodeComponent, bgColor, maskColor, maskStrokeColor, maskStrokeWidth, position =
3513
4332
  M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd", pointerEvents: "none" })] }) }));
3514
4333
  }
3515
4334
  MiniMapComponent.displayName = 'MiniMap';
4335
+ /**
4336
+ * The `<MiniMap />` component can be used to render an overview of your flow. It
4337
+ * renders each node as an SVG element and visualizes where the current viewport is
4338
+ * in relation to the rest of the flow.
4339
+ *
4340
+ * @public
4341
+ * @example
4342
+ *
4343
+ * ```jsx
4344
+ *import { ReactFlow, MiniMap } from '@xyflow/react';
4345
+ *
4346
+ *export default function Flow() {
4347
+ * return (
4348
+ * <ReactFlow nodes={[...]]} edges={[...]]}>
4349
+ * <MiniMap nodeStrokeWidth={3} />
4350
+ * </ReactFlow>
4351
+ * );
4352
+ *}
4353
+ *```
4354
+ */
3516
4355
  const MiniMap = memo(MiniMapComponent);
3517
4356
 
3518
4357
  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, }) {
@@ -3565,8 +4404,10 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3565
4404
  };
3566
4405
  const parentExpandChanges = handleExpandParent([child], nodeLookup, parentLookup, nodeOrigin);
3567
4406
  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
4407
+ /*
4408
+ * when the parent was expanded by the child node, its position will be clamped at
4409
+ * 0,0 when node origin is 0,0 and to width, height if it's 1,1
4410
+ */
3570
4411
  nextPosition.x = change.x ? Math.max(origin[0] * width, change.x) : undefined;
3571
4412
  nextPosition.y = change.y ? Math.max(origin[1] * height, change.y) : undefined;
3572
4413
  }
@@ -3644,8 +4485,37 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3644
4485
  const controlStyle = color ? { ...style, [colorStyleProp]: color } : style;
3645
4486
  return (jsx("div", { className: cc(['react-flow__resize-control', 'nodrag', ...positionClassNames, variant, className]), ref: resizeControlRef, style: controlStyle, children: children }));
3646
4487
  }
4488
+ /**
4489
+ * To create your own resizing UI, you can use the `NodeResizeControl` component where you can pass children (such as icons).
4490
+ * @public
4491
+ *
4492
+ */
3647
4493
  const NodeResizeControl = memo(ResizeControl);
3648
4494
 
4495
+ /**
4496
+ * The `<NodeResizer />` component can be used to add a resize functionality to your
4497
+ * nodes. It renders draggable controls around the node to resize in all directions.
4498
+ * @public
4499
+ *
4500
+ * @example
4501
+ *```jsx
4502
+ *import { memo } from 'react';
4503
+ *import { Handle, Position, NodeResizer } from '@xyflow/react';
4504
+ *
4505
+ *function ResizableNode({ data }) {
4506
+ * return (
4507
+ * <>
4508
+ * <NodeResizer minWidth={100} minHeight={30} />
4509
+ * <Handle type="target" position={Position.Left} />
4510
+ * <div style={{ padding: 10 }}>{data.label}</div>
4511
+ * <Handle type="source" position={Position.Right} />
4512
+ * </>
4513
+ * );
4514
+ *};
4515
+ *
4516
+ *export default memo(ResizableNode);
4517
+ *```
4518
+ */
3649
4519
  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
4520
  if (!isVisible) {
3651
4521
  return null;
@@ -3685,6 +4555,41 @@ const storeSelector = (state) => ({
3685
4555
  zoom: state.transform[2],
3686
4556
  selectedNodesCount: state.nodes.filter((node) => node.selected).length,
3687
4557
  });
4558
+ /**
4559
+ * This component can render a toolbar or tooltip to one side of a custom node. This
4560
+ * toolbar doesn't scale with the viewport so that the content is always visible.
4561
+ *
4562
+ * @public
4563
+ * @example
4564
+ * ```jsx
4565
+ *import { memo } from 'react';
4566
+ *import { Handle, Position, NodeToolbar } from '@xyflow/react';
4567
+ *
4568
+ *function CustomNode({ data }) {
4569
+ * return (
4570
+ * <>
4571
+ * <NodeToolbar isVisible={data.toolbarVisible} position={data.toolbarPosition}>
4572
+ * <button>delete</button>
4573
+ * <button>copy</button>
4574
+ * <button>expand</button>
4575
+ * </NodeToolbar>
4576
+ *
4577
+ * <div style={{ padding: '10px 20px' }}>
4578
+ * {data.label}
4579
+ * </div>
4580
+ *
4581
+ * <Handle type="target" position={Position.Left} />
4582
+ * <Handle type="source" position={Position.Right} />
4583
+ * </>
4584
+ * );
4585
+ *};
4586
+ *
4587
+ *export default memo(CustomNode);
4588
+ *```
4589
+ * @remarks By default, the toolbar is only visible when a node is selected. If multiple
4590
+ * nodes are selected it will not be visible to prevent overlapping toolbars or
4591
+ * clutter. You can override this behavior by setting the `isVisible` prop to `true`.
4592
+ */
3688
4593
  function NodeToolbar({ nodeId, children, className, style, isVisible, position = Position.Top, offset = 10, align = 'center', ...rest }) {
3689
4594
  const contextNodeId = useNodeId();
3690
4595
  const nodesSelector = useCallback((state) => {
@@ -3703,7 +4608,7 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
3703
4608
  // if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
3704
4609
  const isActive = typeof isVisible === 'boolean'
3705
4610
  ? isVisible
3706
- : nodes.size === 1 && nodes.values().next().value.selected && selectedNodesCount === 1;
4611
+ : nodes.size === 1 && nodes.values().next().value?.selected && selectedNodesCount === 1;
3707
4612
  if (!isActive || !nodes.size) {
3708
4613
  return null;
3709
4614
  }