import React, { useRef, useState, useCallback } from 'react'; export const CAPTURE_SELECTOR = '.crop-overlay'; interface CropBoxOverlayProps { image: string; x1: number; y1: number; x2: number; y2: number; aLocked: boolean; bLocked: boolean; nodeId: string; onWidgetChange: (nodeId: string, name: string, value: unknown) => void; } export default function CropBoxOverlay({ image, x1, y1, x2, y2, aLocked, bLocked, nodeId, onWidgetChange, }: CropBoxOverlayProps) { const containerRef = useRef(null); const [dragging, setDragging] = useState(null); const getCoords = useCallback((e: React.PointerEvent) => { const rect = containerRef.current!.getBoundingClientRect(); return { fx: Math.max(0, Math.min(1, (e.clientX - rect.left) / rect.width)), fy: Math.max(0, Math.min(1, (e.clientY - rect.top) / rect.height)), }; }, []); const onPointerDown = useCallback((point: string) => (e: React.PointerEvent) => { if (point === 'p1' && aLocked) return; if (point === 'p2' && bLocked) return; e.stopPropagation(); e.preventDefault(); (e.target as HTMLElement).setPointerCapture(e.pointerId); setDragging(point); }, [aLocked, bLocked]); const onPointerMove = useCallback((e: React.PointerEvent) => { if (!dragging || !containerRef.current) return; const { fx, fy } = getCoords(e); const vx = parseFloat(fx.toFixed(3)); const vy = parseFloat(fy.toFixed(3)); if (dragging === 'p1') { onWidgetChange(nodeId, 'x1', vx); onWidgetChange(nodeId, 'y1', vy); } else { onWidgetChange(nodeId, 'x2', vx); onWidgetChange(nodeId, 'y2', vy); } }, [dragging, getCoords, nodeId, onWidgetChange]); const onPointerUp = useCallback(() => { setDragging(null); }, []); const left = Math.min(x1, x2); const right = Math.max(x1, x2); const top = Math.min(y1, y2); const bottom = Math.max(y1, y2); return (
crop source
); }