import React, { useRef, useState } from 'react'; import Box from '@mui/material/Box'; import Button from '@mui/material/Button'; import Menu from '@mui/material/Menu'; import MenuItem from '@mui/material/MenuItem'; import Divider from '@mui/material/Divider'; import Typography from '@mui/material/Typography'; import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown'; /* ============================================================ * MenuBar:VSCode 风格菜单栏(MUI Menu,配置数组驱动) * * menus = [{ label, items: [{ label, Icon?, action?, shortcut?, divider? }] }] * - role=menubar / menuitem,顶层 roving tabindex + 方向键导航: * ← → / Home / End 移动焦点,↓ / Enter / 空格 打开菜单 * - 菜单内部导航由 MUI Menu 自带(方向键 + Enter/Esc) * ============================================================ */ export default function MenuBar({ menus = [] }) { const [openIdx, setOpenIdx] = useState(null); const [anchorEl, setAnchorEl] = useState(null); const [focusIdx, setFocusIdx] = useState(0); // roving tabindex 停靠点 const barRef = useRef(null); const btnEls = () => Array.from(barRef.current?.querySelectorAll('[data-menu-btn]') || []); const focusBtn = (i) => { const n = menus.length; if (n === 0) return; const j = ((i % n) + n) % n; setFocusIdx(j); btnEls()[j]?.focus(); }; const openMenu = (i) => { setFocusIdx(i); setAnchorEl(btnEls()[i]); setOpenIdx(i); }; const close = () => { setOpenIdx(null); setAnchorEl(null); }; const handleTopKeyDown = (e, i) => { switch (e.key) { case 'ArrowRight': e.preventDefault(); focusBtn(i + 1); break; case 'ArrowLeft': e.preventDefault(); focusBtn(i - 1); break; case 'Home': e.preventDefault(); focusBtn(0); break; case 'End': e.preventDefault(); focusBtn(menus.length - 1); break; case 'ArrowDown': case 'Enter': case ' ': e.preventDefault(); openMenu(i); // MUI Menu 打开后自动聚焦首个菜单项 break; default: break; } }; return ( {menus.map((menu, i) => ( {menu.items.map((item, j) => ( item.divider ? ( ) : ( { close(); item.action?.(); }} > {item.Icon && } {item.label} {item.shortcut && ( {item.shortcut} )} ) ))} ))} ); }