All files / src/components/ui/hooks use-resizable.ts

0% Statements 0/40
0% Branches 0/23
0% Functions 0/8
0% Lines 0/40

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91                                                                                                                                                                                     
import { useState, useEffect, useRef, useCallback } from "react";
 
type ResizeDirection = "left" | "right" | "top" | "bottom";
 
const useResizable = ({
  initialSize,
  minSize = 0,
  maxSize = Infinity,
  direction = "right",
  onResize,
  onResizeComplete,
}: {
  initialSize: number;
  minSize: number;
  maxSize: number;
  direction: ResizeDirection;
  onResize?: (newValue: number) => void;
  onResizeComplete?: (newValue: number) => void;
}): [
  number,
  React.Dispatch<React.SetStateAction<number>>,
  (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => void
] => {
  const [size, setSize] = useState(initialSize);
  const [isResizing, setIsResizing] = useState(false);
  const startSize = useRef<number>(0);
  const startOffset = useRef<number>(0);
 
  const clampSize = useCallback(
    (size: number) => {
      return Math.max(minSize, Math.min(maxSize, size));
    },
    [maxSize, minSize]
  );
 
  const updateSize = useCallback(
    (newSize: number) => {
      onResize?.(clampSize(newSize));
      setSize(newSize);
    },
    [clampSize, onResize]
  );
 
  const onDragStart = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
    if (direction === "left" || direction === "right") {
      startOffset.current = e.pageX;
    } else {
      startOffset.current = e.pageY;
    }
    startSize.current = size;
    setIsResizing(true);
    setSize(clampSize(size));
  };
 
  const onDragMove = useCallback(
    (e: MouseEvent) => {
      if (direction === "left") {
        updateSize(startSize.current - (e.pageX - startOffset.current));
      } else if (direction === "right") {
        updateSize(startSize.current + (e.pageX - startOffset.current));
      } else if (direction === "top") {
        updateSize(startSize.current - (e.pageY - startOffset.current));
      } else Iif (direction === "bottom") {
        updateSize(startSize.current + (e.pageY - startOffset.current));
      }
    },
    [direction, updateSize]
  );
 
  const onDragStop = useCallback(() => {
    setIsResizing(false);
    setSize(clampSize(size));
    onResizeComplete?.(clampSize(size));
  }, [clampSize, onResizeComplete, size]);
 
  useEffect(() => {
    Iif (isResizing) {
      window.addEventListener("mousemove", onDragMove);
      window.addEventListener("mouseup", onDragStop);
      return () => {
        window.removeEventListener("mousemove", onDragMove);
        window.removeEventListener("mouseup", onDragStop);
      };
    }
  }, [isResizing, onDragMove, onDragStop, size]);
 
  return [clampSize(size), setSize, onDragStart];
};
 
export default useResizable;