From ea18e46a1a0923318b27b3f2a2311c345cee399a Mon Sep 17 00:00:00 2001 From: moklick Date: Wed, 3 Apr 2024 14:54:24 +0200 Subject: [PATCH] refactor(nodes): rename computed to measured, add helpers --- .../cypress/components/hooks/useNodes.cy.tsx | 3 +- .../reactflow/on-nodes-change.cy.tsx | 2 +- .../components/utils/apply-changes.cy.ts | 6 +- .../react/src/examples/EasyConnect/utils.tsx | 10 +-- .../react/src/examples/FloatingEdges/utils.ts | 6 +- .../MiniMap/MiniMapNodes.tsx | 4 +- .../NodeResizer/NodeResizeControl.tsx | 6 +- .../NodeToolbar/NodeToolbar.tsx | 4 +- .../src/components/ConnectionLine/index.tsx | 4 +- packages/react/src/container/Pane/index.tsx | 4 +- packages/react/src/hooks/useInternalNode.ts | 21 ++++++ packages/react/src/hooks/useReactFlow.ts | 21 ++---- packages/react/src/hooks/useViewportHelper.ts | 4 +- packages/react/src/hooks/useVisibleNodeIds.ts | 6 +- packages/react/src/index.ts | 1 + packages/react/src/store/index.ts | 42 +++++------ packages/react/src/store/initialState.ts | 4 +- packages/react/src/types/changes.ts | 71 ------------------- packages/react/src/types/general.ts | 4 +- packages/react/src/types/index.ts | 1 - packages/react/src/types/store.ts | 4 +- packages/react/src/utils/changes.ts | 17 +++-- packages/svelte/src/lib/store/utils.ts | 4 +- packages/system/src/types/general.ts | 2 +- packages/system/src/types/nodes.ts | 14 ++-- packages/system/src/utils/edges/positions.ts | 2 +- packages/system/src/utils/general.ts | 20 +++--- packages/system/src/utils/graph.ts | 34 ++++----- packages/system/src/utils/store.ts | 40 +++++------ packages/system/src/xydrag/XYDrag.ts | 4 +- packages/system/src/xydrag/utils.ts | 10 +-- packages/system/src/xyresizer/XYResizer.ts | 10 +-- 32 files changed, 159 insertions(+), 226 deletions(-) create mode 100644 packages/react/src/hooks/useInternalNode.ts delete mode 100644 packages/react/src/types/changes.ts diff --git a/examples/react/cypress/components/hooks/useNodes.cy.tsx b/examples/react/cypress/components/hooks/useNodes.cy.tsx index 9c10ad70..340b4b10 100644 --- a/examples/react/cypress/components/hooks/useNodes.cy.tsx +++ b/examples/react/cypress/components/hooks/useNodes.cy.tsx @@ -15,8 +15,7 @@ const initialNodes: Node[] = nodes.map((n) => ({ const expectedNodes: Node[] = initialNodes.map((n) => ({ ...n, - computed: { - positionAbsolute: n.position, + measured: { ...nodeDimensions, }, })); diff --git a/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx b/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx index a15fc67e..bceb4cbd 100644 --- a/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx +++ b/examples/react/cypress/components/reactflow/on-nodes-change.cy.tsx @@ -48,7 +48,7 @@ describe(': onNodesChange', () => { id: '1', item: { ...nodes[0], - computed: { positionAbsolute: nodes[0].position, width: 200, height: 100 }, + measured: { width: 200, height: 100 }, style: { width: 200, height: 100 }, }, }, diff --git a/examples/react/cypress/components/utils/apply-changes.cy.ts b/examples/react/cypress/components/utils/apply-changes.cy.ts index 1548b7e9..0a75db7b 100644 --- a/examples/react/cypress/components/utils/apply-changes.cy.ts +++ b/examples/react/cypress/components/utils/apply-changes.cy.ts @@ -136,8 +136,8 @@ describe('applyChanges Testing', () => { ]; const nextNodes = applyNodeChanges(nodeChanges, nodes); - expect(nodes[0].computed).to.be.undefined; - expect(nextNodes[0].computed).to.be.deep.equal({ width: newWidth, height: newHeight }); + expect(nodes[0].measured).to.be.undefined; + expect(nextNodes[0].measured).to.be.deep.equal({ width: newWidth, height: newHeight }); expect(nextNodes[0].width).to.be.undefined; expect(nextNodes[0].height).to.be.undefined; }); @@ -153,7 +153,7 @@ describe('applyChanges Testing', () => { const nextNodes = applyNodeChanges(nodeChanges, nodes); expect(nextNodes[0].position).to.be.deep.equal(newPosition); - expect(nextNodes[0].computed).to.be.deep.equal({ width: newWidth, height: newHeight }); + expect(nextNodes[0].measured).to.be.deep.equal({ width: newWidth, height: newHeight }); }); it('replaces nodes/edges', () => { diff --git a/examples/react/src/examples/EasyConnect/utils.tsx b/examples/react/src/examples/EasyConnect/utils.tsx index 6d87795b..c6693015 100644 --- a/examples/react/src/examples/EasyConnect/utils.tsx +++ b/examples/react/src/examples/EasyConnect/utils.tsx @@ -9,8 +9,8 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node) { width: intersectionNodeWidth, height: intersectionNodeHeight, positionAbsolute: intersectionNodePosition, - } = intersectionNode.computed || {}; - const targetPosition = targetNode.computed?.positionAbsolute!; + } = intersectionNode.measured || {}; + const targetPosition = targetNode.measured?.positionAbsolute!; const w = intersectionNodeWidth! / 2; const h = intersectionNodeHeight! / 2; @@ -33,7 +33,7 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node) { // returns the position (top,right,bottom or right) passed node compared to the intersection point function getEdgePosition(node: Node, intersectionPoint: XYPosition) { - const n = { ...node.computed?.positionAbsolute, ...node }; + const n = { ...node.measured?.positionAbsolute, ...node }; const nx = Math.round(n.x!); const ny = Math.round(n.y!); const px = Math.round(intersectionPoint.x); @@ -42,13 +42,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) { if (px <= nx + 1) { return Position.Left; } - if (px >= nx + n.computed?.width! - 1) { + if (px >= nx + n.measured?.width! - 1) { return Position.Right; } if (py <= ny + 1) { return Position.Top; } - if (py >= n.y! + n.computed?.height! - 1) { + if (py >= n.y! + n.measured?.height! - 1) { return Position.Bottom; } diff --git a/examples/react/src/examples/FloatingEdges/utils.ts b/examples/react/src/examples/FloatingEdges/utils.ts index a022207c..08593c5c 100644 --- a/examples/react/src/examples/FloatingEdges/utils.ts +++ b/examples/react/src/examples/FloatingEdges/utils.ts @@ -6,7 +6,7 @@ function getNodeIntersection(intersectionNode: Node, targetNode: Node): XYPositi // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a const { position: intersectionNodePosition } = intersectionNode; - const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.computed ?? { + const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode.measured ?? { width: 0, height: 0, }; @@ -42,13 +42,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) { if (px <= nx + 1) { return Position.Left; } - if (px >= nx + (n.computed?.width ?? 0) - 1) { + if (px >= nx + (n.measured?.width ?? 0) - 1) { return Position.Right; } if (py <= ny + 1) { return Position.Top; } - if (py >= n.y + (n.computed?.height ?? 0) - 1) { + if (py >= n.y + (n.measured?.height ?? 0) - 1) { return Position.Bottom; } diff --git a/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx b/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx index 0abe3177..f2e7944c 100644 --- a/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx +++ b/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx @@ -6,7 +6,7 @@ import { shallow } from 'zustand/shallow'; import { useStore } from '../../hooks/useStore'; import { MiniMapNode } from './MiniMapNode'; -import type { ReactFlowState, Node } from '../../types'; +import type { ReactFlowState, Node, InternalNode } from '../../types'; import type { MiniMapNodes as MiniMapNodesProps, GetMiniMapNodeAttribute, MiniMapNodeProps } from './types'; declare const window: any; @@ -85,7 +85,7 @@ function NodeComponentWrapperInner({ shapeRendering: string; }) { const { node, x, y } = useStore((s) => { - const node = s.nodeLookup.get(id) as NodeType; + const node = s.nodeLookup.get(id) as InternalNode; const { x, y } = getNodePositionWithOrigin(node, node?.origin || nodeOrigin).positionAbsolute; return { diff --git a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx index 1a1107b1..2052c98b 100644 --- a/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/NodeResizeControl.tsx @@ -5,12 +5,14 @@ import { ResizeControlVariant, type XYResizerInstance, type XYResizerChange, - XYResizerChildChange, + type XYResizerChildChange, + type NodeChange, + type NodeDimensionChange, + type NodePositionChange, } from '@xyflow/system'; import { useStoreApi } from '../../hooks/useStore'; import { useNodeId } from '../../contexts/NodeIdContext'; -import type { NodeChange, NodeDimensionChange, NodePositionChange } from '../../types'; import type { ResizeControlProps, ResizeControlLineProps } from './types'; function ResizeControl({ diff --git a/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx b/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx index 6c0236d8..1fc6f25c 100644 --- a/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx +++ b/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx @@ -12,8 +12,8 @@ import type { NodeToolbarProps } from './types'; const nodeEqualityFn = (a?: InternalNode, b?: InternalNode) => a?.internals.positionAbsolute.x !== b?.internals.positionAbsolute.x || a?.internals.positionAbsolute.y !== b?.internals.positionAbsolute.y || - a?.computed.width !== b?.computed.width || - a?.computed.height !== b?.computed.height || + a?.measured.width !== b?.measured.width || + a?.measured.height !== b?.measured.height || a?.selected !== b?.selected || a?.internals.z !== b?.internals.z; diff --git a/packages/react/src/components/ConnectionLine/index.tsx b/packages/react/src/components/ConnectionLine/index.tsx index 86655232..54b3f24d 100644 --- a/packages/react/src/components/ConnectionLine/index.tsx +++ b/packages/react/src/components/ConnectionLine/index.tsx @@ -64,8 +64,8 @@ const ConnectionLine = ({ } const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0]; - const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.computed.width ?? 0) / 2; - const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.computed.height ?? 0; + const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.measured.width ?? 0) / 2; + const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.measured.height ?? 0; const fromX = (fromNode.internals.positionAbsolute.x ?? 0) + fromHandleX; const fromY = (fromNode.internals.positionAbsolute.y ?? 0) + fromHandleY; const fromPosition = fromHandle?.position; diff --git a/packages/react/src/container/Pane/index.tsx b/packages/react/src/container/Pane/index.tsx index ad2b01ed..0c0eb122 100644 --- a/packages/react/src/container/Pane/index.tsx +++ b/packages/react/src/container/Pane/index.tsx @@ -5,13 +5,13 @@ import { useRef, type MouseEvent as ReactMouseEvent, type ReactNode } from 'react'; import { shallow } from 'zustand/shallow'; import cc from 'classcat'; -import { getNodesInside, getEventPosition, SelectionMode } from '@xyflow/system'; +import { getNodesInside, getEventPosition, SelectionMode, type NodeChange, type EdgeChange } from '@xyflow/system'; import { UserSelection } from '../../components/UserSelection'; import { containerStyle } from '../../styles/utils'; import { useStore, useStoreApi } from '../../hooks/useStore'; import { getSelectionChanges } from '../../utils'; -import type { ReactFlowProps, ReactFlowState, NodeChange, EdgeChange } from '../../types'; +import type { ReactFlowProps, ReactFlowState } from '../../types'; type PaneProps = { isSelecting: boolean; diff --git a/packages/react/src/hooks/useInternalNode.ts b/packages/react/src/hooks/useInternalNode.ts new file mode 100644 index 00000000..d16d697b --- /dev/null +++ b/packages/react/src/hooks/useInternalNode.ts @@ -0,0 +1,21 @@ +import { useCallback } from 'react'; +import { shallow } from 'zustand/shallow'; + +import { useStore } from './useStore'; +import type { InternalNode, Node } from '../types'; + +/** + * Hook for getting an internal node by id + * + * @public + * @param id - id of the node + * @returns array with visible node ids + */ +export function useInternalNode(id: string): InternalNode | undefined { + const node = useStore( + useCallback((s) => s.nodeLookup.get(id) as InternalNode | undefined, [id]), + shallow + ); + + return node; +} diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 4e83448a..3a022b15 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -1,12 +1,5 @@ import { useCallback, useMemo, useRef, useState } from 'react'; -import { - getElementsToRemove, - getOverlappingArea, - isRectObject, - nodeHasDimensions, - nodeToRect, - type Rect, -} from '@xyflow/system'; +import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system'; import useViewportHelper from './useViewportHelper'; import { useStoreApi } from './useStore'; @@ -32,7 +25,7 @@ export function useReactFlow>((id) => { - return store.getState().nodeLookup.get(id)?.internals.userProvidedNode as NodeType; + return store.getState().nodeLookup.get(id)?.internals.userNode as NodeType; }, []); const getEdges = useCallback>(() => { @@ -223,13 +216,9 @@ export function useReactFlow { - const node = - isNode(nodeOrRect) && nodeHasDimensions(nodeOrRect) - ? nodeOrRect - : (store.getState().nodeLookup.get(nodeOrRect.id)?.internals.userProvidedNode as NodeType); - - return node ? nodeToRect(node) : null; + const getNodeRect = useCallback(({ id }: { id: string }): Rect | null => { + const internalNode = store.getState().nodeLookup.get(id); + return internalNode ? nodeToRect(internalNode) : null; }, []); const getIntersectingNodes = useCallback>( diff --git a/packages/react/src/hooks/useViewportHelper.ts b/packages/react/src/hooks/useViewportHelper.ts index 0e27d933..eb92b310 100644 --- a/packages/react/src/hooks/useViewportHelper.ts +++ b/packages/react/src/hooks/useViewportHelper.ts @@ -48,12 +48,12 @@ const useViewportHelper = (): ViewportHelperFunctions => { return { x, y, zoom }; }, fitView: (options) => { - const { nodes, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState(); + const { nodeLookup, width, height, nodeOrigin, minZoom, maxZoom, panZoom } = store.getState(); return panZoom ? fitView( { - nodes, + nodeLookup, width, height, nodeOrigin, diff --git a/packages/react/src/hooks/useVisibleNodeIds.ts b/packages/react/src/hooks/useVisibleNodeIds.ts index 309391dc..e8d14c8e 100644 --- a/packages/react/src/hooks/useVisibleNodeIds.ts +++ b/packages/react/src/hooks/useVisibleNodeIds.ts @@ -1,13 +1,13 @@ -import { getNodesInside } from '@xyflow/system'; +import { useCallback } from 'react'; import { shallow } from 'zustand/shallow'; +import { getNodesInside } from '@xyflow/system'; import { useStore } from './useStore'; import type { Node, ReactFlowState } from '../types'; -import { useCallback } from 'react'; const selector = (onlyRenderVisible: boolean) => (s: ReactFlowState) => { return onlyRenderVisible - ? getNodesInside(s.nodes, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map( + ? getNodesInside(s.nodeLookup, { x: 0, y: 0, width: s.width, height: s.height }, s.transform, true).map( (node) => node.id ) : Array.from(s.nodeLookup.keys()); diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts index 91be19eb..e7e6b37f 100644 --- a/packages/react/src/index.ts +++ b/packages/react/src/index.ts @@ -26,6 +26,7 @@ export { useNodesInitialized, type UseNodesInitializedOptions } from './hooks/us export { useHandleConnections } from './hooks/useHandleConnections'; export { useNodesData } from './hooks/useNodesData'; export { useConnection } from './hooks/useConnection'; +export { useInternalNode } from './hooks/useInternalNode'; export { useNodeId } from './contexts/NodeIdContext'; export { applyNodeChanges, applyEdgeChanges } from './utils/changes'; diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index 843a2500..3a5686b2 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -2,27 +2,20 @@ import { createWithEqualityFn } from 'zustand/traditional'; import { clampPosition, fitView as fitViewSystem, - adoptUserProvidedNodes, + adoptUserNodes, updateAbsolutePositions, panBy as panBySystem, updateNodeDimensions as updateNodeDimensionsSystem, updateConnectionLookup, handleParentExpand, NodeChange, + EdgeSelectionChange, + NodeSelectionChange, } from '@xyflow/system'; import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes'; import getInitialState from './initialState'; -import type { - ReactFlowState, - Node, - Edge, - EdgeSelectionChange, - NodeSelectionChange, - UnselectNodesAndEdgesParams, - FitViewOptions, - InternalNode, -} from '../types'; +import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams, FitViewOptions, InternalNode } from '../types'; const createRFStore = ({ nodes, @@ -52,7 +45,7 @@ const createRFStore = ({ // // When this happens, we take the note objects passed by the user and extend them with fields // relevant for internal React Flow operations. - adoptUserProvidedNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect }); + adoptUserNodes(nodes, nodeLookup, { nodeOrigin, elevateNodesOnSelect }); set({ nodes }); }, @@ -263,21 +256,22 @@ const createRFStore = ({ triggerEdgeChanges(edgeChanges); }, setNodeExtent: (nodeExtent) => { - const { nodes } = get(); + const { nodeLookup } = get(); + + for (const [, node] of nodeLookup) { + const positionAbsolute = clampPosition(node.position, nodeExtent); + + nodeLookup.set(node.id, { + ...node, + internals: { + ...node.internals, + positionAbsolute, + }, + }); + } set({ nodeExtent, - nodes: nodes.map((node) => { - const positionAbsolute = clampPosition(node.position, nodeExtent); - - return { - ...node, - computed: { - ...node.computed, - positionAbsolute, - }, - }; - }), }); }, panBy: (delta): boolean => { diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index 9f373719..cc754976 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -1,7 +1,7 @@ import { infiniteExtent, ConnectionMode, - adoptUserProvidedNodes, + adoptUserNodes, getNodesBounds, getViewportForBounds, Transform, @@ -35,7 +35,7 @@ const getInitialState = ({ const storeNodes = defaultNodes ?? nodes ?? []; updateConnectionLookup(connectionLookup, edgeLookup, storeEdges); - adoptUserProvidedNodes(storeNodes, nodeLookup, { + adoptUserNodes(storeNodes, nodeLookup, { nodeOrigin: [0, 0], elevateNodesOnSelect: false, }); diff --git a/packages/react/src/types/changes.ts b/packages/react/src/types/changes.ts deleted file mode 100644 index adcb484d..00000000 --- a/packages/react/src/types/changes.ts +++ /dev/null @@ -1,71 +0,0 @@ -import type { XYPosition, Dimensions } from '@xyflow/system'; - -import type { Node, Edge } from '.'; - -export type NodeDimensionChange = { - id: string; - type: 'dimensions'; - dimensions?: Dimensions; - resizing?: boolean; -}; - -export type NodePositionChange = { - id: string; - type: 'position'; - position?: XYPosition; - positionAbsolute?: XYPosition; - dragging?: boolean; -}; - -export type NodeSelectionChange = { - id: string; - type: 'select'; - selected: boolean; -}; - -export type NodeRemoveChange = { - id: string; - type: 'remove'; -}; - -export type NodeAddChange = { - item: NodeType; - type: 'add'; -}; - -export type NodeReplaceChange = { - id: string; - item: NodeType; - type: 'replace'; -}; - -/** - * Union type of all possible node changes. - * @public - */ -export type NodeChange = - | NodeDimensionChange - | NodePositionChange - | NodeSelectionChange - | NodeRemoveChange - | NodeAddChange - | NodeReplaceChange; - -export type EdgeSelectionChange = NodeSelectionChange; -export type EdgeRemoveChange = NodeRemoveChange; -export type EdgeAddChange = { - item: EdgeType; - type: 'add'; -}; - -export type EdgeReplaceChange = { - id: string; - item: EdgeType; - type: 'replace'; -}; - -export type EdgeChange = - | EdgeSelectionChange - | EdgeRemoveChange - | EdgeAddChange - | EdgeReplaceChange; diff --git a/packages/react/src/types/general.ts b/packages/react/src/types/general.ts index 302ffeee..8b55920f 100644 --- a/packages/react/src/types/general.ts +++ b/packages/react/src/types/general.ts @@ -12,9 +12,11 @@ import { XYPosition, OnBeforeDeleteBase, Connection, + NodeChange, + EdgeChange, } from '@xyflow/system'; -import type { NodeChange, EdgeChange, Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.'; +import type { Node, Edge, ReactFlowInstance, EdgeProps, NodeProps } from '.'; export type OnNodesChange = (changes: NodeChange[]) => void; export type OnEdgesChange = (changes: EdgeChange[]) => void; diff --git a/packages/react/src/types/index.ts b/packages/react/src/types/index.ts index 6ee8dada..f18123f2 100644 --- a/packages/react/src/types/index.ts +++ b/packages/react/src/types/index.ts @@ -1,6 +1,5 @@ export * from './nodes'; export * from './edges'; -export * from './changes'; export * from './component-props'; export * from './general'; export * from './store'; diff --git a/packages/react/src/types/store.ts b/packages/react/src/types/store.ts index eed7b9a3..416f56f3 100644 --- a/packages/react/src/types/store.ts +++ b/packages/react/src/types/store.ts @@ -25,12 +25,13 @@ import { type EdgeLookup, type ConnectionLookup, type NodeLookup, + NodeChange, + EdgeChange, } from '@xyflow/system'; import type { Edge, Node, - NodeChange, OnNodesChange, OnEdgesChange, DefaultEdgeOptions, @@ -43,7 +44,6 @@ import type { OnNodeDrag, OnBeforeDelete, IsValidConnection, - EdgeChange, InternalNode, } from '.'; diff --git a/packages/react/src/utils/changes.ts b/packages/react/src/utils/changes.ts index e4d0464d..44af2451 100644 --- a/packages/react/src/utils/changes.ts +++ b/packages/react/src/utils/changes.ts @@ -1,14 +1,13 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ -import { EdgeLookup, NodeLookup } from '@xyflow/system'; -import type { - Node, - Edge, +import { + EdgeLookup, + NodeLookup, EdgeChange, NodeChange, NodeSelectionChange, EdgeSelectionChange, - InternalNode, -} from '../types'; +} from '@xyflow/system'; +import type { Node, Edge, InternalNode } from '../types'; // This function applies changes to nodes or edges that are triggered by React Flow internally. // When you drag a node for example, React Flow will send a position change update. @@ -97,9 +96,9 @@ function applyChange(change: any, element: any): any { case 'dimensions': { if (typeof change.dimensions !== 'undefined') { - element.computed ??= {}; - element.computed.width = change.dimensions.width; - element.computed.height = change.dimensions.height; + element.measured ??= {}; + element.measured.width = change.dimensions.width; + element.measured.height = change.dimensions.height; if (change.resizing) { element.width = change.dimensions.width; diff --git a/packages/svelte/src/lib/store/utils.ts b/packages/svelte/src/lib/store/utils.ts index 0f72d746..a6ba38de 100644 --- a/packages/svelte/src/lib/store/utils.ts +++ b/packages/svelte/src/lib/store/utils.ts @@ -7,7 +7,7 @@ import { type Writable } from 'svelte/store'; import { - adoptUserProvidedNodes, + adoptUserNodes, updateConnectionLookup, type Viewport, type PanZoomInstance, @@ -141,7 +141,7 @@ export const createNodesStore = ( let elevateNodesOnSelect = true; const _set = (nds: Node[]): Node[] => { - const nextNodes = adoptUserProvidedNodes(nds, nodeLookup, { + const nextNodes = adoptUserNodes(nds, nodeLookup, { elevateNodesOnSelect, defaults }); diff --git a/packages/system/src/types/general.ts b/packages/system/src/types/general.ts index a7c6665b..2fd0e3ab 100644 --- a/packages/system/src/types/general.ts +++ b/packages/system/src/types/general.ts @@ -127,7 +127,7 @@ export type SelectionRect = Rect & { export type OnError = (id: string, message: string) => void; -export type UpdateNodePositions = (dragItems: NodeDragItem[] | NodeBase[], dragging?: boolean) => void; +export type UpdateNodePositions = (dragItems: NodeDragItem[] | InternalNodeBase[], dragging?: boolean) => void; export type PanBy = (delta: XYPosition) => boolean; export type UpdateConnection = (params: { diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index 2d9dfd5d..7b85fef8 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -59,27 +59,25 @@ export type NodeBase< */ origin?: NodeOrigin; handles?: NodeHandle[]; - computed?: { + measured?: { width?: number; height?: number; }; }; export type InternalNodeBase = NodeType & { - computed: { + measured: { width?: number; height?: number; }; - // Only used internally internals: { positionAbsolute: XYPosition; z: number; // @todo should we rename this to "handles" and use same type as node.handles? isParent: boolean; - /** Holds a reference to the original node object provided by the user - * (which may lack some fields, like `computed` or `[internalSymbol]`. Used - * as an optimization to avoid certain operations. */ - userProvidedNode: NodeType; + /** Holds a reference to the original node object provided by the user. + * Used as an optimization to avoid certain operations. */ + userNode: NodeType; handleBounds?: NodeHandleBounds; }; }; @@ -123,7 +121,7 @@ export type NodeDragItem = { position: XYPosition; // distance from the mouse cursor to the node when start dragging distance: XYPosition; - computed: { + measured: { width: number | null; height: number | null; }; diff --git a/packages/system/src/utils/edges/positions.ts b/packages/system/src/utils/edges/positions.ts index 33fa59ee..de8416db 100644 --- a/packages/system/src/utils/edges/positions.ts +++ b/packages/system/src/utils/edges/positions.ts @@ -20,7 +20,7 @@ function isNodeInitialized(node: InternalNodeBase): boolean { return ( node && !!(node.internals.handleBounds || node.handles?.length) && - !!(node.computed.width || node.width || node.initialWidth) + !!(node.measured.width || node.width || node.initialWidth) ); } diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index 5fab4f49..b8af19bc 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -66,23 +66,23 @@ export const boxToRect = ({ x, y, x2, y2 }: Box): Rect => ({ height: y2 - y, }); -export const nodeToRect = (node: InternalNodeBase, nodeOrigin: NodeOrigin = [0, 0]): Rect => { +export const nodeToRect = (node: InternalNodeBase | NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Rect => { const { positionAbsolute } = getNodePositionWithOrigin(node, node.origin || nodeOrigin); return { ...positionAbsolute, - width: node.computed?.width ?? node.width ?? 0, - height: node.computed?.height ?? node.height ?? 0, + width: node.measured?.width ?? node.width ?? 0, + height: node.measured?.height ?? node.height ?? 0, }; }; -export const nodeToBox = (node: InternalNodeBase, nodeOrigin: NodeOrigin = [0, 0]): Box => { +export const nodeToBox = (node: InternalNodeBase | NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Box => { const { positionAbsolute } = getNodePositionWithOrigin(node, node.origin || nodeOrigin); return { ...positionAbsolute, - x2: positionAbsolute.x + (node.computed?.width ?? node.width ?? 0), - y2: positionAbsolute.y + (node.computed?.height ?? node.height ?? 0), + x2: positionAbsolute.x + (node.measured?.width ?? node.width ?? 0), + y2: positionAbsolute.y + (node.measured?.height ?? node.height ?? 0), }; }; @@ -208,14 +208,14 @@ export function getNodeDimensions( node: NodeType ): { width: number; height: number } { return { - width: node.computed?.width ?? node.width ?? node.initialWidth ?? 0, - height: node.computed?.height ?? node.height ?? node.initialHeight ?? 0, + width: node.measured?.width ?? node.width ?? node.initialWidth ?? 0, + height: node.measured?.height ?? node.height ?? node.initialHeight ?? 0, }; } export function nodeHasDimensions(node: NodeType): boolean { return ( - (node.computed?.width ?? node.width ?? node.initialWidth) !== undefined && - (node.computed?.height ?? node.height ?? node.initialHeight) !== undefined + (node.measured?.width ?? node.width ?? node.initialWidth) !== undefined && + (node.measured?.height ?? node.height ?? node.initialHeight) !== undefined ); } diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 9df39893..c53add40 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -50,7 +50,7 @@ export const isNodeBase = (element: any): export const isInternalNodeBase = ( element: any -): element is NodeType => 'id' in element && 'computed' in element && !('source' in element) && !('target' in element); +): element is NodeType => 'id' in element && 'internals' in element && !('source' in element) && !('target' in element); /** * Pass in a node, and get connected nodes where edge.source === node.id @@ -106,7 +106,7 @@ export const getIncomers = { if (!node) { @@ -133,10 +133,13 @@ export const getNodePositionWithOrigin = ( return { position, - positionAbsolute: { - x: node.internals.positionAbsolute.x - offsetX, - y: node.internals.positionAbsolute.y - offsetY, - }, + positionAbsolute: + 'internals' in node + ? { + x: node.internals.positionAbsolute.x - offsetX, + y: node.internals.positionAbsolute.y - offsetY, + } + : position, }; }; @@ -165,7 +168,6 @@ export const getNodesBounds = ( const box = nodes.reduce( (currBox, node) => { - // @ts-expect-error const nodePos = getNodePositionWithOrigin(node, node.origin || params.nodeOrigin); return getBoundsOfBoxes( currBox, @@ -238,9 +240,9 @@ export const getNodesInside = ( const visibleNodes: NodeType[] = []; for (const [, node] of nodeLookup) { - const { computed, selectable = true, hidden = false } = node; - const width = computed?.width ?? node.width ?? node.initialWidth ?? null; - const height = computed?.height ?? node.height ?? node.initialHeight ?? null; + const { measured, selectable = true, hidden = false } = node; + const width = measured.width ?? node.width ?? node.initialWidth ?? null; + const height = measured.height ?? node.height ?? node.initialHeight ?? null; if ((excludeNonSelectableNodes && !selectable) || hidden) { continue; @@ -286,7 +288,7 @@ export function fitView, Options exte const filteredNodes: InternalNodeBase[] = []; nodeLookup.forEach((n) => { - const isVisible = n.computed?.width && n.computed?.height && (options?.includeHiddenNodes || !n.hidden); + const isVisible = n.measured.width && n.measured.height && (options?.includeHiddenNodes || !n.hidden); if ( isVisible && @@ -331,7 +333,7 @@ function clampNodeExtent( if (!extent || extent === 'parent') { return extent; } - return [extent[0], [extent[1][0] - (node.computed?.width ?? 0), extent[1][1] - (node.computed?.height ?? 0)]]; + return [extent[0], [extent[1][0] - (node.measured?.width ?? 0), extent[1][1] - (node.measured?.height ?? 0)]]; } /** @@ -367,10 +369,10 @@ export function calculateNodePosition({ if (!parentNode) { onError?.('005', errorMessages['error005']()); } else { - const nodeWidth = node.computed.width; - const nodeHeight = node.computed.height; - const parentWidth = parentNode.computed.width; - const parentHeight = parentNode.computed.height; + const nodeWidth = node.measured.width; + const nodeHeight = node.measured.height; + const parentWidth = parentNode.measured.width; + const parentHeight = parentNode.measured.height; if (nodeWidth && nodeHeight && parentWidth && parentHeight) { const currNodeOrigin = node.origin || nodeOrigin; diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index b43534eb..e82e2dea 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -70,7 +70,7 @@ type UpdateNodesOptions = { defaults?: Partial; }; -export function adoptUserProvidedNodes( +export function adoptUserNodes( nodes: NodeType[], nodeLookup: Map>, options: UpdateNodesOptions = { @@ -84,33 +84,31 @@ export function adoptUserProvidedNodes( const selectedNodeZ: number = options?.elevateNodesOnSelect ? 1000 : 0; const parentNodeIds = new Set(); - nodes.forEach((n) => { - const currentStoreNode = tmpLookup.get(n.id); + nodes.forEach((userNode) => { + const currentStoreNode = tmpLookup.get(userNode.id); - if (n.parentNode) { - parentNodeIds.add(n.parentNode); + if (userNode.parentNode) { + parentNodeIds.add(userNode.parentNode); } - if (n === currentStoreNode?.internals.userProvidedNode) { - nodeLookup.set(n.id, currentStoreNode); + if (userNode === currentStoreNode?.internals.userNode) { + nodeLookup.set(userNode.id, currentStoreNode); } else { - const node: InternalNodeBase = { + nodeLookup.set(userNode.id, { ...options.defaults, - ...n, - computed: { - width: n.computed?.width, - height: n.computed?.height, + ...userNode, + measured: { + width: userNode.measured?.width, + height: userNode.measured?.height, }, internals: { - positionAbsolute: n.position, + positionAbsolute: userNode.position, handleBounds: currentStoreNode?.internals?.handleBounds, - z: (isNumeric(n.zIndex) ? n.zIndex : 0) + (n.selected ? selectedNodeZ : 0), - userProvidedNode: n, + z: (isNumeric(userNode.zIndex) ? userNode.zIndex : 0) + (userNode.selected ? selectedNodeZ : 0), + userNode, isParent: false, }, - }; - - nodeLookup.set(node.id, node); + }); } }); @@ -241,14 +239,14 @@ export function updateNodeDimensions( const doUpdate = !!( dimensions.width && dimensions.height && - (node.computed?.width !== dimensions.width || node.computed?.height !== dimensions.height || update.force) + (node.measured?.width !== dimensions.width || node.measured?.height !== dimensions.height || update.force) ); if (doUpdate) { const newNode = { ...node, - computed: { - ...node.computed, + measured: { + ...node.measured, ...dimensions, }, internals: { diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index 7d2fbaef..573b87af 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -139,10 +139,10 @@ export function XYDrag voi if (dragItems.length > 1 && nodeExtent && !n.extent) { const { positionAbsolute } = n.internals; const x1 = positionAbsolute.x - nodesBox.x + nodeExtent[0][0]; - const x2 = positionAbsolute.x + (n.computed?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; + const x2 = positionAbsolute.x + (n.measured?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; const y1 = positionAbsolute.y - nodesBox.y + nodeExtent[0][1]; - const y2 = positionAbsolute.y + (n.computed?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; + const y2 = positionAbsolute.y + (n.measured?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; adjustedNodeExtent = [ [x1, y1], diff --git a/packages/system/src/xydrag/utils.ts b/packages/system/src/xydrag/utils.ts index e958cf64..e4570b6e 100644 --- a/packages/system/src/xydrag/utils.ts +++ b/packages/system/src/xydrag/utils.ts @@ -65,9 +65,9 @@ export function getDragItems( internals: { positionAbsolute: internalNode.internals.positionAbsolute || { x: 0, y: 0 }, }, - computed: { - width: internalNode.computed.width || 0, - height: internalNode.computed.height || 0, + measured: { + width: internalNode.measured.width || 0, + height: internalNode.measured.height || 0, }, }); } @@ -93,8 +93,8 @@ export function getEventHandlerParams({ return { ...node, position: n.position, - computed: { - ...n.computed, + measured: { + ...n.measured, }, }; }); diff --git a/packages/system/src/xyresizer/XYResizer.ts b/packages/system/src/xyresizer/XYResizer.ts index 1953b327..ce1bbd12 100644 --- a/packages/system/src/xyresizer/XYResizer.ts +++ b/packages/system/src/xyresizer/XYResizer.ts @@ -71,15 +71,15 @@ export type XYResizerInstance = { function nodeToParentExtent(node: NodeBase): CoordinateExtent { return [ [0, 0], - [node.computed!.width!, node.computed!.height!], + [node.measured!.width!, node.measured!.height!], ]; } function nodeToChildExtent(child: NodeBase, parent: NodeBase, nodeOrigin: NodeOrigin): CoordinateExtent { const x = parent.position.x + child.position.x; const y = parent.position.y + child.position.y; - const width = child.computed!.width! ?? 0; - const height = child.computed!.height! ?? 0; + const width = child.measured!.width! ?? 0; + const height = child.measured!.height! ?? 0; const originOffsetX = nodeOrigin[0] * width; const originOffsetY = nodeOrigin[1] * height; @@ -121,8 +121,8 @@ export function XYResizer({ domNode, nodeId, getStoreItems, onChange }: XYResize const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); prevValues = { - width: node.computed?.width ?? 0, - height: node.computed?.height ?? 0, + width: node.measured?.width ?? 0, + height: node.measured?.height ?? 0, x: node.position.x ?? 0, y: node.position.y ?? 0, };