From 6f8782bbf6bb66deb37020abbfaecf5fc1001e05 Mon Sep 17 00:00:00 2001 From: moklick Date: Sun, 19 Feb 2023 16:14:55 +0100 Subject: [PATCH] package(utils): add fitView --- packages/utils/package.json | 3 ++- packages/utils/src/graph.ts | 45 +++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/packages/utils/package.json b/packages/utils/package.json index 327681e7..887cdd89 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -40,7 +40,8 @@ "@types/d3": "^7.4.0", "@types/d3-drag": "^3.0.1", "@types/d3-selection": "^3.0.3", - "@types/d3-zoom": "^3.0.1" + "@types/d3-zoom": "^3.0.1", + "d3-zoom": "^3.0.0" }, "peerDependencies": { "react": ">=17", diff --git a/packages/utils/src/graph.ts b/packages/utils/src/graph.ts index 5eb520d2..8b7c4e89 100644 --- a/packages/utils/src/graph.ts +++ b/packages/utils/src/graph.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import type { Selection as D3Selection } from 'd3'; +import { zoomIdentity } from 'd3-zoom'; import { boxToRect, clamp, devWarn, getBoundsOfBoxes, getOverlappingArea, rectToBox } from './utils'; import { @@ -13,6 +14,8 @@ import { type Rect, type NodeInternals, type NodeOrigin, + FitViewParams, + FitViewOptions, } from '@reactflow/system'; export const isEdge = (element: Node | Connection | Edge): element is Edge => @@ -285,3 +288,45 @@ export const getTransformForBounds = ( export const getD3Transition = (selection: D3Selection, duration = 0) => { return selection.transition().duration(duration); }; + +export function fitView( + { nodes, width, height, d3Zoom, d3Selection, nodeOrigin, minZoom, maxZoom }: FitViewParams, + options: FitViewOptions = {} +) { + const filteredNodes = nodes.filter((n) => { + const isVisible = options.includeHiddenNodes ? n.width && n.height : !n.hidden; + + if (options.nodes?.length) { + return isVisible && options.nodes.some((optionNode) => optionNode.id === n.id); + } + + return isVisible; + }); + + const nodesInitialized = filteredNodes.every((n) => n.width && n.height); + + if (nodes.length > 0 && nodesInitialized) { + const bounds = getRectOfNodes(nodes, nodeOrigin); + + const [x, y, zoom] = getTransformForBounds( + bounds, + width, + height, + options.minZoom ?? minZoom, + options.maxZoom ?? maxZoom, + options.padding ?? 0.1 + ); + + const nextTransform = zoomIdentity.translate(x, y).scale(zoom); + + if (typeof options.duration === 'number' && options.duration > 0) { + d3Zoom.transform(getD3Transition(d3Selection, options.duration), nextTransform); + } else { + d3Zoom.transform(d3Selection, nextTransform); + } + + return true; + } + + return false; +}