组件

Menu

一个 React 菜单,采用 Liquid Glass 浮层,支持分组、分隔线、图标、快捷键与受控用法。

Menu 内部负责触发器、Portal、定位器、玻璃浮层、列表、操作项、分组与分隔线。默认情况下,选中操作项后会关闭菜单并调用对应的 onSelect。

操作项通过 items 数组传入。使用 type: "group" 创建带标题的分组,使用 type: "separator" 创建分隔线。每个操作项都需要唯一的 value 作为 React key;当可见标签不是纯文本时,请通过 textValue 提供键盘导航文本。

基础示例

示例组合了分组操作、分隔线、图标、快捷键与禁用项。触发器下方的状态文本使用 aria-live="polite",屏幕阅读器会播报选中的操作。

No action selected.

定位

side、align、sideOffset 与 alignOffset 控制浮层位置。空间不足时,菜单会自动翻转或偏移。

受控用法

传入 open 与 onOpenChange 接管显示状态。除非同时从外部控制,触发器仍会切换菜单。

The menu is closed.

无障碍

Menu 遵循菜单按钮模式:触发器暴露 aria-haspopup="menu"、aria-expanded 与 aria-controls;内部列表带有 role="menu",每个操作项都是 menuitem。ArrowDown 与 ArrowUp 在启用的操作项之间移动,typeahead 会匹配操作项标签。Enter 或 Space 激活高亮项,Escape 关闭菜单并把焦点返回触发器。

禁用的操作项仍会被播报,但无法激活。value 只作为 React key。当 label 不是纯文本时,请传入 textValue 供 typeahead 使用,并在操作项需要可访问名称时传入 aria-label 或 aria-labelledby。共享交互模式见可访问性指南。

API 参考

Menu

属性类型默认值说明
triggerReactElement—打开菜单的触发器。必填,必须转发 props 与 ref。
itemsreadonly (MenuItemProps | MenuGroupProps | MenuSeparatorProps)[]—菜单中的操作项、分组与分隔线。
side"top" | "bottom" | "left" | "right" | "inline-start" | "inline-end""bottom"首选方位。
align"start" | "center" | "end""start"相对触发器的对齐方式。
sideOffsetnumber | OffsetFunction8触发器与菜单的像素距离。
alignOffsetnumber | OffsetFunction0沿对齐轴的像素偏移。
collisionPaddingPadding每边 5px浮层与碰撞边界之间的留白。
openboolean—受控显示状态。
defaultOpenbooleanfalse非受控时的初始显示状态。
onOpenChange(open: boolean, details) => void—打开或关闭时调用。
onOpenChangeComplete(open: boolean) => void—打开或关闭动画结束后调用。
modalbooleantrue打开时锁定外部交互。
loopFocusbooleantrue键盘焦点在菜单项之间循环。
highlightItemOnHoverbooleantrue指针移到菜单项上时高亮该项。
orientation"horizontal" | "vertical""vertical"方向键使用的焦点移动轴。
closeParentOnEscbooleanfalse在嵌套菜单中按 Escape 时关闭整个菜单,而不是只关闭子菜单。
onItemHighlighted(item: HTMLElement | undefined, details) => void—高亮项变化时调用。
actionsRefRefObject<Menu.Root.Actions | null>—命令式的 close 与 unmount 操作。
triggerIdstring | null—受控菜单所用触发器的 ID。
defaultTriggerIdstring | null—初始打开菜单所用触发器的 ID。
handleMenu.Handle<Payload>—允许外部触发器控制菜单的 handle。
disabledbooleanfalse禁止打开菜单。
classNamestring—合并到浮层的 Tailwind 类名。
styleCSSProperties—内联样式,合并到与 className 相同的元素。
ui{ positioner?: ClassValue; popup?: ClassValue; glass?: ClassValue; overlay?: ClassValue; list?: ClassValue; item?: ClassValue; itemIcon?: ClassValue; itemLabel?: ClassValue; shortcut?: ClassValue; group?: ClassValue; groupLabel?: ClassValue; separator?: ClassValue }—按 data-slot 组织的内部槽位类名。

MenuItemProps

属性类型默认值说明
type"item"—将条目标记为操作项。未提供 group 或 separator 类型时可省略。
valuestring—该操作项唯一的 React key。必填。
labelReactNode—操作项中的可见内容。必填。
textValuestring—键盘 typeahead 使用的纯文本标签;字符串标签会自动使用。
iconReactNode—标签前的装饰性图标。
shortcutReactNode—标签后的快捷键提示,仅作视觉展示。
onSelect(event) => void—选中操作项时调用。
disabledbooleanfalse禁用该操作项。
closeOnClickbooleantrue选中后是否关闭菜单。
idstring—操作项元素的 ID。
nativeButtonbooleanfalse是否渲染原生 <button>。
renderReactElement | ((props, state) => ReactElement)—替换操作项元素。
classNamestring—合并到操作项的 Tailwind 类名。
styleCSSProperties—内联样式,合并到与 className 相同的元素。
ui{ item?: ClassValue; itemIcon?: ClassValue; itemLabel?: ClassValue; shortcut?: ClassValue }—该操作项内部槽位的类名。

MenuGroupProps

属性类型默认值说明
type"group"—标记为分组。必填。
labelReactNode—分组标题。必填。
itemsreadonly MenuItemProps[]—分组内的操作项。必填。
renderReactElement | ((props, state) => ReactElement)—替换分组元素。
classNamestring—合并到分组的 Tailwind 类名。
styleCSSProperties—内联样式,合并到与 className 相同的元素。
ui{ group?: ClassValue; groupLabel?: ClassValue; item?: ClassValue; itemIcon?: ClassValue; itemLabel?: ClassValue; shortcut?: ClassValue }—分组及其操作项内部槽位的类名。

MenuGroupProps 还会透传其余 Menu.Group 属性。

MenuSeparatorProps

属性类型默认值说明
type"separator"—标记为分隔线。必填。
renderReactElement | ((props, state) => ReactElement)—替换分隔线元素。
classNamestring—合并到分隔线的 Tailwind 类名。
styleCSSProperties—内联样式,合并到与 className 相同的元素。
ui{ separator?: ClassValue }—分隔线槽位的类名。

MenuSeparatorProps 还会透传 Menu.Separator 的 render、id 等属性。

ui 的键对应下方列出的 data-slot 值。其余 Base UI Menu.Root 属性都会透传。

属性说明
data-open菜单打开时存在于浮层上。
data-closed浮层关闭或正在关闭时存在。
data-highlighted键盘或指针高亮该操作项时存在。
data-disabled菜单或操作项禁用时存在。
data-popup-open菜单打开时存在于触发器上。
data-side浮层的实际方位。
data-align实际对齐方式(start、center 或 end)。
data-instant需要跳过动画时存在。
data-starting-style浮层开始进入动画时存在。
data-ending-style浮层正在退出动画时存在。
data-slot标记内部结构(positioner、popup、glass、overlay、list、item、item-icon、item-label、shortcut、group、group-label、separator)。

主题

组件使用共享玻璃材质。可以在父级调整 --ui-glass-* token,用 className 控制布局,并通过 ui 按 data-slot 调整内部槽位。组件级数值位于 src/theme/menu.ts。Token 分层与预设见主题指南。

相关组件

  • Button — 常用的菜单触发器,也是操作项的按钮样式来源。
  • Popover — 需要标题、正文与操作区的更丰富浮层。
  • Select — 从列表中选择一个值,而不是执行操作。