@xyflow/react 12.0.0-next.14 → 12.0.0-next.16
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.
- package/dist/esm/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
- package/dist/esm/components/BatchProvider/index.d.ts +17 -0
- package/dist/esm/components/BatchProvider/index.d.ts.map +1 -0
- package/dist/esm/components/BatchProvider/types.d.ts +7 -0
- package/dist/esm/components/BatchProvider/types.d.ts.map +1 -0
- package/dist/esm/components/BatchProvider/useQueue.d.ts +11 -0
- package/dist/esm/components/BatchProvider/useQueue.d.ts.map +1 -0
- package/dist/esm/components/BatchProvider/utils.d.ts +3 -0
- package/dist/esm/components/BatchProvider/utils.d.ts.map +1 -0
- package/dist/esm/components/ElementBatcher/index.d.ts +3 -0
- package/dist/esm/components/ElementBatcher/index.d.ts.map +1 -0
- package/dist/esm/components/NodeWrapper/index.d.ts.map +1 -1
- package/dist/esm/components/NodeWrapper/useNodeObservation.d.ts +9 -0
- package/dist/esm/components/NodeWrapper/useNodeObservation.d.ts.map +1 -0
- package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts +15 -0
- package/dist/esm/components/NodeWrapper/useNodeObserver.d.ts.map +1 -0
- package/dist/esm/components/NodesSelection/index.d.ts.map +1 -1
- package/dist/esm/components/ReactFlowProvider/index.d.ts +4 -3
- package/dist/esm/components/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/esm/components/SelectionListener/index.d.ts.map +1 -1
- package/dist/esm/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
- package/dist/esm/container/Pane/index.d.ts.map +1 -1
- package/dist/esm/contexts/RFStoreContext.d.ts +2 -2
- package/dist/esm/contexts/RFStoreContext.d.ts.map +1 -1
- package/dist/esm/contexts/StoreContext.d.ts +5 -0
- package/dist/esm/contexts/StoreContext.d.ts.map +1 -0
- package/dist/esm/hooks/useElementBatching.d.ts +3 -0
- package/dist/esm/hooks/useElementBatching.d.ts.map +1 -0
- package/dist/esm/hooks/useNodesInitialized.d.ts.map +1 -1
- package/dist/esm/hooks/useReactFlow.d.ts.map +1 -1
- package/dist/esm/hooks/useStore.d.ts +0 -1
- package/dist/esm/hooks/useStore.d.ts.map +1 -1
- package/dist/esm/index.js +324 -228
- package/dist/esm/index.mjs +324 -228
- package/dist/esm/store/index.d.ts +2 -2
- package/dist/esm/store/index.d.ts.map +1 -1
- package/dist/esm/store/initialState.d.ts.map +1 -1
- package/dist/esm/types/store.d.ts +1 -0
- package/dist/esm/types/store.d.ts.map +1 -1
- package/dist/umd/additional-components/NodeResizer/NodeResizeControl.d.ts.map +1 -1
- package/dist/umd/components/BatchProvider/index.d.ts +17 -0
- package/dist/umd/components/BatchProvider/index.d.ts.map +1 -0
- package/dist/umd/components/BatchProvider/types.d.ts +7 -0
- package/dist/umd/components/BatchProvider/types.d.ts.map +1 -0
- package/dist/umd/components/BatchProvider/useQueue.d.ts +11 -0
- package/dist/umd/components/BatchProvider/useQueue.d.ts.map +1 -0
- package/dist/umd/components/ElementBatcher/index.d.ts +3 -0
- package/dist/umd/components/ElementBatcher/index.d.ts.map +1 -0
- package/dist/umd/components/NodeWrapper/index.d.ts.map +1 -1
- package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts +15 -0
- package/dist/umd/components/NodeWrapper/useNodeObserver.d.ts.map +1 -0
- package/dist/umd/components/NodesSelection/index.d.ts.map +1 -1
- package/dist/umd/components/ReactFlowProvider/index.d.ts +4 -3
- package/dist/umd/components/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/umd/components/SelectionListener/index.d.ts.map +1 -1
- package/dist/umd/container/NodeRenderer/useResizeObserver.d.ts.map +1 -1
- package/dist/umd/container/Pane/index.d.ts.map +1 -1
- package/dist/umd/contexts/StoreContext.d.ts +5 -0
- package/dist/umd/contexts/StoreContext.d.ts.map +1 -0
- package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
- package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
- package/dist/umd/hooks/useStore.d.ts +0 -1
- package/dist/umd/hooks/useStore.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/dist/umd/store/index.d.ts +2 -2
- package/dist/umd/store/index.d.ts.map +1 -1
- package/dist/umd/store/initialState.d.ts.map +1 -1
- package/dist/umd/types/store.d.ts +1 -0
- package/dist/umd/types/store.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
3
3
|
import cc from 'classcat';
|
|
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,
|
|
4
|
+
import { errorMessages, infiniteExtent, isInputDOMNode, fitView, getViewportForBounds, pointToRendererPoint, rendererPointToPoint, isNodeBase, isEdgeBase, getElementsToRemove, evaluateAbsolutePosition, nodeToRect, isRectObject, getOverlappingArea, getDimensions, XYPanZoom, PanOnScrollMode, SelectionMode, getEventPosition, getNodesInside, XYDrag, snapPosition, calculateNodePosition, Position, ConnectionMode, isMouseEvent, XYHandle, getHostForElement, addEdge, getInternalNodesBounds, nodeHasDimensions, getNodeDimensions, clampPosition, getPositionWithOrigin, elementSelectionKeys, isEdgeVisible, MarkerType, createMarkerIds, isNumeric, getBezierEdgeCenter, getSmoothStepPath, getStraightPath, getBezierPath, getEdgePosition, getElevatedEdgeZIndex, getMarkerId, ConnectionLineType, updateConnectionLookup, adoptUserNodes, devWarn, updateNodeInternals, updateAbsolutePositions, handleExpandParent, panBy, isMacOs, areConnectionMapsEqual, handleConnectionChange, shallowNodeData, getNodePositionWithOrigin, XYMinimap, getBoundsOfRects, ResizeControlVariant, XYResizer, XY_RESIZER_LINE_POSITIONS, XY_RESIZER_HANDLE_POSITIONS, getNodesBounds, getNodeToolbarTransform } from '@xyflow/system';
|
|
5
5
|
export { ConnectionLineType, ConnectionMode, MarkerType, PanOnScrollMode, Position, SelectionMode, addEdge, getBezierEdgeCenter, getBezierPath, getConnectedEdges, getEdgeCenter, getIncomers, getNodesBounds, getOutgoers, getSmoothStepPath, getStraightPath, getViewportForBounds, updateEdge } from '@xyflow/system';
|
|
6
6
|
import { createContext, useContext, useMemo, useEffect, useRef, useState, forwardRef, useLayoutEffect, useCallback, memo } from 'react';
|
|
7
7
|
import { useStoreWithEqualityFn, createWithEqualityFn } from 'zustand/traditional';
|
|
@@ -40,7 +40,6 @@ function useStoreApi() {
|
|
|
40
40
|
getState: store.getState,
|
|
41
41
|
setState: store.setState,
|
|
42
42
|
subscribe: store.subscribe,
|
|
43
|
-
destroy: store.destroy,
|
|
44
43
|
}), [store]);
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -82,10 +81,21 @@ function Attribution({ proOptions, position = 'bottom-right' }) {
|
|
|
82
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" }) }));
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
const selector$o = (s) =>
|
|
86
|
-
selectedNodes
|
|
87
|
-
selectedEdges
|
|
88
|
-
|
|
84
|
+
const selector$o = (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
|
+
};
|
|
89
99
|
const selectId = (obj) => obj.id;
|
|
90
100
|
function areEqual(a, b) {
|
|
91
101
|
return (shallow(a.selectedNodes.map(selectId), b.selectedNodes.map(selectId)) &&
|
|
@@ -548,7 +558,7 @@ function applyChange(change, element) {
|
|
|
548
558
|
element.measured ??= {};
|
|
549
559
|
element.measured.width = change.dimensions.width;
|
|
550
560
|
element.measured.height = change.dimensions.height;
|
|
551
|
-
if (change.
|
|
561
|
+
if (change.setAttributes) {
|
|
552
562
|
element.width = change.dimensions.width;
|
|
553
563
|
element.height = change.dimensions.height;
|
|
554
564
|
}
|
|
@@ -615,8 +625,8 @@ function createSelectionChange(id, selected) {
|
|
|
615
625
|
}
|
|
616
626
|
function getSelectionChanges(items, selectedIds = new Set(), mutateItem = false) {
|
|
617
627
|
const changes = [];
|
|
618
|
-
for (const [, item] of items) {
|
|
619
|
-
const willBeSelected = selectedIds.has(
|
|
628
|
+
for (const [id, item] of items) {
|
|
629
|
+
const willBeSelected = selectedIds.has(id);
|
|
620
630
|
// we don't want to set all items to selected=false on the first selection
|
|
621
631
|
if (!(item.selected === undefined && !willBeSelected) && item.selected !== willBeSelected) {
|
|
622
632
|
if (mutateItem) {
|
|
@@ -634,7 +644,8 @@ function getElementsDiffChanges({ items = [], lookup, }) {
|
|
|
634
644
|
const changes = [];
|
|
635
645
|
const itemsLookup = new Map(items.map((item) => [item.id, item]));
|
|
636
646
|
for (const item of items) {
|
|
637
|
-
const
|
|
647
|
+
const lookupItem = lookup.get(item.id);
|
|
648
|
+
const storeItem = lookupItem?.internals?.userNode ?? lookupItem;
|
|
638
649
|
if (storeItem !== undefined && storeItem !== item) {
|
|
639
650
|
changes.push({ id: item.id, item: item, type: 'replace' });
|
|
640
651
|
}
|
|
@@ -677,33 +688,22 @@ function fixedForwardRef(render) {
|
|
|
677
688
|
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
678
689
|
|
|
679
690
|
/**
|
|
680
|
-
*
|
|
691
|
+
* This hook returns a queue that can be used to batch updates.
|
|
681
692
|
*
|
|
682
|
-
* @
|
|
683
|
-
* @
|
|
693
|
+
* @param runQueue - a function that gets called when the queue is flushed
|
|
694
|
+
* @internal
|
|
695
|
+
*
|
|
696
|
+
* @returns a Queue object
|
|
684
697
|
*/
|
|
685
|
-
function
|
|
686
|
-
const viewportHelper = useViewportHelper();
|
|
687
|
-
const store = useStoreApi();
|
|
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]);
|
|
691
|
-
const getEdges = useCallback(() => {
|
|
692
|
-
const { edges = [] } = store.getState();
|
|
693
|
-
return edges.map((e) => ({ ...e }));
|
|
694
|
-
}, []);
|
|
695
|
-
const getEdge = useCallback((id) => {
|
|
696
|
-
const { edges = [] } = store.getState();
|
|
697
|
-
return edges.find((e) => e.id === id);
|
|
698
|
-
}, []);
|
|
699
|
-
// A reference of all the batched updates to process before the next render. We
|
|
700
|
-
// want a mutable reference here so multiple synchronous calls to `setNodes` etc
|
|
701
|
-
// can be batched together.
|
|
702
|
-
const setElementsQueue = useRef({ nodes: [], edges: [] });
|
|
698
|
+
function useQueue(runQueue) {
|
|
703
699
|
// Because we're using a ref above, we need some way to let React know when to
|
|
704
700
|
// actually process the queue. We flip this bit of state to `true` any time we
|
|
705
701
|
// mutate the queue and then flip it back to `false` after flushing the queue.
|
|
706
|
-
const [
|
|
702
|
+
const [shouldFlush, setShouldFlush] = useState(false);
|
|
703
|
+
// A reference of all the batched updates to process before the next render. We
|
|
704
|
+
// want a reference here so multiple synchronous calls to `setNodes` etc can be
|
|
705
|
+
// batched together.
|
|
706
|
+
const [queue] = useState(() => createQueue(() => setShouldFlush(true)));
|
|
707
707
|
// Layout effects are guaranteed to run before the next render which means we
|
|
708
708
|
// shouldn't run into any issues with stale state or weird issues that come from
|
|
709
709
|
// rendering things one frame later than expected (we used to use `setTimeout`).
|
|
@@ -712,70 +712,123 @@ function useReactFlow() {
|
|
|
712
712
|
// trigger the hook again (!). If the hook is being run again we know that any
|
|
713
713
|
// updates should have been processed by now and we can safely clear the queue
|
|
714
714
|
// and bail early.
|
|
715
|
-
if (!
|
|
716
|
-
|
|
715
|
+
if (!shouldFlush) {
|
|
716
|
+
queue.reset();
|
|
717
717
|
return;
|
|
718
718
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
// array methods where we can.
|
|
724
|
-
let next = nodes;
|
|
725
|
-
for (const payload of setElementsQueue.current.nodes) {
|
|
726
|
-
next = typeof payload === 'function' ? payload(next) : payload;
|
|
727
|
-
}
|
|
728
|
-
if (hasDefaultNodes) {
|
|
729
|
-
setNodes(next);
|
|
730
|
-
}
|
|
731
|
-
else if (onNodesChange) {
|
|
732
|
-
onNodesChange(getElementsDiffChanges({
|
|
733
|
-
items: next,
|
|
734
|
-
lookup: nodeLookup,
|
|
735
|
-
}));
|
|
736
|
-
}
|
|
737
|
-
setElementsQueue.current.nodes = [];
|
|
738
|
-
}
|
|
739
|
-
if (setElementsQueue.current.edges.length) {
|
|
740
|
-
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
|
|
741
|
-
let next = edges;
|
|
742
|
-
for (const payload of setElementsQueue.current.edges) {
|
|
743
|
-
next = typeof payload === 'function' ? payload(next) : payload;
|
|
744
|
-
}
|
|
745
|
-
if (hasDefaultEdges) {
|
|
746
|
-
setEdges(next);
|
|
747
|
-
}
|
|
748
|
-
else if (onEdgesChange) {
|
|
749
|
-
onEdgesChange(getElementsDiffChanges({
|
|
750
|
-
items: next,
|
|
751
|
-
lookup: edgeLookup,
|
|
752
|
-
}));
|
|
753
|
-
}
|
|
754
|
-
setElementsQueue.current.edges = [];
|
|
719
|
+
const queueItems = queue.get();
|
|
720
|
+
if (queueItems.length) {
|
|
721
|
+
runQueue(queueItems);
|
|
722
|
+
queue.reset();
|
|
755
723
|
}
|
|
756
724
|
// Beacuse we're using reactive state to trigger this effect, we need to flip
|
|
757
725
|
// it back to false.
|
|
758
|
-
|
|
759
|
-
}, [
|
|
726
|
+
setShouldFlush(false);
|
|
727
|
+
}, [shouldFlush]);
|
|
728
|
+
return queue;
|
|
729
|
+
}
|
|
730
|
+
function createQueue(cb) {
|
|
731
|
+
let queue = [];
|
|
732
|
+
return {
|
|
733
|
+
get: () => queue,
|
|
734
|
+
reset: () => {
|
|
735
|
+
queue = [];
|
|
736
|
+
},
|
|
737
|
+
push: (item) => {
|
|
738
|
+
queue.push(item);
|
|
739
|
+
cb();
|
|
740
|
+
},
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
const BatchContext = createContext(null);
|
|
745
|
+
/**
|
|
746
|
+
* This is a context provider that holds and processes the node and edge update queues
|
|
747
|
+
* that are needed to handle setNodes, addNodes, setEdges and addEdges.
|
|
748
|
+
*
|
|
749
|
+
* @internal
|
|
750
|
+
*/
|
|
751
|
+
function BatchProvider({ children, }) {
|
|
752
|
+
const store = useStoreApi();
|
|
753
|
+
const nodeQueueHandler = useCallback((queueItems) => {
|
|
754
|
+
const { nodes = [], setNodes, hasDefaultNodes, onNodesChange, nodeLookup } = store.getState();
|
|
755
|
+
// This is essentially an `Array.reduce` in imperative clothing. Processing
|
|
756
|
+
// this queue is a relatively hot path so we'd like to avoid the overhead of
|
|
757
|
+
// array methods where we can.
|
|
758
|
+
let next = nodes;
|
|
759
|
+
for (const payload of queueItems) {
|
|
760
|
+
next = typeof payload === 'function' ? payload(next) : payload;
|
|
761
|
+
}
|
|
762
|
+
if (hasDefaultNodes) {
|
|
763
|
+
setNodes(next);
|
|
764
|
+
}
|
|
765
|
+
else if (onNodesChange) {
|
|
766
|
+
onNodesChange(getElementsDiffChanges({
|
|
767
|
+
items: next,
|
|
768
|
+
lookup: nodeLookup,
|
|
769
|
+
}));
|
|
770
|
+
}
|
|
771
|
+
}, []);
|
|
772
|
+
const nodeQueue = useQueue(nodeQueueHandler);
|
|
773
|
+
const edgeQueueHandler = useCallback((queueItems) => {
|
|
774
|
+
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
|
|
775
|
+
let next = edges;
|
|
776
|
+
for (const payload of queueItems) {
|
|
777
|
+
next = typeof payload === 'function' ? payload(next) : payload;
|
|
778
|
+
}
|
|
779
|
+
if (hasDefaultEdges) {
|
|
780
|
+
setEdges(next);
|
|
781
|
+
}
|
|
782
|
+
else if (onEdgesChange) {
|
|
783
|
+
onEdgesChange(getElementsDiffChanges({
|
|
784
|
+
items: next,
|
|
785
|
+
lookup: edgeLookup,
|
|
786
|
+
}));
|
|
787
|
+
}
|
|
788
|
+
}, []);
|
|
789
|
+
const edgeQueue = useQueue(edgeQueueHandler);
|
|
790
|
+
const value = useMemo(() => ({ nodeQueue, edgeQueue }), []);
|
|
791
|
+
return jsx(BatchContext.Provider, { value: value, children: children });
|
|
792
|
+
}
|
|
793
|
+
function useBatchContext() {
|
|
794
|
+
const batchContext = useContext(BatchContext);
|
|
795
|
+
if (!batchContext) {
|
|
796
|
+
throw new Error('useBatchContext must be used within a BatchProvider');
|
|
797
|
+
}
|
|
798
|
+
return batchContext;
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Hook for accessing the ReactFlow instance.
|
|
803
|
+
*
|
|
804
|
+
* @public
|
|
805
|
+
* @returns ReactFlowInstance
|
|
806
|
+
*/
|
|
807
|
+
function useReactFlow() {
|
|
808
|
+
const viewportHelper = useViewportHelper();
|
|
809
|
+
const store = useStoreApi();
|
|
810
|
+
const batchContext = useBatchContext();
|
|
811
|
+
const getNodes = useCallback(() => store.getState().nodes.map((n) => ({ ...n })), []);
|
|
812
|
+
const getInternalNode = useCallback((id) => store.getState().nodeLookup.get(id), []);
|
|
813
|
+
const getNode = useCallback((id) => getInternalNode(id)?.internals.userNode, [getInternalNode]);
|
|
814
|
+
const getEdges = useCallback(() => {
|
|
815
|
+
const { edges = [] } = store.getState();
|
|
816
|
+
return edges.map((e) => ({ ...e }));
|
|
817
|
+
}, []);
|
|
818
|
+
const getEdge = useCallback((id) => store.getState().edgeLookup.get(id), []);
|
|
760
819
|
const setNodes = useCallback((payload) => {
|
|
761
|
-
|
|
762
|
-
setShouldFlushQueue(true);
|
|
820
|
+
batchContext.nodeQueue.push(payload);
|
|
763
821
|
}, []);
|
|
764
822
|
const setEdges = useCallback((payload) => {
|
|
765
|
-
|
|
766
|
-
setShouldFlushQueue(true);
|
|
823
|
+
batchContext.edgeQueue.push(payload);
|
|
767
824
|
}, []);
|
|
768
825
|
const addNodes = useCallback((payload) => {
|
|
769
826
|
const newNodes = Array.isArray(payload) ? payload : [payload];
|
|
770
|
-
|
|
771
|
-
// to `setNodes` that might happen elsewhere.
|
|
772
|
-
setElementsQueue.current.nodes.push((nodes) => [...nodes, ...newNodes]);
|
|
773
|
-
setShouldFlushQueue(true);
|
|
827
|
+
batchContext.nodeQueue.push((nodes) => [...nodes, ...newNodes]);
|
|
774
828
|
}, []);
|
|
775
829
|
const addEdges = useCallback((payload) => {
|
|
776
830
|
const newEdges = Array.isArray(payload) ? payload : [payload];
|
|
777
|
-
|
|
778
|
-
setShouldFlushQueue(true);
|
|
831
|
+
batchContext.edgeQueue.push((edges) => [...edges, ...newEdges]);
|
|
779
832
|
}, []);
|
|
780
833
|
const toObject = useCallback(() => {
|
|
781
834
|
const { nodes = [], edges = [], transform } = store.getState();
|
|
@@ -825,13 +878,25 @@ function useReactFlow() {
|
|
|
825
878
|
}
|
|
826
879
|
return { deletedNodes: matchingNodes, deletedEdges: matchingEdges };
|
|
827
880
|
}, []);
|
|
828
|
-
const getNodeRect = useCallback((
|
|
829
|
-
const
|
|
830
|
-
|
|
881
|
+
const getNodeRect = useCallback((node) => {
|
|
882
|
+
const { nodeLookup, nodeOrigin } = store.getState();
|
|
883
|
+
const nodeToUse = isNode(node) ? node : nodeLookup.get(node.id);
|
|
884
|
+
const position = nodeToUse.parentId
|
|
885
|
+
? evaluateAbsolutePosition(nodeToUse.position, nodeToUse.parentId, nodeLookup, nodeOrigin)
|
|
886
|
+
: nodeToUse.position;
|
|
887
|
+
const nodeWithPosition = {
|
|
888
|
+
id: nodeToUse.id,
|
|
889
|
+
position,
|
|
890
|
+
width: nodeToUse.measured?.width ?? nodeToUse.width,
|
|
891
|
+
height: nodeToUse.measured?.height ?? nodeToUse.height,
|
|
892
|
+
data: nodeToUse.data,
|
|
893
|
+
};
|
|
894
|
+
return nodeToRect(nodeWithPosition);
|
|
831
895
|
}, []);
|
|
832
896
|
const getIntersectingNodes = useCallback((nodeOrRect, partially = true, nodes) => {
|
|
833
897
|
const isRect = isRectObject(nodeOrRect);
|
|
834
898
|
const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect);
|
|
899
|
+
const hasNodesOption = nodes !== undefined;
|
|
835
900
|
if (!nodeRect) {
|
|
836
901
|
return [];
|
|
837
902
|
}
|
|
@@ -840,7 +905,7 @@ function useReactFlow() {
|
|
|
840
905
|
if (internalNode && !isRect && (n.id === nodeOrRect.id || !internalNode.internals.positionAbsolute)) {
|
|
841
906
|
return false;
|
|
842
907
|
}
|
|
843
|
-
const currNodeRect = nodeToRect(n);
|
|
908
|
+
const currNodeRect = nodeToRect(hasNodesOption ? n : internalNode);
|
|
844
909
|
const overlappingArea = getOverlappingArea(currNodeRect, nodeRect);
|
|
845
910
|
const partiallyVisible = partially && overlappingArea > 0;
|
|
846
911
|
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
@@ -856,7 +921,7 @@ function useReactFlow() {
|
|
|
856
921
|
const partiallyVisible = partially && overlappingArea > 0;
|
|
857
922
|
return partiallyVisible || overlappingArea >= nodeRect.width * nodeRect.height;
|
|
858
923
|
}, []);
|
|
859
|
-
const updateNode = useCallback((id, nodeUpdate, options = { replace:
|
|
924
|
+
const updateNode = useCallback((id, nodeUpdate, options = { replace: false }) => {
|
|
860
925
|
setNodes((prevNodes) => prevNodes.map((node) => {
|
|
861
926
|
if (node.id === id) {
|
|
862
927
|
const nextNode = typeof nodeUpdate === 'function' ? nodeUpdate(node) : nodeUpdate;
|
|
@@ -1100,6 +1165,7 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1100
1165
|
const prevSelectedNodesCount = useRef(0);
|
|
1101
1166
|
const prevSelectedEdgesCount = useRef(0);
|
|
1102
1167
|
const containerBounds = useRef();
|
|
1168
|
+
const edgeIdLookup = useRef(new Map());
|
|
1103
1169
|
const { userSelectionActive, elementsSelectable, dragging } = useStore(selector$j, shallow);
|
|
1104
1170
|
const resetUserSelection = () => {
|
|
1105
1171
|
store.setState({ userSelectionActive: false, userSelectionRect: null });
|
|
@@ -1120,7 +1186,7 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1120
1186
|
};
|
|
1121
1187
|
const onWheel = onPaneScroll ? (event) => onPaneScroll(event) : undefined;
|
|
1122
1188
|
const onMouseDown = (event) => {
|
|
1123
|
-
const { resetSelectedElements, domNode } = store.getState();
|
|
1189
|
+
const { resetSelectedElements, domNode, edgeLookup } = store.getState();
|
|
1124
1190
|
containerBounds.current = domNode?.getBoundingClientRect();
|
|
1125
1191
|
if (!elementsSelectable ||
|
|
1126
1192
|
!isSelecting ||
|
|
@@ -1129,6 +1195,11 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1129
1195
|
!containerBounds.current) {
|
|
1130
1196
|
return;
|
|
1131
1197
|
}
|
|
1198
|
+
edgeIdLookup.current = new Map();
|
|
1199
|
+
for (const [id, edge] of edgeLookup) {
|
|
1200
|
+
edgeIdLookup.current.set(edge.source, edgeIdLookup.current.get(edge.source)?.add(id) || new Set([id]));
|
|
1201
|
+
edgeIdLookup.current.set(edge.target, edgeIdLookup.current.get(edge.target)?.add(id) || new Set([id]));
|
|
1202
|
+
}
|
|
1132
1203
|
const { x, y } = getEventPosition(event.nativeEvent, containerBounds.current);
|
|
1133
1204
|
resetSelectedElements();
|
|
1134
1205
|
store.setState({
|
|
@@ -1148,24 +1219,24 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1148
1219
|
if (!isSelecting || !containerBounds.current || !userSelectionRect) {
|
|
1149
1220
|
return;
|
|
1150
1221
|
}
|
|
1151
|
-
|
|
1152
|
-
const
|
|
1153
|
-
const startX = userSelectionRect.startX ?? 0;
|
|
1154
|
-
const startY = userSelectionRect.startY ?? 0;
|
|
1222
|
+
const { x: mouseX, y: mouseY } = getEventPosition(event.nativeEvent, containerBounds.current);
|
|
1223
|
+
const { startX, startY } = userSelectionRect;
|
|
1155
1224
|
const nextUserSelectRect = {
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1225
|
+
startX,
|
|
1226
|
+
startY,
|
|
1227
|
+
x: mouseX < startX ? mouseX : startX,
|
|
1228
|
+
y: mouseY < startY ? mouseY : startY,
|
|
1229
|
+
width: Math.abs(mouseX - startX),
|
|
1230
|
+
height: Math.abs(mouseY - startY),
|
|
1161
1231
|
};
|
|
1162
1232
|
const selectedNodes = getNodesInside(nodeLookup, nextUserSelectRect, transform, selectionMode === SelectionMode.Partial, true, nodeOrigin);
|
|
1163
1233
|
const selectedEdgeIds = new Set();
|
|
1164
1234
|
const selectedNodeIds = new Set();
|
|
1165
1235
|
for (const selectedNode of selectedNodes) {
|
|
1166
1236
|
selectedNodeIds.add(selectedNode.id);
|
|
1167
|
-
|
|
1168
|
-
|
|
1237
|
+
const edgeIds = edgeIdLookup.current.get(selectedNode.id);
|
|
1238
|
+
if (edgeIds) {
|
|
1239
|
+
for (const edgeId of edgeIds) {
|
|
1169
1240
|
selectedEdgeIds.add(edgeId);
|
|
1170
1241
|
}
|
|
1171
1242
|
}
|
|
@@ -1182,6 +1253,8 @@ function Pane({ isSelecting, selectionMode = SelectionMode.Full, panOnDrag, onSe
|
|
|
1182
1253
|
}
|
|
1183
1254
|
store.setState({
|
|
1184
1255
|
userSelectionRect: nextUserSelectRect,
|
|
1256
|
+
userSelectionActive: true,
|
|
1257
|
+
nodesSelectionActive: false,
|
|
1185
1258
|
});
|
|
1186
1259
|
};
|
|
1187
1260
|
const onMouseUp = (event) => {
|
|
@@ -1288,7 +1361,7 @@ function useMoveSelectedNodes() {
|
|
|
1288
1361
|
const store = useStoreApi();
|
|
1289
1362
|
const moveSelectedNodes = useCallback((params) => {
|
|
1290
1363
|
const { nodeExtent, snapToGrid, snapGrid, nodesDraggable, onError, updateNodePositions, nodeLookup, nodeOrigin } = store.getState();
|
|
1291
|
-
const nodeUpdates =
|
|
1364
|
+
const nodeUpdates = new Map();
|
|
1292
1365
|
const isSelected = selectedAndDraggable(nodesDraggable);
|
|
1293
1366
|
// by default a node moves 5px on each key press
|
|
1294
1367
|
// if snap grid is enabled, we use that for the velocity
|
|
@@ -1317,7 +1390,7 @@ function useMoveSelectedNodes() {
|
|
|
1317
1390
|
});
|
|
1318
1391
|
node.position = position;
|
|
1319
1392
|
node.internals.positionAbsolute = positionAbsolute;
|
|
1320
|
-
nodeUpdates.
|
|
1393
|
+
nodeUpdates.set(node.id, node);
|
|
1321
1394
|
}
|
|
1322
1395
|
updateNodePositions(nodeUpdates);
|
|
1323
1396
|
}, []);
|
|
@@ -1515,13 +1588,10 @@ function getNodeInlineStyleDimensions(node) {
|
|
|
1515
1588
|
}
|
|
1516
1589
|
|
|
1517
1590
|
const selector$h = (s) => {
|
|
1518
|
-
const
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
}
|
|
1523
|
-
}
|
|
1524
|
-
const { width, height, x, y } = getNodesBounds(selectedNodes, { nodeOrigin: s.nodeOrigin });
|
|
1591
|
+
const { width, height, x, y } = getInternalNodesBounds(s.nodeLookup, {
|
|
1592
|
+
nodeOrigin: s.nodeOrigin,
|
|
1593
|
+
filter: (node) => !!node.selected,
|
|
1594
|
+
});
|
|
1525
1595
|
return {
|
|
1526
1596
|
width,
|
|
1527
1597
|
height,
|
|
@@ -1605,12 +1675,11 @@ function useVisibleNodeIds(onlyRenderVisible) {
|
|
|
1605
1675
|
const selector$e = (s) => s.updateNodeInternals;
|
|
1606
1676
|
function useResizeObserver() {
|
|
1607
1677
|
const updateNodeInternals = useStore(selector$e);
|
|
1608
|
-
const
|
|
1609
|
-
const resizeObserver = useMemo(() => {
|
|
1678
|
+
const [resizeObserver] = useState(() => {
|
|
1610
1679
|
if (typeof ResizeObserver === 'undefined') {
|
|
1611
1680
|
return null;
|
|
1612
1681
|
}
|
|
1613
|
-
|
|
1682
|
+
return new ResizeObserver((entries) => {
|
|
1614
1683
|
const updates = new Map();
|
|
1615
1684
|
entries.forEach((entry) => {
|
|
1616
1685
|
const id = entry.target.getAttribute('data-id');
|
|
@@ -1621,31 +1690,74 @@ function useResizeObserver() {
|
|
|
1621
1690
|
});
|
|
1622
1691
|
updateNodeInternals(updates);
|
|
1623
1692
|
});
|
|
1624
|
-
|
|
1625
|
-
return observer;
|
|
1626
|
-
}, []);
|
|
1693
|
+
});
|
|
1627
1694
|
useEffect(() => {
|
|
1628
1695
|
return () => {
|
|
1629
|
-
|
|
1696
|
+
resizeObserver?.disconnect();
|
|
1630
1697
|
};
|
|
1631
|
-
}, []);
|
|
1698
|
+
}, [resizeObserver]);
|
|
1632
1699
|
return resizeObserver;
|
|
1633
1700
|
}
|
|
1634
1701
|
|
|
1702
|
+
/**
|
|
1703
|
+
* Hook to handle the resize observation + internal updates for the passed node.
|
|
1704
|
+
*
|
|
1705
|
+
* @internal
|
|
1706
|
+
* @returns nodeRef - reference to the node element
|
|
1707
|
+
*/
|
|
1708
|
+
function useNodeObserver({ node, nodeType, hasDimensions, resizeObserver, }) {
|
|
1709
|
+
const store = useStoreApi();
|
|
1710
|
+
const nodeRef = useRef(null);
|
|
1711
|
+
const observedNode = useRef(null);
|
|
1712
|
+
const prevSourcePosition = useRef(node.sourcePosition);
|
|
1713
|
+
const prevTargetPosition = useRef(node.targetPosition);
|
|
1714
|
+
const prevType = useRef(nodeType);
|
|
1715
|
+
const isInitialized = hasDimensions && !!node.internals.handleBounds && !node.hidden;
|
|
1716
|
+
useEffect(() => {
|
|
1717
|
+
if (nodeRef.current && (!isInitialized || observedNode.current !== nodeRef.current)) {
|
|
1718
|
+
if (observedNode.current) {
|
|
1719
|
+
resizeObserver?.unobserve(observedNode.current);
|
|
1720
|
+
}
|
|
1721
|
+
resizeObserver?.observe(nodeRef.current);
|
|
1722
|
+
observedNode.current = nodeRef.current;
|
|
1723
|
+
}
|
|
1724
|
+
}, [isInitialized]);
|
|
1725
|
+
useEffect(() => {
|
|
1726
|
+
return () => {
|
|
1727
|
+
if (observedNode.current) {
|
|
1728
|
+
resizeObserver?.unobserve(observedNode.current);
|
|
1729
|
+
observedNode.current = null;
|
|
1730
|
+
}
|
|
1731
|
+
};
|
|
1732
|
+
}, []);
|
|
1733
|
+
useEffect(() => {
|
|
1734
|
+
if (nodeRef.current) {
|
|
1735
|
+
// when the user programmatically changes the source or handle position, we need to update the internals
|
|
1736
|
+
// to make sure the edges are updated correctly
|
|
1737
|
+
const typeChanged = prevType.current !== nodeType;
|
|
1738
|
+
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
|
1739
|
+
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
|
1740
|
+
if (typeChanged || sourcePosChanged || targetPosChanged) {
|
|
1741
|
+
prevType.current = nodeType;
|
|
1742
|
+
prevSourcePosition.current = node.sourcePosition;
|
|
1743
|
+
prevTargetPosition.current = node.targetPosition;
|
|
1744
|
+
store
|
|
1745
|
+
.getState()
|
|
1746
|
+
.updateNodeInternals(new Map([[node.id, { id: node.id, nodeElement: nodeRef.current, force: true }]]));
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
}, [node.id, nodeType, node.sourcePosition, node.targetPosition]);
|
|
1750
|
+
return nodeRef;
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1635
1753
|
function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onContextMenu, onDoubleClick, nodesDraggable, elementsSelectable, nodesConnectable, nodesFocusable, resizeObserver, noDragClassName, noPanClassName, disableKeyboardA11y, rfId, nodeTypes, nodeExtent, nodeOrigin, onError, }) {
|
|
1636
|
-
const { node,
|
|
1754
|
+
const { node, internals, isParent } = useStore((s) => {
|
|
1637
1755
|
const node = s.nodeLookup.get(id);
|
|
1638
|
-
const
|
|
1639
|
-
? clampPosition(node.internals.positionAbsolute, nodeExtent)
|
|
1640
|
-
: node.internals.positionAbsolute || { x: 0, y: 0 };
|
|
1756
|
+
const isParent = s.parentLookup.has(id);
|
|
1641
1757
|
return {
|
|
1642
1758
|
node,
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
positionAbsoluteX: positionAbsolute.x,
|
|
1646
|
-
positionAbsoluteY: positionAbsolute.y,
|
|
1647
|
-
zIndex: node.internals.z,
|
|
1648
|
-
isParent: node.internals.isParent,
|
|
1759
|
+
internals: node.internals,
|
|
1760
|
+
isParent,
|
|
1649
1761
|
};
|
|
1650
1762
|
}, shallow);
|
|
1651
1763
|
let nodeType = node.type || 'default';
|
|
@@ -1660,50 +1772,8 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1660
1772
|
const isConnectable = !!(node.connectable || (nodesConnectable && typeof node.connectable === 'undefined'));
|
|
1661
1773
|
const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined'));
|
|
1662
1774
|
const store = useStoreApi();
|
|
1663
|
-
const
|
|
1664
|
-
const
|
|
1665
|
-
const prevTargetPosition = useRef(node.targetPosition);
|
|
1666
|
-
const prevType = useRef(nodeType);
|
|
1667
|
-
const nodeDimensions = getNodeDimensions(node);
|
|
1668
|
-
const inlineDimensions = getNodeInlineStyleDimensions(node);
|
|
1669
|
-
const initialized = nodeHasDimensions(node);
|
|
1670
|
-
const hasHandleBounds = !!node.internals.handleBounds;
|
|
1671
|
-
const moveSelectedNodes = useMoveSelectedNodes();
|
|
1672
|
-
useEffect(() => {
|
|
1673
|
-
const currNode = nodeRef.current;
|
|
1674
|
-
return () => {
|
|
1675
|
-
if (currNode) {
|
|
1676
|
-
resizeObserver?.unobserve(currNode);
|
|
1677
|
-
}
|
|
1678
|
-
};
|
|
1679
|
-
}, []);
|
|
1680
|
-
useEffect(() => {
|
|
1681
|
-
if (nodeRef.current && !node.hidden) {
|
|
1682
|
-
const currNode = nodeRef.current;
|
|
1683
|
-
if (!initialized || !hasHandleBounds) {
|
|
1684
|
-
resizeObserver?.unobserve(currNode);
|
|
1685
|
-
resizeObserver?.observe(currNode);
|
|
1686
|
-
}
|
|
1687
|
-
}
|
|
1688
|
-
}, [node.hidden, initialized, hasHandleBounds]);
|
|
1689
|
-
useEffect(() => {
|
|
1690
|
-
// when the user programmatically changes the source or handle position, we re-initialize the node
|
|
1691
|
-
const typeChanged = prevType.current !== nodeType;
|
|
1692
|
-
const sourcePosChanged = prevSourcePosition.current !== node.sourcePosition;
|
|
1693
|
-
const targetPosChanged = prevTargetPosition.current !== node.targetPosition;
|
|
1694
|
-
if (nodeRef.current && (typeChanged || sourcePosChanged || targetPosChanged)) {
|
|
1695
|
-
if (typeChanged) {
|
|
1696
|
-
prevType.current = nodeType;
|
|
1697
|
-
}
|
|
1698
|
-
if (sourcePosChanged) {
|
|
1699
|
-
prevSourcePosition.current = node.sourcePosition;
|
|
1700
|
-
}
|
|
1701
|
-
if (targetPosChanged) {
|
|
1702
|
-
prevTargetPosition.current = node.targetPosition;
|
|
1703
|
-
}
|
|
1704
|
-
store.getState().updateNodeInternals(new Map([[id, { id, nodeElement: nodeRef.current, force: true }]]));
|
|
1705
|
-
}
|
|
1706
|
-
}, [id, nodeType, node.sourcePosition, node.targetPosition]);
|
|
1775
|
+
const hasDimensions = nodeHasDimensions(node);
|
|
1776
|
+
const nodeRef = useNodeObserver({ node, nodeType, hasDimensions, resizeObserver });
|
|
1707
1777
|
const dragging = useDrag({
|
|
1708
1778
|
nodeRef,
|
|
1709
1779
|
disabled: node.hidden || !isDraggable,
|
|
@@ -1712,12 +1782,17 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1712
1782
|
nodeId: id,
|
|
1713
1783
|
isSelectable,
|
|
1714
1784
|
});
|
|
1785
|
+
const moveSelectedNodes = useMoveSelectedNodes();
|
|
1715
1786
|
if (node.hidden) {
|
|
1716
1787
|
return null;
|
|
1717
1788
|
}
|
|
1718
|
-
const
|
|
1719
|
-
|
|
1720
|
-
|
|
1789
|
+
const nodeDimensions = getNodeDimensions(node);
|
|
1790
|
+
const inlineDimensions = getNodeInlineStyleDimensions(node);
|
|
1791
|
+
const clampedPosition = nodeExtent
|
|
1792
|
+
? clampPosition(internals.positionAbsolute, nodeExtent)
|
|
1793
|
+
: internals.positionAbsolute;
|
|
1794
|
+
const positionWithOrigin = getPositionWithOrigin({
|
|
1795
|
+
...clampedPosition,
|
|
1721
1796
|
...nodeDimensions,
|
|
1722
1797
|
origin: node.origin || nodeOrigin,
|
|
1723
1798
|
});
|
|
@@ -1759,7 +1834,7 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1759
1834
|
store.setState({
|
|
1760
1835
|
ariaLiveMessage: `Moved selected node ${event.key
|
|
1761
1836
|
.replace('Arrow', '')
|
|
1762
|
-
.toLowerCase()}. New position, x: ${~~
|
|
1837
|
+
.toLowerCase()}. New position, x: ${~~clampedPosition.x}, y: ${~~clampedPosition.y}`,
|
|
1763
1838
|
});
|
|
1764
1839
|
moveSelectedNodes({
|
|
1765
1840
|
direction: arrowKeyDiffs[event.key],
|
|
@@ -1783,13 +1858,13 @@ function NodeWrapper({ id, onClick, onMouseEnter, onMouseMove, onMouseLeave, onC
|
|
|
1783
1858
|
dragging,
|
|
1784
1859
|
},
|
|
1785
1860
|
]), ref: nodeRef, style: {
|
|
1786
|
-
zIndex,
|
|
1787
|
-
transform: `translate(${
|
|
1861
|
+
zIndex: internals.z,
|
|
1862
|
+
transform: `translate(${positionWithOrigin.x}px,${positionWithOrigin.y}px)`,
|
|
1788
1863
|
pointerEvents: hasPointerEvents ? 'all' : 'none',
|
|
1789
|
-
visibility:
|
|
1864
|
+
visibility: hasDimensions ? 'visible' : 'hidden',
|
|
1790
1865
|
...node.style,
|
|
1791
1866
|
...inlineDimensions,
|
|
1792
|
-
}, "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:
|
|
1867
|
+
}, "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, isConnectable: isConnectable, sourcePosition: node.sourcePosition, targetPosition: node.targetPosition, dragging: dragging, dragHandle: node.dragHandle, zIndex: internals.z, ...nodeDimensions }) }) }));
|
|
1793
1868
|
}
|
|
1794
1869
|
|
|
1795
1870
|
const selector$d = (s) => ({
|
|
@@ -2460,20 +2535,23 @@ const GraphView = memo(GraphViewComponent);
|
|
|
2460
2535
|
|
|
2461
2536
|
const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView, } = {}) => {
|
|
2462
2537
|
const nodeLookup = new Map();
|
|
2538
|
+
const parentLookup = new Map();
|
|
2463
2539
|
const connectionLookup = new Map();
|
|
2464
2540
|
const edgeLookup = new Map();
|
|
2465
2541
|
const storeEdges = defaultEdges ?? edges ?? [];
|
|
2466
2542
|
const storeNodes = defaultNodes ?? nodes ?? [];
|
|
2467
2543
|
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
|
2468
|
-
adoptUserNodes(storeNodes, nodeLookup, {
|
|
2544
|
+
adoptUserNodes(storeNodes, nodeLookup, parentLookup, {
|
|
2469
2545
|
nodeOrigin: [0, 0],
|
|
2470
2546
|
elevateNodesOnSelect: false,
|
|
2471
2547
|
});
|
|
2472
2548
|
let transform = [0, 0, 1];
|
|
2473
2549
|
if (fitView && width && height) {
|
|
2474
|
-
const nodesWithDimensions = storeNodes.filter((node) => (node.width || node.initialWidth) && (node.height || node.initialHeight));
|
|
2475
2550
|
// @todo users nodeOrigin should be used here
|
|
2476
|
-
const bounds =
|
|
2551
|
+
const bounds = getInternalNodesBounds(nodeLookup, {
|
|
2552
|
+
nodeOrigin: [0, 0],
|
|
2553
|
+
filter: (node) => !!((node.width || node.initialWidth) && (node.height || node.initialHeight)),
|
|
2554
|
+
});
|
|
2477
2555
|
const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1);
|
|
2478
2556
|
transform = [x, y, zoom];
|
|
2479
2557
|
}
|
|
@@ -2484,6 +2562,7 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
|
|
|
2484
2562
|
transform,
|
|
2485
2563
|
nodes: storeNodes,
|
|
2486
2564
|
nodeLookup,
|
|
2565
|
+
parentLookup,
|
|
2487
2566
|
edges: storeEdges,
|
|
2488
2567
|
edgeLookup,
|
|
2489
2568
|
connectionLookup,
|
|
@@ -2538,17 +2617,17 @@ const getInitialState = ({ nodes, edges, defaultNodes, defaultEdges, width, heig
|
|
|
2538
2617
|
};
|
|
2539
2618
|
};
|
|
2540
2619
|
|
|
2541
|
-
const
|
|
2620
|
+
const createStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height, fitView: fitView$1, }) => createWithEqualityFn((set, get) => ({
|
|
2542
2621
|
...getInitialState({ nodes, edges, width, height, fitView: fitView$1, defaultNodes, defaultEdges }),
|
|
2543
2622
|
setNodes: (nodes) => {
|
|
2544
|
-
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2623
|
+
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
2545
2624
|
// setNodes() is called exclusively in response to user actions:
|
|
2546
2625
|
// - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
|
2547
2626
|
// - or when the user calls something like `reactFlowInstance.setNodes()` in an uncontrolled ReactFlow setup.
|
|
2548
2627
|
//
|
|
2549
2628
|
// When this happens, we take the note objects passed by the user and extend them with fields
|
|
2550
2629
|
// relevant for internal React Flow operations.
|
|
2551
|
-
adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
|
2630
|
+
adoptUserNodes(nodes, nodeLookup, parentLookup, { nodeOrigin, elevateNodesOnSelect, checkEquality: true });
|
|
2552
2631
|
set({ nodes });
|
|
2553
2632
|
},
|
|
2554
2633
|
setEdges: (edges) => {
|
|
@@ -2572,8 +2651,8 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
|
|
|
2572
2651
|
// changes its dimensions, this function is called to measure the
|
|
2573
2652
|
// new dimensions and update the nodes.
|
|
2574
2653
|
updateNodeInternals: (updates) => {
|
|
2575
|
-
const { onNodesChange, fitView, nodeLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, } = get();
|
|
2576
|
-
const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, domNode, nodeOrigin);
|
|
2654
|
+
const { onNodesChange, fitView, nodeLookup, parentLookup, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin, debug, } = get();
|
|
2655
|
+
const { changes, updatedInternals } = updateNodeInternals(updates, nodeLookup, parentLookup, domNode, nodeOrigin);
|
|
2577
2656
|
if (!updatedInternals) {
|
|
2578
2657
|
return;
|
|
2579
2658
|
}
|
|
@@ -2600,33 +2679,34 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
|
|
|
2600
2679
|
}
|
|
2601
2680
|
},
|
|
2602
2681
|
updateNodePositions: (nodeDragItems, dragging = false) => {
|
|
2603
|
-
const
|
|
2604
|
-
const
|
|
2605
|
-
const
|
|
2682
|
+
const parentExpandChildren = [];
|
|
2683
|
+
const changes = [];
|
|
2684
|
+
for (const [id, dragItem] of nodeDragItems) {
|
|
2606
2685
|
// @todo add expandParent to drag item so that we can get rid of the look up here
|
|
2607
|
-
const internalNode = nodeLookup.get(node.id);
|
|
2608
2686
|
const change = {
|
|
2609
|
-
id
|
|
2687
|
+
id,
|
|
2610
2688
|
type: 'position',
|
|
2611
|
-
position:
|
|
2689
|
+
position: dragItem.position,
|
|
2612
2690
|
dragging,
|
|
2613
2691
|
};
|
|
2614
|
-
if (
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
|
|
2618
|
-
|
|
2619
|
-
...
|
|
2620
|
-
|
|
2692
|
+
if (dragItem?.expandParent && dragItem?.parentId && change.position) {
|
|
2693
|
+
parentExpandChildren.push({
|
|
2694
|
+
id,
|
|
2695
|
+
parentId: dragItem.parentId,
|
|
2696
|
+
rect: {
|
|
2697
|
+
...dragItem.internals.positionAbsolute,
|
|
2698
|
+
width: dragItem.measured.width,
|
|
2699
|
+
height: dragItem.measured.height,
|
|
2621
2700
|
},
|
|
2622
2701
|
});
|
|
2623
2702
|
change.position.x = Math.max(0, change.position.x);
|
|
2624
2703
|
change.position.y = Math.max(0, change.position.y);
|
|
2625
2704
|
}
|
|
2626
|
-
|
|
2627
|
-
}
|
|
2628
|
-
if (
|
|
2629
|
-
const
|
|
2705
|
+
changes.push(change);
|
|
2706
|
+
}
|
|
2707
|
+
if (parentExpandChildren.length > 0) {
|
|
2708
|
+
const { nodeLookup, parentLookup } = get();
|
|
2709
|
+
const parentExpandChanges = handleExpandParent(parentExpandChildren, nodeLookup, parentLookup);
|
|
2630
2710
|
changes.push(...parentExpandChanges);
|
|
2631
2711
|
}
|
|
2632
2712
|
get().triggerNodeChanges(changes);
|
|
@@ -2761,20 +2841,17 @@ const createRFStore = ({ nodes, edges, defaultNodes, defaultEdges, width, height
|
|
|
2761
2841
|
reset: () => set({ ...getInitialState() }),
|
|
2762
2842
|
}), Object.is);
|
|
2763
2843
|
|
|
2764
|
-
function ReactFlowProvider({
|
|
2765
|
-
const
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
});
|
|
2776
|
-
}
|
|
2777
|
-
return jsx(Provider$1, { value: storeRef.current, children: children });
|
|
2844
|
+
function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNodes, defaultEdges, initialWidth: width, initialHeight: height, fitView, children, }) {
|
|
2845
|
+
const [store] = useState(() => createStore({
|
|
2846
|
+
nodes,
|
|
2847
|
+
edges,
|
|
2848
|
+
defaultNodes,
|
|
2849
|
+
defaultEdges,
|
|
2850
|
+
width,
|
|
2851
|
+
height,
|
|
2852
|
+
fitView,
|
|
2853
|
+
}));
|
|
2854
|
+
return (jsx(Provider$1, { value: store, children: jsx(BatchProvider, { children: children }) }));
|
|
2778
2855
|
}
|
|
2779
2856
|
|
|
2780
2857
|
function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, }) {
|
|
@@ -2949,9 +3026,9 @@ const selector$6 = (options) => (s) => {
|
|
|
2949
3026
|
if (s.nodeLookup.size === 0) {
|
|
2950
3027
|
return false;
|
|
2951
3028
|
}
|
|
2952
|
-
for (const [,
|
|
2953
|
-
if (options.includeHiddenNodes || !
|
|
2954
|
-
if (
|
|
3029
|
+
for (const [, { hidden, internals }] of s.nodeLookup) {
|
|
3030
|
+
if (options.includeHiddenNodes || !hidden) {
|
|
3031
|
+
if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) {
|
|
2955
3032
|
return false;
|
|
2956
3033
|
}
|
|
2957
3034
|
}
|
|
@@ -3193,7 +3270,7 @@ nodeComponent: NodeComponent = MiniMapNode, onClick, }) {
|
|
|
3193
3270
|
function NodeComponentWrapperInner({ id, nodeOrigin, nodeColorFunc, nodeStrokeColorFunc, nodeClassNameFunc, nodeBorderRadius, nodeStrokeWidth, shapeRendering, NodeComponent, onClick, }) {
|
|
3194
3271
|
const { node, x, y } = useStore((s) => {
|
|
3195
3272
|
const node = s.nodeLookup.get(id);
|
|
3196
|
-
const { x, y } = getNodePositionWithOrigin(node,
|
|
3273
|
+
const { x, y } = getNodePositionWithOrigin(node, nodeOrigin).positionAbsolute;
|
|
3197
3274
|
return {
|
|
3198
3275
|
node,
|
|
3199
3276
|
x,
|
|
@@ -3333,24 +3410,43 @@ function ResizeControl({ nodeId, position, variant = ResizeControlVariant.Handle
|
|
|
3333
3410
|
};
|
|
3334
3411
|
},
|
|
3335
3412
|
onChange: (change, childChanges) => {
|
|
3336
|
-
const { triggerNodeChanges } = store.getState();
|
|
3413
|
+
const { triggerNodeChanges, nodeLookup, parentLookup, nodeOrigin } = store.getState();
|
|
3337
3414
|
const changes = [];
|
|
3338
|
-
|
|
3415
|
+
const nextPosition = { x: change.x, y: change.y };
|
|
3416
|
+
const node = nodeLookup.get(id);
|
|
3417
|
+
if (node && node.expandParent && node.parentId) {
|
|
3418
|
+
const child = {
|
|
3419
|
+
id: node.id,
|
|
3420
|
+
parentId: node.parentId,
|
|
3421
|
+
rect: {
|
|
3422
|
+
width: change.width ?? node.measured.width,
|
|
3423
|
+
height: change.height ?? node.measured.height,
|
|
3424
|
+
...evaluateAbsolutePosition({
|
|
3425
|
+
x: change.x ?? node.position.x,
|
|
3426
|
+
y: change.y ?? node.position.y,
|
|
3427
|
+
}, node.parentId, nodeLookup, node.origin ?? nodeOrigin),
|
|
3428
|
+
},
|
|
3429
|
+
};
|
|
3430
|
+
const parentExpandChanges = handleExpandParent([child], nodeLookup, parentLookup, nodeOrigin);
|
|
3431
|
+
changes.push(...parentExpandChanges);
|
|
3432
|
+
// when the parent was expanded by the child node, its position will be clamped at 0,0
|
|
3433
|
+
nextPosition.x = change.x ? Math.max(0, change.x) : undefined;
|
|
3434
|
+
nextPosition.y = change.y ? Math.max(0, change.y) : undefined;
|
|
3435
|
+
}
|
|
3436
|
+
if (nextPosition.x !== undefined && nextPosition.y !== undefined) {
|
|
3339
3437
|
const positionChange = {
|
|
3340
3438
|
id,
|
|
3341
3439
|
type: 'position',
|
|
3342
|
-
position: {
|
|
3343
|
-
x: change.x,
|
|
3344
|
-
y: change.y,
|
|
3345
|
-
},
|
|
3440
|
+
position: { ...nextPosition },
|
|
3346
3441
|
};
|
|
3347
3442
|
changes.push(positionChange);
|
|
3348
3443
|
}
|
|
3349
|
-
if (change.
|
|
3444
|
+
if (change.width !== undefined && change.height !== undefined) {
|
|
3350
3445
|
const dimensionChange = {
|
|
3351
3446
|
id,
|
|
3352
3447
|
type: 'dimensions',
|
|
3353
3448
|
resizing: true,
|
|
3449
|
+
setAttributes: true,
|
|
3354
3450
|
dimensions: {
|
|
3355
3451
|
width: change.width,
|
|
3356
3452
|
height: change.height,
|