import { useState, useEffect, MouseEvent, useRef, Dispatch, SetStateAction } from 'react'; type Coordinates = { x: number; y: number; } interface DraggableBoxProps { position: Coordinates; className?: string; children?: React.ReactNode; coordSetter: Dispatch>; mode?: 'handle' | 'box'; width?: number; height?: number; onDrag?: (delta: Coordinates) => void; } const DraggableBox = ({ position, className = "", children = "", coordSetter, mode = 'handle', width = 0, height = 0, onDrag }: DraggableBoxProps) => { const [isDragging, setIsDragging] = useState(false); const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 }); const dragRef = useRef(null); useEffect(() => { if (!isDragging) return; const handleMouseMove = (e: globalThis.MouseEvent) => { if (!dragRef.current?.parentElement) return; const parent = dragRef.current.parentElement; const parentRect = parent.getBoundingClientRect(); if (mode === 'handle') { const x = ((e.clientX - parentRect.left - dragOffset.x) / parentRect.width) * 100; const y = ((e.clientY - parentRect.top - dragOffset.y) / parentRect.height) * 100; const newX = Math.min(Math.max(0, x), 100); const newY = Math.min(Math.max(0, y), 100); coordSetter({ x: Number(newX.toFixed(1)), y: Number(newY.toFixed(1)) }); } else { const x = ((e.clientX - parentRect.left - dragOffset.x) / parentRect.width) * 100; const y = ((e.clientY - parentRect.top - dragOffset.y) / parentRect.height) * 100; const maxX = 100 - width; const maxY = 100 - height; const newX = Math.min(Math.max(0, x), maxX); const newY = Math.min(Math.max(0, y), maxY); coordSetter({ x: Number(newX.toFixed(1)), y: Number(newY.toFixed(1)) }); onDrag?.({ x: newX, y: newY }); } }; const handleMouseUp = () => setIsDragging(false); window.addEventListener('mousemove', handleMouseMove); window.addEventListener('mouseup', handleMouseUp); return () => { window.removeEventListener('mousemove', handleMouseMove); window.removeEventListener('mouseup', handleMouseUp); }; }, [isDragging, dragOffset, mode, width, height, onDrag]); const handleMouseDown = (e: MouseEvent) => { if (!dragRef.current?.parentElement) return; const parentRect = dragRef.current.parentElement.getBoundingClientRect(); const elementRect = dragRef.current.getBoundingClientRect(); if (mode === 'handle') { setDragOffset({ x: e.clientX - elementRect.left, y: e.clientY - elementRect.top }); } else { setDragOffset({ x: e.clientX - parentRect.left - (position.x * parentRect.width / 100), y: e.clientY - parentRect.top - (position.y * parentRect.height / 100) }); } setIsDragging(true); }; const style = mode === 'handle' ? { left: `${position.x}%`, top: `${position.y}%`, transform: 'translate(-50%, -50%)', // Center the handle } : { left: `${position.x}%`, top: `${position.y}%`, width: `${width}%`, height: `${height}%`, }; return (
{children}
); }; export default DraggableBox;