| import { useCallback, useEffect, useRef, useState } from 'react'; |
|
|
| type Direction = 'horizontal' | 'vertical'; |
|
|
| interface UseResizableOptions { |
| direction: Direction; |
| initialSize: number; |
| minSize: number; |
| maxSize: number; |
| |
| reverse?: boolean; |
| } |
|
|
| export default function useResizable({ |
| direction, |
| initialSize, |
| minSize, |
| maxSize, |
| reverse = false, |
| }: UseResizableOptions) { |
| const [size, setSize] = useState(initialSize); |
| const [isDragging, setIsDragging] = useState(false); |
| const startPos = useRef(0); |
| const startSize = useRef(0); |
|
|
| const onMouseDown = useCallback( |
| (e: React.MouseEvent) => { |
| e.preventDefault(); |
| setIsDragging(true); |
| startPos.current = direction === 'horizontal' ? e.clientY : e.clientX; |
| startSize.current = size; |
| }, |
| [direction, size], |
| ); |
|
|
| useEffect(() => { |
| if (!isDragging) return; |
|
|
| const onMouseMove = (e: MouseEvent) => { |
| const currentPos = direction === 'horizontal' ? e.clientY : e.clientX; |
| const delta = currentPos - startPos.current; |
| const adjust = reverse ? -delta : delta; |
| const next = Math.min(maxSize, Math.max(minSize, startSize.current + adjust)); |
| setSize(next); |
| }; |
|
|
| const onMouseUp = () => setIsDragging(false); |
|
|
| document.addEventListener('mousemove', onMouseMove); |
| document.addEventListener('mouseup', onMouseUp); |
| document.body.style.cursor = direction === 'horizontal' ? 'row-resize' : 'col-resize'; |
| document.body.style.userSelect = 'none'; |
|
|
| return () => { |
| document.removeEventListener('mousemove', onMouseMove); |
| document.removeEventListener('mouseup', onMouseUp); |
| document.body.style.cursor = ''; |
| document.body.style.userSelect = ''; |
| }; |
| }, [isDragging, direction, minSize, maxSize, reverse]); |
|
|
| return { size, isDragging, onMouseDown }; |
| } |
|
|