diff --git a/examples/react/src/examples/Stress/utils.ts b/examples/react/src/examples/Stress/utils.ts index 6278e006..9266bf9c 100644 --- a/examples/react/src/examples/Stress/utils.ts +++ b/examples/react/src/examples/Stress/utils.ts @@ -20,8 +20,6 @@ export function getNodesAndEdges(xElements = 10, yElements = 10): ElementsCollec style: { width: 50, height: 30, fontSize: 11 }, data, position, - width: 50, - height: 30, }; initialNodes.push(node); diff --git a/package.json b/package.json index 4411e137..274670c2 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "preinstall": "npx only-allow pnpm", "dev": "turbo run dev --parallel --concurrency 12", "dev:svelte": "turbo run dev --filter=svelte --filter=system", - "dev:react": "turbo run dev --filter=react", + "dev:react": "turbo run dev --filter=react-examples ", "test:svelte": "pnpm --filter=playwright run test:svelte", "test:svelte:ui": "pnpm --filter=playwright run test:svelte:ui", "test:react": "pnpm --filter=playwright run test:react", diff --git a/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx index 3e038fd0..74028658 100644 --- a/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx @@ -65,8 +65,8 @@ function ResizeControl({ const dragHandler = drag() .on('start', (event: ResizeDragEvent) => { - const { nodes, transform, snapGrid, snapToGrid } = store.getState(); - const node = nodes.find((n) => n.id === id); + const { nodesLookup, transform, snapGrid, snapToGrid } = store.getState(); + const node = nodesLookup.get(id); const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); prevValues.current = { @@ -86,9 +86,9 @@ function ResizeControl({ onResizeStart?.(event, { ...prevValues.current }); }) .on('drag', (event: ResizeDragEvent) => { - const { nodes, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState(); + const { nodesLookup, transform, snapGrid, snapToGrid, triggerNodeChanges } = store.getState(); const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); - const node = nodes.find((n) => n.id === id); + const node = nodesLookup.get(id); if (node) { const changes: NodeChange[] = []; diff --git a/packages/react/src/components/ConnectionLine/index.tsx b/packages/react/src/components/ConnectionLine/index.tsx index 439cc342..b40efa65 100644 --- a/packages/react/src/components/ConnectionLine/index.tsx +++ b/packages/react/src/components/ConnectionLine/index.tsx @@ -43,7 +43,7 @@ const ConnectionLine = ({ const { fromNode, handleId, toX, toY, connectionMode } = useStore( useCallback( (s: ReactFlowStore) => ({ - fromNode: s.nodes.find((n) => n.id === nodeId), + fromNode: s.nodesLookup.get(nodeId), handleId: s.connectionStartHandle?.handleId, toX: (s.connectionPosition.x - s.transform[0]) / s.transform[2], toY: (s.connectionPosition.y - s.transform[1]) / s.transform[2], diff --git a/packages/react/src/components/Edges/wrapEdge.tsx b/packages/react/src/components/Edges/wrapEdge.tsx index 750254ff..4ab6fa45 100644 --- a/packages/react/src/components/Edges/wrapEdge.tsx +++ b/packages/react/src/components/Edges/wrapEdge.tsx @@ -53,9 +53,9 @@ export default (EdgeComponent: ComponentType) => { const [updateHover, setUpdateHover] = useState(false); const [updating, setUpdating] = useState(false); const store = useStoreApi(); - const edgePosition = useStore((state) => { - const sourceNode = state.nodes.find((n) => n.id === source); - const targetNode = state.nodes.find((n) => n.id === target); + const edgePosition = useStore(function edgeSelector(state) { + const sourceNode = state.nodesLookup.get(source); + const targetNode = state.nodesLookup.get(target); if (!sourceNode || !targetNode) { return null; diff --git a/packages/react/src/container/NodeRenderer/index.tsx b/packages/react/src/container/NodeRenderer/index.tsx index f2b3ebe3..98f9fc39 100644 --- a/packages/react/src/container/NodeRenderer/index.tsx +++ b/packages/react/src/container/NodeRenderer/index.tsx @@ -48,11 +48,16 @@ const NodeRenderer = (props: NodeRendererProps) => { } const observer = new ResizeObserver((entries: ResizeObserverEntry[]) => { - const updates = entries.map((entry: ResizeObserverEntry) => ({ - id: entry.target.getAttribute('data-id') as string, - nodeElement: entry.target as HTMLDivElement, - forceUpdate: true, - })); + const updates = new Map(); + + entries.forEach((entry: ResizeObserverEntry) => { + const id = entry.target.getAttribute('data-id') as string; + updates.set(id, { + id, + nodeElement: entry.target as HTMLDivElement, + forceUpdate: true, + }); + }); updateNodeDimensions(updates); }); diff --git a/packages/react/src/hooks/useVisibleEdges.ts b/packages/react/src/hooks/useVisibleEdges.ts index 6d7f808a..e97fe105 100644 --- a/packages/react/src/hooks/useVisibleEdges.ts +++ b/packages/react/src/hooks/useVisibleEdges.ts @@ -12,8 +12,8 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole const visibleEdges = onlyRenderVisible && s.width && s.height ? s.edges.filter((e) => { - const sourceNode = s.nodes.find((n) => n.id === e.source); - const targetNode = s.nodes.find((n) => n.id === e.target); + const sourceNode = s.nodesLookup.get(e.source); + const targetNode = s.nodesLookup.get(e.target); return ( sourceNode && @@ -29,7 +29,7 @@ function useVisibleEdges(onlyRenderVisible: boolean, elevateEdgesOnSelect: boole }) : s.edges; - return groupEdgesByZLevel(visibleEdges, s.nodes, elevateEdgesOnSelect); + return groupEdgesByZLevel(visibleEdges, s.nodesLookup, elevateEdgesOnSelect); }, [onlyRenderVisible, elevateEdgesOnSelect] ), diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index ad42fd6e..6f8ca67b 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -41,8 +41,8 @@ const createRFStore = ({ (set, get) => ({ ...getInitialState({ nodes, edges, width, height, fitView }), setNodes: (nodes: Node[]) => { - const { nodes: storeNodes, nodeOrigin, elevateNodesOnSelect } = get(); - const nextNodes = updateNodes(nodes, storeNodes, { nodeOrigin, elevateNodesOnSelect }); + const { nodesLookup, nodeOrigin, elevateNodesOnSelect } = get(); + const nextNodes = updateNodes(nodes, nodesLookup, { nodeOrigin, elevateNodesOnSelect }); set({ nodes: nextNodes }); }, @@ -68,7 +68,7 @@ const createRFStore = ({ }; if (hasDefaultNodes) { - nextState.nodes = updateNodes(nodes, [], { + nextState.nodes = updateNodes(nodes, new Map(), { nodeOrigin: get().nodeOrigin, elevateNodesOnSelect: get().elevateNodesOnSelect, }); @@ -80,13 +80,23 @@ const createRFStore = ({ set(nextState); }, updateNodeDimensions: (updates) => { - const { onNodesChange, fitView, nodes, fitViewOnInit, fitViewDone, fitViewOnInitOptions, domNode, nodeOrigin } = - get(); + const { + onNodesChange, + fitView, + nodes, + nodesLookup, + fitViewOnInit, + fitViewDone, + fitViewOnInitOptions, + domNode, + nodeOrigin, + } = get(); const changes: NodeDimensionChange[] = []; const updatedNodes = updateNodeDimensionsSystem( updates, nodes, + nodesLookup, domNode, nodeOrigin, (id: string, dimensions: Dimensions) => { @@ -102,7 +112,7 @@ const createRFStore = ({ return; } - const nextNodes = updateAbsolutePositions(updatedNodes, nodeOrigin); + const nextNodes = updateAbsolutePositions(updatedNodes, nodesLookup, nodeOrigin); let nextFitViewDone = fitViewDone; if (!fitViewDone && fitViewOnInit) { @@ -138,12 +148,12 @@ const createRFStore = ({ }, triggerNodeChanges: (changes) => { - const { onNodesChange, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get(); + const { onNodesChange, nodesLookup, nodes, hasDefaultNodes, nodeOrigin, elevateNodesOnSelect } = get(); if (changes?.length) { if (hasDefaultNodes) { const updatedNodes = applyNodeChanges(changes, nodes); - const nextNodes = updateNodes(updatedNodes, nodes, { + const nextNodes = updateNodes(updatedNodes, nodesLookup, { nodeOrigin, elevateNodesOnSelect, }); diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index 8f8fa450..633d9aa5 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -22,7 +22,8 @@ const getInitialState = ({ height?: number; fitView?: boolean; } = {}): ReactFlowStore => { - const nextNodes = updateNodes(nodes, [], { nodeOrigin: [0, 0], elevateNodesOnSelect: false }); + const nodesLookup = new Map(); + const nextNodes = updateNodes(nodes, nodesLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false }); let transform: Transform = [0, 0, 1]; @@ -43,6 +44,7 @@ const getInitialState = ({ height: 0, transform, nodes: nextNodes, + nodesLookup, edges: edges, onNodesChange: null, onEdgesChange: null, diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index 3662f862..3995d208 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -46,6 +46,7 @@ export type ReactFlowStore = { height: number; transform: Transform; nodes: Node[]; + nodesLookup: Map; edges: Edge[]; onNodesChange: OnNodesChange | null; onEdgesChange: OnEdgesChange | null; @@ -141,7 +142,7 @@ export type ReactFlowActions = { getNodes: () => Node[]; setEdges: (edges: Edge[]) => void; setDefaultNodesAndEdges: (nodes?: Node[], edges?: Edge[]) => void; - updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void; + updateNodeDimensions: (updates: Map) => void; updateNodePositions: UpdateNodePositions; resetSelectedElements: () => void; unselectNodesAndEdges: (params?: UnselectNodesAndEdgesParams) => void; diff --git a/packages/svelte/src/lib/actions/drag/index.ts b/packages/svelte/src/lib/actions/drag/index.ts index e936d3a5..9e719c32 100644 --- a/packages/svelte/src/lib/actions/drag/index.ts +++ b/packages/svelte/src/lib/actions/drag/index.ts @@ -30,6 +30,7 @@ export default function drag(domNode: Element, params: UseDragParams) { return { nodes: get(store.nodes), + nodesLookup: get(store.nodesLookup), edges: get(store.edges), nodeExtent: get(store.nodeExtent), snapGrid: snapGrid ? snapGrid : [0, 0], diff --git a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte index c4f9ec6f..6a7acd90 100644 --- a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte +++ b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte @@ -17,11 +17,18 @@ typeof ResizeObserver === 'undefined' ? null : new ResizeObserver((entries: ResizeObserverEntry[]) => { - const updates = entries.map((entry: ResizeObserverEntry) => ({ - id: entry.target.getAttribute('data-id') as string, - nodeElement: entry.target as HTMLDivElement, - forceUpdate: true - })); + const updates = new Map(); + + entries.forEach((entry: ResizeObserverEntry) => { + const id = entry.target.getAttribute('data-id') as string; + + updates.set(id, { + id, + nodeElement: entry.target as HTMLDivElement, + forceUpdate: true + }); + }); + updateNodeDimensions(updates); }); diff --git a/packages/svelte/src/lib/store/derived-connection-props.ts b/packages/svelte/src/lib/store/derived-connection-props.ts index 5be1598c..173362ac 100644 --- a/packages/svelte/src/lib/store/derived-connection-props.ts +++ b/packages/svelte/src/lib/store/derived-connection-props.ts @@ -56,15 +56,15 @@ export function getDerivedConnectionProps( currentConnection, store.connectionLineType, store.connectionMode, - store.nodes, + store.nodesLookup, store.viewport ], - ([connection, connectionLineType, connectionMode, nodes, viewport]) => { + ([connection, connectionLineType, connectionMode, nodesLookup, viewport]) => { if (!connection.connectionStartHandle?.nodeId) { return initConnectionProps; } - const fromNode = nodes.find((n) => n.id === connection.connectionStartHandle?.nodeId); + const fromNode = nodesLookup.get(connection.connectionStartHandle?.nodeId); const fromHandleBounds = fromNode?.[internalsSymbol]?.handleBounds; const handleBoundsStrict = fromHandleBounds?.[connection.connectionStartHandle.type || 'source'] || []; diff --git a/packages/svelte/src/lib/store/edge-tree.ts b/packages/svelte/src/lib/store/edge-tree.ts index ff099868..221563ef 100644 --- a/packages/svelte/src/lib/store/edge-tree.ts +++ b/packages/svelte/src/lib/store/edge-tree.ts @@ -9,17 +9,18 @@ export function getEdgeTree(store: SvelteFlowStoreState) { [ store.edges, store.nodes, + store.nodesLookup, store.onlyRenderVisibleElements, store.viewport, store.width, store.height ], - ([edges, nodes, onlyRenderVisibleElements, viewport, width, height]) => { + ([edges, , nodesLookup, onlyRenderVisibleElements, viewport, width, height]) => { const visibleEdges = onlyRenderVisibleElements && width && height ? edges.filter((edge) => { - const sourceNode = nodes.find((node) => node.id === edge.source); - const targetNode = nodes.find((node) => node.id === edge.target); + const sourceNode = nodesLookup.get(edge.source); + const targetNode = nodesLookup.get(edge.target); return ( sourceNode && @@ -40,11 +41,11 @@ export function getEdgeTree(store: SvelteFlowStoreState) { ); return derived( - [visibleEdges, store.nodes, store.connectionMode, store.onError], - ([visibleEdges, nodes, connectionMode, onError]) => { + [visibleEdges, store.nodes, store.nodesLookup, store.connectionMode, store.onError], + ([visibleEdges, , nodesLookup, connectionMode, onError]) => { const layoutedEdges = visibleEdges.reduce((res, edge) => { - const sourceNode = nodes.find((node) => node.id === edge.source); - const targetNode = nodes.find((node) => node.id === edge.target); + const sourceNode = nodesLookup.get(edge.source); + const targetNode = nodesLookup.get(edge.target); if (!sourceNode || !targetNode) { return res; @@ -70,7 +71,7 @@ export function getEdgeTree(store: SvelteFlowStoreState) { return res; }, []); - const groupedEdges = groupEdgesByZLevel(layoutedEdges, nodes, false); + const groupedEdges = groupEdgesByZLevel(layoutedEdges, nodesLookup, false); return groupedEdges; } diff --git a/packages/svelte/src/lib/store/index.ts b/packages/svelte/src/lib/store/index.ts index b78e6fb2..c9140805 100644 --- a/packages/svelte/src/lib/store/index.ts +++ b/packages/svelte/src/lib/store/index.ts @@ -86,10 +86,11 @@ export function createStore({ }); }; - function updateNodeDimensions(updates: NodeDimensionUpdate[]) { + function updateNodeDimensions(updates: Map) { const nextNodes = updateNodeDimensionsSystem( updates, get(store.nodes), + get(store.nodesLookup), get(store.domNode), get(store.nodeOrigin) ); diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index 7744ac0c..7cabef6e 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -59,7 +59,11 @@ export const getInitialStore = ({ height?: number; fitView?: boolean; }) => { - const nextNodes = updateNodes(nodes, [], { nodeOrigin: [0, 0], elevateNodesOnSelect: false }); + const nodesLookup = new Map(); + const nextNodes = updateNodes(nodes, nodesLookup, { + nodeOrigin: [0, 0], + elevateNodesOnSelect: false + }); let viewport: Viewport = { x: 0, y: 0, zoom: 1 }; @@ -75,7 +79,8 @@ export const getInitialStore = ({ return { flowId: writable(null), - nodes: createNodesStore(nextNodes), + nodes: createNodesStore(nextNodes, nodesLookup), + nodesLookup: readable>(nodesLookup), visibleNodes: readable([]), edges: createEdgesStore(edges), edgeTree: readable[]>([]), diff --git a/packages/svelte/src/lib/store/types.ts b/packages/svelte/src/lib/store/types.ts index ae854cfd..8d41d63f 100644 --- a/packages/svelte/src/lib/store/types.ts +++ b/packages/svelte/src/lib/store/types.ts @@ -27,7 +27,7 @@ export type SvelteFlowStoreActions = { setTranslateExtent: (extent: CoordinateExtent) => void; fitView: (options?: FitViewOptions) => boolean; updateNodePositions: UpdateNodePositions; - updateNodeDimensions: (updates: NodeDimensionUpdate[]) => void; + updateNodeDimensions: (updates: Map) => void; unselectNodesAndEdges: (params?: { nodes?: Node[]; edges?: Edge[] }) => void; addSelectedNodes: (ids: string[]) => void; addSelectedEdges: (ids: string[]) => void; diff --git a/packages/svelte/src/lib/store/utils.ts b/packages/svelte/src/lib/store/utils.ts index cfd5b343..82f8afc6 100644 --- a/packages/svelte/src/lib/store/utils.ts +++ b/packages/svelte/src/lib/store/utils.ts @@ -111,7 +111,8 @@ export type NodeStoreOptions = { // we are creating a custom store for the internals nodes in order to update the zIndex and positionAbsolute. // The user only passes in relative positions, so we need to calculate the absolute positions based on the parent nodes. export const createNodesStore = ( - nodes: Node[] + nodes: Node[], + nodesLookup: Map ): { subscribe: (this: void, run: Subscriber) => Unsubscriber; update: (this: void, updater: Updater) => void; @@ -125,7 +126,7 @@ export const createNodesStore = ( let elevateNodesOnSelect = true; const _set = (nds: Node[]): Node[] => { - const nextNodes = updateNodes(nds, value, { + const nextNodes = updateNodes(nds, nodesLookup, { elevateNodesOnSelect, defaults }); diff --git a/packages/system/src/utils/edges/general.ts b/packages/system/src/utils/edges/general.ts index e15d9269..5d4c3151 100644 --- a/packages/system/src/utils/edges/general.ts +++ b/packages/system/src/utils/edges/general.ts @@ -33,7 +33,7 @@ export type GroupedEdges = { export function groupEdgesByZLevel( edges: EdgeType[], - nodes: NodeBase[], + nodesLookup: Map, elevateEdgesOnSelect = false ): GroupedEdges[] { let maxLevel = -1; @@ -43,8 +43,8 @@ export function groupEdgesByZLevel( let z = hasZIndex ? edge.zIndex! : 0; if (elevateEdgesOnSelect) { - const targetNode = nodes.find((n) => n.id === edge.target); - const sourceNode = nodes.find((n) => n.id === edge.source); + const targetNode = nodesLookup.get(edge.target); + const sourceNode = nodesLookup.get(edge.source); const edgeOrConnectedNodeSelected = edge.selected || targetNode?.selected || sourceNode?.selected; const selectedZIndex = Math.max( sourceNode?.[internalsSymbol]?.z || 0, diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 3fee5d02..ff168d43 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -18,19 +18,21 @@ type ParentNodes = Record; export function updateAbsolutePositions( nodes: NodeType[], + nodesLookup: Map, nodeOrigin: NodeOrigin = [0, 0], parentNodes?: ParentNodes ) { return nodes.map((node) => { - if (node.parentNode && !nodes.find((n) => n.id === node.parentNode)) { + if (node.parentNode && !nodesLookup.has(node.parentNode)) { throw new Error(`Parent node ${node.parentNode} not found`); } if (node.parentNode || parentNodes?.[node.id]) { - const parentNode = node.parentNode ? nodes.find((n) => n.id === node.parentNode) : null; + const parentNode = node.parentNode ? nodesLookup.get(node.parentNode) : null; const { x, y, z } = calculateXYZPosition( node, nodes, + nodesLookup, { ...node.position, z: node[internalsSymbol]?.z ?? 0, @@ -62,7 +64,7 @@ type UpdateNodesOptions = { export function updateNodes( nodes: NodeType[], - storeNodes: NodeType[], + nodesLookup: Map, options: UpdateNodesOptions = { nodeOrigin: [0, 0] as NodeOrigin, elevateNodesOnSelect: true, @@ -73,7 +75,7 @@ export function updateNodes( const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; const nextNodes = nodes.map((n) => { - const currentStoreNode = storeNodes.find((storeNode) => n.id === storeNode.id); + const currentStoreNode = nodesLookup.get(n.id); const node: NodeType = { ...options.defaults, ...n, @@ -96,10 +98,12 @@ export function updateNodes( }, }); + nodesLookup.set(node.id, node); + return node; }); - const nodesWithPositions = updateAbsolutePositions(nextNodes, options.nodeOrigin, parentNodes); + const nodesWithPositions = updateAbsolutePositions(nextNodes, nodesLookup, options.nodeOrigin, parentNodes); return nodesWithPositions; } @@ -107,6 +111,7 @@ export function updateNodes( function calculateXYZPosition( node: NodeType, nodes: NodeType[], + nodesLookup: Map, result: XYZPosition, nodeOrigin: NodeOrigin ): XYZPosition { @@ -114,12 +119,13 @@ function calculateXYZPosition( return result; } - const parentNode = nodes.find((n) => n.id === node.parentNode)!; + const parentNode = nodesLookup.get(node.parentNode)!; const parentNodePosition = getNodePositionWithOrigin(parentNode, parentNode?.origin || nodeOrigin); return calculateXYZPosition( parentNode, nodes, + nodesLookup, { x: (result.x ?? 0) + parentNodePosition.x, y: (result.y ?? 0) + parentNodePosition.y, @@ -130,8 +136,9 @@ function calculateXYZPosition( } export function updateNodeDimensions( - updates: NodeDimensionUpdate[], + updates: Map, nodes: NodeBase[], + nodesLookup: Map, domNode: HTMLElement | null, nodeOrigin?: NodeOrigin, onUpdate?: (id: string, dimensions: Dimensions) => void @@ -146,7 +153,8 @@ export function updateNodeDimensions( const { m22: zoom } = new window.DOMMatrixReadOnly(style.transform); const nextNodes = nodes.map((node) => { - const update = updates.find((u) => u.id === node.id); + const update = updates.get(node.id); + if (update) { const dimensions = getDimensions(update.nodeElement); const doUpdate = !!( @@ -158,7 +166,7 @@ export function updateNodeDimensions( if (doUpdate) { onUpdate?.(node.id, dimensions); - return { + const newNode = { ...node, ...dimensions, [internalsSymbol]: { @@ -169,6 +177,10 @@ export function updateNodeDimensions( }, }, }; + + nodesLookup.set(node.id, newNode); + + return newNode; } } diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index 9271cb9d..66637c70 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -33,6 +33,7 @@ export type OnDrag = (event: MouseEvent, dragItems: NodeDragItem[], node: NodeBa type StoreItems = { nodes: NodeBase[]; + nodesLookup: Map; edges: EdgeBase[]; nodeExtent: CoordinateExtent; snapGrid: SnapGrid; @@ -103,6 +104,7 @@ export function XYDrag({ function updateNodes({ x, y }: XYPosition) { const { nodes, + nodesLookup, nodeExtent, snapGrid, snapToGrid, @@ -163,11 +165,11 @@ export function XYDrag({ updateNodePositions(dragItems, true, true); const onNodeOrSelectionDrag = nodeId ? onNodeDrag : wrapSelectionDragFunc(onSelectionDrag); - if (dragEvent) { + if (dragEvent && (onDrag || onNodeOrSelectionDrag)) { const [currentNode, currentNodes] = getEventHandlerParams({ nodeId, dragItems, - nodes, + nodesLookup, }); onDrag?.(dragEvent as MouseEvent, dragItems, currentNode, currentNodes); onNodeOrSelectionDrag?.(dragEvent as MouseEvent, currentNode, currentNodes); @@ -197,6 +199,7 @@ export function XYDrag({ function startDrag(event: UseDragEvent) { const { nodes, + nodesLookup, multiSelectionActive, nodesDraggable, transform, @@ -227,11 +230,11 @@ export function XYDrag({ const onNodeOrSelectionDragStart = nodeId ? onNodeDragStart : wrapSelectionDragFunc(onSelectionDragStart); - if (dragItems) { + if (dragItems && (onDragStart || onNodeOrSelectionDragStart)) { const [currentNode, currentNodes] = getEventHandlerParams({ nodeId, dragItems, - nodes, + nodesLookup, }); onDragStart?.(event.sourceEvent as MouseEvent, dragItems, currentNode, currentNodes); onNodeOrSelectionDragStart?.(event.sourceEvent as MouseEvent, currentNode, currentNodes); @@ -288,18 +291,20 @@ export function XYDrag({ cancelAnimationFrame(autoPanId); if (dragItems) { - const { nodes, updateNodePositions, onNodeDragStop, onSelectionDragStop } = getStoreItems(); + const { nodesLookup, updateNodePositions, onNodeDragStop, onSelectionDragStop } = getStoreItems(); const onNodeOrSelectionDragStop = nodeId ? onNodeDragStop : wrapSelectionDragFunc(onSelectionDragStop); updateNodePositions(dragItems, false, false); - const [currentNode, currentNodes] = getEventHandlerParams({ - nodeId, - dragItems, - nodes, - }); - onDragStop?.(event.sourceEvent as MouseEvent, dragItems, currentNode, currentNodes); - onNodeOrSelectionDragStop?.(event.sourceEvent as MouseEvent, currentNode, currentNodes); + if (onDragStop || onNodeOrSelectionDragStop) { + const [currentNode, currentNodes] = getEventHandlerParams({ + nodeId, + dragItems, + nodesLookup, + }); + onDragStop?.(event.sourceEvent as MouseEvent, dragItems, currentNode, currentNodes); + onNodeOrSelectionDragStop?.(event.sourceEvent as MouseEvent, currentNode, currentNodes); + } } }) .filter((event: MouseEvent) => { diff --git a/packages/system/src/xydrag/utils.ts b/packages/system/src/xydrag/utils.ts index fbf0e8f9..d8bfdb59 100644 --- a/packages/system/src/xydrag/utils.ts +++ b/packages/system/src/xydrag/utils.ts @@ -75,14 +75,14 @@ export function getDragItems( export function getEventHandlerParams({ nodeId, dragItems, - nodes, + nodesLookup, }: { nodeId?: string; dragItems: NodeDragItem[]; - nodes: NodeType[]; + nodesLookup: Map; }): [NodeType, NodeType[]] { const extentedDragItems: NodeType[] = dragItems.map((n) => { - const node = nodes.find((node) => node.id === n.id)!; + const node = nodesLookup.get(n.id)!; return { ...node,