@xyflow/react 12.0.0-next.11 → 12.0.0-next.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/base.css +7 -0
  2. package/dist/esm/additional-components/Controls/Controls.d.ts +1 -1
  3. package/dist/esm/additional-components/Controls/Controls.d.ts.map +1 -1
  4. package/dist/esm/additional-components/Controls/types.d.ts +1 -0
  5. package/dist/esm/additional-components/Controls/types.d.ts.map +1 -1
  6. package/dist/esm/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  7. package/dist/esm/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  8. package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  9. package/dist/esm/components/ConnectionLine/index.d.ts.map +1 -1
  10. package/dist/esm/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -1
  11. package/dist/esm/components/Handle/index.d.ts +1 -1
  12. package/dist/esm/components/Handle/index.d.ts.map +1 -1
  13. package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
  14. package/dist/esm/components/NodeWrapper/utils.d.ts +2 -2
  15. package/dist/esm/components/NodeWrapper/utils.d.ts.map +1 -1
  16. package/dist/esm/components/NodesSelection/index.d.ts.map +1 -1
  17. package/dist/esm/components/StoreUpdater/index.d.ts.map +1 -1
  18. package/dist/esm/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
  19. package/dist/esm/container/Pane/index.d.ts.map +1 -1
  20. package/dist/esm/container/ReactFlow/index.d.ts +1 -1
  21. package/dist/esm/container/ReactFlow/index.d.ts.map +1 -1
  22. package/dist/esm/contexts/RFStoreContext.d.ts +2 -2
  23. package/dist/esm/contexts/RFStoreContext.d.ts.map +1 -1
  24. package/dist/esm/hooks/useInternalNode.d.ts +10 -0
  25. package/dist/esm/hooks/useInternalNode.d.ts.map +1 -0
  26. package/dist/esm/hooks/useKeyPress.d.ts.map +1 -1
  27. package/dist/esm/hooks/useMoveSelectedNodes.d.ts.map +1 -1
  28. package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -1
  29. package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
  30. package/dist/esm/index.d.ts +3 -2
  31. package/dist/esm/index.d.ts.map +1 -1
  32. package/dist/esm/index.js +174 -180
  33. package/dist/esm/index.mjs +174 -180
  34. package/dist/esm/store/index.d.ts +5 -5
  35. package/dist/esm/store/index.d.ts.map +1 -1
  36. package/dist/esm/store/initialState.d.ts +4 -4
  37. package/dist/esm/types/general.d.ts +2 -5
  38. package/dist/esm/types/general.d.ts.map +1 -1
  39. package/dist/esm/types/index.d.ts +0 -1
  40. package/dist/esm/types/index.d.ts.map +1 -1
  41. package/dist/esm/types/instance.d.ts +9 -1
  42. package/dist/esm/types/instance.d.ts.map +1 -1
  43. package/dist/esm/types/nodes.d.ts +2 -1
  44. package/dist/esm/types/nodes.d.ts.map +1 -1
  45. package/dist/esm/types/store.d.ts +4 -4
  46. package/dist/esm/types/store.d.ts.map +1 -1
  47. package/dist/esm/utils/changes.d.ts +4 -5
  48. package/dist/esm/utils/changes.d.ts.map +1 -1
  49. package/dist/esm/utils/general.d.ts +4 -2
  50. package/dist/esm/utils/general.d.ts.map +1 -1
  51. package/dist/style.css +8 -3
  52. package/dist/umd/additional-components/Controls/Controls.d.ts +1 -1
  53. package/dist/umd/additional-components/Controls/Controls.d.ts.map +1 -1
  54. package/dist/umd/additional-components/Controls/types.d.ts +1 -0
  55. package/dist/umd/additional-components/Controls/types.d.ts.map +1 -1
  56. package/dist/umd/additional-components/MiniMap/MiniMap.d.ts.map +1 -1
  57. package/dist/umd/additional-components/MiniMap/MiniMapNodes.d.ts.map +1 -1
  58. package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
  59. package/dist/umd/components/ConnectionLine/index.d.ts.map +1 -1
  60. package/dist/umd/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -1
  61. package/dist/umd/components/Handle/index.d.ts +1 -1
  62. package/dist/umd/components/Handle/index.d.ts.map +1 -1
  63. package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
  64. package/dist/umd/components/NodeWrapper/utils.d.ts +2 -2
  65. package/dist/umd/components/NodeWrapper/utils.d.ts.map +1 -1
  66. package/dist/umd/components/NodesSelection/index.d.ts.map +1 -1
  67. package/dist/umd/components/StoreUpdater/index.d.ts.map +1 -1
  68. package/dist/umd/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
  69. package/dist/umd/container/Pane/index.d.ts.map +1 -1
  70. package/dist/umd/container/ReactFlow/index.d.ts +1 -1
  71. package/dist/umd/container/ReactFlow/index.d.ts.map +1 -1
  72. package/dist/umd/contexts/RFStoreContext.d.ts +2 -2
  73. package/dist/umd/contexts/RFStoreContext.d.ts.map +1 -1
  74. package/dist/umd/hooks/useInternalNode.d.ts +10 -0
  75. package/dist/umd/hooks/useInternalNode.d.ts.map +1 -0
  76. package/dist/umd/hooks/useKeyPress.d.ts.map +1 -1
  77. package/dist/umd/hooks/useMoveSelectedNodes.d.ts.map +1 -1
  78. package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
  79. package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
  80. package/dist/umd/index.d.ts +3 -2
  81. package/dist/umd/index.d.ts.map +1 -1
  82. package/dist/umd/index.js +2 -2
  83. package/dist/umd/store/index.d.ts +5 -5
  84. package/dist/umd/store/index.d.ts.map +1 -1
  85. package/dist/umd/store/initialState.d.ts +4 -4
  86. package/dist/umd/types/general.d.ts +2 -5
  87. package/dist/umd/types/general.d.ts.map +1 -1
  88. package/dist/umd/types/index.d.ts +0 -1
  89. package/dist/umd/types/index.d.ts.map +1 -1
  90. package/dist/umd/types/instance.d.ts +9 -1
  91. package/dist/umd/types/instance.d.ts.map +1 -1
  92. package/dist/umd/types/nodes.d.ts +2 -1
  93. package/dist/umd/types/nodes.d.ts.map +1 -1
  94. package/dist/umd/types/store.d.ts +4 -4
  95. package/dist/umd/types/store.d.ts.map +1 -1
  96. package/dist/umd/utils/changes.d.ts +4 -5
  97. package/dist/umd/utils/changes.d.ts.map +1 -1
  98. package/dist/umd/utils/general.d.ts +4 -2
  99. package/dist/umd/utils/general.d.ts.map +1 -1
  100. package/package.json +4 -4
@@ -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, useLayoutEffect, useCallback, memo, forwardRef } 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, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getNodesBounds, clampPosition, internalsSymbol, getNodeDimensions, nodeHasDimensions, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionLineType, updateConnectionLookup, adoptUserProvidedNodes, devWarn, updateNodeDimensions, updateAbsolutePositions, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, shallowNodeData, 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, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, nodeToRect, isRectObject, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getNodesBounds, clampPosition, getNodeDimensions, nodeHasDimensions, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionLineType, updateConnectionLookup, adoptUserNodes, devWarn, updateNodeDimensions, updateAbsolutePositions, handleParentExpand, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, shallowNodeData, getNodePositionWithOrigin, XYMinimap, getBoundsOfRects, getInternalNodesBounds, 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, updateEdge } 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';
@@ -83,7 +83,7 @@ function Attribution({ proOptions, position = 'bottom-right' }) {
83
83
  }
84
84
 
85
85
  const selector$o = (s) => ({
86
- selectedNodes: s.nodes.filter((n) => n.selected),
86
+ selectedNodes: Array.from(s.nodeLookup.values()).filter((n) => n.selected),
87
87
  selectedEdges: s.edges.filter((e) => e.selected),
88
88
  });
89
89
  const selectId = (obj) => obj.id;
@@ -356,10 +356,12 @@ keyCode = null, options = { target: defaultDoc, actInsideInputWithModifier: true
356
356
  target?.addEventListener('keydown', downHandler);
357
357
  target?.addEventListener('keyup', upHandler);
358
358
  window.addEventListener('blur', resetHandler);
359
+ window.addEventListener('contextmenu', resetHandler);
359
360
  return () => {
360
361
  target?.removeEventListener('keydown', downHandler);
361
362
  target?.removeEventListener('keyup', upHandler);
362
363
  window.removeEventListener('blur', resetHandler);
364
+ window.removeEventListener('contextmenu', resetHandler);
363
365
  };
364
366
  }
365
367
  }, [keyCode, setKeyPressed]);
@@ -409,10 +411,10 @@ const useViewportHelper = () => {
409
411
  return { x, y, zoom };
410
412
  },
411
413
  fitView: (options) => {
412
- const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
414
+ const { nodeLookup, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState();
413
415
  return panZoom
414
416
  ? fitView({
415
- nodes,
417
+ nodeLookup,
416
418
  width,
417
419
  height,
418
420
  nodeOrigin,
@@ -468,42 +470,6 @@ const useViewportHelper = () => {
468
470
  return viewportHelperFunctions;
469
471
  };
470
472
 
471
- function handleParentExpand(updatedElements, updateItem) {
472
- for (const [index, item] of updatedElements.entries()) {
473
- if (item.id === updateItem.parentNode) {
474
- const parent = { ...item };
475
- parent.computed ??= {};
476
- const extendWidth = updateItem.position.x + updateItem.computed.width - parent.computed.width;
477
- const extendHeight = updateItem.position.y + updateItem.computed.height - parent.computed.height;
478
- if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) {
479
- parent.width = parent.width ?? parent.computed.width;
480
- parent.height = parent.height ?? parent.computed.height;
481
- if (extendWidth > 0) {
482
- parent.width += extendWidth;
483
- }
484
- if (extendHeight > 0) {
485
- parent.height += extendHeight;
486
- }
487
- if (updateItem.position.x < 0) {
488
- const xDiff = Math.abs(updateItem.position.x);
489
- parent.position.x = parent.position.x - xDiff;
490
- parent.width += xDiff;
491
- updateItem.position.x = 0;
492
- }
493
- if (updateItem.position.y < 0) {
494
- const yDiff = Math.abs(updateItem.position.y);
495
- parent.position.y = parent.position.y - yDiff;
496
- parent.height += yDiff;
497
- updateItem.position.y = 0;
498
- }
499
- parent.computed.width = parent.width;
500
- parent.computed.height = parent.height;
501
- updatedElements[index] = parent;
502
- }
503
- break;
504
- }
505
- }
506
- }
507
473
  // This function applies changes to nodes or edges that are triggered by React Flow internally.
508
474
  // When you drag a node for example, React Flow will send a position change update.
509
475
  // This function then applies the changes and returns the updated elements.
@@ -555,14 +521,14 @@ function applyChanges(changes, elements) {
555
521
  /// each _mutate_ this object, so there's only ever one copy.
556
522
  const updatedElement = { ...element };
557
523
  for (const change of changes) {
558
- applyChange(change, updatedElement, updatedElements);
524
+ applyChange(change, updatedElement);
559
525
  }
560
526
  updatedElements.push(updatedElement);
561
527
  }
562
528
  return updatedElements;
563
529
  }
564
530
  // Applies a single change to an element. This is a *mutable* update.
565
- function applyChange(change, element, elements = []) {
531
+ function applyChange(change, element) {
566
532
  switch (change.type) {
567
533
  case 'select': {
568
534
  element.selected = change.selected;
@@ -572,23 +538,16 @@ function applyChange(change, element, elements = []) {
572
538
  if (typeof change.position !== 'undefined') {
573
539
  element.position = change.position;
574
540
  }
575
- if (typeof change.positionAbsolute !== 'undefined') {
576
- element.computed ??= {};
577
- element.computed.positionAbsolute = change.positionAbsolute;
578
- }
579
541
  if (typeof change.dragging !== 'undefined') {
580
542
  element.dragging = change.dragging;
581
543
  }
582
- if (element.expandParent) {
583
- handleParentExpand(elements, element);
584
- }
585
544
  break;
586
545
  }
587
546
  case 'dimensions': {
588
547
  if (typeof change.dimensions !== 'undefined') {
589
- element.computed ??= {};
590
- element.computed.width = change.dimensions.width;
591
- element.computed.height = change.dimensions.height;
548
+ element.measured ??= {};
549
+ element.measured.width = change.dimensions.width;
550
+ element.measured.height = change.dimensions.height;
592
551
  if (change.resizing) {
593
552
  element.width = change.dimensions.width;
594
553
  element.height = change.dimensions.height;
@@ -597,9 +556,6 @@ function applyChange(change, element, elements = []) {
597
556
  if (typeof change.resizing === 'boolean') {
598
557
  element.resizing = change.resizing;
599
558
  }
600
- if (element.expandParent) {
601
- handleParentExpand(elements, element);
602
- }
603
559
  break;
604
560
  }
605
561
  }
@@ -659,7 +615,7 @@ function createSelectionChange(id, selected) {
659
615
  }
660
616
  function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false) {
661
617
  const changes = [];
662
- for (const item of items) {
618
+ for (const [, item] of items) {
663
619
  const willBeSelected = selectedIds.has(item.id);
664
620
  // we don't want to set all items to selected=false on the first selection
665
621
  if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
@@ -711,6 +667,11 @@ const isNode = (element) => isNodeBase(element);
711
667
  * @returns A boolean indicating whether the element is an Edge
712
668
  */
713
669
  const isEdge = (element) => isEdgeBase(element);
670
+ // eslint-disable-next-line @typescript-eslint/ban-types
671
+ function fixedForwardRef(render) {
672
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
673
+ return forwardRef(render);
674
+ }
714
675
 
715
676
  // we need this hook to prevent a warning when using react-flow in SSR
716
677
  const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
@@ -724,12 +685,9 @@ const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffec
724
685
  function useReactFlow() {
725
686
  const viewportHelper = useViewportHelper();
726
687
  const store = useStoreApi();
727
- const getNodes = useCallback(() => {
728
- return store.getState().nodes.map((n) => ({ ...n }));
729
- }, []);
730
- const getNode = useCallback((id) => {
731
- return store.getState().nodeLookup.get(id);
732
- }, []);
688
+ const getNodes = useCallback(() => store.getState().nodes.map((n) => ({ ...n })), []);
689
+ const getInternalNode = useCallback((id) => store.getState().nodeLookup.get(id), []);
690
+ const getNode = useCallback((id) => getInternalNode(id)?.internals.userNode, [getInternalNode]);
733
691
  const getEdges = useCallback(() => {
734
692
  const { edges = [] } = store.getState();
735
693
  return edges.map((e) => ({ ...e }));
@@ -867,22 +825,19 @@ function useReactFlow() {
867
825
  }
868
826
  return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
869
827
  }, []);
870
- const getNodeRect = useCallback((nodeOrRect) => {
871
- const isRect = isRectObject(nodeOrRect);
872
- const node = isRect ? null : store.getState().nodeLookup.get(nodeOrRect.id);
873
- if (!isRect && !node) {
874
- return [null, null, isRect];
875
- }
876
- const nodeRect = isRect ? nodeOrRect : nodeToRect(node);
877
- return [nodeRect, node, isRect];
828
+ const getNodeRect = useCallback(({ id }) => {
829
+ const internalNode = store.getState().nodeLookup.get(id);
830
+ return internalNode ? nodeToRect(internalNode) : null;
878
831
  }, []);
879
832
  const getIntersectingNodes = useCallback((nodeOrRect, partially = true, nodes) => {
880
- const [nodeRect, node, isRect] = getNodeRect(nodeOrRect);
833
+ const isRect = isRectObject(nodeOrRect);
834
+ const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
881
835
  if (!nodeRect) {
882
836
  return [];
883
837
  }
884
838
  return (nodes || store.getState().nodes).filter((n) => {
885
- if (!isRect && (n.id === node.id || !n.computed?.positionAbsolute)) {
839
+ const internalNode = store.getState().nodeLookup.get(n.id);
840
+ if (internalNode && !isRect && (n.id === nodeOrRect.id || !internalNode.internals.positionAbsolute)) {
886
841
  return false;
887
842
  }
888
843
  const currNodeRect = nodeToRect(n);
@@ -892,7 +847,8 @@ function useReactFlow() {
892
847
  });
893
848
  }, []);
894
849
  const isNodeIntersecting = useCallback((nodeOrRect, area, partially = true) => {
895
- const [nodeRect] = getNodeRect(nodeOrRect);
850
+ const isRect = isRectObject(nodeOrRect);
851
+ const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
896
852
  if (!nodeRect) {
897
853
  return false;
898
854
  }
@@ -920,6 +876,7 @@ function useReactFlow() {
920
876
  ...viewportHelper,
921
877
  getNodes,
922
878
  getNode,
879
+ getInternalNode,
923
880
  getEdges,
924
881
  getEdge,
925
882
  setNodes,
@@ -937,6 +894,7 @@ function useReactFlow() {
937
894
  viewportHelper,
938
895
  getNodes,
939
896
  getNode,
897
+ getInternalNode,
940
898
  getEdges,
941
899
  getEdge,
942
900
  setNodes,
@@ -1186,7 +1144,7 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
1186
1144
  onSelectionStart?.(event);
1187
1145
  };
1188
1146
  const onMouseMove = (event) => {
1189
- const { userSelectionRect, edges, transform, nodeOrigin, nodes, triggerNodeChanges, triggerEdgeChanges } = store.getState();
1147
+ const { userSelectionRect, edgeLookup, transform, nodeOrigin, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = store.getState();
1190
1148
  if (!isSelecting || !containerBounds.current || !userSelectionRect) {
1191
1149
  return;
1192
1150
  }
@@ -1201,25 +1159,25 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
1201
1159
  width: Math.abs(mousePos.x - startX),
1202
1160
  height: Math.abs(mousePos.y - startY),
1203
1161
  };
1204
- const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true, nodeOrigin);
1162
+ const selectedNodes = getNodesInside(nodeLookup, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true, nodeOrigin);
1205
1163
  const selectedEdgeIds = new Set();
1206
1164
  const selectedNodeIds = new Set();
1207
1165
  for (const selectedNode of selectedNodes) {
1208
1166
  selectedNodeIds.add(selectedNode.id);
1209
- for (const edge of edges) {
1167
+ for (const [edgeId, edge] of edgeLookup) {
1210
1168
  if (edge.source === selectedNode.id || edge.target === selectedNode.id) {
1211
- selectedEdgeIds.add(edge.id);
1169
+ selectedEdgeIds.add(edgeId);
1212
1170
  }
1213
1171
  }
1214
1172
  }
1215
1173
  if (prevSelectedNodesCount.current !== selectedNodeIds.size) {
1216
1174
  prevSelectedNodesCount.current = selectedNodeIds.size;
1217
- const changes = getSelectionChanges(nodes, selectedNodeIds, true);
1175
+ const changes = getSelectionChanges(nodeLookup, selectedNodeIds, true);
1218
1176
  triggerNodeChanges(changes);
1219
1177
  }
1220
1178
  if (prevSelectedEdgesCount.current !== selectedEdgeIds.size) {
1221
1179
  prevSelectedEdgesCount.current = selectedEdgeIds.size;
1222
- const changes = getSelectionChanges(edges, selectedEdgeIds);
1180
+ const changes = getSelectionChanges(edgeLookup, selectedEdgeIds);
1223
1181
  triggerEdgeChanges(changes);
1224
1182
  }
1225
1183
  store.setState({
@@ -1329,36 +1287,38 @@ const selectedAndDraggable = (nodesDraggable) => (n) => n.selected && (n.draggab
1329
1287
  function useMoveSelectedNodes() {
1330
1288
  const store = useStoreApi();
1331
1289
  const moveSelectedNodes = useCallback((params) => {
1332
- const { nodeExtent, nodes, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin, } = store.getState();
1333
- const selectedNodes = nodes.filter(selectedAndDraggable(nodesDraggable));
1290
+ const { nodeExtent, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin } = store.getState();
1291
+ const nodeUpdates = [];
1292
+ const isSelected = selectedAndDraggable(nodesDraggable);
1334
1293
  // by default a node moves 5px on each key press
1335
1294
  // if snap grid is enabled, we use that for the velocity
1336
1295
  const xVelo = snapToGrid ? snapGrid[0] : 5;
1337
1296
  const yVelo = snapToGrid ? snapGrid[1] : 5;
1338
1297
  const xDiff = params.direction.x * xVelo * params.factor;
1339
1298
  const yDiff = params.direction.y * yVelo * params.factor;
1340
- const nodeUpdates = selectedNodes.map((node) => {
1341
- if (node.computed?.positionAbsolute) {
1342
- let nextPosition = {
1343
- x: node.computed.positionAbsolute.x + xDiff,
1344
- y: node.computed.positionAbsolute.y + yDiff,
1345
- };
1346
- if (snapToGrid) {
1347
- nextPosition = snapPosition(nextPosition, snapGrid);
1348
- }
1349
- const { position, positionAbsolute } = calculateNodePosition({
1350
- nodeId: node.id,
1351
- nextPosition,
1352
- nodeLookup,
1353
- nodeExtent,
1354
- nodeOrigin,
1355
- onError,
1356
- });
1357
- node.position = position;
1358
- node.computed.positionAbsolute = positionAbsolute;
1299
+ for (const [, node] of nodeLookup) {
1300
+ if (!isSelected(node)) {
1301
+ continue;
1359
1302
  }
1360
- return node;
1361
- });
1303
+ let nextPosition = {
1304
+ x: node.internals.positionAbsolute.x + xDiff,
1305
+ y: node.internals.positionAbsolute.y + yDiff,
1306
+ };
1307
+ if (snapToGrid) {
1308
+ nextPosition = snapPosition(nextPosition, snapGrid);
1309
+ }
1310
+ const { position, positionAbsolute } = calculateNodePosition({
1311
+ nodeId: node.id,
1312
+ nextPosition,
1313
+ nodeLookup,
1314
+ nodeExtent,
1315
+ nodeOrigin,
1316
+ onError,
1317
+ });
1318
+ node.position = position;
1319
+ node.internals.positionAbsolute = positionAbsolute;
1320
+ nodeUpdates.push(node);
1321
+ }
1362
1322
  updateNodePositions(nodeUpdates);
1363
1323
  }, []);
1364
1324
  return moveSelectedNodes;
@@ -1427,7 +1387,7 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1427
1387
  connectionMode: currentStore.connectionMode,
1428
1388
  connectionRadius: currentStore.connectionRadius,
1429
1389
  domNode: currentStore.domNode,
1430
- nodes: currentStore.nodes,
1390
+ nodeLookup: currentStore.nodeLookup,
1431
1391
  lib: currentStore.lib,
1432
1392
  isTarget,
1433
1393
  handleId,
@@ -1441,6 +1401,7 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1441
1401
  onConnect: onConnectExtended,
1442
1402
  isValidConnection: isValidConnection || currentStore.isValidConnection,
1443
1403
  getTransform: () => store.getState().transform,
1404
+ getConnectionStartHandle: () => store.getState().connectionStartHandle,
1444
1405
  });
1445
1406
  }
1446
1407
  if (isMouseTriggered) {
@@ -1510,7 +1471,7 @@ function HandleComponent({ type = 'source', position = Position.Top, isValidConn
1510
1471
  /**
1511
1472
  * The Handle component is a UI element that is used to connect nodes.
1512
1473
  */
1513
- const Handle = memo(forwardRef(HandleComponent));
1474
+ const Handle = memo(fixedForwardRef(HandleComponent));
1514
1475
 
1515
1476
  function InputNode({ data, isConnectable, sourcePosition = Position.Bottom }) {
1516
1477
  return (jsxs(Fragment, { children: [data?.label, jsx(Handle, { type: "source", position: sourcePosition, isConnectable: isConnectable })] }));
@@ -1541,7 +1502,7 @@ const builtinNodeTypes = {
1541
1502
  group: GroupNode,
1542
1503
  };
1543
1504
  function getNodeInlineStyleDimensions(node) {
1544
- if (!node.computed) {
1505
+ if (node.internals.handleBounds === undefined) {
1545
1506
  return {
1546
1507
  width: node.width ?? node.initialWidth ?? node.style?.width,
1547
1508
  height: node.height ?? node.initialHeight ?? node.style?.height,
@@ -1554,7 +1515,12 @@ function getNodeInlineStyleDimensions(node) {
1554
1515
  }
1555
1516
 
1556
1517
  const selector$h = (s) => {
1557
- const selectedNodes = s.nodes.filter((n) => n.selected);
1518
+ const selectedNodes = [];
1519
+ for (const [, node] of s.nodeLookup) {
1520
+ if (node.selected) {
1521
+ selectedNodes.push(node);
1522
+ }
1523
+ }
1558
1524
  const { width, height, x, y } = getNodesBounds(selectedNodes, { nodeOrigin: s.nodeOrigin });
1559
1525
  return {
1560
1526
  width,
@@ -1621,7 +1587,7 @@ const FlowRenderer = memo(FlowRendererComponent);
1621
1587
 
1622
1588
  const selector$f = (onlyRenderVisible) => (s) => {
1623
1589
  return onlyRenderVisible
1624
- ? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map((node) => node.id)
1590
+ ? getNodesInside(s.nodeLookup, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map((node) => node.id)
1625
1591
  : Array.from(s.nodeLookup.keys());
1626
1592
  };
1627
1593
  /**
@@ -1651,7 +1617,6 @@ function useResizeObserver() {
1651
1617
  updates.set(id, {
1652
1618
  id,
1653
1619
  nodeElement: entry.target,
1654
- forceUpdate: true,
1655
1620
  });
1656
1621
  });
1657
1622
  updateNodeDimensions(updates);
@@ -1671,16 +1636,16 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1671
1636
  const { node, positionAbsoluteX, positionAbsoluteY, zIndex, isParent } = useStore((s) => {
1672
1637
  const node = s.nodeLookup.get(id);
1673
1638
  const positionAbsolute = nodeExtent
1674
- ? clampPosition(node.computed?.positionAbsolute, nodeExtent)
1675
- : node.computed?.positionAbsolute || { x: 0, y: 0 };
1639
+ ? clampPosition(node.internals.positionAbsolute, nodeExtent)
1640
+ : node.internals.positionAbsolute || { x: 0, y: 0 };
1676
1641
  return {
1677
1642
  node,
1678
1643
  // we are mutating positionAbsolute, z and isParent attributes for sub flows
1679
1644
  // so we we need to force a re-render when some change
1680
1645
  positionAbsoluteX: positionAbsolute.x,
1681
1646
  positionAbsoluteY: positionAbsolute.y,
1682
- zIndex: node[internalsSymbol]?.z ?? 0,
1683
- isParent: !!node[internalsSymbol]?.isParent,
1647
+ zIndex: node.internals.z,
1648
+ isParent: node.internals.isParent,
1684
1649
  };
1685
1650
  }, shallow);
1686
1651
  let nodeType = node.type || 'default';
@@ -1702,12 +1667,13 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1702
1667
  const nodeDimensions = getNodeDimensions(node);
1703
1668
  const inlineDimensions = getNodeInlineStyleDimensions(node);
1704
1669
  const initialized = nodeHasDimensions(node);
1705
- const hasHandleBounds = !!node[internalsSymbol]?.handleBounds;
1670
+ const hasHandleBounds = !!node.internals.handleBounds;
1706
1671
  const moveSelectedNodes = useMoveSelectedNodes();
1707
1672
  useEffect(() => {
1673
+ const currNode = nodeRef.current;
1708
1674
  return () => {
1709
- if (nodeRef.current) {
1710
- resizeObserver?.unobserve(nodeRef.current);
1675
+ if (currNode) {
1676
+ resizeObserver?.unobserve(currNode);
1711
1677
  }
1712
1678
  };
1713
1679
  }, []);
@@ -1735,7 +1701,7 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
1735
1701
  if (targetPosChanged) {
1736
1702
  prevTargetPosition.current = node.targetPosition;
1737
1703
  }
1738
- store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, forceUpdate: true }]]));
1704
+ store.getState().updateNodeDimensions(new Map([[id, { id, nodeElement: nodeRef.current, force: true }]]));
1739
1705
  }
1740
1706
  }, [id, nodeType, node.sourcePosition, node.targetPosition]);
1741
1707
  const dragging = useDrag({
@@ -2158,7 +2124,7 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2158
2124
  if (event.button !== 0) {
2159
2125
  return;
2160
2126
  }
2161
- const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodes, rfId: flowId, panBy, updateConnection, } = store.getState();
2127
+ const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodeLookup, rfId: flowId, panBy, updateConnection, } = store.getState();
2162
2128
  const nodeId = isSourceHandle ? edge.target : edge.source;
2163
2129
  const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
2164
2130
  const handleType = isSourceHandle ? 'target' : 'source';
@@ -2177,7 +2143,7 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2177
2143
  domNode,
2178
2144
  handleId,
2179
2145
  nodeId,
2180
- nodes,
2146
+ nodeLookup,
2181
2147
  isTarget,
2182
2148
  edgeUpdaterType: handleType,
2183
2149
  lib,
@@ -2191,6 +2157,7 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2191
2157
  onEdgeUpdateEnd: _onEdgeUpdateEnd,
2192
2158
  updateConnection,
2193
2159
  getTransform: () => store.getState().transform,
2160
+ getConnectionStartHandle: () => store.getState().connectionStartHandle,
2194
2161
  });
2195
2162
  };
2196
2163
  const onEdgeUpdaterSourceMouseDown = (event) => handleEdgeUpdater(event, true);
@@ -2397,7 +2364,7 @@ const ConnectionLine = ({ nodeId, handleType, style, type = ConnectionLineType.B
2397
2364
  toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2],
2398
2365
  connectionMode: s.connectionMode,
2399
2366
  }), [nodeId]), shallow);
2400
- const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds;
2367
+ const fromHandleBounds = fromNode?.internals?.handleBounds;
2401
2368
  let handleBounds = fromHandleBounds?.[handleType];
2402
2369
  if (connectionMode === ConnectionMode.Loose) {
2403
2370
  handleBounds = handleBounds ? handleBounds : fromHandleBounds?.[handleType === 'source' ? 'target' : 'source'];
@@ -2406,10 +2373,10 @@ const ConnectionLine = ({ nodeId, handleType, style, type = ConnectionLineType.B
2406
2373
  return null;
2407
2374
  }
2408
2375
  const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0];
2409
- const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.computed?.width ?? 0) / 2;
2410
- const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.computed?.height ?? 0;
2411
- const fromX = (fromNode.computed?.positionAbsolute?.x ?? 0) + fromHandleX;
2412
- const fromY = (fromNode.computed?.positionAbsolute?.y ?? 0) + fromHandleY;
2376
+ const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.measured.width ?? 0) / 2;
2377
+ const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.measured.height ?? 0;
2378
+ const fromX = (fromNode.internals.positionAbsolute.x ?? 0) + fromHandleX;
2379
+ const fromY = (fromNode.internals.positionAbsolute.y ?? 0) + fromHandleY;
2413
2380
  const fromPosition = fromHandle?.position;
2414
2381
  const toPosition = fromPosition ? oppositePosition[fromPosition] : null;
2415
2382
  if (!fromPosition || !toPosition) {
@@ -2498,13 +2465,13 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
2498
2465
  const storeEdges = defaultEdges ?? edges ?? [];
2499
2466
  const storeNodes = defaultNodes ?? nodes ?? [];
2500
2467
  updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
2501
- const nextNodes = adoptUserProvidedNodes(storeNodes, nodeLookup, {
2468
+ adoptUserNodes(storeNodes, nodeLookup, {
2502
2469
  nodeOrigin: [0, 0],
2503
2470
  elevateNodesOnSelect: false,
2504
2471
  });
2505
2472
  let transform = [0, 0, 1];
2506
2473
  if (fitView && width && height) {
2507
- const nodesWithDimensions = nextNodes.filter((node) => (node.width || node.initialWidth) && (node.height || node.initialHeight));
2474
+ const nodesWithDimensions = storeNodes.filter((node) => (node.width || node.initialWidth) && (node.height || node.initialHeight));
2508
2475
  // @todo users nodeOrigin should be used here
2509
2476
  const bounds = getNodesBounds(nodesWithDimensions, { nodeOrigin: [0, 0] });
2510
2477
  const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
@@ -2515,7 +2482,7 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
2515
2482
  width: 0,
2516
2483
  height: 0,
2517
2484
  transform,
2518
- nodes: nextNodes,
2485
+ nodes: storeNodes,
2519
2486
  nodeLookup,
2520
2487
  edges: storeEdges,
2521
2488
  edgeLookup,
@@ -2581,8 +2548,8 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
2581
2548
  //
2582
2549
  // When this happens, we take the note objects passed by the user and extend them with fields
2583
2550
  // relevant for internal React Flow operations.
2584
- const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
2585
- set({ nodes: nodesWithInternalData });
2551
+ adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
2552
+ set({ nodes });
2586
2553
  },
2587
2554
  setEdges: (edges) => {
2588
2555
  const { connectionLookup, edgeLookup } = get();
@@ -2605,25 +2572,18 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
2605
2572
  // changes its dimensions, this function is called to measure the
2606
2573
  // new dimensions and update the nodes.
2607
2574
  updateNodeDimensions: (updates) => {
2608
- const { onNodesChange, fitView, nodes, nodeLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, } = get();
2609
- const changes = [];
2610
- const updatedNodes = updateNodeDimensions(updates, nodes, nodeLookup, domNode, nodeOrigin, (id, dimensions) => {
2611
- changes.push({
2612
- id: id,
2613
- type: 'dimensions',
2614
- dimensions,
2615
- });
2616
- });
2617
- if (!updatedNodes) {
2575
+ const { onNodesChange, fitView, nodeLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, } = get();
2576
+ const changes = updateNodeDimensions(updates, nodeLookup, domNode, nodeOrigin);
2577
+ if (changes.length === 0) {
2618
2578
  return;
2619
2579
  }
2620
- const nextNodes = updateAbsolutePositions(updatedNodes, nodeLookup, nodeOrigin);
2580
+ updateAbsolutePositions(nodeLookup, { nodeOrigin });
2621
2581
  // we call fitView once initially after all dimensions are set
2622
2582
  let nextFitViewDone = fitViewDone;
2623
2583
  if (!fitViewDone && fitViewOnInit) {
2624
- nextFitViewDone = fitView(nextNodes, {
2584
+ nextFitViewDone = fitView({
2625
2585
  ...fitViewOnInitOptions,
2626
- nodes: fitViewOnInitOptions?.nodes || nextNodes,
2586
+ nodes: fitViewOnInitOptions?.nodes,
2627
2587
  });
2628
2588
  }
2629
2589
  // here we are cirmumventing the onNodesChange handler
@@ -2631,7 +2591,7 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
2631
2591
  // has not provided an onNodesChange handler.
2632
2592
  // Nodes are only rendered if they have a width and height
2633
2593
  // attribute which they get from this handler.
2634
- set({ nodes: nextNodes, fitViewDone: nextFitViewDone });
2594
+ set({ fitViewDone: nextFitViewDone });
2635
2595
  if (changes?.length > 0) {
2636
2596
  if (debug) {
2637
2597
  console.log('React Flow: trigger node changes', changes);
@@ -2640,16 +2600,35 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
2640
2600
  }
2641
2601
  },
2642
2602
  updateNodePositions: (nodeDragItems, dragging = false) => {
2603
+ const { nodeLookup } = get();
2604
+ const triggerChangeNodes = [];
2643
2605
  const changes = nodeDragItems.map((node) => {
2606
+ // @todo add expandParent to drag item so that we can get rid of the look up here
2607
+ const internalNode = nodeLookup.get(node.id);
2644
2608
  const change = {
2645
2609
  id: node.id,
2646
2610
  type: 'position',
2647
2611
  position: node.position,
2648
- positionAbsolute: node.computed?.positionAbsolute,
2649
2612
  dragging,
2650
2613
  };
2614
+ if (internalNode?.expandParent && change.position) {
2615
+ triggerChangeNodes.push({
2616
+ ...internalNode,
2617
+ position: change.position,
2618
+ internals: {
2619
+ ...internalNode.internals,
2620
+ positionAbsolute: node.internals.positionAbsolute,
2621
+ },
2622
+ });
2623
+ change.position.x = Math.max(0, change.position.x);
2624
+ change.position.y = Math.max(0, change.position.y);
2625
+ }
2651
2626
  return change;
2652
2627
  });
2628
+ if (triggerChangeNodes.length > 0) {
2629
+ const parentExpandChanges = handleParentExpand(triggerChangeNodes, nodeLookup);
2630
+ changes.push(...parentExpandChanges);
2631
+ }
2653
2632
  get().triggerNodeChanges(changes);
2654
2633
  },
2655
2634
  triggerNodeChanges: (changes) => {
@@ -2679,24 +2658,24 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
2679
2658
  }
2680
2659
  },
2681
2660
  addSelectedNodes: (selectedNodeIds) => {
2682
- const { multiSelectionActive, edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
2661
+ const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
2683
2662
  if (multiSelectionActive) {
2684
2663
  const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
2685
2664
  triggerNodeChanges(nodeChanges);
2686
2665
  return;
2687
2666
  }
2688
- triggerNodeChanges(getSelectionChanges(nodes, new Set([...selectedNodeIds]), true));
2689
- triggerEdgeChanges(getSelectionChanges(edges));
2667
+ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set([...selectedNodeIds]), true));
2668
+ triggerEdgeChanges(getSelectionChanges(edgeLookup));
2690
2669
  },
2691
2670
  addSelectedEdges: (selectedEdgeIds) => {
2692
- const { multiSelectionActive, edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
2671
+ const { multiSelectionActive, edgeLookup, nodeLookup, triggerNodeChanges, triggerEdgeChanges } = get();
2693
2672
  if (multiSelectionActive) {
2694
2673
  const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
2695
2674
  triggerEdgeChanges(changedEdges);
2696
2675
  return;
2697
2676
  }
2698
- triggerEdgeChanges(getSelectionChanges(edges, new Set([...selectedEdgeIds])));
2699
- triggerNodeChanges(getSelectionChanges(nodes, new Set(), true));
2677
+ triggerEdgeChanges(getSelectionChanges(edgeLookup, new Set([...selectedEdgeIds])));
2678
+ triggerNodeChanges(getSelectionChanges(nodeLookup, new Set(), true));
2700
2679
  },
2701
2680
  unselectNodesAndEdges: ({ nodes, edges } = {}) => {
2702
2681
  const { edges: storeEdges, nodes: storeNodes, triggerNodeChanges, triggerEdgeChanges } = get();
@@ -2732,32 +2711,32 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
2732
2711
  triggerEdgeChanges(edgeChanges);
2733
2712
  },
2734
2713
  setNodeExtent: (nodeExtent) => {
2735
- const { nodes } = get();
2714
+ const { nodeLookup } = get();
2715
+ for (const [, node] of nodeLookup) {
2716
+ const positionAbsolute = clampPosition(node.position, nodeExtent);
2717
+ nodeLookup.set(node.id, {
2718
+ ...node,
2719
+ internals: {
2720
+ ...node.internals,
2721
+ positionAbsolute,
2722
+ },
2723
+ });
2724
+ }
2736
2725
  set({
2737
2726
  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
2727
  });
2749
2728
  },
2750
2729
  panBy: (delta) => {
2751
2730
  const { transform, width, height, panZoom, translateExtent } = get();
2752
2731
  return panBy({ delta, panZoom, transform, translateExtent, width, height });
2753
2732
  },
2754
- fitView: (nodes, options) => {
2755
- const { panZoom, width, height, minZoom, maxZoom, nodeOrigin } = get();
2733
+ fitView: (options) => {
2734
+ const { panZoom, width, height, minZoom, maxZoom, nodeOrigin, nodeLookup } = get();
2756
2735
  if (!panZoom) {
2757
2736
  return false;
2758
2737
  }
2759
2738
  return fitView({
2760
- nodes,
2739
+ nodeLookup,
2761
2740
  width,
2762
2741
  height,
2763
2742
  panZoom,
@@ -2820,7 +2799,7 @@ function ReactFlow({ nodes, edges, defaultNodes, defaultEdges, className, nodeTy
2820
2799
  const colorModeClassName = useColorModeClass(colorMode);
2821
2800
  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$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, 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, debug: debug }), jsx(SelectionListener, { onSelectionChange: onSelectionChange }), children, jsx(Attribution, { proOptions: proOptions, position: attributionPosition }), jsx(A11yDescriptions, { rfId: rfId, disableKeyboardA11y: disableKeyboardA11y })] }) }));
2822
2801
  }
2823
- var index = forwardRef(ReactFlow);
2802
+ var index = fixedForwardRef(ReactFlow);
2824
2803
 
2825
2804
  const selector$8 = (s) => s.domNode?.querySelector('.react-flow__edgelabel-renderer');
2826
2805
  function EdgeLabelRenderer({ children }) {
@@ -2855,7 +2834,7 @@ function useUpdateNodeInternals() {
2855
2834
  updateIds.forEach((updateId) => {
2856
2835
  const nodeElement = domNode?.querySelector(`.react-flow__node[data-id="${updateId}"]`);
2857
2836
  if (nodeElement) {
2858
- updates.set(updateId, { id: updateId, nodeElement, forceUpdate: true });
2837
+ updates.set(updateId, { id: updateId, nodeElement, force: true });
2859
2838
  }
2860
2839
  });
2861
2840
  requestAnimationFrame(() => updateNodeDimensions(updates));
@@ -2967,12 +2946,12 @@ function useOnSelectionChange({ onChange }) {
2967
2946
  }
2968
2947
 
2969
2948
  const selector$6 = (options) => (s) => {
2970
- if (s.nodes.length === 0) {
2949
+ if (s.nodeLookup.size === 0) {
2971
2950
  return false;
2972
2951
  }
2973
- for (const node of s.nodes) {
2952
+ for (const [, node] of s.nodeLookup) {
2974
2953
  if (options.includeHiddenNodes || !node.hidden) {
2975
- if (node[internalsSymbol]?.handleBounds === undefined) {
2954
+ if (node.internals.handleBounds === undefined) {
2976
2955
  return false;
2977
2956
  }
2978
2957
  }
@@ -3060,6 +3039,18 @@ function useConnection() {
3060
3039
  return ongoingConnection;
3061
3040
  }
3062
3041
 
3042
+ /**
3043
+ * Hook for getting an internal node by id
3044
+ *
3045
+ * @public
3046
+ * @param id - id of the node
3047
+ * @returns array with visible node ids
3048
+ */
3049
+ function useInternalNode(id) {
3050
+ const node = useStore(useCallback((s) => s.nodeLookup.get(id), [id]), shallow);
3051
+ return node;
3052
+ }
3053
+
3063
3054
  function LinePattern({ dimensions, lineWidth, variant, className }) {
3064
3055
  return (jsx("path", { strokeWidth: lineWidth, d: `M${dimensions[0] / 2} 0 V${dimensions[1]} M0 ${dimensions[1] / 2} H${dimensions[0]}`, className: cc(['react-flow__background-pattern', variant, className]) }));
3065
3056
  }
@@ -3137,7 +3128,7 @@ const selector$3 = (s) => ({
3137
3128
  minZoomReached: s.transform[2] <= s.minZoom,
3138
3129
  maxZoomReached: s.transform[2] >= s.maxZoom,
3139
3130
  });
3140
- 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', }) {
3131
+ 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', }) {
3141
3132
  const store = useStoreApi();
3142
3133
  const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector$3, shallow);
3143
3134
  const { zoomIn, zoomOut, fitView } = useReactFlow();
@@ -3161,7 +3152,8 @@ function ControlsComponent({ style, showZoom = true, showFitView = true, showInt
3161
3152
  });
3162
3153
  onInteractiveChange?.(!isInteractive);
3163
3154
  };
3164
- 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] }));
3155
+ const orientationClass = orientation === 'horizontal' ? 'horizontal' : 'vertical';
3156
+ 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] }));
3165
3157
  }
3166
3158
  ControlsComponent.displayName = 'Controls';
3167
3159
  const Controls = memo(ControlsComponent);
@@ -3228,7 +3220,9 @@ const selector$1 = (s) => {
3228
3220
  };
3229
3221
  return {
3230
3222
  viewBB,
3231
- boundingRect: s.nodes.length > 0 ? getBoundsOfRects(getNodesBounds(s.nodes, { nodeOrigin: s.nodeOrigin }), viewBB) : viewBB,
3223
+ boundingRect: s.nodeLookup.size > 0
3224
+ ? getBoundsOfRects(getInternalNodesBounds(s.nodeLookup, { nodeOrigin: s.nodeOrigin }), viewBB)
3225
+ : viewBB,
3232
3226
  rfId: s.rfId,
3233
3227
  nodeOrigin: s.nodeOrigin,
3234
3228
  panZoom: s.panZoom,
@@ -3435,12 +3429,12 @@ function NodeToolbarPortal({ children }) {
3435
3429
  return createPortal(children, wrapperRef);
3436
3430
  }
3437
3431
 
3438
- const nodeEqualityFn = (a, b) => a?.computed?.positionAbsolute?.x !== b?.computed?.positionAbsolute?.x ||
3439
- a?.computed?.positionAbsolute?.y !== b?.computed?.positionAbsolute?.y ||
3440
- a?.computed?.width !== b?.computed?.width ||
3441
- a?.computed?.height !== b?.computed?.height ||
3432
+ const nodeEqualityFn = (a, b) => a?.internals.positionAbsolute.x !== b?.internals.positionAbsolute.x ||
3433
+ a?.internals.positionAbsolute.y !== b?.internals.positionAbsolute.y ||
3434
+ a?.measured.width !== b?.measured.width ||
3435
+ a?.measured.height !== b?.measured.height ||
3442
3436
  a?.selected !== b?.selected ||
3443
- a?.[internalsSymbol]?.z !== b?.[internalsSymbol]?.z;
3437
+ a?.internals.z !== b?.internals.z;
3444
3438
  const nodesEqualityFn = (a, b) => {
3445
3439
  if (a.length !== b.length) {
3446
3440
  return false;
@@ -3476,7 +3470,7 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
3476
3470
  return null;
3477
3471
  }
3478
3472
  const nodeRect = getNodesBounds(nodes, { nodeOrigin });
3479
- const zIndex = Math.max(...nodes.map((node) => (node[internalsSymbol]?.z || 1) + 1));
3473
+ const zIndex = Math.max(...nodes.map((node) => (node.internals?.z || 1) + 1));
3480
3474
  const wrapperStyle = {
3481
3475
  position: 'absolute',
3482
3476
  transform: getNodeToolbarTransform(nodeRect, viewport, position, offset, align),
@@ -3486,4 +3480,4 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
3486
3480
  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 }) }));
3487
3481
  }
3488
3482
 
3489
- 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, handleParentExpand, isEdge, isNode, useConnection, useEdges, useEdgesState, useHandleConnections, useKeyPress, useNodeId, useNodes, useNodesData, useNodesInitialized, useNodesState, useOnSelectionChange, useOnViewportChange, useReactFlow, useStore, useStoreApi, useUpdateNodeInternals, useViewport };
3483
+ 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 };