@xyflow/react 12.0.0-next.15 → 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/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/ReactFlowProvider/index.d.ts.map +1 -1
- 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/index.js +120 -74
- package/dist/esm/index.mjs +120 -74
- 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/ReactFlowProvider/index.d.ts.map +1 -1
- package/dist/umd/hooks/useNodesInitialized.d.ts.map +1 -1
- package/dist/umd/hooks/useReactFlow.d.ts.map +1 -1
- package/dist/umd/index.js +2 -2
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Queue } from './types';
|
|
3
|
+
import type { Edge, Node } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* This is a context provider that holds and processes the node and edge update queues
|
|
6
|
+
* that are needed to handle setNodes, addNodes, setEdges and addEdges.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare function BatchProvider<NodeType extends Node = Node, EdgeType extends Edge = Edge>({ children, }: {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function useBatchContext(): {
|
|
14
|
+
nodeQueue: Queue<any>;
|
|
15
|
+
edgeQueue: Queue<any>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,SAAS,EAAoC,MAAM,OAAO,CAAC;AAKnF,OAAO,EAAE,KAAK,EAAa,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAU9C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,EACxF,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAmDA;AAED,wBAAgB,eAAe;;;EAQ9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;AAEvD,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI;IACrB,GAAG,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CACpC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Queue, QueueItem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* This hook returns a queue that can be used to batch updates.
|
|
4
|
+
*
|
|
5
|
+
* @param runQueue - a function that gets called when the queue is flushed
|
|
6
|
+
* @internal
|
|
7
|
+
*
|
|
8
|
+
* @returns a Queue object
|
|
9
|
+
*/
|
|
10
|
+
export declare function useQueue<T>(runQueue: (items: QueueItem<T>[]) => void): Queue<T>;
|
|
11
|
+
//# sourceMappingURL=useQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQueue.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/useQueue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,YAsCpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAa,MAAM,SAAS,CAAC;AAEhD,wBAAgB,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAavD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/ElementBatcher/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAkB,MAAM,aAAa,CAAC;AAM9D,wBAAgB,cAAc,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,IAAI,GAAG,IAAI,UA6ExF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/ReactFlowProvider/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/ReactFlowProvider/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAKjD,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,IAAI,EAAE,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,EAChC,YAAY,EAAE,KAAK,EACnB,YAAY,EAAE,KAAK,EACnB,YAAY,EACZ,YAAY,EACZ,YAAY,EAAE,KAAK,EACnB,aAAa,EAAE,MAAM,EACrB,OAAO,EACP,QAAQ,GACT,EAAE,sBAAsB,2CAkBxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useElementBatching.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/hooks/useElementBatching.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAI3C,wBAAgB,kBAAkB,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,IAAI,GAAG,IAAI,UAiF5F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useNodesInitialized.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/hooks/useNodesInitialized.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useNodesInitialized.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/hooks/useNodesInitialized.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,0BAA0B,GAAG;IACvC,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,CAAC;AAsBF;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA2C,GAAG,OAAO,CAIjG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useReactFlow.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/hooks/useReactFlow.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useReactFlow.d.ts","sourceRoot":"","sources":["../../../../packages/react/src/hooks/useReactFlow.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,iBAAiB,EAAY,IAAI,EAAE,IAAI,EAAgB,MAAM,UAAU,CAAC;AAEtF;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,IAAI,GAAG,IAAI,KAAK,iBAAiB,CAC3G,QAAQ,EACR,QAAQ,CACT,CAwPA"}
|
package/dist/esm/index.js
CHANGED
|
@@ -644,7 +644,8 @@ function getElementsDiffChanges({ items = [], lookup, }) {
|
|
|
644
644
|
const changes = [];
|
|
645
645
|
const itemsLookup = new Map(items.map((item) => [item.id, item]));
|
|
646
646
|
for (const item of items) {
|
|
647
|
-
const
|
|
647
|
+
const lookupItem = lookup.get(item.id);
|
|
648
|
+
const storeItem = lookupItem?.internals?.userNode ?? lookupItem;
|
|
648
649
|
if (storeItem !== undefined && storeItem !== item) {
|
|
649
650
|
changes.push({ id: item.id, item: item, type: 'replace' });
|
|
650
651
|
}
|
|
@@ -687,30 +688,22 @@ function fixedForwardRef(render) {
|
|
|
687
688
|
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
688
689
|
|
|
689
690
|
/**
|
|
690
|
-
*
|
|
691
|
+
* This hook returns a queue that can be used to batch updates.
|
|
691
692
|
*
|
|
692
|
-
* @
|
|
693
|
-
* @
|
|
693
|
+
* @param runQueue - a function that gets called when the queue is flushed
|
|
694
|
+
* @internal
|
|
695
|
+
*
|
|
696
|
+
* @returns a Queue object
|
|
694
697
|
*/
|
|
695
|
-
function
|
|
696
|
-
const viewportHelper = useViewportHelper();
|
|
697
|
-
const store = useStoreApi();
|
|
698
|
-
const getNodes = useCallback(() => store.getState().nodes.map((n) => ({ ...n })), []);
|
|
699
|
-
const getInternalNode = useCallback((id) => store.getState().nodeLookup.get(id), []);
|
|
700
|
-
const getNode = useCallback((id) => getInternalNode(id)?.internals.userNode, [getInternalNode]);
|
|
701
|
-
const getEdges = useCallback(() => {
|
|
702
|
-
const { edges = [] } = store.getState();
|
|
703
|
-
return edges.map((e) => ({ ...e }));
|
|
704
|
-
}, []);
|
|
705
|
-
const getEdge = useCallback((id) => store.getState().edgeLookup.get(id), []);
|
|
706
|
-
// A reference of all the batched updates to process before the next render. We
|
|
707
|
-
// want a mutable reference here so multiple synchronous calls to `setNodes` etc
|
|
708
|
-
// can be batched together.
|
|
709
|
-
const setElementsQueue = useRef({ nodes: [], edges: [] });
|
|
698
|
+
function useQueue(runQueue) {
|
|
710
699
|
// Because we're using a ref above, we need some way to let React know when to
|
|
711
700
|
// actually process the queue. We flip this bit of state to `true` any time we
|
|
712
701
|
// mutate the queue and then flip it back to `false` after flushing the queue.
|
|
713
|
-
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)));
|
|
714
707
|
// Layout effects are guaranteed to run before the next render which means we
|
|
715
708
|
// shouldn't run into any issues with stale state or weird issues that come from
|
|
716
709
|
// rendering things one frame later than expected (we used to use `setTimeout`).
|
|
@@ -719,70 +712,123 @@ function useReactFlow() {
|
|
|
719
712
|
// trigger the hook again (!). If the hook is being run again we know that any
|
|
720
713
|
// updates should have been processed by now and we can safely clear the queue
|
|
721
714
|
// and bail early.
|
|
722
|
-
if (!
|
|
723
|
-
|
|
715
|
+
if (!shouldFlush) {
|
|
716
|
+
queue.reset();
|
|
724
717
|
return;
|
|
725
718
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
// array methods where we can.
|
|
731
|
-
let next = nodes;
|
|
732
|
-
for (const payload of setElementsQueue.current.nodes) {
|
|
733
|
-
next = typeof payload === 'function' ? payload(next) : payload;
|
|
734
|
-
}
|
|
735
|
-
if (hasDefaultNodes) {
|
|
736
|
-
setNodes(next);
|
|
737
|
-
}
|
|
738
|
-
else if (onNodesChange) {
|
|
739
|
-
onNodesChange(getElementsDiffChanges({
|
|
740
|
-
items: next,
|
|
741
|
-
lookup: nodeLookup,
|
|
742
|
-
}));
|
|
743
|
-
}
|
|
744
|
-
setElementsQueue.current.nodes = [];
|
|
745
|
-
}
|
|
746
|
-
if (setElementsQueue.current.edges.length) {
|
|
747
|
-
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
|
|
748
|
-
let next = edges;
|
|
749
|
-
for (const payload of setElementsQueue.current.edges) {
|
|
750
|
-
next = typeof payload === 'function' ? payload(next) : payload;
|
|
751
|
-
}
|
|
752
|
-
if (hasDefaultEdges) {
|
|
753
|
-
setEdges(next);
|
|
754
|
-
}
|
|
755
|
-
else if (onEdgesChange) {
|
|
756
|
-
onEdgesChange(getElementsDiffChanges({
|
|
757
|
-
items: next,
|
|
758
|
-
lookup: edgeLookup,
|
|
759
|
-
}));
|
|
760
|
-
}
|
|
761
|
-
setElementsQueue.current.edges = [];
|
|
719
|
+
const queueItems = queue.get();
|
|
720
|
+
if (queueItems.length) {
|
|
721
|
+
runQueue(queueItems);
|
|
722
|
+
queue.reset();
|
|
762
723
|
}
|
|
763
724
|
// Beacuse we're using reactive state to trigger this effect, we need to flip
|
|
764
725
|
// it back to false.
|
|
765
|
-
|
|
766
|
-
}, [
|
|
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), []);
|
|
767
819
|
const setNodes = useCallback((payload) => {
|
|
768
|
-
|
|
769
|
-
setShouldFlushQueue(true);
|
|
820
|
+
batchContext.nodeQueue.push(payload);
|
|
770
821
|
}, []);
|
|
771
822
|
const setEdges = useCallback((payload) => {
|
|
772
|
-
|
|
773
|
-
setShouldFlushQueue(true);
|
|
823
|
+
batchContext.edgeQueue.push(payload);
|
|
774
824
|
}, []);
|
|
775
825
|
const addNodes = useCallback((payload) => {
|
|
776
826
|
const newNodes = Array.isArray(payload) ? payload : [payload];
|
|
777
|
-
|
|
778
|
-
// to `setNodes` that might happen elsewhere.
|
|
779
|
-
setElementsQueue.current.nodes.push((nodes) => [...nodes, ...newNodes]);
|
|
780
|
-
setShouldFlushQueue(true);
|
|
827
|
+
batchContext.nodeQueue.push((nodes) => [...nodes, ...newNodes]);
|
|
781
828
|
}, []);
|
|
782
829
|
const addEdges = useCallback((payload) => {
|
|
783
830
|
const newEdges = Array.isArray(payload) ? payload : [payload];
|
|
784
|
-
|
|
785
|
-
setShouldFlushQueue(true);
|
|
831
|
+
batchContext.edgeQueue.push((edges) => [...edges, ...newEdges]);
|
|
786
832
|
}, []);
|
|
787
833
|
const toObject = useCallback(() => {
|
|
788
834
|
const { nodes = [], edges = [], transform } = store.getState();
|
|
@@ -2805,7 +2851,7 @@ function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNo
|
|
|
2805
2851
|
height,
|
|
2806
2852
|
fitView,
|
|
2807
2853
|
}));
|
|
2808
|
-
return jsx(Provider$1, { value: store, children: children });
|
|
2854
|
+
return (jsx(Provider$1, { value: store, children: jsx(BatchProvider, { children: children }) }));
|
|
2809
2855
|
}
|
|
2810
2856
|
|
|
2811
2857
|
function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, }) {
|
|
@@ -2980,9 +3026,9 @@ const selector$6 = (options) => (s) => {
|
|
|
2980
3026
|
if (s.nodeLookup.size === 0) {
|
|
2981
3027
|
return false;
|
|
2982
3028
|
}
|
|
2983
|
-
for (const [,
|
|
2984
|
-
if (options.includeHiddenNodes || !
|
|
2985
|
-
if (
|
|
3029
|
+
for (const [, { hidden, internals }] of s.nodeLookup) {
|
|
3030
|
+
if (options.includeHiddenNodes || !hidden) {
|
|
3031
|
+
if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) {
|
|
2986
3032
|
return false;
|
|
2987
3033
|
}
|
|
2988
3034
|
}
|
package/dist/esm/index.mjs
CHANGED
|
@@ -644,7 +644,8 @@ function getElementsDiffChanges({ items = [], lookup, }) {
|
|
|
644
644
|
const changes = [];
|
|
645
645
|
const itemsLookup = new Map(items.map((item) => [item.id, item]));
|
|
646
646
|
for (const item of items) {
|
|
647
|
-
const
|
|
647
|
+
const lookupItem = lookup.get(item.id);
|
|
648
|
+
const storeItem = lookupItem?.internals?.userNode ?? lookupItem;
|
|
648
649
|
if (storeItem !== undefined && storeItem !== item) {
|
|
649
650
|
changes.push({ id: item.id, item: item, type: 'replace' });
|
|
650
651
|
}
|
|
@@ -687,30 +688,22 @@ function fixedForwardRef(render) {
|
|
|
687
688
|
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;
|
|
688
689
|
|
|
689
690
|
/**
|
|
690
|
-
*
|
|
691
|
+
* This hook returns a queue that can be used to batch updates.
|
|
691
692
|
*
|
|
692
|
-
* @
|
|
693
|
-
* @
|
|
693
|
+
* @param runQueue - a function that gets called when the queue is flushed
|
|
694
|
+
* @internal
|
|
695
|
+
*
|
|
696
|
+
* @returns a Queue object
|
|
694
697
|
*/
|
|
695
|
-
function
|
|
696
|
-
const viewportHelper = useViewportHelper();
|
|
697
|
-
const store = useStoreApi();
|
|
698
|
-
const getNodes = useCallback(() => store.getState().nodes.map((n) => ({ ...n })), []);
|
|
699
|
-
const getInternalNode = useCallback((id) => store.getState().nodeLookup.get(id), []);
|
|
700
|
-
const getNode = useCallback((id) => getInternalNode(id)?.internals.userNode, [getInternalNode]);
|
|
701
|
-
const getEdges = useCallback(() => {
|
|
702
|
-
const { edges = [] } = store.getState();
|
|
703
|
-
return edges.map((e) => ({ ...e }));
|
|
704
|
-
}, []);
|
|
705
|
-
const getEdge = useCallback((id) => store.getState().edgeLookup.get(id), []);
|
|
706
|
-
// A reference of all the batched updates to process before the next render. We
|
|
707
|
-
// want a mutable reference here so multiple synchronous calls to `setNodes` etc
|
|
708
|
-
// can be batched together.
|
|
709
|
-
const setElementsQueue = useRef({ nodes: [], edges: [] });
|
|
698
|
+
function useQueue(runQueue) {
|
|
710
699
|
// Because we're using a ref above, we need some way to let React know when to
|
|
711
700
|
// actually process the queue. We flip this bit of state to `true` any time we
|
|
712
701
|
// mutate the queue and then flip it back to `false` after flushing the queue.
|
|
713
|
-
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)));
|
|
714
707
|
// Layout effects are guaranteed to run before the next render which means we
|
|
715
708
|
// shouldn't run into any issues with stale state or weird issues that come from
|
|
716
709
|
// rendering things one frame later than expected (we used to use `setTimeout`).
|
|
@@ -719,70 +712,123 @@ function useReactFlow() {
|
|
|
719
712
|
// trigger the hook again (!). If the hook is being run again we know that any
|
|
720
713
|
// updates should have been processed by now and we can safely clear the queue
|
|
721
714
|
// and bail early.
|
|
722
|
-
if (!
|
|
723
|
-
|
|
715
|
+
if (!shouldFlush) {
|
|
716
|
+
queue.reset();
|
|
724
717
|
return;
|
|
725
718
|
}
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
// array methods where we can.
|
|
731
|
-
let next = nodes;
|
|
732
|
-
for (const payload of setElementsQueue.current.nodes) {
|
|
733
|
-
next = typeof payload === 'function' ? payload(next) : payload;
|
|
734
|
-
}
|
|
735
|
-
if (hasDefaultNodes) {
|
|
736
|
-
setNodes(next);
|
|
737
|
-
}
|
|
738
|
-
else if (onNodesChange) {
|
|
739
|
-
onNodesChange(getElementsDiffChanges({
|
|
740
|
-
items: next,
|
|
741
|
-
lookup: nodeLookup,
|
|
742
|
-
}));
|
|
743
|
-
}
|
|
744
|
-
setElementsQueue.current.nodes = [];
|
|
745
|
-
}
|
|
746
|
-
if (setElementsQueue.current.edges.length) {
|
|
747
|
-
const { edges = [], setEdges, hasDefaultEdges, onEdgesChange, edgeLookup } = store.getState();
|
|
748
|
-
let next = edges;
|
|
749
|
-
for (const payload of setElementsQueue.current.edges) {
|
|
750
|
-
next = typeof payload === 'function' ? payload(next) : payload;
|
|
751
|
-
}
|
|
752
|
-
if (hasDefaultEdges) {
|
|
753
|
-
setEdges(next);
|
|
754
|
-
}
|
|
755
|
-
else if (onEdgesChange) {
|
|
756
|
-
onEdgesChange(getElementsDiffChanges({
|
|
757
|
-
items: next,
|
|
758
|
-
lookup: edgeLookup,
|
|
759
|
-
}));
|
|
760
|
-
}
|
|
761
|
-
setElementsQueue.current.edges = [];
|
|
719
|
+
const queueItems = queue.get();
|
|
720
|
+
if (queueItems.length) {
|
|
721
|
+
runQueue(queueItems);
|
|
722
|
+
queue.reset();
|
|
762
723
|
}
|
|
763
724
|
// Beacuse we're using reactive state to trigger this effect, we need to flip
|
|
764
725
|
// it back to false.
|
|
765
|
-
|
|
766
|
-
}, [
|
|
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), []);
|
|
767
819
|
const setNodes = useCallback((payload) => {
|
|
768
|
-
|
|
769
|
-
setShouldFlushQueue(true);
|
|
820
|
+
batchContext.nodeQueue.push(payload);
|
|
770
821
|
}, []);
|
|
771
822
|
const setEdges = useCallback((payload) => {
|
|
772
|
-
|
|
773
|
-
setShouldFlushQueue(true);
|
|
823
|
+
batchContext.edgeQueue.push(payload);
|
|
774
824
|
}, []);
|
|
775
825
|
const addNodes = useCallback((payload) => {
|
|
776
826
|
const newNodes = Array.isArray(payload) ? payload : [payload];
|
|
777
|
-
|
|
778
|
-
// to `setNodes` that might happen elsewhere.
|
|
779
|
-
setElementsQueue.current.nodes.push((nodes) => [...nodes, ...newNodes]);
|
|
780
|
-
setShouldFlushQueue(true);
|
|
827
|
+
batchContext.nodeQueue.push((nodes) => [...nodes, ...newNodes]);
|
|
781
828
|
}, []);
|
|
782
829
|
const addEdges = useCallback((payload) => {
|
|
783
830
|
const newEdges = Array.isArray(payload) ? payload : [payload];
|
|
784
|
-
|
|
785
|
-
setShouldFlushQueue(true);
|
|
831
|
+
batchContext.edgeQueue.push((edges) => [...edges, ...newEdges]);
|
|
786
832
|
}, []);
|
|
787
833
|
const toObject = useCallback(() => {
|
|
788
834
|
const { nodes = [], edges = [], transform } = store.getState();
|
|
@@ -2805,7 +2851,7 @@ function ReactFlowProvider({ initialNodes: nodes, initialEdges: edges, defaultNo
|
|
|
2805
2851
|
height,
|
|
2806
2852
|
fitView,
|
|
2807
2853
|
}));
|
|
2808
|
-
return jsx(Provider$1, { value: store, children: children });
|
|
2854
|
+
return (jsx(Provider$1, { value: store, children: jsx(BatchProvider, { children: children }) }));
|
|
2809
2855
|
}
|
|
2810
2856
|
|
|
2811
2857
|
function Wrapper({ children, nodes, edges, defaultNodes, defaultEdges, width, height, fitView, }) {
|
|
@@ -2980,9 +3026,9 @@ const selector$6 = (options) => (s) => {
|
|
|
2980
3026
|
if (s.nodeLookup.size === 0) {
|
|
2981
3027
|
return false;
|
|
2982
3028
|
}
|
|
2983
|
-
for (const [,
|
|
2984
|
-
if (options.includeHiddenNodes || !
|
|
2985
|
-
if (
|
|
3029
|
+
for (const [, { hidden, internals }] of s.nodeLookup) {
|
|
3030
|
+
if (options.includeHiddenNodes || !hidden) {
|
|
3031
|
+
if (internals.handleBounds === undefined || !nodeHasDimensions(internals.userNode)) {
|
|
2986
3032
|
return false;
|
|
2987
3033
|
}
|
|
2988
3034
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Queue } from './types';
|
|
3
|
+
import type { Edge, Node } from '../../types';
|
|
4
|
+
/**
|
|
5
|
+
* This is a context provider that holds and processes the node and edge update queues
|
|
6
|
+
* that are needed to handle setNodes, addNodes, setEdges and addEdges.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
export declare function BatchProvider<NodeType extends Node = Node, EdgeType extends Edge = Edge>({ children, }: {
|
|
11
|
+
children: ReactNode;
|
|
12
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
export declare function useBatchContext(): {
|
|
14
|
+
nodeQueue: Queue<any>;
|
|
15
|
+
edgeQueue: Queue<any>;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAiB,SAAS,EAAoC,MAAM,OAAO,CAAC;AAKnF,OAAO,EAAE,KAAK,EAAa,MAAM,SAAS,CAAC;AAC3C,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAU9C;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,EACxF,QAAQ,GACT,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAmDA;AAED,wBAAgB,eAAe;;;EAQ9B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;AAEvD,MAAM,MAAM,KAAK,CAAC,CAAC,IAAI;IACrB,GAAG,EAAE,MAAM,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1B,KAAK,EAAE,MAAM,IAAI,CAAC;IAClB,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC;CACpC,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Queue, QueueItem } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* This hook returns a queue that can be used to batch updates.
|
|
4
|
+
*
|
|
5
|
+
* @param runQueue - a function that gets called when the queue is flushed
|
|
6
|
+
* @internal
|
|
7
|
+
*
|
|
8
|
+
* @returns a Queue object
|
|
9
|
+
*/
|
|
10
|
+
export declare function useQueue<T>(runQueue: (items: QueueItem<T>[]) => void): Queue<T>;
|
|
11
|
+
//# sourceMappingURL=useQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useQueue.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/BatchProvider/useQueue.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAE3C;;;;;;;GAOG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,YAsCpE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../packages/react/src/components/ElementBatcher/index.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAkB,MAAM,aAAa,CAAC;AAM9D,wBAAgB,cAAc,CAAC,QAAQ,SAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,SAAS,IAAI,GAAG,IAAI,UA6ExF"}
|