菜单栏
概述
Menubar 用于构建常驻应用顶部的横向菜单栏:顶层为一组可横向漫游焦点的触发器,点击或悬停展开下拉菜单,支持任意层级嵌套子菜单与完整键盘导航。
SMenubar 是数据驱动的聚合组件:传入 items 数组即可渲染完整的 role="menubar" 结构、触发器、下拉内容与嵌套子菜单。顶层项既可以是"展开下拉"的触发器,也可以是直接跳转的链接(提供 href / to 即可)。逻辑与无障碍语义由 Aria MenubarCompact 承载,样式通过 scv() 配方注入。
SMenubar把全部结构组合委托给 AriaMenubarCompact。无样式的数据驱动用法可导入@vean/aria/menubar;下拉菜单部分复用@vean/aria/menu的MenuOptionsCompact。
功能特性
- 数据驱动组合 — 传
items(MenuOptionData)即可渲染菜单栏;顶层项通过children声明下拉菜单,子项支持label/icon/shortcut/disabled/separator/href/to,子项可继续嵌套。 - 横向 Roving Focus — 根节点保持单一 Tab 停止点,
ArrowLeft/ArrowRight在触发器间移动焦点,loop开启时首尾循环;禁用触发器被移出焦点顺序。 - 完整键盘导航 —
Enter/Space切换菜单、ArrowDown打开;菜单内方向键漫游条目、ArrowRight/ArrowLeft切换相邻顶层菜单;Escape关闭并归还焦点。 - 悬停 / 指针切换 — 菜单打开后悬停其他触发器即切换打开的菜单;悬停链接型触发器会收起菜单并聚焦该链接。
- 点击 / 悬停触发模式 —
trigger支持click(默认)与hover:click模式点击展开菜单,菜单打开后悬停其他触发器仍可切换;hover模式悬停即展开(可用delayDuration/skipDelayDuration控制延迟与连开窗口),并带指针宽限区,可在菜单栏与内容之间平滑移动而不误关。 - 溢出折叠 —
collapsible开启后,当菜单栏内容超出容器宽度时,末尾的顶层项自动合并进一个"更多"菜单,始终保证内容宽度不超过容器宽度;可用moreLabel/moreIcon/moreProps/more-trigger插槽定制。 - 链接型顶层项 — 提供
href/to的顶层项渲染为链接(不展开下拉),与导航菜单模式一致;链接保留target/external/disabled语义。 - 嵌套子菜单 — 子项通过
MenuSub组合渲染任意层级子菜单,支持方向键进入 / 退出、指针宽限区防抖(100ms 打开延迟)。 - 受控 / 非受控 — 提供
modelValue时打开的菜单跟随 prop 并触发update:modelValue;否则使用defaultValue做非受控初始值。 - 逐项与整体禁用 —
item.disabled使单个顶层项完全惰性(aria-disabled+ 移出 Tab 顺序 + 阻断交互);紧凑组件级disabled一次禁用全部触发器(含链接型)。 - 隐藏项 —
item.hidden移除整个顶层项及其下拉菜单;隐藏项在溢出计算之前就被过滤,因此collapsible只统计可见触发器。子项标记hidden则从其下拉菜单中移除。 - 双向方向 —
dir支持 LTR / RTL,方向键与子菜单滑入方向均按逻辑方向处理;portalProps控制内容是否传送到 body。 - 六种尺寸 —
size(xs…2xl)变体覆盖根节点与触发器的间距、内边距与字号。 - 菜单槽位透传 —
item-leading/item-trailing/trigger/item-link-icon等插槽透传至菜单层,可按项定制内容;uiprop 覆盖根节点与触发器槽位。 - Aria 组合 —
MenubarRoot/MenubarMenu/MenubarTrigger/MenubarContent/MenubarSubTrigger/MenubarSubContent及Compact均可从@vean/aria/menubar导出,菜单基础件复用@vean/aria/menu。
用法
演示
API
Aria 层
21Menubar<T>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
class | ClassValue | — | 菜单栏根类 |
size | ThemeSize | — | 组件的视觉尺寸。 |
ui | Partial<MenubarUi> | — | 该组件的按槽类重写。 |
indicatorPosition | AlignSide | — | 指示符位置。 |
disabled | boolean | — | 该组件是否已禁用。 |
placement | Placement | — | 位置。 |
showArrow | boolean | — | 是否显示箭头。 |
triggerProps | MenubarTriggerProps | — | 转发给触发元素的属性。 |
collapsible | boolean | — | 当 `true` 时,如果菜单栏内容宽度超过其容器,
末尾的项目将折叠为一个“更多”下拉菜单,以确保内容始终
能完全显示在容器内。 |
moreLabel | string | undefined | 当 `collapsible` 时,末尾“更多”触发器的标签。 |
moreIcon | IconValue | — | 当 `collapsible` 时,末尾“更多”触发器的图标。 |
moreProps | MenubarTriggerProps | — | 属性将转发至末尾的“more”触发器。 |
portalProps | MenuPortalProps | — | 转发给 portal 元素的属性。 |
contentProps | MenubarContentProps | — | 转发给内容元素的属性。 |
popupProps | MenuPopupProps | — | 转发给弹出窗口元素的属性。 |
arrowProps | MenuArrowProps | — | 转发给箭头元素的属性。 |
modelValue | T | — | 要打开的菜单的受控值。可作为 `v-model` 使用。 |
defaultValue | T | — | 初始渲染时应打开的菜单值。 |
dir | Direction | — | 如适用,请保留菜单栏的阅读方向。 |
loop | boolean | — | 当值为 `true` 时,键盘导航会在最后一次触发点与第一个触发点之间循环。 |
trigger | MenubarTriggerType | 'click' | 菜单栏的触发类型。
- `click`:点击触发器时将打开菜单(将鼠标悬停在
已打开的菜单栏上仍可在菜单之间切换)。
- `hover`:将鼠标悬停在触发器上时将打开菜单。 |
delayDuration | number | 150 | 从指针进入触发器到菜单在悬停模式下
打开所花费的时间。 |
skipDelayDuration | number | 300 | 用户在触发延迟之前,还有多少时间可以输入另一个触发条件。
延迟。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
items | MenuOptionData<T>[] | — | 由该组件渲染的项。 |
selectedValue | T | — | 菜单中的选定值。 |
itemProps | MenuItemProps | — | 转发给 item 元素的属性。 |
linkProps | LinkExtraProps | — | 传递给 link 元素的属性。 |
subProps | MenuSubProps | — | 转发给子元素的属性。 |
groupProps | MenuGroupProps | — | 转发给 group 元素的属性。 |
groupLabelProps | MenuGroupLabelProps | — | 转发至组标签元素的属性。 |
shortcutProps | MenuShortcutProps | — | 转发给快捷方式元素的属性。 |
separatorProps | MenuSeparatorProps | — | 转发给分隔符元素的属性。 |
subTriggerProps | MenuSubTriggerProps | — | 转发给子触发器元素的属性。 |
subContentProps | MenuSubContentProps | — | 转发至子内容元素的属性。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | T | 当模型值发生变化时触发。 |
escapeKeyDown | KeyboardEvent | 当 Esc 键被按下时触发的事件处理程序。可被阻止。 |
pointerDownOutside | PointerDownOutsideEvent | 当 `pointerdown` 事件发生在 `DismissableLayer` 外部时调用的事件处理程序。可被阻止。 |
focusOutside | FocusOutsideEvent | 当焦点移出 `DismissableLayer` 时触发的事件处理程序。可被阻止。 |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | 当在 `DismissableLayer` 外部发生交互时触发的事件处理程序。具体而言,当 `pointerdown`
事件在该区域外部发生,或焦点移出该区域时触发。此事件可被阻止。 |
openAutoFocus | Event | 在打开页面时自动聚焦时触发的事件处理程序。可被阻止。 |
closeAutoFocus | Event | 在关闭时自动聚焦时触发的事件处理程序。可被阻止。 |
entryFocus | Event | 在条目获得焦点时触发。 |
update:open | boolean | 当对话框的打开状态发生变化时调用的事件处理程序。 |
select | MenuOptionData<T>, event: Event | 在发生 select 事件时触发。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
item | { item: MenuOptionData<T>; isTrigger?: boolean; } | 项插槽的自定义内容。 |
item-leading | { item: MenuOptionData<T>; } | 用于项目引导槽位的自定义内容。 |
item-trailing | { item: MenuOptionData<T>; } | 用于项目尾部槽位的自定义内容。 |
item-trigger-icon | { item: MenuOptionData<T>; } | 项目触发图标槽位的自定义内容。 |
item-link-icon | { item: MenuOptionData<T>; } | 项目链接图标槽位的自定义内容。 |
trigger | ((data: { item: MenuOptionData<T>; }) => any) | 触发器插槽的自定义内容。
可选:当消费者未提供内容时,`MenubarCompact` 会渲染完整的默认触发器内容(图标、
标签、链接图标、尾部插槽)。 |
more-trigger | — | 当 `collapsible` 时,为末尾的“更多”触发器提供自定义内容。 |
MenubarArrow
MenubarCheckboxGroup
MenubarCheckboxItem
MenubarCompact<T>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
disabled | boolean | — | 该组件是否已禁用。 |
placement | Placement | — | 位置。 |
showArrow | boolean | — | 是否显示箭头。 |
triggerProps | MenubarTriggerProps | — | 转发给触发元素的属性。 |
collapsible | boolean | — | 当 `true` 时,如果菜单栏内容宽度超过其容器,
末尾的项目将折叠为一个“更多”下拉菜单,以确保内容始终
能完全显示在容器内。 |
moreLabel | string | undefined | 当 `collapsible` 时,末尾“更多”触发器的标签。 |
moreIcon | IconValue | — | 当 `collapsible` 时,末尾“更多”触发器的图标。 |
moreProps | MenubarTriggerProps | — | 属性将转发至末尾的“more”触发器。 |
portalProps | MenuPortalProps | — | 转发给 portal 元素的属性。 |
contentProps | MenubarContentProps | — | 转发给内容元素的属性。 |
popupProps | MenuPopupProps | — | 转发给弹出窗口元素的属性。 |
arrowProps | MenuArrowProps | — | 转发给箭头元素的属性。 |
modelValue | T | — | 要打开的菜单的受控值。可作为 `v-model` 使用。 |
defaultValue | T | — | 初始渲染时应打开的菜单值。 |
dir | Direction | — | 如适用,请保留菜单栏的阅读方向。 |
loop | boolean | — | 当值为 `true` 时,键盘导航会在最后一次触发点与第一个触发点之间循环。 |
trigger | MenubarTriggerType | 'click' | 菜单栏的触发类型。
- `click`:点击触发器时将打开菜单(将鼠标悬停在
已打开的菜单栏上仍可在菜单之间切换)。
- `hover`:将鼠标悬停在触发器上时将打开菜单。 |
delayDuration | number | 150 | 从指针进入触发器到菜单在悬停模式下
打开所花费的时间。 |
skipDelayDuration | number | 300 | 用户在触发延迟之前,还有多少时间可以输入另一个触发条件。
延迟。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
items | MenuOptionData<T>[] | — | 由该组件渲染的项。 |
selectedValue | T | — | 菜单中的选定值。 |
itemProps | MenuItemProps | — | 转发给 item 元素的属性。 |
linkProps | LinkExtraProps | — | 传递给 link 元素的属性。 |
subProps | MenuSubProps | — | 转发给子元素的属性。 |
groupProps | MenuGroupProps | — | 转发给 group 元素的属性。 |
groupLabelProps | MenuGroupLabelProps | — | 转发至组标签元素的属性。 |
shortcutProps | MenuShortcutProps | — | 转发给快捷方式元素的属性。 |
separatorProps | MenuSeparatorProps | — | 转发给分隔符元素的属性。 |
subTriggerProps | MenuSubTriggerProps | — | 转发给子触发器元素的属性。 |
subContentProps | MenuSubContentProps | — | 转发至子内容元素的属性。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | T | 当模型值发生变化时触发。 |
escapeKeyDown | KeyboardEvent | 当 Esc 键被按下时触发的事件处理程序。可被阻止。 |
pointerDownOutside | PointerDownOutsideEvent | 当 `pointerdown` 事件发生在 `DismissableLayer` 外部时调用的事件处理程序。可被阻止。 |
focusOutside | FocusOutsideEvent | 当焦点移出 `DismissableLayer` 时触发的事件处理程序。可被阻止。 |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | 当在 `DismissableLayer` 外部发生交互时触发的事件处理程序。具体而言,当 `pointerdown`
事件在该区域外部发生,或焦点移出该区域时触发。此事件可被阻止。 |
openAutoFocus | Event | 在打开页面时自动聚焦时触发的事件处理程序。可被阻止。 |
closeAutoFocus | Event | 在关闭时自动聚焦时触发的事件处理程序。可被阻止。 |
entryFocus | Event | 在条目获得焦点时触发。 |
update:open | boolean | 当对话框的打开状态发生变化时调用的事件处理程序。 |
select | MenuOptionData<T>, event: Event | 在发生 select 事件时触发。 |
插槽
| 插槽名 | 参数 | 描述 |
|---|---|---|
item | { item: MenuOptionData<T>; isTrigger?: boolean; } | 项插槽的自定义内容。 |
item-leading | { item: MenuOptionData<T>; } | 用于项目引导槽位的自定义内容。 |
item-trailing | { item: MenuOptionData<T>; } | 用于项目尾部槽位的自定义内容。 |
item-trigger-icon | { item: MenuOptionData<T>; } | 项目触发图标槽位的自定义内容。 |
item-link-icon | { item: MenuOptionData<T>; } | 项目链接图标槽位的自定义内容。 |
trigger | ((data: { item: MenuOptionData<T>; }) => any) | 触发器插槽的自定义内容。
可选:当消费者未提供内容时,`MenubarCompact` 会渲染完整的默认触发器内容(图标、
标签、链接图标、尾部插槽)。 |
more-trigger | — | 当 `collapsible` 时,为末尾的“更多”触发器提供自定义内容。 |
MenubarContent
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
popupProps | MenuPopupProps | — | 转发给弹出窗口元素的属性。 |
placement | Placement | undefined | 浮动元素的位置。
若使用该属性,它将覆盖 `side` 和 `align` 属性。 |
side | Side | 'bottom' | 打开时优先渲染的触发器一侧。当发生碰撞且
enableAvoidCollisions 启用时,该方向将被反转。 |
sideOffset | number | 0 | 距触发器的像素距离。 |
sideFlip | boolean | true | 遇到边界时,请翻转至另一侧。 |
align | Align | 'center' | 首选与触发条件保持一致。若发生冲突,则可能调整。 |
alignOffset | number | 0 | 相对于 `start` 或 `end` 对齐选项的像素偏移量。 |
alignFlip | boolean | true | 当与边界发生冲突时,翻转对齐方式。
此情况仅在 `prioritizePosition` 为 true 时才会发生。 |
avoidCollisions | boolean | true | 当值为 `true` 时,将覆盖“侧边”和“对齐”首选项,以避免与边界边缘发生冲突。 |
collisionBoundary | Element | (Element | null)[] | null | [ ] | 用作碰撞边界的元素。默认情况下,该边界为视口,但您可以提供额外的
元素以纳入此检查范围。 |
collisionPadding | Padding | 0 | 从边界边缘开始,碰撞检测应发生的距离(以像素为单位)。接受一个数字(
所有边均相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。 |
arrowPadding | number | 0 | 箭头与内容边缘之间的间距。如果您的内容使用了 border-radius,这将防止内容
溢出到角落。 |
hideShiftedArrow | boolean | true | 当值为 `true` 时,若无法将箭头居中对齐
到引用元素上,则隐藏该箭头。 |
sticky | 'partial' | 'always' | 'partial' | 在对齐轴上的“粘附”行为。`partial` 会在触发器至少部分位于边界内时将内容保留在边界内,而
“always” 则无论触发器是否位于边界内,都会将内容保留在边界内。 |
hideWhenDetached | boolean | false | 当触发器被完全遮挡时,是否隐藏内容。 |
positionStrategy | 'fixed' | 'absolute' | 'fixed' | 要使用的 CSS `position` 属性的类型。 |
updatePositionStrategy | 'always' | 'optimized' | 'optimized' | 在每个动画帧中更新浮动元素位置的策略。 |
disableUpdateOnLayoutShift | boolean | false | 当布局发生变化时,是否禁用内容的更新位置。 |
prioritizePosition | boolean | false | 强制内容位于视口内。可能会与参考元素重叠,这可能并非预期效果。 |
reference | ReferenceElement | — | 将作为浮动元素定位参考点的自定义元素或虚拟元素。如果
提供了该元素,它将替换默认的锚点元素。 |
trapFocus | boolean | modal | 弹出窗口打开时,焦点是否被困在其中(Tab 键在层内循环,且
模态层的页面滚动被锁定)。 |
disableHoverableContent | boolean | false | 当设置为 `true` 时,鼠标悬停会关闭弹出窗口而非保持其打开状态(宽限区域已禁用,
因此离开触发区域会立即关闭该层)。 |
onGracePointerExit | (() => void) | — | 当指针最终离开宽限区域时触发的回调。允许上层组件(例如
Tooltip)在无需注册自己的第二个宽限区域的情况下,执行区域范围内的关闭逻辑。 |
forceMount | boolean | — | 用于在需要更多控制时强制挂载。
在使用 Vue 动画库控制动画时非常有用。 |
loop | boolean | false | 键盘导航是否应循环往复 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
escapeKeyDown | KeyboardEvent | 当 Esc 键被按下时触发的事件处理程序。可被阻止。 |
pointerDownOutside | PointerDownOutsideEvent | 当 `pointerdown` 事件发生在 `DismissableLayer` 外部时调用的事件处理程序。可被阻止。 |
focusOutside | FocusOutsideEvent | 当焦点移出 `DismissableLayer` 时触发的事件处理程序。可被阻止。 |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | 当在 `DismissableLayer` 外部发生交互时触发的事件处理程序。具体而言,当 `pointerdown`
事件在该区域外部发生,或焦点移出该区域时触发。此事件可被阻止。 |
openAutoFocus | Event | 在打开页面时自动聚焦时触发的事件处理程序。可被阻止。 |
closeAutoFocus | Event | 在关闭时自动聚焦时触发的事件处理程序。可被阻止。 |
entryFocus | Event | 在条目获得焦点时触发。 |
MenubarGroup
MenubarGroupLabel
MenubarItem
MenubarItemIndicator
MenubarMenu
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
value | DefinedValue | — | 一个唯一值,用于在受控时将触发器与活动根值关联起来。 |
MenubarMenus<T>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
items | MenuOptionData<T>[] | — | 顶级项目应渲染为可见的触发器。 |
moreItems | MenuOptionData<T>[] | — | 项目已折叠至末尾的“更多”菜单中。 |
rootProps | { [x: string]: unknown; } | — | 转发给菜单栏根节点的属性。 |
listeners | { [x: string]: unknown; } | — | 将监听器转发至菜单栏根节点。 |
optionsProps | { [x: string]: unknown; } | — | 这些 prop 会被转发至每个已展开菜单的菜单选项中。 |
linkProps | LinkExtraProps | — | 传递给链接触发器的 props。 |
contentProps | MenubarContentProps | — | 已将属性转发至各菜单的内容中。 |
portalProps | MenuPortalProps | — | 转发至各菜单入口页面的 props。 |
getTriggerProps | (item: MenuOptionData<T>) => MenubarTriggerProps | — | 解析顶级触发项的有效 props。 |
moreTriggerProps | { [x: string]: unknown; } | — | 将 props 转发给末尾的“more”触发器。 |
moreLabel | string | — | 尾部“more”触发器的标签。 |
moreIcon | IconValue | — | 尾部“更多”触发器的图标。 |
MenubarPortal
MenubarRadioGroup
MenubarRadioItem
MenubarRoot<T>
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
modelValue | T | — | 要打开的菜单的受控值。可作为 `v-model` 使用。 |
defaultValue | T | — | 初始渲染时应打开的菜单值。 |
dir | Direction | — | 如适用,请保留菜单栏的阅读方向。 |
loop | boolean | — | 当值为 `true` 时,键盘导航会在最后一次触发点与第一个触发点之间循环。 |
trigger | MenubarTriggerType | 'click' | 菜单栏的触发类型。
- `click`:点击触发器时将打开菜单(将鼠标悬停在
已打开的菜单栏上仍可在菜单之间切换)。
- `hover`:将鼠标悬停在触发器上时将打开菜单。 |
delayDuration | number | 150 | 从指针进入触发器到菜单在悬停模式下
打开所花费的时间。 |
skipDelayDuration | number | 300 | 用户在触发延迟之前,还有多少时间可以输入另一个触发条件。
延迟。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
update:modelValue | T | 当模型值发生变化时触发。 |
MenubarSeparator
MenubarSub
MenubarSubContent
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
loop | boolean | false | 键盘导航是否应循环往复 |
placement | Placement | undefined | 浮动元素的位置。
若使用该属性,它将覆盖 `side` 和 `align` 属性。 |
popupProps | MenuPopupProps | — | 转发给弹出窗口元素的属性。 |
sideOffset | number | 0 | 距触发器的像素距离。 |
arrowPadding | number | 0 | 箭头与内容边缘之间的间距。如果您的内容使用了 border-radius,这将防止内容
溢出到角落。 |
updatePositionStrategy | 'always' | 'optimized' | 'optimized' | 在每个动画帧中更新浮动元素位置的策略。 |
sideFlip | boolean | true | 遇到边界时,请翻转至另一侧。 |
alignOffset | number | 0 | 相对于 `start` 或 `end` 对齐选项的像素偏移量。 |
alignFlip | boolean | true | 当与边界发生冲突时,翻转对齐方式。
此情况仅在 `prioritizePosition` 为 true 时才会发生。 |
avoidCollisions | boolean | true | 当值为 `true` 时,将覆盖“侧边”和“对齐”首选项,以避免与边界边缘发生冲突。 |
collisionBoundary | Element | (Element | null)[] | null | [ ] | 用作碰撞边界的元素。默认情况下,该边界为视口,但您可以提供额外的
元素以纳入此检查范围。 |
collisionPadding | Padding | 0 | 从边界边缘开始,碰撞检测应发生的距离(以像素为单位)。接受一个数字(
所有边均相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。 |
hideShiftedArrow | boolean | true | 当值为 `true` 时,若无法将箭头居中对齐
到引用元素上,则隐藏该箭头。 |
sticky | 'partial' | 'always' | 'partial' | 在对齐轴上的“粘附”行为。`partial` 会在触发器至少部分位于边界内时将内容保留在边界内,而
“always” 则无论触发器是否位于边界内,都会将内容保留在边界内。 |
hideWhenDetached | boolean | false | 当触发器被完全遮挡时,是否隐藏内容。 |
positionStrategy | 'fixed' | 'absolute' | 'fixed' | 要使用的 CSS `position` 属性的类型。 |
disableUpdateOnLayoutShift | boolean | false | 当布局发生变化时,是否禁用内容的更新位置。 |
prioritizePosition | boolean | false | 强制内容位于视口内。可能会与参考元素重叠,这可能并非预期效果。 |
reference | ReferenceElement | — | 将作为浮动元素定位参考点的自定义元素或虚拟元素。如果
提供了该元素,它将替换默认的锚点元素。 |
trapFocus | boolean | modal | 弹出窗口打开时,焦点是否被困在其中(Tab 键在层内循环,且
模态层的页面滚动被锁定)。 |
disableHoverableContent | boolean | false | 当设置为 `true` 时,鼠标悬停会关闭弹出窗口而非保持其打开状态(宽限区域已禁用,
因此离开触发区域会立即关闭该层)。 |
onGracePointerExit | (() => void) | — | 当指针最终离开宽限区域时触发的回调。允许上层组件(例如
Tooltip)在无需注册自己的第二个宽限区域的情况下,执行区域范围内的关闭逻辑。 |
forceMount | boolean | — | 用于在需要更多控制时强制挂载。
在使用 Vue 动画库控制动画时非常有用。 |
事件
| 事件名 | 参数 | 描述 |
|---|---|---|
escapeKeyDown | KeyboardEvent | 当 Esc 键被按下时触发的事件处理程序。可被阻止。 |
pointerDownOutside | PointerDownOutsideEvent | 当 `pointerdown` 事件发生在 `DismissableLayer` 外部时调用的事件处理程序。可被阻止。 |
focusOutside | FocusOutsideEvent | 当焦点移出 `DismissableLayer` 时触发的事件处理程序。可被阻止。 |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | 当在 `DismissableLayer` 外部发生交互时触发的事件处理程序。具体而言,当 `pointerdown`
事件在该区域外部发生,或焦点移出该区域时触发。此事件可被阻止。 |
openAutoFocus | Event | 在打开页面时自动聚焦时触发的事件处理程序。可被阻止。 |
closeAutoFocus | Event | 在关闭时自动聚焦时触发的事件处理程序。可被阻止。 |
entryFocus | Event | 在条目获得焦点时触发。 |
MenubarSubTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
disabled | boolean | — | 当值为 `true` 时,禁止用户与该项进行交互。 |
textValue | string | — | 用于联想输入的可选文本。默认情况下,联想输入功能将使用该
项的 `.textContent`。<br> 当内容较为复杂,或其中包含非文本内容时,请使用此选项。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
MenubarTrigger
属性
| 属性名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
type | ButtonType | 'button' | 按钮元素的类型。可以是 'button'、'submit' 或 'reset' 之一。 |
disabled | boolean | — | 该组件是否已禁用。 |
asChild | boolean | — | 将默认渲染元素替换为作为子元素传递的元素,并合并两者的属性及行为。 |
as | AsTag | Component | 'div' | 该组件应渲染为的元素或组件。可通过 `asChild` 进行覆盖。 |
备注
架构与行业对标
| 能力 | VeanUI | Ant Design Menu | Element Plus Menu | Radix Menubar |
|---|---|---|---|---|
| Aria/styled 分离 | ✅ @vean/aria/menubar + scv() | ❌ 单包 | ❌ 单包 | ✅ @radix-ui/react-menubar |
| 数据驱动 compact API | ✅ MenubarCompact + 嵌套 items | ✅ items | ✅ items | ❌ 面向 JSX 组合 |
| 顶层触发 | ✅ 点击 / 悬停切换 + 方向键 | ✅ 点击 / 悬停 | ✅ 点击 / 悬停 | ✅ 点击 / 悬停 + 方向键 |
| 横向键盘漫游 | ✅ Roving Focus + loop | ✅ | ✅ | ✅ |
| 菜单间切换 | ✅ ArrowRight / ArrowLeft 跨菜单切换 | ✅ | ✅ | ✅ |
| 嵌套子菜单 | ✅ 任意层级(复用 menu 层) | ✅ SubMenu | ✅ el-sub-menu | ✅ |
| 链接型顶层项 | ✅ href / to + target / external / disabled | ✅ danger 等 | — | ✅ LinkItem |
| 禁用 | ✅ 逐项 + 紧凑级整体禁用 | ✅ disabled | ✅ disabled | ✅ disabled |
| 受控模式 | ✅ modelValue / defaultValue | ✅ openKeys / selectedKeys | ✅ default-active | ✅ value / onValueChange |
| 方向 | ✅ LTR / RTL + 逻辑方向键 | ✅ RTL | ✅ RTL | ✅ RTL |
| 尺寸变体 | ✅ size xs…2xl | ✅ size | ✅ size | — |
| 菜单项类型 | ✅ 复选框 / 单选 / 分隔符 / 快捷键(复用 menu 层) | ✅ 全类型 | ✅ 全类型 | ✅ 全类型 |
运行时注意事项
- 打开状态与指针 — 默认模式(
trigger="click")下,顶层触发器在pointerdown时打开菜单;菜单已打开时再次点击同一触发器会通过 dismissable layer 关闭(“再次点击收起”)。悬停其他触发器则切换打开的菜单。trigger="hover"模式悬停即展开,delayDuration(默认 150ms)控制展开延迟,skipDelayDuration(默认 300ms)控制连开窗口内的无延迟切换。 - 链接型顶层项 — 提供
href/to的项不渲染下拉内容,其点击 / 键盘激活直接导航;在菜单打开时悬停到链接项会收起当前菜单并把焦点移到链接。 - 禁用语义 —
item.disabled作用于单个顶层项:渲染aria-disabled、tabindex="-1"并阻断点击与键盘激活;紧凑组件级disabled会禁用全部触发器(含链接型)。子菜单中的禁用项遵循 menu 层语义。 - Portal 与定位 — 下拉内容默认通过
Teleport传送到 body(可用portalProps.disabled关闭)。若内容出现在错误位置,请检查是否处于transform/ 动画容器祖先内;定位依赖getBoundingClientRect测量。 - 受控 / 非受控 — 提供
modelValue时,内部写入仅触发update:modelValue,打开的菜单完全跟随 prop;未受控时defaultValue指定初始打开项。 - 焦点归还 — 键盘路径(
Escape/ 方向键切换)会精确归还焦点到触发器;指针路径(点击外部关闭)不主动移动焦点,符合"指针交互不劫持焦点"的惯例。 - RTL — 设置
dir="rtl"后方向键语义镜像(ArrowLeft变为"下一个"),菜单布局依赖逻辑属性自动翻转。 - 溢出折叠 — 开启
collapsible后,菜单栏内容超出容器宽度时末尾项合并进"更多"菜单;测量基于渲染后的真实布局,容器的父级需提供固定/受限宽度(例如max-w-*)。若菜单栏处于 flex 容器且未收缩,请给父容器设置min-w-0或宽度约束。
常见问题
如何让顶层项直接跳转而不展开下拉?
给项设置 href 或 to 即可——链接型顶层项渲染为 <a>,不渲染下拉内容;同时可用 target / external 控制打开方式。
如何精确控制当前打开的菜单?
使用 v-model 绑定 modelValue。顶层项的 value 成为受控值,切换即触发 update:modelValue;外部改值会同步打开对应菜单。未受控时用 defaultValue 指定初始打开的项。
如何禁用整个菜单栏或单个项?
整体禁用传 disabled 给 SMenubar;单个顶层项禁用在该项上设置 disabled: true。禁用项渲染 aria-disabled、移出 Tab 顺序,并阻断点击与键盘激活。
键盘如何操作?
Tab 进入菜单栏后,ArrowLeft / ArrowRight 在触发器间移动;Enter / Space 切换菜单、ArrowDown 打开;菜单内方向键漫游条目,ArrowRight / ArrowLeft 切换相邻顶层菜单,Escape 关闭。禁用项不在 Tab 顺序中。
能在子菜单里用复选框或单选吗?
可以——下拉内容复用 menu 层的能力:item-checked 槽位、MenuCheckboxItem / MenuRadioItem 以及 separator / shortcut 均可用;紧凑用法中给子项传对应字段即可。
为什么悬停链接型项时菜单会收起?
链接型顶层项没有下拉内容,指针进入时菜单栏会收起当前打开的菜单并把焦点移到该链接——这与 Radix Menubar 的链接项行为一致,避免焦点停留在已关闭菜单的触发器上。
如何让菜单在悬停时自动展开(而不是点击)?
给 SMenubar 传 trigger="hover" 即可。悬停触发器会展开菜单(delayDuration 控制展开延迟,skipDelayDuration 控制连开窗口);指针可在菜单栏与菜单内容之间移动而不误关。键盘操作(方向键 / Enter / Space / Escape)在两种模式下完全一致。
菜单项太多放不下怎么办?
给 SMenubar 传 collapsible。当菜单栏内容超出容器宽度时,末尾的顶层项会自动合并进一个"更多"菜单(默认标签 More,可用 moreLabel / moreIcon / moreProps 定制),保证内容宽度不超过容器。容器宽度变化时折叠状态自动重算。