Context Menu 上下文菜单

用于上下文菜单场景的 MoriUI 组件。

导入

import { ContextMenu, ContextMenuArrow, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuItemIndicator, ContextMenuLabel, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from 'moriui'

示例

概览

右键菜单展示上下文操作。

在此区域右键点击
<script setup lang="ts">
  import { ref } from 'vue'
  import { Check } from '@lucide/vue'
  import {
    ContextMenu,
    ContextMenuCheckboxItem,
    ContextMenuContent,
    ContextMenuGroup,
    ContextMenuItem,
    ContextMenuItemIndicator,
    ContextMenuLabel,
    ContextMenuRadioGroup,
    ContextMenuRadioItem,
    ContextMenuSeparator,
    ContextMenuShortcut,
    ContextMenuSub,
    ContextMenuSubContent,
    ContextMenuSubTrigger,
    ContextMenuTrigger
  } from 'moriui'

  const bookmark = ref(true)
  const theme = ref<string>('system')
</script>

<template>
  <div class="flex items-center justify-center">
    <ContextMenu>
      <ContextMenuTrigger>
        <div class="flex h-48 w-full max-w-96 items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
          在此区域右键点击
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent class="w-56">
        <ContextMenuGroup>
          <ContextMenuLabel>操作</ContextMenuLabel>
          <ContextMenuItem>
            复制
            <ContextMenuShortcut>⌘C</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem>
            粘贴
            <ContextMenuShortcut>⌘V</ContextMenuShortcut>
          </ContextMenuItem>
          <ContextMenuItem disabled>
            删除
            <ContextMenuShortcut>⌘⌫</ContextMenuShortcut>
          </ContextMenuItem>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuCheckboxItem v-model="bookmark">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          添加书签
        </ContextMenuCheckboxItem>
        <ContextMenuSeparator />
        <ContextMenuGroup>
          <ContextMenuLabel>主题</ContextMenuLabel>
          <ContextMenuRadioGroup v-model="theme">
            <ContextMenuRadioItem value="light">
              <ContextMenuItemIndicator>
                <Check class="size-3" />
              </ContextMenuItemIndicator>
              浅色
            </ContextMenuRadioItem>
            <ContextMenuRadioItem value="dark">
              <ContextMenuItemIndicator>
                <Check class="size-3" />
              </ContextMenuItemIndicator>
              深色
            </ContextMenuRadioItem>
            <ContextMenuRadioItem value="system">
              <ContextMenuItemIndicator>
                <Check class="size-3" />
              </ContextMenuItemIndicator>
              跟随系统
            </ContextMenuRadioItem>
          </ContextMenuRadioGroup>
        </ContextMenuGroup>
        <ContextMenuSeparator />
        <ContextMenuSub>
          <ContextMenuSubTrigger>更多工具</ContextMenuSubTrigger>
          <ContextMenuSubContent class="w-48">
            <ContextMenuItem>
              开发者工具
              <ContextMenuShortcut>⌘⇧I</ContextMenuShortcut>
            </ContextMenuItem>
            <ContextMenuItem>
              控制台
              <ContextMenuShortcut>⌘⇧J</ContextMenuShortcut>
            </ContextMenuItem>
          </ContextMenuSubContent>
        </ContextMenuSub>
      </ContextMenuContent>
    </ContextMenu>
  </div>
</template>

基础

最小化的菜单内容。

右键点击此区域
<script setup lang="ts">
  import { ContextMenu, ContextMenuContent, ContextMenuItem, ContextMenuSeparator, ContextMenuTrigger } from 'moriui'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        右键点击此区域
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>打开文件</ContextMenuItem>
      <ContextMenuItem>保存文件</ContextMenuItem>
      <ContextMenuItem>另存为</ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem>打印</ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>

图标

为菜单项添加图标。

在项目文件上右键点击
<script setup lang="ts">
  import {
    Copy,
    ExternalLink,
    FilePen,
    Import,
    Trash2
  } from '@lucide/vue'
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuItem,
    ContextMenuSeparator,
    ContextMenuTrigger
  } from 'moriui'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        在项目文件上右键点击
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>
        <FilePen class="mr-1.5 size-4" />
        编辑文件
      </ContextMenuItem>
      <ContextMenuItem>
        <ExternalLink class="mr-1.5 size-4" />
        在新窗口打开
      </ContextMenuItem>
      <ContextMenuItem>
        <Copy class="mr-1.5 size-4" />
        复制路径
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem>
        <Import class="mr-1.5 size-4" />
        导出
      </ContextMenuItem>
      <ContextMenuItem class="text-destructive focus:text-destructive-foreground">
        <Trash2 class="mr-1.5 size-4" />
        移出项目
      </ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>

复选项

CheckboxItem 支持多选状态。

在编辑器区域右键点击
<script setup lang="ts">
  import { ref } from 'vue'
  import { Check } from '@lucide/vue'
  import {
    ContextMenu,
    ContextMenuCheckboxItem,
    ContextMenuContent,
    ContextMenuItemIndicator,
    ContextMenuTrigger
  } from 'moriui'

  const showToolbar = ref(true)
  const showStatusBar = ref(false)
  const showSidebar = ref(true)
  const wordWrap = ref(true)
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        在编辑器区域右键点击
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuCheckboxItem v-model="showToolbar">
        <ContextMenuItemIndicator>
          <Check class="size-3" />
        </ContextMenuItemIndicator>
        显示工具栏
      </ContextMenuCheckboxItem>
      <ContextMenuCheckboxItem v-model="showStatusBar">
        <ContextMenuItemIndicator>
          <Check class="size-3" />
        </ContextMenuItemIndicator>
        显示状态栏
      </ContextMenuCheckboxItem>
      <ContextMenuCheckboxItem v-model="showSidebar">
        <ContextMenuItemIndicator>
          <Check class="size-3" />
        </ContextMenuItemIndicator>
        显示侧边栏
      </ContextMenuCheckboxItem>
      <ContextMenuCheckboxItem v-model="wordWrap">
        <ContextMenuItemIndicator>
          <Check class="size-3" />
        </ContextMenuItemIndicator>
        自动换行
      </ContextMenuCheckboxItem>
    </ContextMenuContent>
  </ContextMenu>
</template>

单选项

RadioItem 与 RadioGroup 管理唯一选择。

在文本上右键点击
<script setup lang="ts">
  import { ref } from 'vue'
  import { Check } from '@lucide/vue'
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuItemIndicator,
    ContextMenuRadioGroup,
    ContextMenuRadioItem,
    ContextMenuSeparator,
    ContextMenuTrigger
  } from 'moriui'

  const alignment = ref<string>('left')
  const lineHeight = ref<string>('1.5')
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        在文本上右键点击
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuRadioGroup v-model="alignment">
        <ContextMenuRadioItem value="left">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          左对齐
        </ContextMenuRadioItem>
        <ContextMenuRadioItem value="center">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          居中对齐
        </ContextMenuRadioItem>
        <ContextMenuRadioItem value="right">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          右对齐
        </ContextMenuRadioItem>
      </ContextMenuRadioGroup>
      <ContextMenuSeparator />
      <ContextMenuRadioGroup v-model="lineHeight">
        <ContextMenuRadioItem value="1.0">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          行距 1.0
        </ContextMenuRadioItem>
        <ContextMenuRadioItem value="1.5">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          行距 1.5
        </ContextMenuRadioItem>
        <ContextMenuRadioItem value="2.0">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          行距 2.0
        </ContextMenuRadioItem>
      </ContextMenuRadioGroup>
    </ContextMenuContent>
  </ContextMenu>
</template>

分组

Group 与 Label 组织菜单层级。

在文本区域右键点击
<script setup lang="ts">
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuGroup,
    ContextMenuItem,
    ContextMenuLabel,
    ContextMenuSeparator,
    ContextMenuTrigger
  } from 'moriui'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        在文本区域右键点击
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuGroup>
        <ContextMenuLabel>编辑</ContextMenuLabel>
        <ContextMenuItem>撤销</ContextMenuItem>
        <ContextMenuItem>重做</ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem>剪切</ContextMenuItem>
        <ContextMenuItem>复制</ContextMenuItem>
        <ContextMenuItem>粘贴</ContextMenuItem>
      </ContextMenuGroup>
      <ContextMenuSeparator />
      <ContextMenuGroup>
        <ContextMenuLabel>查看</ContextMenuLabel>
        <ContextMenuItem>放大</ContextMenuItem>
        <ContextMenuItem>缩小</ContextMenuItem>
        <ContextMenuItem>适应窗口</ContextMenuItem>
      </ContextMenuGroup>
    </ContextMenuContent>
  </ContextMenu>
</template>

快捷键

Shortcut 展示菜单项的键位提示。

右键查看快捷键
<script setup lang="ts">
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuItem,
    ContextMenuSeparator,
    ContextMenuShortcut,
    ContextMenuTrigger
  } from 'moriui'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        右键查看快捷键
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>
        撤销
        <ContextMenuShortcut>⌘Z</ContextMenuShortcut>
      </ContextMenuItem>
      <ContextMenuItem>
        重做
        <ContextMenuShortcut>⇧⌘Z</ContextMenuShortcut>
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem>
        剪切
        <ContextMenuShortcut>⌘X</ContextMenuShortcut>
      </ContextMenuItem>
      <ContextMenuItem>
        复制
        <ContextMenuShortcut>⌘C</ContextMenuShortcut>
      </ContextMenuItem>
      <ContextMenuItem>
        粘贴
        <ContextMenuShortcut>⌘V</ContextMenuShortcut>
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem>
        全选
        <ContextMenuShortcut>⌘A</ContextMenuShortcut>
      </ContextMenuItem>
      <ContextMenuItem>
        查找
        <ContextMenuShortcut>⌘F</ContextMenuShortcut>
      </ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>

子菜单

Sub、SubTrigger 与 SubContent 构建多级菜单。

在代码区域右键点击
<script setup lang="ts">
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuItem,
    ContextMenuSeparator,
    ContextMenuSub,
    ContextMenuSubContent,
    ContextMenuSubTrigger,
    ContextMenuTrigger
  } from 'moriui'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        在代码区域右键点击
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>格式化文档</ContextMenuItem>
      <ContextMenuSub>
        <ContextMenuSubTrigger>插入代码片段</ContextMenuSubTrigger>
        <ContextMenuSubContent>
          <ContextMenuItem>函数</ContextMenuItem>
          <ContextMenuItem>循环</ContextMenuItem>
          <ContextMenuItem>条件判断</ContextMenuItem>
          <ContextMenuSeparator />
          <ContextMenuSub>
            <ContextMenuSubTrigger>组件模板</ContextMenuSubTrigger>
            <ContextMenuSubContent>
              <ContextMenuItem>Vue 组件</ContextMenuItem>
              <ContextMenuItem>React 组件</ContextMenuItem>
            </ContextMenuSubContent>
          </ContextMenuSub>
        </ContextMenuSubContent>
      </ContextMenuSub>
      <ContextMenuSub>
        <ContextMenuSubTrigger>重构</ContextMenuSubTrigger>
        <ContextMenuSubContent>
          <ContextMenuItem>提取变量</ContextMenuItem>
          <ContextMenuItem>提取函数</ContextMenuItem>
          <ContextMenuItem>重命名符号</ContextMenuItem>
        </ContextMenuSubContent>
      </ContextMenuSub>
      <ContextMenuSeparator />
      <ContextMenuItem>在终端中打开</ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>

破坏性

使用样式区分危险操作。

右键点击文件区域
<script setup lang="ts">
  import { Trash2 } from '@lucide/vue'
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuItem,
    ContextMenuSeparator,
    ContextMenuTrigger
  } from 'moriui'
</script>

<template>
  <ContextMenu>
    <ContextMenuTrigger>
      <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
        右键点击文件区域
      </div>
    </ContextMenuTrigger>
    <ContextMenuContent>
      <ContextMenuItem>
        <Trash2 class="mr-1.5 size-4" />
        重命名
      </ContextMenuItem>
      <ContextMenuItem>
        <Trash2 class="mr-1.5 size-4" />
        复制
      </ContextMenuItem>
      <ContextMenuItem>
        <Trash2 class="mr-1.5 size-4" />
        移动
      </ContextMenuItem>
      <ContextMenuSeparator />
      <ContextMenuItem class="text-destructive focus:text-destructive-foreground">
        <Trash2 class="mr-1.5 size-4" />
        删除文件
      </ContextMenuItem>
    </ContextMenuContent>
  </ContextMenu>
</template>

定位

Content 的 side 控制弹出方向。

右键点击 — 菜单将出现在下方
<script setup lang="ts">
  import { ref } from 'vue'
  import {
    ContextMenu,
    ContextMenuContent,
    ContextMenuItem,
    ContextMenuSeparator,
    ContextMenuTrigger
  } from 'moriui'

  const side = ref<'top' | 'bottom' | 'left' | 'right'>('bottom')
  const sides = [
    { label: '上方', value: 'top' as const },
    { label: '下方', value: 'bottom' as const },
    { label: '左侧', value: 'left' as const },
    { label: '右侧', value: 'right' as const }
  ]
</script>

<template>
  <div class="flex flex-col items-center gap-4">
    <div class="flex items-center gap-2">
      <button
        v-for="s in sides"
        :key="s.value"
        class="rounded-md px-3 py-1 text-xs font-medium transition-colors"
        :class="side === s.value ? 'bg-primary text-primary-foreground' : 'bg-secondary text-secondary-foreground hover:bg-accent'"
        @click="side = s.value"
      >
        {{ s.label }}
      </button>
    </div>
    <ContextMenu>
      <ContextMenuTrigger>
        <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
          右键点击 — 菜单将出现在{{ sides.find(s => s.value === side)?.label }}
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent :side="side">
        <ContextMenuItem>查看详情</ContextMenuItem>
        <ContextMenuItem>编辑</ContextMenuItem>
        <ContextMenuItem>删除</ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuItem>属性</ContextMenuItem>
      </ContextMenuContent>
    </ContextMenu>
  </div>
</template>

RTL

菜单在 RTL 上下文中的定位。

انقر بالزر الأيمن هنا
<script setup lang="ts">
  import { ref } from 'vue'
  import { Check } from '@lucide/vue'
  import {
    ContextMenu,
    ContextMenuCheckboxItem,
    ContextMenuContent,
    ContextMenuItem,
    ContextMenuItemIndicator,
    ContextMenuSeparator,
    ContextMenuShortcut,
    ContextMenuTrigger,
    DirectionProvider
  } from 'moriui'

  const wrap = ref(true)
</script>

<template>
  <DirectionProvider dir="rtl">
    <ContextMenu>
      <ContextMenuTrigger>
        <div class="flex items-center justify-center rounded-lg border-2 border-dashed p-8 text-sm text-muted-foreground">
          انقر بالزر الأيمن هنا
        </div>
      </ContextMenuTrigger>
      <ContextMenuContent>
        <ContextMenuItem>
          نسخ
          <ContextMenuShortcut>⌘C</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem>
          قص
          <ContextMenuShortcut>⌘X</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuItem>
          لصق
          <ContextMenuShortcut>⌘V</ContextMenuShortcut>
        </ContextMenuItem>
        <ContextMenuSeparator />
        <ContextMenuCheckboxItem v-model="wrap">
          <ContextMenuItemIndicator>
            <Check class="size-3" />
          </ContextMenuItemIndicator>
          التفاف النص
        </ContextMenuCheckboxItem>
      </ContextMenuContent>
    </ContextMenu>
  </DirectionProvider>
</template>

使用说明

组合方式

ContextMenu 由 Trigger、Content、Item、Separator、Group、Label 与 Sub 组成,CheckboxItem 和 RadioItem 支持选择和设置。

子菜单

Sub、SubTrigger 与 SubContent 构建多级嵌套菜单。

复选与单选

CheckboxItem 和 RadioItem 使用 Indicator 表达选中状态;RadioGroup 管理单项选择。

RTL

dir 控制弹层定位与方向键行为。

变体

ContextMenuContent 的 side、align 来自 Reka Popper 定位,无公开视觉变体。

可访问性

ContextMenu 复用 Reka UI 的 menu ARIA、方向键导航、Enter/Space 选择与 Escape 关闭。Trigger 应表示其弹出行为;Content 内需要为 Label 等提供合适的语义。

API 参考

属性类型默认值说明
ContextMenu v-model:openbooleanundefined右键菜单的受控打开状态。
ContextMenu defaultOpenbooleanfalse非受控初始打开状态。
ContextMenuTrigger as / asChildstring | Component / boolean'span' / false右键触发区域的元素组合能力。
ContextMenuContent side / alignPopperContentProps'bottom' / 'start'弹层方向与对齐。
ContextMenuItem disabledbooleanfalse禁用菜单项。
ContextMenuCheckboxItem v-modelboolean | 'indeterminate'undefined复选框的选中状态。
ContextMenuRadioItem valueAcceptableValue必填的单选项值。
ContextMenuSeparator classHTMLAttributes["class"]undefined合并到分隔线的调用方类。
ContextMenuLabel classHTMLAttributes["class"]undefined合并到标签的调用方类。
ContextMenuSub v-model:openbooleanundefined子菜单的受控打开状态。
ContextMenuSubTrigger disabledbooleanfalse禁用子菜单触发器。
ContextMenuShortcut classHTMLAttributes["class"]undefined合并到快捷键标签的调用方类。