Refactor fitView
This commit is contained in:
@@ -154,8 +154,8 @@ export function StoreUpdater<NodeType extends Node = Node, EdgeType extends Edge
|
||||
else if (fieldName === 'nodeExtent') setNodeExtent(fieldValue as CoordinateExtent);
|
||||
else if (fieldName === 'paneClickDistance') setPaneClickDistance(fieldValue as number);
|
||||
// Renamed fields
|
||||
else if (fieldName === 'fitView') store.setState({ fitViewOnInit: fieldValue as boolean });
|
||||
else if (fieldName === 'fitViewOptions') store.setState({ fitViewOnInitOptions: fieldValue as FitViewOptions });
|
||||
else if (fieldName === 'fitView') store.setState({ fitViewQueued: fieldValue as boolean });
|
||||
else if (fieldName === 'fitViewOptions') store.setState({ fitViewOptions: fieldValue as FitViewOptions });
|
||||
// General case
|
||||
else store.setState({ [fieldName]: fieldValue });
|
||||
}
|
||||
|
||||
@@ -15,7 +15,15 @@ import useViewportHelper from './useViewportHelper';
|
||||
import { useStore, useStoreApi } from './useStore';
|
||||
import { useBatchContext } from '../components/BatchProvider';
|
||||
import { elementToRemoveChange, isEdge, isNode } from '../utils';
|
||||
import type { ReactFlowInstance, Node, Edge, InternalNode, ReactFlowState, GeneralHelpers } from '../types';
|
||||
import type {
|
||||
ReactFlowInstance,
|
||||
Node,
|
||||
Edge,
|
||||
InternalNode,
|
||||
ReactFlowState,
|
||||
GeneralHelpers,
|
||||
FitViewOptions,
|
||||
} from '../types';
|
||||
|
||||
const selector = (s: ReactFlowState) => !!s.panZoom;
|
||||
|
||||
@@ -271,6 +279,10 @@ export function useReactFlow<NodeType extends Node = Node, EdgeType extends Edge
|
||||
.connectionLookup.get(`${nodeId}${type ? (handleId ? `-${type}-${handleId}` : `-${type}`) : ''}`)
|
||||
?.values() ?? []
|
||||
),
|
||||
fitView: (options: FitViewOptions<NodeType> | undefined) => {
|
||||
store.setState({ fitViewQueued: true, fitViewOptions: options });
|
||||
batchContext.nodeQueue.push((nodes) => [...nodes]);
|
||||
},
|
||||
};
|
||||
}, []);
|
||||
|
||||
|
||||
@@ -2,11 +2,8 @@ import { useMemo } from 'react';
|
||||
import {
|
||||
pointToRendererPoint,
|
||||
getViewportForBounds,
|
||||
getFitViewNodes,
|
||||
fitView,
|
||||
type XYPosition,
|
||||
rendererPointToPoint,
|
||||
getDimensions,
|
||||
SnapGrid,
|
||||
} from '@xyflow/system';
|
||||
|
||||
@@ -65,28 +62,6 @@ const useViewportHelper = (): ViewportHelperFunctions => {
|
||||
const [x, y, zoom] = store.getState().transform;
|
||||
return { x, y, zoom };
|
||||
},
|
||||
fitView: (options) => {
|
||||
const { nodeLookup, minZoom, maxZoom, panZoom, domNode } = store.getState();
|
||||
|
||||
if (!panZoom || !domNode) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const fitViewNodes = getFitViewNodes(nodeLookup, options);
|
||||
const { width, height } = getDimensions(domNode);
|
||||
|
||||
return fitView(
|
||||
{
|
||||
nodes: fitViewNodes,
|
||||
width,
|
||||
height,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
panZoom,
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
setCenter: async (x, y, options) => {
|
||||
const { width, height, maxZoom, panZoom } = store.getState();
|
||||
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { createWithEqualityFn } from 'zustand/traditional';
|
||||
import {
|
||||
getFitViewNodes,
|
||||
fitView as fitViewSystem,
|
||||
adoptUserNodes,
|
||||
updateAbsolutePositions,
|
||||
panBy as panBySystem,
|
||||
@@ -15,11 +13,12 @@ import {
|
||||
initialConnection,
|
||||
NodeOrigin,
|
||||
CoordinateExtent,
|
||||
fitViewport,
|
||||
} from '@xyflow/system';
|
||||
|
||||
import { applyEdgeChanges, applyNodeChanges, createSelectionChange, getSelectionChanges } from '../utils/changes';
|
||||
import getInitialState from './initialState';
|
||||
import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams, FitViewOptions } from '../types';
|
||||
import type { ReactFlowState, Node, Edge, UnselectNodesAndEdgesParams } from '../types';
|
||||
|
||||
const createStore = ({
|
||||
nodes,
|
||||
@@ -46,7 +45,7 @@ const createStore = ({
|
||||
(set, get) => ({
|
||||
...getInitialState({ nodes, edges, width, height, fitView, nodeOrigin, nodeExtent, defaultNodes, defaultEdges }),
|
||||
setNodes: (nodes: Node[]) => {
|
||||
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect } = get();
|
||||
const { nodeLookup, parentLookup, nodeOrigin, elevateNodesOnSelect, fitViewQueued, panZoom } = get();
|
||||
/*
|
||||
* setNodes() is called exclusively in response to user actions:
|
||||
* - either when the `<ReactFlow nodes>` prop is updated in the controlled ReactFlow setup,
|
||||
@@ -55,14 +54,35 @@ const createStore = ({
|
||||
* When this happens, we take the note objects passed by the user and extend them with fields
|
||||
* relevant for internal React Flow operations.
|
||||
*/
|
||||
adoptUserNodes(nodes, nodeLookup, parentLookup, {
|
||||
|
||||
const nodesInitialized = adoptUserNodes(nodes, nodeLookup, parentLookup, {
|
||||
nodeOrigin,
|
||||
nodeExtent,
|
||||
elevateNodesOnSelect,
|
||||
checkEquality: true,
|
||||
});
|
||||
|
||||
set({ nodes });
|
||||
let viewportFitted = false;
|
||||
if (fitViewQueued && nodesInitialized && panZoom) {
|
||||
const { fitViewOptions, width, height, minZoom, maxZoom } = get();
|
||||
viewportFitted = fitViewport(
|
||||
{
|
||||
nodes: nodeLookup,
|
||||
width,
|
||||
height,
|
||||
panZoom,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
},
|
||||
fitViewOptions
|
||||
);
|
||||
}
|
||||
|
||||
if (viewportFitted) {
|
||||
set({ nodes, fitViewQueued: false, fitViewOptions: undefined });
|
||||
} else {
|
||||
set({ nodes });
|
||||
}
|
||||
},
|
||||
setEdges: (edges: Edge[]) => {
|
||||
const { connectionLookup, edgeLookup } = get();
|
||||
@@ -88,20 +108,8 @@ const createStore = ({
|
||||
* changes its dimensions, this function is called to measure the
|
||||
* new dimensions and update the nodes.
|
||||
*/
|
||||
updateNodeInternals: (updates, params = { triggerFitView: true }) => {
|
||||
const {
|
||||
triggerNodeChanges,
|
||||
nodeLookup,
|
||||
parentLookup,
|
||||
fitViewOnInit,
|
||||
fitViewDone,
|
||||
fitViewOnInitOptions,
|
||||
domNode,
|
||||
nodeOrigin,
|
||||
nodeExtent,
|
||||
debug,
|
||||
fitViewSync,
|
||||
} = get();
|
||||
updateNodeInternals: (updates) => {
|
||||
const { triggerNodeChanges, nodeLookup, parentLookup, domNode, nodeOrigin, nodeExtent, debug } = get();
|
||||
|
||||
const { changes, updatedInternals } = updateNodeInternalsSystem(
|
||||
updates,
|
||||
@@ -118,29 +126,8 @@ const createStore = ({
|
||||
|
||||
updateAbsolutePositions(nodeLookup, parentLookup, { nodeOrigin, nodeExtent });
|
||||
|
||||
if (params.triggerFitView) {
|
||||
// we call fitView once initially after all dimensions are set
|
||||
let nextFitViewDone = fitViewDone;
|
||||
|
||||
if (!fitViewDone && fitViewOnInit) {
|
||||
nextFitViewDone = fitViewSync({
|
||||
...fitViewOnInitOptions,
|
||||
nodes: fitViewOnInitOptions?.nodes,
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* here we are cirmumventing the onNodesChange handler
|
||||
* in order to be able to display nodes even if the user
|
||||
* has not provided an onNodesChange handler.
|
||||
* Nodes are only rendered if they have a width and height
|
||||
* attribute which they get from this handler.
|
||||
*/
|
||||
set({ fitViewDone: nextFitViewDone });
|
||||
} else {
|
||||
// we always want to trigger useStore calls whenever updateNodeInternals is called
|
||||
set({});
|
||||
}
|
||||
// we always want to trigger useStore calls whenever updateNodeInternals is called
|
||||
set({});
|
||||
|
||||
if (changes?.length > 0) {
|
||||
if (debug) {
|
||||
@@ -332,54 +319,6 @@ const createStore = ({
|
||||
|
||||
return panBySystem({ delta, panZoom, transform, translateExtent, width, height });
|
||||
},
|
||||
fitView: (options?: FitViewOptions): Promise<boolean> => {
|
||||
const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
|
||||
|
||||
if (!panZoom) {
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
const fitViewNodes = getFitViewNodes(nodeLookup, options);
|
||||
|
||||
return fitViewSystem(
|
||||
{
|
||||
nodes: fitViewNodes,
|
||||
width,
|
||||
height,
|
||||
panZoom,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
},
|
||||
options
|
||||
);
|
||||
},
|
||||
/*
|
||||
* we can't call an asnychronous function in updateNodeInternals
|
||||
* for that we created this sync version of fitView
|
||||
*/
|
||||
fitViewSync: (options?: FitViewOptions): boolean => {
|
||||
const { panZoom, width, height, minZoom, maxZoom, nodeLookup } = get();
|
||||
|
||||
if (!panZoom) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const fitViewNodes = getFitViewNodes(nodeLookup, options);
|
||||
|
||||
fitViewSystem(
|
||||
{
|
||||
nodes: fitViewNodes,
|
||||
width,
|
||||
height,
|
||||
panZoom,
|
||||
minZoom,
|
||||
maxZoom,
|
||||
},
|
||||
options
|
||||
);
|
||||
|
||||
return fitViewNodes.size > 0;
|
||||
},
|
||||
cancelConnection: () => {
|
||||
set({
|
||||
connection: { ...initialConnection },
|
||||
|
||||
@@ -104,13 +104,13 @@ const getInitialState = ({
|
||||
elementsSelectable: true,
|
||||
elevateNodesOnSelect: true,
|
||||
elevateEdgesOnSelect: false,
|
||||
fitViewOnInit: false,
|
||||
fitViewDone: false,
|
||||
fitViewOnInitOptions: undefined,
|
||||
selectNodesOnDrag: true,
|
||||
|
||||
multiSelectionActive: false,
|
||||
|
||||
fitViewQueued: false,
|
||||
fitViewOptions: undefined,
|
||||
|
||||
connection: { ...initialConnection },
|
||||
connectionClickStartHandle: null,
|
||||
connectOnClick: true,
|
||||
|
||||
@@ -109,7 +109,7 @@ export type FitViewParams<NodeType extends Node = Node> = FitViewParamsBase<Node
|
||||
* @public
|
||||
*/
|
||||
export type FitViewOptions<NodeType extends Node = Node> = FitViewOptionsBase<NodeType>;
|
||||
export type FitView = (fitViewOptions?: FitViewOptions) => Promise<boolean>;
|
||||
export type FitView<NodeType extends Node = Node> = (fitViewOptions?: FitViewOptions<NodeType>) => void;
|
||||
export type OnInit<NodeType extends Node = Node, EdgeType extends Edge = Edge> = (
|
||||
reactFlowInstance: ReactFlowInstance<NodeType, EdgeType>
|
||||
) => void;
|
||||
@@ -156,17 +156,6 @@ export type ViewportHelperFunctions = {
|
||||
* @returns Viewport
|
||||
*/
|
||||
getViewport: GetViewport;
|
||||
/**
|
||||
* Fits the view.
|
||||
*
|
||||
* @param options.padding - optional padding
|
||||
* @param options.includeHiddenNodes - optional includeHiddenNodes
|
||||
* @param options.minZoom - optional minZoom
|
||||
* @param options.maxZoom - optional maxZoom
|
||||
* @param options.duration - optional duration. If set, a transition will be applied
|
||||
* @param options.nodes - optional nodes to fit the view to
|
||||
*/
|
||||
fitView: FitView;
|
||||
/**
|
||||
* Sets the center of the view to the given position.
|
||||
*
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import type { HandleConnection, HandleType, NodeConnection, Rect, Viewport } from '@xyflow/system';
|
||||
import type { Node, Edge, ViewportHelperFunctions, InternalNode } from '.';
|
||||
import type { Node, Edge, ViewportHelperFunctions, InternalNode, FitView } from '.';
|
||||
|
||||
export type ReactFlowJsonObject<NodeType extends Node = Node, EdgeType extends Edge = Edge> = {
|
||||
nodes: NodeType[];
|
||||
@@ -217,6 +217,17 @@ export type GeneralHelpers<NodeType extends Node = Node, EdgeType extends Edge =
|
||||
nodeId: string;
|
||||
handleId?: string | null;
|
||||
}) => NodeConnection[];
|
||||
// /**
|
||||
// * Fits the view.
|
||||
// *
|
||||
// * @param options.padding - optional padding
|
||||
// * @param options.includeHiddenNodes - optional includeHiddenNodes
|
||||
// * @param options.minZoom - optional minZoom
|
||||
// * @param options.maxZoom - optional maxZoom
|
||||
// * @param options.duration - optional duration. If set, a transition will be applied
|
||||
// * @param options.nodes - optional nodes to fit the view to
|
||||
// */
|
||||
fitView: FitView<NodeType>;
|
||||
};
|
||||
/**
|
||||
* The `ReactFlowInstance` provides a collection of methods to query and manipulate
|
||||
|
||||
@@ -119,9 +119,8 @@ export type ReactFlowStore<NodeType extends Node = Node, EdgeType extends Edge =
|
||||
connectOnClick: boolean;
|
||||
defaultEdgeOptions?: DefaultEdgeOptions;
|
||||
|
||||
fitViewOnInit: boolean;
|
||||
fitViewDone: boolean;
|
||||
fitViewOnInitOptions: FitViewOptions | undefined;
|
||||
fitViewQueued: boolean;
|
||||
fitViewOptions: FitViewOptions | undefined;
|
||||
|
||||
onNodesDelete?: OnNodesDelete<NodeType>;
|
||||
onEdgesDelete?: OnEdgesDelete<EdgeType>;
|
||||
@@ -168,8 +167,6 @@ export type ReactFlowActions<NodeType extends Node, EdgeType extends Edge> = {
|
||||
triggerNodeChanges: (changes: NodeChange<NodeType>[]) => void;
|
||||
triggerEdgeChanges: (changes: EdgeChange<EdgeType>[]) => void;
|
||||
panBy: PanBy;
|
||||
fitView: (options?: FitViewOptions) => Promise<boolean>;
|
||||
fitViewSync: (options?: FitViewOptions) => boolean;
|
||||
setPaneClickDistance: (distance: number) => void;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user