From d812dbbe53336f9d794ed7005f59896d42dc12e4 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 21 Nov 2023 15:06:42 +0100 Subject: [PATCH 1/5] refactor(nodes): add computed attr for width/height and absolute position --- .../react/src/examples/EasyConnect/utils.tsx | 15 +++----- .../react/src/examples/FloatingEdges/utils.ts | 4 +- .../MiniMap/MiniMapNodes.tsx | 9 +++-- .../NodeToolbar/NodeToolbar.tsx | 4 +- .../src/components/ConnectionLine/index.tsx | 8 ++-- .../react/src/components/Nodes/wrapNode.tsx | 8 ++-- .../src/container/NodeRenderer/index.tsx | 14 +++---- packages/react/src/hooks/useReactFlow.ts | 2 +- .../react/src/hooks/useUpdateNodePositions.ts | 12 ++++-- packages/react/src/store/index.ts | 7 +++- packages/react/src/store/initialState.ts | 6 +-- packages/react/src/types/nodes.ts | 4 +- packages/react/src/utils/changes.ts | 27 ++++++++----- .../components/NodeWrapper/NodeWrapper.svelte | 6 +-- .../src/lib/components/NodeWrapper/types.ts | 3 +- .../NodeRenderer/NodeRenderer.svelte | 19 ++++++---- .../svelte/src/lib/hooks/useSvelteFlow.ts | 2 +- .../src/lib/hooks/useUpdateNodeInternals.ts | 2 +- .../src/lib/plugins/Minimap/Minimap.svelte | 6 +-- .../src/lib/store/derived-connection-props.ts | 10 +++-- packages/svelte/src/lib/store/index.ts | 17 +++++---- packages/system/src/types/nodes.ts | 12 +++--- packages/system/src/utils/dom.ts | 2 + packages/system/src/utils/edges/positions.ts | 12 +++--- packages/system/src/utils/general.ts | 8 ++-- packages/system/src/utils/graph.ts | 38 ++++++++++--------- packages/system/src/utils/store.ts | 17 ++++++--- packages/system/src/xydrag/XYDrag.ts | 12 +++--- packages/system/src/xydrag/utils.ts | 21 ++++++---- packages/system/src/xyhandle/utils.ts | 4 +- 30 files changed, 177 insertions(+), 134 deletions(-) diff --git a/examples/react/src/examples/EasyConnect/utils.tsx b/examples/react/src/examples/EasyConnect/utils.tsx index cef818f5..d7f3e689 100644 --- a/examples/react/src/examples/EasyConnect/utils.tsx +++ b/examples/react/src/examples/EasyConnect/utils.tsx @@ -4,12 +4,9 @@ import { Node, Position, MarkerType, XYPosition } from '@xyflow/react'; // of the line between the center of the intersectionNode and the target node function getNodeIntersection(intersectionNode: Node, targetNode: Node) { // https://math.stackexchange.com/questions/1724792/an-algorithm-for-finding-the-intersection-point-between-a-center-of-vision-and-a - const { - width: intersectionNodeWidth, - height: intersectionNodeHeight, - positionAbsolute: intersectionNodePosition, - } = intersectionNode; - const targetPosition = targetNode.positionAbsolute!; + const { width: intersectionNodeWidth, height: intersectionNodeHeight } = intersectionNode; + const intersectionNodePosition = intersectionNode.computed?.positionAbsolute!; + const targetPosition = targetNode.computed?.positionAbsolute!; const w = intersectionNodeWidth! / 2; const h = intersectionNodeHeight! / 2; @@ -32,7 +29,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.positionAbsolute, ...node }; + const n = { ...node.computed?.positionAbsolute, ...node }; const nx = Math.round(n.x!); const ny = Math.round(n.y!); const px = Math.round(intersectionPoint.x); @@ -41,13 +38,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) { if (px <= nx + 1) { return Position.Left; } - if (px >= nx + n.width! - 1) { + if (px >= nx + n.computed?.width! - 1) { return Position.Right; } if (py <= ny + 1) { return Position.Top; } - if (py >= n.y! + n.height! - 1) { + if (py >= n.y! + n.computed?.height! - 1) { return Position.Bottom; } diff --git a/examples/react/src/examples/FloatingEdges/utils.ts b/examples/react/src/examples/FloatingEdges/utils.ts index 543b0945..0a2d6c03 100644 --- a/examples/react/src/examples/FloatingEdges/utils.ts +++ b/examples/react/src/examples/FloatingEdges/utils.ts @@ -42,13 +42,13 @@ function getEdgePosition(node: Node, intersectionPoint: XYPosition) { if (px <= nx + 1) { return Position.Left; } - if (px >= nx + (n.width ?? 0) - 1) { + if (px >= nx + (n.computed?.width ?? 0) - 1) { return Position.Right; } if (py <= ny + 1) { return Position.Top; } - if (py >= n.y + (n.height ?? 0) - 1) { + if (py >= n.y + (n.computed?.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 21bb8fc5..d72700a7 100644 --- a/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx +++ b/packages/react/src/additional-components/MiniMap/MiniMapNodes.tsx @@ -12,7 +12,10 @@ import type { MiniMapNodes, GetMiniMapNodeAttribute } from './types'; declare const window: any; const selector = (s: ReactFlowState) => s.nodeOrigin; -const selectorNodes = (s: ReactFlowState) => s.nodes.filter((node) => !node.hidden && node.width && node.height); +const selectorNodes = (s: ReactFlowState) => + s.nodes.filter( + (node) => !node.hidden && (node.computed?.width || node.width) && (node.computed?.height || node.height) + ); const getAttrFunction = (func: any): GetMiniMapNodeAttribute => (func instanceof Function ? func : () => func); function MiniMapNodes({ @@ -44,8 +47,8 @@ function MiniMapNodes({ key={node.id} x={x} y={y} - width={node.width!} - height={node.height!} + width={node.computed?.width ?? node.width ?? 0} + height={node.computed?.height ?? node.height ?? 0} style={node.style} selected={!!node.selected} className={nodeClassNameFunc(node)} diff --git a/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx b/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx index c880edb7..b7028a76 100644 --- a/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx +++ b/packages/react/src/additional-components/NodeToolbar/NodeToolbar.tsx @@ -10,8 +10,8 @@ import NodeToolbarPortal from './NodeToolbarPortal'; import { NodeToolbarProps } from './types'; const nodeEqualityFn = (a: Node | undefined, b: Node | undefined) => - a?.positionAbsolute?.x === b?.positionAbsolute?.x && - a?.positionAbsolute?.y === b?.positionAbsolute?.y && + a?.computed?.positionAbsolute?.x === b?.computed?.positionAbsolute?.x && + a?.computed?.positionAbsolute?.y === b?.computed?.positionAbsolute?.y && a?.width === b?.width && a?.height === b?.height && a?.selected === b?.selected && diff --git a/packages/react/src/components/ConnectionLine/index.tsx b/packages/react/src/components/ConnectionLine/index.tsx index ed1a2115..bdf73089 100644 --- a/packages/react/src/components/ConnectionLine/index.tsx +++ b/packages/react/src/components/ConnectionLine/index.tsx @@ -65,10 +65,10 @@ const ConnectionLine = ({ } const fromHandle = handleId ? handleBounds.find((d) => d.id === handleId) : handleBounds[0]; - const fromHandleX = fromHandle ? fromHandle.x + fromHandle.width / 2 : (fromNode.width ?? 0) / 2; - const fromHandleY = fromHandle ? fromHandle.y + fromHandle.height / 2 : fromNode.height ?? 0; - const fromX = (fromNode.positionAbsolute?.x ?? 0) + fromHandleX; - const fromY = (fromNode.positionAbsolute?.y ?? 0) + fromHandleY; + 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 fromX = (fromNode.computed?.positionAbsolute?.x ?? 0) + fromHandleX; + const fromY = (fromNode.computed?.positionAbsolute?.y ?? 0) + fromHandleY; const fromPosition = fromHandle?.position; const toPosition = fromPosition ? oppositePosition[fromPosition] : null; diff --git a/packages/react/src/components/Nodes/wrapNode.tsx b/packages/react/src/components/Nodes/wrapNode.tsx index e50ffcf7..ab56350b 100644 --- a/packages/react/src/components/Nodes/wrapNode.tsx +++ b/packages/react/src/components/Nodes/wrapNode.tsx @@ -52,8 +52,8 @@ export default (NodeComponent: ComponentType) => { disableKeyboardA11y, ariaLabel, rfId, - sizeWidth, - sizeHeight, + width, + height, }: WrapNodeProps) => { const store = useStoreApi(); const nodeRef = useRef(null); @@ -186,8 +186,8 @@ export default (NodeComponent: ComponentType) => { transform: `translate(${xPosOrigin}px,${yPosOrigin}px)`, pointerEvents: hasPointerEvents ? 'all' : 'none', visibility: initialized ? 'visible' : 'hidden', - width: sizeWidth, - height: sizeHeight, + width, + height, ...style, }} data-id={id} diff --git a/packages/react/src/container/NodeRenderer/index.tsx b/packages/react/src/container/NodeRenderer/index.tsx index 98f9fc39..3797bc45 100644 --- a/packages/react/src/container/NodeRenderer/index.tsx +++ b/packages/react/src/container/NodeRenderer/index.tsx @@ -91,19 +91,19 @@ const NodeRenderer = (props: NodeRendererProps) => { const isFocusable = !!(node.focusable || (nodesFocusable && typeof node.focusable === 'undefined')); const clampedPosition = props.nodeExtent - ? clampPosition(node.positionAbsolute, props.nodeExtent) - : node.positionAbsolute; + ? clampPosition(node.computed?.positionAbsolute, props.nodeExtent) + : node.computed?.positionAbsolute; const posX = clampedPosition?.x ?? 0; const posY = clampedPosition?.y ?? 0; const posOrigin = getPositionWithOrigin({ x: posX, y: posY, - width: node.width ?? 0, - height: node.height ?? 0, + width: node.computed?.width ?? node.width ?? 0, + height: node.computed?.height ?? node.height ?? 0, origin: node.origin || props.nodeOrigin, }); - const initialized = (!!node.width && !!node.height) || (!!node.size?.width && !!node.size?.height); + const initialized = (!!node.computed?.width && !!node.computed?.height) || (!!node.width && !!node.height); return ( { id={node.id} className={node.className} style={node.style} - sizeWidth={node.size?.width} - sizeHeight={node.size?.height} + width={node.width ?? undefined} + height={node.height ?? undefined} type={nodeType} data={node.data} sourcePosition={node.sourcePosition || Position.Bottom} diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 257dd0f2..44e29c14 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -206,7 +206,7 @@ export default function useReactFlow(): ReactFlo } return (nodes || store.getState().nodes).filter((n) => { - if (!isRect && (n.id === node!.id || !n.positionAbsolute)) { + if (!isRect && (n.id === node!.id || !n.computed?.positionAbsolute)) { return false; } diff --git a/packages/react/src/hooks/useUpdateNodePositions.ts b/packages/react/src/hooks/useUpdateNodePositions.ts index d303fb9b..06577601 100644 --- a/packages/react/src/hooks/useUpdateNodePositions.ts +++ b/packages/react/src/hooks/useUpdateNodePositions.ts @@ -23,8 +23,11 @@ function useUpdateNodePositions() { const yDiff = params.y * yVelo * factor; const nodeUpdates = selectedNodes.map((node) => { - if (node.positionAbsolute) { - let nextPosition = { x: node.positionAbsolute.x + xDiff, y: node.positionAbsolute.y + yDiff }; + if (node.computed?.positionAbsolute) { + let nextPosition = { + x: node.computed?.positionAbsolute.x + xDiff, + y: node.computed?.positionAbsolute.y + yDiff, + }; if (snapToGrid) { nextPosition = snapPosition(nextPosition, snapGrid); @@ -40,7 +43,10 @@ function useUpdateNodePositions() { ); node.position = position; - node.positionAbsolute = positionAbsolute; + if (!node.computed) { + node.computed = {}; + } + node.computed.positionAbsolute = positionAbsolute; } return node; diff --git a/packages/react/src/store/index.ts b/packages/react/src/store/index.ts index a6898f6d..0f50f385 100644 --- a/packages/react/src/store/index.ts +++ b/packages/react/src/store/index.ts @@ -147,7 +147,7 @@ const createRFStore = ({ }; if (positionChanged) { - change.positionAbsolute = node.positionAbsolute; + change.positionAbsolute = node.computed?.positionAbsolute; change.position = node.position; } @@ -276,7 +276,10 @@ const createRFStore = ({ return { ...node, - positionAbsolute, + computed: { + ...node.computed, + positionAbsolute, + }, }; }), }); diff --git a/packages/react/src/store/initialState.ts b/packages/react/src/store/initialState.ts index c87c29f1..48e5434f 100644 --- a/packages/react/src/store/initialState.ts +++ b/packages/react/src/store/initialState.ts @@ -28,11 +28,7 @@ const getInitialState = ({ let transform: Transform = [0, 0, 1]; if (fitView && width && height) { - const nodesWithDimensions = nextNodes.map((node) => ({ - ...node, - width: node.size?.width, - height: node.size?.height, - })); + const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height); const bounds = getNodesBounds(nodesWithDimensions, [0, 0]); const { x, y, zoom } = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1); transform = [x, y, zoom]; diff --git a/packages/react/src/types/nodes.ts b/packages/react/src/types/nodes.ts index 4e648f8e..ab691af5 100644 --- a/packages/react/src/types/nodes.ts +++ b/packages/react/src/types/nodes.ts @@ -40,6 +40,6 @@ export type WrapNodeProps = Pick< noPanClassName: string; rfId: string; disableKeyboardA11y: boolean; - sizeWidth?: number; - sizeHeight?: number; + width?: number; + height?: number; }; diff --git a/packages/react/src/utils/changes.ts b/packages/react/src/utils/changes.ts index 61ff2b19..c113ebe1 100644 --- a/packages/react/src/utils/changes.ts +++ b/packages/react/src/utils/changes.ts @@ -5,14 +5,17 @@ export function handleParentExpand(res: any[], updateItem: any) { const parent = res.find((e) => e.id === updateItem.parentNode); if (parent) { - const extendWidth = updateItem.position.x + updateItem.width - parent.width; - const extendHeight = updateItem.position.y + updateItem.height - parent.height; + if (!parent.computed) { + parent.computed = {}; + } + const extendWidth = updateItem.position.x + updateItem.computed.width - parent.computed.width; + const extendHeight = updateItem.position.y + updateItem.computed.height - parent.computed.height; if (extendWidth > 0 || extendHeight > 0 || updateItem.position.x < 0 || updateItem.position.y < 0) { parent.style = { ...parent.style } || {}; - parent.style.width = parent.style.width ?? parent.width; - parent.style.height = parent.style.height ?? parent.height; + parent.style.width = parent.style.width ?? parent.computed.width; + parent.style.height = parent.style.height ?? parent.computed.height; if (extendWidth > 0) { parent.style.width += extendWidth; @@ -36,8 +39,8 @@ export function handleParentExpand(res: any[], updateItem: any) { updateItem.position.y = 0; } - parent.width = parent.style.width; - parent.height = parent.style.height; + parent.computed.width = parent.style.width; + parent.computed.height = parent.style.height; } } } @@ -87,7 +90,10 @@ function applyChanges(changes: any[], elements: any[]): any[] { } if (typeof currentChange.positionAbsolute !== 'undefined') { - updateItem.positionAbsolute = currentChange.positionAbsolute; + if (!updateItem.computed) { + updateItem.computed = {}; + } + updateItem.computed.positionAbsolute = currentChange.positionAbsolute; } if (typeof currentChange.dragging !== 'undefined') { @@ -101,8 +107,11 @@ function applyChanges(changes: any[], elements: any[]): any[] { } case 'dimensions': { if (typeof currentChange.dimensions !== 'undefined') { - updateItem.width = currentChange.dimensions.width; - updateItem.height = currentChange.dimensions.height; + if (!updateItem.computed) { + updateItem.computed = {}; + } + updateItem.computed.width = currentChange.dimensions.width; + updateItem.computed.height = currentChange.dimensions.height; } if (typeof currentChange.updateStyle !== 'undefined') { diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index 33a954b3..eb2c1472 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -34,6 +34,8 @@ export let sourcePosition: NodeWrapperProps['sourcePosition'] = undefined; export let targetPosition: NodeWrapperProps['targetPosition'] = undefined; export let zIndex: NodeWrapperProps['zIndex']; + export let width: NodeWrapperProps['width'] = undefined; + export let height: NodeWrapperProps['height'] = undefined; export let dragHandle: NodeWrapperProps['dragHandle'] = undefined; export let initialized: NodeWrapperProps['initialized'] = false; let className: string = ''; @@ -168,9 +170,7 @@ style:z-index={zIndex} style:transform="translate({positionOriginX}px, {positionOriginY}px)" style:visibility={initialized ? 'visible' : 'hidden'} - style="{style} {node.size?.width ? `;width=${node.size?.width}px` : ''} {node.size?.height - ? `;height=${node.size?.height}px;` - : ''}" + style="{style} {width ? `;width=${width}px` : ''} {height ? `;height=${height}px;` : ''}" on:click={onSelectNodeHandler} on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })} on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })} diff --git a/packages/svelte/src/lib/components/NodeWrapper/types.ts b/packages/svelte/src/lib/components/NodeWrapper/types.ts index e833ef54..3da65c2f 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/types.ts +++ b/packages/svelte/src/lib/components/NodeWrapper/types.ts @@ -11,14 +11,15 @@ export type NodeWrapperProps = Pick< | 'selected' | 'selectable' | 'style' - | 'type' | 'width' | 'height' + | 'type' | 'sourcePosition' | 'targetPosition' | 'dragHandle' | 'hidden' > & { + type: string; positionX: number; positionY: number; positionOriginX: number; diff --git a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte index e856b70b..48ec792b 100644 --- a/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte +++ b/packages/svelte/src/lib/container/NodeRenderer/NodeRenderer.svelte @@ -40,10 +40,10 @@
{#each $visibleNodes as node (node.id)} {@const posOrigin = getPositionWithOrigin({ - x: node.positionAbsolute?.x ?? 0, - y: node.positionAbsolute?.y ?? 0, - width: (node.size?.width || node.width) ?? 0, - height: (node.size?.height || node.height) ?? 0, + x: node.computed?.positionAbsolute?.x ?? 0, + y: node.computed?.positionAbsolute?.y ?? 0, + width: node.computed?.width ?? node.width ?? 0, + height: node.computed?.height ?? node.height ?? 0, origin: node.origin })} { - if (!isRect && (n.id === node.id || !n.positionAbsolute)) { + if (!isRect && (n.id === node.id || !n.computed?.positionAbsolute)) { return false; } diff --git a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts index c66bb688..dad8ed88 100644 --- a/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts +++ b/packages/svelte/src/lib/hooks/useUpdateNodeInternals.ts @@ -1,5 +1,5 @@ import { get } from 'svelte/store'; -import type { UpdateNodeInternals, NodeDimensionUpdate } from '@xyflow/system'; +import type { UpdateNodeInternals } from '@xyflow/system'; import { useStore } from '$lib/store'; diff --git a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte index f3fba426..b331a04e 100644 --- a/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte +++ b/packages/svelte/src/lib/plugins/Minimap/Minimap.svelte @@ -114,13 +114,13 @@ {#if ariaLabel}{ariaLabel}{/if} {#each $nodes as node (node.id)} - {#if node.width && node.height} + {#if (node.computed?.width || node?.width) && (node.computed?.height || node.height)} {@const pos = getNodePositionWithOrigin(node).positionAbsolute} { store.nodes.update((nds) => { - return nds.map((n) => { + return nds.map((node) => { const nodeDragItem = (nodeDragItems as Array).find( - (ndi) => ndi.id === n.id + (ndi) => ndi.id === node.id ); if (nodeDragItem) { return { - ...n, - [internalsSymbol]: n[internalsSymbol], + ...node, dragging, - positionAbsolute: nodeDragItem.positionAbsolute, - position: nodeDragItem.position + position: nodeDragItem.position, + computed: { + ...node.computed, + positionAbsolute: nodeDragItem.computed?.positionAbsolute + }, + [internalsSymbol]: node[internalsSymbol] }; } - return n; + return node; }); }); }; diff --git a/packages/system/src/types/nodes.ts b/packages/system/src/types/nodes.ts index cea41c65..1e354a55 100644 --- a/packages/system/src/types/nodes.ts +++ b/packages/system/src/types/nodes.ts @@ -25,14 +25,14 @@ export type NodeBase zIndex?: number; extent?: 'parent' | CoordinateExtent; expandParent?: boolean; - positionAbsolute?: XYPosition; ariaLabel?: string; focusable?: boolean; origin?: NodeOrigin; handles?: NodeHandle[]; - size?: { + computed?: { width?: number; height?: number; + positionAbsolute?: XYPosition; }; // only used internally @@ -78,11 +78,13 @@ export type NodeBounds = XYPosition & { export type NodeDragItem = { id: string; position: XYPosition; - positionAbsolute: XYPosition; // distance from the mouse cursor to the node when start dragging distance: XYPosition; - width?: number | null; - height?: number | null; + computed: { + width: number | null; + height: number | null; + positionAbsolute: XYPosition; + }; extent?: 'parent' | CoordinateExtent; parentNode?: string; dragging?: boolean; diff --git a/packages/system/src/utils/dom.ts b/packages/system/src/utils/dom.ts index 74bb14aa..3cf5cd61 100644 --- a/packages/system/src/utils/dom.ts +++ b/packages/system/src/utils/dom.ts @@ -73,6 +73,8 @@ export const getHandleBounds = ( } const handlesArray = Array.from(handles) as HTMLDivElement[]; + + // @todo can't we use the node dimensions here? const nodeBounds = nodeElement.getBoundingClientRect(); const nodeOffset = { x: nodeBounds.width * nodeOrigin[0], diff --git a/packages/system/src/utils/edges/positions.ts b/packages/system/src/utils/edges/positions.ts index 8159d46b..35e07834 100644 --- a/packages/system/src/utils/edges/positions.ts +++ b/packages/system/src/utils/edges/positions.ts @@ -88,20 +88,20 @@ function toHandleBounds(handles?: NodeHandle[]) { function getHandleDataByNode(node?: NodeBase): [Rect, NodeHandleBounds | null, boolean] { const handleBounds = node?.[internalsSymbol]?.handleBounds || toHandleBounds(node?.handles) || null; - const nodeWidth = node?.width || node?.size?.width; - const nodeHeight = node?.height || node?.size?.height; + const nodeWidth = node?.computed?.width || node?.width; + const nodeHeight = node?.computed?.height || node?.height; const isValid = handleBounds && nodeWidth && nodeHeight && - typeof node?.positionAbsolute?.x !== 'undefined' && - typeof node?.positionAbsolute?.y !== 'undefined'; + typeof node?.computed?.positionAbsolute?.x !== 'undefined' && + typeof node?.computed?.positionAbsolute?.y !== 'undefined'; return [ { - x: node?.positionAbsolute?.x || 0, - y: node?.positionAbsolute?.y || 0, + x: node?.computed?.positionAbsolute?.x || 0, + y: node?.computed?.positionAbsolute?.y || 0, width: nodeWidth || 0, height: nodeHeight || 0, }, diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index 976d105f..2db59322 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -64,8 +64,8 @@ export const nodeToRect = (node: NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Rec return { ...positionAbsolute, - width: node.width || 0, - height: node.height || 0, + width: node.computed?.width ?? node.width ?? 0, + height: node.computed?.height ?? node.height ?? 0, }; }; @@ -74,8 +74,8 @@ export const nodeToBox = (node: NodeBase, nodeOrigin: NodeOrigin = [0, 0]): Box return { ...positionAbsolute, - x2: positionAbsolute.x + (node.width || 0), - y2: positionAbsolute.y + (node.height || 0), + x2: positionAbsolute.x + (node.computed?.width ?? node.width ?? 0), + y2: positionAbsolute.y + (node.computed?.height ?? node.height ?? 0), }; }; diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index 43a80ef6..c2e276ae 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -86,8 +86,8 @@ export const getNodePositionWithOrigin = ( }; } - const offsetX = (node.width ?? 0) * nodeOrigin[0]; - const offsetY = (node.height ?? 0) * nodeOrigin[1]; + const offsetX = (node.computed?.width ?? node.width ?? 0) * nodeOrigin[0]; + const offsetY = (node.computed?.height ?? node.height ?? 0) * nodeOrigin[1]; const position: XYPosition = { x: node.position.x - offsetX, @@ -96,10 +96,10 @@ export const getNodePositionWithOrigin = ( return { ...position, - positionAbsolute: node.positionAbsolute + positionAbsolute: node.computed?.positionAbsolute ? { - x: node.positionAbsolute.x - offsetX, - y: node.positionAbsolute.y - offsetY, + x: node.computed.positionAbsolute.x - offsetX, + y: node.computed.positionAbsolute.y - offsetY, } : position, }; @@ -118,8 +118,8 @@ export const getNodesBounds = (nodes: NodeBase[], nodeOrigin: NodeOrigin = [0, 0 rectToBox({ x, y, - width: node.width || 0, - height: node.height || 0, + width: node.computed?.width ?? node.width ?? 0, + height: node.computed?.height ?? node.height ?? 0, }) ); }, @@ -145,17 +145,19 @@ export const getNodesInside = ( }; const visibleNodes = nodes.reduce((res, node) => { - const { width, height, selectable = true, hidden = false } = node; + const { computed, selectable = true, hidden = false } = node; + const width = computed?.width ?? node.width ?? null; + const height = computed?.height ?? node.height ?? null; if ((excludeNonSelectableNodes && !selectable) || hidden) { return res; } const overlappingArea = getOverlappingArea(paneRect, nodeToRect(node, nodeOrigin)); - const notInitialized = width === undefined || height === undefined || width === null || height === null; + const notInitialized = width === null || height === null; const partiallyVisible = partially && overlappingArea > 0; - const area = (width || 0) * (height || 0); + const area = (width ?? 0) * (height ?? 0); const isVisible = notInitialized || partiallyVisible || overlappingArea >= area; if (isVisible || node.dragging) { @@ -185,7 +187,7 @@ export function fitView, Options exte options?: Options ) { const filteredNodes = nodes.filter((n) => { - const isVisible = n.width && n.height && (options?.includeHiddenNodes || !n.hidden); + const isVisible = n.computed?.width && n.computed?.height && (options?.includeHiddenNodes || !n.hidden); if (options?.nodes?.length) { return isVisible && options?.nodes.some((optionNode) => optionNode.id === n.id); @@ -218,7 +220,7 @@ function clampNodeExtent(node: NodeDragItem | NodeBase, extent?: CoordinateExten if (!extent || extent === 'parent') { return extent; } - return [extent[0], [extent[1][0] - (node.width || 0), extent[1][1] - (node.height || 0)]]; + return [extent[0], [extent[1][0] - (node.computed?.width ?? 0), extent[1][1] - (node.computed?.height ?? 0)]]; } export function calcNextPosition( @@ -242,16 +244,18 @@ export function calcNextPosition( } if (node.extent === 'parent' && !node.expandParent) { - if (node.parentNode && node.width && node.height) { + const nodeWidth = node.computed?.width; + const nodeHeight = node.computed?.height; + if (node.parentNode && nodeWidth && nodeHeight) { const currNodeOrigin = node.origin || nodeOrigin; currentExtent = - parentNode && isNumeric(parentNode.width) && isNumeric(parentNode.height) + parentNode && isNumeric(parentNode.computed?.width) && isNumeric(parentNode.computed?.height) ? [ - [parentPos.x + node.width * currNodeOrigin[0], parentPos.y + node.height * currNodeOrigin[1]], + [parentPos.x + nodeWidth * currNodeOrigin[0], parentPos.y + nodeHeight * currNodeOrigin[1]], [ - parentPos.x + parentNode.width - node.width + node.width * currNodeOrigin[0], - parentPos.y + parentNode.height - node.height + node.height * currNodeOrigin[1], + parentPos.x + parentNode.computed?.width - nodeWidth + nodeWidth * currNodeOrigin[0], + parentPos.y + parentNode.computed?.height - nodeHeight + nodeHeight * currNodeOrigin[1], ], ] : currentExtent; diff --git a/packages/system/src/utils/store.ts b/packages/system/src/utils/store.ts index 9ed4f871..eca6b8de 100644 --- a/packages/system/src/utils/store.ts +++ b/packages/system/src/utils/store.ts @@ -40,7 +40,7 @@ export function updateAbsolutePositions( parentNode?.origin || nodeOrigin ); - node.positionAbsolute = { + node.computed!.positionAbsolute = { x, y, }; @@ -79,9 +79,11 @@ export function updateNodes( const node: NodeType = { ...options.defaults, ...n, - positionAbsolute: n.position, - width: n.width || currentStoreNode?.width, - height: n.height || currentStoreNode?.height, + computed: { + positionAbsolute: n.position, + width: n.computed?.width || currentStoreNode?.computed?.width, + height: n.computed?.height || currentStoreNode?.computed?.height, + }, }; const z = (isNumeric(n.zIndex) ? n.zIndex : 0) + (n.selected ? selectedNodeZ : 0); const currInternals = n?.[internalsSymbol] || currentStoreNode?.[internalsSymbol]; @@ -160,7 +162,7 @@ export function updateNodeDimensions( const doUpdate = !!( dimensions.width && dimensions.height && - (node.width !== dimensions.width || node.height !== dimensions.height || update.forceUpdate) + (node.computed?.width !== dimensions.width || node.computed?.height !== dimensions.height || update.forceUpdate) ); if (doUpdate) { @@ -168,7 +170,10 @@ export function updateNodeDimensions( const newNode = { ...node, - ...dimensions, + computed: { + ...node.computed, + ...dimensions, + }, [internalsSymbol]: { ...node[internalsSymbol], handleBounds: { diff --git a/packages/system/src/xydrag/XYDrag.ts b/packages/system/src/xydrag/XYDrag.ts index 69d50b41..79a0823a 100644 --- a/packages/system/src/xydrag/XYDrag.ts +++ b/packages/system/src/xydrag/XYDrag.ts @@ -140,11 +140,13 @@ export function XYDrag({ ]; if (dragItems.length > 1 && nodeExtent && !n.extent) { - adjustedNodeExtent[0][0] = n.positionAbsolute.x - nodesBox.x + nodeExtent[0][0]; - adjustedNodeExtent[1][0] = n.positionAbsolute.x + (n.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; + adjustedNodeExtent[0][0] = n.computed.positionAbsolute.x - nodesBox.x + nodeExtent[0][0]; + adjustedNodeExtent[1][0] = + n.computed.positionAbsolute.x + (n.computed?.width ?? 0) - nodesBox.x2 + nodeExtent[1][0]; - adjustedNodeExtent[0][1] = n.positionAbsolute.y - nodesBox.y + nodeExtent[0][1]; - adjustedNodeExtent[1][1] = n.positionAbsolute.y + (n.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; + adjustedNodeExtent[0][1] = n.computed.positionAbsolute.y - nodesBox.y + nodeExtent[0][1]; + adjustedNodeExtent[1][1] = + n.computed.positionAbsolute.y + (n.computed?.height ?? 0) - nodesBox.y2 + nodeExtent[1][1]; } const updatedPos = calcNextPosition(n, nextPosition, nodes, adjustedNodeExtent, nodeOrigin, onError); @@ -153,7 +155,7 @@ export function XYDrag({ hasChange = hasChange || n.position.x !== updatedPos.position.x || n.position.y !== updatedPos.position.y; n.position = updatedPos.position; - n.positionAbsolute = updatedPos.positionAbsolute; + n.computed.positionAbsolute = updatedPos.positionAbsolute; return n; }); diff --git a/packages/system/src/xydrag/utils.ts b/packages/system/src/xydrag/utils.ts index 274c2213..15c9af68 100644 --- a/packages/system/src/xydrag/utils.ts +++ b/packages/system/src/xydrag/utils.ts @@ -51,10 +51,9 @@ export function getDragItems( .map((n) => ({ id: n.id, position: n.position || { x: 0, y: 0 }, - positionAbsolute: n.positionAbsolute || { x: 0, y: 0 }, distance: { - x: mousePos.x - (n.positionAbsolute?.x ?? 0), - y: mousePos.y - (n.positionAbsolute?.y ?? 0), + x: mousePos.x - (n.computed?.positionAbsolute?.x ?? 0), + y: mousePos.y - (n.computed?.positionAbsolute?.y ?? 0), }, delta: { x: 0, @@ -62,10 +61,13 @@ export function getDragItems( }, extent: n.extent, parentNode: n.parentNode, - width: n.width, - height: n.height, origin: n.origin, expandParent: n.expandParent, + computed: { + positionAbsolute: n.computed?.positionAbsolute || { x: 0, y: 0 }, + width: n.computed?.width || 0, + height: n.computed?.height || 0, + }, })); } @@ -81,15 +83,18 @@ export function getEventHandlerParams({ dragItems: NodeDragItem[]; nodeLookup: Map; }): [NodeType, NodeType[]] { - const extentedDragItems: NodeType[] = dragItems.map((n) => { + const nodesFromDragItems: NodeType[] = dragItems.map((n) => { const node = nodeLookup.get(n.id)!; return { ...node, position: n.position, - positionAbsolute: n.positionAbsolute, + computed: { + ...n.computed, + positionAbsolute: n.computed.positionAbsolute, + }, }; }); - return [nodeId ? extentedDragItems.find((n) => n.id === nodeId)! : extentedDragItems[0], extentedDragItems]; + return [nodeId ? nodesFromDragItems.find((n) => n.id === nodeId)! : nodesFromDragItems[0], nodesFromDragItems]; } diff --git a/packages/system/src/xyhandle/utils.ts b/packages/system/src/xyhandle/utils.ts index 35b17601..b829f691 100644 --- a/packages/system/src/xyhandle/utils.ts +++ b/packages/system/src/xyhandle/utils.ts @@ -22,8 +22,8 @@ export function getHandles( id: h.id || null, type, nodeId: node.id, - x: (node.positionAbsolute?.x ?? 0) + h.x + h.width / 2, - y: (node.positionAbsolute?.y ?? 0) + h.y + h.height / 2, + x: (node.computed?.positionAbsolute?.x ?? 0) + h.x + h.width / 2, + y: (node.computed?.positionAbsolute?.y ?? 0) + h.y + h.height / 2, }); } return res; From 24c999a335186cd457b38f99e4ef9590447134df Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 21 Nov 2023 15:18:35 +0100 Subject: [PATCH 2/5] chore(astro): use width/height attrs --- .../src/components/ReactFlowExample/index.tsx | 6 ++--- .../components/SvelteFlowExample/index.svelte | 24 +++++++------------ .../components/NodeWrapper/NodeWrapper.svelte | 4 +++- .../svelte/src/lib/store/initial-store.ts | 6 +---- 4 files changed, 15 insertions(+), 25 deletions(-) diff --git a/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx b/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx index ae5a5c87..ca21eeb7 100644 --- a/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx +++ b/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx @@ -27,7 +27,7 @@ const initialNodes: Node[] = [ type: 'input', data: { label: 'Node 1' }, position: { x: 250, y: 5 }, - size: nodeSize, + ...nodeSize, handles: [ { type: 'source', @@ -41,7 +41,7 @@ const initialNodes: Node[] = [ id: '2', data: { label: 'Node 2' }, position: { x: 100, y: 100 }, - size: nodeSize, + ...nodeSize, handles: [ { type: 'source', @@ -65,7 +65,7 @@ const initialNodes: Node[] = [ id: '3', data: { label: 'Node 3' }, position: { x: 400, y: 100 }, - size: nodeSize, + ...nodeSize, handles: [ { type: 'source', diff --git a/examples/astro-xyflow/src/components/SvelteFlowExample/index.svelte b/examples/astro-xyflow/src/components/SvelteFlowExample/index.svelte index cfa8a449..73196d53 100644 --- a/examples/astro-xyflow/src/components/SvelteFlowExample/index.svelte +++ b/examples/astro-xyflow/src/components/SvelteFlowExample/index.svelte @@ -11,10 +11,8 @@ data: { label: 'Node 0' }, sourcePosition: Position.Right, targetPosition: Position.Left, - size: { - width: 100, - height: 40, - }, + width: 100, + height: 40, handles: [ { type: 'source', x: 100, y: 20, position: Position.Right }, { type: 'target', x: 0, y: 20, position: Position.Left }, @@ -26,10 +24,8 @@ data: { label: 'A' }, sourcePosition: Position.Right, targetPosition: Position.Left, - size: { - width: 100, - height: 40, - }, + width: 100, + height: 40, handles: [ { type: 'source', x: 100, y: 20, position: Position.Right }, { type: 'target', x: 0, y: 20, position: Position.Left }, @@ -41,10 +37,8 @@ data: { label: 'B' }, sourcePosition: Position.Right, targetPosition: Position.Left, - size: { - width: 100, - height: 40, - }, + width: 100, + height: 40, handles: [ { type: 'source', x: 100, y: 20, position: Position.Right }, { type: 'target', x: 0, y: 20, position: Position.Left }, @@ -56,10 +50,8 @@ data: { label: 'C' }, sourcePosition: Position.Right, targetPosition: Position.Left, - size: { - width: 100, - height: 40, - }, + width: 100, + height: 40, handles: [ { type: 'source', x: 100, y: 20, position: Position.Right }, { type: 'target', x: 0, y: 20, position: Position.Left }, diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index eb2c1472..a59e522d 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -170,7 +170,9 @@ style:z-index={zIndex} style:transform="translate({positionOriginX}px, {positionOriginY}px)" style:visibility={initialized ? 'visible' : 'hidden'} - style="{style} {width ? `;width=${width}px` : ''} {height ? `;height=${height}px;` : ''}" + style:width={width === undefined ? undefined : `${width}px`} + style:height={height === undefined ? undefined : `${height}px`} + {style} on:click={onSelectNodeHandler} on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })} on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })} diff --git a/packages/svelte/src/lib/store/initial-store.ts b/packages/svelte/src/lib/store/initial-store.ts index d6b621d9..dbbb17fc 100644 --- a/packages/svelte/src/lib/store/initial-store.ts +++ b/packages/svelte/src/lib/store/initial-store.ts @@ -76,11 +76,7 @@ export const getInitialStore = ({ let viewport: Viewport = { x: 0, y: 0, zoom: 1 }; if (fitView && width && height) { - const nodesWithDimensions = nextNodes.map((node) => ({ - ...node, - width: node.size?.width, - height: node.size?.height - })); + const nodesWithDimensions = nextNodes.filter((node) => node.width && node.height); const bounds = getNodesBounds(nodesWithDimensions, [0, 0]); viewport = getViewportForBounds(bounds, width, height, 0.5, 2, 0.1); } From a166129547227284cde5e5031deb48d122498691 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 21 Nov 2023 16:03:07 +0100 Subject: [PATCH 3/5] fix(svelte): use node.style.width and node.style --- .../src/components/ReactFlowExample/index.tsx | 8 -------- .../src/lib/components/NodeWrapper/NodeWrapper.svelte | 6 +++--- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx b/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx index ca21eeb7..4f8ef0ab 100644 --- a/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx +++ b/examples/astro-xyflow/src/components/ReactFlowExample/index.tsx @@ -48,16 +48,12 @@ const initialNodes: Node[] = [ position: Position.Bottom, x: nodeSize.width * 0.5, y: nodeSize.height, - width: 1, - height: 1, }, { type: 'target', position: Position.Top, x: nodeSize.width * 0.5, y: 0, - width: 1, - height: 1, }, ], }, @@ -72,16 +68,12 @@ const initialNodes: Node[] = [ position: Position.Bottom, x: nodeSize.width * 0.5, y: nodeSize.height, - width: 1, - height: 1, }, { type: 'target', position: Position.Top, x: nodeSize.width * 0.5, y: 0, - width: 1, - height: 1, }, ], }, diff --git a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte index a59e522d..335bff71 100644 --- a/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte +++ b/packages/svelte/src/lib/components/NodeWrapper/NodeWrapper.svelte @@ -170,9 +170,9 @@ style:z-index={zIndex} style:transform="translate({positionOriginX}px, {positionOriginY}px)" style:visibility={initialized ? 'visible' : 'hidden'} - style:width={width === undefined ? undefined : `${width}px`} - style:height={height === undefined ? undefined : `${height}px`} - {style} + style="{style ?? ''}; {!width ? '' : `width:${width}px;`} {!height + ? '' + : `height:${height}px;`}" on:click={onSelectNodeHandler} on:mouseenter={(event) => dispatch('nodemouseenter', { node, event })} on:mouseleave={(event) => dispatch('nodemouseleave', { node, event })} From 39b0deb9a45e94c971198deaae1a7f85512fa171 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 21 Nov 2023 16:08:13 +0100 Subject: [PATCH 4/5] fix(react): node resizer use new width/height attr --- .../src/additional-components/NodeResizer/ResizeControl.tsx | 4 ++-- packages/system/src/utils/graph.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx b/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx index 36783fcf..1df32e06 100644 --- a/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx +++ b/packages/react/src/additional-components/NodeResizer/ResizeControl.tsx @@ -70,8 +70,8 @@ function ResizeControl({ const { xSnapped, ySnapped } = getPointerPosition(event.sourceEvent, { transform, snapGrid, snapToGrid }); prevValues.current = { - width: node?.width ?? 0, - height: node?.height ?? 0, + width: node?.computed?.width ?? 0, + height: node?.computed?.height ?? 0, x: node?.position.x ?? 0, y: node?.position.y ?? 0, }; diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index c2e276ae..fe50e1be 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -254,8 +254,8 @@ export function calcNextPosition( ? [ [parentPos.x + nodeWidth * currNodeOrigin[0], parentPos.y + nodeHeight * currNodeOrigin[1]], [ - parentPos.x + parentNode.computed?.width - nodeWidth + nodeWidth * currNodeOrigin[0], - parentPos.y + parentNode.computed?.height - nodeHeight + nodeHeight * currNodeOrigin[1], + parentPos.x + parentNode.computed.width - nodeWidth + nodeWidth * currNodeOrigin[0], + parentPos.y + parentNode.computed.height - nodeHeight + nodeHeight * currNodeOrigin[1], ], ] : currentExtent; From 51e98ea44d29e7a6607dfbb8cabd09c9338436f2 Mon Sep 17 00:00:00 2001 From: moklick Date: Tue, 21 Nov 2023 16:37:38 +0100 Subject: [PATCH 5/5] fix(svelte): node toolbar use new width/height attrs --- .../svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte b/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte index b01bbb48..fab6ce2d 100644 --- a/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte +++ b/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte @@ -51,10 +51,11 @@ let nodeRect: Rect | undefined = undefined; if (toolbarNodes.length === 1) { + const toolbarNode = toolbarNodes[0]; nodeRect = { - ...toolbarNodes[0].position, - width: toolbarNodes[0].width ?? 0, - height: toolbarNodes[0].height ?? 0 + ...toolbarNode.position, + width: toolbarNode.computed?.width ?? toolbarNode.width ?? 0, + height: toolbarNode.computed?.height ?? toolbarNode.height ?? 0 }; } else if (toolbarNodes.length > 1) { nodeRect = getNodesBounds(toolbarNodes, $nodeOrigin);