@xyflow/react 12.0.0-next.5 → 12.0.0-next.7

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 (77) hide show
  1. package/LICENSE +1 -1
  2. package/dist/esm/additional-components/Background/types.d.ts +13 -0
  3. package/dist/esm/additional-components/Background/types.d.ts.map +1 -1
  4. package/dist/esm/additional-components/Controls/Controls.d.ts +1 -1
  5. package/dist/esm/additional-components/Controls/Controls.d.ts.map +1 -1
  6. package/dist/esm/additional-components/Controls/types.d.ts +19 -2
  7. package/dist/esm/additional-components/Controls/types.d.ts.map +1 -1
  8. package/dist/esm/additional-components/MiniMap/types.d.ts +21 -0
  9. package/dist/esm/additional-components/MiniMap/types.d.ts.map +1 -1
  10. package/dist/esm/additional-components/NodeResizer/types.d.ts +25 -0
  11. package/dist/esm/additional-components/NodeResizer/types.d.ts.map +1 -1
  12. package/dist/esm/additional-components/NodeToolbar/types.d.ts +10 -0
  13. package/dist/esm/additional-components/NodeToolbar/types.d.ts.map +1 -1
  14. package/dist/esm/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -1
  15. package/dist/esm/components/Handle/index.d.ts.map +1 -1
  16. package/dist/esm/components/Panel/index.d.ts +3 -0
  17. package/dist/esm/components/Panel/index.d.ts.map +1 -1
  18. package/dist/esm/container/ReactFlow/index.d.ts +3 -3
  19. package/dist/esm/hooks/useNodesEdgesState.d.ts +2 -2
  20. package/dist/esm/hooks/useNodesEdgesState.d.ts.map +1 -1
  21. package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
  22. package/dist/esm/index.d.ts +2 -2
  23. package/dist/esm/index.d.ts.map +1 -1
  24. package/dist/esm/index.js +168 -163
  25. package/dist/esm/index.mjs +168 -163
  26. package/dist/esm/types/changes.d.ts +8 -6
  27. package/dist/esm/types/changes.d.ts.map +1 -1
  28. package/dist/esm/types/component-props.d.ts +339 -1
  29. package/dist/esm/types/component-props.d.ts.map +1 -1
  30. package/dist/esm/types/edges.d.ts +11 -0
  31. package/dist/esm/types/edges.d.ts.map +1 -1
  32. package/dist/esm/types/general.d.ts +5 -3
  33. package/dist/esm/types/general.d.ts.map +1 -1
  34. package/dist/esm/types/instance.d.ts +1 -11
  35. package/dist/esm/types/instance.d.ts.map +1 -1
  36. package/dist/esm/utils/changes.d.ts +20 -2
  37. package/dist/esm/utils/changes.d.ts.map +1 -1
  38. package/dist/esm/utils/general.d.ts +2 -49
  39. package/dist/esm/utils/general.d.ts.map +1 -1
  40. package/dist/umd/additional-components/Background/types.d.ts +13 -0
  41. package/dist/umd/additional-components/Background/types.d.ts.map +1 -1
  42. package/dist/umd/additional-components/Controls/Controls.d.ts +1 -1
  43. package/dist/umd/additional-components/Controls/Controls.d.ts.map +1 -1
  44. package/dist/umd/additional-components/Controls/types.d.ts +19 -2
  45. package/dist/umd/additional-components/Controls/types.d.ts.map +1 -1
  46. package/dist/umd/additional-components/MiniMap/types.d.ts +21 -0
  47. package/dist/umd/additional-components/MiniMap/types.d.ts.map +1 -1
  48. package/dist/umd/additional-components/NodeResizer/types.d.ts +25 -0
  49. package/dist/umd/additional-components/NodeResizer/types.d.ts.map +1 -1
  50. package/dist/umd/additional-components/NodeToolbar/types.d.ts +10 -0
  51. package/dist/umd/additional-components/NodeToolbar/types.d.ts.map +1 -1
  52. package/dist/umd/components/EdgeWrapper/EdgeUpdateAnchors.d.ts.map +1 -1
  53. package/dist/umd/components/Handle/index.d.ts.map +1 -1
  54. package/dist/umd/components/Panel/index.d.ts +3 -0
  55. package/dist/umd/components/Panel/index.d.ts.map +1 -1
  56. package/dist/umd/container/ReactFlow/index.d.ts +3 -3
  57. package/dist/umd/hooks/useNodesEdgesState.d.ts +2 -2
  58. package/dist/umd/hooks/useNodesEdgesState.d.ts.map +1 -1
  59. package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
  60. package/dist/umd/index.d.ts +2 -2
  61. package/dist/umd/index.d.ts.map +1 -1
  62. package/dist/umd/index.js +2 -2
  63. package/dist/umd/types/changes.d.ts +8 -6
  64. package/dist/umd/types/changes.d.ts.map +1 -1
  65. package/dist/umd/types/component-props.d.ts +339 -1
  66. package/dist/umd/types/component-props.d.ts.map +1 -1
  67. package/dist/umd/types/edges.d.ts +11 -0
  68. package/dist/umd/types/edges.d.ts.map +1 -1
  69. package/dist/umd/types/general.d.ts +5 -3
  70. package/dist/umd/types/general.d.ts.map +1 -1
  71. package/dist/umd/types/instance.d.ts +1 -11
  72. package/dist/umd/types/instance.d.ts.map +1 -1
  73. package/dist/umd/utils/changes.d.ts +20 -2
  74. package/dist/umd/utils/changes.d.ts.map +1 -1
  75. package/dist/umd/utils/general.d.ts +2 -49
  76. package/dist/umd/utils/general.d.ts.map +1 -1
  77. package/package.json +4 -4
package/dist/esm/index.js CHANGED
@@ -2,8 +2,8 @@
2
2
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
3
3
  import { createContext, useContext, useMemo, useEffect, useRef, useState, useCallback, forwardRef, memo } from 'react';
4
4
  import cc from 'classcat';
5
- import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getOutgoersBase, getIncomersBase, addEdgeBase, updateEdgeBase, getConnectedEdgesBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calcNextPosition, Position, isMouseEvent, XYHandle, getHostForElement, getNodesBounds, clampPosition, internalsSymbol, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionMode, ConnectionLineType, updateConnectionLookup, adoptUserProvidedNodes, 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, getBezierEdgeCenter, getBezierPath, getEdgeCenter, getNodesBounds, getSmoothStepPath, getStraightPath, getViewportForBounds, internalsSymbol } from '@xyflow/system';
5
+ import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, isRectObject, nodeToRect, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calcNextPosition, 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, 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';
7
7
  import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
8
8
  import { shallow } from 'zustand/shallow';
9
9
  import { createPortal } from 'react-dom';
@@ -424,8 +424,8 @@ const useViewportHelper = () => {
424
424
  const viewport = getViewportForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
425
425
  panZoom?.setViewport(viewport, { duration: options?.duration });
426
426
  },
427
- screenToFlowPosition: (position) => {
428
- const { transform, snapToGrid, snapGrid, domNode } = store.getState();
427
+ screenToFlowPosition: (position, options = { snapToGrid: true }) => {
428
+ const { transform, snapGrid, domNode } = store.getState();
429
429
  if (!domNode) {
430
430
  return position;
431
431
  }
@@ -434,7 +434,7 @@ const useViewportHelper = () => {
434
434
  x: position.x - domX,
435
435
  y: position.y - domY,
436
436
  };
437
- return pointToRendererPoint(correctedPosition, transform, snapToGrid, snapGrid || [1, 1]);
437
+ return pointToRendererPoint(correctedPosition, transform, options.snapToGrid, snapGrid);
438
438
  },
439
439
  flowToScreenPosition: (position) => {
440
440
  const { transform, domNode } = store.getState();
@@ -496,97 +496,102 @@ function handleParentExpand(updatedElements, updateItem) {
496
496
  // When you drag a node for example, React Flow will send a position change update.
497
497
  // This function then applies the changes and returns the updated elements.
498
498
  function applyChanges(changes, elements) {
499
- // we need this hack to handle the setNodes and setEdges function of the useReactFlow hook for controlled flows
500
- if (changes.some((c) => c.type === 'reset')) {
501
- return changes.filter((c) => c.type === 'reset').map((c) => c.item);
502
- }
503
- let remainingChanges = [];
504
499
  const updatedElements = [];
500
+ // By storing a map of changes for each element, we can a quick lookup as we
501
+ // iterate over the elements array!
502
+ const changesMap = new Map();
505
503
  for (const change of changes) {
506
504
  if (change.type === 'add') {
507
505
  updatedElements.push(change.item);
506
+ continue;
508
507
  }
509
- else {
510
- remainingChanges.push(change);
508
+ else if (change.type === 'remove' || change.type === 'replace') {
509
+ // For a 'remove' change we can safely ignore any other changes queued for
510
+ // the same element, it's going to be removed anyway!
511
+ changesMap.set(change.id, [change]);
511
512
  }
512
- }
513
- for (const item of elements) {
514
- const nextChanges = [];
515
- const _remainingChanges = [];
516
- for (const change of remainingChanges) {
517
- if (change.id === item.id) {
518
- nextChanges.push(change);
513
+ else {
514
+ const elementChanges = changesMap.get(change.id);
515
+ if (elementChanges) {
516
+ // If we have some changes queued already, we can do a mutable update of
517
+ // that array and save ourselves some copying.
518
+ elementChanges.push(change);
519
519
  }
520
520
  else {
521
- _remainingChanges.push(change);
521
+ changesMap.set(change.id, [change]);
522
522
  }
523
523
  }
524
- remainingChanges = _remainingChanges;
525
- if (nextChanges.length === 0) {
526
- updatedElements.push(item);
524
+ }
525
+ for (const element of elements) {
526
+ const changes = changesMap.get(element.id);
527
+ // When there are no changes for an element we can just push it unmodified,
528
+ // no need to copy it.
529
+ if (!changes) {
530
+ updatedElements.push(element);
527
531
  continue;
528
532
  }
529
- const updateItem = { ...item };
530
- let isDeletion = false;
531
- for (const currentChange of nextChanges) {
532
- if (currentChange) {
533
- switch (currentChange.type) {
534
- case 'select': {
535
- updateItem.selected = currentChange.selected;
536
- break;
537
- }
538
- case 'position': {
539
- if (typeof currentChange.position !== 'undefined') {
540
- updateItem.position = currentChange.position;
541
- }
542
- if (typeof currentChange.positionAbsolute !== 'undefined') {
543
- if (!updateItem.computed) {
544
- updateItem.computed = {};
545
- }
546
- updateItem.computed.positionAbsolute = currentChange.positionAbsolute;
547
- }
548
- if (typeof currentChange.dragging !== 'undefined') {
549
- updateItem.dragging = currentChange.dragging;
550
- }
551
- if (updateItem.expandParent) {
552
- handleParentExpand(updatedElements, updateItem);
553
- }
554
- break;
555
- }
556
- case 'dimensions': {
557
- if (typeof currentChange.dimensions !== 'undefined') {
558
- if (!updateItem.computed) {
559
- updateItem.computed = {};
560
- }
561
- updateItem.computed.width = currentChange.dimensions.width;
562
- updateItem.computed.height = currentChange.dimensions.height;
563
- // this is needed for the node resizer to work
564
- if (currentChange.resizing) {
565
- updateItem.width = currentChange.dimensions.width;
566
- updateItem.height = currentChange.dimensions.height;
567
- }
568
- }
569
- if (typeof currentChange.resizing === 'boolean') {
570
- updateItem.resizing = currentChange.resizing;
571
- }
572
- if (updateItem.expandParent) {
573
- handleParentExpand(updatedElements, updateItem);
574
- }
575
- break;
576
- }
577
- case 'remove': {
578
- isDeletion = true;
579
- continue;
580
- }
581
- }
582
- }
533
+ // If we have a 'remove' change queued, it'll be the only change in the array
534
+ if (changes[0].type === 'remove') {
535
+ continue;
536
+ }
537
+ if (changes[0].type === 'replace') {
538
+ updatedElements.push({ ...changes[0].item });
539
+ continue;
583
540
  }
584
- if (!isDeletion) {
585
- updatedElements.push(updateItem);
541
+ // For other types of changes, we want to start with a shallow copy of the
542
+ // object so React knows this element has changed. Sequential changes will
543
+ /// each _mutate_ this object, so there's only ever one copy.
544
+ const updatedElement = { ...element };
545
+ for (const change of changes) {
546
+ applyChange(change, updatedElement, elements);
586
547
  }
548
+ updatedElements.push(updatedElement);
587
549
  }
588
550
  return updatedElements;
589
551
  }
552
+ // Applies a single change to an element. This is a *mutable* update.
553
+ function applyChange(change, element, elements = []) {
554
+ switch (change.type) {
555
+ case 'select': {
556
+ element.selected = change.selected;
557
+ break;
558
+ }
559
+ case 'position': {
560
+ if (typeof change.position !== 'undefined') {
561
+ element.position = change.position;
562
+ }
563
+ if (typeof change.positionAbsolute !== 'undefined') {
564
+ element.computed ??= {};
565
+ element.computed.positionAbsolute = change.positionAbsolute;
566
+ }
567
+ if (typeof change.dragging !== 'undefined') {
568
+ element.dragging = change.dragging;
569
+ }
570
+ if (element.expandParent) {
571
+ handleParentExpand(elements, element);
572
+ }
573
+ break;
574
+ }
575
+ case 'dimensions': {
576
+ if (typeof change.dimensions !== 'undefined') {
577
+ element.computed ??= {};
578
+ element.computed.width = change.dimensions.width;
579
+ element.computed.height = change.dimensions.height;
580
+ if (change.resizing) {
581
+ element.width = change.dimensions.width;
582
+ element.height = change.dimensions.height;
583
+ }
584
+ }
585
+ if (typeof change.resizing === 'boolean') {
586
+ element.resizing = change.resizing;
587
+ }
588
+ if (element.expandParent) {
589
+ handleParentExpand(elements, element);
590
+ }
591
+ break;
592
+ }
593
+ }
594
+ }
590
595
  /**
591
596
  * Drop in function that applies node changes to an array of nodes.
592
597
  * @public
@@ -655,6 +660,26 @@ function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false)
655
660
  }
656
661
  return changes;
657
662
  }
663
+ function getElementsDiffChanges({ items = [], lookup, }) {
664
+ const changes = [];
665
+ const itemsLookup = new Map(items.map((item) => [item.id, item]));
666
+ for (const item of items) {
667
+ const storeItem = lookup.get(item.id);
668
+ if (storeItem !== undefined && storeItem !== item) {
669
+ changes.push({ id: item.id, item: item, type: 'replace' });
670
+ }
671
+ if (storeItem === undefined) {
672
+ changes.push({ item: item, type: 'add' });
673
+ }
674
+ }
675
+ for (const [id] of lookup) {
676
+ const nextNode = itemsLookup.get(id);
677
+ if (nextNode === undefined) {
678
+ changes.push({ id, type: 'remove' });
679
+ }
680
+ }
681
+ return changes;
682
+ }
658
683
 
659
684
  /**
660
685
  * Test whether an object is useable as a Node
@@ -663,7 +688,7 @@ function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false)
663
688
  * @param element - The element to test
664
689
  * @returns A boolean indicating whether the element is an Node
665
690
  */
666
- const isNode = (isNodeBase);
691
+ const isNode = (element) => isNodeBase(element);
667
692
  /**
668
693
  * Test whether an object is useable as an Edge
669
694
  * @public
@@ -671,50 +696,7 @@ const isNode = (isNodeBase);
671
696
  * @param element - The element to test
672
697
  * @returns A boolean indicating whether the element is an Edge
673
698
  */
674
- const isEdge = (isEdgeBase);
675
- /**
676
- * Pass in a node, and get connected nodes where edge.source === node.id
677
- * @public
678
- * @param node - The node to get the connected nodes from
679
- * @param nodes - The array of all nodes
680
- * @param edges - The array of all edges
681
- * @returns An array of nodes that are connected over eges where the source is the given node
682
- */
683
- const getOutgoers = (getOutgoersBase);
684
- /**
685
- * Pass in a node, and get connected nodes where edge.target === node.id
686
- * @public
687
- * @param node - The node to get the connected nodes from
688
- * @param nodes - The array of all nodes
689
- * @param edges - The array of all edges
690
- * @returns An array of nodes that are connected over eges where the target is the given node
691
- */
692
- const getIncomers = (getIncomersBase);
693
- /**
694
- * This util is a convenience function to add a new Edge to an array of edges
695
- * @remarks It also performs some validation to make sure you don't add an invalid edge or duplicate an existing one.
696
- * @public
697
- * @param edgeParams - Either an Edge or a Connection you want to add
698
- * @param edges - The array of all current edges
699
- * @returns A new array of edges with the new edge added
700
- */
701
- const addEdge = (addEdgeBase);
702
- /**
703
- * A handy utility to update an existing Edge with new properties
704
- * @param oldEdge - The edge you want to update
705
- * @param newConnection - The new connection you want to update the edge with
706
- * @param edges - The array of all current edges
707
- * @param options.shouldReplaceId - should the id of the old edge be replaced with the new connection id
708
- * @returns the updated edges array
709
- */
710
- const updateEdge = (updateEdgeBase);
711
- /**
712
- * Get all connecting edges for a given set of nodes
713
- * @param nodes - Nodes you want to get the connected edges for
714
- * @param edges - All edges
715
- * @returns Array of edges that connect any of the given nodes with each other
716
- */
717
- const getConnectedEdges = (getConnectedEdgesBase);
699
+ const isEdge = (element) => isEdgeBase(element);
718
700
 
719
701
  /**
720
702
  * Hook for accessing the ReactFlow instance.
@@ -739,31 +721,50 @@ function useReactFlow() {
739
721
  const { edges = [] } = store.getState();
740
722
  return edges.find((e) => e.id === id);
741
723
  }, []);
724
+ // this is used to handle multiple syncronous setNodes calls
725
+ const setNodesData = useRef();
726
+ const setNodesTimeout = useRef();
742
727
  const setNodes = useCallback((payload) => {
743
- const { nodes, setNodes, hasDefaultNodes, onNodesChange } = store.getState();
744
- const nextNodes = typeof payload === 'function' ? payload(nodes) : payload;
745
- if (hasDefaultNodes) {
746
- setNodes(nextNodes);
747
- }
748
- else if (onNodesChange) {
749
- const changes = nextNodes.length === 0
750
- ? nodes.map((node) => ({ type: 'remove', id: node.id }))
751
- : nextNodes.map((node) => ({ item: node, type: 'reset' }));
752
- onNodesChange(changes);
753
- }
728
+ const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState();
729
+ const nextNodes = typeof payload === 'function' ? payload(setNodesData.current || nodes) : payload;
730
+ setNodesData.current = nextNodes;
731
+ if (setNodesTimeout.current) {
732
+ clearTimeout(setNodesTimeout.current);
733
+ }
734
+ // if there are multiple synchronous setNodes calls, we only want to call onNodesChange once
735
+ // for this, we use a timeout to wait for the last call and store updated nodes in setNodesData
736
+ // this is not perfect, but should work in most cases
737
+ setNodesTimeout.current = setTimeout(() => {
738
+ if (hasDefaultNodes) {
739
+ setNodes(nextNodes);
740
+ }
741
+ else if (onNodesChange) {
742
+ const changes = getElementsDiffChanges({ items: setNodesData.current, lookup: nodeLookup });
743
+ onNodesChange(changes);
744
+ }
745
+ setNodesData.current = undefined;
746
+ }, 0);
754
747
  }, []);
748
+ // this is used to handle multiple syncronous setEdges calls
749
+ const setEdgesData = useRef();
750
+ const setEdgesTimeout = useRef();
755
751
  const setEdges = useCallback((payload) => {
756
- const { edges = [], setEdges, hasDefaultEdges, onEdgesChange } = store.getState();
757
- const nextEdges = typeof payload === 'function' ? payload(edges) : payload;
758
- if (hasDefaultEdges) {
759
- setEdges(nextEdges);
760
- }
761
- else if (onEdgesChange) {
762
- const changes = nextEdges.length === 0
763
- ? edges.map((edge) => ({ type: 'remove', id: edge.id }))
764
- : nextEdges.map((edge) => ({ item: edge, type: 'reset' }));
765
- onEdgesChange(changes);
752
+ const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
753
+ const nextEdges = typeof payload === 'function' ? payload(setEdgesData.current || edges) : payload;
754
+ setEdgesData.current = nextEdges;
755
+ if (setEdgesTimeout.current) {
756
+ clearTimeout(setEdgesTimeout.current);
766
757
  }
758
+ setEdgesTimeout.current = setTimeout(() => {
759
+ if (hasDefaultEdges) {
760
+ setEdges(nextEdges);
761
+ }
762
+ else if (onEdgesChange) {
763
+ const changes = getElementsDiffChanges({ items: nextEdges, lookup: edgeLookup });
764
+ onEdgesChange(changes);
765
+ }
766
+ setEdgesData.current = undefined;
767
+ }, 0);
767
768
  }, []);
768
769
  const addNodes = useCallback((payload) => {
769
770
  const nodes = Array.isArray(payload) ? payload : [payload];
@@ -801,8 +802,8 @@ function useReactFlow() {
801
802
  },
802
803
  };
803
804
  }, []);
804
- const deleteElements = useCallback(async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [], onBeforeDelete }) => {
805
- const { nodes, edges, hasDefaultNodes, hasDefaultEdges, onNodesDelete, onEdgesDelete, onNodesChange, onEdgesChange, onDelete, } = store.getState();
805
+ const deleteElements = useCallback(async ({ nodes: nodesToRemove = [], edges: edgesToRemove = [] }) => {
806
+ const { nodes, edges, hasDefaultNodes, hasDefaultEdges, onNodesDelete, onEdgesDelete, onNodesChange, onEdgesChange, onDelete, onBeforeDelete, } = store.getState();
806
807
  const { nodes: matchingNodes, edges: matchingEdges } = await getElementsToRemove({
807
808
  nodesToRemove,
808
809
  edgesToRemove,
@@ -814,9 +815,8 @@ function useReactFlow() {
814
815
  const hasMatchingNodes = matchingNodes.length > 0;
815
816
  if (hasMatchingEdges) {
816
817
  if (hasDefaultEdges) {
817
- store.setState({
818
- edges: edges.filter((e) => !matchingEdges.some((mE) => mE.id === e.id)),
819
- });
818
+ const nextEdges = edges.filter((e) => !matchingEdges.some((mE) => mE.id === e.id));
819
+ store.getState().setEdges(nextEdges);
820
820
  }
821
821
  onEdgesDelete?.(matchingEdges);
822
822
  onEdgesChange?.(matchingEdges.map((edge) => ({
@@ -826,9 +826,8 @@ function useReactFlow() {
826
826
  }
827
827
  if (hasMatchingNodes) {
828
828
  if (hasDefaultNodes) {
829
- store.setState({
830
- nodes: nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id)),
831
- });
829
+ const nextNodes = nodes.filter((n) => !matchingNodes.some((mN) => mN.id === n.id));
830
+ store.getState().setNodes(nextNodes);
832
831
  }
833
832
  onNodesDelete?.(matchingNodes);
834
833
  onNodesChange?.(matchingNodes.map((node) => ({ id: node.id, type: 'remove' })));
@@ -841,6 +840,9 @@ function useReactFlow() {
841
840
  const getNodeRect = useCallback((nodeOrRect) => {
842
841
  const isRect = isRectObject(nodeOrRect);
843
842
  const node = isRect ? null : store.getState().nodeLookup.get(nodeOrRect.id);
843
+ if (!isRect && !node) {
844
+ return [null, null, isRect];
845
+ }
844
846
  const nodeRect = isRect ? nodeOrRect : nodeToRect(node);
845
847
  return [nodeRect, node, isRect];
846
848
  }, []);
@@ -934,8 +936,8 @@ function useGlobalKeyHandler({ deleteKeyCode, multiSelectionKeyCode, }) {
934
936
  const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
935
937
  useEffect(() => {
936
938
  if (deleteKeyPressed) {
937
- const { edges, nodes, onBeforeDelete } = store.getState();
938
- deleteElements({ nodes: nodes.filter(selected), edges: edges.filter(selected), onBeforeDelete });
939
+ const { edges, nodes } = store.getState();
940
+ deleteElements({ nodes: nodes.filter(selected), edges: edges.filter(selected) });
939
941
  store.setState({ nodesSelectionActive: false });
940
942
  }
941
943
  }, [deleteKeyPressed]);
@@ -1345,9 +1347,9 @@ const useNodeId = () => {
1345
1347
  };
1346
1348
 
1347
1349
  const selector$i = (s) => ({
1348
- connectionStartHandle: s.connectionStartHandle,
1349
1350
  connectOnClick: s.connectOnClick,
1350
1351
  noPanClassName: s.noPanClassName,
1352
+ rfId: s.rfId,
1351
1353
  });
1352
1354
  const connectingSelector = (nodeId, handleId, type) => (state) => {
1353
1355
  const { connectionStartHandle: startHandle, connectionEndHandle: endHandle, connectionClickStartHandle: clickHandle, } = state;
@@ -1362,7 +1364,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1362
1364
  const isTarget = type === 'target';
1363
1365
  const store = useStoreApi();
1364
1366
  const nodeId = useNodeId();
1365
- const { connectOnClick, noPanClassName } = useStore(selector$i, shallow);
1367
+ const { connectOnClick, noPanClassName, rfId } = useStore(selector$i, shallow);
1366
1368
  const { connecting, clickConnecting } = useStore(connectingSelector(nodeId, handleId, type), shallow);
1367
1369
  if (!nodeId) {
1368
1370
  store.getState().onError?.('010', errorMessages['error010']());
@@ -1398,6 +1400,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1398
1400
  isTarget,
1399
1401
  handleId,
1400
1402
  nodeId,
1403
+ flowId: currentStore.rfId,
1401
1404
  panBy: currentStore.panBy,
1402
1405
  cancelConnection: currentStore.cancelConnection,
1403
1406
  onConnectStart: currentStore.onConnectStart,
@@ -1416,7 +1419,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1416
1419
  }
1417
1420
  };
1418
1421
  const onClick = (event) => {
1419
- const { onClickConnectStart, onClickConnectEnd, connectionClickStartHandle, connectionMode, isValidConnection: isValidConnectionStore, lib, } = store.getState();
1422
+ const { onClickConnectStart, onClickConnectEnd, connectionClickStartHandle, connectionMode, isValidConnection: isValidConnectionStore, lib, rfId: flowId, } = store.getState();
1420
1423
  if (!nodeId || (!connectionClickStartHandle && !isConnectableStart)) {
1421
1424
  return;
1422
1425
  }
@@ -1438,6 +1441,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1438
1441
  fromHandleId: connectionClickStartHandle.handleId || null,
1439
1442
  fromType: connectionClickStartHandle.type,
1440
1443
  isValidConnection: isValidConnectionHandler,
1444
+ flowId,
1441
1445
  doc,
1442
1446
  lib,
1443
1447
  });
@@ -1447,7 +1451,7 @@ const HandleComponent = forwardRef(({ type = 'source', position = Position.Top,
1447
1451
  onClickConnectEnd?.(event);
1448
1452
  store.setState({ connectionClickStartHandle: null });
1449
1453
  };
1450
- return (jsx("div", { "data-handleid": handleId, "data-nodeid": nodeId, "data-handlepos": position, "data-id": `${nodeId}-${handleId}-${type}`, className: cc([
1454
+ return (jsx("div", { "data-handleid": handleId, "data-nodeid": nodeId, "data-handlepos": position, "data-id": `${rfId}-${nodeId}-${handleId}-${type}`, className: cc([
1451
1455
  'react-flow__handle',
1452
1456
  `react-flow__handle-${position}`,
1453
1457
  'nodrag',
@@ -2095,7 +2099,7 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2095
2099
  if (event.button !== 0) {
2096
2100
  return;
2097
2101
  }
2098
- const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodes, panBy, updateConnection, } = store.getState();
2102
+ const { autoPanOnConnect, domNode, isValidConnection, connectionMode, connectionRadius, lib, onConnectStart, onConnectEnd, cancelConnection, nodes, rfId: flowId, panBy, updateConnection, } = store.getState();
2099
2103
  const nodeId = isSourceHandle ? edge.target : edge.source;
2100
2104
  const handleId = (isSourceHandle ? targetHandleId : sourceHandleId) || null;
2101
2105
  const handleType = isSourceHandle ? 'target' : 'source';
@@ -2118,6 +2122,7 @@ function EdgeUpdateAnchors({ isUpdatable, edgeUpdaterRadius, edge, targetHandleI
2118
2122
  isTarget,
2119
2123
  edgeUpdaterType: handleType,
2120
2124
  lib,
2125
+ flowId,
2121
2126
  cancelConnection,
2122
2127
  panBy,
2123
2128
  isValidConnection,
@@ -3120,7 +3125,7 @@ const selector$3 = (s) => ({
3120
3125
  minZoomReached: s.transform[2] <= s.minZoom,
3121
3126
  maxZoomReached: s.transform[2] >= s.maxZoom,
3122
3127
  });
3123
- function ControlsComponent({ style, showZoom = true, showFitView = true, showInteractive = true, fitViewOptions, onZoomIn, onZoomOut, onFitView, onInteractiveChange, className, children, position = 'bottom-left', }) {
3128
+ 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', }) {
3124
3129
  const store = useStoreApi();
3125
3130
  const { isInteractive, minZoomReached, maxZoomReached } = useStore(selector$3, shallow);
3126
3131
  const { zoomIn, zoomOut, fitView } = useReactFlow();
@@ -3144,7 +3149,7 @@ function ControlsComponent({ style, showZoom = true, showFitView = true, showInt
3144
3149
  });
3145
3150
  onInteractiveChange?.(!isInteractive);
3146
3151
  };
3147
- return (jsxs(Panel, { className: cc(['react-flow__controls', className]), position: position, style: style, "data-testid": "rf__controls", 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] }));
3152
+ 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] }));
3148
3153
  }
3149
3154
  ControlsComponent.displayName = 'Controls';
3150
3155
  const Controls = memo(ControlsComponent);
@@ -3456,4 +3461,4 @@ function NodeToolbar({ nodeId, children, className, style, isVisible, position =
3456
3461
  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 }) }));
3457
3462
  }
3458
3463
 
3459
- export { Background, BackgroundVariant, BaseEdge, BezierEdge, ControlButton, Controls, EdgeLabelRenderer, EdgeText, Handle, MiniMap, NodeResizeControl, NodeResizer, NodeToolbar, Panel, ReactFlow, ReactFlowProvider, SimpleBezierEdge, SmoothStepEdge, StepEdge, StraightEdge, ViewportPortal, addEdge, applyEdgeChanges, applyNodeChanges, getConnectedEdges, getIncomers, getOutgoers, getSimpleBezierPath, handleParentExpand, isEdge, isNode, updateEdge, useConnection, useEdges, useEdgesState, useHandleConnections, useKeyPress, useNodeId, useNodes, useNodesData, useNodesInitialized, useNodesState, useOnSelectionChange, useOnViewportChange, useReactFlow, useStore, useStoreApi, useUpdateNodeInternals, useViewport };
3464
+ 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 };