package(core): use system fitView
This commit is contained in:
@@ -1,11 +1,10 @@
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
import { shallow } from 'zustand/shallow';
|
import { shallow } from 'zustand/shallow';
|
||||||
import { pointToRendererPoint, getTransformForBounds, getD3Transition } from '@reactflow/utils';
|
import { pointToRendererPoint, getTransformForBounds, getD3Transition, fitView } from '@reactflow/utils';
|
||||||
import type { ViewportHelperFunctions, ReactFlowState, XYPosition } from '@reactflow/system';
|
import type { ViewportHelperFunctions, ReactFlowState, XYPosition } from '@reactflow/system';
|
||||||
|
|
||||||
import { useStoreApi, useStore } from '../hooks/useStore';
|
import { useStoreApi, useStore } from '../hooks/useStore';
|
||||||
import { fitView } from '../store/utils';
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||||
const noop = () => {};
|
const noop = () => {};
|
||||||
@@ -51,7 +50,28 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
|||||||
const [x, y, zoom] = store.getState().transform;
|
const [x, y, zoom] = store.getState().transform;
|
||||||
return { x, y, zoom };
|
return { x, y, zoom };
|
||||||
},
|
},
|
||||||
fitView: (options) => fitView(store.getState, options),
|
fitView: (options) => {
|
||||||
|
const { getNodes, width, height, nodeOrigin, minZoom, maxZoom, d3Selection, d3Zoom } = store.getState();
|
||||||
|
const d3Initialized = d3Selection && d3Zoom;
|
||||||
|
|
||||||
|
if (!d3Initialized) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fitView(
|
||||||
|
{
|
||||||
|
nodes: getNodes(),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
nodeOrigin,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
d3Selection,
|
||||||
|
d3Zoom,
|
||||||
|
},
|
||||||
|
options
|
||||||
|
);
|
||||||
|
},
|
||||||
setCenter: (x, y, options) => {
|
setCenter: (x, y, options) => {
|
||||||
const { width, height, maxZoom } = store.getState();
|
const { width, height, maxZoom } = store.getState();
|
||||||
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
|
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { createStore } from 'zustand';
|
import { createStore } from 'zustand';
|
||||||
import { zoomIdentity } from 'd3-zoom';
|
import { zoomIdentity } from 'd3-zoom';
|
||||||
import { clampPosition, getDimensions } from '@reactflow/utils';
|
import { clampPosition, getDimensions, fitView } from '@reactflow/utils';
|
||||||
import {
|
import {
|
||||||
internalsSymbol,
|
internalsSymbol,
|
||||||
type ReactFlowState,
|
type ReactFlowState,
|
||||||
@@ -20,7 +20,7 @@ import {
|
|||||||
|
|
||||||
import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
import { applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||||
import { getHandleBounds } from '../components/Nodes/utils';
|
import { getHandleBounds } from '../components/Nodes/utils';
|
||||||
import { createNodeInternals, fitView, updateAbsoluteNodePositions, updateNodesAndEdgesSelections } from './utils';
|
import { createNodeInternals, updateAbsoluteNodePositions, updateNodesAndEdgesSelections } from './utils';
|
||||||
import initialState from './initialState';
|
import initialState from './initialState';
|
||||||
|
|
||||||
const createRFStore = () =>
|
const createRFStore = () =>
|
||||||
@@ -57,6 +57,12 @@ const createRFStore = () =>
|
|||||||
fitViewOnInitOptions,
|
fitViewOnInitOptions,
|
||||||
domNode,
|
domNode,
|
||||||
nodeOrigin,
|
nodeOrigin,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
d3Selection,
|
||||||
|
d3Zoom,
|
||||||
} = get();
|
} = get();
|
||||||
const viewportNode = domNode?.querySelector('.react-flow__viewport');
|
const viewportNode = domNode?.querySelector('.react-flow__viewport');
|
||||||
|
|
||||||
@@ -106,7 +112,23 @@ const createRFStore = () =>
|
|||||||
|
|
||||||
const nextFitViewOnInitDone =
|
const nextFitViewOnInitDone =
|
||||||
fitViewOnInitDone ||
|
fitViewOnInitDone ||
|
||||||
(fitViewOnInit && !fitViewOnInitDone && fitView(get, { initial: true, ...fitViewOnInitOptions }));
|
(fitViewOnInit &&
|
||||||
|
!fitViewOnInitDone &&
|
||||||
|
!!d3Zoom &&
|
||||||
|
!!d3Selection &&
|
||||||
|
fitView(
|
||||||
|
{
|
||||||
|
nodes: Array.from(nodeInternals.values()),
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
d3Zoom,
|
||||||
|
d3Selection,
|
||||||
|
minZoom,
|
||||||
|
maxZoom,
|
||||||
|
nodeOrigin,
|
||||||
|
},
|
||||||
|
fitViewOnInitOptions
|
||||||
|
));
|
||||||
set({ nodeInternals: new Map(nodeInternals), fitViewOnInitDone: nextFitViewOnInitDone });
|
set({ nodeInternals: new Map(nodeInternals), fitViewOnInitDone: nextFitViewOnInitDone });
|
||||||
|
|
||||||
if (changes?.length > 0) {
|
if (changes?.length > 0) {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { zoomIdentity } from 'd3-zoom';
|
|
||||||
import type { StoreApi } from 'zustand';
|
import type { StoreApi } from 'zustand';
|
||||||
import {
|
import {
|
||||||
internalsSymbol,
|
internalsSymbol,
|
||||||
@@ -9,16 +8,9 @@ import {
|
|||||||
type NodeSelectionChange,
|
type NodeSelectionChange,
|
||||||
type ReactFlowState,
|
type ReactFlowState,
|
||||||
type XYZPosition,
|
type XYZPosition,
|
||||||
type FitViewOptions,
|
|
||||||
type NodeOrigin,
|
type NodeOrigin,
|
||||||
} from '@reactflow/system';
|
} from '@reactflow/system';
|
||||||
import {
|
import { isNumeric, getNodePositionWithOrigin } from '@reactflow/utils';
|
||||||
isNumeric,
|
|
||||||
getD3Transition,
|
|
||||||
getRectOfNodes,
|
|
||||||
getTransformForBounds,
|
|
||||||
getNodePositionWithOrigin,
|
|
||||||
} from '@reactflow/utils';
|
|
||||||
|
|
||||||
type ParentNodes = Record<string, boolean>;
|
type ParentNodes = Record<string, boolean>;
|
||||||
|
|
||||||
@@ -126,66 +118,6 @@ export function createNodeInternals(
|
|||||||
return nextNodeInternals;
|
return nextNodeInternals;
|
||||||
}
|
}
|
||||||
|
|
||||||
type InternalFitViewOptions = {
|
|
||||||
initial?: boolean;
|
|
||||||
} & FitViewOptions;
|
|
||||||
|
|
||||||
export function fitView(get: StoreApi<ReactFlowState>['getState'], options: InternalFitViewOptions = {}) {
|
|
||||||
const {
|
|
||||||
getNodes,
|
|
||||||
width,
|
|
||||||
height,
|
|
||||||
minZoom,
|
|
||||||
maxZoom,
|
|
||||||
d3Zoom,
|
|
||||||
d3Selection,
|
|
||||||
fitViewOnInitDone,
|
|
||||||
fitViewOnInit,
|
|
||||||
nodeOrigin,
|
|
||||||
} = get();
|
|
||||||
const isInitialFitView = options.initial && !fitViewOnInitDone && fitViewOnInit;
|
|
||||||
const d3initialized = d3Zoom && d3Selection;
|
|
||||||
|
|
||||||
if (d3initialized && (isInitialFitView || !options.initial)) {
|
|
||||||
const nodes = getNodes().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 = nodes.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function handleControlledNodeSelectionChange(nodeChanges: NodeSelectionChange[], nodeInternals: NodeInternals) {
|
export function handleControlledNodeSelectionChange(nodeChanges: NodeSelectionChange[], nodeInternals: NodeInternals) {
|
||||||
nodeChanges.forEach((change) => {
|
nodeChanges.forEach((change) => {
|
||||||
const node = nodeInternals.get(change.id);
|
const node = nodeInternals.get(change.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user