import { onMounted, onUnmounted, watch } from 'vue'; import type { Ref } from 'vue'; interface Constraints { top: number; right: number; bottom: number; left: number; } interface ResizeOptions { onResize: (data: { x: number; y: number; width: number; height: number; }) => void; onResizeStart?: () => void; onResizeEnd?: () => void; initialSize: { width: number; height: number; }; initialPosition: { x: number; y: number; }; constraints?: Ref; enabled: Ref; } export function useResizable( target: Ref, options: ResizeOptions ) { const { onResize, onResizeStart, onResizeEnd, initialSize, initialPosition, constraints, enabled } = options; let activeHandle: string | null = null; let initialMouseX = 0; let initialMouseY = 0; let initialRect = { x: 0, y: 0, width: 0, height: 0 }; const handleMouseDown = (event: MouseEvent) => { if (!enabled.value) return; const handle = (event.target as HTMLElement).dataset.direction; if (!handle) return; event.preventDefault(); event.stopPropagation(); if (onResizeStart) onResizeStart(); activeHandle = handle; initialMouseX = event.clientX; initialMouseY = event.clientY; initialRect = { ...initialSize, ...initialPosition, }; document.addEventListener('mousemove', handleMouseMove); document.addEventListener('mouseup', handleMouseUp); }; const handleMouseMove = (event: MouseEvent) => { if (!activeHandle) return; const dx = event.clientX - initialMouseX; const dy = event.clientY - initialMouseY; let { x, y, width, height } = initialRect; if (activeHandle.includes('e')) { width = initialRect.width + dx; } if (activeHandle.includes('w')) { width = initialRect.width - dx; x = initialRect.x + dx; } if (activeHandle.includes('s')) { height = initialRect.height + dy; } if (activeHandle.includes('n')) { height = initialRect.height - dy; y = initialRect.y + dy; } if (constraints?.value) { const { top, right, bottom, left } = constraints.value; if (x < left) { width -= (left - x); x = left; } if (y < top) { height -= (top - y); y = top; } if (x + width > right) { width = right - x; } if (y + height > bottom) { height = bottom - y; } } width = Math.max(200, width); height = Math.max(150, height); onResize({ x, y, width, height }); }; const handleMouseUp = () => { if (onResizeEnd) onResizeEnd(); activeHandle = null; document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }; const addListener = () => { if (target.value) { target.value.addEventListener('mousedown', handleMouseDown); } }; const removeListener = () => { if (target.value) { target.value.removeEventListener('mousedown', handleMouseDown); } }; // Watch both the target and the enabled flag watch([target, enabled], ([newTarget, isEnabled]) => { if (newTarget) { if (isEnabled) { addListener(); } else { removeListener(); } } }, { immediate: true }); onUnmounted(() => { removeListener(); document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mouseup', handleMouseUp); }); }