import { useCallback, useState, type Dispatch, type SetStateAction } from 'react' import type { VectorSelectionBBox } from '../types' import { bboxToInputState, normalizeBboxFromCorners, parseBboxInput } from '../components/map/mapWorkspaceUtils' /** * The state of drawing a rectangle on the map: whether a gesture is in * progress, which corner has been placed, and what is typed in the coordinate * fields. * * Deliberately only the interaction state. What happens *with* a finished * rectangle — retiring stale results, starting an analysis — belongs to the * workspace, which owns those. Injecting that here would make the hook depend * on values declared after it. */ export type BboxInputState = ReturnType export interface MapRectangleSelection { /** A rectangle gesture is in progress. */ drawing: boolean setDrawing: Dispatch> /** The first click of a two-click rectangle, if one has been placed. */ firstCorner: [number, number] | null bboxInput: BboxInputState setBboxInput: Dispatch> /** The rectangle currently in the coordinate fields, if it parses. */ typedBbox: VectorSelectionBBox | null /** * Place a corner. Returns the finished rectangle on the second corner, and * ``null`` on the first — the caller only acts when a rectangle exists. */ placeCorner: (coordinate: [number, number]) => VectorSelectionBBox | null /** Abandon a half-drawn rectangle and leave drawing mode. */ reset: () => void /** Show a rectangle in the coordinate fields. */ showBbox: (bbox: VectorSelectionBBox | null) => void } export function useMapRectangleSelection(initialBbox: VectorSelectionBBox | null): MapRectangleSelection { const [drawing, setDrawing] = useState(false) const [firstCorner, setFirstCorner] = useState<[number, number] | null>(null) const [bboxInput, setBboxInput] = useState(bboxToInputState(initialBbox)) const placeCorner = useCallback( (coordinate: [number, number]): VectorSelectionBBox | null => { if (!firstCorner) { setFirstCorner(coordinate) return null } setFirstCorner(null) setDrawing(false) return normalizeBboxFromCorners(firstCorner, coordinate) }, [firstCorner], ) const reset = useCallback(() => { setFirstCorner(null) setDrawing(false) }, []) const showBbox = useCallback((bbox: VectorSelectionBBox | null) => { setBboxInput(bboxToInputState(bbox)) }, []) return { drawing, setDrawing, firstCorner, bboxInput, setBboxInput, typedBbox: parseBboxInput(bboxInput), placeCorner, reset, showBbox, } }