All files / src/components/ui/hooks use-context-menu.tsx

0% Statements 0/29
0% Branches 0/9
0% Functions 0/7
0% Lines 0/28

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 98 99 100 101 102                                                                                                                                                                                                           
import React, {
  JSX,
  ReactNode,
  useCallback,
  useEffect,
  useMemo,
  useState,
} from "react";
import { MaybePromise } from "shared/types";
import { ContextMenu } from "ui/menu/ContextMenu";
 
const CLOSE_CONTEXT_MENUS_EVENT = "close-context-menus";
 
const closeAllContextMenus = () => {
  window.dispatchEvent(new Event(CLOSE_CONTEXT_MENUS_EVENT));
};
 
interface ContextMenuState {
  x: number;
  y: number;
  menu: JSX.Element[];
}
 
interface UseContextMenuOptions {
  enabled?: boolean;
  getIsEnabled?: (e: React.MouseEvent) => boolean;
  getMenu: (args: {
    closeMenu: () => void;
    event: React.MouseEvent;
  }) => MaybePromise<JSX.Element[] | undefined>;
}
 
interface UseContextMenuResult {
  onContextMenu: (e: React.MouseEvent) => void;
  closeMenu: () => void;
  contextMenuElement: ReactNode;
  isOpen: boolean;
}
 
export const useContextMenu = ({
  enabled = true,
  getIsEnabled,
  getMenu,
}: UseContextMenuOptions): UseContextMenuResult => {
  const [contextMenu, setContextMenu] = useState<ContextMenuState | null>(null);
 
  const closeMenu = useCallback(() => {
    setContextMenu(null);
  }, []);
 
  useEffect(() => {
    window.addEventListener(CLOSE_CONTEXT_MENUS_EVENT, closeMenu);
    return () => {
      window.removeEventListener(CLOSE_CONTEXT_MENUS_EVENT, closeMenu);
    };
  }, [closeMenu]);
 
  const onContextMenu = useCallback(
    async (e: React.MouseEvent) => {
      Iif (!enabled || (getIsEnabled !== undefined && !getIsEnabled(e))) {
        return;
      }
 
      e.preventDefault();
      e.stopPropagation();
      closeAllContextMenus();
 
      const menu = await getMenu({ closeMenu, event: e });
 
      Iif (!menu || menu.length === 0) {
        return;
      }
 
      setContextMenu({
        x: e.pageX,
        y: e.pageY,
        menu,
      });
    },
    [enabled, getIsEnabled, getMenu, closeMenu],
  );
 
  const contextMenuElement = useMemo(() => {
    Iif (!contextMenu) {
      return null;
    }
 
    return (
      <ContextMenu x={contextMenu.x} y={contextMenu.y} onClose={closeMenu}>
        {contextMenu.menu}
      </ContextMenu>
    );
  }, [contextMenu, closeMenu]);
 
  return {
    onContextMenu,
    closeMenu,
    contextMenuElement,
    isOpen: contextMenu !== null,
  };
};