ContextMenu 右键菜单

右键或者长按某个区域时在指针处展开的菜单。

在这里点击右键

<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。