Files
xyflow/packages/react/src/hooks/useViewportHelper.ts
T

138 lines
4.0 KiB
TypeScript

import { useMemo } from 'react';
import {
pointToRendererPoint,
getViewportForBounds,
type XYPosition,
rendererPointToPoint,
SnapGrid,
} from '@xyflow/system';
import { useStoreApi } from '../hooks/useStore';
import type { ViewportHelperFunctions } from '../types';
/**
* Hook for getting viewport helper functions.
*
* @internal
* @returns viewport helper functions
*/
const useViewportHelper = (): ViewportHelperFunctions => {
const store = useStoreApi();
return useMemo<ViewportHelperFunctions>(() => {
return {
zoomIn: (options) => {
const { panZoom } = store.getState();
return panZoom ? panZoom.scaleBy(1.2, { duration: options?.duration }) : Promise.resolve(false);
},
zoomOut: (options) => {
const { panZoom } = store.getState();
return panZoom ? panZoom.scaleBy(1 / 1.2, { duration: options?.duration }) : Promise.resolve(false);
},
zoomTo: (zoomLevel, options) => {
const { panZoom } = store.getState();
return panZoom ? panZoom.scaleTo(zoomLevel, { duration: options?.duration }) : Promise.resolve(false);
},
getZoom: () => store.getState().transform[2],
setViewport: async (viewport, options) => {
const {
transform: [tX, tY, tZoom],
panZoom,
} = store.getState();
if (!panZoom) {
return Promise.resolve(false);
}
await panZoom.setViewport(
{
x: viewport.x ?? tX,
y: viewport.y ?? tY,
zoom: viewport.zoom ?? tZoom,
},
{ duration: options?.duration }
);
return Promise.resolve(true);
},
getViewport: () => {
const [x, y, zoom] = store.getState().transform;
return { x, y, zoom };
},
setCenter: async (x, y, options) => {
const { width, height, maxZoom, panZoom } = store.getState();
const nextZoom = typeof options?.zoom !== 'undefined' ? options.zoom : maxZoom;
const centerX = width / 2 - x * nextZoom;
const centerY = height / 2 - y * nextZoom;
if (!panZoom) {
return Promise.resolve(false);
}
await panZoom.setViewport(
{
x: centerX,
y: centerY,
zoom: nextZoom,
},
{ duration: options?.duration }
);
return Promise.resolve(true);
},
fitBounds: async (bounds, options) => {
const { width, height, minZoom, maxZoom, panZoom } = store.getState();
const viewport = getViewportForBounds(bounds, width, height, minZoom, maxZoom, options?.padding ?? 0.1);
if (!panZoom) {
return Promise.resolve(false);
}
await panZoom.setViewport(viewport, { duration: options?.duration });
return Promise.resolve(true);
},
screenToFlowPosition: (
clientPosition: XYPosition,
options: { snapToGrid?: boolean; snapGrid?: SnapGrid } = {}
) => {
const { transform, snapGrid, snapToGrid, domNode } = store.getState();
if (!domNode) {
return clientPosition;
}
const { x: domX, y: domY } = domNode.getBoundingClientRect();
const correctedPosition = {
x: clientPosition.x - domX,
y: clientPosition.y - domY,
};
const _snapGrid = options.snapGrid ?? snapGrid;
const _snapToGrid = options.snapToGrid ?? snapToGrid;
return pointToRendererPoint(correctedPosition, transform, _snapToGrid, _snapGrid);
},
flowToScreenPosition: (flowPosition: XYPosition) => {
const { transform, domNode } = store.getState();
if (!domNode) {
return flowPosition;
}
const { x: domX, y: domY } = domNode.getBoundingClientRect();
const rendererPosition = rendererPointToPoint(flowPosition, transform);
return {
x: rendererPosition.x + domX,
y: rendererPosition.y + domY,
};
},
};
}, []);
};
export default useViewportHelper;