From 330eefe6953f8e8a2fc5fb24cdf619c9af662e1c Mon Sep 17 00:00:00 2001 From: moklick Date: Fri, 26 Nov 2021 18:42:52 +0100 Subject: [PATCH] refactor(selection): only fire selection changes that changed --- example/src/UpdatableEdge/index.tsx | 1 + src/components/Edges/wrapEdge.tsx | 6 +- src/components/Nodes/wrapNode.tsx | 14 +- src/components/NodesSelection/index.tsx | 31 ++-- src/components/UserSelection/index.tsx | 198 +++++++++++++++--------- src/container/FlowRenderer/index.tsx | 6 +- src/hooks/useGlobalKeyHandler.ts | 6 +- src/store/index.ts | 97 ++---------- src/store/utils.ts | 8 +- src/types/general.ts | 8 +- src/utils/changes.ts | 20 +++ 11 files changed, 191 insertions(+), 204 deletions(-) diff --git a/example/src/UpdatableEdge/index.tsx b/example/src/UpdatableEdge/index.tsx index 0cdd5ab7..2b814472 100644 --- a/example/src/UpdatableEdge/index.tsx +++ b/example/src/UpdatableEdge/index.tsx @@ -65,6 +65,7 @@ const UpdatableEdge = () => { const onConnect = (connection: Connection) => setEdges((els) => addEdge(connection, els)); const onNodesChange = useCallback((changes: NodeChange[]) => { + console.log(changes); setNodes((ns) => applyNodeChanges(changes, ns)); }, []); diff --git a/src/components/Edges/wrapEdge.tsx b/src/components/Edges/wrapEdge.tsx index 9a63e4fd..1697ef64 100644 --- a/src/components/Edges/wrapEdge.tsx +++ b/src/components/Edges/wrapEdge.tsx @@ -11,7 +11,7 @@ import { getMarkerId } from '../../utils/graph'; const selector = (s: ReactFlowState) => ({ addSelectedEdges: s.addSelectedEdges, setConnectionNodeId: s.setConnectionNodeId, - unsetNodesSelection: s.unsetNodesSelection, + setNodesSelectionActive: s.setNodesSelectionActive, setPosition: s.setConnectionPosition, connectionMode: s.connectionMode, }); @@ -57,7 +57,7 @@ export default (EdgeComponent: ComponentType) => { markerStart, }: WrapEdgeProps): JSX.Element | null => { const store = useStoreApi(); - const { addSelectedEdges, setConnectionNodeId, unsetNodesSelection, setPosition, connectionMode } = useStore( + const { addSelectedEdges, setConnectionNodeId, setNodesSelectionActive, setPosition, connectionMode } = useStore( selector, shallow ); @@ -99,7 +99,7 @@ export default (EdgeComponent: ComponentType) => { const onEdgeClick = useCallback( (event: React.MouseEvent): void => { if (elementsSelectable) { - unsetNodesSelection(); + setNodesSelectionActive(false); addSelectedEdges([edgeElement.id]); } diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index 4ad23b1b..75c91f3f 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -9,7 +9,7 @@ import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types'; const selector = (s: ReactFlowState) => ({ addSelectedNodes: s.addSelectedNodes, - unsetNodesSelection: s.unsetNodesSelection, + setNodesSelectionActive: s.setNodesSelectionActive, updateNodePosition: s.updateNodePosition, updateNodeDimensions: s.updateNodeDimensions, unselectNodesAndEdges: s.unselectNodesAndEdges, @@ -56,7 +56,7 @@ export default (NodeComponent: ComponentType) => { const { addSelectedNodes, unselectNodesAndEdges, - unsetNodesSelection, + setNodesSelectionActive, updateNodePosition, // updateNodeDimensions, } = useStore(selector, shallow); @@ -127,7 +127,7 @@ export default (NodeComponent: ComponentType) => { (event: MouseEvent) => { if (!isDraggable) { if (isSelectable) { - unsetNodesSelection(); + setNodesSelectionActive(false); if (!selected) { addSelectedNodes([node.id]); @@ -145,14 +145,14 @@ export default (NodeComponent: ComponentType) => { onNodeDragStart?.(event as MouseEvent, node); if (selectNodesOnDrag && isSelectable) { - unsetNodesSelection(); + setNodesSelectionActive(false); if (!selected) { addSelectedNodes([node.id]); } } else if (!selectNodesOnDrag && !selected && isSelectable) { unselectNodesAndEdges(); - unsetNodesSelection(); + setNodesSelectionActive(false); } }, [node, selected, selectNodesOnDrag, isSelectable, onNodeDragStart] @@ -163,9 +163,7 @@ export default (NodeComponent: ComponentType) => { node.position.x += draggableData.deltaX; node.position.y += draggableData.deltaY; - if (onNodeDrag) { - onNodeDrag(event as MouseEvent, node); - } + onNodeDrag?.(event as MouseEvent, node); updateNodePosition({ id, dragging: true, diff: { x: draggableData.deltaX, y: draggableData.deltaY } }); }, diff --git a/src/components/NodesSelection/index.tsx b/src/components/NodesSelection/index.tsx index b75c5ea6..188045ee 100644 --- a/src/components/NodesSelection/index.tsx +++ b/src/components/NodesSelection/index.tsx @@ -3,12 +3,13 @@ * made a selectio with on or several nodes */ -import React, { useMemo, useCallback, useRef, MouseEvent } from 'react'; -import ReactDraggable, { DraggableData } from 'react-draggable'; +import React, { memo, useMemo, useCallback, useRef, MouseEvent } from 'react'; +import { DraggableCore, DraggableData } from 'react-draggable'; import cc from 'classcat'; import { useStore } from '../../store'; import { Node, ReactFlowState } from '../../types'; +import { getRectOfNodes } from '../..'; export interface NodesSelectionProps { onSelectionDragStart?: (event: MouseEvent, nodes: Node[]) => void; @@ -21,7 +22,7 @@ export interface NodesSelectionProps { const selector = (s: ReactFlowState) => ({ transform: s.transform, selectedNodesBbox: s.selectedNodesBbox, - selectionActive: s.selectionActive, + userSelectionActive: s.userSelectionActive, selectedNodes: Array.from(s.nodeInternals) .filter(([_, n]) => n.selected) .map(([_, n]) => n), @@ -30,14 +31,14 @@ const selector = (s: ReactFlowState) => ({ updateNodePosition: s.updateNodePosition, }); -export default ({ +function NodesSelection({ onSelectionDragStart, onSelectionDrag, onSelectionDragStop, onSelectionContextMenu, noPanClassName, -}: NodesSelectionProps) => { - const { transform, selectedNodesBbox, selectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } = +}: NodesSelectionProps) { + const { transform, userSelectionActive, selectedNodes, snapToGrid, snapGrid, updateNodePosition } = useStore(selector); const [tX, tY, tScale] = transform; const nodeRef = useRef(null); @@ -51,6 +52,8 @@ export default ({ [tX, tY, tScale] ); + const selectedNodesBbox = useMemo(() => getRectOfNodes(selectedNodes), [selectedNodes]); + const innerStyle = useMemo( () => ({ width: selectedNodesBbox.width, @@ -70,10 +73,6 @@ export default ({ const onDrag = useCallback( (event: MouseEvent, data: DraggableData) => { - if (onSelectionDrag) { - onSelectionDrag(event, selectedNodes); - } - updateNodePosition({ diff: { x: data.deltaX, @@ -81,6 +80,8 @@ export default ({ }, dragging: true, }); + + onSelectionDrag?.(event, selectedNodes); }, [onSelectionDrag, selectedNodes, updateNodePosition] ); @@ -103,13 +104,13 @@ export default ({ [onSelectionContextMenu, selectedNodes] ); - if (!selectedNodes || selectionActive) { + if (!selectedNodes?.length || userSelectionActive) { return null; } return (
- onStart(event as MouseEvent)} @@ -124,7 +125,9 @@ export default ({ onContextMenu={onContextMenu} style={innerStyle} /> - +
); -}; +} + +export default memo(NodesSelection); diff --git a/src/components/UserSelection/index.tsx b/src/components/UserSelection/index.tsx index 5ffbef7e..faca068b 100644 --- a/src/components/UserSelection/index.tsx +++ b/src/components/UserSelection/index.tsx @@ -2,104 +2,139 @@ * The user selection rectangle gets displayed when a user drags the mouse while pressing shift */ -import React, { memo } from 'react'; +import React, { memo, useState, useRef, useCallback } from 'react'; +import shallow from 'zustand/shallow'; -import { useStore } from '../../store'; -import { XYPosition, ReactFlowState } from '../../types'; +import { useStore, useStoreApi } from '../../store'; +import { getSelectionChanges } from '../../utils/changes'; +import { XYPosition, ReactFlowState, SelectionRect, NodeChange, EdgeChange } from '../../types'; +import { getConnectedEdges, getNodesInside } from '../../utils/graph'; type UserSelectionProps = { selectionKeyPressed: boolean; }; -function getMousePosition(event: React.MouseEvent): XYPosition | void { - const reactFlowNode = (event.target as Element).closest('.react-flow'); - if (!reactFlowNode) { - return; - } - - const containerBounds = reactFlowNode.getBoundingClientRect(); - +function getMousePosition(event: React.MouseEvent, containerBounds: DOMRect): XYPosition { return { x: event.clientX - containerBounds.left, y: event.clientY - containerBounds.top, }; } -const userSelectionRectSelector = (state: ReactFlowState) => state.userSelectionRect; - -const SelectionRect = () => { - const userSelectionRect = useStore(userSelectionRectSelector); - - if (!userSelectionRect.draw) { - return null; - } - - return ( -
- ); -}; - const selector = (s: ReactFlowState) => ({ - selectionActive: s.selectionActive, + userSelectionActive: s.userSelectionActive, elementsSelectable: s.elementsSelectable, - setUserSelection: s.setUserSelection, - updateUserSelection: s.updateUserSelection, - unsetUserSelection: s.unsetUserSelection, - unsetNodesSelection: s.unsetNodesSelection, + setUserSelectionActive: s.setUserSelectionActive, + setNodesSelectionActive: s.setNodesSelectionActive, }); -export default memo(({ selectionKeyPressed }: UserSelectionProps) => { - const { - selectionActive, - elementsSelectable, - setUserSelection, - updateUserSelection, - unsetUserSelection, - unsetNodesSelection, - } = useStore(selector); +const initialRect: SelectionRect = { + startX: 0, + startY: 0, + x: 0, + y: 0, + width: 0, + height: 0, + draw: false, +}; - const renderUserSelectionPane = selectionActive || selectionKeyPressed; +export default memo(({ selectionKeyPressed }: UserSelectionProps) => { + const store = useStoreApi(); + const prevSelectedNodesCount = useRef(0); + const prevSelectedEdgesCount = useRef(0); + const containerBounds = useRef(); + const [userSelectionRect, setUserSelectionRect] = useState(initialRect); + const { userSelectionActive, elementsSelectable, setUserSelectionActive, setNodesSelectionActive } = useStore( + selector, + shallow + ); + + const renderUserSelectionPane = userSelectionActive || selectionKeyPressed; + + const resetUserSelection = useCallback(() => { + setUserSelectionRect(initialRect); + setUserSelectionActive(false); + + prevSelectedNodesCount.current = 0; + prevSelectedEdgesCount.current = 0; + }, []); + + const onMouseDown = useCallback((event: React.MouseEvent): void => { + const reactFlowNode = (event.target as Element).closest('.react-flow')!; + containerBounds.current = reactFlowNode.getBoundingClientRect(); + + const mousePos = getMousePosition(event, containerBounds.current!); + + setUserSelectionRect({ + width: 0, + height: 0, + startX: mousePos.x, + startY: mousePos.y, + x: mousePos.x, + y: mousePos.y, + draw: true, + }); + + setUserSelectionActive(true); + setNodesSelectionActive(false); + }, []); + + const onMouseMove = (event: React.MouseEvent): void => { + if (!selectionKeyPressed || !userSelectionRect.draw || !containerBounds.current) { + return; + } + + const mousePos = getMousePosition(event, containerBounds.current!); + const startX = userSelectionRect.startX ?? 0; + const startY = userSelectionRect.startY ?? 0; + + const nextUserSelectRect = { + ...userSelectionRect, + x: mousePos.x < startX ? mousePos.x : userSelectionRect.x, + y: mousePos.y < startY ? mousePos.y : userSelectionRect.y, + width: Math.abs(mousePos.x - startX), + height: Math.abs(mousePos.y - startY), + }; + + const { nodeInternals, edges, transform, onNodesChange, onEdgesChange } = store.getState(); + const nodes = Array.from(nodeInternals).map(([_, node]) => node); + const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, false, true); + const selectedEdgeIds = getConnectedEdges(selectedNodes, edges).map((e) => e.id); + const selectedNodeIds = selectedNodes.map((n) => n.id); + + if (prevSelectedNodesCount.current !== selectedNodeIds.length) { + prevSelectedNodesCount.current = selectedNodeIds.length; + const changes = getSelectionChanges(nodes, selectedNodeIds) as NodeChange[]; + if (changes.length) { + onNodesChange?.(changes); + } + } + + if (prevSelectedEdgesCount.current !== selectedEdgeIds.length) { + prevSelectedEdgesCount.current = selectedEdgeIds.length; + const changes = getSelectionChanges(edges, selectedEdgeIds) as EdgeChange[]; + if (changes.length) { + onEdgesChange?.(changes); + } + } + + setUserSelectionRect(nextUserSelectRect); + }; + + const onMouseUp = useCallback(() => { + setNodesSelectionActive(prevSelectedNodesCount.current > 0); + resetUserSelection(); + }, []); + + const onMouseLeave = useCallback(() => { + setNodesSelectionActive(false); + resetUserSelection(); + }, []); if (!elementsSelectable || !renderUserSelectionPane) { return null; } - const onMouseDown = (event: React.MouseEvent): void => { - const mousePos = getMousePosition(event); - if (!mousePos) { - return; - } - - setUserSelection(mousePos); - }; - - const onMouseMove = (event: React.MouseEvent): void => { - if (!selectionKeyPressed || !selectionActive) { - return; - } - const mousePos = getMousePosition(event); - - if (!mousePos) { - return; - } - - updateUserSelection(mousePos); - }; - - const onMouseUp = () => unsetUserSelection(); - - const onMouseLeave = () => { - unsetUserSelection(); - unsetNodesSelection(); - }; - return (
{ onMouseUp={onMouseUp} onMouseLeave={onMouseLeave} > - + {userSelectionRect.draw && ( +
+ )}
); }); diff --git a/src/container/FlowRenderer/index.tsx b/src/container/FlowRenderer/index.tsx index 458e9c59..02f10bd7 100644 --- a/src/container/FlowRenderer/index.tsx +++ b/src/container/FlowRenderer/index.tsx @@ -27,7 +27,7 @@ interface FlowRendererProps } const selector = (s: ReactFlowState) => ({ - unsetNodesSelection: s.unsetNodesSelection, + setNodesSelectionActive: s.setNodesSelectionActive, resetSelectedElements: s.resetSelectedElements, nodesSelectionActive: s.nodesSelectionActive, }); @@ -62,7 +62,7 @@ const FlowRenderer = ({ noWheelClassName, noPanClassName, }: FlowRendererProps) => { - const { unsetNodesSelection, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow); + const { setNodesSelectionActive, resetSelectedElements, nodesSelectionActive } = useStore(selector, shallow); const selectionKeyPressed = useKeyPress(selectionKeyCode); @@ -71,7 +71,7 @@ const FlowRenderer = ({ const onClick = useCallback( (event: MouseEvent) => { onPaneClick?.(event); - unsetNodesSelection(); + setNodesSelectionActive(false); resetSelectedElements(); }, [onPaneClick] diff --git a/src/hooks/useGlobalKeyHandler.ts b/src/hooks/useGlobalKeyHandler.ts index ead31a62..27f283e7 100644 --- a/src/hooks/useGlobalKeyHandler.ts +++ b/src/hooks/useGlobalKeyHandler.ts @@ -12,7 +12,7 @@ interface HookParams { } const selector = (s: ReactFlowState) => ({ - unsetNodesSelection: s.unsetNodesSelection, + setNodesSelectionActive: s.setNodesSelectionActive, setMultiSelectionActive: s.setMultiSelectionActive, resetSelectedElements: s.resetSelectedElements, onNodesChange: s.onNodesChange, @@ -21,7 +21,7 @@ const selector = (s: ReactFlowState) => ({ export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => { const store = useStoreApi(); - const { unsetNodesSelection, setMultiSelectionActive, resetSelectedElements, onNodesChange, onEdgesChange } = + const { setNodesSelectionActive, setMultiSelectionActive, resetSelectedElements, onNodesChange, onEdgesChange } = useStore(selector, shallow); const deleteKeyPressed = useKeyPress(deleteKeyCode); @@ -46,7 +46,7 @@ export default ({ deleteKeyCode, multiSelectionKeyCode }: HookParams): void => { onNodesChange?.(nodeChanges); onEdgesChange?.(edgeChanges); - unsetNodesSelection(); + setNodesSelectionActive(false); resetSelectedElements(); } }, [deleteKeyPressed, onNodesChange, onEdgesChange]); diff --git a/src/store/index.ts b/src/store/index.ts index 5448af68..04b1ea31 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -27,9 +27,9 @@ import { XYPosition, ReactFlowStore, } from '../types'; -import { getRectOfNodes, getNodesInside, getConnectedEdges } from '../utils/graph'; import { getHandleBounds } from '../components/Nodes/utils'; -import { createNodeInternals, createNodeOrEdgeSelectionChange } from './utils'; +import { createSelectionChange, getSelectionChanges } from '../utils/changes'; +import { createNodeInternals } from './utils'; const { Provider, useStore, useStoreApi } = createContext(); @@ -55,7 +55,7 @@ const initialState: ReactFlowStore = { translateExtent: infiniteExtent, nodeExtent: infiniteExtent, nodesSelectionActive: false, - selectionActive: false, + userSelectionActive: false, userSelectionRect: { startX: 0, startY: 0, @@ -181,74 +181,6 @@ const createStore = () => } } }, - setUserSelection: (mousePos: XYPosition) => { - set({ - selectionActive: true, - userSelectionRect: { - width: 0, - height: 0, - startX: mousePos.x, - startY: mousePos.y, - x: mousePos.x, - y: mousePos.y, - draw: true, - }, - }); - }, - updateUserSelection: (mousePos: XYPosition) => { - const { userSelectionRect, nodeInternals, edges, transform, onNodesChange, onEdgesChange } = get(); - const startX = userSelectionRect.startX ?? 0; - const startY = userSelectionRect.startY ?? 0; - - const nextUserSelectRect = { - ...userSelectionRect, - x: mousePos.x < startX ? mousePos.x : userSelectionRect.x, - y: mousePos.y < startY ? mousePos.y : userSelectionRect.y, - width: Math.abs(mousePos.x - startX), - height: Math.abs(mousePos.y - startY), - }; - - // @TODO: work with nodeInternals instead of converting it to an array - const nodes = Array.from(nodeInternals).map(([_, node]) => node); - const selectedNodes = getNodesInside(nodes, nextUserSelectRect, transform, false, true); - const selectedEdgeIds = getConnectedEdges(selectedNodes, edges).map((e) => e.id); - const selectedNodeIds = selectedNodes.map((n) => n.id); - - onNodesChange?.( - nodes.map((n) => createNodeOrEdgeSelectionChange(selectedNodeIds.includes(n.id))(n)) as NodeChange[] - ); - onEdgesChange?.( - edges.map((e) => createNodeOrEdgeSelectionChange(selectedEdgeIds.includes(e.id))(e)) as EdgeChange[] - ); - - set({ - userSelectionRect: nextUserSelectRect, - }); - }, - unsetUserSelection: () => { - const { userSelectionRect, nodeInternals } = get(); - // @TODO: work with nodeInternals instead of converting it to an array - const nodes = Array.from(nodeInternals).map(([_, node]) => node); - const selectedNodes = nodes.filter((node) => node.selected); - - const stateUpdate = { - selectionActive: false, - userSelectionRect: { - ...userSelectionRect, - draw: false, - }, - selectedNodesBbox: { x: 0, y: 0, width: 0, height: 0 }, - nodesSelectionActive: false, - }; - - if (selectedNodes && selectedNodes.length > 0) { - const selectedNodesBbox = getRectOfNodes(selectedNodes); - stateUpdate.selectedNodesBbox = selectedNodesBbox; - stateUpdate.nodesSelectionActive = true; - } - - set(stateUpdate); - }, addSelectedNodes: (selectedNodeIds: string[]) => { const { multiSelectionActive, onNodesChange, nodeInternals } = get(); // @TODO: work with nodeInternals instead of converting it to an array @@ -256,11 +188,9 @@ const createStore = () => let changedNodes; if (multiSelectionActive) { - changedNodes = selectedNodeIds.map(createNodeOrEdgeSelectionChange(true)); + changedNodes = selectedNodeIds.map((nodeId) => createSelectionChange(nodeId, true)); } else { - changedNodes = nodes.map((node) => - createNodeOrEdgeSelectionChange(selectedNodeIds.some((nodeId) => nodeId === node.id))(node.id) - ); + changedNodes = getSelectionChanges(nodes, selectedNodeIds); } if (changedNodes.length) { @@ -273,11 +203,9 @@ const createStore = () => let changedEdges; if (multiSelectionActive) { - changedEdges = selectedEdgeIds.map(createNodeOrEdgeSelectionChange(true)); + changedEdges = selectedEdgeIds.map((edgeId) => createSelectionChange(edgeId, true)); } else { - changedEdges = edges.map((edge) => - createNodeOrEdgeSelectionChange(selectedEdgeIds.some((edgeId) => edgeId === edge.id))(edge.id) - ); + changedEdges = getSelectionChanges(edges, selectedEdgeIds); } if (changedEdges.length) { @@ -291,9 +219,9 @@ const createStore = () => const nodesToUnselect = nodes.map((n) => { n.selected = false; - return createNodeOrEdgeSelectionChange(false)(n); + return createSelectionChange(n.id, false); }) as NodeChange[]; - const edgesToUnselect = edges.map(createNodeOrEdgeSelectionChange(false)) as EdgeChange[]; + const edgesToUnselect = edges.map((edge) => createSelectionChange(edge.id, false)) as EdgeChange[]; if (nodesToUnselect.length) { onNodesChange?.(nodesToUnselect); @@ -332,8 +260,8 @@ const createStore = () => const { nodeInternals, edges, onNodesChange, onEdgesChange } = get(); // @TODO: work with nodeInternals instead of converting it to an array const nodes = Array.from(nodeInternals).map(([_, node]) => node); - const nodesToUnselect = nodes.filter((e) => e.selected).map(createNodeOrEdgeSelectionChange(false)); - const edgesToUnselect = edges.filter((e) => e.selected).map(createNodeOrEdgeSelectionChange(false)); + const nodesToUnselect = nodes.filter((e) => e.selected).map((n) => createSelectionChange(n.id, false)); + const edgesToUnselect = edges.filter((e) => e.selected).map((e) => createSelectionChange(e.id, false)); if (nodesToUnselect.length) { onNodesChange?.(nodesToUnselect as NodeChange[]); @@ -354,7 +282,8 @@ const createStore = () => nodeInternals: new Map(nodeInternals), }); }, - unsetNodesSelection: () => set({ nodesSelectionActive: false }), + setUserSelectionActive: (userSelectionActive: boolean) => set({ userSelectionActive }), + setNodesSelectionActive: (nodesSelectionActive: boolean) => set({ nodesSelectionActive }), updateTransform: (transform: Transform) => set({ transform }), updateSize: (size: Dimensions) => set({ width: size.width || 500, height: size.height || 500 }), setOnConnect: (onConnect: OnConnect) => set({ onConnect }), diff --git a/src/store/utils.ts b/src/store/utils.ts index 700aeefa..6d947e4d 100644 --- a/src/store/utils.ts +++ b/src/store/utils.ts @@ -1,4 +1,4 @@ -import { Node, Edge, NodeInternals, NodeInternalsItem, XYZPosition } from '../types'; +import { Node, NodeInternals, NodeInternalsItem, XYZPosition } from '../types'; import { isNumeric } from '../utils'; type ParentNodes = Record; @@ -104,9 +104,3 @@ export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals) return nextNodeInternals; } - -export const createNodeOrEdgeSelectionChange = (selected: boolean) => (item: Node | Edge | string) => ({ - id: typeof item === 'string' ? item : item.id, - type: 'select', - selected, -}); diff --git a/src/types/general.ts b/src/types/general.ts index 5aea0b75..6500882c 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -149,7 +149,7 @@ export type ReactFlowStore = { nodeExtent: CoordinateExtent; nodesSelectionActive: boolean; - selectionActive: boolean; + userSelectionActive: boolean; userSelectionRect: SelectionRect; @@ -176,10 +176,8 @@ export type ReactFlowActions = { setEdges: (edges: Edge[]) => void; updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void; updateNodePosition: (update: NodeDiffUpdate) => void; - setUserSelection: (mousePos: XYPosition) => void; - updateUserSelection: (mousePos: XYPosition) => void; - unsetUserSelection: () => void; - unsetNodesSelection: () => void; + setNodesSelectionActive: (isActive: boolean) => void; + setUserSelectionActive: (isActive: boolean) => void; resetSelectedElements: () => void; unselectNodesAndEdges: () => void; addSelectedNodes: (nodeIds: string[]) => void; diff --git a/src/utils/changes.ts b/src/utils/changes.ts index 64743226..520f3642 100644 --- a/src/utils/changes.ts +++ b/src/utils/changes.ts @@ -49,3 +49,23 @@ export function applyNodeChanges(changes: NodeChange[], nodes: Node[]): Node[] { export function applyEdgeChanges(changes: EdgeChange[], edges: Edge[]): Edge[] { return applyChanges(changes, edges) as Edge[]; } + +export const createSelectionChange = (id: string, selected: boolean) => ({ + id, + type: 'select', + selected, +}); + +export function getSelectionChanges(items: any[], selectedIds: string[]) { + return items.reduce((res, item) => { + const willBeSelected = selectedIds.includes(item.id); + + if (!item.selected && willBeSelected) { + res.push(createSelectionChange(item.id, true)); + } else if (item.selected && !willBeSelected) { + res.push(createSelectionChange(item.id, false)); + } + + return res; + }, []); +}