All files / Rindu/components/ContextMenu ContextMenu.tsx

100% Statements 17/17
100% Branches 8/8
100% Functions 3/3
100% Lines 17/17

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 92 93 94 95 96 97 9819x           19x   19x           19x   88x 52x 52x       52x       52x   52x               52x 45x                 52x 42x     10x           10x                       12x                                                          
import { ReactElement, useEffect, useState } from "react";
 
import {
  CardContentContextMenu,
  CardTrackContextMenu,
  PortalTarget,
} from "components";
import type { ICardContentContextMenu } from "components/CardContentContextMenu";
import { useContextMenu, useEventListener } from "hooks";
import type { ITrack } from "types/spotify";
import {
  calculateContextMenuPosition,
  CONTEXT_MENU_SIDE_OFFSET,
  positionContextMenu,
} from "utils";
 
export default function ContextMenu(): ReactElement | null {
  const { contextMenuData, removeContextMenu } = useContextMenu();
  const [contextMenuPos, setContextMenuPos] = useState({
    x: (contextMenuData?.position.x ?? 0) - 30,
    y: (contextMenuData?.position.y ?? 0) - 40,
  });
  const [isContextMenuOffscreen, setIsContextMenuOffscreen] = useState({
    x: false,
    y: false,
  });
  const [element, setElement] = useState<HTMLElement | null>(null);
 
  useEventListener({
    target: document.querySelector("#__next"),
    type: "click",
    listener: removeContextMenu,
    options: { once: true },
    ignore: !contextMenuData?.data,
  });
 
  useEffect(() => {
    positionContextMenu({
      currentPosition: contextMenuData?.position,
      element: element,
      setPosition: setContextMenuPos,
      setIsOffScreen: setIsContextMenuOffscreen,
      offsets: { x: 30, y: 10 },
    });
  }, [contextMenuData?.position, element]);
 
  if (!contextMenuData) {
    return null;
  }
 
  const top = calculateContextMenuPosition(
    isContextMenuOffscreen.y,
    contextMenuPos.y,
    contextMenuData.position.y,
    CONTEXT_MENU_SIDE_OFFSET
  );
  const left = calculateContextMenuPosition(
    isContextMenuOffscreen.x,
    contextMenuPos.x,
    contextMenuData.position.x,
    CONTEXT_MENU_SIDE_OFFSET
  );
 
  return (
    <PortalTarget targetId="contextMenu">
      <section
        role="menu"
        data-type={contextMenuData.data?.type}
        ref={(element) => setElement(element)}
        style={{ top, left }}
      >
        {contextMenuData.data?.type === "track" ||
        contextMenuData.data?.type === "episode" ? (
          <CardTrackContextMenu track={contextMenuData.data as ITrack} />
        ) : (
          <CardContentContextMenu
            data={contextMenuData.data as ICardContentContextMenu["data"]}
          />
        )}
        <style jsx>{`
          section {
            max-width: 400px;
            width: fit-content;
            position: absolute;
            margin: 0 auto;
            border-radius: 5px;
            background-color: #282828;
            box-shadow: 0px 2px 9px 0px rgb(0 0 0 / 5%);
            padding: 3px;
            max-height: 95vh;
            z-index: 999999999999;
          }
        `}</style>
      </section>
    </PortalTarget>
  );
}