pankod/refine

View on GitHub
packages/devtools/src/components/devtools-selector.tsx

Summary

Maintainability
A
0 mins
Test Coverage
import React from "react";
import { createPortal } from "react-dom";
import { useSelector } from "src/utilities/use-selector";
import { SelectorBox } from "./selector-box";
import { SelectorHint } from "./selector-hint";

type Props = {
  onSelectorOpen: () => void;
  onHighlight: (name: string) => void;
  icon?: React.ReactNode;
  style?: React.CSSProperties;
};

export const DevtoolsSelector = ({
  onSelectorOpen,
  onHighlight,
  icon,
  style,
}: Props) => {
  const [hover, setHover] = React.useState(false);
  const [active, setActive] = React.useState(false);
  const { rect, name } = useSelector(active);

  const [selectorBoxRoot, setSelectorBoxRoot] =
    React.useState<HTMLElement | null>(null);

  React.useEffect(() => {
    if (!selectorBoxRoot) {
      const element = document.createElement("div");
      element.id = "selector-box-root";

      document.body.appendChild(element);

      setSelectorBoxRoot(element);
    }
  }, []);

  React.useEffect(() => {
    if (active) {
      document.body.style.cursor = "crosshair";
    } else {
      document.body.style.cursor = "default";
    }
  }, [active]);

  React.useEffect(() => {
    const onMouseClick = (e: MouseEvent) => {
      if (!active) return;
      if (!name) return;

      e?.preventDefault();
      e?.stopPropagation();
      e.stopImmediatePropagation();
      onHighlight(name);
      setActive(false);
    };

    if (active) {
      document.addEventListener("click", onMouseClick, {
        capture: true,
      });

      return () => {
        document.removeEventListener("click", onMouseClick, {
          capture: true,
        });
      };
    }

    return () => 0;
  }, [name, onHighlight, active]);

  React.useEffect(() => {
    if (active) {
      onSelectorOpen();
    }
  }, [active, onSelectorOpen]);

  return (
    <div style={style}>
      <div
        role="button"
        title="Element Selector"
        onMouseOver={() => setHover(true)}
        onMouseOut={() => setHover(false)}
        onClick={(event) => {
          event.preventDefault();
          event.stopPropagation();
          (document?.activeElement as HTMLElement)?.blur();
          setActive((active) => !active);
        }}
        style={{
          padding: 0,
          margin: 0,
          height: "100%",
          width: "100%",
          transform: hover ? "rotate(180deg)" : "rotate(0deg)",
          transition: "transform 0.2s ease-in-out",
        }}
      >
        {icon}
      </div>
      <SelectorHint active={active} />
      {active &&
        selectorBoxRoot &&
        createPortal(<SelectorBox {...rect} name={name} />, selectorBoxRoot)}
    </div>
  );
};