<script setup lang="ts">
import { ref } from 'vue'
import {
Kbd,
Menubar,
MenubarCheckboxItem,
MenubarItem,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
MenubarSub,
Stack,
Text,
} from '@hina-ui/vue'
const last = ref('')
const wrap = ref(true)
const numbers = ref(false)
const theme = ref('system')
</script>
<template>
<Stack gap="sm" align="start">
<Menubar label="编辑器菜单">
<MenubarMenu label="文件">
<MenubarItem @select="last = '新建'">
新建
<template #trailing><Kbd>Ctrl N</Kbd></template>
</MenubarItem>
<MenubarItem @select="last = '打开'">
打开
<template #trailing><Kbd>Ctrl O</Kbd></template>
</MenubarItem>
<MenubarSub label="最近打开">
<MenubarItem @select="last = '第一章.md'">第一章.md</MenubarItem>
<MenubarItem @select="last = '大纲.md'">大纲.md</MenubarItem>
</MenubarSub>
<MenubarSeparator />
<MenubarItem @select="last = '保存'">
保存
<template #trailing><Kbd>Ctrl S</Kbd></template>
</MenubarItem>
</MenubarMenu>
<MenubarMenu label="编辑">
<MenubarItem @select="last = '撤销'">
撤销
<template #trailing><Kbd>Ctrl Z</Kbd></template>
</MenubarItem>
<MenubarItem @select="last = '重做'">
重做
<template #trailing><Kbd>Ctrl Y</Kbd></template>
</MenubarItem>
<MenubarSeparator />
<MenubarItem @select="last = '查找'">
查找
<template #trailing><Kbd>Ctrl F</Kbd></template>
</MenubarItem>
</MenubarMenu>
<MenubarMenu label="视图">
<MenubarCheckboxItem v-model:checked="wrap">自动换行</MenubarCheckboxItem>
<MenubarCheckboxItem v-model:checked="numbers">显示行号</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarRadioGroup v-model="theme">
<MenubarRadioItem value="light">浅色</MenubarRadioItem>
<MenubarRadioItem value="dark">深色</MenubarRadioItem>
<MenubarRadioItem value="system">跟随系统</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarMenu>
</Menubar>
<Text v-if="last" tone="muted" size="sm">选择了:{{ last }}</Text>
</Stack>
</template>
vue
用法
import { Menubar, MenubarMenu, MenubarItem } from '@hina-ui/vue'
ts
Menubar 是一条横向的栏,里面放若干个 MenubarMenu,每个菜单的 label 是栏上的触发器文字,默认插槽是菜单里的条目。点击触发器展开菜单,展开期间把指针移到另一个触发器上就切换过去;条目、多选项、单选项、分组、标题、分隔线与子菜单都与 DropdownMenu 同一套,只是名字换成 Menubar 开头。
<script setup lang="ts">
import { Menubar, MenubarItem, MenubarMenu } from '@hina-ui/vue'
</script>
<template>
<Menubar label="示例菜单">
<MenubarMenu label="文件">
<MenubarItem>新建</MenubarItem>
<MenubarItem>打开</MenubarItem>
<MenubarItem>保存</MenubarItem>
</MenubarMenu>
<MenubarMenu label="编辑">
<MenubarItem>撤销</MenubarItem>
<MenubarItem>重做</MenubarItem>
</MenubarMenu>
<MenubarMenu label="帮助">
<MenubarItem>文档</MenubarItem>
<MenubarItem>关于</MenubarItem>
</MenubarMenu>
</Menubar>
</template>
vue
示例
条目
条目的 icon 插槽用于前置图标,trailing 插槽放快捷键这类尾部内容,删除这类不可撤销的操作设置 tone="danger";MenubarLabel 是不可选中的标题,MenubarSeparator 画一条分隔线。
<script setup lang="ts">
import { Copy, FilePlus, Trash2 } from '@lucide/vue'
import {
Kbd,
Menubar,
MenubarItem,
MenubarLabel,
MenubarMenu,
MenubarSeparator,
} from '@hina-ui/vue'
</script>
<template>
<Menubar label="条目示例">
<MenubarMenu label="文件">
<MenubarLabel>当前文档</MenubarLabel>
<MenubarItem>
<template #icon><FilePlus /></template>
新建
<template #trailing><Kbd>Ctrl N</Kbd></template>
</MenubarItem>
<MenubarItem>
<template #icon><Copy /></template>
复制一份
</MenubarItem>
<MenubarSeparator />
<MenubarItem tone="danger">
<template #icon><Trash2 /></template>
删除
</MenubarItem>
</MenubarMenu>
</Menubar>
</template>
vue
多选项与单选项
MenubarCheckboxItem 是可以同时选中多项的开关,切换后菜单保持展开;一组互斥的选项用 MenubarRadioGroup 包裹。
自动换行开,行号关,字号中
<script setup lang="ts">
import { ref } from 'vue'
import {
Menubar,
MenubarCheckboxItem,
MenubarLabel,
MenubarMenu,
MenubarRadioGroup,
MenubarRadioItem,
MenubarSeparator,
Stack,
Text,
} from '@hina-ui/vue'
const wrap = ref(true)
const numbers = ref(false)
const size = ref('md')
const names: Record<string, string> = { sm: '小', md: '中', lg: '大' }
</script>
<template>
<Stack gap="sm" align="start">
<Menubar label="视图设置">
<MenubarMenu label="视图">
<MenubarCheckboxItem v-model:checked="wrap">自动换行</MenubarCheckboxItem>
<MenubarCheckboxItem v-model:checked="numbers">显示行号</MenubarCheckboxItem>
<MenubarSeparator />
<MenubarLabel>字号</MenubarLabel>
<MenubarRadioGroup v-model="size">
<MenubarRadioItem value="sm">小</MenubarRadioItem>
<MenubarRadioItem value="md">中</MenubarRadioItem>
<MenubarRadioItem value="lg">大</MenubarRadioItem>
</MenubarRadioGroup>
</MenubarMenu>
</Menubar>
<Text tone="muted" size="sm">
自动换行{{ wrap ? '开' : '关' }},行号{{ numbers ? '开' : '关' }},字号{{ names[size] }}
</Text>
</Stack>
</template>
vue
受控
v-model 绑定当前展开的菜单的 value,没有展开时是空字符串;可以从外部展开某个菜单。
当前展开:无
<script setup lang="ts">
import { ref } from 'vue'
import { Button, Inline, Menubar, MenubarItem, MenubarMenu, Stack, Text } from '@hina-ui/vue'
const open = ref('')
</script>
<template>
<Stack gap="md" align="start">
<Inline gap="sm">
<Button variant="outline" tone="neutral" @click="open = 'file'">展开「文件」</Button>
<Button variant="outline" tone="neutral" @click="open = 'help'">展开「帮助」</Button>
</Inline>
<Menubar v-model="open" label="受控菜单">
<MenubarMenu label="文件" value="file">
<MenubarItem>新建</MenubarItem>
<MenubarItem>打开</MenubarItem>
</MenubarMenu>
<MenubarMenu label="帮助" value="help">
<MenubarItem>文档</MenubarItem>
<MenubarItem>关于</MenubarItem>
</MenubarMenu>
</Menubar>
<Text tone="muted" size="sm">当前展开:{{ open || '无' }}</Text>
</Stack>
</template>
vue
不可用的菜单
MenubarMenu 的 disabled 让触发器不可用,条目上的 disabled 只让那一条不可选。
<script setup lang="ts">
import { Menubar, MenubarItem, MenubarMenu } from '@hina-ui/vue'
</script>
<template>
<Menubar label="不可用示例">
<MenubarMenu label="文件">
<MenubarItem>新建</MenubarItem>
<MenubarItem disabled>另存为</MenubarItem>
</MenubarMenu>
<MenubarMenu label="调试" disabled>
<MenubarItem>开始调试</MenubarItem>
</MenubarMenu>
</Menubar>
</template>
vue
行为
- 点击触发器展开菜单,再次点击收起;展开期间指针移到另一个触发器即切换到那个菜单。
- 左右方向键在触发器之间移动,展开期间左右键切换菜单,上下键在条目间移动,回车或者空格选中,Esc 收起并把焦点还给触发器。
- 菜单打开期间页面照常可以滚动与交互,指针才能在触发器之间移动;选中条目或者点击外部都会收起。
无障碍
- 根是
role="menubar",用label给它一个名称;触发器是带aria-haspopup的menuitem,展开的面板是role="menu"。 - 触发器构成一组漫游焦点,Tab 只进出一次,方向键在其中移动。
API
Menubar
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
label | string | — | 菜单栏的无障碍名称 |
loop | boolean | true | 方向键到达两端时是否绕回 |
modelValue | string | — | 当前展开的菜单的 value,支持双向绑定 |
class | string | — | 追加至根元素的类名 |
条目与其他子件
MenubarItem、MenubarCheckboxItem、MenubarRadioGroup、MenubarRadioItem、MenubarGroup、MenubarLabel、MenubarSeparator 与 MenubarSub 的属性、插槽与事件与 DropdownMenu 的同名子件完全一致,见 DropdownMenu。