@xyflow/react 12.0.0-next.8 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (374) hide show
  1. package/dist/base.css +141 -9
  2. package/dist/esm/additional-components/Background/Background.d.ts.map +1 -1
  3. package/dist/esm/additional-components/Background/Patterns.d.ts.map +1 -1
  4. package/dist/esm/additional-components/Background/index.d.ts.map +1 -1
  5. package/dist/esm/additional-components/Background/types.d.ts.map +1 -1
  6. package/dist/esm/additional-components/Controls/ControlButton.d.ts.map +1 -1
  7. package/dist/esm/additional-components/Controls/Controls.d.ts +1 -1
  8. package/dist/esm/additional-components/Controls/Controls.d.ts.map +1 -1
  9. package/dist/esm/additional-components/Controls/Icons/FitView.d.ts.map +1 -1
  10. package/dist/esm/additional-components/Controls/Icons/Lock.d.ts.map +1 -1
  11. package/dist/esm/additional-components/Controls/Icons/Minus.d.ts.map +1 -1
  12. package/dist/esm/additional-components/Controls/Icons/Plus.d.ts.map +1 -1
  13. package/dist/esm/additional-components/Controls/Icons/Unlock.d.ts.map +1 -1
  14. package/dist/esm/additional-components/Controls/index.d.ts.map +1 -1
  15. package/dist/esm/additional-components/Controls/types.d.ts +1 -0
  16. package/dist/esm/additional-components/Controls/types.d.ts.map +1 -1
  17. package/dist/esm/additional-components/MiniMap/MiniMap.d.ts +3 -3
  18. package/dist/esm/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  19. package/dist/esm/additional-components/MiniMap/MiniMapNode.d.ts.map +1 -1
  20. package/dist/esm/additional-components/MiniMap/MiniMapNodes.d.ts +3 -3
  21. package/dist/esm/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  22. package/dist/esm/additional-components/MiniMap/index.d.ts.map +1 -1
  23. package/dist/esm/additional-components/MiniMap/types.d.ts +2 -0
  24. package/dist/esm/additional-components/MiniMap/types.d.ts.map +1 -1
  25. package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  26. package/dist/esm/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -1
  27. package/dist/esm/additional-components/NodeResizer/index.d.ts.map +1 -1
  28. package/dist/esm/additional-components/NodeResizer/types.d.ts.map +1 -1
  29. package/dist/esm/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -1
  30. package/dist/esm/additional-components/NodeToolbar/NodeToolbarPortal.d.ts.map +1 -1
  31. package/dist/esm/additional-components/NodeToolbar/index.d.ts.map +1 -1
  32. package/dist/esm/additional-components/NodeToolbar/types.d.ts.map +1 -1
  33. package/dist/esm/additional-components/index.d.ts.map +1 -1
  34. package/dist/esm/components/A11yDescriptions/index.d.ts.map +1 -1
  35. package/dist/esm/components/Attribution/index.d.ts.map +1 -1
  36. package/dist/esm/components/BatchProvider/index.d.ts +17 -0
  37. package/dist/esm/components/BatchProvider/index.d.ts.map +1 -0
  38. package/dist/esm/components/BatchProvider/types.d.ts +7 -0
  39. package/dist/esm/components/BatchProvider/types.d.ts.map +1 -0
  40. package/dist/esm/components/BatchProvider/useQueue.d.ts +11 -0
  41. package/dist/esm/components/BatchProvider/useQueue.d.ts.map +1 -0
  42. package/dist/esm/components/ConnectionLine/index.d.ts.map +1 -1
  43. package/dist/esm/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  44. package/dist/esm/components/EdgeWrapper/EdgeUpdateAnchors.d.ts +9 -9
  45. package/dist/esm/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -1
  46. package/dist/esm/components/EdgeWrapper/index.d.ts +2 -2
  47. package/dist/esm/components/EdgeWrapper/index.d.ts.map +1 -1
  48. package/dist/esm/components/EdgeWrapper/utils.d.ts.map +1 -1
  49. package/dist/esm/components/Edges/BaseEdge.d.ts.map +1 -1
  50. package/dist/esm/components/Edges/BezierEdge.d.ts.map +1 -1
  51. package/dist/esm/components/Edges/EdgeAnchor.d.ts.map +1 -1
  52. package/dist/esm/components/Edges/EdgeText.d.ts.map +1 -1
  53. package/dist/esm/components/Edges/SimpleBezierEdge.d.ts.map +1 -1
  54. package/dist/esm/components/Edges/SmoothStepEdge.d.ts.map +1 -1
  55. package/dist/esm/components/Edges/StepEdge.d.ts.map +1 -1
  56. package/dist/esm/components/Edges/StraightEdge.d.ts.map +1 -1
  57. package/dist/esm/components/Edges/index.d.ts.map +1 -1
  58. package/dist/esm/components/Handle/index.d.ts +7 -5
  59. package/dist/esm/components/Handle/index.d.ts.map +1 -1
  60. package/dist/esm/components/NodeWrapper/index.d.ts +2 -2
  61. package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
  62. package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts +15 -0
  63. package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts.map +1 -0
  64. package/dist/esm/components/NodeWrapper/utils.d.ts +5 -1
  65. package/dist/esm/components/NodeWrapper/utils.d.ts.map +1 -1
  66. package/dist/esm/components/Nodes/DefaultNode.d.ts +2 -2
  67. package/dist/esm/components/Nodes/DefaultNode.d.ts.map +1 -1
  68. package/dist/esm/components/Nodes/GroupNode.d.ts.map +1 -1
  69. package/dist/esm/components/Nodes/InputNode.d.ts +2 -2
  70. package/dist/esm/components/Nodes/InputNode.d.ts.map +1 -1
  71. package/dist/esm/components/Nodes/OutputNode.d.ts +2 -2
  72. package/dist/esm/components/Nodes/OutputNode.d.ts.map +1 -1
  73. package/dist/esm/components/Nodes/utils.d.ts.map +1 -1
  74. package/dist/esm/components/NodesSelection/index.d.ts +3 -3
  75. package/dist/esm/components/NodesSelection/index.d.ts.map +1 -1
  76. package/dist/esm/components/Panel/index.d.ts.map +1 -1
  77. package/dist/esm/components/ReactFlowProvider/index.d.ts +8 -3
  78. package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -1
  79. package/dist/esm/components/SelectionListener/index.d.ts.map +1 -1
  80. package/dist/esm/components/StoreUpdater/index.d.ts +4 -4
  81. package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
  82. package/dist/esm/components/UserSelection/index.d.ts.map +1 -1
  83. package/dist/esm/components/ViewportPortal/index.d.ts.map +1 -1
  84. package/dist/esm/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
  85. package/dist/esm/container/EdgeRenderer/MarkerSymbols.d.ts.map +1 -1
  86. package/dist/esm/container/EdgeRenderer/index.d.ts +4 -3
  87. package/dist/esm/container/EdgeRenderer/index.d.ts.map +1 -1
  88. package/dist/esm/container/FlowRenderer/index.d.ts +8 -5
  89. package/dist/esm/container/FlowRenderer/index.d.ts.map +1 -1
  90. package/dist/esm/container/GraphView/index.d.ts +4 -5
  91. package/dist/esm/container/GraphView/index.d.ts.map +1 -1
  92. package/dist/esm/container/GraphView/useNodeOrEdgeTypesWarning.d.ts +6 -0
  93. package/dist/esm/container/GraphView/useNodeOrEdgeTypesWarning.d.ts.map +1 -1
  94. package/dist/esm/container/GraphView/useStylesLoadedWarning.d.ts +2 -0
  95. package/dist/esm/container/GraphView/useStylesLoadedWarning.d.ts.map +1 -0
  96. package/dist/esm/container/NodeRenderer/index.d.ts +8 -6
  97. package/dist/esm/container/NodeRenderer/index.d.ts.map +1 -1
  98. package/dist/esm/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
  99. package/dist/esm/container/Pane/index.d.ts +2 -1
  100. package/dist/esm/container/Pane/index.d.ts.map +1 -1
  101. package/dist/esm/container/ReactFlow/Wrapper.d.ts +5 -1
  102. package/dist/esm/container/ReactFlow/Wrapper.d.ts.map +1 -1
  103. package/dist/esm/container/ReactFlow/index.d.ts +3 -5
  104. package/dist/esm/container/ReactFlow/index.d.ts.map +1 -1
  105. package/dist/esm/container/ReactFlow/init-values.d.ts +4 -0
  106. package/dist/esm/container/ReactFlow/init-values.d.ts.map +1 -0
  107. package/dist/esm/container/Viewport/index.d.ts.map +1 -1
  108. package/dist/esm/container/ZoomPane/index.d.ts +1 -1
  109. package/dist/esm/container/ZoomPane/index.d.ts.map +1 -1
  110. package/dist/esm/contexts/NodeIdContext.d.ts.map +1 -1
  111. package/dist/esm/contexts/{RFStoreContext.d.ts → StoreContext.d.ts} +1 -1
  112. package/dist/esm/contexts/StoreContext.d.ts.map +1 -0
  113. package/dist/esm/hooks/useColorModeClass.d.ts.map +1 -1
  114. package/dist/esm/hooks/useConnection.d.ts +4 -15
  115. package/dist/esm/hooks/useConnection.d.ts.map +1 -1
  116. package/dist/esm/hooks/useDrag.d.ts.map +1 -1
  117. package/dist/esm/hooks/useEdges.d.ts +1 -1
  118. package/dist/esm/hooks/useEdges.d.ts.map +1 -1
  119. package/dist/esm/hooks/useGlobalKeyHandler.d.ts.map +1 -1
  120. package/dist/esm/hooks/useHandleConnections.d.ts +3 -3
  121. package/dist/esm/hooks/useHandleConnections.d.ts.map +1 -1
  122. package/dist/esm/hooks/useInternalNode.d.ts +10 -0
  123. package/dist/esm/hooks/useInternalNode.d.ts.map +1 -0
  124. package/dist/esm/hooks/useIsomorphicLayoutEffect.d.ts +3 -0
  125. package/dist/esm/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -0
  126. package/dist/esm/hooks/useKeyPress.d.ts.map +1 -1
  127. package/dist/esm/hooks/useMoveSelectedNodes.d.ts +12 -0
  128. package/dist/esm/hooks/useMoveSelectedNodes.d.ts.map +1 -0
  129. package/dist/esm/hooks/useNodes.d.ts.map +1 -1
  130. package/dist/esm/hooks/useNodesData.d.ts +3 -4
  131. package/dist/esm/hooks/useNodesData.d.ts.map +1 -1
  132. package/dist/esm/hooks/useNodesEdgesState.d.ts +3 -3
  133. package/dist/esm/hooks/useNodesEdgesState.d.ts.map +1 -1
  134. package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -1
  135. package/dist/esm/hooks/useOnInitHandler.d.ts +2 -2
  136. package/dist/esm/hooks/useOnInitHandler.d.ts.map +1 -1
  137. package/dist/esm/hooks/useOnSelectionChange.d.ts.map +1 -1
  138. package/dist/esm/hooks/useOnViewportChange.d.ts.map +1 -1
  139. package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
  140. package/dist/esm/hooks/useResizeHandler.d.ts.map +1 -1
  141. package/dist/esm/hooks/useStore.d.ts +10 -11
  142. package/dist/esm/hooks/useStore.d.ts.map +1 -1
  143. package/dist/esm/hooks/useUpdateNodeInternals.d.ts.map +1 -1
  144. package/dist/esm/hooks/useViewport.d.ts.map +1 -1
  145. package/dist/esm/hooks/useViewportHelper.d.ts.map +1 -1
  146. package/dist/esm/hooks/useViewportSync.d.ts.map +1 -1
  147. package/dist/esm/hooks/useVisibleEdgeIds.d.ts.map +1 -1
  148. package/dist/esm/hooks/useVisibleNodeIds.d.ts.map +1 -1
  149. package/dist/esm/index.d.ts +5 -4
  150. package/dist/esm/index.d.ts.map +1 -1
  151. package/dist/esm/index.js +1095 -952
  152. package/dist/esm/index.mjs +1095 -952
  153. package/dist/esm/store/index.d.ts +8 -4
  154. package/dist/esm/store/index.d.ts.map +1 -1
  155. package/dist/esm/store/initialState.d.ts +7 -3
  156. package/dist/esm/store/initialState.d.ts.map +1 -1
  157. package/dist/esm/styles/utils.d.ts.map +1 -1
  158. package/dist/esm/types/component-props.d.ts +53 -41
  159. package/dist/esm/types/component-props.d.ts.map +1 -1
  160. package/dist/esm/types/edges.d.ts +33 -33
  161. package/dist/esm/types/edges.d.ts.map +1 -1
  162. package/dist/esm/types/general.d.ts +18 -16
  163. package/dist/esm/types/general.d.ts.map +1 -1
  164. package/dist/esm/types/index.d.ts +0 -1
  165. package/dist/esm/types/index.d.ts.map +1 -1
  166. package/dist/esm/types/instance.d.ts +63 -44
  167. package/dist/esm/types/instance.d.ts.map +1 -1
  168. package/dist/esm/types/nodes.d.ts +20 -10
  169. package/dist/esm/types/nodes.d.ts.map +1 -1
  170. package/dist/esm/types/store.d.ts +32 -29
  171. package/dist/esm/types/store.d.ts.map +1 -1
  172. package/dist/esm/utils/changes.d.ts +6 -6
  173. package/dist/esm/utils/changes.d.ts.map +1 -1
  174. package/dist/esm/utils/general.d.ts +4 -2
  175. package/dist/esm/utils/general.d.ts.map +1 -1
  176. package/dist/esm/utils/index.d.ts.map +1 -1
  177. package/dist/style.css +49 -12
  178. package/dist/umd/additional-components/Background/Background.d.ts.map +1 -1
  179. package/dist/umd/additional-components/Background/Patterns.d.ts.map +1 -1
  180. package/dist/umd/additional-components/Background/index.d.ts.map +1 -1
  181. package/dist/umd/additional-components/Background/types.d.ts.map +1 -1
  182. package/dist/umd/additional-components/Controls/ControlButton.d.ts.map +1 -1
  183. package/dist/umd/additional-components/Controls/Controls.d.ts +1 -1
  184. package/dist/umd/additional-components/Controls/Controls.d.ts.map +1 -1
  185. package/dist/umd/additional-components/Controls/Icons/FitView.d.ts.map +1 -1
  186. package/dist/umd/additional-components/Controls/Icons/Lock.d.ts.map +1 -1
  187. package/dist/umd/additional-components/Controls/Icons/Minus.d.ts.map +1 -1
  188. package/dist/umd/additional-components/Controls/Icons/Plus.d.ts.map +1 -1
  189. package/dist/umd/additional-components/Controls/Icons/Unlock.d.ts.map +1 -1
  190. package/dist/umd/additional-components/Controls/index.d.ts.map +1 -1
  191. package/dist/umd/additional-components/Controls/types.d.ts +1 -0
  192. package/dist/umd/additional-components/Controls/types.d.ts.map +1 -1
  193. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts +3 -3
  194. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  195. package/dist/umd/additional-components/MiniMap/MiniMapNode.d.ts.map +1 -1
  196. package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts +3 -3
  197. package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  198. package/dist/umd/additional-components/MiniMap/index.d.ts.map +1 -1
  199. package/dist/umd/additional-components/MiniMap/types.d.ts +2 -0
  200. package/dist/umd/additional-components/MiniMap/types.d.ts.map +1 -1
  201. package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  202. package/dist/umd/additional-components/NodeResizer/NodeResizer.d.ts.map +1 -1
  203. package/dist/umd/additional-components/NodeResizer/index.d.ts.map +1 -1
  204. package/dist/umd/additional-components/NodeResizer/types.d.ts.map +1 -1
  205. package/dist/umd/additional-components/NodeToolbar/NodeToolbar.d.ts.map +1 -1
  206. package/dist/umd/additional-components/NodeToolbar/NodeToolbarPortal.d.ts.map +1 -1
  207. package/dist/umd/additional-components/NodeToolbar/index.d.ts.map +1 -1
  208. package/dist/umd/additional-components/NodeToolbar/types.d.ts.map +1 -1
  209. package/dist/umd/additional-components/index.d.ts.map +1 -1
  210. package/dist/umd/components/A11yDescriptions/index.d.ts.map +1 -1
  211. package/dist/umd/components/Attribution/index.d.ts.map +1 -1
  212. package/dist/umd/components/BatchProvider/index.d.ts +17 -0
  213. package/dist/umd/components/BatchProvider/index.d.ts.map +1 -0
  214. package/dist/umd/components/BatchProvider/types.d.ts +7 -0
  215. package/dist/umd/components/BatchProvider/types.d.ts.map +1 -0
  216. package/dist/umd/components/BatchProvider/useQueue.d.ts +11 -0
  217. package/dist/umd/components/BatchProvider/useQueue.d.ts.map +1 -0
  218. package/dist/umd/components/ConnectionLine/index.d.ts.map +1 -1
  219. package/dist/umd/components/EdgeLabelRenderer/index.d.ts.map +1 -1
  220. package/dist/umd/components/EdgeWrapper/EdgeUpdateAnchors.d.ts +9 -9
  221. package/dist/umd/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -1
  222. package/dist/umd/components/EdgeWrapper/index.d.ts +2 -2
  223. package/dist/umd/components/EdgeWrapper/index.d.ts.map +1 -1
  224. package/dist/umd/components/EdgeWrapper/utils.d.ts.map +1 -1
  225. package/dist/umd/components/Edges/BaseEdge.d.ts.map +1 -1
  226. package/dist/umd/components/Edges/BezierEdge.d.ts.map +1 -1
  227. package/dist/umd/components/Edges/EdgeAnchor.d.ts.map +1 -1
  228. package/dist/umd/components/Edges/EdgeText.d.ts.map +1 -1
  229. package/dist/umd/components/Edges/SimpleBezierEdge.d.ts.map +1 -1
  230. package/dist/umd/components/Edges/SmoothStepEdge.d.ts.map +1 -1
  231. package/dist/umd/components/Edges/StepEdge.d.ts.map +1 -1
  232. package/dist/umd/components/Edges/StraightEdge.d.ts.map +1 -1
  233. package/dist/umd/components/Edges/index.d.ts.map +1 -1
  234. package/dist/umd/components/Handle/index.d.ts +7 -5
  235. package/dist/umd/components/Handle/index.d.ts.map +1 -1
  236. package/dist/umd/components/NodeWrapper/index.d.ts +2 -2
  237. package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
  238. package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts +15 -0
  239. package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts.map +1 -0
  240. package/dist/umd/components/NodeWrapper/utils.d.ts +5 -1
  241. package/dist/umd/components/NodeWrapper/utils.d.ts.map +1 -1
  242. package/dist/umd/components/Nodes/DefaultNode.d.ts +2 -2
  243. package/dist/umd/components/Nodes/DefaultNode.d.ts.map +1 -1
  244. package/dist/umd/components/Nodes/GroupNode.d.ts.map +1 -1
  245. package/dist/umd/components/Nodes/InputNode.d.ts +2 -2
  246. package/dist/umd/components/Nodes/InputNode.d.ts.map +1 -1
  247. package/dist/umd/components/Nodes/OutputNode.d.ts +2 -2
  248. package/dist/umd/components/Nodes/OutputNode.d.ts.map +1 -1
  249. package/dist/umd/components/Nodes/utils.d.ts.map +1 -1
  250. package/dist/umd/components/NodesSelection/index.d.ts +3 -3
  251. package/dist/umd/components/NodesSelection/index.d.ts.map +1 -1
  252. package/dist/umd/components/Panel/index.d.ts.map +1 -1
  253. package/dist/umd/components/ReactFlowProvider/index.d.ts +8 -3
  254. package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -1
  255. package/dist/umd/components/SelectionListener/index.d.ts.map +1 -1
  256. package/dist/umd/components/StoreUpdater/index.d.ts +4 -4
  257. package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
  258. package/dist/umd/components/UserSelection/index.d.ts.map +1 -1
  259. package/dist/umd/components/ViewportPortal/index.d.ts.map +1 -1
  260. package/dist/umd/container/EdgeRenderer/MarkerDefinitions.d.ts.map +1 -1
  261. package/dist/umd/container/EdgeRenderer/MarkerSymbols.d.ts.map +1 -1
  262. package/dist/umd/container/EdgeRenderer/index.d.ts +4 -3
  263. package/dist/umd/container/EdgeRenderer/index.d.ts.map +1 -1
  264. package/dist/umd/container/FlowRenderer/index.d.ts +8 -5
  265. package/dist/umd/container/FlowRenderer/index.d.ts.map +1 -1
  266. package/dist/umd/container/GraphView/index.d.ts +4 -5
  267. package/dist/umd/container/GraphView/index.d.ts.map +1 -1
  268. package/dist/umd/container/GraphView/useNodeOrEdgeTypesWarning.d.ts +6 -0
  269. package/dist/umd/container/GraphView/useNodeOrEdgeTypesWarning.d.ts.map +1 -1
  270. package/dist/umd/container/GraphView/useStylesLoadedWarning.d.ts +2 -0
  271. package/dist/umd/container/GraphView/useStylesLoadedWarning.d.ts.map +1 -0
  272. package/dist/umd/container/NodeRenderer/index.d.ts +8 -6
  273. package/dist/umd/container/NodeRenderer/index.d.ts.map +1 -1
  274. package/dist/umd/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
  275. package/dist/umd/container/Pane/index.d.ts +2 -1
  276. package/dist/umd/container/Pane/index.d.ts.map +1 -1
  277. package/dist/umd/container/ReactFlow/Wrapper.d.ts +5 -1
  278. package/dist/umd/container/ReactFlow/Wrapper.d.ts.map +1 -1
  279. package/dist/umd/container/ReactFlow/index.d.ts +3 -5
  280. package/dist/umd/container/ReactFlow/index.d.ts.map +1 -1
  281. package/dist/umd/container/ReactFlow/init-values.d.ts +4 -0
  282. package/dist/umd/container/ReactFlow/init-values.d.ts.map +1 -0
  283. package/dist/umd/container/Viewport/index.d.ts.map +1 -1
  284. package/dist/umd/container/ZoomPane/index.d.ts +1 -1
  285. package/dist/umd/container/ZoomPane/index.d.ts.map +1 -1
  286. package/dist/umd/contexts/NodeIdContext.d.ts.map +1 -1
  287. package/dist/umd/contexts/{RFStoreContext.d.ts → StoreContext.d.ts} +1 -1
  288. package/dist/umd/contexts/StoreContext.d.ts.map +1 -0
  289. package/dist/umd/hooks/useColorModeClass.d.ts.map +1 -1
  290. package/dist/umd/hooks/useConnection.d.ts +4 -15
  291. package/dist/umd/hooks/useConnection.d.ts.map +1 -1
  292. package/dist/umd/hooks/useDrag.d.ts.map +1 -1
  293. package/dist/umd/hooks/useEdges.d.ts +1 -1
  294. package/dist/umd/hooks/useEdges.d.ts.map +1 -1
  295. package/dist/umd/hooks/useGlobalKeyHandler.d.ts.map +1 -1
  296. package/dist/umd/hooks/useHandleConnections.d.ts +3 -3
  297. package/dist/umd/hooks/useHandleConnections.d.ts.map +1 -1
  298. package/dist/umd/hooks/useInternalNode.d.ts +10 -0
  299. package/dist/umd/hooks/useInternalNode.d.ts.map +1 -0
  300. package/dist/umd/hooks/useIsomorphicLayoutEffect.d.ts +3 -0
  301. package/dist/umd/hooks/useIsomorphicLayoutEffect.d.ts.map +1 -0
  302. package/dist/umd/hooks/useKeyPress.d.ts.map +1 -1
  303. package/dist/umd/hooks/useMoveSelectedNodes.d.ts +12 -0
  304. package/dist/umd/hooks/useMoveSelectedNodes.d.ts.map +1 -0
  305. package/dist/umd/hooks/useNodes.d.ts.map +1 -1
  306. package/dist/umd/hooks/useNodesData.d.ts +3 -4
  307. package/dist/umd/hooks/useNodesData.d.ts.map +1 -1
  308. package/dist/umd/hooks/useNodesEdgesState.d.ts +3 -3
  309. package/dist/umd/hooks/useNodesEdgesState.d.ts.map +1 -1
  310. package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
  311. package/dist/umd/hooks/useOnInitHandler.d.ts +2 -2
  312. package/dist/umd/hooks/useOnInitHandler.d.ts.map +1 -1
  313. package/dist/umd/hooks/useOnSelectionChange.d.ts.map +1 -1
  314. package/dist/umd/hooks/useOnViewportChange.d.ts.map +1 -1
  315. package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
  316. package/dist/umd/hooks/useResizeHandler.d.ts.map +1 -1
  317. package/dist/umd/hooks/useStore.d.ts +10 -11
  318. package/dist/umd/hooks/useStore.d.ts.map +1 -1
  319. package/dist/umd/hooks/useUpdateNodeInternals.d.ts.map +1 -1
  320. package/dist/umd/hooks/useViewport.d.ts.map +1 -1
  321. package/dist/umd/hooks/useViewportHelper.d.ts.map +1 -1
  322. package/dist/umd/hooks/useViewportSync.d.ts.map +1 -1
  323. package/dist/umd/hooks/useVisibleEdgeIds.d.ts.map +1 -1
  324. package/dist/umd/hooks/useVisibleNodeIds.d.ts.map +1 -1
  325. package/dist/umd/index.d.ts +5 -4
  326. package/dist/umd/index.d.ts.map +1 -1
  327. package/dist/umd/index.js +2 -2
  328. package/dist/umd/store/index.d.ts +8 -4
  329. package/dist/umd/store/index.d.ts.map +1 -1
  330. package/dist/umd/store/initialState.d.ts +7 -3
  331. package/dist/umd/store/initialState.d.ts.map +1 -1
  332. package/dist/umd/styles/utils.d.ts.map +1 -1
  333. package/dist/umd/types/component-props.d.ts +53 -41
  334. package/dist/umd/types/component-props.d.ts.map +1 -1
  335. package/dist/umd/types/edges.d.ts +33 -33
  336. package/dist/umd/types/edges.d.ts.map +1 -1
  337. package/dist/umd/types/general.d.ts +18 -16
  338. package/dist/umd/types/general.d.ts.map +1 -1
  339. package/dist/umd/types/index.d.ts +0 -1
  340. package/dist/umd/types/index.d.ts.map +1 -1
  341. package/dist/umd/types/instance.d.ts +63 -44
  342. package/dist/umd/types/instance.d.ts.map +1 -1
  343. package/dist/umd/types/nodes.d.ts +20 -10
  344. package/dist/umd/types/nodes.d.ts.map +1 -1
  345. package/dist/umd/types/store.d.ts +32 -29
  346. package/dist/umd/types/store.d.ts.map +1 -1
  347. package/dist/umd/utils/changes.d.ts +6 -6
  348. package/dist/umd/utils/changes.d.ts.map +1 -1
  349. package/dist/umd/utils/general.d.ts +4 -2
  350. package/dist/umd/utils/general.d.ts.map +1 -1
  351. package/dist/umd/utils/index.d.ts.map +1 -1
  352. package/package.json +2 -2
  353. package/dist/esm/additional-components/NodeResizer/ResizeControl.d.ts +0 -7
  354. package/dist/esm/additional-components/NodeResizer/ResizeControl.d.ts.map +0 -1
  355. package/dist/esm/additional-components/NodeResizer/utils.d.ts +0 -11
  356. package/dist/esm/additional-components/NodeResizer/utils.d.ts.map +0 -1
  357. package/dist/esm/contexts/RFStoreContext.d.ts.map +0 -1
  358. package/dist/esm/hooks/useUpdateNodePositions.d.ts +0 -12
  359. package/dist/esm/hooks/useUpdateNodePositions.d.ts.map +0 -1
  360. package/dist/esm/store/utils.d.ts +0 -12
  361. package/dist/esm/store/utils.d.ts.map +0 -1
  362. package/dist/esm/types/changes.d.ts +0 -51
  363. package/dist/esm/types/changes.d.ts.map +0 -1
  364. package/dist/umd/additional-components/NodeResizer/ResizeControl.d.ts +0 -7
  365. package/dist/umd/additional-components/NodeResizer/ResizeControl.d.ts.map +0 -1
  366. package/dist/umd/additional-components/NodeResizer/utils.d.ts +0 -11
  367. package/dist/umd/additional-components/NodeResizer/utils.d.ts.map +0 -1
  368. package/dist/umd/contexts/RFStoreContext.d.ts.map +0 -1
  369. package/dist/umd/hooks/useUpdateNodePositions.d.ts +0 -12
  370. package/dist/umd/hooks/useUpdateNodePositions.d.ts.map +0 -1
  371. package/dist/umd/store/utils.d.ts +0 -12
  372. package/dist/umd/store/utils.d.ts.map +0 -1
  373. package/dist/umd/types/changes.d.ts +0 -51
  374. package/dist/umd/types/changes.d.ts.map +0 -1
@@ -1,9 +1,9 @@
1
1
  "use client"
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
- import { createContext, useContext, useMemo, useEffect, useRef, useState, useCallback, forwardRef, memo } from 'react';
4
3
  import cc from 'classcat';
5
- import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, isMouseEvent, XYHandle, getHostForElement, addEdge, getNodesBounds, clampPosition, internalsSymbol, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionMode, ConnectionLineType, updateConnectionLookup, adoptUserProvidedNodes, devWarn, updateNodeDimensions, updateAbsolutePositions, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, getNodePositionWithOrigin, XYMinimap, getBoundsOfRects, ResizeControlVariant, XYResizer, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, getNodeToolbarTransform } from '@xyflow/system';
6
- export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, internalsSymbol, updateEdge } from '@xyflow/system';
4
+ import { errorMessages, infiniteExtent, isInputDOMNode, getFitViewNodes, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, evaluateAbsolutePosition, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getInternalNodesBounds, isNumeric, nodeHasDimensions, getNodeDimensions, clampPosition, 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
+ export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, reconnectEdge } from '@xyflow/system';
6
+ import { createContext, useContext, useMemo, useEffect, useRef, useState, forwardRef, 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';
@@ -19,6 +19,10 @@ const zustandErrorMessage = errorMessages['error001']();
19
19
  * @param selector
20
20
  * @param equalityFn
21
21
  * @returns The selected state slice
22
+ *
23
+ * @example
24
+ * const nodes = useStore((state: ReactFlowState<MyNodeType>) => state.nodes);
25
+ *
22
26
  */
23
27
  function useStore(selector, equalityFn) {
24
28
  const store = useContext(StoreContext);
@@ -27,7 +31,7 @@ function useStore(selector, equalityFn) {
27
31
  }
28
32
  return useStoreWithEqualityFn(store, selector, equalityFn);
29
33
  }
30
- const useStoreApi = () => {
34
+ function useStoreApi() {
31
35
  const store = useContext(StoreContext);
32
36
  if (store === null) {
33
37
  throw new Error(zustandErrorMessage);
@@ -36,9 +40,8 @@ const useStoreApi = () => {
36
40
  getState: store.getState,
37
41
  setState: store.setState,
38
42
  subscribe: store.subscribe,
39
- destroy: store.destroy,
40
43
  }), [store]);
41
- };
44
+ }
42
45
 
43
46
  const style = { display: 'none' };
44
47
  const ariaLiveStyle = {
@@ -55,18 +58,18 @@ const ariaLiveStyle = {
55
58
  const ARIA_NODE_DESC_KEY = 'react-flow__node-desc';
56
59
  const ARIA_EDGE_DESC_KEY = 'react-flow__edge-desc';
57
60
  const ARIA_LIVE_MESSAGE = 'react-flow__aria-live';
58
- const selector$q = (s) => s.ariaLiveMessage;
61
+ const selector$p = (s) => s.ariaLiveMessage;
59
62
  function AriaLiveMessage({ rfId }) {
60
- const ariaLiveMessage = useStore(selector$q);
63
+ const ariaLiveMessage = useStore(selector$p);
61
64
  return (jsx("div", { id: `${ARIA_LIVE_MESSAGE}-${rfId}`, "aria-live": "assertive", "aria-atomic": "true", style: ariaLiveStyle, children: ariaLiveMessage }));
62
65
  }
63
66
  function A11yDescriptions({ rfId, disableKeyboardA11y }) {
64
67
  return (jsxs(Fragment, { children: [jsxs("div", { id: `${ARIA_NODE_DESC_KEY}-${rfId}`, style: style, children: ["Press enter or space to select a node.", !disableKeyboardA11y && 'You can then use the arrow keys to move the node around.', " Press delete to remove it and escape to cancel.", ' '] }), jsx("div", { id: `${ARIA_EDGE_DESC_KEY}-${rfId}`, style: style, children: "Press enter or space to select an edge. You can then press delete to remove it or escape to cancel." }), !disableKeyboardA11y && jsx(AriaLiveMessage, { rfId: rfId })] }));
65
68
  }
66
69
 
67
- const selector$p = (s) => (s.userSelectionActive ? 'none' : 'all');
70
+ const selector$o = (s) => (s.userSelectionActive ? 'none' : 'all');
68
71
  function Panel({ position = 'top-left', children, className, style, ...rest }) {
69
- const pointerEvents = useStore(selector$p);
72
+ const pointerEvents = useStore(selector$o);
70
73
  const positionClasses = `${position}`.split('-');
71
74
  return (jsx("div", { className: cc(['react-flow__panel', className, ...positionClasses]), style: { ...style, pointerEvents }, ...rest, children: children }));
72
75
  }
@@ -78,10 +81,21 @@ function Attribution({ proOptions, position = 'bottom-right' }) {
78
81
  return (jsx(Panel, { position: position, className: "react-flow__attribution", "data-message": "Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev", children: jsx("a", { href: "https://reactflow.dev", target: "_blank", rel: "noopener noreferrer", "aria-label": "React Flow attribution", children: "React Flow" }) }));
79
82
  }
80
83
 
81
- const selector$o = (s) => ({
82
- selectedNodes: s.nodes.filter((n) => n.selected),
83
- selectedEdges: s.edges.filter((e) => e.selected),
84
- });
84
+ const selector$n = (s) => {
85
+ const selectedNodes = [];
86
+ const selectedEdges = [];
87
+ for (const [, node] of s.nodeLookup) {
88
+ if (node.selected) {
89
+ selectedNodes.push(node.internals.userNode);
90
+ }
91
+ }
92
+ for (const [, edge] of s.edgeLookup) {
93
+ if (edge.selected) {
94
+ selectedEdges.push(edge);
95
+ }
96
+ }
97
+ return { selectedNodes, selectedEdges };
98
+ };
85
99
  const selectId = (obj) => obj.id;
86
100
  function areEqual(a, b) {
87
101
  return (shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
@@ -89,7 +103,7 @@ function areEqual(a, b) {
89
103
  }
90
104
  function SelectionListenerInner({ onSelectionChange }) {
91
105
  const store = useStoreApi();
92
- const { selectedNodes, selectedEdges } = useStore(selector$o, areEqual);
106
+ const { selectedNodes, selectedEdges } = useStore(selector$n, areEqual);
93
107
  useEffect(() => {
94
108
  const params = { nodes: selectedNodes, edges: selectedEdges };
95
109
  onSelectionChange?.(params);
@@ -106,6 +120,9 @@ function SelectionListener({ onSelectionChange }) {
106
120
  return null;
107
121
  }
108
122
 
123
+ const defaultNodeOrigin = [0, 0];
124
+ const defaultViewport = { x: 0, y: 0, zoom: 1 };
125
+
109
126
  /*
110
127
  * This component helps us to update the store with the vlues coming from the user.
111
128
  * We distinguish between values we can update directly with `useDirectStoreUpdater` (like `snapGrid`)
@@ -126,7 +143,7 @@ const reactFlowFieldsToTrack = [
126
143
  'nodesConnectable',
127
144
  'nodesFocusable',
128
145
  'edgesFocusable',
129
- 'edgesUpdatable',
146
+ 'edgesReconnectable',
130
147
  'elevateNodesOnSelect',
131
148
  'elevateEdgesOnSelect',
132
149
  'minZoom',
@@ -165,41 +182,48 @@ const reactFlowFieldsToTrack = [
165
182
  'selectNodesOnDrag',
166
183
  'nodeDragThreshold',
167
184
  'onBeforeDelete',
185
+ 'debug',
186
+ 'autoPanSpeed',
187
+ 'paneClickDistance',
168
188
  ];
169
189
  // rfId doesn't exist in ReactFlowProps, but it's one of the fields we want to update
170
190
  const fieldsToTrack = [...reactFlowFieldsToTrack, 'rfId'];
171
- const selector$n = (s) => ({
191
+ const selector$m = (s) => ({
172
192
  setNodes: s.setNodes,
173
193
  setEdges: s.setEdges,
174
- setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
175
194
  setMinZoom: s.setMinZoom,
176
195
  setMaxZoom: s.setMaxZoom,
177
196
  setTranslateExtent: s.setTranslateExtent,
178
197
  setNodeExtent: s.setNodeExtent,
179
198
  reset: s.reset,
199
+ setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
200
+ setPaneClickDistance: s.setPaneClickDistance,
180
201
  });
202
+ 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.
206
+ translateExtent: infiniteExtent,
207
+ nodeOrigin: defaultNodeOrigin,
208
+ minZoom: 0.5,
209
+ maxZoom: 2,
210
+ elementsSelectable: true,
211
+ noPanClassName: 'nopan',
212
+ rfId: '1',
213
+ paneClickDistance: 0,
214
+ };
181
215
  function StoreUpdater(props) {
182
- const { setNodes, setEdges, setDefaultNodesAndEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset, } = useStore(selector$n, shallow);
216
+ const { setNodes, setEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset, setDefaultNodesAndEdges, setPaneClickDistance, } = useStore(selector$m, shallow);
183
217
  const store = useStoreApi();
184
218
  useEffect(() => {
185
- const edgesWithDefaults = props.defaultEdges?.map((e) => ({ ...e, ...props.defaultEdgeOptions }));
186
- setDefaultNodesAndEdges(props.defaultNodes, edgesWithDefaults);
219
+ setDefaultNodesAndEdges(props.defaultNodes, props.defaultEdges);
187
220
  return () => {
221
+ // when we reset the store we also need to reset the previous fields
222
+ previousFields.current = initPrevValues;
188
223
  reset();
189
224
  };
190
225
  }, []);
191
- const previousFields = useRef({
192
- // these are values that are also passed directly to other components
193
- // than the StoreUpdater. We can reduce the number of setStore calls
194
- // by setting the same values here as prev fields.
195
- translateExtent: infiniteExtent,
196
- nodeOrigin: initNodeOrigin,
197
- minZoom: 0.5,
198
- maxZoom: 2,
199
- elementsSelectable: true,
200
- noPanClassName: 'nopan',
201
- rfId: '1',
202
- });
226
+ const previousFields = useRef(initPrevValues);
203
227
  useEffect(() => {
204
228
  for (const fieldName of fieldsToTrack) {
205
229
  const fieldValue = props[fieldName];
@@ -221,6 +245,8 @@ function StoreUpdater(props) {
221
245
  setTranslateExtent(fieldValue);
222
246
  else if (fieldName === 'nodeExtent')
223
247
  setNodeExtent(fieldValue);
248
+ else if (fieldName === 'paneClickDistance')
249
+ setPaneClickDistance(fieldValue);
224
250
  // Renamed fields
225
251
  else if (fieldName === 'fitView')
226
252
  store.setState({ fitViewOnInit: fieldValue });
@@ -333,6 +359,10 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
333
359
  else {
334
360
  pressedKeys.current.delete(event[keyOrCode]);
335
361
  }
362
+ // fix for Mac: when cmd key is pressed, keyup is not triggered for any other key, see: https://stackoverflow.com/questions/27380018/when-cmd-key-is-kept-pressed-keyup-is-not-triggered-for-any-other-key
363
+ if (event.key === 'Meta') {
364
+ pressedKeys.current.clear();
365
+ }
336
366
  modifierPressed.current = false;
337
367
  };
338
368
  const resetHandler = () => {
@@ -342,10 +372,12 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
342
372
  target?.addEventListener('keydown', downHandler);
343
373
  target?.addEventListener('keyup', upHandler);
344
374
  window.addEventListener('blur', resetHandler);
375
+ window.addEventListener('contextmenu', resetHandler);
345
376
  return () => {
346
377
  target?.removeEventListener('keydown', downHandler);
347
378
  target?.removeEventListener('keyup', upHandler);
348
379
  window.removeEventListener('blur', resetHandler);
380
+ window.removeEventListener('contextmenu', resetHandler);
349
381
  };
350
382
  }
351
383
  }, [keyCode, setKeyPressed]);
@@ -366,7 +398,6 @@ function useKeyOrCode(eventCode, keysToWatch) {
366
398
  return keysToWatch.includes(eventCode) ? 'code' : 'key';
367
399
  }
368
400
 
369
- const selector$m = (s) => !!s.panZoom;
370
401
  /**
371
402
  * Hook for getting viewport helper functions.
372
403
  *
@@ -375,54 +406,74 @@ const selector$m = (s) => !!s.panZoom;
375
406
  */
376
407
  const useViewportHelper = () => {
377
408
  const store = useStoreApi();
378
- const panZoomInitialized = useStore(selector$m);
379
- const viewportHelperFunctions = useMemo(() => {
409
+ return useMemo(() => {
380
410
  return {
381
- zoomIn: (options) => store.getState().panZoom?.scaleBy(1.2, { duration: options?.duration }),
382
- zoomOut: (options) => store.getState().panZoom?.scaleBy(1 / 1.2, { duration: options?.duration }),
383
- zoomTo: (zoomLevel, options) => store.getState().panZoom?.scaleTo(zoomLevel, { duration: options?.duration }),
411
+ zoomIn: (options) => {
412
+ const { panZoom } = store.getState();
413
+ return panZoom ? panZoom.scaleBy(1.2, { duration: options?.duration }) : Promise.resolve(false);
414
+ },
415
+ zoomOut: (options) => {
416
+ const { panZoom } = store.getState();
417
+ return panZoom ? panZoom.scaleBy(1 / 1.2, { duration: options?.duration }) : Promise.resolve(false);
418
+ },
419
+ zoomTo: (zoomLevel, options) => {
420
+ const { panZoom } = store.getState();
421
+ return panZoom ? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(false);
422
+ },
384
423
  getZoom: () => store.getState().transform[2],
385
- setViewport: (viewport, options) => {
424
+ setViewport: async (viewport, options) => {
386
425
  const { transform: [tX, tY, tZoom], panZoom, } = store.getState();
387
- panZoom?.setViewport({
426
+ if (!panZoom) {
427
+ return Promise.resolve(false);
428
+ }
429
+ await panZoom.setViewport({
388
430
  x: viewport.x ?? tX,
389
431
  y: viewport.y ?? tY,
390
432
  zoom: viewport.zoom ?? tZoom,
391
433
  }, { duration: options?.duration });
434
+ return Promise.resolve(true);
392
435
  },
393
436
  getViewport: () => {
394
437
  const [x, y, zoom] = store.getState().transform;
395
438
  return { x, y, zoom };
396
439
  },
397
440
  fitView: (options) => {
398
- const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
441
+ const { nodeLookup, width, height, minZoom, maxZoom, panZoom } = store.getState();
442
+ const fitViewNodes = getFitViewNodes(nodeLookup, options);
399
443
  return panZoom
400
444
  ? fitView({
401
- nodes,
445
+ nodes: fitViewNodes,
402
446
  width,
403
447
  height,
404
- nodeOrigin,
405
448
  minZoom,
406
449
  maxZoom,
407
450
  panZoom,
408
451
  }, options)
409
- : false;
452
+ : Promise.resolve(false);
410
453
  },
411
- setCenter: (x, y, options) => {
454
+ setCenter: async (x, y, options) => {
412
455
  const { width, height, maxZoom, panZoom } = store.getState();
413
456
  const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
414
457
  const centerX = width / 2 - x * nextZoom;
415
458
  const centerY = height / 2 - y * nextZoom;
416
- panZoom?.setViewport({
459
+ if (!panZoom) {
460
+ return Promise.resolve(false);
461
+ }
462
+ await panZoom.setViewport({
417
463
  x: centerX,
418
464
  y: centerY,
419
465
  zoom: nextZoom,
420
466
  }, { duration: options?.duration });
467
+ return Promise.resolve(true);
421
468
  },
422
- fitBounds: (bounds, options) => {
469
+ fitBounds: async (bounds, options) => {
423
470
  const { width, height, minZoom, maxZoom, panZoom } = store.getState();
424
471
  const viewport = getViewportForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
425
- panZoom?.setViewport(viewport, { duration: options?.duration });
472
+ if (!panZoom) {
473
+ return Promise.resolve(false);
474
+ }
475
+ await panZoom.setViewport(viewport, { duration: options?.duration });
476
+ return Promise.resolve(true);
426
477
  },
427
478
  screenToFlowPosition: (clientPosition, options = { snapToGrid: true }) => {
428
479
  const { transform, snapGrid, domNode } = store.getState();
@@ -448,48 +499,10 @@ const useViewportHelper = () => {
448
499
  y: rendererPosition.y + domY,
449
500
  };
450
501
  },
451
- viewportInitialized: panZoomInitialized,
452
502
  };
453
- }, [panZoomInitialized]);
454
- return viewportHelperFunctions;
503
+ }, []);
455
504
  };
456
505
 
457
- function handleParentExpand(updatedElements, updateItem) {
458
- for (const [index, item] of updatedElements.entries()) {
459
- if (item.id === updateItem.parentNode) {
460
- const parent = { ...item };
461
- parent.computed ??= {};
462
- const extendWidth = updateItem.position.x + updateItem.computed.width - parent.computed.width;
463
- const extendHeight = updateItem.position.y + updateItem.computed.height - parent.computed.height;
464
- if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
465
- parent.width = parent.width ?? parent.computed.width;
466
- parent.height = parent.height ?? parent.computed.height;
467
- if (extendWidth > 0) {
468
- parent.width += extendWidth;
469
- }
470
- if (extendHeight > 0) {
471
- parent.height += extendHeight;
472
- }
473
- if (updateItem.position.x < 0) {
474
- const xDiff = Math.abs(updateItem.position.x);
475
- parent.position.x = parent.position.x - xDiff;
476
- parent.width += xDiff;
477
- updateItem.position.x = 0;
478
- }
479
- if (updateItem.position.y < 0) {
480
- const yDiff = Math.abs(updateItem.position.y);
481
- parent.position.y = parent.position.y - yDiff;
482
- parent.height += yDiff;
483
- updateItem.position.y = 0;
484
- }
485
- parent.computed.width = parent.width;
486
- parent.computed.height = parent.height;
487
- updatedElements[index] = parent;
488
- }
489
- break;
490
- }
491
- }
492
- }
493
506
  // This function applies changes to nodes or edges that are triggered by React Flow internally.
494
507
  // When you drag a node for example, React Flow will send a position change update.
495
508
  // This function then applies the changes and returns the updated elements.
@@ -541,14 +554,14 @@ function applyChanges(changes, elements) {
541
554
  /// each _mutate_ this object, so there's only ever one copy.
542
555
  const updatedElement = { ...element };
543
556
  for (const change of changes) {
544
- applyChange(change, updatedElement, updatedElements);
557
+ applyChange(change, updatedElement);
545
558
  }
546
559
  updatedElements.push(updatedElement);
547
560
  }
548
561
  return updatedElements;
549
562
  }
550
563
  // Applies a single change to an element. This is a *mutable* update.
551
- function applyChange(change, element, elements = []) {
564
+ function applyChange(change, element) {
552
565
  switch (change.type) {
553
566
  case 'select': {
554
567
  element.selected = change.selected;
@@ -558,24 +571,17 @@ function applyChange(change, element, elements = []) {
558
571
  if (typeof change.position !== 'undefined') {
559
572
  element.position = change.position;
560
573
  }
561
- if (typeof change.positionAbsolute !== 'undefined') {
562
- element.computed ??= {};
563
- element.computed.positionAbsolute = change.positionAbsolute;
564
- }
565
574
  if (typeof change.dragging !== 'undefined') {
566
575
  element.dragging = change.dragging;
567
576
  }
568
- if (element.expandParent) {
569
- handleParentExpand(elements, element);
570
- }
571
577
  break;
572
578
  }
573
579
  case 'dimensions': {
574
580
  if (typeof change.dimensions !== 'undefined') {
575
- element.computed ??= {};
576
- element.computed.width = change.dimensions.width;
577
- element.computed.height = change.dimensions.height;
578
- if (change.resizing) {
581
+ element.measured ??= {};
582
+ element.measured.width = change.dimensions.width;
583
+ element.measured.height = change.dimensions.height;
584
+ if (change.setAttributes) {
579
585
  element.width = change.dimensions.width;
580
586
  element.height = change.dimensions.height;
581
587
  }
@@ -583,9 +589,6 @@ function applyChange(change, element, elements = []) {
583
589
  if (typeof change.resizing === 'boolean') {
584
590
  element.resizing = change.resizing;
585
591
  }
586
- if (element.expandParent) {
587
- handleParentExpand(elements, element);
588
- }
589
592
  break;
590
593
  }
591
594
  }
@@ -636,15 +639,17 @@ function applyNodeChanges(changes, nodes) {
636
639
  function applyEdgeChanges(changes, edges) {
637
640
  return applyChanges(changes, edges);
638
641
  }
639
- const createSelectionChange = (id, selected) => ({
640
- id,
641
- type: 'select',
642
- selected,
643
- });
642
+ function createSelectionChange(id, selected) {
643
+ return {
644
+ id,
645
+ type: 'select',
646
+ selected,
647
+ };
648
+ }
644
649
  function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false) {
645
650
  const changes = [];
646
- for (const item of items) {
647
- const willBeSelected = selectedIds.has(item.id);
651
+ for (const [id, item] of items) {
652
+ const willBeSelected = selectedIds.has(id);
648
653
  // we don't want to set all items to selected=false on the first selection
649
654
  if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
650
655
  if (mutateItem) {
@@ -662,7 +667,8 @@ function getElementsDiffChanges({ items = [], lookup, }) {
662
667
  const changes = [];
663
668
  const itemsLookup = new Map(items.map((item) => [item.id, item]));
664
669
  for (const item of items) {
665
- const storeItem = lookup.get(item.id);
670
+ const lookupItem = lookup.get(item.id);
671
+ const storeItem = lookupItem?.internals?.userNode ?? lookupItem;
666
672
  if (storeItem !== undefined && storeItem !== item) {
667
673
  changes.push({ id: item.id, item: item, type: 'replace' });
668
674
  }
@@ -678,6 +684,12 @@ function getElementsDiffChanges({ items = [], lookup, }) {
678
684
  }
679
685
  return changes;
680
686
  }
687
+ function elementToRemoveChange(item) {
688
+ return {
689
+ id: item.id,
690
+ type: 'remove',
691
+ };
692
+ }
681
693
 
682
694
  /**
683
695
  * Test whether an object is useable as a Node
@@ -695,233 +707,293 @@ const isNode = (element) => isNodeBase(element);
695
707
  * @returns A boolean indicating whether the element is an Edge
696
708
  */
697
709
  const isEdge = (element) => isEdgeBase(element);
710
+ // eslint-disable-next-line @typescript-eslint/ban-types
711
+ function fixedForwardRef(render) {
712
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
713
+ return forwardRef(render);
714
+ }
715
+
716
+ // we need this hook to prevent a warning when using react-flow in SSR
717
+ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
698
718
 
699
719
  /**
700
- * Hook for accessing the ReactFlow instance.
720
+ * This hook returns a queue that can be used to batch updates.
701
721
  *
702
- * @public
703
- * @returns ReactFlowInstance
722
+ * @param runQueue - a function that gets called when the queue is flushed
723
+ * @internal
724
+ *
725
+ * @returns a Queue object
704
726
  */
705
- function useReactFlow() {
706
- const viewportHelper = useViewportHelper();
727
+ function useQueue(runQueue) {
728
+ // Because we're using a ref above, we need some way to let React know when to
729
+ // actually process the queue. We flip this bit of state to `true` any time we
730
+ // mutate the queue and then flip it back to `false` after flushing the queue.
731
+ const [shouldFlush, setShouldFlush] = useState(false);
732
+ // A reference of all the batched updates to process before the next render. We
733
+ // want a reference here so multiple synchronous calls to `setNodes` etc can be
734
+ // batched together.
735
+ const [queue] = useState(() => createQueue(() => setShouldFlush(true)));
736
+ // Layout effects are guaranteed to run before the next render which means we
737
+ // shouldn't run into any issues with stale state or weird issues that come from
738
+ // rendering things one frame later than expected (we used to use `setTimeout`).
739
+ useIsomorphicLayoutEffect(() => {
740
+ // Because we need to flip the state back to false after flushing, this should
741
+ // trigger the hook again (!). If the hook is being run again we know that any
742
+ // updates should have been processed by now and we can safely clear the queue
743
+ // and bail early.
744
+ if (!shouldFlush) {
745
+ queue.reset();
746
+ return;
747
+ }
748
+ const queueItems = queue.get();
749
+ if (queueItems.length) {
750
+ runQueue(queueItems);
751
+ queue.reset();
752
+ }
753
+ // Beacuse we're using reactive state to trigger this effect, we need to flip
754
+ // it back to false.
755
+ setShouldFlush(false);
756
+ }, [shouldFlush]);
757
+ return queue;
758
+ }
759
+ function createQueue(cb) {
760
+ let queue = [];
761
+ return {
762
+ get: () => queue,
763
+ reset: () => {
764
+ queue = [];
765
+ },
766
+ push: (item) => {
767
+ queue.push(item);
768
+ cb();
769
+ },
770
+ };
771
+ }
772
+
773
+ const BatchContext = createContext(null);
774
+ /**
775
+ * This is a context provider that holds and processes the node and edge update queues
776
+ * that are needed to handle setNodes, addNodes, setEdges and addEdges.
777
+ *
778
+ * @internal
779
+ */
780
+ function BatchProvider({ children, }) {
707
781
  const store = useStoreApi();
708
- const getNodes = useCallback(() => {
709
- return store.getState().nodes.map((n) => ({ ...n }));
710
- }, []);
711
- const getNode = useCallback((id) => {
712
- return store.getState().nodeLookup.get(id);
713
- }, []);
714
- const getEdges = useCallback(() => {
715
- const { edges = [] } = store.getState();
716
- return edges.map((e) => ({ ...e }));
717
- }, []);
718
- const getEdge = useCallback((id) => {
719
- const { edges = [] } = store.getState();
720
- return edges.find((e) => e.id === id);
721
- }, []);
722
- // this is used to handle multiple syncronous setNodes calls
723
- const setNodesData = useRef();
724
- const setNodesTimeout = useRef();
725
- const setNodes = useCallback((payload) => {
782
+ const nodeQueueHandler = useCallback((queueItems) => {
726
783
  const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState();
727
- const nextNodes = typeof payload === 'function' ? payload(setNodesData.current || nodes) : payload;
728
- setNodesData.current = nextNodes;
729
- if (setNodesTimeout.current) {
730
- clearTimeout(setNodesTimeout.current);
731
- }
732
- // if there are multiple synchronous setNodes calls, we only want to call onNodesChange once
733
- // for this, we use a timeout to wait for the last call and store updated nodes in setNodesData
734
- // this is not perfect, but should work in most cases
735
- setNodesTimeout.current = setTimeout(() => {
736
- if (hasDefaultNodes) {
737
- setNodes(nextNodes);
738
- }
739
- else if (onNodesChange) {
740
- const changes = getElementsDiffChanges({ items: setNodesData.current, lookup: nodeLookup });
741
- onNodesChange(changes);
742
- }
743
- setNodesData.current = undefined;
744
- }, 0);
745
- }, []);
746
- // this is used to handle multiple syncronous setEdges calls
747
- const setEdgesData = useRef();
748
- const setEdgesTimeout = useRef();
749
- const setEdges = useCallback((payload) => {
750
- const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
751
- const nextEdges = typeof payload === 'function' ? payload(setEdgesData.current || edges) : payload;
752
- setEdgesData.current = nextEdges;
753
- if (setEdgesTimeout.current) {
754
- clearTimeout(setEdgesTimeout.current);
784
+ // This is essentially an `Array.reduce` in imperative clothing. Processing
785
+ // this queue is a relatively hot path so we'd like to avoid the overhead of
786
+ // array methods where we can.
787
+ let next = nodes;
788
+ for (const payload of queueItems) {
789
+ next = typeof payload === 'function' ? payload(next) : payload;
755
790
  }
756
- setEdgesTimeout.current = setTimeout(() => {
757
- if (hasDefaultEdges) {
758
- setEdges(nextEdges);
759
- }
760
- else if (onEdgesChange) {
761
- const changes = getElementsDiffChanges({ items: nextEdges, lookup: edgeLookup });
762
- onEdgesChange(changes);
763
- }
764
- setEdgesData.current = undefined;
765
- }, 0);
766
- }, []);
767
- const addNodes = useCallback((payload) => {
768
- const nodes = Array.isArray(payload) ? payload : [payload];
769
- const { nodes: currentNodes, hasDefaultNodes, onNodesChange, setNodes } = store.getState();
770
791
  if (hasDefaultNodes) {
771
- const nextNodes = [...currentNodes, ...nodes];
772
- setNodes(nextNodes);
792
+ setNodes(next);
773
793
  }
774
794
  else if (onNodesChange) {
775
- const changes = nodes.map((node) => ({ item: node, type: 'add' }));
776
- onNodesChange(changes);
795
+ onNodesChange(getElementsDiffChanges({
796
+ items: next,
797
+ lookup: nodeLookup,
798
+ }));
777
799
  }
778
800
  }, []);
779
- const addEdges = useCallback((payload) => {
780
- const nextEdges = Array.isArray(payload) ? payload : [payload];
781
- const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
801
+ const nodeQueue = useQueue(nodeQueueHandler);
802
+ const edgeQueueHandler = useCallback((queueItems) => {
803
+ const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
804
+ let next = edges;
805
+ for (const payload of queueItems) {
806
+ next = typeof payload === 'function' ? payload(next) : payload;
807
+ }
782
808
  if (hasDefaultEdges) {
783
- setEdges([...edges, ...nextEdges]);
809
+ setEdges(next);
784
810
  }
785
811
  else if (onEdgesChange) {
786
- const changes = nextEdges.map((edge) => ({ item: edge, type: 'add' }));
787
- onEdgesChange(changes);
812
+ onEdgesChange(getElementsDiffChanges({
813
+ items: next,
814
+ lookup: edgeLookup,
815
+ }));
788
816
  }
789
817
  }, []);
790
- const toObject = useCallback(() => {
791
- const { nodes = [], edges = [], transform } = store.getState();
792
- const [x, y, zoom] = transform;
818
+ const edgeQueue = useQueue(edgeQueueHandler);
819
+ const value = useMemo(() => ({ nodeQueue, edgeQueue }), []);
820
+ return jsx(BatchContext.Provider, { value: value, children: children });
821
+ }
822
+ function useBatchContext() {
823
+ const batchContext = useContext(BatchContext);
824
+ if (!batchContext) {
825
+ throw new Error('useBatchContext must be used within a BatchProvider');
826
+ }
827
+ return batchContext;
828
+ }
829
+
830
+ const selector$l = (s) => !!s.panZoom;
831
+ /**
832
+ * Hook for accessing the ReactFlow instance.
833
+ *
834
+ * @public
835
+ * @returns ReactFlowInstance
836
+ */
837
+ function useReactFlow() {
838
+ const viewportHelper = useViewportHelper();
839
+ const store = useStoreApi();
840
+ const batchContext = useBatchContext();
841
+ const viewportInitialized = useStore(selector$l);
842
+ const generalHelper = useMemo(() => {
843
+ const getInternalNode = (id) => store.getState().nodeLookup.get(id);
844
+ const setNodes = (payload) => {
845
+ batchContext.nodeQueue.push(payload);
846
+ };
847
+ const setEdges = (payload) => {
848
+ batchContext.edgeQueue.push(payload);
849
+ };
850
+ const getNodeRect = (node) => {
851
+ const { nodeLookup, nodeOrigin } = store.getState();
852
+ const nodeToUse = isNode(node) ? node : nodeLookup.get(node.id);
853
+ const position = nodeToUse.parentId
854
+ ? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.measured, nodeToUse.parentId, nodeLookup, nodeOrigin)
855
+ : nodeToUse.position;
856
+ const nodeWithPosition = {
857
+ id: nodeToUse.id,
858
+ position,
859
+ width: nodeToUse.measured?.width ?? nodeToUse.width,
860
+ height: nodeToUse.measured?.height ?? nodeToUse.height,
861
+ data: nodeToUse.data,
862
+ };
863
+ return nodeToRect(nodeWithPosition);
864
+ };
865
+ const updateNode = (id, nodeUpdate, options = { replace: false }) => {
866
+ setNodes((prevNodes) => prevNodes.map((node) => {
867
+ if (node.id === id) {
868
+ const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate;
869
+ return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
870
+ }
871
+ return node;
872
+ }));
873
+ };
874
+ const updateEdge = (id, edgeUpdate, options = { replace: false }) => {
875
+ setEdges((prevEdges) => prevEdges.map((edge) => {
876
+ if (edge.id === id) {
877
+ const nextEdge = typeof edgeUpdate === 'function' ? edgeUpdate(edge) : edgeUpdate;
878
+ return options.replace && isEdge(nextEdge) ? nextEdge : { ...edge, ...nextEdge };
879
+ }
880
+ return edge;
881
+ }));
882
+ };
793
883
  return {
794
- nodes: nodes.map((n) => ({ ...n })),
795
- edges: edges.map((e) => ({ ...e })),
796
- viewport: {
797
- x,
798
- y,
799
- zoom,
884
+ getNodes: () => store.getState().nodes.map((n) => ({ ...n })),
885
+ getNode: (id) => getInternalNode(id)?.internals.userNode,
886
+ getInternalNode,
887
+ getEdges: () => {
888
+ const { edges = [] } = store.getState();
889
+ return edges.map((e) => ({ ...e }));
890
+ },
891
+ getEdge: (id) => store.getState().edgeLookup.get(id),
892
+ setNodes,
893
+ setEdges,
894
+ addNodes: (payload) => {
895
+ const newNodes = Array.isArray(payload) ? payload : [payload];
896
+ batchContext.nodeQueue.push((nodes) => [...nodes, ...newNodes]);
897
+ },
898
+ addEdges: (payload) => {
899
+ const newEdges = Array.isArray(payload) ? payload : [payload];
900
+ batchContext.edgeQueue.push((edges) => [...edges, ...newEdges]);
901
+ },
902
+ toObject: () => {
903
+ const { nodes = [], edges = [], transform } = store.getState();
904
+ const [x, y, zoom] = transform;
905
+ return {
906
+ nodes: nodes.map((n) => ({ ...n })),
907
+ edges: edges.map((e) => ({ ...e })),
908
+ viewport: {
909
+ x,
910
+ y,
911
+ zoom,
912
+ },
913
+ };
914
+ },
915
+ deleteElements: async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
916
+ const { nodes, edges, onNodesDelete, onEdgesDelete, triggerNodeChanges, triggerEdgeChanges, onDelete, onBeforeDelete, } = store.getState();
917
+ const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
918
+ nodesToRemove,
919
+ edgesToRemove,
920
+ nodes,
921
+ edges,
922
+ onBeforeDelete,
923
+ });
924
+ const hasMatchingEdges = matchingEdges.length > 0;
925
+ const hasMatchingNodes = matchingNodes.length > 0;
926
+ if (hasMatchingEdges) {
927
+ const edgeChanges = matchingEdges.map(elementToRemoveChange);
928
+ onEdgesDelete?.(matchingEdges);
929
+ triggerEdgeChanges(edgeChanges);
930
+ }
931
+ if (hasMatchingNodes) {
932
+ const nodeChanges = matchingNodes.map(elementToRemoveChange);
933
+ onNodesDelete?.(matchingNodes);
934
+ triggerNodeChanges(nodeChanges);
935
+ }
936
+ if (hasMatchingNodes || hasMatchingEdges) {
937
+ onDelete?.({ nodes: matchingNodes, edges: matchingEdges });
938
+ }
939
+ return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
940
+ },
941
+ getIntersectingNodes: (nodeOrRect, partially = true, nodes) => {
942
+ const isRect = isRectObject(nodeOrRect);
943
+ const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
944
+ const hasNodesOption = nodes !== undefined;
945
+ if (!nodeRect) {
946
+ return [];
947
+ }
948
+ return (nodes || store.getState().nodes).filter((n) => {
949
+ const internalNode = store.getState().nodeLookup.get(n.id);
950
+ if (internalNode && !isRect && (n.id === nodeOrRect.id || !internalNode.internals.positionAbsolute)) {
951
+ return false;
952
+ }
953
+ const currNodeRect = nodeToRect(hasNodesOption ? n : internalNode);
954
+ const overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
955
+ const partiallyVisible = partially && overlappingArea > 0;
956
+ return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
957
+ });
958
+ },
959
+ isNodeIntersecting: (nodeOrRect, area, partially = true) => {
960
+ const isRect = isRectObject(nodeOrRect);
961
+ const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
962
+ if (!nodeRect) {
963
+ return false;
964
+ }
965
+ const overlappingArea = getOverlappingArea(nodeRect, area);
966
+ const partiallyVisible = partially && overlappingArea > 0;
967
+ return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
968
+ },
969
+ updateNode,
970
+ updateNodeData: (id, dataUpdate, options = { replace: false }) => {
971
+ updateNode(id, (node) => {
972
+ const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate;
973
+ return options.replace ? { ...node, data: nextData } : { ...node, data: { ...node.data, ...nextData } };
974
+ }, options);
975
+ },
976
+ updateEdge,
977
+ updateEdgeData: (id, dataUpdate, options = { replace: false }) => {
978
+ updateEdge(id, (edge) => {
979
+ const nextData = typeof dataUpdate === 'function' ? dataUpdate(edge) : dataUpdate;
980
+ return options.replace ? { ...edge, data: nextData } : { ...edge, data: { ...edge.data, ...nextData } };
981
+ }, options);
800
982
  },
801
983
  };
802
984
  }, []);
803
- const deleteElements = useCallback(async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
804
- const { nodes, edges, hasDefaultNodes, hasDefaultEdges, onNodesDelete, onEdgesDelete, onNodesChange, onEdgesChange, onDelete, onBeforeDelete, } = store.getState();
805
- const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
806
- nodesToRemove,
807
- edgesToRemove,
808
- nodes,
809
- edges,
810
- onBeforeDelete,
811
- });
812
- const hasMatchingEdges = matchingEdges.length > 0;
813
- const hasMatchingNodes = matchingNodes.length > 0;
814
- if (hasMatchingEdges) {
815
- if (hasDefaultEdges) {
816
- const nextEdges = edges.filter((e) => !matchingEdges.some((mE) => mE.id === e.id));
817
- store.getState().setEdges(nextEdges);
818
- }
819
- onEdgesDelete?.(matchingEdges);
820
- onEdgesChange?.(matchingEdges.map((edge) => ({
821
- id: edge.id,
822
- type: 'remove',
823
- })));
824
- }
825
- if (hasMatchingNodes) {
826
- if (hasDefaultNodes) {
827
- const nextNodes = nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id));
828
- store.getState().setNodes(nextNodes);
829
- }
830
- onNodesDelete?.(matchingNodes);
831
- onNodesChange?.(matchingNodes.map((node) => ({ id: node.id, type: 'remove' })));
832
- }
833
- if (hasMatchingNodes || hasMatchingEdges) {
834
- onDelete?.({ nodes: matchingNodes, edges: matchingEdges });
835
- }
836
- return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
837
- }, []);
838
- const getNodeRect = useCallback((nodeOrRect) => {
839
- const isRect = isRectObject(nodeOrRect);
840
- const node = isRect ? null : store.getState().nodeLookup.get(nodeOrRect.id);
841
- if (!isRect && !node) {
842
- return [null, null, isRect];
843
- }
844
- const nodeRect = isRect ? nodeOrRect : nodeToRect(node);
845
- return [nodeRect, node, isRect];
846
- }, []);
847
- const getIntersectingNodes = useCallback((nodeOrRect, partially = true, nodes) => {
848
- const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
849
- if (!nodeRect) {
850
- return [];
851
- }
852
- return (nodes || store.getState().nodes).filter((n) => {
853
- if (!isRect && (n.id === node.id || !n.computed?.positionAbsolute)) {
854
- return false;
855
- }
856
- const currNodeRect = nodeToRect(n);
857
- const overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
858
- const partiallyVisible = partially && overlappingArea > 0;
859
- return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
860
- });
861
- }, []);
862
- const isNodeIntersecting = useCallback((nodeOrRect, area, partially = true) => {
863
- const [nodeRect] = getNodeRect(nodeOrRect);
864
- if (!nodeRect) {
865
- return false;
866
- }
867
- const overlappingArea = getOverlappingArea(nodeRect, area);
868
- const partiallyVisible = partially && overlappingArea > 0;
869
- return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
870
- }, []);
871
- const updateNode = useCallback((id, nodeUpdate, options = { replace: true }) => {
872
- setNodes((prevNodes) => prevNodes.map((node) => {
873
- if (node.id === id) {
874
- const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate;
875
- return options.replace && isNode(nextNode) ? nextNode : { ...node, ...nextNode };
876
- }
877
- return node;
878
- }));
879
- }, [setNodes]);
880
- const updateNodeData = useCallback((id, dataUpdate, options = { replace: false }) => {
881
- updateNode(id, (node) => {
882
- const nextData = typeof dataUpdate === 'function' ? dataUpdate(node) : dataUpdate;
883
- return options.replace ? { ...node, data: nextData } : { ...node, data: { ...node.data, ...nextData } };
884
- }, options);
885
- }, [updateNode]);
886
985
  return useMemo(() => {
887
986
  return {
987
+ ...generalHelper,
888
988
  ...viewportHelper,
889
- getNodes,
890
- getNode,
891
- getEdges,
892
- getEdge,
893
- setNodes,
894
- setEdges,
895
- addNodes,
896
- addEdges,
897
- toObject,
898
- deleteElements,
899
- getIntersectingNodes,
900
- isNodeIntersecting,
901
- updateNode,
902
- updateNodeData,
989
+ viewportInitialized,
903
990
  };
904
- }, [
905
- viewportHelper,
906
- getNodes,
907
- getNode,
908
- getEdges,
909
- getEdge,
910
- setNodes,
911
- setEdges,
912
- addNodes,
913
- addEdges,
914
- toObject,
915
- deleteElements,
916
- getIntersectingNodes,
917
- isNodeIntersecting,
918
- updateNode,
919
- updateNodeData,
920
- ]);
991
+ }, [viewportInitialized]);
921
992
  }
922
993
 
923
994
  const selected = (item) => item.selected;
924
995
  const deleteKeyOptions = { actInsideInputWithModifier: false };
996
+ const win$1 = typeof window !== 'undefined' ? window : undefined;
925
997
  /**
926
998
  * Hook for handling global key events.
927
999
  *
@@ -931,7 +1003,7 @@ function useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode, }) {
931
1003
  const store = useStoreApi();
932
1004
  const { deleteElements } = useReactFlow();
933
1005
  const deleteKeyPressed = useKeyPress(deleteKeyCode, deleteKeyOptions);
934
- const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
1006
+ const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode, { target: win$1 });
935
1007
  useEffect(() => {
936
1008
  if (deleteKeyPressed) {
937
1009
  const { edges, nodes } = store.getState();
@@ -985,14 +1057,14 @@ const containerStyle = {
985
1057
  left: 0,
986
1058
  };
987
1059
 
988
- const selector$l = (s) => ({
1060
+ const selector$k = (s) => ({
989
1061
  userSelectionActive: s.userSelectionActive,
990
1062
  lib: s.lib,
991
1063
  });
992
- function ZoomPane({ onPaneContextMenu, zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, panOnScrollSpeed = 0.5, panOnScrollMode = PanOnScrollMode.Free, zoomOnDoubleClick = true, panOnDrag = true, defaultViewport, translateExtent, minZoom, maxZoom, zoomActivationKeyCode, preventScrolling = true, children, noWheelClassName, noPanClassName, onViewportChange, isControlledViewport, }) {
1064
+ function ZoomPane({ onPaneContextMenu, zoomOnScroll = true, zoomOnPinch = true, panOnScroll = false, panOnScrollSpeed = 0.5, panOnScrollMode = PanOnScrollMode.Free, zoomOnDoubleClick = true, panOnDrag = true, defaultViewport, translateExtent, minZoom, maxZoom, zoomActivationKeyCode, preventScrolling = true, children, noWheelClassName, noPanClassName, onViewportChange, isControlledViewport, paneClickDistance, }) {
993
1065
  const store = useStoreApi();
994
1066
  const zoomPane = useRef(null);
995
- const { userSelectionActive, lib } = useStore(selector$l, shallow);
1067
+ const { userSelectionActive, lib } = useStore(selector$k, shallow);
996
1068
  const zoomActivationKeyPressed = useKeyPress(zoomActivationKeyCode);
997
1069
  const panZoom = useRef();
998
1070
  useResizeHandler(zoomPane);
@@ -1004,6 +1076,7 @@ function ZoomPane({ onPaneContextMenu, zoomOnScroll = true, zoomOnPinch = true,
1004
1076
  maxZoom,
1005
1077
  translateExtent,
1006
1078
  viewport: defaultViewport,
1079
+ paneClickDistance,
1007
1080
  onTransformChange: (transform) => {
1008
1081
  onViewportChange?.({ x: transform[0], y: transform[1], zoom: transform[2] });
1009
1082
  if (!isControlledViewport) {
@@ -1074,12 +1147,12 @@ function ZoomPane({ onPaneContextMenu, zoomOnScroll = true, zoomOnPinch = true,
1074
1147
  return (jsx("div", { className: "react-flow__renderer", ref: zoomPane, style: containerStyle, children: children }));
1075
1148
  }
1076
1149
 
1077
- const selector$k = (s) => ({
1150
+ const selector$j = (s) => ({
1078
1151
  userSelectionActive: s.userSelectionActive,
1079
1152
  userSelectionRect: s.userSelectionRect,
1080
1153
  });
1081
1154
  function UserSelection() {
1082
- const { userSelectionActive, userSelectionRect } = useStore(selector$k, shallow);
1155
+ const { userSelectionActive, userSelectionRect } = useStore(selector$j, shallow);
1083
1156
  const isActive = userSelectionActive && userSelectionRect;
1084
1157
  if (!isActive) {
1085
1158
  return null;
@@ -1099,24 +1172,33 @@ const wrapHandler = (handler, containerRef) => {
1099
1172
  handler?.(event);
1100
1173
  };
1101
1174
  };
1102
- const selector$j = (s) => ({
1175
+ const selector$i = (s) => ({
1103
1176
  userSelectionActive: s.userSelectionActive,
1104
1177
  elementsSelectable: s.elementsSelectable,
1105
1178
  dragging: s.paneDragging,
1106
1179
  });
1107
- function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSelectionStart, onSelectionEnd, onPaneClick, onPaneContextMenu, onPaneScroll, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, children, }) {
1180
+ function Pane({ isSelecting, selectionKeyPressed, selectionMode = SelectionMode.Full, panOnDrag, onSelectionStart, onSelectionEnd, onPaneClick, onPaneContextMenu, onPaneScroll, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, children, }) {
1108
1181
  const container = useRef(null);
1109
1182
  const store = useStoreApi();
1110
1183
  const prevSelectedNodesCount = useRef(0);
1111
1184
  const prevSelectedEdgesCount = useRef(0);
1112
1185
  const containerBounds = useRef();
1113
- const { userSelectionActive, elementsSelectable, dragging } = useStore(selector$j, shallow);
1186
+ const edgeIdLookup = useRef(new Map());
1187
+ const { userSelectionActive, elementsSelectable, dragging } = useStore(selector$i, shallow);
1188
+ const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
1189
+ // Used to prevent click events when the user lets go of the selectionKey during a selection
1190
+ const selectionInProgress = useRef(false);
1114
1191
  const resetUserSelection = () => {
1115
1192
  store.setState({ userSelectionActive: false, userSelectionRect: null });
1116
1193
  prevSelectedNodesCount.current = 0;
1117
1194
  prevSelectedEdgesCount.current = 0;
1118
1195
  };
1119
1196
  const onClick = (event) => {
1197
+ // We prevent click events when the user let go of the selectionKey during a selection
1198
+ if (selectionInProgress.current) {
1199
+ selectionInProgress.current = false;
1200
+ return;
1201
+ }
1120
1202
  onPaneClick?.(event);
1121
1203
  store.getState().resetSelectedElements();
1122
1204
  store.setState({ nodesSelectionActive: false });
@@ -1129,9 +1211,10 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
1129
1211
  onPaneContextMenu?.(event);
1130
1212
  };
1131
1213
  const onWheel = onPaneScroll ? (event) => onPaneScroll(event) : undefined;
1132
- const onMouseDown = (event) => {
1133
- const { resetSelectedElements, domNode } = store.getState();
1214
+ const onPointerDown = (event) => {
1215
+ const { resetSelectedElements, domNode, edgeLookup } = store.getState();
1134
1216
  containerBounds.current = domNode?.getBoundingClientRect();
1217
+ container.current?.setPointerCapture(event.pointerId);
1135
1218
  if (!elementsSelectable ||
1136
1219
  !isSelecting ||
1137
1220
  event.button !== 0 ||
@@ -1139,6 +1222,11 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
1139
1222
  !containerBounds.current) {
1140
1223
  return;
1141
1224
  }
1225
+ edgeIdLookup.current = new Map();
1226
+ for (const [id, edge] of edgeLookup) {
1227
+ edgeIdLookup.current.set(edge.source, edgeIdLookup.current.get(edge.source)?.add(id) || new Set([id]));
1228
+ edgeIdLookup.current.set(edge.target, edgeIdLookup.current.get(edge.target)?.add(id) || new Set([id]));
1229
+ }
1142
1230
  const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
1143
1231
  resetSelectedElements();
1144
1232
  store.setState({
@@ -1153,55 +1241,55 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
1153
1241
  });
1154
1242
  onSelectionStart?.(event);
1155
1243
  };
1156
- const onMouseMove = (event) => {
1157
- const { userSelectionRect, edges, transform, nodeOrigin, nodes, onNodesChange, onEdgesChange } = store.getState();
1158
- if (!isSelecting || !containerBounds.current || !userSelectionRect) {
1244
+ const onPointerMove = (event) => {
1245
+ const { userSelectionRect, edgeLookup, transform, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = store.getState();
1246
+ if (!containerBounds.current || !userSelectionRect) {
1159
1247
  return;
1160
1248
  }
1161
- store.setState({ userSelectionActive: true, nodesSelectionActive: false });
1162
- const mousePos = getEventPosition(event.nativeEvent, containerBounds.current);
1163
- const startX = userSelectionRect.startX ?? 0;
1164
- const startY = userSelectionRect.startY ?? 0;
1249
+ selectionInProgress.current = true;
1250
+ const { x: mouseX, y: mouseY } = getEventPosition(event.nativeEvent, containerBounds.current);
1251
+ const { startX, startY } = userSelectionRect;
1165
1252
  const nextUserSelectRect = {
1166
- ...userSelectionRect,
1167
- x: mousePos.x < startX ? mousePos.x : startX,
1168
- y: mousePos.y < startY ? mousePos.y : startY,
1169
- width: Math.abs(mousePos.x - startX),
1170
- height: Math.abs(mousePos.y - startY),
1253
+ startX,
1254
+ startY,
1255
+ x: mouseX < startX ? mouseX : startX,
1256
+ y: mouseY < startY ? mouseY : startY,
1257
+ width: Math.abs(mouseX - startX),
1258
+ height: Math.abs(mouseY - startY),
1171
1259
  };
1172
- const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true, nodeOrigin);
1260
+ const selectedNodes = getNodesInside(nodeLookup, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true);
1173
1261
  const selectedEdgeIds = new Set();
1174
1262
  const selectedNodeIds = new Set();
1175
1263
  for (const selectedNode of selectedNodes) {
1176
1264
  selectedNodeIds.add(selectedNode.id);
1177
- for (const edge of edges) {
1178
- if (edge.source === selectedNode.id || edge.target === selectedNode.id) {
1179
- selectedEdgeIds.add(edge.id);
1265
+ const edgeIds = edgeIdLookup.current.get(selectedNode.id);
1266
+ if (edgeIds) {
1267
+ for (const edgeId of edgeIds) {
1268
+ selectedEdgeIds.add(edgeId);
1180
1269
  }
1181
1270
  }
1182
1271
  }
1183
1272
  if (prevSelectedNodesCount.current !== selectedNodeIds.size) {
1184
1273
  prevSelectedNodesCount.current = selectedNodeIds.size;
1185
- const changes = getSelectionChanges(nodes, selectedNodeIds, true);
1186
- if (changes.length) {
1187
- onNodesChange?.(changes);
1188
- }
1274
+ const changes = getSelectionChanges(nodeLookup, selectedNodeIds, true);
1275
+ triggerNodeChanges(changes);
1189
1276
  }
1190
1277
  if (prevSelectedEdgesCount.current !== selectedEdgeIds.size) {
1191
1278
  prevSelectedEdgesCount.current = selectedEdgeIds.size;
1192
- const changes = getSelectionChanges(edges, selectedEdgeIds);
1193
- if (changes.length) {
1194
- onEdgesChange?.(changes);
1195
- }
1279
+ const changes = getSelectionChanges(edgeLookup, selectedEdgeIds);
1280
+ triggerEdgeChanges(changes);
1196
1281
  }
1197
1282
  store.setState({
1198
1283
  userSelectionRect: nextUserSelectRect,
1284
+ userSelectionActive: true,
1285
+ nodesSelectionActive: false,
1199
1286
  });
1200
1287
  };
1201
- const onMouseUp = (event) => {
1288
+ const onPointerUp = (event) => {
1202
1289
  if (event.button !== 0) {
1203
1290
  return;
1204
1291
  }
1292
+ container.current?.releasePointerCapture(event.pointerId);
1205
1293
  const { userSelectionRect } = store.getState();
1206
1294
  // We only want to trigger click functions when in selection mode if
1207
1295
  // the user did not move the mouse.
@@ -1211,16 +1299,13 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
1211
1299
  store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
1212
1300
  resetUserSelection();
1213
1301
  onSelectionEnd?.(event);
1214
- };
1215
- const onMouseLeave = (event) => {
1216
- if (userSelectionActive) {
1217
- store.setState({ nodesSelectionActive: prevSelectedNodesCount.current > 0 });
1218
- onSelectionEnd?.(event);
1302
+ // If the user kept holding the selectionKey during the selection,
1303
+ // we need to reset the selectionInProgress, so the next click event is not prevented
1304
+ if (selectionKeyPressed) {
1305
+ selectionInProgress.current = false;
1219
1306
  }
1220
- resetUserSelection();
1221
1307
  };
1222
- const hasActiveSelection = elementsSelectable && (isSelecting || userSelectionActive);
1223
- return (jsxs("div", { className: cc(['react-flow__pane', { dragging, selection: isSelecting }]), onClick: hasActiveSelection ? undefined : wrapHandler(onClick, container), onContextMenu: wrapHandler(onContextMenu, container), onWheel: wrapHandler(onWheel, container), onMouseEnter: hasActiveSelection ? undefined : onPaneMouseEnter, onMouseDown: hasActiveSelection ? onMouseDown : undefined, onMouseMove: hasActiveSelection ? onMouseMove : onPaneMouseMove, onMouseUp: hasActiveSelection ? onMouseUp : undefined, onMouseLeave: hasActiveSelection ? onMouseLeave : onPaneMouseLeave, ref: container, style: containerStyle, children: [children, jsx(UserSelection, {})] }));
1308
+ return (jsxs("div", { className: cc(['react-flow__pane', { draggable: panOnDrag, 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, {})] }));
1224
1309
  }
1225
1310
 
1226
1311
  // this handler is called by
@@ -1254,31 +1339,28 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
1254
1339
  const [dragging, setDragging] = useState(false);
1255
1340
  const xyDrag = useRef();
1256
1341
  useEffect(() => {
1257
- if (nodeRef?.current) {
1258
- xyDrag.current = XYDrag({
1259
- domNode: nodeRef.current,
1260
- getStoreItems: () => store.getState(),
1261
- onNodeMouseDown: (id) => {
1262
- handleNodeClick({
1263
- id,
1264
- store,
1265
- nodeRef,
1266
- });
1267
- },
1268
- onDragStart: () => {
1269
- setDragging(true);
1270
- },
1271
- onDragStop: () => {
1272
- setDragging(false);
1273
- },
1274
- });
1275
- }
1342
+ xyDrag.current = XYDrag({
1343
+ getStoreItems: () => store.getState(),
1344
+ onNodeMouseDown: (id) => {
1345
+ handleNodeClick({
1346
+ id,
1347
+ store,
1348
+ nodeRef,
1349
+ });
1350
+ },
1351
+ onDragStart: () => {
1352
+ setDragging(true);
1353
+ },
1354
+ onDragStop: () => {
1355
+ setDragging(false);
1356
+ },
1357
+ });
1276
1358
  }, []);
1277
1359
  useEffect(() => {
1278
1360
  if (disabled) {
1279
1361
  xyDrag.current?.destroy();
1280
1362
  }
1281
- else {
1363
+ else if (nodeRef.current) {
1282
1364
  xyDrag.current?.update({
1283
1365
  noDragClassName,
1284
1366
  handleSelector,
@@ -1296,48 +1378,49 @@ function useDrag({ nodeRef, disabled = false, noDragClassName, handleSelector, n
1296
1378
 
1297
1379
  const selectedAndDraggable = (nodesDraggable) => (n) => n.selected && (n.draggable || (nodesDraggable && typeof n.draggable === 'undefined'));
1298
1380
  /**
1299
- * Hook for updating node positions.
1381
+ * Hook for updating node positions by passing a direction and factor
1300
1382
  *
1301
1383
  * @internal
1302
1384
  * @returns function for updating node positions
1303
1385
  */
1304
- function useUpdateNodePositions() {
1386
+ function useMoveSelectedNodes() {
1305
1387
  const store = useStoreApi();
1306
- const updatePositions = useCallback((params) => {
1307
- const { nodeExtent, nodes, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin, } = store.getState();
1308
- const selectedNodes = nodes.filter(selectedAndDraggable(nodesDraggable));
1309
- // by default a node moves 5px on each key press, or 20px if shift is pressed
1310
- // if snap grid is enabled, we use that for the velocity.
1388
+ const moveSelectedNodes = useCallback((params) => {
1389
+ const { nodeExtent, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin } = store.getState();
1390
+ const nodeUpdates = new Map();
1391
+ const isSelected = selectedAndDraggable(nodesDraggable);
1392
+ // by default a node moves 5px on each key press
1393
+ // if snap grid is enabled, we use that for the velocity
1311
1394
  const xVelo = snapToGrid ? snapGrid[0] : 5;
1312
1395
  const yVelo = snapToGrid ? snapGrid[1] : 5;
1313
- const factor = params.isShiftPressed ? 4 : 1;
1314
- const xDiff = params.x * xVelo * factor;
1315
- const yDiff = params.y * yVelo * factor;
1316
- const nodeUpdates = selectedNodes.map((node) => {
1317
- if (node.computed?.positionAbsolute) {
1318
- let nextPosition = {
1319
- x: node.computed.positionAbsolute.x + xDiff,
1320
- y: node.computed.positionAbsolute.y + yDiff,
1321
- };
1322
- if (snapToGrid) {
1323
- nextPosition = snapPosition(nextPosition, snapGrid);
1324
- }
1325
- const { position, positionAbsolute } = calculateNodePosition({
1326
- nodeId: node.id,
1327
- nextPosition,
1328
- nodeLookup,
1329
- nodeExtent,
1330
- nodeOrigin,
1331
- onError,
1332
- });
1333
- node.position = position;
1334
- node.computed.positionAbsolute = positionAbsolute;
1396
+ const xDiff = params.direction.x * xVelo * params.factor;
1397
+ const yDiff = params.direction.y * yVelo * params.factor;
1398
+ for (const [, node] of nodeLookup) {
1399
+ if (!isSelected(node)) {
1400
+ continue;
1335
1401
  }
1336
- return node;
1337
- });
1338
- updateNodePositions(nodeUpdates, true, false);
1402
+ let nextPosition = {
1403
+ x: node.internals.positionAbsolute.x + xDiff,
1404
+ y: node.internals.positionAbsolute.y + yDiff,
1405
+ };
1406
+ if (snapToGrid) {
1407
+ nextPosition = snapPosition(nextPosition, snapGrid);
1408
+ }
1409
+ const { position, positionAbsolute } = calculateNodePosition({
1410
+ nodeId: node.id,
1411
+ nextPosition,
1412
+ nodeLookup,
1413
+ nodeExtent,
1414
+ nodeOrigin,
1415
+ onError,
1416
+ });
1417
+ node.position = position;
1418
+ node.internals.positionAbsolute = positionAbsolute;
1419
+ nodeUpdates.set(node.id, node);
1420
+ }
1421
+ updateNodePositions(nodeUpdates);
1339
1422
  }, []);
1340
- return updatePositions;
1423
+ return moveSelectedNodes;
1341
1424
  }
1342
1425
 
1343
1426
  const NodeIdContext = createContext(null);
@@ -1348,26 +1431,33 @@ const useNodeId = () => {
1348
1431
  return nodeId;
1349
1432
  };
1350
1433
 
1351
- const selector$i = (s) => ({
1434
+ const selector$h = (s) => ({
1352
1435
  connectOnClick: s.connectOnClick,
1353
1436
  noPanClassName: s.noPanClassName,
1354
1437
  rfId: s.rfId,
1355
1438
  });
1356
1439
  const connectingSelector = (nodeId, handleId, type) => (state) => {
1357
- const { connectionStartHandle: startHandle, connectionEndHandle: endHandle, connectionClickStartHandle: clickHandle, } = state;
1440
+ const { connectionClickStartHandle: clickHandle, connectionMode, connection } = state;
1441
+ const { fromHandle, toHandle, isValid } = connection;
1442
+ const connectingTo = toHandle?.nodeId === nodeId && toHandle?.id === handleId && toHandle?.type === type;
1358
1443
  return {
1359
- connecting: (startHandle?.nodeId === nodeId && startHandle?.handleId === handleId && startHandle?.type === type) ||
1360
- (endHandle?.nodeId === nodeId && endHandle?.handleId === handleId && endHandle?.type === type),
1361
- clickConnecting: clickHandle?.nodeId === nodeId && clickHandle?.handleId === handleId && clickHandle?.type === type,
1444
+ connectingFrom: fromHandle?.nodeId === nodeId && fromHandle?.id === handleId && fromHandle?.type === type,
1445
+ connectingTo,
1446
+ clickConnecting: clickHandle?.nodeId === nodeId && clickHandle?.id === handleId && clickHandle?.type === type,
1447
+ isPossibleEndHandle: connectionMode === ConnectionMode.Strict
1448
+ ? fromHandle?.type !== type
1449
+ : nodeId !== fromHandle?.nodeId || handleId !== fromHandle?.id,
1450
+ connectionInProcess: !!fromHandle,
1451
+ valid: connectingTo && isValid,
1362
1452
  };
1363
1453
  };
1364
- const HandleComponent = forwardRef(({ type = 'source', position = Position.Top, isValidConnection, isConnectable = true, isConnectableStart = true, isConnectableEnd = true, id, onConnect, children, className, onMouseDown, onTouchStart, ...rest }, ref) => {
1454
+ function HandleComponent({ type = 'source', position = Position.Top, isValidConnection, isConnectable = true, isConnectableStart = true, isConnectableEnd = true, id, onConnect, children, className, onMouseDown, onTouchStart, ...rest }, ref) {
1365
1455
  const handleId = id || null;
1366
1456
  const isTarget = type === 'target';
1367
1457
  const store = useStoreApi();
1368
1458
  const nodeId = useNodeId();
1369
- const { connectOnClick, noPanClassName, rfId } = useStore(selector$i, shallow);
1370
- const { connecting, clickConnecting } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1459
+ const { connectOnClick, noPanClassName, rfId } = useStore(selector$h, shallow);
1460
+ const { connectingFrom, connectingTo, clickConnecting, isPossibleEndHandle, connectionInProcess, valid } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1371
1461
  if (!nodeId) {
1372
1462
  store.getState().onError?.('010', errorMessages['error010']());
1373
1463
  }
@@ -1397,7 +1487,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1397
1487
  connectionMode: currentStore.connectionMode,
1398
1488
  connectionRadius: currentStore.connectionRadius,
1399
1489
  domNode: currentStore.domNode,
1400
- nodes: currentStore.nodes,
1490
+ nodeLookup: currentStore.nodeLookup,
1401
1491
  lib: currentStore.lib,
1402
1492
  isTarget,
1403
1493
  handleId,
@@ -1411,6 +1501,8 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1411
1501
  onConnect: onConnectExtended,
1412
1502
  isValidConnection: isValidConnection || currentStore.isValidConnection,
1413
1503
  getTransform: () => store.getState().transform,
1504
+ getFromHandle: () => store.getState().connection.fromHandle,
1505
+ autoPanSpeed: currentStore.autoPanSpeed,
1414
1506
  });
1415
1507
  }
1416
1508
  if (isMouseTriggered) {
@@ -1427,7 +1519,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1427
1519
  }
1428
1520
  if (!connectionClickStartHandle) {
1429
1521
  onClickConnectStart?.(event.nativeEvent, { nodeId, handleId, handleType: type });
1430
- store.setState({ connectionClickStartHandle: { nodeId, type, handleId } });
1522
+ store.setState({ connectionClickStartHandle: { nodeId, type, id: handleId } });
1431
1523
  return;
1432
1524
  }
1433
1525
  const doc = getHostForElement(event.target);
@@ -1440,7 +1532,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1440
1532
  },
1441
1533
  connectionMode,
1442
1534
  fromNodeId: connectionClickStartHandle.nodeId,
1443
- fromHandleId: connectionClickStartHandle.handleId || null,
1535
+ fromHandleId: connectionClickStartHandle.id || null,
1444
1536
  fromType: connectionClickStartHandle.type,
1445
1537
  isValidConnection: isValidConnectionHandler,
1446
1538
  flowId,
@@ -1465,17 +1557,22 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1465
1557
  connectable: isConnectable,
1466
1558
  connectablestart: isConnectableStart,
1467
1559
  connectableend: isConnectableEnd,
1468
- connecting: clickConnecting,
1469
- // this class is used to style the handle when the user is connecting
1470
- connectionindicator: isConnectable && ((isConnectableStart && !connecting) || (isConnectableEnd && connecting)),
1560
+ clickconnecting: clickConnecting,
1561
+ connectingfrom: connectingFrom,
1562
+ connectingto: connectingTo,
1563
+ valid,
1564
+ // shows where you can start a connection from
1565
+ // and where you can end it while connecting
1566
+ connectionindicator: isConnectable &&
1567
+ (!connectionInProcess || isPossibleEndHandle) &&
1568
+ (connectionInProcess ? isConnectableEnd : isConnectableStart),
1471
1569
  },
1472
1570
  ]), onMouseDown: onPointerDown, onTouchStart: onPointerDown, onClick: connectOnClick ? onClick : undefined, ref: ref, ...rest, children: children }));
1473
- });
1474
- HandleComponent.displayName = 'Handle';
1571
+ }
1475
1572
  /**
1476
- * The Handle component is the part of a node that can be used to connect nodes.
1573
+ * The Handle component is a UI element that is used to connect nodes.
1477
1574
  */
1478
- const Handle = memo(HandleComponent);
1575
+ const Handle = memo(fixedForwardRef(HandleComponent));
1479
1576
 
1480
1577
  function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }) {
1481
1578
  return (jsxs(Fragment, { children: [data?.label, jsx(Handle, { type: "source", position: sourcePosition, isConnectable: isConnectable })] }));
@@ -1505,21 +1602,34 @@ const builtinNodeTypes = {
1505
1602
  output: OutputNode,
1506
1603
  group: GroupNode,
1507
1604
  };
1605
+ function getNodeInlineStyleDimensions(node) {
1606
+ if (node.internals.handleBounds === undefined) {
1607
+ return {
1608
+ width: node.width ?? node.initialWidth ?? node.style?.width,
1609
+ height: node.height ?? node.initialHeight ?? node.style?.height,
1610
+ };
1611
+ }
1612
+ return {
1613
+ width: node.width ?? node.style?.width,
1614
+ height: node.height ?? node.style?.height,
1615
+ };
1616
+ }
1508
1617
 
1509
- const selector$h = (s) => {
1510
- const selectedNodes = s.nodes.filter((n) => n.selected);
1511
- const { width, height, x, y } = getNodesBounds(selectedNodes, { nodeOrigin: s.nodeOrigin });
1618
+ const selector$g = (s) => {
1619
+ const { width, height, x, y } = getInternalNodesBounds(s.nodeLookup, {
1620
+ filter: (node) => !!node.selected,
1621
+ });
1512
1622
  return {
1513
- width,
1514
- height,
1623
+ width: isNumeric(width) ? width : null,
1624
+ height: isNumeric(height) ? height : null,
1515
1625
  userSelectionActive: s.userSelectionActive,
1516
1626
  transformString: `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]}) translate(${x}px,${y}px)`,
1517
1627
  };
1518
1628
  };
1519
- function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y }) {
1629
+ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboardA11y, }) {
1520
1630
  const store = useStoreApi();
1521
- const { width, height, transformString, userSelectionActive } = useStore(selector$h, shallow);
1522
- const updatePositions = useUpdateNodePositions();
1631
+ const { width, height, transformString, userSelectionActive } = useStore(selector$g, shallow);
1632
+ const moveSelectedNodes = useMoveSelectedNodes();
1523
1633
  const nodeRef = useRef(null);
1524
1634
  useEffect(() => {
1525
1635
  if (!disableKeyboardA11y) {
@@ -1542,10 +1652,9 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
1542
1652
  : undefined;
1543
1653
  const onKeyDown = (event) => {
1544
1654
  if (Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
1545
- updatePositions({
1546
- x: arrowKeyDiffs[event.key].x,
1547
- y: arrowKeyDiffs[event.key].y,
1548
- isShiftPressed: event.shiftKey,
1655
+ moveSelectedNodes({
1656
+ direction: arrowKeyDiffs[event.key],
1657
+ factor: event.shiftKey ? 4 : 1,
1549
1658
  });
1550
1659
  }
1551
1660
  };
@@ -1557,25 +1666,26 @@ function NodesSelection({ onSelectionContextMenu, noPanClassName, disableKeyboar
1557
1666
  } }) }));
1558
1667
  }
1559
1668
 
1560
- const selector$g = (s) => {
1669
+ const win = typeof window !== 'undefined' ? window : undefined;
1670
+ const selector$f = (s) => {
1561
1671
  return { nodesSelectionActive: s.nodesSelectionActive, userSelectionActive: s.userSelectionActive };
1562
1672
  };
1563
- const FlowRendererComponent = ({ children, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneContextMenu, onPaneScroll, deleteKeyCode, selectionKeyCode, selectionOnDrag, selectionMode, onSelectionStart, onSelectionEnd, multiSelectionKeyCode, panActivationKeyCode, zoomActivationKeyCode, elementsSelectable, zoomOnScroll, zoomOnPinch, panOnScroll: _panOnScroll, panOnScrollSpeed, panOnScrollMode, zoomOnDoubleClick, panOnDrag: _panOnDrag, defaultViewport, translateExtent, minZoom, maxZoom, preventScrolling, onSelectionContextMenu, noWheelClassName, noPanClassName, disableKeyboardA11y, onViewportChange, isControlledViewport, }) => {
1564
- const { nodesSelectionActive, userSelectionActive } = useStore(selector$g);
1565
- const selectionKeyPressed = useKeyPress(selectionKeyCode);
1566
- const panActivationKeyPressed = useKeyPress(panActivationKeyCode);
1673
+ function FlowRendererComponent({ children, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneContextMenu, onPaneScroll, paneClickDistance, deleteKeyCode, selectionKeyCode, selectionOnDrag, selectionMode, onSelectionStart, onSelectionEnd, multiSelectionKeyCode, panActivationKeyCode, zoomActivationKeyCode, elementsSelectable, zoomOnScroll, zoomOnPinch, panOnScroll: _panOnScroll, panOnScrollSpeed, panOnScrollMode, zoomOnDoubleClick, panOnDrag: _panOnDrag, defaultViewport, translateExtent, minZoom, maxZoom, preventScrolling, onSelectionContextMenu, noWheelClassName, noPanClassName, disableKeyboardA11y, onViewportChange, isControlledViewport, }) {
1674
+ const { nodesSelectionActive, userSelectionActive } = useStore(selector$f);
1675
+ const selectionKeyPressed = useKeyPress(selectionKeyCode, { target: win });
1676
+ const panActivationKeyPressed = useKeyPress(panActivationKeyCode, { target: win });
1567
1677
  const panOnDrag = panActivationKeyPressed || _panOnDrag;
1568
1678
  const panOnScroll = panActivationKeyPressed || _panOnScroll;
1569
1679
  const isSelecting = selectionKeyPressed || userSelectionActive || (selectionOnDrag && panOnDrag !== true);
1570
1680
  useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode });
1571
- return (jsx(ZoomPane, { onPaneContextMenu: onPaneContextMenu, elementsSelectable: elementsSelectable, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, zoomOnDoubleClick: zoomOnDoubleClick, panOnDrag: !selectionKeyPressed && panOnDrag, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, zoomActivationKeyCode: zoomActivationKeyCode, preventScrolling: preventScrolling, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, onViewportChange: onViewportChange, isControlledViewport: isControlledViewport, children: jsxs(Pane, { onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneContextMenu: onPaneContextMenu, onPaneScroll: onPaneScroll, panOnDrag: panOnDrag, isSelecting: !!isSelecting, selectionMode: selectionMode, children: [children, nodesSelectionActive && (jsx(NodesSelection, { onSelectionContextMenu: onSelectionContextMenu, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y }))] }) }));
1572
- };
1681
+ return (jsx(ZoomPane, { onPaneContextMenu: onPaneContextMenu, elementsSelectable: elementsSelectable, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, zoomOnDoubleClick: zoomOnDoubleClick, panOnDrag: !selectionKeyPressed && panOnDrag, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, zoomActivationKeyCode: zoomActivationKeyCode, preventScrolling: preventScrolling, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, onViewportChange: onViewportChange, isControlledViewport: isControlledViewport, paneClickDistance: paneClickDistance, children: jsxs(Pane, { onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneContextMenu: onPaneContextMenu, onPaneScroll: onPaneScroll, panOnDrag: panOnDrag, isSelecting: !!isSelecting, selectionMode: selectionMode, selectionKeyPressed: selectionKeyPressed, children: [children, nodesSelectionActive && (jsx(NodesSelection, { onSelectionContextMenu: onSelectionContextMenu, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y }))] }) }));
1682
+ }
1573
1683
  FlowRendererComponent.displayName = 'FlowRenderer';
1574
1684
  const FlowRenderer = memo(FlowRendererComponent);
1575
1685
 
1576
- const selector$f = (onlyRenderVisible) => (s) => {
1686
+ const selector$e = (onlyRenderVisible) => (s) => {
1577
1687
  return onlyRenderVisible
1578
- ? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map((node) => node.id)
1688
+ ? getNodesInside(s.nodeLookup, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map((node) => node.id)
1579
1689
  : Array.from(s.nodeLookup.keys());
1580
1690
  };
1581
1691
  /**
@@ -1586,55 +1696,97 @@ const selector$f = (onlyRenderVisible) => (s) => {
1586
1696
  * @returns array with visible node ids
1587
1697
  */
1588
1698
  function useVisibleNodeIds(onlyRenderVisible) {
1589
- const nodeIds = useStore(useCallback(selector$f(onlyRenderVisible), [onlyRenderVisible]), shallow);
1699
+ const nodeIds = useStore(useCallback(selector$e(onlyRenderVisible), [onlyRenderVisible]), shallow);
1590
1700
  return nodeIds;
1591
1701
  }
1592
1702
 
1593
- const selector$e = (s) => s.updateNodeDimensions;
1703
+ const selector$d = (s) => s.updateNodeInternals;
1594
1704
  function useResizeObserver() {
1595
- const updateNodeDimensions = useStore(selector$e);
1596
- const resizeObserverRef = useRef();
1597
- const resizeObserver = useMemo(() => {
1705
+ const updateNodeInternals = useStore(selector$d);
1706
+ const [resizeObserver] = useState(() => {
1598
1707
  if (typeof ResizeObserver === 'undefined') {
1599
1708
  return null;
1600
1709
  }
1601
- const observer = new ResizeObserver((entries) => {
1710
+ return new ResizeObserver((entries) => {
1602
1711
  const updates = new Map();
1603
1712
  entries.forEach((entry) => {
1604
1713
  const id = entry.target.getAttribute('data-id');
1605
1714
  updates.set(id, {
1606
1715
  id,
1607
1716
  nodeElement: entry.target,
1608
- forceUpdate: true,
1717
+ force: true,
1609
1718
  });
1610
1719
  });
1611
- updateNodeDimensions(updates);
1720
+ updateNodeInternals(updates);
1612
1721
  });
1613
- resizeObserverRef.current = observer;
1614
- return observer;
1615
- }, []);
1722
+ });
1616
1723
  useEffect(() => {
1617
1724
  return () => {
1618
- resizeObserverRef?.current?.disconnect();
1725
+ resizeObserver?.disconnect();
1619
1726
  };
1620
- }, []);
1727
+ }, [resizeObserver]);
1621
1728
  return resizeObserver;
1622
1729
  }
1623
1730
 
1624
- function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, nodeOrigin, onError, }) {
1625
- const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => {
1731
+ /**
1732
+ * Hook to handle the resize observation + internal updates for the passed node.
1733
+ *
1734
+ * @internal
1735
+ * @returns nodeRef - reference to the node element
1736
+ */
1737
+ function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
1738
+ const store = useStoreApi();
1739
+ const nodeRef = useRef(null);
1740
+ const observedNode = useRef(null);
1741
+ const prevSourcePosition = useRef(node.sourcePosition);
1742
+ const prevTargetPosition = useRef(node.targetPosition);
1743
+ const prevType = useRef(nodeType);
1744
+ const isInitialized = hasDimensions && !!node.internals.handleBounds;
1745
+ useEffect(() => {
1746
+ if (nodeRef.current && !node.hidden && (!isInitialized || observedNode.current !== nodeRef.current)) {
1747
+ if (observedNode.current) {
1748
+ resizeObserver?.unobserve(observedNode.current);
1749
+ }
1750
+ resizeObserver?.observe(nodeRef.current);
1751
+ observedNode.current = nodeRef.current;
1752
+ }
1753
+ }, [isInitialized, node.hidden]);
1754
+ useEffect(() => {
1755
+ return () => {
1756
+ if (observedNode.current) {
1757
+ resizeObserver?.unobserve(observedNode.current);
1758
+ observedNode.current = null;
1759
+ }
1760
+ };
1761
+ }, []);
1762
+ useEffect(() => {
1763
+ if (nodeRef.current) {
1764
+ // when the user programmatically changes the source or handle position, we need to update the internals
1765
+ // to make sure the edges are updated correctly
1766
+ const typeChanged = prevType.current !== nodeType;
1767
+ const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
1768
+ const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
1769
+ if (typeChanged || sourcePosChanged || targetPosChanged) {
1770
+ prevType.current = nodeType;
1771
+ prevSourcePosition.current = node.sourcePosition;
1772
+ prevTargetPosition.current = node.targetPosition;
1773
+ store
1774
+ .getState()
1775
+ .updateNodeInternals(new Map([[node.id, { id: node.id, nodeElement: nodeRef.current, force: true }]]));
1776
+ }
1777
+ }
1778
+ }, [node.id, nodeType, node.sourcePosition, node.targetPosition]);
1779
+ return nodeRef;
1780
+ }
1781
+
1782
+ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, onError, }) {
1783
+ const { node, internals, isParent } = useStore((s) => {
1626
1784
  const node = s.nodeLookup.get(id);
1627
- const positionAbsolute = nodeExtent
1628
- ? clampPosition(node.computed?.positionAbsolute, nodeExtent)
1629
- : node.computed?.positionAbsolute || { x: 0, y: 0 };
1785
+ const isParent = s.parentLookup.has(id);
1630
1786
  return {
1631
1787
  node,
1632
- // we are mutating positionAbsolute, z and isParent attributes for sub flows
1633
- // so we we need to force a re-render when some change
1634
- positionAbsoluteX: positionAbsolute.x,
1635
- positionAbsoluteY: positionAbsolute.y,
1636
- zIndex: node[internalsSymbol]?.z ?? 0,
1637
- isParent: !!node[internalsSymbol]?.isParent,
1788
+ internals: node.internals,
1789
+ isParent,
1638
1790
  };
1639
1791
  }, shallow);
1640
1792
  let nodeType = node.type || 'default';
@@ -1649,36 +1801,8 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1649
1801
  const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
1650
1802
  const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
1651
1803
  const store = useStoreApi();
1652
- const nodeRef = useRef(null);
1653
- const prevSourcePosition = useRef(node.sourcePosition);
1654
- const prevTargetPosition = useRef(node.targetPosition);
1655
- const prevType = useRef(nodeType);
1656
- const updatePositions = useUpdateNodePositions();
1657
- useEffect(() => {
1658
- if (nodeRef.current && !node.hidden) {
1659
- const currNode = nodeRef.current;
1660
- resizeObserver?.observe(currNode);
1661
- return () => resizeObserver?.unobserve(currNode);
1662
- }
1663
- }, [node.hidden]);
1664
- useEffect(() => {
1665
- // when the user programmatically changes the source or handle position, we re-initialize the node
1666
- const typeChanged = prevType.current !== nodeType;
1667
- const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
1668
- const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
1669
- if (nodeRef.current && (typeChanged || sourcePosChanged || targetPosChanged)) {
1670
- if (typeChanged) {
1671
- prevType.current = nodeType;
1672
- }
1673
- if (sourcePosChanged) {
1674
- prevSourcePosition.current = node.sourcePosition;
1675
- }
1676
- if (targetPosChanged) {
1677
- prevTargetPosition.current = node.targetPosition;
1678
- }
1679
- store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, forceUpdate: true }]]));
1680
- }
1681
- }, [id, nodeType, node.sourcePosition, node.targetPosition]);
1804
+ const hasDimensions = nodeHasDimensions(node);
1805
+ const nodeRef = useNodeObserver({ node, nodeType, hasDimensions, resizeObserver });
1682
1806
  const dragging = useDrag({
1683
1807
  nodeRef,
1684
1808
  disabled: node.hidden || !isDraggable,
@@ -1687,27 +1811,32 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1687
1811
  nodeId: id,
1688
1812
  isSelectable,
1689
1813
  });
1814
+ const moveSelectedNodes = useMoveSelectedNodes();
1690
1815
  if (node.hidden) {
1691
1816
  return null;
1692
1817
  }
1693
- const width = node.width ?? undefined;
1694
- const height = node.height ?? undefined;
1695
- const computedWidth = node.computed?.width;
1696
- const computedHeight = node.computed?.height;
1697
- const positionAbsoluteOrigin = getPositionWithOrigin({
1698
- x: positionAbsoluteX,
1699
- y: positionAbsoluteY,
1700
- width: computedWidth ?? width ?? 0,
1701
- height: computedHeight ?? height ?? 0,
1702
- origin: node.origin || nodeOrigin,
1703
- });
1704
- const initialized = (!!computedWidth && !!computedHeight) || (!!width && !!height);
1818
+ const nodeDimensions = getNodeDimensions(node);
1819
+ const inlineDimensions = getNodeInlineStyleDimensions(node);
1820
+ // TODO: clamping should happen earlier
1821
+ const clampedPosition = nodeExtent
1822
+ ? clampPosition(internals.positionAbsolute, nodeExtent)
1823
+ : internals.positionAbsolute;
1705
1824
  const hasPointerEvents = isSelectable || isDraggable || onClick || onMouseEnter || onMouseMove || onMouseLeave;
1706
- const onMouseEnterHandler = onMouseEnter ? (event) => onMouseEnter(event, { ...node }) : undefined;
1707
- const onMouseMoveHandler = onMouseMove ? (event) => onMouseMove(event, { ...node }) : undefined;
1708
- const onMouseLeaveHandler = onMouseLeave ? (event) => onMouseLeave(event, { ...node }) : undefined;
1709
- const onContextMenuHandler = onContextMenu ? (event) => onContextMenu(event, { ...node }) : undefined;
1710
- const onDoubleClickHandler = onDoubleClick ? (event) => onDoubleClick(event, { ...node }) : undefined;
1825
+ const onMouseEnterHandler = onMouseEnter
1826
+ ? (event) => onMouseEnter(event, { ...internals.userNode })
1827
+ : undefined;
1828
+ const onMouseMoveHandler = onMouseMove
1829
+ ? (event) => onMouseMove(event, { ...internals.userNode })
1830
+ : undefined;
1831
+ const onMouseLeaveHandler = onMouseLeave
1832
+ ? (event) => onMouseLeave(event, { ...internals.userNode })
1833
+ : undefined;
1834
+ const onContextMenuHandler = onContextMenu
1835
+ ? (event) => onContextMenu(event, { ...internals.userNode })
1836
+ : undefined;
1837
+ const onDoubleClickHandler = onDoubleClick
1838
+ ? (event) => onDoubleClick(event, { ...internals.userNode })
1839
+ : undefined;
1711
1840
  const onSelectNodeHandler = (event) => {
1712
1841
  const { selectNodesOnDrag, nodeDragThreshold } = store.getState();
1713
1842
  if (isSelectable && (!selectNodesOnDrag || !isDraggable || nodeDragThreshold > 0)) {
@@ -1720,11 +1849,11 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1720
1849
  });
1721
1850
  }
1722
1851
  if (onClick) {
1723
- onClick(event, { ...node });
1852
+ onClick(event, { ...internals.userNode });
1724
1853
  }
1725
1854
  };
1726
1855
  const onKeyDown = (event) => {
1727
- if (isInputDOMNode(event.nativeEvent)) {
1856
+ if (isInputDOMNode(event.nativeEvent) || disableKeyboardA11y) {
1728
1857
  return;
1729
1858
  }
1730
1859
  if (elementSelectionKeys.includes(event.key) && isSelectable) {
@@ -1736,19 +1865,15 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1736
1865
  nodeRef,
1737
1866
  });
1738
1867
  }
1739
- else if (!disableKeyboardA11y &&
1740
- isDraggable &&
1741
- node.selected &&
1742
- Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
1868
+ else if (isDraggable && node.selected && Object.prototype.hasOwnProperty.call(arrowKeyDiffs, event.key)) {
1743
1869
  store.setState({
1744
1870
  ariaLiveMessage: `Moved selected node ${event.key
1745
1871
  .replace('Arrow', '')
1746
- .toLowerCase()}. New position, x: ${~~positionAbsoluteX}, y: ${~~positionAbsoluteY}`,
1872
+ .toLowerCase()}. New position, x: ${~~clampedPosition.x}, y: ${~~clampedPosition.y}`,
1747
1873
  });
1748
- updatePositions({
1749
- x: arrowKeyDiffs[event.key].x,
1750
- y: arrowKeyDiffs[event.key].y,
1751
- isShiftPressed: event.shiftKey,
1874
+ moveSelectedNodes({
1875
+ direction: arrowKeyDiffs[event.key],
1876
+ factor: event.shiftKey ? 4 : 1,
1752
1877
  });
1753
1878
  }
1754
1879
  };
@@ -1764,28 +1889,28 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1764
1889
  selected: node.selected,
1765
1890
  selectable: isSelectable,
1766
1891
  parent: isParent,
1892
+ draggable: isDraggable,
1767
1893
  dragging,
1768
1894
  },
1769
1895
  ]), ref: nodeRef, style: {
1770
- zIndex,
1771
- transform: `translate(${positionAbsoluteOrigin.x}px,${positionAbsoluteOrigin.y}px)`,
1896
+ zIndex: internals.z,
1897
+ transform: `translate(${clampedPosition.x}px,${clampedPosition.y}px)`,
1772
1898
  pointerEvents: hasPointerEvents ? 'all' : 'none',
1773
- visibility: initialized ? 'visible' : 'hidden',
1899
+ visibility: hasDimensions ? 'visible' : 'hidden',
1774
1900
  ...node.style,
1775
- width: width ?? node.style?.width,
1776
- height: height ?? node.style?.height,
1777
- }, "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, width: computedWidth, height: computedHeight, positionAbsoluteX: positionAbsoluteX, positionAbsoluteY: positionAbsoluteY, selected: node.selected, isConnectable: isConnectable, sourcePosition: node.sourcePosition, targetPosition: node.targetPosition, dragging: dragging, dragHandle: node.dragHandle, zIndex: zIndex }) }) }));
1901
+ ...inlineDimensions,
1902
+ }, "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: clampedPosition.x, positionAbsoluteY: clampedPosition.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 }) }) }));
1778
1903
  }
1779
1904
 
1780
- const selector$d = (s) => ({
1905
+ const selector$c = (s) => ({
1781
1906
  nodesDraggable: s.nodesDraggable,
1782
1907
  nodesConnectable: s.nodesConnectable,
1783
1908
  nodesFocusable: s.nodesFocusable,
1784
1909
  elementsSelectable: s.elementsSelectable,
1785
1910
  onError: s.onError,
1786
1911
  });
1787
- const NodeRendererComponent = (props) => {
1788
- const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector$d, shallow);
1912
+ function NodeRendererComponent(props) {
1913
+ const { nodesDraggable, nodesConnectable, nodesFocusable, elementsSelectable, onError } = useStore(selector$c, shallow);
1789
1914
  const nodeIds = useVisibleNodeIds(props.onlyRenderVisibleElements);
1790
1915
  const resizeObserver = useResizeObserver();
1791
1916
  return (jsx("div", { className: "react-flow__nodes", style: containerStyle, children: nodeIds.map((nodeId) => {
@@ -1813,9 +1938,9 @@ const NodeRendererComponent = (props) => {
1813
1938
  // moved into `NodeComponentWrapper`. This ensures they are
1814
1939
  // memorized – so if `NodeRenderer` *has* to rerender, it only
1815
1940
  // needs to regenerate the list of nodes, nothing else.
1816
- jsx(NodeWrapper, { id: nodeId, nodeTypes: props.nodeTypes, nodeExtent: props.nodeExtent, nodeOrigin: props.nodeOrigin, 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, onError: onError }, nodeId));
1941
+ 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, onError: onError }, nodeId));
1817
1942
  }) }));
1818
- };
1943
+ }
1819
1944
  NodeRendererComponent.displayName = 'NodeRenderer';
1820
1945
  const NodeRenderer = memo(NodeRendererComponent);
1821
1946
 
@@ -1917,21 +2042,22 @@ var MarkerDefinitions$1 = memo(MarkerDefinitions);
1917
2042
  function EdgeTextComponent({ x, y, label, labelStyle = {}, labelShowBg = true, labelBgStyle = {}, labelBgPadding = [2, 4], labelBgBorderRadius = 2, children, className, ...rest }) {
1918
2043
  const [edgeTextBbox, setEdgeTextBbox] = useState({ x: 1, y: 0, width: 0, height: 0 });
1919
2044
  const edgeTextClasses = cc(['react-flow__edge-textwrapper', className]);
1920
- const onEdgeTextRefChange = useCallback((edgeRef) => {
1921
- if (edgeRef === null)
1922
- return;
1923
- const textBbox = edgeRef.getBBox();
1924
- setEdgeTextBbox({
1925
- x: textBbox.x,
1926
- y: textBbox.y,
1927
- width: textBbox.width,
1928
- height: textBbox.height,
1929
- });
1930
- }, []);
2045
+ const edgeTextRef = useRef(null);
2046
+ useEffect(() => {
2047
+ if (edgeTextRef.current) {
2048
+ const textBbox = edgeTextRef.current.getBBox();
2049
+ setEdgeTextBbox({
2050
+ x: textBbox.x,
2051
+ y: textBbox.y,
2052
+ width: textBbox.width,
2053
+ height: textBbox.height,
2054
+ });
2055
+ }
2056
+ }, [label]);
1931
2057
  if (typeof label === 'undefined' || !label) {
1932
2058
  return null;
1933
2059
  }
1934
- 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: onEdgeTextRefChange, style: labelStyle, children: label }), children] }));
2060
+ 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] }));
1935
2061
  }
1936
2062
  EdgeTextComponent.displayName = 'EdgeText';
1937
2063
  const EdgeText = memo(EdgeTextComponent);
@@ -2102,25 +2228,25 @@ function EdgeAnchor({ position, centerX, centerY, radius = 10, onMouseDown, onMo
2102
2228
  return (jsx("circle", { onMouseDown: onMouseDown, onMouseEnter: onMouseEnter, onMouseOut: onMouseOut, className: cc([EdgeUpdaterClassName, `${EdgeUpdaterClassName}-${type}`]), cx: shiftX(centerX, radius, position), cy: shiftY(centerY, radius, position), r: radius, stroke: "transparent", fill: "transparent" }));
2103
2229
  }
2104
2230
 
2105
- function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleId, sourceHandleId, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, onEdgeUpdate, onEdgeUpdateStart, onEdgeUpdateEnd, setUpdating, setUpdateHover, }) {
2231
+ function EdgeUpdateAnchors({ isReconnectable, reconnectRadius, edge, targetHandleId, sourceHandleId, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition, onReconnect, onReconnectStart, onReconnectEnd, setReconnecting, setUpdateHover, }) {
2106
2232
  const store = useStoreApi();
2107
2233
  const handleEdgeUpdater = (event, isSourceHandle) => {
2108
2234
  // avoid triggering edge updater if mouse btn is not left
2109
2235
  if (event.button !== 0) {
2110
2236
  return;
2111
2237
  }
2112
- const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodes, rfId: flowId, panBy, updateConnection, } = store.getState();
2238
+ const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodeLookup, rfId: flowId, panBy, updateConnection, } = store.getState();
2113
2239
  const nodeId = isSourceHandle ? edge.target : edge.source;
2114
2240
  const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
2115
2241
  const handleType = isSourceHandle ? 'target' : 'source';
2116
2242
  const isTarget = isSourceHandle;
2117
- setUpdating(true);
2118
- onEdgeUpdateStart?.(event, edge, handleType);
2119
- const _onEdgeUpdateEnd = (evt) => {
2120
- setUpdating(false);
2121
- onEdgeUpdateEnd?.(evt, edge, handleType);
2243
+ setReconnecting(true);
2244
+ onReconnectStart?.(event, edge, handleType);
2245
+ const _onReconnectEnd = (evt) => {
2246
+ setReconnecting(false);
2247
+ onReconnectEnd?.(evt, edge, handleType);
2122
2248
  };
2123
- const onConnectEdge = (connection) => onEdgeUpdate?.(edge, connection);
2249
+ const onConnectEdge = (connection) => onReconnect?.(edge, connection);
2124
2250
  XYHandle.onPointerDown(event.nativeEvent, {
2125
2251
  autoPanOnConnect,
2126
2252
  connectionMode,
@@ -2128,7 +2254,7 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2128
2254
  domNode,
2129
2255
  handleId,
2130
2256
  nodeId,
2131
- nodes,
2257
+ nodeLookup,
2132
2258
  isTarget,
2133
2259
  edgeUpdaterType: handleType,
2134
2260
  lib,
@@ -2139,19 +2265,20 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2139
2265
  onConnect: onConnectEdge,
2140
2266
  onConnectStart,
2141
2267
  onConnectEnd,
2142
- onEdgeUpdateEnd: _onEdgeUpdateEnd,
2268
+ onReconnectEnd: _onReconnectEnd,
2143
2269
  updateConnection,
2144
2270
  getTransform: () => store.getState().transform,
2271
+ getFromHandle: () => store.getState().connection.fromHandle,
2145
2272
  });
2146
2273
  };
2147
- const onEdgeUpdaterSourceMouseDown = (event) => handleEdgeUpdater(event, true);
2148
- const onEdgeUpdaterTargetMouseDown = (event) => handleEdgeUpdater(event, false);
2149
- const onEdgeUpdaterMouseEnter = () => setUpdateHover(true);
2150
- const onEdgeUpdaterMouseOut = () => setUpdateHover(false);
2151
- return (jsxs(Fragment, { children: [(isUpdatable === 'source' || isUpdatable === true) && (jsx(EdgeAnchor, { position: sourcePosition, centerX: sourceX, centerY: sourceY, radius: edgeUpdaterRadius, onMouseDown: onEdgeUpdaterSourceMouseDown, onMouseEnter: onEdgeUpdaterMouseEnter, onMouseOut: onEdgeUpdaterMouseOut, type: "source" })), (isUpdatable === 'target' || isUpdatable === true) && (jsx(EdgeAnchor, { position: targetPosition, centerX: targetX, centerY: targetY, radius: edgeUpdaterRadius, onMouseDown: onEdgeUpdaterTargetMouseDown, onMouseEnter: onEdgeUpdaterMouseEnter, onMouseOut: onEdgeUpdaterMouseOut, type: "target" }))] }));
2274
+ const onReconnectSourceMouseDown = (event) => handleEdgeUpdater(event, true);
2275
+ const onReconnectTargetMouseDown = (event) => handleEdgeUpdater(event, false);
2276
+ const onReconnectMouseEnter = () => setUpdateHover(true);
2277
+ const onReconnectMouseOut = () => setUpdateHover(false);
2278
+ return (jsxs(Fragment, { children: [(isReconnectable === 'source' || isReconnectable === true) && (jsx(EdgeAnchor, { position: sourcePosition, centerX: sourceX, centerY: sourceY, radius: reconnectRadius, onMouseDown: onReconnectSourceMouseDown, onMouseEnter: onReconnectMouseEnter, onMouseOut: onReconnectMouseOut, type: "source" })), (isReconnectable === 'target' || isReconnectable === true) && (jsx(EdgeAnchor, { position: targetPosition, centerX: targetX, centerY: targetY, radius: reconnectRadius, onMouseDown: onReconnectTargetMouseDown, onMouseEnter: onReconnectMouseEnter, onMouseOut: onReconnectMouseOut, type: "target" }))] }));
2152
2279
  }
2153
2280
 
2154
- function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, onClick, onDoubleClick, onContextMenu, onMouseEnter, onMouseMove, onMouseLeave, edgeUpdaterRadius, onEdgeUpdate, onEdgeUpdateStart, onEdgeUpdateEnd, rfId, edgeTypes, noPanClassName, onError, }) {
2281
+ function EdgeWrapper({ id, edgesFocusable, edgesReconnectable, elementsSelectable, onClick, onDoubleClick, onContextMenu, onMouseEnter, onMouseMove, onMouseLeave, reconnectRadius, onReconnect, onReconnectStart, onReconnectEnd, rfId, edgeTypes, noPanClassName, onError, disableKeyboardA11y, }) {
2155
2282
  let edge = useStore((s) => s.edgeLookup.get(id));
2156
2283
  const defaultEdgeOptions = useStore((s) => s.defaultEdgeOptions);
2157
2284
  edge = defaultEdgeOptions ? { ...defaultEdgeOptions, ...edge } : edge;
@@ -2163,12 +2290,12 @@ function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, o
2163
2290
  EdgeComponent = builtinEdgeTypes.default;
2164
2291
  }
2165
2292
  const isFocusable = !!(edge.focusable || (edgesFocusable && typeof edge.focusable === 'undefined'));
2166
- const isUpdatable = typeof onEdgeUpdate !== 'undefined' &&
2167
- (edge.updatable || (edgesUpdatable && typeof edge.updatable === 'undefined'));
2293
+ const isReconnectable = typeof onReconnect !== 'undefined' &&
2294
+ (edge.reconnectable || (edgesReconnectable && typeof edge.reconnectable === 'undefined'));
2168
2295
  const isSelectable = !!(edge.selectable || (elementsSelectable && typeof edge.selectable === 'undefined'));
2169
2296
  const edgeRef = useRef(null);
2170
2297
  const [updateHover, setUpdateHover] = useState(false);
2171
- const [updating, setUpdating] = useState(false);
2298
+ const [reconnecting, setReconnecting] = useState(false);
2172
2299
  const store = useStoreApi();
2173
2300
  const { zIndex, sourceX, sourceY, targetX, targetY, sourcePosition, targetPosition } = useStore(useCallback((store) => {
2174
2301
  const sourceNode = store.nodeLookup.get(edge.source);
@@ -2200,9 +2327,9 @@ function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, o
2200
2327
  ...(edgePosition || nullPosition),
2201
2328
  };
2202
2329
  }, [edge.source, edge.target, edge.sourceHandle, edge.targetHandle, edge.selected, edge.zIndex]), shallow);
2203
- const markerStartUrl = useMemo(() => (edge.markerStart ? `url(#${getMarkerId(edge.markerStart, rfId)})` : undefined), [edge.markerStart, rfId]);
2204
- const markerEndUrl = useMemo(() => (edge.markerEnd ? `url(#${getMarkerId(edge.markerEnd, rfId)})` : undefined), [edge.markerEnd, rfId]);
2205
- if (edge.hidden || !sourceX || !sourceY || !targetX || !targetY) {
2330
+ const markerStartUrl = useMemo(() => (edge.markerStart ? `url('#${getMarkerId(edge.markerStart, rfId)}')` : undefined), [edge.markerStart, rfId]);
2331
+ const markerEndUrl = useMemo(() => (edge.markerEnd ? `url('#${getMarkerId(edge.markerEnd, rfId)}')` : undefined), [edge.markerEnd, rfId]);
2332
+ if (edge.hidden || sourceX === null || sourceY === null || targetX === null || targetY === null) {
2206
2333
  return null;
2207
2334
  }
2208
2335
  const onEdgeClick = (event) => {
@@ -2247,7 +2374,7 @@ function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, o
2247
2374
  }
2248
2375
  : undefined;
2249
2376
  const onKeyDown = (event) => {
2250
- if (elementSelectionKeys.includes(event.key) && isSelectable) {
2377
+ if (!disableKeyboardA11y && elementSelectionKeys.includes(event.key) && isSelectable) {
2251
2378
  const { unselectNodesAndEdges, addSelectedEdges } = store.getState();
2252
2379
  const unselect = event.key === 'Escape';
2253
2380
  if (unselect) {
@@ -2271,31 +2398,31 @@ function EdgeWrapper({ id, edgesFocusable, edgesUpdatable, elementsSelectable, o
2271
2398
  updating: updateHover,
2272
2399
  selectable: isSelectable,
2273
2400
  },
2274
- ]), onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, onMouseEnter: onEdgeMouseEnter, onMouseMove: onEdgeMouseMove, onMouseLeave: onEdgeMouseLeave, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : 'img', "data-id": id, "data-testid": `rf__edge-${id}`, "aria-label": edge.ariaLabel === null ? undefined : edge.ariaLabel || `Edge from ${edge.source} to ${edge.target}`, "aria-describedby": isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined, ref: edgeRef, children: [!updating && (jsx(EdgeComponent, { id: id, source: edge.source, target: edge.target, selected: edge.selected, animated: edge.animated, label: edge.label, labelStyle: edge.labelStyle, labelShowBg: edge.labelShowBg, labelBgStyle: edge.labelBgStyle, labelBgPadding: edge.labelBgPadding, labelBgBorderRadius: edge.labelBgBorderRadius, sourceX: sourceX, sourceY: sourceY, targetX: targetX, targetY: targetY, sourcePosition: sourcePosition, targetPosition: targetPosition, data: edge.data, style: edge.style, sourceHandleId: edge.sourceHandle, targetHandleId: edge.targetHandle, markerStart: markerStartUrl, markerEnd: markerEndUrl, pathOptions: 'pathOptions' in edge ? edge.pathOptions : undefined, interactionWidth: edge.interactionWidth })), isUpdatable && (jsx(EdgeUpdateAnchors, { edge: edge, isUpdatable: isUpdatable, edgeUpdaterRadius: edgeUpdaterRadius, onEdgeUpdate: onEdgeUpdate, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, sourceX: sourceX, sourceY: sourceY, targetX: targetX, targetY: targetY, sourcePosition: sourcePosition, targetPosition: targetPosition, setUpdateHover: setUpdateHover, setUpdating: setUpdating, sourceHandleId: edge.sourceHandle, targetHandleId: edge.targetHandle }))] }) }));
2401
+ ]), onClick: onEdgeClick, onDoubleClick: onEdgeDoubleClick, onContextMenu: onEdgeContextMenu, onMouseEnter: onEdgeMouseEnter, onMouseMove: onEdgeMouseMove, onMouseLeave: onEdgeMouseLeave, onKeyDown: isFocusable ? onKeyDown : undefined, tabIndex: isFocusable ? 0 : undefined, role: isFocusable ? 'button' : 'img', "data-id": id, "data-testid": `rf__edge-${id}`, "aria-label": edge.ariaLabel === null ? undefined : edge.ariaLabel || `Edge from ${edge.source} to ${edge.target}`, "aria-describedby": isFocusable ? `${ARIA_EDGE_DESC_KEY}-${rfId}` : undefined, ref: edgeRef, children: [!reconnecting && (jsx(EdgeComponent, { id: id, source: edge.source, target: edge.target, type: edge.type, selected: edge.selected, animated: edge.animated, selectable: isSelectable, deletable: edge.deletable ?? true, label: edge.label, labelStyle: edge.labelStyle, labelShowBg: edge.labelShowBg, labelBgStyle: edge.labelBgStyle, labelBgPadding: edge.labelBgPadding, labelBgBorderRadius: edge.labelBgBorderRadius, sourceX: sourceX, sourceY: sourceY, targetX: targetX, targetY: targetY, sourcePosition: sourcePosition, targetPosition: targetPosition, data: edge.data, style: edge.style, sourceHandleId: edge.sourceHandle, targetHandleId: edge.targetHandle, markerStart: markerStartUrl, markerEnd: markerEndUrl, pathOptions: 'pathOptions' in edge ? edge.pathOptions : undefined, interactionWidth: edge.interactionWidth })), isReconnectable && (jsx(EdgeUpdateAnchors, { edge: edge, isReconnectable: isReconnectable, reconnectRadius: reconnectRadius, onReconnect: onReconnect, onReconnectStart: onReconnectStart, onReconnectEnd: onReconnectEnd, sourceX: sourceX, sourceY: sourceY, targetX: targetX, targetY: targetY, sourcePosition: sourcePosition, targetPosition: targetPosition, setUpdateHover: setUpdateHover, setReconnecting: setReconnecting, sourceHandleId: edge.sourceHandle, targetHandleId: edge.targetHandle }))] }) }));
2275
2402
  }
2276
2403
 
2277
- const selector$c = (s) => ({
2404
+ const selector$b = (s) => ({
2278
2405
  width: s.width,
2279
2406
  height: s.height,
2280
2407
  edgesFocusable: s.edgesFocusable,
2281
- edgesUpdatable: s.edgesUpdatable,
2408
+ edgesReconnectable: s.edgesReconnectable,
2282
2409
  elementsSelectable: s.elementsSelectable,
2283
2410
  connectionMode: s.connectionMode,
2284
2411
  onError: s.onError,
2285
2412
  });
2286
- function EdgeRendererComponent({ defaultMarkerColor, onlyRenderVisibleElements, rfId, edgeTypes, noPanClassName, onEdgeUpdate, onEdgeContextMenu, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, onEdgeClick, edgeUpdaterRadius, onEdgeDoubleClick, onEdgeUpdateStart, onEdgeUpdateEnd, }) {
2287
- const { edgesFocusable, edgesUpdatable, elementsSelectable, onError } = useStore(selector$c, shallow);
2413
+ function EdgeRendererComponent({ defaultMarkerColor, onlyRenderVisibleElements, rfId, edgeTypes, noPanClassName, onReconnect, onEdgeContextMenu, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, onEdgeClick, reconnectRadius, onEdgeDoubleClick, onReconnectStart, onReconnectEnd, disableKeyboardA11y, }) {
2414
+ const { edgesFocusable, edgesReconnectable, elementsSelectable, onError } = useStore(selector$b, shallow);
2288
2415
  const edgeIds = useVisibleEdgeIds(onlyRenderVisibleElements);
2289
2416
  return (jsxs("div", { className: "react-flow__edges", children: [jsx(MarkerDefinitions$1, { defaultColor: defaultMarkerColor, rfId: rfId }), edgeIds.map((id) => {
2290
- return (jsx(EdgeWrapper, { id: id, edgesFocusable: edgesFocusable, edgesUpdatable: edgesUpdatable, elementsSelectable: elementsSelectable, noPanClassName: noPanClassName, onEdgeUpdate: onEdgeUpdate, onContextMenu: onEdgeContextMenu, onMouseEnter: onEdgeMouseEnter, onMouseMove: onEdgeMouseMove, onMouseLeave: onEdgeMouseLeave, onClick: onEdgeClick, edgeUpdaterRadius: edgeUpdaterRadius, onDoubleClick: onEdgeDoubleClick, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, rfId: rfId, onError: onError, edgeTypes: edgeTypes }, id));
2417
+ return (jsx(EdgeWrapper, { id: id, edgesFocusable: edgesFocusable, edgesReconnectable: edgesReconnectable, elementsSelectable: elementsSelectable, noPanClassName: noPanClassName, onReconnect: onReconnect, onContextMenu: onEdgeContextMenu, onMouseEnter: onEdgeMouseEnter, onMouseMove: onEdgeMouseMove, onMouseLeave: onEdgeMouseLeave, onClick: onEdgeClick, reconnectRadius: reconnectRadius, onDoubleClick: onEdgeDoubleClick, onReconnectStart: onReconnectStart, onReconnectEnd: onReconnectEnd, rfId: rfId, onError: onError, edgeTypes: edgeTypes, disableKeyboardA11y: disableKeyboardA11y }, id));
2291
2418
  })] }));
2292
2419
  }
2293
2420
  EdgeRendererComponent.displayName = 'EdgeRenderer';
2294
2421
  const EdgeRenderer = memo(EdgeRendererComponent);
2295
2422
 
2296
- const selector$b = (s) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
2423
+ const selector$a = (s) => `translate(${s.transform[0]}px,${s.transform[1]}px) scale(${s.transform[2]})`;
2297
2424
  function Viewport({ children }) {
2298
- const transform = useStore(selector$b);
2425
+ const transform = useStore(selector$a);
2299
2426
  return (jsx("div", { className: "react-flow__viewport xyflow__viewport react-flow__container", style: { transform }, children: children }));
2300
2427
  }
2301
2428
 
@@ -2315,7 +2442,7 @@ function useOnInitHandler(onInit) {
2315
2442
  }, [onInit, rfInstance.viewportInitialized]);
2316
2443
  }
2317
2444
 
2318
- const selector$a = (state) => state.panZoom?.syncViewport;
2445
+ const selector$9 = (state) => state.panZoom?.syncViewport;
2319
2446
  /**
2320
2447
  * Hook for syncing the viewport with the panzoom instance.
2321
2448
  *
@@ -2323,7 +2450,7 @@ const selector$a = (state) => state.panZoom?.syncViewport;
2323
2450
  * @param viewport
2324
2451
  */
2325
2452
  function useViewportSync(viewport) {
2326
- const syncViewport = useStore(selector$a);
2453
+ const syncViewport = useStore(selector$9);
2327
2454
  const store = useStoreApi();
2328
2455
  useEffect(() => {
2329
2456
  if (viewport) {
@@ -2334,153 +2461,140 @@ function useViewportSync(viewport) {
2334
2461
  return null;
2335
2462
  }
2336
2463
 
2337
- const oppositePosition = {
2338
- [Position.Left]: Position.Right,
2339
- [Position.Right]: Position.Left,
2340
- [Position.Top]: Position.Bottom,
2341
- [Position.Bottom]: Position.Top,
2464
+ const selector$8 = (s) => {
2465
+ return s.connection.inProgress
2466
+ ? { ...s.connection, to: pointToRendererPoint(s.connection.to, s.transform) }
2467
+ : { ...s.connection };
2342
2468
  };
2343
- const ConnectionLine = ({ nodeId, handleType, style, type = ConnectionLineType.Bezier, CustomComponent, connectionStatus, }) => {
2344
- const { fromNode, handleId, toX, toY, connectionMode } = useStore(useCallback((s) => ({
2345
- fromNode: s.nodeLookup.get(nodeId),
2346
- handleId: s.connectionStartHandle?.handleId,
2347
- toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2],
2348
- toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
2349
- connectionMode: s.connectionMode,
2350
- }), [nodeId]), shallow);
2351
- const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
2352
- let handleBounds = fromHandleBounds?.[handleType];
2353
- if (connectionMode === ConnectionMode.Loose) {
2354
- handleBounds = handleBounds ? handleBounds : fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'];
2355
- }
2356
- if (!fromNode || !handleBounds) {
2469
+ /**
2470
+ * Hook for accessing the connection state.
2471
+ *
2472
+ * @public
2473
+ * @returns ConnectionState
2474
+ */
2475
+ function useConnection() {
2476
+ return useStore(selector$8, shallow);
2477
+ }
2478
+
2479
+ const selector$7 = (s) => ({
2480
+ nodesConnectable: s.nodesConnectable,
2481
+ isValid: s.connection.isValid,
2482
+ inProgress: s.connection.inProgress,
2483
+ width: s.width,
2484
+ height: s.height,
2485
+ });
2486
+ function ConnectionLineWrapper({ containerStyle, style, type, component }) {
2487
+ const { nodesConnectable, width, height, isValid, inProgress } = useStore(selector$7, shallow);
2488
+ const renderConnection = !!(width && nodesConnectable && inProgress);
2489
+ if (!renderConnection) {
2357
2490
  return null;
2358
2491
  }
2359
- const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
2360
- const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.computed?.width ?? 0) / 2;
2361
- const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.computed?.height ?? 0;
2362
- const fromX = (fromNode.computed?.positionAbsolute?.x ?? 0) + fromHandleX;
2363
- const fromY = (fromNode.computed?.positionAbsolute?.y ?? 0) + fromHandleY;
2364
- const fromPosition = fromHandle?.position;
2365
- const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
2366
- if (!fromPosition || !toPosition) {
2367
- return null;
2492
+ 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 }) }) }));
2493
+ }
2494
+ const ConnectionLine = ({ style, type = ConnectionLineType.Bezier, CustomComponent, isValid }) => {
2495
+ const { inProgress, from, fromNode, fromHandle, fromPosition, to, toNode, toHandle, toPosition } = useConnection();
2496
+ if (!inProgress) {
2497
+ return;
2368
2498
  }
2369
2499
  if (CustomComponent) {
2370
- return (jsx(CustomComponent, { connectionLineType: type, connectionLineStyle: style, fromNode: fromNode, fromHandle: fromHandle, fromX: fromX, fromY: fromY, toX: toX, toY: toY, fromPosition: fromPosition, toPosition: toPosition, connectionStatus: connectionStatus }));
2500
+ return (jsx(CustomComponent, { connectionLineType: type, connectionLineStyle: style, fromNode: fromNode, fromHandle: fromHandle, fromX: from.x, fromY: from.y, toX: to.x, toY: to.y, fromPosition: fromPosition, toPosition: toPosition, connectionStatus: getConnectionStatus(isValid), toNode: toNode, toHandle: toHandle }));
2371
2501
  }
2372
- let dAttr = '';
2502
+ let path = '';
2373
2503
  const pathParams = {
2374
- sourceX: fromX,
2375
- sourceY: fromY,
2504
+ sourceX: from.x,
2505
+ sourceY: from.y,
2376
2506
  sourcePosition: fromPosition,
2377
- targetX: toX,
2378
- targetY: toY,
2507
+ targetX: to.x,
2508
+ targetY: to.y,
2379
2509
  targetPosition: toPosition,
2380
2510
  };
2381
- if (type === ConnectionLineType.Bezier) {
2382
- // we assume the destination position is opposite to the source position
2383
- [dAttr] = getBezierPath(pathParams);
2384
- }
2385
- else if (type === ConnectionLineType.Step) {
2386
- [dAttr] = getSmoothStepPath({
2387
- ...pathParams,
2388
- borderRadius: 0,
2389
- });
2390
- }
2391
- else if (type === ConnectionLineType.SmoothStep) {
2392
- [dAttr] = getSmoothStepPath(pathParams);
2393
- }
2394
- else if (type === ConnectionLineType.SimpleBezier) {
2395
- [dAttr] = getSimpleBezierPath(pathParams);
2396
- }
2397
- else {
2398
- dAttr = `M${fromX},${fromY} ${toX},${toY}`;
2511
+ switch (type) {
2512
+ case ConnectionLineType.Bezier:
2513
+ [path] = getBezierPath(pathParams);
2514
+ break;
2515
+ case ConnectionLineType.SimpleBezier:
2516
+ [path] = getSimpleBezierPath(pathParams);
2517
+ break;
2518
+ case ConnectionLineType.Step:
2519
+ [path] = getSmoothStepPath({
2520
+ ...pathParams,
2521
+ borderRadius: 0,
2522
+ });
2523
+ break;
2524
+ case ConnectionLineType.SmoothStep:
2525
+ [path] = getSmoothStepPath(pathParams);
2526
+ break;
2527
+ default:
2528
+ [path] = getStraightPath(pathParams);
2399
2529
  }
2400
- return jsx("path", { d: dAttr, fill: "none", className: "react-flow__connection-path", style: style });
2530
+ return jsx("path", { d: path, fill: "none", className: "react-flow__connection-path", style: style });
2401
2531
  };
2402
2532
  ConnectionLine.displayName = 'ConnectionLine';
2403
- const selector$9 = (s) => ({
2404
- nodeId: s.connectionStartHandle?.nodeId,
2405
- handleType: s.connectionStartHandle?.type,
2406
- nodesConnectable: s.nodesConnectable,
2407
- connectionStatus: s.connectionStatus,
2408
- width: s.width,
2409
- height: s.height,
2410
- });
2411
- function ConnectionLineWrapper({ containerStyle, style, type, component }) {
2412
- const { nodeId, handleType, nodesConnectable, width, height, connectionStatus } = useStore(selector$9, shallow);
2413
- const isValid = !!(nodeId && handleType && width && nodesConnectable);
2414
- if (!isValid) {
2415
- return null;
2416
- }
2417
- return (jsx("svg", { style: containerStyle, width: width, height: height, className: "react-flow__connectionline react-flow__container", children: jsx("g", { className: cc(['react-flow__connection', connectionStatus]), children: jsx(ConnectionLine, { nodeId: nodeId, handleType: handleType, style: style, type: type, CustomComponent: component, connectionStatus: connectionStatus }) }) }));
2418
- }
2419
2533
 
2420
2534
  const emptyTypes = {};
2421
2535
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2422
2536
  function useNodeOrEdgeTypesWarning(nodeOrEdgeTypes = emptyTypes) {
2423
- const updateCount = useRef(0);
2537
+ const typesRef = useRef(nodeOrEdgeTypes);
2424
2538
  const store = useStoreApi();
2425
2539
  useEffect(() => {
2426
2540
  if (process.env.NODE_ENV === 'development') {
2427
- if (updateCount.current > 1) {
2428
- store.getState().onError?.('002', errorMessages['error002']());
2541
+ const usedKeys = new Set([...Object.keys(typesRef.current), ...Object.keys(nodeOrEdgeTypes)]);
2542
+ for (const key of usedKeys) {
2543
+ if (typesRef.current[key] !== nodeOrEdgeTypes[key]) {
2544
+ store.getState().onError?.('002', errorMessages['error002']());
2545
+ break;
2546
+ }
2429
2547
  }
2430
- updateCount.current += 1;
2548
+ typesRef.current = nodeOrEdgeTypes;
2431
2549
  }
2432
2550
  }, [nodeOrEdgeTypes]);
2433
2551
  }
2434
2552
 
2435
- function GraphViewComponent({ nodeTypes, edgeTypes, onInit, onNodeClick, onEdgeClick, onNodeDoubleClick, onEdgeDoubleClick, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onSelectionContextMenu, onSelectionStart, onSelectionEnd, connectionLineType, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, selectionKeyCode, selectionOnDrag, selectionMode, multiSelectionKeyCode, panActivationKeyCode, zoomActivationKeyCode, deleteKeyCode, onlyRenderVisibleElements, elementsSelectable, defaultViewport, translateExtent, minZoom, maxZoom, preventScrolling, defaultMarkerColor, zoomOnScroll, zoomOnPinch, panOnScroll, panOnScrollSpeed, panOnScrollMode, zoomOnDoubleClick, panOnDrag, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneScroll, onPaneContextMenu, onEdgeUpdate, onEdgeContextMenu, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, edgeUpdaterRadius, onEdgeUpdateStart, onEdgeUpdateEnd, noDragClassName, noWheelClassName, noPanClassName, disableKeyboardA11y, nodeOrigin, nodeExtent, rfId, viewport, onViewportChange, }) {
2553
+ function useStylesLoadedWarning() {
2554
+ const store = useStoreApi();
2555
+ const checked = useRef(false);
2556
+ useEffect(() => {
2557
+ if (process.env.NODE_ENV === 'development') {
2558
+ if (!checked.current) {
2559
+ const pane = document.querySelector('.react-flow__pane');
2560
+ if (pane && !(window.getComputedStyle(pane).zIndex === '1')) {
2561
+ store.getState().onError?.('013', errorMessages['error013']('react'));
2562
+ }
2563
+ checked.current = true;
2564
+ }
2565
+ }
2566
+ }, []);
2567
+ }
2568
+
2569
+ function GraphViewComponent({ nodeTypes, edgeTypes, onInit, onNodeClick, onEdgeClick, onNodeDoubleClick, onEdgeDoubleClick, onNodeMouseEnter, onNodeMouseMove, onNodeMouseLeave, onNodeContextMenu, onSelectionContextMenu, onSelectionStart, onSelectionEnd, connectionLineType, connectionLineStyle, connectionLineComponent, connectionLineContainerStyle, selectionKeyCode, selectionOnDrag, selectionMode, multiSelectionKeyCode, panActivationKeyCode, zoomActivationKeyCode, deleteKeyCode, onlyRenderVisibleElements, elementsSelectable, defaultViewport, translateExtent, minZoom, maxZoom, preventScrolling, defaultMarkerColor, zoomOnScroll, zoomOnPinch, panOnScroll, panOnScrollSpeed, panOnScrollMode, zoomOnDoubleClick, panOnDrag, onPaneClick, onPaneMouseEnter, onPaneMouseMove, onPaneMouseLeave, onPaneScroll, onPaneContextMenu, paneClickDistance, onEdgeContextMenu, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, reconnectRadius, onReconnect, onReconnectStart, onReconnectEnd, noDragClassName, noWheelClassName, noPanClassName, disableKeyboardA11y, nodeExtent, rfId, viewport, onViewportChange, }) {
2436
2570
  useNodeOrEdgeTypesWarning(nodeTypes);
2437
2571
  useNodeOrEdgeTypesWarning(edgeTypes);
2572
+ useStylesLoadedWarning();
2438
2573
  useOnInitHandler(onInit);
2439
2574
  useViewportSync(viewport);
2440
- return (jsx(FlowRenderer, { onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneContextMenu: onPaneContextMenu, onPaneScroll: onPaneScroll, deleteKeyCode: deleteKeyCode, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, elementsSelectable: elementsSelectable, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, onSelectionContextMenu: onSelectionContextMenu, preventScrolling: preventScrolling, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y, onViewportChange: onViewportChange, isControlledViewport: !!viewport, children: jsxs(Viewport, { children: [jsx(EdgeRenderer, { edgeTypes: edgeTypes, onEdgeClick: onEdgeClick, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeUpdate: onEdgeUpdate, onlyRenderVisibleElements: onlyRenderVisibleElements, onEdgeContextMenu: onEdgeContextMenu, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, edgeUpdaterRadius: edgeUpdaterRadius, defaultMarkerColor: defaultMarkerColor, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y, rfId: rfId }), jsx(ConnectionLineWrapper, { style: connectionLineStyle, type: connectionLineType, component: connectionLineComponent, containerStyle: connectionLineContainerStyle }), jsx("div", { className: "react-flow__edgelabel-renderer" }), jsx("div", { className: "react-flow__viewport-portal" }), jsx(NodeRenderer, { nodeTypes: nodeTypes, onNodeClick: onNodeClick, onNodeDoubleClick: onNodeDoubleClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onlyRenderVisibleElements: onlyRenderVisibleElements, noPanClassName: noPanClassName, noDragClassName: noDragClassName, disableKeyboardA11y: disableKeyboardA11y, nodeOrigin: nodeOrigin, nodeExtent: nodeExtent, rfId: rfId })] }) }));
2575
+ return (jsx(FlowRenderer, { onPaneClick: onPaneClick, onPaneMouseEnter: onPaneMouseEnter, onPaneMouseMove: onPaneMouseMove, onPaneMouseLeave: onPaneMouseLeave, onPaneContextMenu: onPaneContextMenu, onPaneScroll: onPaneScroll, paneClickDistance: paneClickDistance, deleteKeyCode: deleteKeyCode, selectionKeyCode: selectionKeyCode, selectionOnDrag: selectionOnDrag, selectionMode: selectionMode, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, multiSelectionKeyCode: multiSelectionKeyCode, panActivationKeyCode: panActivationKeyCode, zoomActivationKeyCode: zoomActivationKeyCode, elementsSelectable: elementsSelectable, zoomOnScroll: zoomOnScroll, zoomOnPinch: zoomOnPinch, zoomOnDoubleClick: zoomOnDoubleClick, panOnScroll: panOnScroll, panOnScrollSpeed: panOnScrollSpeed, panOnScrollMode: panOnScrollMode, panOnDrag: panOnDrag, defaultViewport: defaultViewport, translateExtent: translateExtent, minZoom: minZoom, maxZoom: maxZoom, onSelectionContextMenu: onSelectionContextMenu, preventScrolling: preventScrolling, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y, onViewportChange: onViewportChange, isControlledViewport: !!viewport, children: jsxs(Viewport, { children: [jsx(EdgeRenderer, { edgeTypes: edgeTypes, onEdgeClick: onEdgeClick, onEdgeDoubleClick: onEdgeDoubleClick, onReconnect: onReconnect, onReconnectStart: onReconnectStart, onReconnectEnd: onReconnectEnd, onlyRenderVisibleElements: onlyRenderVisibleElements, onEdgeContextMenu: onEdgeContextMenu, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, reconnectRadius: reconnectRadius, defaultMarkerColor: defaultMarkerColor, noPanClassName: noPanClassName, disableKeyboardA11y: disableKeyboardA11y, rfId: rfId }), jsx(ConnectionLineWrapper, { style: connectionLineStyle, type: connectionLineType, component: connectionLineComponent, containerStyle: connectionLineContainerStyle }), jsx("div", { className: "react-flow__edgelabel-renderer" }), jsx(NodeRenderer, { nodeTypes: nodeTypes, onNodeClick: onNodeClick, onNodeDoubleClick: onNodeDoubleClick, onNodeMouseEnter: onNodeMouseEnter, onNodeMouseMove: onNodeMouseMove, onNodeMouseLeave: onNodeMouseLeave, onNodeContextMenu: onNodeContextMenu, onlyRenderVisibleElements: onlyRenderVisibleElements, noPanClassName: noPanClassName, noDragClassName: noDragClassName, disableKeyboardA11y: disableKeyboardA11y, nodeExtent: nodeExtent, rfId: rfId }), jsx("div", { className: "react-flow__viewport-portal" })] }) }));
2441
2576
  }
2442
2577
  GraphViewComponent.displayName = 'GraphView';
2443
2578
  const GraphView = memo(GraphViewComponent);
2444
2579
 
2445
- function handleControlledSelectionChange(changes, items) {
2446
- return items.map((item) => {
2447
- const change = changes.find((change) => change.id === item.id);
2448
- if (change) {
2449
- item.selected = change.selected;
2450
- }
2451
- return item;
2452
- });
2453
- }
2454
- function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }) {
2455
- const { nodes, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
2456
- if (changedNodes?.length) {
2457
- if (hasDefaultNodes) {
2458
- set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
2459
- }
2460
- onNodesChange?.(changedNodes);
2461
- }
2462
- if (changedEdges?.length) {
2463
- if (hasDefaultEdges) {
2464
- set({ edges: handleControlledSelectionChange(changedEdges, edges) });
2465
- }
2466
- onEdgesChange?.(changedEdges);
2467
- }
2468
- }
2469
-
2470
- const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {}) => {
2580
+ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView, nodeOrigin, } = {}) => {
2471
2581
  const nodeLookup = new Map();
2582
+ const parentLookup = new Map();
2472
2583
  const connectionLookup = new Map();
2473
2584
  const edgeLookup = new Map();
2474
- updateConnectionLookup(connectionLookup, edgeLookup, edges);
2475
- const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
2476
- nodeOrigin: [0, 0],
2585
+ const storeEdges = defaultEdges ?? edges ?? [];
2586
+ const storeNodes = defaultNodes ?? nodes ?? [];
2587
+ const storeNodeOrigin = nodeOrigin ?? [0, 0];
2588
+ updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
2589
+ adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
2590
+ nodeOrigin: storeNodeOrigin,
2477
2591
  elevateNodesOnSelect: false,
2478
2592
  });
2479
2593
  let transform = [0, 0, 1];
2480
2594
  if (fitView && width && height) {
2481
- const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height);
2482
- // @todo users nodeOrigin should be used here
2483
- const bounds = getNodesBounds(nodesWithDimensions, { nodeOrigin: [0, 0] });
2595
+ const bounds = getInternalNodesBounds(nodeLookup, {
2596
+ filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
2597
+ });
2484
2598
  const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
2485
2599
  transform = [x, y, zoom];
2486
2600
  }
@@ -2489,15 +2603,16 @@ const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {
2489
2603
  width: 0,
2490
2604
  height: 0,
2491
2605
  transform,
2492
- nodes: nextNodes,
2606
+ nodes: storeNodes,
2493
2607
  nodeLookup,
2494
- edges,
2608
+ parentLookup,
2609
+ edges: storeEdges,
2495
2610
  edgeLookup,
2496
2611
  connectionLookup,
2497
2612
  onNodesChange: null,
2498
2613
  onEdgesChange: null,
2499
- hasDefaultNodes: false,
2500
- hasDefaultEdges: false,
2614
+ hasDefaultNodes: defaultNodes !== undefined,
2615
+ hasDefaultEdges: defaultEdges !== undefined,
2501
2616
  panZoom: null,
2502
2617
  minZoom: 0.5,
2503
2618
  maxZoom: 2,
@@ -2506,13 +2621,11 @@ const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {
2506
2621
  nodesSelectionActive: false,
2507
2622
  userSelectionActive: false,
2508
2623
  userSelectionRect: null,
2509
- connectionPosition: { x: 0, y: 0 },
2510
- connectionStatus: null,
2511
2624
  connectionMode: ConnectionMode.Strict,
2512
2625
  domNode: null,
2513
2626
  paneDragging: false,
2514
2627
  noPanClassName: 'nopan',
2515
- nodeOrigin: [0, 0],
2628
+ nodeOrigin: storeNodeOrigin,
2516
2629
  nodeDragThreshold: 1,
2517
2630
  snapGrid: [15, 15],
2518
2631
  snapToGrid: false,
@@ -2520,7 +2633,7 @@ const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {
2520
2633
  nodesConnectable: true,
2521
2634
  nodesFocusable: true,
2522
2635
  edgesFocusable: true,
2523
- edgesUpdatable: true,
2636
+ edgesReconnectable: true,
2524
2637
  elementsSelectable: true,
2525
2638
  elevateNodesOnSelect: true,
2526
2639
  elevateEdgesOnSelect: false,
@@ -2529,86 +2642,68 @@ const getInitialState = ({ nodes = [], edges = [], width, height, fitView, } = {
2529
2642
  fitViewOnInitOptions: undefined,
2530
2643
  selectNodesOnDrag: true,
2531
2644
  multiSelectionActive: false,
2532
- connectionStartHandle: null,
2533
- connectionEndHandle: null,
2645
+ connection: { ...initialConnection },
2534
2646
  connectionClickStartHandle: null,
2535
2647
  connectOnClick: true,
2536
2648
  ariaLiveMessage: '',
2537
2649
  autoPanOnConnect: true,
2538
2650
  autoPanOnNodeDrag: true,
2651
+ autoPanSpeed: 15,
2539
2652
  connectionRadius: 20,
2540
2653
  onError: devWarn,
2541
2654
  isValidConnection: undefined,
2542
2655
  onSelectionChangeHandlers: [],
2543
2656
  lib: 'react',
2657
+ debug: false,
2544
2658
  };
2545
2659
  };
2546
2660
 
2547
- const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) => createWithEqualityFn((set, get) => ({
2548
- ...getInitialState({ nodes, edges, width, height, fitView: fitView$1 }),
2661
+ const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView: fitView$1, nodeOrigin, }) => createWithEqualityFn((set, get) => ({
2662
+ ...getInitialState({ nodes, edges, width, height, fitView: fitView$1, nodeOrigin, defaultNodes, defaultEdges }),
2549
2663
  setNodes: (nodes) => {
2550
- const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
2664
+ const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
2551
2665
  // setNodes() is called exclusively in response to user actions:
2552
2666
  // - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
2553
2667
  // - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
2554
2668
  //
2555
2669
  // When this happens, we take the note objects passed by the user and extend them with fields
2556
2670
  // relevant for internal React Flow operations.
2557
- // TODO: consider updating the types to reflect the distinction between user-provided nodes and internal nodes.
2558
- const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
2559
- set({ nodes: nodesWithInternalData });
2671
+ adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, elevateNodesOnSelect, checkEquality: true });
2672
+ set({ nodes });
2560
2673
  },
2561
2674
  setEdges: (edges) => {
2562
2675
  const { connectionLookup, edgeLookup } = get();
2563
2676
  updateConnectionLookup(connectionLookup, edgeLookup, edges);
2564
2677
  set({ edges });
2565
2678
  },
2566
- // when the user works with an uncontrolled flow,
2567
- // we set a flag `hasDefaultNodes` / `hasDefaultEdges`
2568
2679
  setDefaultNodesAndEdges: (nodes, edges) => {
2569
- const hasDefaultNodes = typeof nodes !== 'undefined';
2570
- const hasDefaultEdges = typeof edges !== 'undefined';
2571
- const nextState = {
2572
- hasDefaultNodes,
2573
- hasDefaultEdges,
2574
- };
2575
- if (hasDefaultNodes) {
2576
- const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
2577
- nextState.nodes = adoptUserProvidedNodes(nodes, nodeLookup, {
2578
- nodeOrigin,
2579
- elevateNodesOnSelect,
2580
- });
2680
+ if (nodes) {
2681
+ const { setNodes } = get();
2682
+ setNodes(nodes);
2683
+ set({ hasDefaultNodes: true });
2581
2684
  }
2582
- if (hasDefaultEdges) {
2583
- const { connectionLookup, edgeLookup } = get();
2584
- updateConnectionLookup(connectionLookup, edgeLookup, edges);
2585
- nextState.edges = edges;
2685
+ if (edges) {
2686
+ const { setEdges } = get();
2687
+ setEdges(edges);
2688
+ set({ hasDefaultEdges: true });
2586
2689
  }
2587
- set(nextState);
2588
2690
  },
2589
2691
  // Every node gets registerd at a ResizeObserver. Whenever a node
2590
2692
  // changes its dimensions, this function is called to measure the
2591
2693
  // new dimensions and update the nodes.
2592
- updateNodeDimensions: (updates) => {
2593
- const { onNodesChange, fitView, nodes, nodeLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, } = get();
2594
- const changes = [];
2595
- const updatedNodes = updateNodeDimensions(updates, nodes, nodeLookup, domNode, nodeOrigin, (id, dimensions) => {
2596
- changes.push({
2597
- id: id,
2598
- type: 'dimensions',
2599
- dimensions,
2600
- });
2601
- });
2602
- if (!updatedNodes) {
2694
+ updateNodeInternals: (updates) => {
2695
+ const { triggerNodeChanges, nodeLookup, parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, fitViewSync, } = get();
2696
+ const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin);
2697
+ if (!updatedInternals) {
2603
2698
  return;
2604
2699
  }
2605
- const nextNodes = updateAbsolutePositions(updatedNodes, nodeLookup, nodeOrigin);
2700
+ updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin });
2606
2701
  // we call fitView once initially after all dimensions are set
2607
2702
  let nextFitViewDone = fitViewDone;
2608
2703
  if (!fitViewDone && fitViewOnInit) {
2609
- nextFitViewDone = fitView(nextNodes, {
2704
+ nextFitViewDone = fitViewSync({
2610
2705
  ...fitViewOnInitOptions,
2611
- nodes: fitViewOnInitOptions?.nodes || nextNodes,
2706
+ nodes: fitViewOnInitOptions?.nodes,
2612
2707
  });
2613
2708
  }
2614
2709
  // here we are cirmumventing the onNodesChange handler
@@ -2616,91 +2711,103 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
2616
2711
  // has not provided an onNodesChange handler.
2617
2712
  // Nodes are only rendered if they have a width and height
2618
2713
  // attribute which they get from this handler.
2619
- set({ nodes: nextNodes, fitViewDone: nextFitViewDone });
2714
+ set({ fitViewDone: nextFitViewDone });
2620
2715
  if (changes?.length > 0) {
2621
- onNodesChange?.(changes);
2716
+ if (debug) {
2717
+ console.log('React Flow: trigger node changes', changes);
2718
+ }
2719
+ triggerNodeChanges?.(changes);
2622
2720
  }
2623
2721
  },
2624
- updateNodePositions: (nodeDragItems, positionChanged = true, dragging = false) => {
2625
- const changes = nodeDragItems.map((node) => {
2722
+ updateNodePositions: (nodeDragItems, dragging = false) => {
2723
+ const parentExpandChildren = [];
2724
+ const changes = [];
2725
+ for (const [id, dragItem] of nodeDragItems) {
2626
2726
  const change = {
2627
- id: node.id,
2727
+ id,
2628
2728
  type: 'position',
2729
+ position: dragItem.position,
2629
2730
  dragging,
2630
2731
  };
2631
- if (positionChanged) {
2632
- change.positionAbsolute = node.computed?.positionAbsolute;
2633
- change.position = node.position;
2732
+ if (dragItem?.expandParent && dragItem?.parentId && change.position) {
2733
+ parentExpandChildren.push({
2734
+ id,
2735
+ parentId: dragItem.parentId,
2736
+ rect: {
2737
+ ...dragItem.internals.positionAbsolute,
2738
+ width: dragItem.measured.width,
2739
+ height: dragItem.measured.height,
2740
+ },
2741
+ });
2742
+ change.position.x = Math.max(0, change.position.x);
2743
+ change.position.y = Math.max(0, change.position.y);
2634
2744
  }
2635
- return change;
2636
- });
2745
+ changes.push(change);
2746
+ }
2747
+ if (parentExpandChildren.length > 0) {
2748
+ const { nodeLookup, parentLookup, nodeOrigin } = get();
2749
+ const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup, nodeOrigin);
2750
+ changes.push(...parentExpandChanges);
2751
+ }
2637
2752
  get().triggerNodeChanges(changes);
2638
2753
  },
2639
2754
  triggerNodeChanges: (changes) => {
2640
- const { onNodesChange, nodeLookup, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get();
2755
+ const { onNodesChange, setNodes, nodes, hasDefaultNodes, debug } = get();
2641
2756
  if (changes?.length) {
2642
2757
  if (hasDefaultNodes) {
2643
2758
  const updatedNodes = applyNodeChanges(changes, nodes);
2644
- const nextNodes = adoptUserProvidedNodes(updatedNodes, nodeLookup, {
2645
- nodeOrigin,
2646
- elevateNodesOnSelect,
2647
- });
2648
- set({ nodes: nextNodes });
2759
+ setNodes(updatedNodes);
2760
+ }
2761
+ if (debug) {
2762
+ console.log('React Flow: trigger node changes', changes);
2649
2763
  }
2650
2764
  onNodesChange?.(changes);
2651
2765
  }
2652
2766
  },
2767
+ triggerEdgeChanges: (changes) => {
2768
+ const { onEdgesChange, setEdges, edges, hasDefaultEdges, debug } = get();
2769
+ if (changes?.length) {
2770
+ if (hasDefaultEdges) {
2771
+ const updatedEdges = applyEdgeChanges(changes, edges);
2772
+ setEdges(updatedEdges);
2773
+ }
2774
+ if (debug) {
2775
+ console.log('React Flow: trigger edge changes', changes);
2776
+ }
2777
+ onEdgesChange?.(changes);
2778
+ }
2779
+ },
2653
2780
  addSelectedNodes: (selectedNodeIds) => {
2654
- const { multiSelectionActive, edges, nodes } = get();
2655
- let changedNodes;
2656
- let changedEdges = null;
2781
+ const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
2657
2782
  if (multiSelectionActive) {
2658
- changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
2783
+ const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
2784
+ triggerNodeChanges(nodeChanges);
2785
+ return;
2659
2786
  }
2660
- else {
2661
- changedNodes = getSelectionChanges(nodes, new Set([...selectedNodeIds]), true);
2662
- changedEdges = getSelectionChanges(edges);
2663
- }
2664
- updateNodesAndEdgesSelections({
2665
- changedNodes,
2666
- changedEdges,
2667
- get,
2668
- set,
2669
- });
2787
+ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set([...selectedNodeIds]), true));
2788
+ triggerEdgeChanges(getSelectionChanges(edgeLookup));
2670
2789
  },
2671
2790
  addSelectedEdges: (selectedEdgeIds) => {
2672
- const { multiSelectionActive, edges, nodes } = get();
2673
- let changedEdges;
2674
- let changedNodes = null;
2791
+ const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
2675
2792
  if (multiSelectionActive) {
2676
- changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
2793
+ const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
2794
+ triggerEdgeChanges(changedEdges);
2795
+ return;
2677
2796
  }
2678
- else {
2679
- changedEdges = getSelectionChanges(edges, new Set([...selectedEdgeIds]));
2680
- changedNodes = getSelectionChanges(nodes, new Set(), true);
2681
- }
2682
- updateNodesAndEdgesSelections({
2683
- changedNodes,
2684
- changedEdges,
2685
- get,
2686
- set,
2687
- });
2797
+ triggerEdgeChanges(getSelectionChanges(edgeLookup, new Set([...selectedEdgeIds])));
2798
+ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set(), true));
2688
2799
  },
2689
2800
  unselectNodesAndEdges: ({ nodes, edges } = {}) => {
2690
- const { edges: storeEdges, nodes: storeNodes } = get();
2801
+ const { edges: storeEdges, nodes: storeNodes, triggerNodeChanges, triggerEdgeChanges } = get();
2691
2802
  const nodesToUnselect = nodes ? nodes : storeNodes;
2692
2803
  const edgesToUnselect = edges ? edges : storeEdges;
2693
- const changedNodes = nodesToUnselect.map((n) => {
2804
+ const nodeChanges = nodesToUnselect.map((n) => {
2694
2805
  n.selected = false;
2695
2806
  return createSelectionChange(n.id, false);
2696
2807
  });
2697
- const changedEdges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
2698
- updateNodesAndEdgesSelections({
2699
- changedNodes,
2700
- changedEdges,
2701
- get,
2702
- set,
2703
- });
2808
+ const edgeChanges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
2809
+ triggerNodeChanges(nodeChanges);
2810
+ triggerEdgeChanges(edgeChanges);
2704
2811
  },
2705
2812
  setMinZoom: (minZoom) => {
2706
2813
  const { panZoom, maxZoom } = get();
@@ -2716,106 +2823,104 @@ const createRFStore = ({ nodes, edges, width, height, fitView: fitView$1, }) =>
2716
2823
  get().panZoom?.setTranslateExtent(translateExtent);
2717
2824
  set({ translateExtent });
2718
2825
  },
2826
+ setPaneClickDistance: (clickDistance) => {
2827
+ get().panZoom?.setClickDistance(clickDistance);
2828
+ },
2719
2829
  resetSelectedElements: () => {
2720
- const { edges, nodes } = get();
2721
- const nodesToUnselect = nodes
2722
- .filter((e) => e.selected)
2723
- .map((n) => createSelectionChange(n.id, false));
2724
- const edgesToUnselect = edges
2725
- .filter((e) => e.selected)
2726
- .map((e) => createSelectionChange(e.id, false));
2727
- updateNodesAndEdgesSelections({
2728
- changedNodes: nodesToUnselect,
2729
- changedEdges: edgesToUnselect,
2730
- get,
2731
- set,
2732
- });
2830
+ const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
2831
+ const nodeChanges = nodes.reduce((res, node) => (node.selected ? [...res, createSelectionChange(node.id, false)] : res), []);
2832
+ const edgeChanges = edges.reduce((res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false)] : res), []);
2833
+ triggerNodeChanges(nodeChanges);
2834
+ triggerEdgeChanges(edgeChanges);
2733
2835
  },
2734
2836
  setNodeExtent: (nodeExtent) => {
2735
- const { nodes } = get();
2837
+ const { nodeLookup } = get();
2838
+ for (const [, node] of nodeLookup) {
2839
+ const positionAbsolute = clampPosition(node.position, nodeExtent);
2840
+ nodeLookup.set(node.id, {
2841
+ ...node,
2842
+ internals: {
2843
+ ...node.internals,
2844
+ positionAbsolute,
2845
+ },
2846
+ });
2847
+ }
2736
2848
  set({
2737
2849
  nodeExtent,
2738
- nodes: nodes.map((node) => {
2739
- const positionAbsolute = clampPosition(node.position, nodeExtent);
2740
- return {
2741
- ...node,
2742
- computed: {
2743
- ...node.computed,
2744
- positionAbsolute,
2745
- },
2746
- };
2747
- }),
2748
2850
  });
2749
2851
  },
2750
2852
  panBy: (delta) => {
2751
2853
  const { transform, width, height, panZoom, translateExtent } = get();
2752
2854
  return panBy({ delta, panZoom, transform, translateExtent, width, height });
2753
2855
  },
2754
- fitView: (nodes, options) => {
2755
- const { panZoom, width, height, minZoom, maxZoom, nodeOrigin } = get();
2856
+ fitView: (options) => {
2857
+ const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
2756
2858
  if (!panZoom) {
2757
- return false;
2859
+ return Promise.resolve(false);
2758
2860
  }
2861
+ const fitViewNodes = getFitViewNodes(nodeLookup, options);
2759
2862
  return fitView({
2760
- nodes,
2863
+ nodes: fitViewNodes,
2761
2864
  width,
2762
2865
  height,
2763
2866
  panZoom,
2764
2867
  minZoom,
2765
2868
  maxZoom,
2766
- nodeOrigin,
2767
2869
  }, options);
2768
2870
  },
2769
- cancelConnection: () => set({
2770
- connectionStatus: null,
2771
- connectionStartHandle: null,
2772
- connectionEndHandle: null,
2773
- }),
2774
- updateConnection: (params) => {
2775
- const { connectionStatus, connectionStartHandle, connectionEndHandle, connectionPosition } = get();
2776
- const currentConnection = {
2777
- connectionPosition: params.connectionPosition ?? connectionPosition,
2778
- connectionStatus: params.connectionStatus ?? connectionStatus,
2779
- connectionStartHandle: params.connectionStartHandle ?? connectionStartHandle,
2780
- connectionEndHandle: params.connectionEndHandle ?? connectionEndHandle,
2781
- };
2782
- set(currentConnection);
2871
+ // we can't call an asnychronous function in updateNodeInternals
2872
+ // for that we created this sync version of fitView
2873
+ fitViewSync: (options) => {
2874
+ const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
2875
+ if (!panZoom) {
2876
+ return false;
2877
+ }
2878
+ const fitViewNodes = getFitViewNodes(nodeLookup, options);
2879
+ fitView({
2880
+ nodes: fitViewNodes,
2881
+ width,
2882
+ height,
2883
+ panZoom,
2884
+ minZoom,
2885
+ maxZoom,
2886
+ }, options);
2887
+ return fitViewNodes.size > 0;
2783
2888
  },
2784
- reset: () => {
2785
- // @todo: what should we do about this? Do we still need it?
2786
- // if you are on a SPA with multiple flows, we want to make sure that the store gets resetted
2787
- // when you switch pages. Does this reset solves this? Currently it always gets called. This
2788
- // leads to an emtpy nodes array at the beginning.
2789
- // set({ ...getInitialState() });
2889
+ cancelConnection: () => {
2890
+ set({
2891
+ connection: { ...initialConnection },
2892
+ });
2893
+ },
2894
+ updateConnection: (connection) => {
2895
+ set({ connection });
2790
2896
  },
2897
+ reset: () => set({ ...getInitialState() }),
2791
2898
  }), Object.is);
2792
2899
 
2793
- function ReactFlowProvider({ children, initialNodes, initialEdges, initialWidth, initialHeight, fitView, }) {
2794
- const storeRef = useRef(null);
2795
- if (!storeRef.current) {
2796
- storeRef.current = createRFStore({
2797
- nodes: initialNodes,
2798
- edges: initialEdges,
2799
- width: initialWidth,
2800
- height: initialHeight,
2801
- fitView,
2802
- });
2803
- }
2804
- return jsx(Provider$1, { value: storeRef.current, children: children });
2900
+ function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNodes, defaultEdges, initialWidth: width, initialHeight: height, fitView, nodeOrigin, children, }) {
2901
+ const [store] = useState(() => createStore({
2902
+ nodes,
2903
+ edges,
2904
+ defaultNodes,
2905
+ defaultEdges,
2906
+ width,
2907
+ height,
2908
+ fitView,
2909
+ nodeOrigin,
2910
+ }));
2911
+ return (jsx(Provider$1, { value: store, children: jsx(BatchProvider, { children: children }) }));
2805
2912
  }
2806
2913
 
2807
- function Wrapper({ children, nodes, edges, width, height, fitView, }) {
2914
+ function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, nodeOrigin, }) {
2808
2915
  const isWrapped = useContext(StoreContext);
2809
2916
  if (isWrapped) {
2810
2917
  // we need to wrap it with a fragment because it's not allowed for children to be a ReactNode
2811
2918
  // https://github.com/DefinitelyTyped/DefinitelyTyped/issues/18051
2812
2919
  return jsx(Fragment, { children: children });
2813
2920
  }
2814
- return (jsx(ReactFlowProvider, { initialNodes: nodes, initialEdges: edges, initialWidth: width, initialHeight: height, fitView: fitView, children: children }));
2921
+ return (jsx(ReactFlowProvider, { initialNodes: nodes, initialEdges: edges, defaultNodes: defaultNodes, defaultEdges: defaultEdges, initialWidth: width, initialHeight: height, fitView: fitView, nodeOrigin: nodeOrigin, children: children }));
2815
2922
  }
2816
2923
 
2817
- const initNodeOrigin = [0, 0];
2818
- const initDefaultViewport = { x: 0, y: 0, zoom: 1 };
2819
2924
  const wrapperStyle = {
2820
2925
  width: '100%',
2821
2926
  height: '100%',
@@ -2823,25 +2928,25 @@ const wrapperStyle = {
2823
2928
  position: 'relative',
2824
2929
  zIndex: 0,
2825
2930
  };
2826
- const ReactFlow = forwardRef(({ 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 = initNodeOrigin, edgesFocusable, edgesUpdatable, elementsSelectable = true, defaultViewport = initDefaultViewport, 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, children, onEdgeUpdate, onEdgeContextMenu, onEdgeDoubleClick, onEdgeMouseEnter, onEdgeMouseMove, onEdgeMouseLeave, onEdgeUpdateStart, onEdgeUpdateEnd, edgeUpdaterRadius = 10, onNodesChange, onEdgesChange, noDragClassName = 'nodrag', noWheelClassName = 'nowheel', noPanClassName = 'nopan', fitView, fitViewOptions, connectOnClick, attributionPosition, proOptions, defaultEdgeOptions, elevateNodesOnSelect, elevateEdgesOnSelect, disableKeyboardA11y = false, autoPanOnConnect, autoPanOnNodeDrag, connectionRadius, isValidConnection, onError, style, id, nodeDragThreshold, viewport, onViewportChange, width, height, colorMode = 'light', ...rest }, ref) => {
2931
+ 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, 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) {
2827
2932
  const rfId = id || '1';
2828
2933
  const colorModeClassName = useColorModeClass(colorMode);
2829
- return (jsx("div", { ...rest, style: { ...style, ...wrapperStyle }, ref: ref, className: cc(['react-flow', className, colorModeClassName]), "data-testid": "rf__wrapper", id: id, children: jsxs(Wrapper, { nodes: nodes, edges: edges, width: width, height: height, fitView: fitView, 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, 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, onSelectionContextMenu: onSelectionContextMenu, onSelectionStart: onSelectionStart, onSelectionEnd: onSelectionEnd, onEdgeUpdate: onEdgeUpdate, onEdgeContextMenu: onEdgeContextMenu, onEdgeDoubleClick: onEdgeDoubleClick, onEdgeMouseEnter: onEdgeMouseEnter, onEdgeMouseMove: onEdgeMouseMove, onEdgeMouseLeave: onEdgeMouseLeave, onEdgeUpdateStart: onEdgeUpdateStart, onEdgeUpdateEnd: onEdgeUpdateEnd, edgeUpdaterRadius: edgeUpdaterRadius, defaultMarkerColor: defaultMarkerColor, noDragClassName: noDragClassName, noWheelClassName: noWheelClassName, noPanClassName: noPanClassName, rfId: rfId, disableKeyboardA11y: disableKeyboardA11y, nodeOrigin: nodeOrigin, 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, edgesUpdatable: edgesUpdatable, 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, onError: onError, connectionRadius: connectionRadius, isValidConnection: isValidConnection, selectNodesOnDrag: selectNodesOnDrag, nodeDragThreshold: nodeDragThreshold, onBeforeDelete: onBeforeDelete }), jsx(SelectionListener, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
2830
- });
2831
- ReactFlow.displayName = 'ReactFlow';
2934
+ return (jsx("div", { ...rest, style: { ...style, ...wrapperStyle }, ref: ref, className: cc(['react-flow', className, colorModeClassName]), "data-testid": "rf__wrapper", id: id, children: jsxs(Wrapper, { nodes: nodes, edges: edges, width: width, height: height, fitView: fitView, nodeOrigin: nodeOrigin, 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, 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 })] }) }));
2935
+ }
2936
+ var index = fixedForwardRef(ReactFlow);
2832
2937
 
2833
- const selector$8 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
2938
+ const selector$6 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
2834
2939
  function EdgeLabelRenderer({ children }) {
2835
- const edgeLabelRenderer = useStore(selector$8);
2940
+ const edgeLabelRenderer = useStore(selector$6);
2836
2941
  if (!edgeLabelRenderer) {
2837
2942
  return null;
2838
2943
  }
2839
2944
  return createPortal(children, edgeLabelRenderer);
2840
2945
  }
2841
2946
 
2842
- const selector$7 = (s) => s.domNode?.querySelector('.react-flow__viewport-portal');
2947
+ const selector$5 = (s) => s.domNode?.querySelector('.react-flow__viewport-portal');
2843
2948
  function ViewportPortal({ children }) {
2844
- const viewPortalDiv = useStore(selector$7);
2949
+ const viewPortalDiv = useStore(selector$5);
2845
2950
  if (!viewPortalDiv) {
2846
2951
  return null;
2847
2952
  }
@@ -2857,16 +2962,16 @@ function ViewportPortal({ children }) {
2857
2962
  function useUpdateNodeInternals() {
2858
2963
  const store = useStoreApi();
2859
2964
  return useCallback((id) => {
2860
- const { domNode, updateNodeDimensions } = store.getState();
2965
+ const { domNode, updateNodeInternals } = store.getState();
2861
2966
  const updateIds = Array.isArray(id) ? id : [id];
2862
2967
  const updates = new Map();
2863
2968
  updateIds.forEach((updateId) => {
2864
2969
  const nodeElement = domNode?.querySelector(`.react-flow__node[data-id="${updateId}"]`);
2865
2970
  if (nodeElement) {
2866
- updates.set(updateId, { id: updateId, nodeElement, forceUpdate: true });
2971
+ updates.set(updateId, { id: updateId, nodeElement, force: true });
2867
2972
  }
2868
2973
  });
2869
- requestAnimationFrame(() => updateNodeDimensions(updates));
2974
+ requestAnimationFrame(() => updateNodeInternals(updates));
2870
2975
  }, []);
2871
2976
  }
2872
2977
 
@@ -2974,13 +3079,13 @@ function useOnSelectionChange({ onChange }) {
2974
3079
  }, [onChange]);
2975
3080
  }
2976
3081
 
2977
- const selector$6 = (options) => (s) => {
2978
- if (s.nodes.length === 0) {
3082
+ const selector$4 = (options) => (s) => {
3083
+ if (s.nodeLookup.size === 0) {
2979
3084
  return false;
2980
3085
  }
2981
- for (const node of s.nodes) {
2982
- if (options.includeHiddenNodes || !node.hidden) {
2983
- if (node[internalsSymbol]?.handleBounds === undefined) {
3086
+ for (const [, { hidden, internals }] of s.nodeLookup) {
3087
+ if (options.includeHiddenNodes || !hidden) {
3088
+ if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) {
2984
3089
  return false;
2985
3090
  }
2986
3091
  }
@@ -2998,7 +3103,7 @@ const defaultOptions = {
2998
3103
  * @returns boolean indicating whether all nodes are initialized
2999
3104
  */
3000
3105
  function useNodesInitialized(options = defaultOptions) {
3001
- const initialized = useStore(selector$6(options));
3106
+ const initialized = useStore(selector$4(options));
3002
3107
  return initialized;
3003
3108
  }
3004
3109
 
@@ -3011,12 +3116,12 @@ function useNodesInitialized(options = defaultOptions) {
3011
3116
  * @param param.id - the handle id (this is only needed if the node has multiple handles of the same type)
3012
3117
  * @param param.onConnect - gets called when a connection is established
3013
3118
  * @param param.onDisconnect - gets called when a connection is removed
3014
- * @returns an array with connections
3119
+ * @returns an array with handle connections
3015
3120
  */
3016
3121
  function useHandleConnections({ type, id = null, nodeId, onConnect, onDisconnect, }) {
3017
3122
  const _nodeId = useNodeId();
3123
+ const currentNodeId = nodeId ?? _nodeId;
3018
3124
  const prevConnections = useRef(null);
3019
- const currentNodeId = nodeId || _nodeId;
3020
3125
  const connections = useStore((state) => state.connectionLookup.get(`${currentNodeId}-${type}-${id}`), areConnectionMapsEqual);
3021
3126
  useEffect(() => {
3022
3127
  // @todo dicuss if onConnect/onDisconnect should be called when the component mounts/unmounts
@@ -3033,36 +3138,34 @@ function useHandleConnections({ type, id = null, nodeId, onConnect, onDisconnect
3033
3138
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
3034
3139
  function useNodesData(nodeIds) {
3035
3140
  const nodesData = useStore(useCallback((s) => {
3036
- if (!Array.isArray(nodeIds)) {
3037
- return s.nodeLookup.get(nodeIds)?.data || null;
3038
- }
3039
3141
  const data = [];
3040
- for (const nodeId of nodeIds) {
3041
- const nodeData = s.nodeLookup.get(nodeId)?.data;
3042
- if (nodeData) {
3043
- data.push(nodeData);
3142
+ const isArrayOfIds = Array.isArray(nodeIds);
3143
+ const _nodeIds = isArrayOfIds ? nodeIds : [nodeIds];
3144
+ for (const nodeId of _nodeIds) {
3145
+ const node = s.nodeLookup.get(nodeId);
3146
+ if (node) {
3147
+ data.push({
3148
+ id: node.id,
3149
+ type: node.type,
3150
+ data: node.data,
3151
+ });
3044
3152
  }
3045
3153
  }
3046
- return data;
3047
- }, [nodeIds]), shallow);
3154
+ return isArrayOfIds ? data : data[0] ?? null;
3155
+ }, [nodeIds]), shallowNodeData);
3048
3156
  return nodesData;
3049
3157
  }
3050
3158
 
3051
- const selector$5 = (s) => ({
3052
- startHandle: s.connectionStartHandle,
3053
- endHandle: s.connectionEndHandle,
3054
- status: s.connectionStatus,
3055
- position: s.connectionStartHandle ? s.connectionPosition : null,
3056
- });
3057
3159
  /**
3058
- * Hook for accessing the ongoing connection.
3160
+ * Hook for getting an internal node by id
3059
3161
  *
3060
3162
  * @public
3061
- * @returns ongoing connection
3163
+ * @param id - id of the node
3164
+ * @returns array with visible node ids
3062
3165
  */
3063
- function useConnection() {
3064
- const ongoingConnection = useStore(selector$5, shallow);
3065
- return ongoingConnection;
3166
+ function useInternalNode(id) {
3167
+ const node = useStore(useCallback((s) => s.nodeLookup.get(id), [id]), shallow);
3168
+ return node;
3066
3169
  }
3067
3170
 
3068
3171
  function LinePattern({ dimensions, lineWidth, variant, className }) {
@@ -3084,14 +3187,14 @@ const defaultSize = {
3084
3187
  [BackgroundVariant.Lines]: 1,
3085
3188
  [BackgroundVariant.Cross]: 6,
3086
3189
  };
3087
- const selector$4 = (s) => ({ transform: s.transform, patternId: `pattern-${s.rfId}` });
3190
+ const selector$3 = (s) => ({ transform: s.transform, patternId: `pattern-${s.rfId}` });
3088
3191
  function BackgroundComponent({ id, variant = BackgroundVariant.Dots,
3089
3192
  // only used for dots and cross
3090
3193
  gap = 20,
3091
3194
  // only used for lines and cross
3092
3195
  size, lineWidth = 1, offset = 2, color, bgColor, style, className, patternClassName, }) {
3093
3196
  const ref = useRef(null);
3094
- const { transform, patternId } = useStore(selector$4, shallow);
3197
+ const { transform, patternId } = useStore(selector$3, shallow);
3095
3198
  const patternSize = size || defaultSize[variant];
3096
3199
  const isDots = variant === BackgroundVariant.Dots;
3097
3200
  const isCross = variant === BackgroundVariant.Cross;
@@ -3137,14 +3240,14 @@ function ControlButton({ children, className, ...rest }) {
3137
3240
  return (jsx("button", { type: "button", className: cc(['react-flow__controls-button', className]), ...rest, children: children }));
3138
3241
  }
3139
3242
 
3140
- const selector$3 = (s) => ({
3243
+ const selector$2 = (s) => ({
3141
3244
  isInteractive: s.nodesDraggable || s.nodesConnectable || s.elementsSelectable,
3142
3245
  minZoomReached: s.transform[2] <= s.minZoom,
3143
3246
  maxZoomReached: s.transform[2] >= s.maxZoom,
3144
3247
  });
3145
- function ControlsComponent({ style, showZoom = true, showFitView = true, showInteractive = true, fitViewOptions, onZoomIn, onZoomOut, onFitView, onInteractiveChange, className, children, position = 'bottom-left', 'aria-label': ariaLabel = 'React Flow controls', }) {
3248
+ function ControlsComponent({ style, showZoom = true, showFitView = true, showInteractive = true, fitViewOptions, onZoomIn, onZoomOut, onFitView, onInteractiveChange, className, children, position = 'bottom-left', orientation = 'vertical', 'aria-label': ariaLabel = 'React Flow controls', }) {
3146
3249
  const store = useStoreApi();
3147
- const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector$3, shallow);
3250
+ const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector$2, shallow);
3148
3251
  const { zoomIn, zoomOut, fitView } = useReactFlow();
3149
3252
  const onZoomInHandler = () => {
3150
3253
  zoomIn();
@@ -3166,7 +3269,8 @@ function ControlsComponent({ style, showZoom = true, showFitView = true, showInt
3166
3269
  });
3167
3270
  onInteractiveChange?.(!isInteractive);
3168
3271
  };
3169
- return (jsxs(Panel, { className: cc(['react-flow__controls', 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] }));
3272
+ const orientationClass = orientation === 'horizontal' ? 'horizontal' : 'vertical';
3273
+ 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] }));
3170
3274
  }
3171
3275
  ControlsComponent.displayName = 'Controls';
3172
3276
  const Controls = memo(ControlsComponent);
@@ -3182,15 +3286,13 @@ function MiniMapNodeComponent({ id, x, y, width, height, style, color, strokeCol
3182
3286
  }
3183
3287
  const MiniMapNode = memo(MiniMapNodeComponent);
3184
3288
 
3185
- const selector$2 = (s) => s.nodeOrigin;
3186
3289
  const selectorNodeIds = (s) => s.nodes.map((node) => node.id);
3187
- const getAttrFunction = (func) => (func instanceof Function ? func : () => func);
3290
+ const getAttrFunction = (func) => func instanceof Function ? func : () => func;
3188
3291
  function MiniMapNodes({ nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
3189
3292
  // We need to rename the prop to be `CapitalCase` so that JSX will render it as
3190
3293
  // a component properly.
3191
3294
  nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
3192
3295
  const nodeIds = useStore(selectorNodeIds, shallow);
3193
- const nodeOrigin = useStore(selector$2);
3194
3296
  const nodeColorFunc = getAttrFunction(nodeColor);
3195
3297
  const nodeStrokeColorFunc = getAttrFunction(nodeStrokeColor);
3196
3298
  const nodeClassNameFunc = getAttrFunction(nodeClassName);
@@ -3201,23 +3303,25 @@ nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
3201
3303
  // minimize the cost of updates when individual nodes change.
3202
3304
  //
3203
3305
  // For more details, see a similar commit in `NodeRenderer/index.tsx`.
3204
- jsx(NodeComponentWrapper, { id: nodeId, nodeOrigin: nodeOrigin, nodeColorFunc: nodeColorFunc, nodeStrokeColorFunc: nodeStrokeColorFunc, nodeClassNameFunc: nodeClassNameFunc, nodeBorderRadius: nodeBorderRadius, nodeStrokeWidth: nodeStrokeWidth, NodeComponent: NodeComponent, onClick: onClick, shapeRendering: shapeRendering }, nodeId))) }));
3306
+ jsx(NodeComponentWrapper, { id: nodeId, nodeColorFunc: nodeColorFunc, nodeStrokeColorFunc: nodeStrokeColorFunc, nodeClassNameFunc: nodeClassNameFunc, nodeBorderRadius: nodeBorderRadius, nodeStrokeWidth: nodeStrokeWidth, NodeComponent: NodeComponent, onClick: onClick, shapeRendering: shapeRendering }, nodeId))) }));
3205
3307
  }
3206
- const NodeComponentWrapper = memo(function NodeComponentWrapper({ id, nodeOrigin, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
3308
+ function NodeComponentWrapperInner({ id, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
3207
3309
  const { node, x, y } = useStore((s) => {
3208
3310
  const node = s.nodeLookup.get(id);
3209
- const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute;
3311
+ const { x, y } = node.internals.positionAbsolute;
3210
3312
  return {
3211
3313
  node,
3212
3314
  x,
3213
3315
  y,
3214
3316
  };
3215
3317
  }, shallow);
3216
- if (!node || node.hidden || !(node.computed?.width || node.width) || !(node.computed?.height || node.height)) {
3318
+ if (!node || node.hidden || !nodeHasDimensions(node)) {
3217
3319
  return null;
3218
3320
  }
3219
- return (jsx(NodeComponent, { x: x, y: y, width: node.computed?.width ?? node.width ?? 0, height: node.computed?.height ?? node.height ?? 0, style: node.style, selected: !!node.selected, className: nodeClassNameFunc(node), color: nodeColorFunc(node), borderRadius: nodeBorderRadius, strokeColor: nodeStrokeColorFunc(node), strokeWidth: nodeStrokeWidth, shapeRendering: shapeRendering, onClick: onClick, id: node.id }));
3220
- });
3321
+ const { width, height } = getNodeDimensions(node);
3322
+ return (jsx(NodeComponent, { x: x, y: y, width: width, height: height, style: node.style, selected: !!node.selected, className: nodeClassNameFunc(node), color: nodeColorFunc(node), borderRadius: nodeBorderRadius, strokeColor: nodeStrokeColorFunc(node), strokeWidth: nodeStrokeWidth, shapeRendering: shapeRendering, onClick: onClick, id: node.id }));
3323
+ }
3324
+ const NodeComponentWrapper = memo(NodeComponentWrapperInner);
3221
3325
  var MiniMapNodes$1 = memo(MiniMapNodes);
3222
3326
 
3223
3327
  const defaultWidth = 200;
@@ -3231,9 +3335,8 @@ const selector$1 = (s) => {
3231
3335
  };
3232
3336
  return {
3233
3337
  viewBB,
3234
- boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getNodesBounds(s.nodes, { nodeOrigin: s.nodeOrigin }), viewBB) : viewBB,
3338
+ boundingRect: s.nodeLookup.size > 0 ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup), viewBB) : viewBB,
3235
3339
  rfId: s.rfId,
3236
- nodeOrigin: s.nodeOrigin,
3237
3340
  panZoom: s.panZoom,
3238
3341
  translateExtent: s.translateExtent,
3239
3342
  flowWidth: s.width,
@@ -3244,7 +3347,7 @@ const ARIA_LABEL_KEY = 'react-flow__minimap-desc';
3244
3347
  function MiniMapComponent({ style, className, nodeStrokeColor, nodeColor, nodeClassName = '', nodeBorderRadius = 5, nodeStrokeWidth,
3245
3348
  // We need to rename the prop to be `CapitalCase` so that JSX will render it as
3246
3349
  // a component properly.
3247
- nodeComponent, maskColor, maskStrokeColor = 'none', maskStrokeWidth = 1, position = 'bottom-right', onClick, onNodeClick, pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', inversePan, zoomStep = 10, offsetScale = 5, }) {
3350
+ nodeComponent, bgColor, maskColor, maskStrokeColor, maskStrokeWidth, position = 'bottom-right', onClick, onNodeClick, pannable = false, zoomable = false, ariaLabel = 'React Flow mini map', inversePan, zoomStep = 10, offsetScale = 5, }) {
3248
3351
  const store = useStoreApi();
3249
3352
  const svg = useRef(null);
3250
3353
  const { boundingRect, viewBB, rfId, panZoom, translateExtent, flowWidth, flowHeight } = useStore(selector$1, shallow);
@@ -3302,12 +3405,15 @@ nodeComponent, maskColor, maskStrokeColor = 'none', maskStrokeWidth = 1, positio
3302
3405
  : undefined;
3303
3406
  return (jsx(Panel, { position: position, style: {
3304
3407
  ...style,
3305
- '--xy-minimap-mask-color-props': typeof maskColor === 'string' ? maskColor : undefined,
3408
+ '--xy-minimap-background-color-props': typeof bgColor === 'string' ? bgColor : undefined,
3409
+ '--xy-minimap-mask-background-color-props': typeof maskColor === 'string' ? maskColor : undefined,
3410
+ '--xy-minimap-mask-stroke-color-props': typeof maskStrokeColor === 'string' ? maskStrokeColor : undefined,
3411
+ '--xy-minimap-mask-stroke-width-props': typeof maskStrokeWidth === 'number' ? maskStrokeWidth * viewScale : undefined,
3306
3412
  '--xy-minimap-node-background-color-props': typeof nodeColor === 'string' ? nodeColor : undefined,
3307
3413
  '--xy-minimap-node-stroke-color-props': typeof nodeStrokeColor === 'string' ? nodeStrokeColor : undefined,
3308
3414
  '--xy-minimap-node-stroke-width-props': typeof nodeStrokeWidth === 'string' ? nodeStrokeWidth : undefined,
3309
- }, className: cc(['react-flow__minimap', className]), "data-testid": "rf__minimap", children: jsxs("svg", { width: elementWidth, height: elementHeight, viewBox: `${x} ${y} ${width} ${height}`, role: "img", "aria-labelledby": labelledBy, ref: svg, onClick: onSvgClick, children: [ariaLabel && jsx("title", { id: labelledBy, children: ariaLabel }), jsx(MiniMapNodes$1, { onClick: onSvgNodeClick, nodeColor: nodeColor, nodeStrokeColor: nodeStrokeColor, nodeBorderRadius: nodeBorderRadius, nodeClassName: nodeClassName, nodeStrokeWidth: nodeStrokeWidth, nodeComponent: nodeComponent }), jsx("path", { className: "react-flow__minimap-mask", d: `M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
3310
- M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd", stroke: maskStrokeColor, strokeWidth: maskStrokeWidth, pointerEvents: "none" })] }) }));
3415
+ }, className: cc(['react-flow__minimap', className]), "data-testid": "rf__minimap", children: jsxs("svg", { width: elementWidth, height: elementHeight, viewBox: `${x} ${y} ${width} ${height}`, className: "react-flow__minimap-svg", role: "img", "aria-labelledby": labelledBy, ref: svg, onClick: onSvgClick, children: [ariaLabel && jsx("title", { id: labelledBy, children: ariaLabel }), jsx(MiniMapNodes$1, { onClick: onSvgNodeClick, nodeColor: nodeColor, nodeStrokeColor: nodeStrokeColor, nodeBorderRadius: nodeBorderRadius, nodeClassName: nodeClassName, nodeStrokeWidth: nodeStrokeWidth, nodeComponent: nodeComponent }), jsx("path", { className: "react-flow__minimap-mask", d: `M${x - offset},${y - offset}h${width + offset * 2}v${height + offset * 2}h${-width - offset * 2}z
3416
+ M${viewBB.x},${viewBB.y}h${viewBB.width}v${viewBB.height}h${-viewBB.width}z`, fillRule: "evenodd", pointerEvents: "none" })] }) }));
3311
3417
  }
3312
3418
  MiniMapComponent.displayName = 'MiniMap';
3313
3419
  const MiniMap = memo(MiniMapComponent);
@@ -3329,33 +3435,57 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3329
3435
  domNode: resizeControlRef.current,
3330
3436
  nodeId: id,
3331
3437
  getStoreItems: () => {
3332
- const { nodeLookup, transform, snapGrid, snapToGrid } = store.getState();
3438
+ const { nodeLookup, transform, snapGrid, snapToGrid, nodeOrigin } = store.getState();
3333
3439
  return {
3334
3440
  nodeLookup,
3335
3441
  transform,
3336
3442
  snapGrid,
3337
3443
  snapToGrid,
3444
+ nodeOrigin,
3338
3445
  };
3339
3446
  },
3340
- onChange: (change) => {
3341
- const { triggerNodeChanges } = store.getState();
3447
+ onChange: (change, childChanges) => {
3448
+ const { triggerNodeChanges, nodeLookup, parentLookup, nodeOrigin } = store.getState();
3342
3449
  const changes = [];
3343
- if (change.isXPosChange || change.isYPosChange) {
3450
+ const nextPosition = { x: change.x, y: change.y };
3451
+ const node = nodeLookup.get(id);
3452
+ if (node && node.expandParent && node.parentId) {
3453
+ const origin = node.origin ?? nodeOrigin;
3454
+ const width = change.width ?? node.measured.width;
3455
+ const height = change.height ?? node.measured.height;
3456
+ const child = {
3457
+ id: node.id,
3458
+ parentId: node.parentId,
3459
+ rect: {
3460
+ width,
3461
+ height,
3462
+ ...evaluateAbsolutePosition({
3463
+ x: change.x ?? node.position.x,
3464
+ y: change.y ?? node.position.y,
3465
+ }, { width, height }, node.parentId, nodeLookup, origin),
3466
+ },
3467
+ };
3468
+ const parentExpandChanges = handleExpandParent([child], nodeLookup, parentLookup, nodeOrigin);
3469
+ changes.push(...parentExpandChanges);
3470
+ // when the parent was expanded by the child node, its position will be clamped at
3471
+ // 0,0 when node origin is 0,0 and to width, height if it's 1,1
3472
+ nextPosition.x = change.x ? Math.max(origin[0] * width, change.x) : undefined;
3473
+ nextPosition.y = change.y ? Math.max(origin[1] * height, change.y) : undefined;
3474
+ }
3475
+ if (nextPosition.x !== undefined && nextPosition.y !== undefined) {
3344
3476
  const positionChange = {
3345
3477
  id,
3346
3478
  type: 'position',
3347
- position: {
3348
- x: change.x,
3349
- y: change.y,
3350
- },
3479
+ position: { ...nextPosition },
3351
3480
  };
3352
3481
  changes.push(positionChange);
3353
3482
  }
3354
- if (change.isWidthChange || change.isHeightChange) {
3483
+ if (change.width !== undefined && change.height !== undefined) {
3355
3484
  const dimensionChange = {
3356
3485
  id,
3357
3486
  type: 'dimensions',
3358
3487
  resizing: true,
3488
+ setAttributes: true,
3359
3489
  dimensions: {
3360
3490
  width: change.width,
3361
3491
  height: change.height,
@@ -3363,6 +3493,13 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
3363
3493
  };
3364
3494
  changes.push(dimensionChange);
3365
3495
  }
3496
+ for (const childChange of childChanges) {
3497
+ const positionChange = {
3498
+ ...childChange,
3499
+ type: 'position',
3500
+ };
3501
+ changes.push(positionChange);
3502
+ }
3366
3503
  triggerNodeChanges(changes);
3367
3504
  },
3368
3505
  onEnd: () => {
@@ -3427,55 +3564,61 @@ function NodeToolbarPortal({ children }) {
3427
3564
  return createPortal(children, wrapperRef);
3428
3565
  }
3429
3566
 
3430
- const nodeEqualityFn = (a, b) => a?.computed?.positionAbsolute?.x !== b?.computed?.positionAbsolute?.x ||
3431
- a?.computed?.positionAbsolute?.y !== b?.computed?.positionAbsolute?.y ||
3432
- a?.computed?.width !== b?.computed?.width ||
3433
- a?.computed?.height !== b?.computed?.height ||
3567
+ const nodeEqualityFn = (a, b) => a?.internals.positionAbsolute.x !== b?.internals.positionAbsolute.x ||
3568
+ a?.internals.positionAbsolute.y !== b?.internals.positionAbsolute.y ||
3569
+ a?.measured.width !== b?.measured.width ||
3570
+ a?.measured.height !== b?.measured.height ||
3434
3571
  a?.selected !== b?.selected ||
3435
- a?.[internalsSymbol]?.z !== b?.[internalsSymbol]?.z;
3572
+ a?.internals.z !== b?.internals.z;
3436
3573
  const nodesEqualityFn = (a, b) => {
3437
- if (a.length !== b.length) {
3574
+ if (a.size !== b.size) {
3438
3575
  return false;
3439
3576
  }
3440
- return !a.some((node, i) => nodeEqualityFn(node, b[i]));
3577
+ for (const [key, node] of a) {
3578
+ if (nodeEqualityFn(node, b.get(key))) {
3579
+ return false;
3580
+ }
3581
+ }
3582
+ return true;
3441
3583
  };
3442
3584
  const storeSelector = (state) => ({
3443
- viewport: {
3444
- x: state.transform[0],
3445
- y: state.transform[1],
3446
- zoom: state.transform[2],
3447
- },
3448
- nodeOrigin: state.nodeOrigin,
3585
+ x: state.transform[0],
3586
+ y: state.transform[1],
3587
+ zoom: state.transform[2],
3449
3588
  selectedNodesCount: state.nodes.filter((node) => node.selected).length,
3450
3589
  });
3451
3590
  function NodeToolbar({ nodeId, children, className, style, isVisible, position = Position.Top, offset = 10, align = 'center', ...rest }) {
3452
3591
  const contextNodeId = useNodeId();
3453
3592
  const nodesSelector = useCallback((state) => {
3454
3593
  const nodeIds = Array.isArray(nodeId) ? nodeId : [nodeId || contextNodeId || ''];
3455
- return nodeIds.reduce((acc, id) => {
3594
+ const internalNodes = nodeIds.reduce((res, id) => {
3456
3595
  const node = state.nodeLookup.get(id);
3457
3596
  if (node) {
3458
- acc.push(node);
3597
+ res.set(node.id, node);
3459
3598
  }
3460
- return acc;
3461
- }, []);
3599
+ return res;
3600
+ }, new Map());
3601
+ return internalNodes;
3462
3602
  }, [nodeId, contextNodeId]);
3463
3603
  const nodes = useStore(nodesSelector, nodesEqualityFn);
3464
- const { viewport, nodeOrigin, selectedNodesCount } = useStore(storeSelector, shallow);
3604
+ const { x, y, zoom, selectedNodesCount } = useStore(storeSelector, shallow);
3465
3605
  // if isVisible is not set, we show the toolbar only if its node is selected and no other node is selected
3466
- const isActive = typeof isVisible === 'boolean' ? isVisible : nodes.length === 1 && nodes[0].selected && selectedNodesCount === 1;
3467
- if (!isActive || !nodes.length) {
3606
+ const isActive = typeof isVisible === 'boolean'
3607
+ ? isVisible
3608
+ : nodes.size === 1 && nodes.values().next().value.selected && selectedNodesCount === 1;
3609
+ if (!isActive || !nodes.size) {
3468
3610
  return null;
3469
3611
  }
3470
- const nodeRect = getNodesBounds(nodes, { nodeOrigin });
3471
- const zIndex = Math.max(...nodes.map((node) => (node[internalsSymbol]?.z || 1) + 1));
3612
+ const nodeRect = getInternalNodesBounds(nodes);
3613
+ const nodesArray = Array.from(nodes.values());
3614
+ const zIndex = Math.max(...nodesArray.map((node) => node.internals.z + 1));
3472
3615
  const wrapperStyle = {
3473
3616
  position: 'absolute',
3474
- transform: getNodeToolbarTransform(nodeRect, viewport, position, offset, align),
3617
+ transform: getNodeToolbarTransform(nodeRect, { x, y, zoom }, position, offset, align),
3475
3618
  zIndex,
3476
3619
  ...style,
3477
3620
  };
3478
- return (jsx(NodeToolbarPortal, { children: jsx("div", { style: wrapperStyle, className: cc(['react-flow__node-toolbar', className]), ...rest, "data-id": nodes.reduce((acc, node) => `${acc}${node.id} `, '').trim(), children: children }) }));
3621
+ return (jsx(NodeToolbarPortal, { children: jsx("div", { style: wrapperStyle, className: cc(['react-flow__node-toolbar', className]), ...rest, "data-id": nodesArray.reduce((acc, node) => `${acc}${node.id} `, '').trim(), children: children }) }));
3479
3622
  }
3480
3623
 
3481
- export { Background, BackgroundVariant, BaseEdge, BezierEdge, ControlButton, Controls, EdgeLabelRenderer, EdgeText, Handle, MiniMap, NodeResizeControl, NodeResizer, NodeToolbar, Panel, ReactFlow, ReactFlowProvider, SimpleBezierEdge, SmoothStepEdge, StepEdge, StraightEdge, ViewportPortal, applyEdgeChanges, applyNodeChanges, getSimpleBezierPath, handleParentExpand, isEdge, isNode, useConnection, useEdges, useEdgesState, useHandleConnections, useKeyPress, useNodeId, useNodes, useNodesData, useNodesInitialized, useNodesState, useOnSelectionChange, useOnViewportChange, useReactFlow, useStore, useStoreApi, useUpdateNodeInternals, useViewport };
3624
+ export { Background, BackgroundVariant, BaseEdge, BezierEdge, ControlButton, Controls, EdgeLabelRenderer, EdgeText, Handle, MiniMap, NodeResizeControl, NodeResizer, NodeToolbar, Panel, index as ReactFlow, ReactFlowProvider, SimpleBezierEdge, SmoothStepEdge, StepEdge, StraightEdge, ViewportPortal, applyEdgeChanges, applyNodeChanges, getSimpleBezierPath, isEdge, isNode, useConnection, useEdges, useEdgesState, useHandleConnections, useInternalNode, useKeyPress, useNodeId, useNodes, useNodesData, useNodesInitialized, useNodesState, useOnSelectionChange, useOnViewportChange, useReactFlow, useStore, useStoreApi, useUpdateNodeInternals, useViewport };