refactor defaultNodes/defaultEdges handling (#3882)
* refactor(react): cleanup defaultnodes/defaultedges handling * chore(examples): cleanup * chore(examples): cleanup * fix(react): selection for uncontrolled flows
This commit is contained in:
@@ -41,7 +41,6 @@ const initNodes: MyNode[] = [
|
|||||||
data: {},
|
data: {},
|
||||||
position: { x: 100, y: 0 },
|
position: { x: 100, y: 0 },
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
id: '2',
|
id: '2',
|
||||||
type: 'text',
|
type: 'text',
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ export function ReactFlowProvider({
|
|||||||
children,
|
children,
|
||||||
initialNodes,
|
initialNodes,
|
||||||
initialEdges,
|
initialEdges,
|
||||||
|
defaultNodes,
|
||||||
|
defaultEdges,
|
||||||
initialWidth,
|
initialWidth,
|
||||||
initialHeight,
|
initialHeight,
|
||||||
fitView,
|
fitView,
|
||||||
@@ -17,6 +19,8 @@ export function ReactFlowProvider({
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
initialNodes?: Node[];
|
initialNodes?: Node[];
|
||||||
initialEdges?: Edge[];
|
initialEdges?: Edge[];
|
||||||
|
defaultNodes?: Node[];
|
||||||
|
defaultEdges?: Edge[];
|
||||||
initialWidth?: number;
|
initialWidth?: number;
|
||||||
initialHeight?: number;
|
initialHeight?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
@@ -27,6 +31,8 @@ export function ReactFlowProvider({
|
|||||||
storeRef.current = createRFStore({
|
storeRef.current = createRFStore({
|
||||||
nodes: initialNodes,
|
nodes: initialNodes,
|
||||||
edges: initialEdges,
|
edges: initialEdges,
|
||||||
|
defaultNodes,
|
||||||
|
defaultEdges,
|
||||||
width: initialWidth,
|
width: initialWidth,
|
||||||
height: initialHeight,
|
height: initialHeight,
|
||||||
fitView,
|
fitView,
|
||||||
|
|||||||
@@ -15,8 +15,6 @@ import { initNodeOrigin } from '../../container/ReactFlow';
|
|||||||
const reactFlowFieldsToTrack = [
|
const reactFlowFieldsToTrack = [
|
||||||
'nodes',
|
'nodes',
|
||||||
'edges',
|
'edges',
|
||||||
'defaultNodes',
|
|
||||||
'defaultEdges',
|
|
||||||
'onConnect',
|
'onConnect',
|
||||||
'onConnectStart',
|
'onConnectStart',
|
||||||
'onConnectEnd',
|
'onConnectEnd',
|
||||||
@@ -76,7 +74,6 @@ const fieldsToTrack = [...reactFlowFieldsToTrack, 'rfId'] as const;
|
|||||||
const selector = (s: ReactFlowState) => ({
|
const selector = (s: ReactFlowState) => ({
|
||||||
setNodes: s.setNodes,
|
setNodes: s.setNodes,
|
||||||
setEdges: s.setEdges,
|
setEdges: s.setEdges,
|
||||||
setDefaultNodesAndEdges: s.setDefaultNodesAndEdges,
|
|
||||||
setMinZoom: s.setMinZoom,
|
setMinZoom: s.setMinZoom,
|
||||||
setMaxZoom: s.setMaxZoom,
|
setMaxZoom: s.setMaxZoom,
|
||||||
setTranslateExtent: s.setTranslateExtent,
|
setTranslateExtent: s.setTranslateExtent,
|
||||||
@@ -85,22 +82,13 @@ const selector = (s: ReactFlowState) => ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export function StoreUpdater(props: StoreUpdaterProps) {
|
export function StoreUpdater(props: StoreUpdaterProps) {
|
||||||
const {
|
const { setNodes, setEdges, setMinZoom, setMaxZoom, setTranslateExtent, setNodeExtent, reset } = useStore(
|
||||||
setNodes,
|
selector,
|
||||||
setEdges,
|
shallow
|
||||||
setDefaultNodesAndEdges,
|
);
|
||||||
setMinZoom,
|
|
||||||
setMaxZoom,
|
|
||||||
setTranslateExtent,
|
|
||||||
setNodeExtent,
|
|
||||||
reset,
|
|
||||||
} = useStore(selector, shallow);
|
|
||||||
const store = useStoreApi();
|
const store = useStoreApi();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const edgesWithDefaults = props.defaultEdges?.map((e) => ({ ...e, ...props.defaultEdgeOptions }));
|
|
||||||
setDefaultNodesAndEdges(props.defaultNodes, edgesWithDefaults);
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
reset();
|
reset();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ export function Wrapper({
|
|||||||
children,
|
children,
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
|
defaultNodes,
|
||||||
|
defaultEdges,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
@@ -15,6 +17,8 @@ export function Wrapper({
|
|||||||
children: ReactNode;
|
children: ReactNode;
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
|
defaultNodes?: Node[];
|
||||||
|
defaultEdges?: Edge[];
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
@@ -31,6 +35,8 @@ export function Wrapper({
|
|||||||
<ReactFlowProvider
|
<ReactFlowProvider
|
||||||
initialNodes={nodes}
|
initialNodes={nodes}
|
||||||
initialEdges={edges}
|
initialEdges={edges}
|
||||||
|
defaultNodes={defaultNodes}
|
||||||
|
defaultEdges={defaultEdges}
|
||||||
initialWidth={width}
|
initialWidth={width}
|
||||||
initialHeight={height}
|
initialHeight={height}
|
||||||
fitView={fitView}
|
fitView={fitView}
|
||||||
|
|||||||
@@ -166,7 +166,15 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
data-testid="rf__wrapper"
|
data-testid="rf__wrapper"
|
||||||
id={id}
|
id={id}
|
||||||
>
|
>
|
||||||
<Wrapper nodes={nodes} edges={edges} width={width} height={height} fitView={fitView}>
|
<Wrapper
|
||||||
|
nodes={nodes}
|
||||||
|
edges={edges}
|
||||||
|
defaultNodes={defaultNodes}
|
||||||
|
defaultEdges={defaultEdges}
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
fitView={fitView}
|
||||||
|
>
|
||||||
<GraphView
|
<GraphView
|
||||||
onInit={onInit}
|
onInit={onInit}
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
@@ -234,8 +242,6 @@ const ReactFlow = forwardRef<ReactFlowRefType, ReactFlowProps>(
|
|||||||
<StoreUpdater
|
<StoreUpdater
|
||||||
nodes={nodes}
|
nodes={nodes}
|
||||||
edges={edges}
|
edges={edges}
|
||||||
defaultNodes={defaultNodes}
|
|
||||||
defaultEdges={defaultEdges}
|
|
||||||
onConnect={onConnect}
|
onConnect={onConnect}
|
||||||
onConnectStart={onConnectStart}
|
onConnectStart={onConnectStart}
|
||||||
onConnectEnd={onConnectEnd}
|
onConnectEnd={onConnectEnd}
|
||||||
|
|||||||
@@ -10,8 +10,7 @@ import {
|
|||||||
updateConnectionLookup,
|
updateConnectionLookup,
|
||||||
} from '@xyflow/system';
|
} from '@xyflow/system';
|
||||||
|
|
||||||
import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||||
import { updateNodesAndEdgesSelections } from './utils';
|
|
||||||
import getInitialState from './initialState';
|
import getInitialState from './initialState';
|
||||||
import type {
|
import type {
|
||||||
ReactFlowState,
|
ReactFlowState,
|
||||||
@@ -28,19 +27,23 @@ import type {
|
|||||||
const createRFStore = ({
|
const createRFStore = ({
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges,
|
||||||
|
defaultNodes,
|
||||||
|
defaultEdges,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
|
defaultNodes?: Node[];
|
||||||
|
defaultEdges?: Edge[];
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
}) =>
|
}) =>
|
||||||
createWithEqualityFn<ReactFlowState>(
|
createWithEqualityFn<ReactFlowState>(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
...getInitialState({ nodes, edges, width, height, fitView }),
|
...getInitialState({ nodes, edges, width, height, fitView, defaultNodes, defaultEdges }),
|
||||||
setNodes: (nodes: Node[]) => {
|
setNodes: (nodes: Node[]) => {
|
||||||
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
||||||
// setNodes() is called exclusively in response to user actions:
|
// setNodes() is called exclusively in response to user actions:
|
||||||
@@ -49,7 +52,6 @@ const createRFStore = ({
|
|||||||
//
|
//
|
||||||
// When this happens, we take the note objects passed by the user and extend them with fields
|
// When this happens, we take the note objects passed by the user and extend them with fields
|
||||||
// relevant for internal React Flow operations.
|
// relevant for internal React Flow operations.
|
||||||
// TODO: consider updating the types to reflect the distinction between user-provided nodes and internal nodes.
|
|
||||||
const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
const nodesWithInternalData = adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect });
|
||||||
|
|
||||||
set({ nodes: nodesWithInternalData });
|
set({ nodes: nodesWithInternalData });
|
||||||
@@ -61,38 +63,6 @@ const createRFStore = ({
|
|||||||
|
|
||||||
set({ edges });
|
set({ edges });
|
||||||
},
|
},
|
||||||
// when the user works with an uncontrolled flow,
|
|
||||||
// we set a flag `hasDefaultNodes` / `hasDefaultEdges`
|
|
||||||
setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => {
|
|
||||||
const hasDefaultNodes = typeof nodes !== 'undefined';
|
|
||||||
const hasDefaultEdges = typeof edges !== 'undefined';
|
|
||||||
|
|
||||||
const nextState: {
|
|
||||||
nodes?: Node[];
|
|
||||||
edges?: Edge[];
|
|
||||||
hasDefaultNodes: boolean;
|
|
||||||
hasDefaultEdges: boolean;
|
|
||||||
} = {
|
|
||||||
hasDefaultNodes,
|
|
||||||
hasDefaultEdges,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (hasDefaultNodes) {
|
|
||||||
const { nodeLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
|
||||||
nextState.nodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
|
||||||
nodeOrigin,
|
|
||||||
elevateNodesOnSelect,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (hasDefaultEdges) {
|
|
||||||
const { connectionLookup, edgeLookup } = get();
|
|
||||||
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
|
||||||
|
|
||||||
nextState.edges = edges;
|
|
||||||
}
|
|
||||||
|
|
||||||
set(nextState);
|
|
||||||
},
|
|
||||||
// Every node gets registerd at a ResizeObserver. Whenever a node
|
// Every node gets registerd at a ResizeObserver. Whenever a node
|
||||||
// changes its dimensions, this function is called to measure the
|
// changes its dimensions, this function is called to measure the
|
||||||
// new dimensions and update the nodes.
|
// new dimensions and update the nodes.
|
||||||
@@ -166,81 +136,67 @@ const createRFStore = ({
|
|||||||
|
|
||||||
get().triggerNodeChanges(changes);
|
get().triggerNodeChanges(changes);
|
||||||
},
|
},
|
||||||
|
|
||||||
triggerNodeChanges: (changes) => {
|
triggerNodeChanges: (changes) => {
|
||||||
const { onNodesChange, nodeLookup, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get();
|
const { onNodesChange, setNodes, nodes, hasDefaultNodes } = get();
|
||||||
|
|
||||||
if (changes?.length) {
|
if (changes?.length) {
|
||||||
if (hasDefaultNodes) {
|
if (hasDefaultNodes) {
|
||||||
const updatedNodes = applyNodeChanges(changes, nodes);
|
const updatedNodes = applyNodeChanges(changes, nodes);
|
||||||
const nextNodes = adoptUserProvidedNodes(updatedNodes, nodeLookup, {
|
setNodes(updatedNodes);
|
||||||
nodeOrigin,
|
|
||||||
elevateNodesOnSelect,
|
|
||||||
});
|
|
||||||
set({ nodes: nextNodes });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onNodesChange?.(changes);
|
onNodesChange?.(changes);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
triggerEdgeChanges: (changes) => {
|
||||||
|
const { onEdgesChange, setEdges, edges, hasDefaultEdges } = get();
|
||||||
|
|
||||||
|
if (changes?.length) {
|
||||||
|
if (hasDefaultEdges) {
|
||||||
|
const updatedEdges = applyEdgeChanges(changes, edges);
|
||||||
|
setEdges(updatedEdges);
|
||||||
|
}
|
||||||
|
|
||||||
|
onEdgesChange?.(changes);
|
||||||
|
}
|
||||||
|
},
|
||||||
addSelectedNodes: (selectedNodeIds) => {
|
addSelectedNodes: (selectedNodeIds) => {
|
||||||
const { multiSelectionActive, edges, nodes } = get();
|
const { multiSelectionActive, edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
||||||
let changedNodes: NodeSelectionChange[];
|
|
||||||
let changedEdges: EdgeSelectionChange[] | null = null;
|
|
||||||
|
|
||||||
if (multiSelectionActive) {
|
if (multiSelectionActive) {
|
||||||
changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)) as NodeSelectionChange[];
|
const nodeChanges = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true));
|
||||||
} else {
|
triggerNodeChanges(nodeChanges as NodeSelectionChange[]);
|
||||||
changedNodes = getSelectionChanges(nodes, new Set([...selectedNodeIds]), true);
|
return;
|
||||||
changedEdges = getSelectionChanges(edges);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodesAndEdgesSelections({
|
triggerNodeChanges(getSelectionChanges(nodes, new Set([...selectedNodeIds]), true));
|
||||||
changedNodes,
|
triggerEdgeChanges(getSelectionChanges(edges));
|
||||||
changedEdges,
|
|
||||||
get,
|
|
||||||
set,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
addSelectedEdges: (selectedEdgeIds) => {
|
addSelectedEdges: (selectedEdgeIds) => {
|
||||||
const { multiSelectionActive, edges, nodes } = get();
|
const { multiSelectionActive, edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
||||||
let changedEdges: EdgeSelectionChange[];
|
|
||||||
let changedNodes: NodeSelectionChange[] | null = null;
|
|
||||||
|
|
||||||
if (multiSelectionActive) {
|
if (multiSelectionActive) {
|
||||||
changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)) as EdgeSelectionChange[];
|
const changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true));
|
||||||
} else {
|
triggerEdgeChanges(changedEdges as EdgeSelectionChange[]);
|
||||||
changedEdges = getSelectionChanges(edges, new Set([...selectedEdgeIds]));
|
return;
|
||||||
changedNodes = getSelectionChanges(nodes, new Set(), true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateNodesAndEdgesSelections({
|
triggerEdgeChanges(getSelectionChanges(edges, new Set([...selectedEdgeIds])));
|
||||||
changedNodes,
|
triggerNodeChanges(getSelectionChanges(nodes, new Set(), true));
|
||||||
changedEdges,
|
|
||||||
get,
|
|
||||||
set,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => {
|
unselectNodesAndEdges: ({ nodes, edges }: UnselectNodesAndEdgesParams = {}) => {
|
||||||
const { edges: storeEdges, nodes: storeNodes } = get();
|
const { edges: storeEdges, nodes: storeNodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
||||||
const nodesToUnselect = nodes ? nodes : storeNodes;
|
const nodesToUnselect = nodes ? nodes : storeNodes;
|
||||||
const edgesToUnselect = edges ? edges : storeEdges;
|
const edgesToUnselect = edges ? edges : storeEdges;
|
||||||
|
|
||||||
const changedNodes = nodesToUnselect.map((n) => {
|
const nodeChanges = nodesToUnselect.map((n) => {
|
||||||
n.selected = false;
|
n.selected = false;
|
||||||
return createSelectionChange(n.id, false);
|
return createSelectionChange(n.id, false);
|
||||||
}) as NodeSelectionChange[];
|
|
||||||
const changedEdges = edgesToUnselect.map((edge) =>
|
|
||||||
createSelectionChange(edge.id, false)
|
|
||||||
) as EdgeSelectionChange[];
|
|
||||||
|
|
||||||
updateNodesAndEdgesSelections({
|
|
||||||
changedNodes,
|
|
||||||
changedEdges,
|
|
||||||
get,
|
|
||||||
set,
|
|
||||||
});
|
});
|
||||||
|
const edgeChanges = edgesToUnselect.map((edge) => createSelectionChange(edge.id, false));
|
||||||
|
|
||||||
|
triggerNodeChanges(nodeChanges as NodeSelectionChange[]);
|
||||||
|
triggerEdgeChanges(edgeChanges as EdgeSelectionChange[]);
|
||||||
},
|
},
|
||||||
setMinZoom: (minZoom) => {
|
setMinZoom: (minZoom) => {
|
||||||
const { panZoom, maxZoom } = get();
|
const { panZoom, maxZoom } = get();
|
||||||
@@ -260,21 +216,19 @@ const createRFStore = ({
|
|||||||
set({ translateExtent });
|
set({ translateExtent });
|
||||||
},
|
},
|
||||||
resetSelectedElements: () => {
|
resetSelectedElements: () => {
|
||||||
const { edges, nodes } = get();
|
const { edges, nodes, triggerNodeChanges, triggerEdgeChanges } = get();
|
||||||
|
|
||||||
const nodesToUnselect = nodes
|
const nodeChanges = nodes.reduce<NodeSelectionChange[]>(
|
||||||
.filter((e) => e.selected)
|
(res, node) => (node.selected ? [...res, createSelectionChange(node.id, false) as NodeSelectionChange] : res),
|
||||||
.map((n) => createSelectionChange(n.id, false)) as NodeSelectionChange[];
|
[]
|
||||||
const edgesToUnselect = edges
|
);
|
||||||
.filter((e) => e.selected)
|
const edgeChanges = edges.reduce<EdgeSelectionChange[]>(
|
||||||
.map((e) => createSelectionChange(e.id, false)) as EdgeSelectionChange[];
|
(res, edge) => (edge.selected ? [...res, createSelectionChange(edge.id, false) as EdgeSelectionChange] : res),
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
updateNodesAndEdgesSelections({
|
triggerNodeChanges(nodeChanges);
|
||||||
changedNodes: nodesToUnselect,
|
triggerEdgeChanges(edgeChanges);
|
||||||
changedEdges: edgesToUnselect,
|
|
||||||
get,
|
|
||||||
set,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
setNodeExtent: (nodeExtent) => {
|
setNodeExtent: (nodeExtent) => {
|
||||||
const { nodes } = get();
|
const { nodes } = get();
|
||||||
|
|||||||
@@ -12,14 +12,18 @@ import {
|
|||||||
import type { Edge, Node, ReactFlowStore } from '../types';
|
import type { Edge, Node, ReactFlowStore } from '../types';
|
||||||
|
|
||||||
const getInitialState = ({
|
const getInitialState = ({
|
||||||
nodes = [],
|
nodes,
|
||||||
edges = [],
|
edges,
|
||||||
|
defaultNodes,
|
||||||
|
defaultEdges,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
fitView,
|
fitView,
|
||||||
}: {
|
}: {
|
||||||
nodes?: Node[];
|
nodes?: Node[];
|
||||||
edges?: Edge[];
|
edges?: Edge[];
|
||||||
|
defaultNodes?: Node[];
|
||||||
|
defaultEdges?: Edge[];
|
||||||
width?: number;
|
width?: number;
|
||||||
height?: number;
|
height?: number;
|
||||||
fitView?: boolean;
|
fitView?: boolean;
|
||||||
@@ -27,9 +31,11 @@ const getInitialState = ({
|
|||||||
const nodeLookup = new Map();
|
const nodeLookup = new Map();
|
||||||
const connectionLookup = new Map();
|
const connectionLookup = new Map();
|
||||||
const edgeLookup = new Map();
|
const edgeLookup = new Map();
|
||||||
|
const storeEdges = defaultEdges ?? edges ?? [];
|
||||||
|
const storeNodes = defaultNodes ?? nodes ?? [];
|
||||||
|
|
||||||
updateConnectionLookup(connectionLookup, edgeLookup, edges);
|
updateConnectionLookup(connectionLookup, edgeLookup, storeEdges);
|
||||||
const nextNodes = adoptUserProvidedNodes(nodes, nodeLookup, {
|
const nextNodes = adoptUserProvidedNodes(storeNodes, nodeLookup, {
|
||||||
nodeOrigin: [0, 0],
|
nodeOrigin: [0, 0],
|
||||||
elevateNodesOnSelect: false,
|
elevateNodesOnSelect: false,
|
||||||
});
|
});
|
||||||
@@ -51,13 +57,13 @@ const getInitialState = ({
|
|||||||
transform,
|
transform,
|
||||||
nodes: nextNodes,
|
nodes: nextNodes,
|
||||||
nodeLookup,
|
nodeLookup,
|
||||||
edges,
|
edges: storeEdges,
|
||||||
edgeLookup,
|
edgeLookup,
|
||||||
connectionLookup,
|
connectionLookup,
|
||||||
onNodesChange: null,
|
onNodesChange: null,
|
||||||
onEdgesChange: null,
|
onEdgesChange: null,
|
||||||
hasDefaultNodes: false,
|
hasDefaultNodes: defaultNodes !== undefined,
|
||||||
hasDefaultEdges: false,
|
hasDefaultEdges: defaultEdges !== undefined,
|
||||||
panZoom: null,
|
panZoom: null,
|
||||||
minZoom: 0.5,
|
minZoom: 0.5,
|
||||||
maxZoom: 2,
|
maxZoom: 2,
|
||||||
|
|||||||
@@ -1,44 +0,0 @@
|
|||||||
import type { StoreApi } from 'zustand';
|
|
||||||
import type { Edge, EdgeSelectionChange, Node, NodeSelectionChange, ReactFlowState } from '../types';
|
|
||||||
|
|
||||||
export function handleControlledSelectionChange<NodeOrEdge extends Node | Edge>(
|
|
||||||
changes: NodeSelectionChange[] | EdgeSelectionChange[],
|
|
||||||
items: NodeOrEdge[]
|
|
||||||
): NodeOrEdge[] {
|
|
||||||
return items.map((item) => {
|
|
||||||
const change = changes.find((change) => change.id === item.id);
|
|
||||||
|
|
||||||
if (change) {
|
|
||||||
item.selected = change.selected;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
type UpdateNodesAndEdgesParams = {
|
|
||||||
changedNodes: NodeSelectionChange[] | null;
|
|
||||||
changedEdges: EdgeSelectionChange[] | null;
|
|
||||||
get: StoreApi<ReactFlowState>['getState'];
|
|
||||||
set: StoreApi<ReactFlowState>['setState'];
|
|
||||||
};
|
|
||||||
|
|
||||||
export function updateNodesAndEdgesSelections({ changedNodes, changedEdges, get, set }: UpdateNodesAndEdgesParams) {
|
|
||||||
const { nodes, edges, onNodesChange, onEdgesChange, hasDefaultNodes, hasDefaultEdges } = get();
|
|
||||||
|
|
||||||
if (changedNodes?.length) {
|
|
||||||
if (hasDefaultNodes) {
|
|
||||||
set({ nodes: handleControlledSelectionChange(changedNodes, nodes) });
|
|
||||||
}
|
|
||||||
|
|
||||||
onNodesChange?.(changedNodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changedEdges?.length) {
|
|
||||||
if (hasDefaultEdges) {
|
|
||||||
set({ edges: handleControlledSelectionChange(changedEdges, edges) });
|
|
||||||
}
|
|
||||||
|
|
||||||
onEdgesChange?.(changedEdges);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -43,6 +43,7 @@ import type {
|
|||||||
OnDelete,
|
OnDelete,
|
||||||
OnNodeDrag,
|
OnNodeDrag,
|
||||||
OnBeforeDelete,
|
OnBeforeDelete,
|
||||||
|
EdgeChange,
|
||||||
} from '.';
|
} from '.';
|
||||||
|
|
||||||
export type ReactFlowStore = {
|
export type ReactFlowStore = {
|
||||||
@@ -150,7 +151,6 @@ export type ReactFlowStore = {
|
|||||||
export type ReactFlowActions = {
|
export type ReactFlowActions = {
|
||||||
setNodes: (nodes: Node[]) => void;
|
setNodes: (nodes: Node[]) => void;
|
||||||
setEdges: (edges: Edge[]) => void;
|
setEdges: (edges: Edge[]) => void;
|
||||||
setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => void;
|
|
||||||
updateNodeDimensions: (updates: Map<string, NodeDimensionUpdate>) => void;
|
updateNodeDimensions: (updates: Map<string, NodeDimensionUpdate>) => void;
|
||||||
updateNodePositions: UpdateNodePositions;
|
updateNodePositions: UpdateNodePositions;
|
||||||
resetSelectedElements: () => void;
|
resetSelectedElements: () => void;
|
||||||
@@ -164,7 +164,8 @@ export type ReactFlowActions = {
|
|||||||
cancelConnection: () => void;
|
cancelConnection: () => void;
|
||||||
updateConnection: UpdateConnection;
|
updateConnection: UpdateConnection;
|
||||||
reset: () => void;
|
reset: () => void;
|
||||||
triggerNodeChanges: (changes: NodeChange[]) => void;
|
triggerNodeChanges: (changes: NodeChange[] | null) => void;
|
||||||
|
triggerEdgeChanges: (changes: EdgeChange[] | null) => void;
|
||||||
panBy: PanBy;
|
panBy: PanBy;
|
||||||
fitView: (nodes: Node[], options?: FitViewOptions) => boolean;
|
fitView: (nodes: Node[], options?: FitViewOptions) => boolean;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -219,11 +219,13 @@ export function applyEdgeChanges<EdgeType extends Edge = Edge>(
|
|||||||
return applyChanges(changes, edges) as EdgeType[];
|
return applyChanges(changes, edges) as EdgeType[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export const createSelectionChange = (id: string, selected: boolean): NodeSelectionChange | EdgeSelectionChange => ({
|
export function createSelectionChange(id: string, selected: boolean) {
|
||||||
id,
|
return {
|
||||||
type: 'select',
|
id,
|
||||||
selected,
|
type: 'select',
|
||||||
});
|
selected,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function getSelectionChanges(
|
export function getSelectionChanges(
|
||||||
items: any[],
|
items: any[],
|
||||||
|
|||||||
Reference in New Issue
Block a user