diff --git a/example/src/UpdateNode/index.tsx b/example/src/UpdateNode/index.tsx index e38babad..4a460dcb 100644 --- a/example/src/UpdateNode/index.tsx +++ b/example/src/UpdateNode/index.tsx @@ -1,5 +1,5 @@ -import { useEffect, useState, useCallback } from 'react'; -import ReactFlow, { Node, Edge, applyNodeChanges, NodeChange } from 'react-flow-renderer'; +import { useEffect, useState } from 'react'; +import ReactFlow, { Node, Edge, useNodesState, useEdgesState } from 'react-flow-renderer'; import './updatenode.css'; @@ -11,8 +11,8 @@ const initialNodes: Node[] = [ const initialEdges: Edge[] = [{ id: 'e1-2', source: '1', target: '2' }]; const UpdateNode = () => { - const [nodes, setNodes] = useState(initialNodes); - const [edges, setEdges] = useState(initialEdges); + const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes); + const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges); const [nodeName, setNodeName] = useState('Node 1'); const [nodeBg, setNodeBg] = useState('#eee'); @@ -60,12 +60,16 @@ const UpdateNode = () => { ); }, [nodeHidden]); - const onNodesChange = useCallback((changes: NodeChange[]) => { - setNodes((ns) => applyNodeChanges(changes, ns)); - }, []); - return ( - +
setNodeName(evt.target.value)} /> diff --git a/src/additional-components/index.ts b/src/additional-components/index.ts index 3c96dfb6..3f030a79 100644 --- a/src/additional-components/index.ts +++ b/src/additional-components/index.ts @@ -1,5 +1,5 @@ // These components are not used by React Flow directly -// but the user can add them as children of a React Flow component +// They can be added as children of a React Flow component export { default as MiniMap } from './MiniMap'; export { default as Controls, ControlButton } from './Controls'; diff --git a/src/components/Edges/wrapEdge.tsx b/src/components/Edges/wrapEdge.tsx index f249863c..9a63e4fd 100644 --- a/src/components/Edges/wrapEdge.tsx +++ b/src/components/Edges/wrapEdge.tsx @@ -9,7 +9,7 @@ import { EdgeAnchor } from './EdgeAnchor'; import { getMarkerId } from '../../utils/graph'; const selector = (s: ReactFlowState) => ({ - addSelectedElements: s.addSelectedElements, + addSelectedEdges: s.addSelectedEdges, setConnectionNodeId: s.setConnectionNodeId, unsetNodesSelection: s.unsetNodesSelection, setPosition: s.setConnectionPosition, @@ -57,7 +57,7 @@ export default (EdgeComponent: ComponentType) => { markerStart, }: WrapEdgeProps): JSX.Element | null => { const store = useStoreApi(); - const { addSelectedElements, setConnectionNodeId, unsetNodesSelection, setPosition, connectionMode } = useStore( + const { addSelectedEdges, setConnectionNodeId, unsetNodesSelection, setPosition, connectionMode } = useStore( selector, shallow ); @@ -100,7 +100,7 @@ export default (EdgeComponent: ComponentType) => { (event: React.MouseEvent): void => { if (elementsSelectable) { unsetNodesSelection(); - addSelectedElements([edgeElement]); + addSelectedEdges([edgeElement.id]); } onClick?.(event, edgeElement); diff --git a/src/components/Nodes/wrapNode.tsx b/src/components/Nodes/wrapNode.tsx index dc3a495a..4ad23b1b 100644 --- a/src/components/Nodes/wrapNode.tsx +++ b/src/components/Nodes/wrapNode.tsx @@ -8,7 +8,7 @@ import { Provider } from '../../contexts/NodeIdContext'; import { NodeProps, WrapNodeProps, ReactFlowState } from '../../types'; const selector = (s: ReactFlowState) => ({ - addSelectedElements: s.addSelectedElements, + addSelectedNodes: s.addSelectedNodes, unsetNodesSelection: s.unsetNodesSelection, updateNodePosition: s.updateNodePosition, updateNodeDimensions: s.updateNodeDimensions, @@ -54,7 +54,7 @@ export default (NodeComponent: ComponentType) => { noDragClassName, }: WrapNodeProps) => { const { - addSelectedElements, + addSelectedNodes, unselectNodesAndEdges, unsetNodesSelection, updateNodePosition, @@ -130,7 +130,7 @@ export default (NodeComponent: ComponentType) => { unsetNodesSelection(); if (!selected) { - addSelectedElements([node]); + addSelectedNodes([node.id]); } } @@ -148,7 +148,7 @@ export default (NodeComponent: ComponentType) => { unsetNodesSelection(); if (!selected) { - addSelectedElements([node]); + addSelectedNodes([node.id]); } } else if (!selectNodesOnDrag && !selected && isSelectable) { unselectNodesAndEdges(); @@ -178,7 +178,7 @@ export default (NodeComponent: ComponentType) => { // Because of that we set dragging to true inside the onDrag handler and handle the click here if (!dragging) { if (isSelectable && !selectNodesOnDrag && !selected) { - addSelectedElements([node]); + addSelectedNodes([node.id]); } onClick?.(event as MouseEvent, node); diff --git a/src/container/EdgeRenderer/index.tsx b/src/container/EdgeRenderer/index.tsx index 216c53d5..536f5005 100644 --- a/src/container/EdgeRenderer/index.tsx +++ b/src/container/EdgeRenderer/index.tsx @@ -88,13 +88,7 @@ const EdgeRenderer = (props: EdgeRendererProps) => { const [sourceNodeRect, sourceHandleBounds, sourceIsValid] = getNodeData(nodeInternals, edge.source); const [targetNodeRect, targetHandleBounds, targetIsValid] = getNodeData(nodeInternals, edge.target); - if (!sourceIsValid) { - console.warn(`couldn't create edge for source id: ${edge.source}; edge id: ${edge.id}`); - return null; - } - - if (!targetIsValid) { - console.warn(`couldn't create edge for target id: ${edge.target}; edge id: ${edge.id}`); + if (!sourceIsValid || !targetIsValid) { return null; } diff --git a/src/hooks/useResizeHandler.ts b/src/hooks/useResizeHandler.ts index fc2c830b..12450359 100644 --- a/src/hooks/useResizeHandler.ts +++ b/src/hooks/useResizeHandler.ts @@ -6,7 +6,7 @@ import { ReactFlowState } from '../types'; const updateSizeSelector = (state: ReactFlowState) => state.updateSize; -function useResizeHandler(rendererNode: MutableRefObject) { +function useResizeHandler(rendererNode: MutableRefObject): void { const updateSize = useStore(updateSizeSelector); useEffect(() => { diff --git a/src/store/index.ts b/src/store/index.ts index 286b3a49..5448af68 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -27,7 +27,7 @@ import { XYPosition, ReactFlowStore, } from '../types'; -import { isNode, isEdge, getRectOfNodes, getNodesInside, getConnectedEdges } from '../utils/graph'; +import { getRectOfNodes, getNodesInside, getConnectedEdges } from '../utils/graph'; import { getHandleBounds } from '../components/Nodes/utils'; import { createNodeInternals, createNodeOrEdgeSelectionChange } from './utils'; @@ -249,28 +249,36 @@ const createStore = () => set(stateUpdate); }, - addSelectedElements: (selectedElementsArr: Array) => { - const { multiSelectionActive, onNodesChange, onEdgesChange, nodeInternals, edges } = get(); + addSelectedNodes: (selectedNodeIds: string[]) => { + const { multiSelectionActive, onNodesChange, nodeInternals } = get(); // @TODO: work with nodeInternals instead of converting it to an array const nodes = Array.from(nodeInternals).map(([_, node]) => node); let changedNodes; - let changedEdges; if (multiSelectionActive) { - changedNodes = selectedElementsArr.filter(isNode).map(createNodeOrEdgeSelectionChange(true)); - changedEdges = selectedElementsArr.filter(isEdge).map(createNodeOrEdgeSelectionChange(true)); + changedNodes = selectedNodeIds.map(createNodeOrEdgeSelectionChange(true)); } else { changedNodes = nodes.map((node) => - createNodeOrEdgeSelectionChange(selectedElementsArr.some((e) => e.id === node.id))(node) - ); - changedEdges = edges.map((edge) => - createNodeOrEdgeSelectionChange(selectedElementsArr.some((e) => e.id === edge.id))(edge) + createNodeOrEdgeSelectionChange(selectedNodeIds.some((nodeId) => nodeId === node.id))(node.id) ); } if (changedNodes.length) { onNodesChange?.(changedNodes as NodeChange[]); } + }, + addSelectedEdges: (selectedEdgeIds: string[]) => { + const { multiSelectionActive, onEdgesChange, edges } = get(); + + let changedEdges; + + if (multiSelectionActive) { + changedEdges = selectedEdgeIds.map(createNodeOrEdgeSelectionChange(true)); + } else { + changedEdges = edges.map((edge) => + createNodeOrEdgeSelectionChange(selectedEdgeIds.some((edgeId) => edgeId === edge.id))(edge.id) + ); + } if (changedEdges.length) { onEdgesChange?.(changedEdges as EdgeChange[]); diff --git a/src/store/utils.ts b/src/store/utils.ts index 8247df6b..700aeefa 100644 --- a/src/store/utils.ts +++ b/src/store/utils.ts @@ -105,8 +105,8 @@ export function createNodeInternals(nodes: Node[], nodeInternals: NodeInternals) return nextNodeInternals; } -export const createNodeOrEdgeSelectionChange = (selected: boolean) => (item: Node | Edge) => ({ - id: item.id, +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 b8dc5c15..5aea0b75 100644 --- a/src/types/general.ts +++ b/src/types/general.ts @@ -182,7 +182,8 @@ export type ReactFlowActions = { unsetNodesSelection: () => void; resetSelectedElements: () => void; unselectNodesAndEdges: () => void; - addSelectedElements: (elements: Elements) => void; + addSelectedNodes: (nodeIds: string[]) => void; + addSelectedEdges: (edgeIds: string[]) => void; updateTransform: (transform: Transform) => void; updateSize: (size: Dimensions) => void; initD3Zoom: (payload: InitD3ZoomPayload) => void;