From 7f4d56b394aad24e419b211e01da03257fd09f9b Mon Sep 17 00:00:00 2001 From: peterkogo Date: Mon, 22 Jul 2024 17:43:32 +0200 Subject: [PATCH 1/9] added getNodesBounds inside main hook --- packages/react/src/hooks/useReactFlow.ts | 23 ++++++++++ packages/react/src/types/instance.ts | 8 ++++ .../svelte/src/lib/hooks/useSvelteFlow.ts | 43 ++++++++++++++++++- 3 files changed, 72 insertions(+), 2 deletions(-) diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index faf9d31f..b793bfe0 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -1,11 +1,14 @@ import { useMemo } from 'react'; import { + boxToRect, EdgeRemoveChange, evaluateAbsolutePosition, + getBoundsOfBoxes, getElementsToRemove, getOverlappingArea, isRectObject, NodeRemoveChange, + nodeToBox, nodeToRect, type Rect, } from '@xyflow/system'; @@ -230,6 +233,26 @@ export function useReactFlow { + if (nodes.length === 0) { + return { x: 0, y: 0, width: 0, height: 0 }; + } + + const { nodeLookup, nodeOrigin } = store.getState(); + + const box = nodes.reduce( + (currBox, node) => { + const internalNode = + typeof node === 'string' ? nodeLookup.get(node) : node.parentId ? nodeLookup.get(node.id) : node; + + const nodeBox = internalNode ? nodeToBox(internalNode, nodeOrigin) : { x: 0, y: 0, x2: 0, y2: 0 }; + return getBoundsOfBoxes(currBox, nodeBox); + }, + { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity } + ); + + return boxToRect(box); + }, }; }, []); diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index 5f4aa3b3..d545771e 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -173,6 +173,14 @@ export type GeneralHelpers | ((edge: EdgeType) => Partial), options?: { replace: boolean } ) => void; + /** + * Returns the bounds of the given nodes or node ids. + * + * @param nodes - the nodes or node ids to calculate the bounds for + * + * @returns the bounds of the given nodes + */ + getNodesBounds: (nodes: (NodeType | string)[]) => Rect; }; export type ReactFlowInstance = GeneralHelpers< diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index f46c1757..2de1e393 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -14,7 +14,10 @@ import { getViewportForBounds, getElementsToRemove, rendererPointToPoint, - nodeHasDimensions + nodeHasDimensions, + nodeToBox, + getBoundsOfBoxes, + boxToRect } from '@xyflow/system'; import { useStore } from '$lib/store'; @@ -230,6 +233,14 @@ export function useSvelteFlow(): { * @returns the nodes, edges and the viewport as a JSON object */ toObject: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; + /** + * Returns the bounds of the given nodes or node ids. + * + * @param nodes - the nodes or node ids to calculate the bounds for + * + * @returns the bounds of the given nodes + */ + getNodesBounds: (nodes: (Node | string)[]) => Rect; } { const { zoomIn, @@ -247,6 +258,7 @@ export function useSvelteFlow(): { edges, domNode, nodeLookup, + nodeOrigin, edgeLookup } = useStore(); @@ -506,7 +518,34 @@ export function useSvelteFlow(): { nodes.update((nds) => nds); }, - viewport + viewport, + getNodesBounds: (nodes) => { + if (nodes.length === 0) { + return { x: 0, y: 0, width: 0, height: 0 }; + } + + const _nodeLookup = get(nodeLookup); + const _nodeOrigin = get(nodeOrigin); + + const box = nodes.reduce( + (currBox, node) => { + const internalNode = + typeof node === 'string' + ? _nodeLookup.get(node) + : node.parentId + ? _nodeLookup.get(node.id) + : node; + + const nodeBox = internalNode + ? nodeToBox(internalNode, _nodeOrigin) + : { x: 0, y: 0, x2: 0, y2: 0 }; + return getBoundsOfBoxes(currBox, nodeBox); + }, + { x: Infinity, y: Infinity, x2: -Infinity, y2: -Infinity } + ); + + return boxToRect(box); + } }; } function getElements(lookup: Map, ids: string[]): Node[]; From 33dbb57aacf2c643a9751f4ca333cc04ff49ae6c Mon Sep 17 00:00:00 2001 From: peterkogo Date: Tue, 23 Jul 2024 16:01:54 +0200 Subject: [PATCH 2/9] fixed node bounds calculations if it is an internal node --- packages/react/src/hooks/useReactFlow.ts | 9 +++++++-- packages/svelte/src/lib/hooks/useSvelteFlow.ts | 7 ++++--- .../src/lib/plugins/NodeToolbar/NodeToolbar.svelte | 8 +++++--- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index b793bfe0..6149084c 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -6,6 +6,7 @@ import { getBoundsOfBoxes, getElementsToRemove, getOverlappingArea, + isInternalNodeBase, isRectObject, NodeRemoveChange, nodeToBox, @@ -233,7 +234,7 @@ export function useReactFlow { + getNodesBounds: (nodes: (NodeType | InternalNode | string)[]): Rect => { if (nodes.length === 0) { return { x: 0, y: 0, width: 0, height: 0 }; } @@ -243,7 +244,11 @@ export function useReactFlow { const internalNode = - typeof node === 'string' ? nodeLookup.get(node) : node.parentId ? nodeLookup.get(node.id) : node; + typeof node === 'string' + ? nodeLookup.get(node) + : !isInternalNodeBase(node) + ? nodeLookup.get(node.id) + : node; const nodeBox = internalNode ? nodeToBox(internalNode, nodeOrigin) : { x: 0, y: 0, x2: 0, y2: 0 }; return getBoundsOfBoxes(currBox, nodeBox); diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index 2de1e393..936945f3 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -17,7 +17,8 @@ import { nodeHasDimensions, nodeToBox, getBoundsOfBoxes, - boxToRect + boxToRect, + isInternalNodeBase } from '@xyflow/system'; import { useStore } from '$lib/store'; @@ -240,7 +241,7 @@ export function useSvelteFlow(): { * * @returns the bounds of the given nodes */ - getNodesBounds: (nodes: (Node | string)[]) => Rect; + getNodesBounds: (nodes: (Node | InternalNode | string)[]) => Rect; } { const { zoomIn, @@ -532,7 +533,7 @@ export function useSvelteFlow(): { const internalNode = typeof node === 'string' ? _nodeLookup.get(node) - : node.parentId + : !isInternalNodeBase(node) ? _nodeLookup.get(node.id) : node; diff --git a/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte b/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte index 1de880b3..58d73db5 100644 --- a/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte +++ b/packages/svelte/src/lib/plugins/NodeToolbar/NodeToolbar.svelte @@ -1,11 +1,12 @@