import { useCallback } from 'react'; import { fractionToZoom, zoomToFraction } from '../core/geometry.ts'; import { useCropper, useCropperState } from './context.ts'; /** zoom control state. */ export interface ZoomControl { /** whether zoom is unavailable. */ disabled: boolean; /** logarithmic slider position from `0` to `1`. */ fraction: number; /** * set zoom from a slider position. * * @param fraction position from `0` to `1`. */ setFraction: (fraction: number) => void; /** zoom relative to the cover fit. */ zoom: number; } /** * bind a logarithmic slider to zoom. * * @returns zoom control state. * @throws when called outside a ``. */ export const useZoomControl = (): ZoomControl => { const { config, setZoom } = useCropper(); const zoom = useCropperState((state) => state.zoom); const measured = useCropperState((state) => state.value.width > 0); const { maxZoom } = config; const setFraction = useCallback( (fraction: number) => { setZoom(fractionToZoom(fraction, maxZoom)); }, [maxZoom, setZoom], ); return { disabled: !measured || maxZoom <= 1, fraction: zoomToFraction(zoom, maxZoom), setFraction, zoom, }; }; /** pan control state. */ export interface PanControl { /** whether the axis has no travel. */ disabled: boolean; /** position from `0` to `1`. */ fraction: number; /** * set the axis position. * * @param fraction position from `0` to `1`. */ setFraction: (fraction: number) => void; } /** * bind a slider to one pan axis. * * @param axis crop axis. * @returns pan control state. * @throws when called outside a ``. */ export const usePanControl = (axis: 'x' | 'y'): PanControl => { const { setPan } = useCropper(); const fraction = useCropperState((state) => state.pan[axis]); const slack = useCropperState((state) => state.panSlack[axis]); const setFraction = useCallback( (next: number) => { setPan(axis === 'x' ? { x: next } : { y: next }); }, [axis, setPan], ); return { disabled: slack <= 0, fraction, setFraction, }; };