import classNames from 'classnames'; import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react'; import { useCallback, useRef, useState } from 'react'; const START_DISTANCE = 7; export interface ResizeHandleEvent { x: number; y: number; xStart: number; yStart: number; } interface Props { style?: CSSProperties; className?: string; onResizeStart?: () => void; onResizeEnd?: () => void; onResizeMove?: (e: ResizeHandleEvent) => void; onReset?: () => void; side: 'left' | 'right' | 'top'; justify: 'center' | 'end' | 'start'; } export function ResizeHandle({ style, justify, className, onResizeStart, onResizeEnd, onResizeMove, onReset, side, }: Props) { const vertical = side === 'top'; const [isResizing, setIsResizing] = useState(false); const moveState = useRef<{ move: (e: MouseEvent) => void; up: (e: MouseEvent) => void; calledStart: boolean; xStart: number; yStart: number; } | null>(null); const handlePointerDown = useCallback( (e: ReactMouseEvent) => { function move(e: MouseEvent) { if (moveState.current == null) return; const xDistance = moveState.current.xStart - e.clientX; const yDistance = moveState.current.yStart - e.clientY; const distance = Math.abs(vertical ? yDistance : xDistance); if (moveState.current.calledStart) { onResizeMove?.({ x: e.clientX, y: e.clientY, xStart: moveState.current.xStart, yStart: moveState.current.yStart, }); } else if (distance > START_DISTANCE) { onResizeStart?.(); moveState.current.calledStart = true; setIsResizing(true); } } function up() { setIsResizing(false); moveState.current = null; document.documentElement.removeEventListener('mousemove', move); document.documentElement.removeEventListener('mouseup', up); onResizeEnd?.(); } moveState.current = { calledStart: false, xStart: e.clientX, yStart: e.clientY, move, up }; document.documentElement.addEventListener('mousemove', move); document.documentElement.addEventListener('mouseup', up); }, [onResizeEnd, onResizeMove, onResizeStart, vertical], ); return (
{/* Show global overlay with cursor style to ensure cursor remains the same when moving quickly */} {isResizing && (
)}
); }