diff --git a/src/components/ViewFitter/index.tsx b/src/components/ViewFitter/index.tsx new file mode 100644 index 00000000..f92733d2 --- /dev/null +++ b/src/components/ViewFitter/index.tsx @@ -0,0 +1,51 @@ +import React, { MutableRefObject, useEffect, useRef } from 'react'; + +import { Node } from '../../types'; +import { getRectOfNodes, getTransformForBounds } from '../../utils/graph'; +import { useStoreApi } from '../../store'; +import useZoomPanHelper from '../../hooks/useZoomPanHelper'; + +type ViewFitterInnerProps = { + nodes: Node[]; + viewFitted: MutableRefObject; +}; + +function ViewFitterInner({ nodes, viewFitted }: ViewFitterInnerProps) { + const store = useStoreApi(); + const { setTransform } = useZoomPanHelper(); + + useEffect(() => { + if (nodes.length > 0 && !viewFitted.current) { + const nodesInitialized = nodes.every((n) => n.width && n.height); + + if (nodesInitialized) { + const { width, height, minZoom, maxZoom } = store.getState(); + const bounds = getRectOfNodes(nodes); + const [x, y, zoom] = getTransformForBounds(bounds, width, height, minZoom ?? 0.5, maxZoom ?? 2); + + viewFitted.current = true; + setTransform({ x, y, zoom }); + } + } + }, [nodes]); + + return null; +} + +export type ViewFitterProps = { + nodes: Node[]; +}; + +function ViewFitter({ nodes }: ViewFitterProps) { + // we are storing the viewFitted in this wrapper to get rid of the useEffect of the inner component + // that needs to check if the nodes changed after we fitted the view. + const viewFitted = useRef(false); + + if (viewFitted.current) { + return null; + } + + return ; +} + +export default ViewFitter; diff --git a/src/container/ReactFlow/index.tsx b/src/container/ReactFlow/index.tsx index 87c3bbfb..d43e8cfd 100644 --- a/src/container/ReactFlow/index.tsx +++ b/src/container/ReactFlow/index.tsx @@ -19,6 +19,7 @@ import SelectionListener from '../../components/SelectionListener'; import { BezierEdge, StepEdge, SmoothStepEdge, StraightEdge } from '../../components/Edges'; import { createEdgeTypes } from '../EdgeRenderer/utils'; import Wrapper from './Wrapper'; +import ViewFitter from '../../components/ViewFitter'; import { OnSelectionChangeFunc, NodeTypesType, @@ -133,6 +134,7 @@ export interface ReactFlowProps extends Omit, 'on noDragClassName?: string; noWheelClassName?: string; noPanClassName?: string; + fitViewOnInit?: boolean; } export type ReactFlowRefType = HTMLDivElement; @@ -219,6 +221,7 @@ const ReactFlow: FunctionComponent = forwardRef = forwardRef - + {fitViewOnInit && } {onSelectionChange && } {children}