import { useEffect, useRef, useState } from 'react' import { useContextMenuStore, type ContextMenuIcon } from '@/stores/useContextMenuStore' function MenuIcon({ icon }: { icon: ContextMenuIcon }) { const c = 'currentColor' switch (icon) { case 'undo': return ( ) case 'redo': return ( ) case 'cut': return ( ) case 'copy': return ( ) case 'paste': return ( ) case 'selectAll': return ( ) case 'close': return ( ) case 'rename': return ( ) case 'trash': return ( ) case 'folder': return ( ) case 'file': return ( ) case 'link': return ( ) } } export default function ContextMenu() { const isOpen = useContextMenuStore((s) => s.isOpen) const x = useContextMenuStore((s) => s.x) const y = useContextMenuStore((s) => s.y) const items = useContextMenuStore((s) => s.items) const close = useContextMenuStore((s) => s.close) const menuRef = useRef(null) const [adjustedPos, setAdjustedPos] = useState({ x, y }) const [selectedIndex, setSelectedIndex] = useState(0) const visibleItems = items.filter((i) => !i.separator) const totalItems = visibleItems.length useEffect(() => { if (!isOpen || !menuRef.current) return const rect = menuRef.current.getBoundingClientRect() const vw = window.innerWidth const vh = window.innerHeight setAdjustedPos({ x: x + rect.width > vw ? vw - rect.width - 12 : x, y: y + rect.height > vh ? vh - rect.height - 12 : y, }) setSelectedIndex(0) }, [isOpen, x, y]) useEffect(() => { if (!isOpen) return function handleKeyDown(e: KeyboardEvent) { if (e.key === 'Escape') { e.preventDefault(); close(); return } if (e.key === 'ArrowDown') { e.preventDefault(); setSelectedIndex((i) => Math.min(i + 1, totalItems - 1)); return } if (e.key === 'ArrowUp') { e.preventDefault(); setSelectedIndex((i) => Math.max(i - 1, 0)); return } if (e.key === 'Enter') { e.preventDefault() const item = visibleItems[selectedIndex] if (item && !item.disabled) { item.action(); close() } } } window.addEventListener('keydown', handleKeyDown) return () => window.removeEventListener('keydown', handleKeyDown) }, [isOpen, selectedIndex, totalItems, visibleItems, close]) useEffect(() => { if (!isOpen || !menuRef.current) return function handleClickOutside(e: MouseEvent) { if (menuRef.current && !menuRef.current.contains(e.target as Node)) { close() } } requestAnimationFrame(() => window.addEventListener('mousedown', handleClickOutside)) return () => window.removeEventListener('mousedown', handleClickOutside) }, [isOpen, close]) if (!isOpen) return null return ( {items.map((item) => { if (item.separator) { return ( ) } const idx = visibleItems.indexOf(item) const isSelected = idx === selectedIndex return ( setSelectedIndex(idx)} onClick={() => { if (!item.disabled) { item.action(); close() } }} > {item.icon && ( )} {!item.icon && } {item.label} {item.shortcut && ( {item.shortcut} )} ) })} ) }