import { Paragraph, Spinner } from "@digdir/designsystemet-react"; import { getLocalizedString } from "@olenbetong/appframe-core"; import { CropCanvas } from "../../CropCanvas.js"; import { DragHandle } from "../../DragHandle.js"; import { EdgeHandle } from "../../EdgeHandle.js"; import { useZoomLevel } from "../zoomPlugin/ZoomContext.js"; import { useCropContext } from "./CropContext.js"; /** Canvas-area overlay for the crop tool. Reads all state from CropContext. */ export function CropOverlay() { let { imageUrl, activeCorners, mode, hasSelection, scale, displaySize, canvasRef, cvReady, cvError, cropping, error, handleCanvasReady, updateCorner, startRectDraw, updateRectDraw, finishRectDraw, } = useCropContext(); let zoom = useZoomLevel(); let showHandles = mode === "corners" && displaySize.width > 0; function pointerPosition(e: React.PointerEvent) { // getBoundingClientRect() is screen-space (affected by the zoom transform), // so divide by zoom to get back to the CSS-pixel space the tool expects. let rect = e.currentTarget.getBoundingClientRect(); return { x: (e.clientX - rect.left) / zoom, y: (e.clientY - rect.top) / zoom }; } function onPointerDown(e: React.PointerEvent) { e.currentTarget.setPointerCapture(e.pointerId); let { x, y } = pointerPosition(e); startRectDraw(x, y); } function onPointerMove(e: React.PointerEvent) { if (!(e.buttons & 1)) return; let { x, y } = pointerPosition(e); updateRectDraw(x, y); } return (
{mode === "rect" && displaySize.width > 0 && (
)} {showHandles && activeCorners.map((corner, i) => ( updateCorner(i, x, y)} containerWidth={displaySize.width} containerHeight={displaySize.height} /> ))} {showHandles && (() => { let [tl, tr, br, bl] = activeCorners; let s = scale; return ( <> {/* Top edge — moves TL (0) and TR (1) */} { updateCorner(0, tl.x * s + dx, tl.y * s + dy); updateCorner(1, tr.x * s + dx, tr.y * s + dy); }} /> {/* Right edge — moves TR (1) and BR (2) */} { updateCorner(1, tr.x * s + dx, tr.y * s + dy); updateCorner(2, br.x * s + dx, br.y * s + dy); }} /> {/* Bottom edge — moves BR (2) and BL (3) */} { updateCorner(2, br.x * s + dx, br.y * s + dy); updateCorner(3, bl.x * s + dx, bl.y * s + dy); }} /> {/* Left edge — moves BL (3) and TL (0) */} { updateCorner(3, bl.x * s + dx, bl.y * s + dy); updateCorner(0, tl.x * s + dx, tl.y * s + dy); }} /> ); })()}
{/* Status line. OpenCV only powers the border suggestion, so a load failure is informational — cropping stays available either way. */} {cropping ? (
) : error ? (
{error}
) : mode === "rect" ? (
{hasSelection ? getLocalizedString("Release to use this rectangle") : getLocalizedString("Drag to draw a rectangle to crop to")}
) : !cvReady && !cvError ? (
{getLocalizedString("Detecting borders…")}
) : cvError ? (
{getLocalizedString("Automatic border detection is unavailable. Adjust the selection manually.")}
) : null}
); }