import type { RefObject } from 'react'; import type { MenuItemProps, MenuProps } from 'react-aria-components'; import { useEffect, useRef, useState } from 'react'; import { Menu, MenuItem, Popover } from 'react-aria-components'; import { twMerge } from 'tailwind-merge'; export interface ContextMenuProps extends MenuProps { className?: string; isOpen: boolean; setOpen: (open: boolean) => void; triggerRef: RefObject; } export function ContextMenu({ className, isOpen, setOpen, triggerRef, children, ...rest }: ContextMenuProps) { const [position, setPosition] = useState({ x: 0, y: 0 }); const menuRef = useRef(null); useEffect(() => { const trigger = triggerRef.current; if (!trigger) return; const onContextMenu = (e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); if (isOpen) { setOpen(false); return; } setPosition({ x: e.clientX, y: e.clientY, }); setOpen(true); }; trigger.addEventListener('contextmenu', onContextMenu); return () => { trigger.removeEventListener('contextmenu', onContextMenu); }; }, [isOpen, setOpen, triggerRef]); useEffect(() => { const onClick = (e: MouseEvent) => { if (e.target && menuRef.current?.contains(e.target as Node)) return; setOpen(false); }; window.addEventListener('click', onClick); return () => { window.removeEventListener('click', onClick); }; }, [setOpen, triggerRef]); return ( {children} ); } ContextMenu.Item = ({ className, ...rest }: MenuItemProps) => ;