import React, { useState, useEffect, useRef } from 'react' import ReactDOM from 'react-dom' import { LucideIcon } from 'lucide-react' interface MenuItem { label?: string icon?: LucideIcon onClick?: () => void danger?: boolean divider?: boolean } interface MenuState { x: number y: number items: MenuItem[] } export function useContextMenu() { const [menu, setMenu] = useState(null) const open = (e: React.MouseEvent, items: MenuItem[]) => { e.preventDefault() e.stopPropagation() setMenu({ x: e.clientX, y: e.clientY, items }) } const close = () => setMenu(null) return { menu, open, close } } interface ContextMenuProps { menu: MenuState | null onClose: () => void } export function ContextMenu({ menu, onClose }: ContextMenuProps) { const ref = useRef(null) useEffect(() => { if (!menu) return const handler = () => onClose() document.addEventListener('click', handler) document.addEventListener('contextmenu', handler) return () => { document.removeEventListener('click', handler) document.removeEventListener('contextmenu', handler) } }, [menu, onClose]) useEffect(() => { if (!menu || !ref.current) return const el = ref.current const rect = el.getBoundingClientRect() let { x, y } = menu if (x + rect.width > window.innerWidth - 8) x = window.innerWidth - rect.width - 8 if (y + rect.height > window.innerHeight - 8) y = window.innerHeight - rect.height - 8 if (x !== menu.x || y !== menu.y) { el.style.left = `${x}px` el.style.top = `${y}px` } }, [menu]) if (!menu) return null return ReactDOM.createPortal(
{menu.items.filter(Boolean).map((item, i) => { if (item.divider) return
const Icon = item.icon return ( ) })}
, document.body ) }