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:open | boolean | undefined | 右键菜单的受控打开状态。 |
ContextMenu defaultOpen | boolean | false | 非受控初始打开状态。 |
ContextMenuTrigger as / asChild | string | Component / boolean | 'span' / false | 右键触发区域的元素组合能力。 |
ContextMenuContent side / align | PopperContentProps | 'bottom' / 'start' | 弹层方向与对齐。 |
ContextMenuItem disabled | boolean | false | 禁用菜单项。 |
ContextMenuCheckboxItem v-model | boolean | 'indeterminate' | undefined | 复选框的选中状态。 |
ContextMenuRadioItem value | AcceptableValue | — | 必填的单选项值。 |
ContextMenuSeparator class | HTMLAttributes["class"] | undefined | 合并到分隔线的调用方类。 |
ContextMenuLabel class | HTMLAttributes["class"] | undefined | 合并到标签的调用方类。 |
ContextMenuSub v-model:open | boolean | undefined | 子菜单的受控打开状态。 |
ContextMenuSubTrigger disabled | boolean | false | 禁用子菜单触发器。 |
ContextMenuShortcut class | HTMLAttributes["class"] | undefined | 合并到快捷键标签的调用方类。 |