/** * AccountMenu — enterprise-grade account popover anchored to the sidebar avatar. * * Structure (Claude / Slack / GitHub pattern): * [Avatar] Display Name * email / username * ───────────────────────── * Personalization * Settings * ───────────────────────── * About * ───────────────────────── * Log out * * UX rules: * - Opens upward from the avatar (bottom-left anchor) * - Closes on outside click or Esc * - Keyboard-navigable (arrow keys, Enter) * - Log out: one click, no confirmation, instant redirect * - Identity at top, exit at bottom */ import React, { useEffect, useRef, useCallback } from 'react'; import { Settings, LogOut, Palette, Info } from 'lucide-react'; import UserAvatar from './UserAvatar'; export default function AccountMenu({ user, onClose, onOpenSettings, onOpenProfile, onOpenAbout, onLogout, }) { const menuRef = useRef(null); const itemsRef = useRef([]); // Close on outside click useEffect(() => { function handleClickOutside(e) { if (menuRef.current && !menuRef.current.contains(e.target)) { onClose(); } } // Delay to avoid the same click that opened the menu from closing it const timer = setTimeout(() => { document.addEventListener('mousedown', handleClickOutside); }, 0); return () => { clearTimeout(timer); document.removeEventListener('mousedown', handleClickOutside); }; }, [onClose]); // Keyboard navigation const handleKeyDown = useCallback((e) => { const items = itemsRef.current.filter(Boolean); const currentIndex = items.indexOf(document.activeElement); switch (e.key) { case 'Escape': e.preventDefault(); onClose(); break; case 'ArrowDown': e.preventDefault(); if (currentIndex < items.length - 1) { items[currentIndex + 1]?.focus(); } else { items[0]?.focus(); } break; case 'ArrowUp': e.preventDefault(); if (currentIndex > 0) { items[currentIndex - 1]?.focus(); } else { items[items.length - 1]?.focus(); } break; case 'Tab': e.preventDefault(); onClose(); break; } }, [onClose]); // Focus first item on mount useEffect(() => { const first = itemsRef.current.find(Boolean); first?.focus(); }, []); const setItemRef = (index) => (el) => { if (el) itemsRef.current[index] = el; }; const menuItemClass = [ 'w-full text-left px-3 py-2 text-[13px] rounded-lg', 'flex items-center gap-2.5', 'text-white/80 hover:bg-white/8 hover:text-white', 'focus:bg-white/8 focus:text-white focus:outline-none', 'transition-colors cursor-pointer', ].join(' '); return (