import { useCallback, useEffect, useRef, type RefObject } from 'react'; import { bindWindowDrag, isPrimaryPointerButton, } from '../utils/pointerDrag'; export interface Size2d { width: number; height: number; } export interface Position2d { x: number; y: number; } export interface FloatingWidgetResizeResult { width: number; height: number; x?: number; } export type FloatingWidgetResizeCorner = 'se' | 'sw'; function clampSize( width: number, height: number, minSize: Size2d, maxW: number, maxH: number, ): Size2d { return { width: Math.round(Math.max(minSize.width, Math.min(maxW, width))), height: Math.round(Math.max(minSize.height, Math.min(maxH, height))), }; } export function useFloatingWidgetResize( panelRef: RefObject, size: Size2d, minSize: Size2d, onResize: (result: FloatingWidgetResizeResult) => void, ) { const unbindDragRef = useRef<(() => void) | null>(null); const endResize = useCallback(() => { unbindDragRef.current?.(); unbindDragRef.current = null; }, []); useEffect(() => () => { unbindDragRef.current?.(); }, []); const startResize = useCallback(( e: React.PointerEvent, corner: FloatingWidgetResizeCorner, ) => { if (!isPrimaryPointerButton(e)) return; e.preventDefault(); e.stopPropagation(); const handle = e.currentTarget as HTMLElement; try { handle.setPointerCapture(e.pointerId); } catch { /* ignore */ } const el = panelRef.current; if (!el) return; const rect = el.getBoundingClientRect(); const startX = e.clientX; const startY = e.clientY; const startW = size.width; const startH = size.height; const startRight = rect.right; const maxH = window.innerHeight - rect.top - 8; unbindDragRef.current?.(); unbindDragRef.current = bindWindowDrag( (xy) => { if (corner === 'se') { const maxW = window.innerWidth - rect.left - 8; const next = clampSize( startW + xy.x - startX, startH + xy.y - startY, minSize, maxW, maxH, ); onResize(next); return; } /* sw — 우측 가장자리 고정, 좌측·하단 조절 */ let width = startRight - xy.x; let x = xy.x; if (x < 8) { x = 8; width = startRight - 8; } const maxW = startRight - 8; const next = clampSize(width, startH + xy.y - startY, minSize, maxW, maxH); onResize({ ...next, x: Math.round(startRight - next.width) }); }, endResize, { preventTouchScroll: true }, ); }, [panelRef, size.width, size.height, minSize, onResize, endResize]); const onResizeSePointerDown = useCallback( (e: React.PointerEvent) => startResize(e, 'se'), [startResize], ); const onResizeSwPointerDown = useCallback( (e: React.PointerEvent) => startResize(e, 'sw'), [startResize], ); return { onResizeSePointerDown, onResizeSwPointerDown }; }