在这里点击右键
<script setup lang="ts">
import { ref } from 'vue'
import { Copy, FolderInput, Pencil, Trash2 } from '@lucide/vue'
import {
Center,
ContextMenu,
ContextMenuItem,
ContextMenuSeparator,
ContextMenuSub,
Stack,
Text,
} from '@hina-ui/vue'
const last = ref('')
</script>
<template>
<Stack gap="sm" align="stretch" class="w-96">
<ContextMenu label="文件操作">
<Center class="bg-inset h-32 rounded-lg border border-dashed select-none">
<Text tone="muted" size="sm">在这里点击右键</Text>
</Center>
<template #content>
<ContextMenuItem @select="last = '重命名'">
<template #icon><Pencil /></template>
重命名
</ContextMenuItem>
<ContextMenuItem @select="last = '复制'">
<template #icon><Copy /></template>
复制
</ContextMenuItem>
<ContextMenuSub label="移动到">
<template #icon><FolderInput /></template>
<ContextMenuItem @select="last = '移动到收藏夹'">收藏夹</ContextMenuItem>
<ContextMenuItem @select="last = '移动到归档'">归档</ContextMenuItem>
</ContextMenuSub>
<ContextMenuSeparator />
<ContextMenuItem tone="danger" @select="last = '删除'">
<template #icon><Trash2 /></template>
删除
</ContextMenuItem>
</template>
</ContextMenu>
<Text v-if="last" tone="muted" size="sm">选择了:{{ last }}</Text>
</Stack>
</template>
vue
用法
import { ContextMenu, ContextMenuItem } from '@hina-ui/vue'
ts
默认插槽是响应右键的区域,content 插槽是菜单里的条目。在区域内点击右键,或者在触屏上长按,菜单在指针的位置展开,选中条目后自动收起。条目、多选项、单选项、分组、标题、分隔线与子菜单都与 DropdownMenu 同一套,只是名字换成 ContextMenu 开头。
在这里点击右键
<script setup lang="ts">
import { Center, ContextMenu, ContextMenuItem, Text } from '@hina-ui/vue'
</script>
<template>
<ContextMenu label="图片操作">
<Center class="bg-inset h-32 w-96 rounded-lg border border-dashed select-none">
<Text tone="muted" size="sm">在这里点击右键</Text>
</Center>
<template #content>
<ContextMenuItem>在新标签页打开</ContextMenuItem>
<ContextMenuItem>复制图片地址</ContextMenuItem>
<ContextMenuItem>保存图片</ContextMenuItem>
</template>
</ContextMenu>
</template>
vue
示例
条目
条目的 icon 插槽用于前置图标,trailing 插槽用于尾部内容,删除这类不可撤销的操作设置 tone="danger";ContextMenuLabel 是不可选中的标题,ContextMenuSeparator 画一条分隔线。
在这里点击右键
<script setup lang="ts">
import { Copy, Pencil, Trash2 } from '@lucide/vue'
import {
Center,
ContextMenu,
ContextMenuItem,
ContextMenuLabel,
ContextMenuSeparator,
Kbd,
Text,
} from '@hina-ui/vue'
</script>
<template>
<ContextMenu label="条目操作">
<Center class="bg-inset h-32 w-96 rounded-lg border border-dashed select-none">
<Text tone="muted" size="sm">在这里点击右键</Text>
</Center>
<template #content>
<ContextMenuLabel>这条笔记</ContextMenuLabel>
<ContextMenuItem>
<template #icon><Pencil /></template>
编辑
<template #trailing><Kbd>E</Kbd></template>
</ContextMenuItem>
<ContextMenuItem>
<template #icon><Copy /></template>
复制
<template #trailing><Kbd>⌘C</Kbd></template>
</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem tone="danger">
<template #icon><Trash2 /></template>
删除
</ContextMenuItem>
</template>
</ContextMenu>
</template>
vue
多选项
ContextMenuCheckboxItem 用于可以同时选中多项的开关,checked 支持双向绑定,切换后菜单保持展开。
网格开,标尺关
<script setup lang="ts">
import { ref } from 'vue'
import { Center, ContextMenu, ContextMenuCheckboxItem, Text } from '@hina-ui/vue'
const grid = ref(true)
const ruler = ref(false)
</script>
<template>
<ContextMenu label="视图">
<Center class="bg-inset h-32 w-96 rounded-lg border border-dashed select-none">
<Text tone="muted" size="sm">
网格{{ grid ? '开' : '关' }},标尺{{ ruler ? '开' : '关' }}
</Text>
</Center>
<template #content>
<ContextMenuCheckboxItem v-model:checked="grid">显示网格</ContextMenuCheckboxItem>
<ContextMenuCheckboxItem v-model:checked="ruler">显示标尺</ContextMenuCheckboxItem>
</template>
</ContextMenu>
</template>
vue
单选项
一组互斥的选项用 ContextMenuRadioGroup 包裹,当前项自动带选中标记。
按名称排序
<script setup lang="ts">
import { ref } from 'vue'
import {
Center,
ContextMenu,
ContextMenuLabel,
ContextMenuRadioGroup,
ContextMenuRadioItem,
Text,
} from '@hina-ui/vue'
const sort = ref('name')
const names: Record<string, string> = { name: '名称', date: '修改日期', size: '大小' }
</script>
<template>
<ContextMenu label="排序">
<Center class="bg-inset h-32 w-96 rounded-lg border border-dashed select-none">
<Text tone="muted" size="sm">按{{ names[sort] }}排序</Text>
</Center>
<template #content>
<ContextMenuLabel>排序方式</ContextMenuLabel>
<ContextMenuRadioGroup v-model="sort">
<ContextMenuRadioItem value="name">名称</ContextMenuRadioItem>
<ContextMenuRadioItem value="date">修改日期</ContextMenuRadioItem>
<ContextMenuRadioItem value="size">大小</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</template>
</ContextMenu>
</template>
vue
不可用
disabled 让整块区域不再响应右键,浏览器自己的菜单照常出现;条目上的 disabled 只让那一条不可选。
在这里点击右键
<script setup lang="ts">
import { ref } from 'vue'
import { Center, ContextMenu, ContextMenuItem, Stack, Switch, Text } from '@hina-ui/vue'
const locked = ref(false)
</script>
<template>
<Stack gap="md" align="start">
<Switch v-model="locked">锁定区域</Switch>
<ContextMenu label="条目操作" :disabled="locked">
<Center class="bg-inset h-32 w-96 rounded-lg border border-dashed select-none">
<Text tone="muted" size="sm">
{{ locked ? '已锁定,右键是浏览器自己的菜单' : '在这里点击右键' }}
</Text>
</Center>
<template #content>
<ContextMenuItem>编辑</ContextMenuItem>
<ContextMenuItem disabled>移动</ContextMenuItem>
<ContextMenuItem tone="danger">删除</ContextMenuItem>
</template>
</ContextMenu>
</Stack>
</template>
vue
行为
- 菜单在指针的位置展开,靠近视口边缘时向内翻转。
- 菜单打开期间页面停止滚动;点击菜单外部、按 Esc 或者选中条目都会收起。
- 方向键在条目间移动,回车或者空格选中,输入字母跳到匹配的条目。
- 触屏上长按区域打开菜单。
无障碍
- 菜单是
role="menu",用label给它一个名称;条目分别是menuitem、menuitemcheckbox与menuitemradio。 - 右键菜单里的操作应当在页面上另有可达的入口,键盘与辅助技术的用户未必能触发它。
API
ContextMenu
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
label | string | — | 菜单的无障碍名称 |
disabled | boolean | false | 区域是否不再响应右键 |
open | boolean | — | 是否打开,支持双向绑定 |
class | string | — | 追加至菜单面板的类名 |
| 插槽 | 说明 |
|---|---|
| default | 响应右键的区域 |
content | 菜单里的条目 |
条目与其他子件
ContextMenuItem、ContextMenuCheckboxItem、ContextMenuRadioGroup、ContextMenuRadioItem、ContextMenuGroup、ContextMenuLabel、ContextMenuSeparator 与 ContextMenuSub 的属性、插槽与事件与 DropdownMenu 的同名子件完全一致,见 DropdownMenu。