DivBucket / src / utils / hooks / useDrag.js
useDrag.js
Raw
import { useRef } from "react";
import { useDispatch } from "react-redux";
import { createTemplate } from "../template";
import { moveItem } from "../../store/reducers/treeReducer";
import styles from "../Resizable/resizable.module.css";

const getPosi = (x, y, rect, display, direction) => {
  const xoff = x - rect.x;
  const yoff = y - rect.y;
  let xpos = 0,
    ypos = 0;
  if (xoff > (rect.width * 20) / 100) xpos = 1;
  if (xoff > (rect.width * 80) / 100) xpos = 2;
  if (yoff > (rect.height * 20) / 100) ypos = 1;
  if (yoff > (rect.height * 80) / 100) ypos = 2;

  if (display === "block" || ["column", "column-reverse"].includes(direction)) {
    if (ypos === 0) return "top";
    if (ypos === 1) return "inside";
    return "bottom";
  } else {
    if (xpos === 0) return "left";
    if (xpos === 1) return "inside";
    return "right";
  }
};

export function useDrag({ root }) {
  const dispatch = useDispatch();
  const draggedNodeRef = useRef(null);
  const draggedWrapperRef = useRef(null);

  const handleDragStart = (e) => {
    e.stopPropagation();
    if (!e.target.getAttribute("data-id")) return;
    draggedNodeRef.current = e.target;
    let draggedWrapper = document.createElement("div");
    let draggedImage = document.createElement("div");
    draggedWrapper.classList.add(styles.dragwrapper);
    draggedImage.classList.add(styles.dragimage);
    draggedImage.innerText = e.target.parentNode.children[0].innerText;
    draggedWrapper.appendChild(draggedImage);
    document.body.appendChild(draggedWrapper);
    e.dataTransfer.setDragImage(draggedImage, -20, -20);
    draggedWrapperRef.current = draggedWrapper;
  };
  const handleDragEnd = () => {
    draggedNodeRef.current = null;
    if (draggedWrapperRef && draggedWrapperRef.current)
      draggedWrapperRef.current.remove();
    draggedWrapperRef.current = null;
  };
  const handleDragOver = (e) => {
    e.preventDefault();
    e.stopPropagation();
    const targetId =
      e.target.getAttribute("data-target") ||
      e.target.getAttribute("data-root");
    const draggedId = draggedNodeRef.current?.getAttribute("data-id");

    if (!targetId || draggedId == targetId) return;

    if (targetId == root) {
    } else {
      let aParent = e.target.parentNode?.parentNode;
      if (e.target.getAttribute("data-target")) aParent = aParent?.parentNode;
      if (!aParent) return;
      const display = aParent.style.display || "block";
      const direction = aParent.style.flexDirection || "row";

      e.target.classList.add(styles.dborder);
      const rect = e.target.getBoundingClientRect();
      switch (getPosi(e.clientX, e.clientY, rect, display, direction)) {
        case "inside":
          e.target.classList.remove(
            styles.dtop,
            styles.dbottom,
            styles.dleft,
            styles.dright
          );
          e.target.classList.add(styles.dinside);
          break;
        case "top":
          e.target.classList.remove(
            styles.dinside,
            styles.dbottom,
            styles.dleft,
            styles.dright
          );
          e.target.classList.add(styles.dtop);
          break;
        case "right":
          e.target.classList.remove(
            styles.dinside,
            styles.dtop,
            styles.dbottom,
            styles.dleft
          );
          e.target.classList.add(styles.dright);
          break;
        case "bottom":
          e.target.classList.remove(
            styles.dinside,
            styles.dtop,
            styles.dleft,
            styles.dright
          );
          e.target.classList.add(styles.dbottom);
          break;
        case "left":
          e.target.classList.remove(
            styles.dinside,
            styles.dtop,
            styles.dbottom,
            styles.dright
          );
          e.target.classList.add(styles.dleft);
          break;
        default:
          break;
      }
    }
  };
  const handleDrop = (e) => {
    e.stopPropagation();

    e.target.classList.remove(
      styles.dborder,
      styles.dinside,
      styles.dtop,
      styles.dbottom,
      styles.dleft,
      styles.dright
    );

    const targetId =
      e.target.getAttribute("data-target") ||
      e.target.getAttribute("data-root");
    const draggedId =
      draggedNodeRef.current?.getAttribute("data-id") ||
      createTemplate({ type: e.dataTransfer.getData("type"), dispatch });

    if (!targetId || !draggedId || draggedId == targetId) return;

    if (targetId == root) {
      dispatch(moveItem({ node: draggedId, referenceNode: targetId, pos: -1 }));
    } else {
      let aParent = e.target.parentNode?.parentNode;
      if (e.target.getAttribute("data-target")) aParent = aParent?.parentNode;
      if (!aParent) return;
      const display = aParent.style.display || "block";
      const direction = aParent.style.flexDirection || "row";

      const rect = e.target.getBoundingClientRect();
      switch (getPosi(e.clientX, e.clientY, rect, display, direction)) {
        case "inside":
          dispatch(
            moveItem({ node: draggedId, referenceNode: targetId, pos: -1 })
          );
          break;
        case "top":
          dispatch(
            moveItem({ node: draggedId, referenceNode: targetId, pos: 0 })
          );
          break;
        case "right":
          dispatch(
            moveItem({ node: draggedId, referenceNode: targetId, pos: 1 })
          );
          break;
        case "bottom":
          dispatch(
            moveItem({ node: draggedId, referenceNode: targetId, pos: 1 })
          );
          break;
        case "left":
          dispatch(
            moveItem({ node: draggedId, referenceNode: targetId, pos: 0 })
          );
          break;
        default:
          break;
      }
    }
    draggedNodeRef.current = null;
    if (draggedWrapperRef && draggedWrapperRef.current)
      draggedWrapperRef.current.remove();
    draggedWrapperRef.current = null;
  };
  const handleDragLeave = (e) => {
    e.target.classList.remove(
      styles.dborder,
      styles.dinside,
      styles.dtop,
      styles.dbottom,
      styles.dleft,
      styles.dright
    );
  };

  return {
    handleDragStart,
    handleDragEnd,
    handleDragOver,
    handleDrop,
    handleDragLeave,
  };
}