diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index b210cd0c..bf119621 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,11 @@ # @xyflow/react +## 12.0.0-next.xx + +## Patch changes + +- use correct positions for intersection helpers + ## 12.0.0-next.14 ## Patch changes diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 6be3f90b..31035bd1 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -1,5 +1,12 @@ import { useCallback, useMemo, useRef, useState } from 'react'; -import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system'; +import { + evaluateNodePosition, + getElementsToRemove, + getOverlappingArea, + isRectObject, + nodeToRect, + type Rect, +} from '@xyflow/system'; import useViewportHelper from './useViewportHelper'; import { useStoreApi } from './useStore'; @@ -223,15 +230,19 @@ export function useReactFlow { - const internalNode = store.getState().nodeLookup.get(id); - return internalNode ? nodeToRect(internalNode) : null; + const getNodeRect = useCallback((node: NodeType | { id: string }): Rect | null => { + const { nodeLookup, nodeOrigin } = store.getState(); + const nodeToUse = isNode(node) ? node : nodeLookup.get(node.id)!; + const nodeWithPos = evaluateNodePosition(nodeToUse, nodeLookup, nodeOrigin); + + return nodeWithPos ? nodeToRect(nodeWithPos) : null; }, []); const getIntersectingNodes = useCallback>( (nodeOrRect, partially = true, nodes) => { const isRect = isRectObject(nodeOrRect); const nodeRect = isRect ? nodeOrRect : getNodeRect(nodeOrRect); + const hasNodesOption = nodes !== undefined; if (!nodeRect) { return []; @@ -244,7 +255,7 @@ export function useReactFlow 0; diff --git a/packages/system/src/utils/general.ts b/packages/system/src/utils/general.ts index b8af19bc..e76d24a4 100644 --- a/packages/system/src/utils/general.ts +++ b/packages/system/src/utils/general.ts @@ -9,6 +9,7 @@ import type { SnapGrid, Transform, InternalNodeBase, + NodeLookup, } from '../types'; import { type Viewport } from '../types'; import { getNodePositionWithOrigin } from './graph'; @@ -219,3 +220,48 @@ export function nodeHasDimensions(node: No (node.measured?.height ?? node.height ?? node.initialHeight) !== undefined ); } + +/** + * Helper to calculate the absolute position of a node + * + * @internal + * @param node + * @param nodeLookup + * @param nodeOrigin + * @returns an internal node with an absolute position + */ +export function evaluateNodePosition( + node: NodeBase | InternalNodeBase, + nodeLookup: NodeLookup, + nodeOrigin: NodeOrigin = [0, 0] +): InternalNodeBase | null { + const internalNode = nodeLookup.get(node.id); + + if (!internalNode) { + return null; + } + + let parentId = internalNode.parentId; + const positionAbsolute = { ...node.position }; + + while (parentId) { + const parent = nodeLookup.get(parentId); + parentId = parent?.parentId; + + if (parent) { + const origin = parent.origin || nodeOrigin; + const xOffset = (parent.measured.width ?? 0) * origin[0]; + const yOffset = (parent.measured.height ?? 0) * origin[1]; + positionAbsolute.x += parent.position.x - xOffset; + positionAbsolute.y += parent.position.y - yOffset; + } + } + + return { + ...internalNode, + internals: { + ...internalNode.internals, + positionAbsolute, + }, + }; +}