refactor(zustand): use shallow when needed

This commit is contained in:
moklick
2021-10-13 16:19:46 +02:00
parent 3f8c252096
commit 1f41cc0d9a
19 changed files with 159 additions and 226 deletions
+4 -3
View File
@@ -15,7 +15,6 @@
"classcat": "^5.0.3",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0",
"fast-deep-equal": "^3.1.3",
"react-draggable": "^4.4.4",
"react-redux": "^7.2.5",
"redux": "^4.1.1",
@@ -5285,7 +5284,8 @@
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"node_modules/fast-glob": {
"version": "3.2.7",
@@ -15220,7 +15220,8 @@
"fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"fast-glob": {
"version": "3.2.7",
-1
View File
@@ -37,7 +37,6 @@
"classcat": "^5.0.3",
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0",
"fast-deep-equal": "^3.1.3",
"react-draggable": "^4.4.4",
"react-redux": "^7.2.5",
"redux": "^4.1.1",
+2 -1
View File
@@ -1,5 +1,6 @@
import React, { memo, HTMLAttributes } from 'react';
import cc from 'classcat';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import { getRectOfNodes, getBoundsofRects } from '../../utils/graph';
@@ -34,7 +35,7 @@ const MiniMap = ({
nodeStrokeWidth = 2,
maskColor = 'rgb(240, 242, 243, 0.7)',
}: MiniMapProps) => {
const { width: containerWidth, height: containerHeight, transform, nodes } = useStore(selector);
const { width: containerWidth, height: containerHeight, transform, nodes } = useStore(selector, shallow);
const [tX, tY, tScale] = transform;
const mapClasses = cc(['react-flow__minimap', className]);
+5 -2
View File
@@ -1,5 +1,6 @@
import React, { memo, ComponentType, useCallback, useState, useMemo } from 'react';
import cc from 'classcat';
import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../store';
import { Edge, EdgeProps, WrapEdgeProps, ReactFlowState } from '../../types';
@@ -56,8 +57,10 @@ export default (EdgeComponent: ComponentType<EdgeProps>) => {
onEdgeUpdateEnd,
}: WrapEdgeProps): JSX.Element | null => {
const store = useStoreApi();
const { addSelectedElements, setConnectionNodeId, unsetNodesSelection, setPosition, connectionMode } =
useStore(selector);
const { addSelectedElements, setConnectionNodeId, unsetNodesSelection, setPosition, connectionMode } = useStore(
selector,
shallow
);
const [updating, setUpdating] = useState<boolean>(false);
+2 -1
View File
@@ -1,4 +1,5 @@
import { useEffect } from 'react';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import { Node, Edge, ReactFlowState } from '../../types';
@@ -14,7 +15,7 @@ const selector = (s: ReactFlowState) => ({
});
const ElementUpdater = ({ nodes, edges }: ElementUpdaterProps) => {
const { setNodes, setEdges } = useStore(selector);
const { setNodes, setEdges } = useStore(selector, shallow);
useEffect(() => {
setNodes(nodes);
+2 -1
View File
@@ -1,5 +1,6 @@
import React, { memo, useContext, useCallback, HTMLAttributes, forwardRef } from 'react';
import cc from 'classcat';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import NodeIdContext from '../../contexts/NodeIdContext';
@@ -45,7 +46,7 @@ const Handle = forwardRef<HTMLDivElement, HandleComponentProps>(
onConnectStop,
onConnectEnd,
connectionMode,
} = useStore(selector);
} = useStore(selector, shallow);
const handleId = id || null;
const isTarget = type === 'target';
+4 -13
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useRef, memo, ComponentType, CSSProperties, useMemo, MouseEvent, useCallback } from 'react';
import { DraggableCore, DraggableData, DraggableEvent } from 'react-draggable';
import cc from 'classcat';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import { Provider } from '../../contexts/NodeIdContext';
@@ -10,6 +11,7 @@ const selector = (s: ReactFlowState) => ({
addSelectedElements: s.addSelectedElements,
onNodesChange: s.onNodesChange,
unsetNodesSelection: s.unsetNodesSelection,
updateNodePosDiff: s.updateNodePosDiff,
});
export default (NodeComponent: ComponentType<NodeComponentProps>) => {
@@ -47,7 +49,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
dragHandle,
}: WrapNodeProps) => {
// const updateNodeDimensions = useStoreActions((actions) => actions.updateNodeDimensions);
const { addSelectedElements, onNodesChange, unsetNodesSelection } = useStore(selector);
const { addSelectedElements, onNodesChange, unsetNodesSelection, updateNodePosDiff } = useStore(selector, shallow);
const nodeElement = useRef<HTMLDivElement>(null);
const node = useMemo(() => ({ id, type, position: { x: xPos, y: yPos }, data }), [id, type, xPos, yPos, data]);
@@ -154,18 +156,7 @@ export default (NodeComponent: ComponentType<NodeComponentProps>) => {
onNodeDrag(event as MouseEvent, node);
}
onNodesChange?.([
{
id,
change: {
position: {
x: node.position.x,
y: node.position.y,
},
isDragging: true,
},
},
]);
updateNodePosDiff({ id, isDragging: true, diff: { x: draggableData.deltaX, y: draggableData.deltaY } });
},
[id, node, onNodeDrag]
);
+5 -35
View File
@@ -7,7 +7,6 @@ import React, { useMemo, useCallback, useRef, MouseEvent } from 'react';
import ReactDraggable, { DraggableData } from 'react-draggable';
import { useStore } from '../../store';
import { isNode } from '../../utils/graph';
import { Node, ReactFlowState } from '../../types';
export interface NodesSelectionProps {
@@ -21,10 +20,9 @@ const selector = (s: ReactFlowState) => ({
transform: s.transform,
selectedNodesBbox: s.selectedNodesBbox,
selectionActive: s.selectionActive,
selectedElements: s.selectedElements,
selectedNodes: s.nodes.filter((n) => n.selected),
snapToGrid: s.snapToGrid,
snapGrid: s.snapGrid,
nodes: s.nodes,
updateNodePosDiff: s.updateNodePosDiff,
});
@@ -34,37 +32,13 @@ export default ({
onSelectionDragStop,
onSelectionContextMenu,
}: NodesSelectionProps) => {
const {
transform,
selectedNodesBbox,
selectionActive,
selectedElements,
snapToGrid,
snapGrid,
nodes,
updateNodePosDiff,
} = useStore(selector);
const { transform, selectedNodesBbox, selectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosDiff } =
useStore(selector);
const [tX, tY, tScale] = transform;
const nodeRef = useRef(null);
const grid = useMemo(() => (snapToGrid ? snapGrid : [1, 1])! as [number, number], [snapToGrid, snapGrid]);
const selectedNodes = useMemo(
() =>
selectedElements
? selectedElements.filter(isNode).map((selectedNode) => {
const matchingNode = nodes.find((node) => node.id === selectedNode.id);
return {
...matchingNode,
position: matchingNode?.position,
} as Node;
})
: [],
[selectedElements, nodes]
);
const style = useMemo(
() => ({
transform: `translate(${tX}px,${tY}px) scale(${tScale})`,
@@ -119,16 +93,12 @@ export default ({
const onContextMenu = useCallback(
(event: MouseEvent) => {
const selectedNodes = selectedElements
? selectedElements.filter(isNode).map((selectedNode) => nodes.find((node) => node.id === selectedNode.id)!)
: [];
onSelectionContextMenu?.(event, selectedNodes);
},
[onSelectionContextMenu]
[onSelectionContextMenu, selectedNodes]
);
if (!selectedElements || selectionActive) {
if (!selectedNodes || selectionActive) {
return null;
}
+6 -2
View File
@@ -1,4 +1,5 @@
import { useEffect } from 'react';
import shallow from 'zustand/shallow';
import { Elements, ReactFlowState } from '../../types';
import { useStore } from '../../store';
@@ -7,12 +8,15 @@ interface SelectionListenerProps {
onSelectionChange: (elements: Elements | null) => void;
}
const selectedElementsSelector = (s: ReactFlowState) => s.selectedElements;
const selectedElementsSelector = (s: ReactFlowState) => [
...s.nodes.filter((n) => n.selected),
...s.edges.filter((e) => e.selected),
];
// This is just a helper component for calling the onSelectionChange listener.
export default ({ onSelectionChange }: SelectionListenerProps) => {
const selectedElements = useStore(selectedElementsSelector);
const selectedElements = useStore(selectedElementsSelector, shallow);
useEffect(() => {
onSelectionChange(selectedElements);
+3 -11
View File
@@ -1,15 +1,14 @@
import React, { memo, CSSProperties, useCallback } from 'react';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import ConnectionLine from '../../components/ConnectionLine/index';
import { isEdge } from '../../utils/graph';
import MarkerDefinitions from './MarkerDefinitions';
import { getEdgePositions, getHandle } from './utils';
import {
Position,
Edge,
Node,
Elements,
Connection,
ConnectionLineType,
ConnectionLineComponent,
@@ -55,7 +54,6 @@ interface EdgeWrapperProps {
onEdgeUpdate?: OnEdgeUpdateFunc;
targetNode?: Node;
sourceNode?: Node;
selectedElements: Elements | null;
elementsSelectable: boolean;
connectionMode?: ConnectionMode;
}
@@ -77,7 +75,6 @@ const Edge = memo(
onEdgeUpdate,
targetNode,
sourceNode,
selectedElements,
elementsSelectable,
connectionMode,
}: EdgeWrapperProps) => {
@@ -152,8 +149,6 @@ const Edge = memo(
// return null;
// }
const isSelected = selectedElements?.some((elm) => isEdge(elm) && elm.id === edge.id) || false;
return (
<EdgeComponent
key={edge.id}
@@ -162,7 +157,7 @@ const Edge = memo(
type={edge.type}
data={edge.data}
onClick={onElementClick}
selected={isSelected}
selected={!!edge.selected}
animated={edge.animated}
label={edge.label}
labelStyle={edge.labelStyle}
@@ -207,7 +202,6 @@ const selector = (s: ReactFlowState) => ({
connectionHandleId: s.connectionHandleId,
connectionHandleType: s.connectionHandleType,
connectionPosition: s.connectionPosition,
selectedElements: s.selectedElements,
nodesConnectable: s.nodesConnectable,
elementsSelectable: s.elementsSelectable,
width: s.width,
@@ -222,12 +216,11 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
connectionHandleId,
connectionHandleType,
connectionPosition,
selectedElements,
nodesConnectable,
elementsSelectable,
width,
height,
} = useStore(selector);
} = useStore(selector, shallow);
if (!width) {
return null;
@@ -247,7 +240,6 @@ const EdgeRenderer = (props: EdgeRendererProps) => {
edge={edge}
sourceNode={edge.sourceNode}
targetNode={edge.targetNode}
selectedElements={selectedElements}
elementsSelectable={elementsSelectable}
markerEndId={props.markerEndId}
onEdgeContextMenu={props.onEdgeContextMenu}
+13
View File
@@ -169,3 +169,16 @@ export const getSourceTargetNodes = (edge: Edge, nodes: Node[]): SourceTargetNod
{ sourceNode: null, targetNode: null } as SourceTargetNode
);
};
export const extendEdgeWithSourceAndTarget = (edge: Edge, nodes: Node[]): Edge => {
const { sourceNode, targetNode } = getSourceTargetNodes(edge, nodes);
if (sourceNode) {
edge.sourceNode = sourceNode;
}
if (targetNode) {
edge.targetNode = targetNode;
}
return edge;
};
+2 -1
View File
@@ -1,4 +1,5 @@
import React, { useCallback, memo, ReactNode, WheelEvent, MouseEvent } from 'react';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import useGlobalKeyHandler from '../../hooks/useGlobalKeyHandler';
@@ -60,7 +61,7 @@ const FlowRenderer = ({
onSelectionDragStop,
onSelectionContextMenu,
}: FlowRendererProps) => {
const { unsetNodesSelection, resetSelectedElements, nodesSelectionActive } = useStore(selector);
const { unsetNodesSelection, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow);
const selectionKeyPressed = useKeyPress(selectionKeyCode);
+2 -1
View File
@@ -1,4 +1,5 @@
import React, { useEffect, useRef, memo } from 'react';
import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../../store';
import FlowRenderer from '../FlowRenderer';
@@ -136,7 +137,7 @@ const GraphView = ({
setConnectionMode,
setOnNodesChange,
setOnEdgesChange,
} = useStore(selector);
} = useStore(selector, shallow);
const { zoomIn, zoomOut, zoomTo, transform, fitView, initialized } = useZoomPanHelper();
+6 -11
View File
@@ -1,4 +1,5 @@
import React, { memo, useMemo, ComponentType, MouseEvent } from 'react';
import shallow from 'zustand/shallow';
import { useStore } from '../../store';
import { Node, NodeTypesType, ReactFlowState, Edge, WrapNodeProps } from '../../types';
@@ -21,7 +22,6 @@ interface NodeRendererProps {
const selector = (s: ReactFlowState) => ({
transform: s.transform,
selectedElements: s.selectedElements,
nodesDraggable: s.nodesDraggable,
nodesConnectable: s.nodesConnectable,
elementsSelectable: s.elementsSelectable,
@@ -30,15 +30,10 @@ const selector = (s: ReactFlowState) => ({
});
const NodeRenderer = (props: NodeRendererProps) => {
const {
transform,
selectedElements,
nodesDraggable,
nodesConnectable,
elementsSelectable,
nodes,
updateNodeDimensions,
} = useStore(selector);
const { transform, nodesDraggable, nodesConnectable, elementsSelectable, nodes, updateNodeDimensions } = useStore(
selector,
shallow
);
// const visibleNodes = props.onlyRenderVisibleElements
// ? getNodesInside(nodes, { x: 0, y: 0, width, height }, transform, true)
@@ -108,7 +103,7 @@ const NodeRenderer = (props: NodeRendererProps) => {
onNodeDrag={props.onNodeDrag}
onNodeDragStop={props.onNodeDragStop}
scale={transform[2]}
selected={selectedElements?.some(({ id }) => id === node.id) || false}
selected={!!node.selected}
isDraggable={isDraggable}
isSelectable={isSelectable}
isConnectable={isConnectable}
+2 -1
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useRef, ReactNode } from 'react';
import { zoom, zoomIdentity } from 'd3-zoom';
import { select, pointer } from 'd3-selection';
import shallow from 'zustand/shallow';
import { clamp } from '../../utils';
import useKeyPress from '../../hooks/useKeyPress';
@@ -74,7 +75,7 @@ const ZoomPane = ({
const store = useStoreApi();
const zoomPane = useRef<HTMLDivElement>(null);
const prevTransform = useRef<FlowTransform>({ x: 0, y: 0, zoom: 0 });
const { d3Zoom, d3Selection, d3ZoomHandler, initD3Zoom, updateTransform } = useStore(selector);
const { d3Zoom, d3Selection, d3ZoomHandler, initD3Zoom, updateTransform } = useStore(selector, shallow);
const zoomActivationKeyPressed = useKeyPress(zoomActivationKeyCode);
useResizeHandler(zoomPane);
+7 -6
View File
@@ -1,8 +1,9 @@
import { useEffect } from 'react';
import shallow from 'zustand/shallow';
import { useStore, useStoreApi } from '../store';
import useKeyPress from './useKeyPress';
import { isNode, isEdge, getConnectedEdges } from '../utils/graph';
import { getConnectedEdges } from '../utils/graph';
import { KeyCode, ReactFlowState } from '../types';
interface HookParams {
@@ -21,17 +22,17 @@ const selector = (s: ReactFlowState) => ({
export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => {
const store = useStoreApi();
const { unsetNodesSelection, setMultiSelectionActive, resetSelectedElements, onNodesChange, onEdgesChange } =
useStore(selector);
useStore(selector, shallow);
const deleteKeyPressed = useKeyPress(deleteKeyCode);
const multiSelectionKeyPressed = useKeyPress(multiSelectionKeyCode);
useEffect(() => {
const { edges, selectedElements } = store.getState();
const { nodes, edges } = store.getState();
const selectedNodes = nodes.filter((n) => n.selected);
const selectedEdges = edges.filter((e) => e.selected);
if (deleteKeyPressed && selectedElements) {
const selectedNodes = selectedElements.filter(isNode);
const selectedEdges = selectedElements.filter(isEdge);
if (deleteKeyPressed && (selectedNodes || selectedEdges)) {
const connectedEdges = getConnectedEdges(selectedNodes, edges);
const nodeChanges = selectedNodes.map((n) => ({ id: n.id, delete: true }));
+2 -1
View File
@@ -1,5 +1,6 @@
import { useMemo } from 'react';
import { zoomIdentity } from 'd3-zoom';
import shallow from 'zustand/shallow';
import { useStoreApi, useStore } from '../store';
import { getRectOfNodes, pointToRendererPoint, getTransformForBounds } from '../utils/graph';
@@ -26,7 +27,7 @@ const selector = (s: ReactFlowState) => ({
const useZoomPanHelper = (): ZoomPanHelperFunctions => {
const store = useStoreApi();
const { d3Zoom, d3Selection } = useStore(selector);
const { d3Zoom, d3Selection } = useStore(selector, shallow);
const zoomPanHelperFunctions = useMemo<ZoomPanHelperFunctions>(() => {
if (d3Selection && d3Zoom) {
+90 -133
View File
@@ -1,6 +1,5 @@
import create from 'zustand';
import createContext from 'zustand/context';
import isEqual from 'fast-deep-equal';
import { clampPosition, getDimensions } from '../utils';
import {
@@ -26,8 +25,16 @@ import {
SnapGrid,
OnElementsChange,
} from '../types';
import { parseNode, parseEdge, isNode, getRectOfNodes, getNodesInside, getConnectedEdges } from '../utils/graph';
import { getSourceTargetNodes } from '../container/EdgeRenderer/utils';
import {
parseNode,
parseEdge,
isNode,
isEdge,
getRectOfNodes,
getNodesInside,
getConnectedEdges,
} from '../utils/graph';
import { extendEdgeWithSourceAndTarget } from '../container/EdgeRenderer/utils';
import { getHandleBounds } from '../components/Nodes/utils';
const { Provider, useStore, useStoreApi } = createContext<ReactFlowState>();
@@ -42,7 +49,6 @@ const createStore = () =>
onNodesChange: null,
onEdgesChange: null,
selectedElements: null,
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
d3Zoom: null,
@@ -110,18 +116,7 @@ const createStore = () =>
return parseNode(propNode, nodeExtent);
});
const updatedEdges = edges.map((edge) => {
const { sourceNode, targetNode } = getSourceTargetNodes(edge, nextNodes);
if (sourceNode) {
edge.sourceNode = sourceNode;
}
if (targetNode) {
edge.targetNode = targetNode;
}
return edge;
});
const updatedEdges = edges.map((edge) => extendEdgeWithSourceAndTarget(edge, nextNodes));
set({
nodes: nextNodes,
@@ -137,17 +132,7 @@ const createStore = () =>
if (storeEdge) {
return parseEdge(propEdge);
} else {
const parsedEdge = parseEdge(propEdge);
const { sourceNode, targetNode } = getSourceTargetNodes(parsedEdge, nodes);
if (sourceNode) {
parsedEdge.sourceNode = sourceNode;
}
if (targetNode) {
parsedEdge.targetNode = targetNode;
}
return parsedEdge;
return extendEdgeWithSourceAndTarget(parseEdge(propEdge), nodes);
}
});
@@ -190,23 +175,21 @@ const createStore = () =>
updateNodePosDiff: ({ id, diff, isDragging }: NodeDiffUpdate) => {
const { onNodesChange, nodes } = get();
if (onNodesChange && id && diff) {
const matchingNode = nodes.find((n) => n.id === id);
if (onNodesChange && diff) {
const matchingNodes = nodes.filter((n) => n.id === id || n.selected);
if (matchingNode) {
requestAnimationFrame(() =>
onNodesChange([
{
id,
change: {
position: {
x: matchingNode.position.x + diff.x,
y: matchingNode.position.y + diff.y,
isDragging,
},
if (matchingNodes?.length) {
onNodesChange(
matchingNodes.map((n) => ({
id: n.id,
change: {
position: {
x: n.position.x + diff.x,
y: n.position.y + diff.y,
isDragging,
},
},
])
}))
);
}
}
@@ -226,7 +209,7 @@ const createStore = () =>
});
},
updateUserSelection: (mousePos: XYPosition) => {
const { userSelectionRect, nodes, edges, transform, selectedElements } = get();
const { userSelectionRect, nodes, edges, transform, onNodesChange, onEdgesChange } = get();
const startX = userSelectionRect.startX ?? 0;
const startY = userSelectionRect.startY ?? 0;
@@ -241,23 +224,21 @@ const createStore = () =>
const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, false, true);
const selectedEdges = getConnectedEdges(selectedNodes, edges);
const nextSelectedElements = [...selectedNodes, ...selectedEdges];
const selectedElementsChanged = !isEqual(nextSelectedElements, selectedElements);
if (selectedElementsChanged) {
set({
selectedElements: nextSelectedElements.length > 0 ? nextSelectedElements : null,
userSelectionRect: nextUserSelectRect,
});
} else {
set({
userSelectionRect: nextUserSelectRect,
});
if (selectedNodes?.length) {
onNodesChange?.(selectedNodes.map((n) => ({ id: n.id, change: { selected: true } })));
}
if (selectedEdges?.length) {
onEdgesChange?.(selectedEdges.map((n) => ({ id: n.id, change: { selected: true } })));
}
set({
userSelectionRect: nextUserSelectRect,
});
},
unsetUserSelection: () => {
const { selectedElements, userSelectionRect } = get();
const selectedNodes = selectedElements?.filter((node) => isNode(node) && node.position) as Node[];
const { userSelectionRect, nodes } = get();
const selectedNodes = nodes.filter((node) => node.selected);
const stateUpdate = {
selectionActive: false,
@@ -265,7 +246,6 @@ const createStore = () =>
...userSelectionRect,
draw: false,
},
selectedElements: null,
selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 },
nodesSelectionActive: false,
};
@@ -278,28 +258,29 @@ const createStore = () =>
set(stateUpdate);
},
setSelectedElements: (elements: Elements) => {
const { selectedElements } = get();
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
const selectedElementsUpdated = !isEqual(selectedElementsArr, selectedElements);
set({
selectedElements: selectedElementsUpdated ? selectedElementsArr : selectedElements,
});
},
addSelectedElements: (elements: Elements) => {
const { multiSelectionActive, selectedElements } = get();
const { multiSelectionActive, onNodesChange, onEdgesChange, nodes, edges } = get();
const selectedElementsArr = Array.isArray(elements) ? elements : [elements];
let nextElements = selectedElementsArr;
let selectedNodes;
let selectedEdges;
if (multiSelectionActive) {
nextElements = selectedElements ? [...selectedElements, ...selectedElementsArr] : selectedElementsArr;
selectedNodes = selectedElementsArr.filter(isNode).map((node) => ({ id: node.id, change: { selected: true } }));
selectedEdges = selectedElementsArr.filter(isEdge).map((edge) => ({ id: edge.id, change: { selected: true } }));
} else {
selectedNodes = nodes.map((node) => ({
id: node.id,
change: { selected: selectedElementsArr.some((e) => e.id === node.id) },
}));
selectedEdges = edges.map((edge) => ({
id: edge.id,
change: { selected: selectedElementsArr.some((e) => e.id === edge.id) },
}));
}
const selectedElementsUpdated = !isEqual(nextElements, selectedElements);
set({ selectedElements: selectedElementsUpdated ? nextElements : selectedElements });
onNodesChange?.(selectedNodes);
onEdgesChange?.(selectedEdges);
},
initD3Zoom: ({ d3Zoom, d3Selection, d3ZoomHandler, transform }: InitD3ZoomPayload) => {
set({
@@ -334,77 +315,53 @@ const createStore = () =>
return {
...node,
position: clampPosition(node.position, nodeExtent),
__rf: {
...node.__rf,
},
};
}),
});
},
unsetNodesSelection: () => {
set({ nodesSelectionActive: false });
},
resetSelectedElements: () => {
set({ selectedElements: null });
const { nodes, edges, onNodesChange, onEdgesChange } = get();
const selectedNodes = nodes
.filter((n) => n.selected)
.map((n) => ({
id: n.id,
change: { selected: false },
}));
const selectedEdges = edges
.filter((e) => e.selected)
.map((e) => ({
id: e.id,
change: { selected: false },
}));
onNodesChange?.(selectedNodes);
onEdgesChange?.(selectedEdges);
},
updateTransform: (transform: Transform) => {
set({ transform });
},
updateSize: (size: Dimensions) => {
set({ width: size.width || 500, height: size.height || 500 });
},
setOnConnect: (onConnect: OnConnectFunc) => {
set({ onConnect });
},
setOnConnectStart: (onConnectStart: OnConnectStartFunc) => {
set({ onConnectStart });
},
setOnConnectStop: (onConnectStop: OnConnectStopFunc) => {
set({ onConnectStop });
},
setOnConnectEnd: (onConnectEnd: OnConnectEndFunc) => {
set({ onConnectEnd });
},
setConnectionPosition: (connectionPosition: XYPosition) => {
set({ connectionPosition });
},
setConnectionNodeId: (params: SetConnectionId) => {
set({ ...params });
},
setSnapToGrid: (snapToGrid: boolean) => {
set({ snapToGrid });
},
setSnapGrid: (snapGrid: SnapGrid) => {
set({ snapGrid });
},
setInteractive: (isInteractive: boolean) => {
unsetNodesSelection: () => set({ nodesSelectionActive: false }),
updateTransform: (transform: Transform) => set({ transform }),
updateSize: (size: Dimensions) => set({ width: size.width || 500, height: size.height || 500 }),
setOnConnect: (onConnect: OnConnectFunc) => set({ onConnect }),
setOnConnectStart: (onConnectStart: OnConnectStartFunc) => set({ onConnectStart }),
setOnConnectStop: (onConnectStop: OnConnectStopFunc) => set({ onConnectStop }),
setOnConnectEnd: (onConnectEnd: OnConnectEndFunc) => set({ onConnectEnd }),
setConnectionPosition: (connectionPosition: XYPosition) => set({ connectionPosition }),
setConnectionNodeId: (params: SetConnectionId) => set({ ...params }),
setSnapToGrid: (snapToGrid: boolean) => set({ snapToGrid }),
setSnapGrid: (snapGrid: SnapGrid) => set({ snapGrid }),
setInteractive: (isInteractive: boolean) =>
set({
nodesDraggable: isInteractive,
nodesConnectable: isInteractive,
elementsSelectable: isInteractive,
});
},
setNodesDraggable: (nodesDraggable: boolean) => {
set({ nodesDraggable });
},
setNodesConnectable: (nodesConnectable: boolean) => {
set({ nodesConnectable });
},
setElementsSelectable: (elementsSelectable: boolean) => {
set({ elementsSelectable });
},
setMultiSelectionActive: (multiSelectionActive: boolean) => {
set({ multiSelectionActive });
},
setConnectionMode: (connectionMode: ConnectionMode) => {
set({ connectionMode });
},
setOnNodesChange: (onNodesChange: OnElementsChange) => {
set({ onNodesChange });
},
setOnEdgesChange: (onEdgesChange: OnElementsChange) => {
set({ onEdgesChange });
},
}),
setNodesDraggable: (nodesDraggable: boolean) => set({ nodesDraggable }),
setNodesConnectable: (nodesConnectable: boolean) => set({ nodesConnectable }),
setElementsSelectable: (elementsSelectable: boolean) => set({ elementsSelectable }),
setMultiSelectionActive: (multiSelectionActive: boolean) => set({ multiSelectionActive }),
setConnectionMode: (connectionMode: ConnectionMode) => set({ connectionMode }),
setOnNodesChange: (onNodesChange: OnElementsChange) => set({ onNodesChange }),
setOnEdgesChange: (onEdgesChange: OnElementsChange) => set({ onEdgesChange }),
}));
export { Provider, useStore, createStore, useStoreApi };
+2 -2
View File
@@ -60,6 +60,7 @@ export interface Node<T = any> {
width?: number | null;
height?: number | null;
handleBounds?: any;
selected?: boolean;
}
export enum ArrowHeadType {
@@ -88,6 +89,7 @@ export interface Edge<T = any> {
className?: string;
sourceNode?: Node;
targetNode?: Node;
selected?: boolean;
}
export enum BackgroundVariant {
@@ -418,7 +420,6 @@ export interface ReactFlowState {
transform: Transform;
nodes: Node[];
edges: Edge[];
selectedElements: Elements | null;
selectedNodesBbox: Rect;
onNodesChange: OnElementsChange | null;
onEdgesChange: OnElementsChange | null;
@@ -462,7 +463,6 @@ export interface ReactFlowState {
unsetUserSelection: () => void;
unsetNodesSelection: () => void;
resetSelectedElements: () => void;
setSelectedElements: (elements: Elements) => void;
addSelectedElements: (elements: Elements) => void;
updateTransform: (transform: Transform) => void;
updateSize: (size: Dimensions) => void;