From 18a22e6f4c04eb47e2912d0648460575fd167cc9 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 5 Oct 2023 12:10:35 +0200 Subject: [PATCH 1/5] refactor(xyflow) improved performance of getConnectedEdgesBase --- packages/system/src/utils/graph.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index fb7d74c2..dca0dacc 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -161,9 +161,12 @@ export const getConnectedEdgesBase = { - const nodeIds = nodes.map((node) => node.id); + const nodeIds = new Set(); + nodes.forEach((node) => { + nodeIds.add(node.id); + }); - return edges.filter((edge) => nodeIds.includes(edge.source) || nodeIds.includes(edge.target)); + return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); }; export function fitView, Options extends FitViewOptionsBase>( From 8e7ee88ca3241a0a71682389cb8886eef3995501 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 5 Oct 2023 12:24:35 +0200 Subject: [PATCH 2/5] feat(react) added getConnectedEdges to useReactFlow --- packages/react/src/hooks/useReactFlow.ts | 17 +++++++++++++++++ packages/react/src/types/instance.ts | 1 + 2 files changed, 18 insertions(+) diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index 45a8dbe1..cec31cc8 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -223,6 +223,21 @@ export default function useReactFlow(): ReactFlo [] ); + const getConnectedEdges = useCallback((node) => { + const edges = store.getState().edges; + + const nodeIds = new Set(); + if (typeof node === 'string') { + nodeIds.add(node); + } else if (node.length >= 1) { + node.forEach((n) => { + nodeIds.add(n.id); + }); + } + + return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); + }, []); + return useMemo(() => { return { ...viewportHelper, @@ -238,6 +253,7 @@ export default function useReactFlow(): ReactFlo deleteElements, getIntersectingNodes, isNodeIntersecting, + getConnectedEdges, }; }, [ viewportHelper, @@ -253,5 +269,6 @@ export default function useReactFlow(): ReactFlo deleteElements, getIntersectingNodes, isNodeIntersecting, + getConnectedEdges, ]); } diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index 239d339f..0bc61617 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -39,6 +39,7 @@ export namespace Instance { area: Rect, partially?: boolean ) => boolean; + export type getConnectedEdges = (id: string | (Partial & { id: Node['id'] })[]) => Edge[]; } export type ReactFlowInstance = { From d4179ac5925e8144dd29ff383b85deb5ee165b26 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 5 Oct 2023 12:28:06 +0200 Subject: [PATCH 3/5] feat(svelte) added getConnectedEdges to useSvelteFlow --- packages/svelte/src/lib/hooks/useSvelteFlow.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index 366f104d..191b2ea0 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -10,7 +10,7 @@ import { } from '@xyflow/system'; import { useStore } from '$lib/store'; -import type { FitViewOptions } from '$lib/types'; +import type { Edge, FitViewOptions, Node } from '$lib/types'; import type { SvelteFlowStore } from '$lib/store/types'; export function useSvelteFlow(): { @@ -26,6 +26,7 @@ export function useSvelteFlow(): { viewport: Writable; nodes: SvelteFlowStore['nodes']; edges: SvelteFlowStore['edges']; + getConnectedEdges: (id: string | (Partial & { id: Node['id'] })[]) => Edge[]; } { const { zoomIn, @@ -86,6 +87,20 @@ export function useSvelteFlow(): { }, nodes, edges, + getConnectedEdges: (node) => { + const _edges = get(edges); + + const nodeIds = new Set(); + if (typeof node === 'string') { + nodeIds.add(node); + } else if (node.length >= 1) { + node.forEach((n) => { + nodeIds.add(n.id); + }); + } + + return _edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); + }, viewport: viewport }; } From 1b0790e79f2d7ef310a88158cf9c062457ef5df4 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 5 Oct 2023 13:00:20 +0200 Subject: [PATCH 4/5] refactor(xyflow) improved performance of getOutgoersBase & getIncomersBase --- packages/system/src/utils/graph.ts | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index dca0dacc..e72642be 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -43,8 +43,14 @@ export const getOutgoersBase = e.source === node.id).map((e) => e.target); - return nodes.filter((n) => outgoerIds.includes(n.id)); + const outgoerIds = new Set(); + edges.forEach((edge) => { + if (edge.source === node.id) { + outgoerIds.add(edge.target); + } + }); + + return nodes.filter((n) => outgoerIds.has(n.id)); }; export const getIncomersBase = ( @@ -55,9 +61,14 @@ export const getIncomersBase = { + if (edge.target === node.id) { + incomersIds.add(edge.source); + } + }); - const incomersIds = edges.filter((e) => e.target === node.id).map((e) => e.source); - return nodes.filter((n) => incomersIds.includes(n.id)); + return nodes.filter((n) => incomersIds.has(n.id)); }; export const getNodePositionWithOrigin = ( From 6fd9d19797ca72997d0c0afdbb71418e2238c825 Mon Sep 17 00:00:00 2001 From: Peter Date: Thu, 5 Oct 2023 13:28:04 +0200 Subject: [PATCH 5/5] feat(react, svelte) added getIncomers & getOutgoers to use...Flow() --- packages/react/src/hooks/useReactFlow.ts | 36 +++++++++++++++++-- packages/react/src/types/instance.ts | 2 ++ .../svelte/src/lib/hooks/useSvelteFlow.ts | 24 +++++++++++++ packages/system/src/utils/graph.ts | 8 ++--- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/packages/react/src/hooks/useReactFlow.ts b/packages/react/src/hooks/useReactFlow.ts index cec31cc8..8e94954a 100644 --- a/packages/react/src/hooks/useReactFlow.ts +++ b/packages/react/src/hooks/useReactFlow.ts @@ -1,5 +1,13 @@ import { useCallback, useMemo } from 'react'; -import { getElementsToRemove, getOverlappingArea, isRectObject, nodeToRect, type Rect } from '@xyflow/system'; +import { + getElementsToRemove, + getIncomersBase, + getOutgoersBase, + getOverlappingArea, + isRectObject, + nodeToRect, + type Rect, +} from '@xyflow/system'; import useViewportHelper from './useViewportHelper'; import { useStoreApi } from '../hooks/useStore'; @@ -224,7 +232,7 @@ export default function useReactFlow(): ReactFlo ); const getConnectedEdges = useCallback((node) => { - const edges = store.getState().edges; + const { edges } = store.getState(); const nodeIds = new Set(); if (typeof node === 'string') { @@ -238,6 +246,26 @@ export default function useReactFlow(): ReactFlo return edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); }, []); + const getIncomers = useCallback((node) => { + const { nodes, edges } = store.getState(); + + if (typeof node === 'string') { + return getIncomersBase({ id: node }, nodes, edges); + } + + return getIncomersBase(node, nodes, edges); + }, []); + + const getOutgoers = useCallback((node) => { + const { nodes, edges } = store.getState(); + + if (typeof node == 'string') { + return getOutgoersBase({ id: node }, nodes, edges); + } + + return getOutgoersBase(node, nodes, edges); + }, []); + return useMemo(() => { return { ...viewportHelper, @@ -254,6 +282,8 @@ export default function useReactFlow(): ReactFlo getIntersectingNodes, isNodeIntersecting, getConnectedEdges, + getIncomers, + getOutgoers, }; }, [ viewportHelper, @@ -270,5 +300,7 @@ export default function useReactFlow(): ReactFlo getIntersectingNodes, isNodeIntersecting, getConnectedEdges, + getIncomers, + getOutgoers, ]); } diff --git a/packages/react/src/types/instance.ts b/packages/react/src/types/instance.ts index 0bc61617..694329fb 100644 --- a/packages/react/src/types/instance.ts +++ b/packages/react/src/types/instance.ts @@ -40,6 +40,8 @@ export namespace Instance { partially?: boolean ) => boolean; export type getConnectedEdges = (id: string | (Partial & { id: Node['id'] })[]) => Edge[]; + export type getIncomers = (node: string | (Partial & { id: Node['id'] })) => Node[]; + export type getOutgoers = (node: string | (Partial & { id: Node['id'] })) => Node[]; } export type ReactFlowInstance = { diff --git a/packages/svelte/src/lib/hooks/useSvelteFlow.ts b/packages/svelte/src/lib/hooks/useSvelteFlow.ts index 191b2ea0..c3b6caec 100644 --- a/packages/svelte/src/lib/hooks/useSvelteFlow.ts +++ b/packages/svelte/src/lib/hooks/useSvelteFlow.ts @@ -1,5 +1,7 @@ import { get, type Writable } from 'svelte/store'; import { + getIncomersBase, + getOutgoersBase, pointToRendererPoint, type Project, type SetCenterOptions, @@ -27,6 +29,8 @@ export function useSvelteFlow(): { nodes: SvelteFlowStore['nodes']; edges: SvelteFlowStore['edges']; getConnectedEdges: (id: string | (Partial & { id: Node['id'] })[]) => Edge[]; + getIncomers: (node: string | (Partial & { id: Node['id'] })) => Node[]; + getOutgoers: (node: string | (Partial & { id: Node['id'] })) => Node[]; } { const { zoomIn, @@ -101,6 +105,26 @@ export function useSvelteFlow(): { return _edges.filter((edge) => nodeIds.has(edge.source) || nodeIds.has(edge.target)); }, + getIncomers: (node) => { + const _edges = get(edges); + const _nodes = get(nodes); + + if (typeof node === 'string') { + return getIncomersBase({ id: node }, _nodes, _edges); + } + + return getIncomersBase(node, _nodes, _edges); + }, + getOutgoers: (node) => { + const _edges = get(edges); + const _nodes = get(nodes); + + if (typeof node == 'string') { + return getOutgoersBase({ id: node }, _nodes, _edges); + } + + return getOutgoersBase(node, _nodes, _edges); + }, viewport: viewport }; } diff --git a/packages/system/src/utils/graph.ts b/packages/system/src/utils/graph.ts index e72642be..17467503 100644 --- a/packages/system/src/utils/graph.ts +++ b/packages/system/src/utils/graph.ts @@ -35,11 +35,11 @@ export const isNodeBase = 'id' in element && !('source' in element) && !('target' in element); export const getOutgoersBase = ( - node: NodeType, + node: Partial & { id: string }, nodes: NodeType[], edges: EdgeType[] ): NodeType[] => { - if (!isNodeBase(node)) { + if (!node.id) { return []; } @@ -54,11 +54,11 @@ export const getOutgoersBase = ( - node: NodeType, + node: Partial & { id: string }, nodes: NodeType[], edges: EdgeType[] ): NodeType[] => { - if (!isNodeBase(node)) { + if (!node.id) { return []; } const incomersIds = new Set();