File size: 1,917 Bytes
f91a684 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | import { useCallback, useEffect, useRef, useState } from 'react';
type Direction = 'horizontal' | 'vertical';
interface UseResizableOptions {
direction: Direction;
initialSize: number;
minSize: number;
maxSize: number;
/** If true, the "size" represents the *second* panel (e.g. right panel width). */
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 };
}
|