import React, { useEffect, useRef } from "react"; import Link from "next/link"; // hooks import useOutsideClickDetector from "hooks/use-outside-click-detector"; type Props = { clickEvent: React.MouseEvent | null; children: React.ReactNode; title?: string | JSX.Element; isOpen: boolean; setIsOpen: React.Dispatch>; }; const ContextMenu = ({ clickEvent, children, title, isOpen, setIsOpen }: Props) => { const contextMenuRef = useRef(null); // Close the context menu when clicked outside useOutsideClickDetector(contextMenuRef, () => { if (isOpen) setIsOpen(false); }); useEffect(() => { const hideContextMenu = () => { if (isOpen) setIsOpen(false); }; const escapeKeyEvent = (e: KeyboardEvent) => { if (e.key === "Escape") hideContextMenu(); }; window.addEventListener("click", hideContextMenu); window.addEventListener("keydown", escapeKeyEvent); return () => { window.removeEventListener("click", hideContextMenu); window.removeEventListener("keydown", escapeKeyEvent); }; }, [isOpen, setIsOpen]); useEffect(() => { const contextMenu = contextMenuRef.current; if (contextMenu && isOpen) { const contextMenuWidth = contextMenu.clientWidth; const contextMenuHeight = contextMenu.clientHeight; const clickX = clickEvent?.pageX || 0; const clickY = clickEvent?.pageY || 0; let top = clickY; // check if there's enough space at the bottom, otherwise show at the top if (clickY + contextMenuHeight > window.innerHeight) top = clickY - contextMenuHeight; // check if there's enough space on the right, otherwise show on the left let left = clickX; if (clickX + contextMenuWidth > window.innerWidth) left = clickX - contextMenuWidth; contextMenu.style.top = `${top}px`; contextMenu.style.left = `${left}px`; } }, [clickEvent, isOpen]); return (
{title && (

{title}

)} {children}
); }; type MenuItemProps = { children: JSX.Element | string; renderAs?: "button" | "a"; href?: string; onClick?: () => void; className?: string; Icon?: any; }; const MenuItem: React.FC = ({ children, renderAs, href = "", onClick, className = "", Icon, }) => ( <> {renderAs === "a" ? ( <> {Icon && } {children} ) : ( )} ); ContextMenu.Item = MenuItem; export { ContextMenu };