DivBucket / src / utils / hooks / useResizer.js
useResizer.js
Raw
import { useEffect, useRef, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { updateStyleMap } from "../../store/reducers/treeReducer";
import styles from '../Resizable/resizable.module.css';

export function useResizer({ id }) {
  const styleMap = useSelector((state) => state.treeReducer.styleMap[id]);
  const virtualPos = useRef({
    top: null,
    bottom: null,
    left: null,
    right: null,
  });
  const [dim, setDim] = useState({
    width: styleMap.width,
    height: styleMap.height,
  });
  const divRef = useRef();
  const dirRef = useRef();
  const isResizingRef = useRef(false);
  const dispatch = useDispatch();

  useEffect(() => {
    setDim({
      width: styleMap.width,
      height: styleMap.height,
    });
  }, [styleMap, id]);

  const calWidth = () =>
    Math.floor(
      Math.max(0, virtualPos.current.right - virtualPos.current.left)
    ) + "px";
  const calHeight = () =>
    Math.floor(
      Math.max(0, virtualPos.current.bottom - virtualPos.current.top)
    ) + "px";

  const initVirtualPosition = () => {
    if (divRef.current) {
      const rect = divRef.current.getBoundingClientRect();
      virtualPos.current = {
        top: rect.top,
        left: rect.left,
        right: rect.right,
        bottom: rect.bottom,
      };
    }
  };
  const handleMouseMove = (e) => {
    if (!isResizingRef.current || !divRef.current) return;
    let newDim = { ...dim };
    switch (dirRef.current) {
      case 0:
        virtualPos.current.top = e.clientY;
        newDim.height = calHeight();
        break;
      case 1:
        virtualPos.current.right = e.clientX;
        newDim.width = calWidth();
        break;
      case 2:
        virtualPos.current.bottom = e.clientY;
        newDim.height = calHeight();
        break;
      case 3:
        virtualPos.current.left = e.clientX;
        newDim.width = calWidth();
        break;
    }
    setDim(newDim);
  };
  const handleMouseUp = () => {
    divRef.current.classList.remove(styles.layer)
    divRef.current.style.userSelect = "";
    isResizingRef.current = false;
    dispatch(
      updateStyleMap({
        id,
        style: {
          ...styleMap,
          width: Number(dirRef.current) % 2 ? calWidth() : styleMap.width,
          height: Number(dirRef.current) % 2 ? styleMap.height : calHeight(),
        },
      })
    );
    dirRef.current = null;
    document.removeEventListener("mousemove", handleMouseMove);
    document.removeEventListener("mouseup", handleMouseUp);
  };
  const handleMouseDown = (e, direction) => {
    e.preventDefault();
    e.stopPropagation();
    divRef.current.classList.add(styles.layer)
    isResizingRef.current = true;
    dirRef.current = direction;
    initVirtualPosition();
    divRef.current.style.userSelect = "none";
    document.addEventListener("mousemove", handleMouseMove);
    document.addEventListener("mouseup", handleMouseUp);
  };

  return {
    divRef,
    dim,
    handleMouseDown,
  };
}