ContextMenu 上下文菜单

基于 BrConfigProvider 实现全局主题配置,支持右键唤起、嵌套子菜单、自适应定位的企业级上下文菜单组件,可无缝集成 BrTableBrTreeBrCard 等组件,支持自定义快捷键唤起和全键盘导航。

组件特性

  • 🖱️ 原生右键劫持:完美替代浏览器默认右键菜单,提供一致的 UI 体验。
  • 📋 嵌套子菜单:支持无限级嵌套的子菜单,适用于复杂的层级结构。
  • ⌨️ 键盘无障碍:完全支持键盘上下左右导航,支持 Esc 关闭,符合 W3C 标准。
  • 🎯 自适应定位:基于浮层引擎自动计算位置,防止菜单在屏幕边缘被遮挡。
  • 🎨 主题定制:基于 BrConfigProvider 支持全局主题定制,同时支持 TailwindCSS 局部样式覆盖。

基础用法

在区域内右键点击,展示基础的操作菜单,包含普通项和禁用项,以及快捷键标注。

<script setup lang="ts">
import {
  BrContextMenu,
  BrContextMenuTrigger,
  BrContextMenuContent,
  BrContextMenuItem,
  BrContextMenuSeparator
} from '@breezeui/vue'
</script>

<template>
  <BrContextMenu>
    <BrContextMenuTrigger class="flex h-[150px] w-full items-center justify-center rounded-md border border-dashed text-sm">
      Right-click here (Right click here)
    </BrContextMenuTrigger>
    <BrContextMenuContent class="w-64">
      <BrContextMenuItem shortcut="⌘[">
        Back (Back)
      </BrContextMenuItem>
      <BrContextMenuItem shortcut="⌘]" disabled>
        Forward (Forward)
      </BrContextMenuItem>
      <BrContextMenuItem shortcut="⌘R">
        Reload (Reload)
      </BrContextMenuItem>
      <BrContextMenuSeparator />
      <BrContextMenuItem shortcut="⌘S">
        Save As... (Save As...)
      </BrContextMenuItem>
      <BrContextMenuItem shortcut="⌘P">
        Print... (Print...)
      </BrContextMenuItem>
    </BrContextMenuContent>
  </BrContextMenu>
</template>

嵌套子菜单

支持多层级嵌套的子菜单,且能自动处理边缘溢出问题。

<script setup lang="ts">
import {
  BrContextMenu,
  BrContextMenuTrigger,
  BrContextMenuContent,
  BrContextMenuItem,
  BrContextMenuSeparator,
  BrContextMenuSub,
  BrContextMenuSubTrigger,
  BrContextMenuSubContent
} from '@breezeui/vue'
</script>

<template>
  <BrContextMenu>
    <BrContextMenuTrigger class="flex h-[150px] w-full items-center justify-center rounded-md border border-dashed text-sm">
      Right-click to open nested menu (Right click to open nested menu)
    </BrContextMenuTrigger>
    <BrContextMenuContent class="w-64">
      <BrContextMenuItem>
        New File (New File)
      </BrContextMenuItem>
      <BrContextMenuItem>
        New Folder (New Folder)
      </BrContextMenuItem>
      <BrContextMenuSeparator />
      <BrContextMenuSub>
        <BrContextMenuSubTrigger>
          Open With (Open With)
        </BrContextMenuSubTrigger>
        <BrContextMenuSubContent class="w-48">
          <BrContextMenuItem>Breeze Code</BrContextMenuItem>
          <BrContextMenuItem>System Default (System Default)</BrContextMenuItem>
          <BrContextMenuSeparator />
          <BrContextMenuItem>Select Other App... (Choose other app...)</BrContextMenuItem>
        </BrContextMenuSubContent>
      </BrContextMenuSub>
      <BrContextMenuSeparator />
      <BrContextMenuItem shortcut="⌘⌫" class="text-destructive focus:text-destructive-foreground">
        Delete (Delete)
      </BrContextMenuItem>
    </BrContextMenuContent>
  </BrContextMenu>
</template>

与卡片集成

在卡片或复杂区域绑定右键操作。可以使用 as-child 将触发器属性绑定到自定义组件上。

<script setup lang="ts">
import {
  BrContextMenu,
  BrContextMenuTrigger,
  BrContextMenuContent,
  BrContextMenuItem,
  BrContextMenuSeparator,
  BrCard,
  BrCardHeader,
  BrCardTitle,
  BrCardDescription,
  BrCardContent
} from '@breezeui/vue'
</script>

<template>
  <BrContextMenu>
    <BrContextMenuTrigger as-child>
      <BrCard class="w-[350px] cursor-context-menu transition-shadow hover:shadow-md">
        <BrCardHeader>
          <BrCardTitle>Breeze UI Project (Breeze UI Project)</BrCardTitle>
          <BrCardDescription>Right-click the card to view actions (Right click card to see actions)</BrCardDescription>
        </BrCardHeader>
        <BrCardContent>
          The current project contains various enterprise-level UI Components。
          (This project contains various enterprise-level UI components.)
        </BrCardContent>
      </BrCard>
    </BrContextMenuTrigger>
    
    <BrContextMenuContent class="w-48">
      <BrContextMenuItem>Pin project (Pin Project)</BrContextMenuItem>
      <BrContextMenuItem>Share link... (Share Link...)</BrContextMenuItem>
      <BrContextMenuSeparator />
      <BrContextMenuItem>Rename (Rename)</BrContextMenuItem>
      <BrContextMenuItem class="text-destructive focus:text-destructive">Delete project (Delete Project)</BrContextMenuItem>
    </BrContextMenuContent>
  </BrContextMenu>
</template>

表格行右键操作

演示如何将右键菜单与数据表格无缝结合,在每一行绑定右键事件。

<script setup lang="ts">
import { ref } from 'vue'
import {
  BrContextMenu,
  BrContextMenuTrigger,
  BrContextMenuContent,
  BrContextMenuItem,
  BrContextMenuSeparator
} from '@breezeui/vue'

const tableData = ref([
  { id: 1, name: 'Breeze-Core', status: 'Active' },
  { id: 2, name: 'Breeze-Vue', status: 'Developing' },
])

const handleAction = (action: string, row: any) => {
  console.log(`Execute action: ${action} on project: ${row.name}`)
}
</script>

<template>
  <div class="rounded-md border">
    <table class="w-full text-sm">
      <thead>
        <tr class="border-b bg-muted/50 text-left">
          <th class="p-4 font-medium">Project Name (Project Name)</th>
          <th class="p-4 font-medium">Status (Status)</th>
        </tr>
      </thead>
      <tbody>
        <template v-for="row in tableData" :key="row.id">
          <BrContextMenu>
            <!-- as-child Allows merging trigger attributes into native tr elements -->
            <BrContextMenuTrigger as-child>
              <tr class="border-b transition-colors hover:bg-muted/50 cursor-context-menu">
                <td class="p-4">{{ row.name }}</td>
                <td class="p-4">{{ row.status }}</td>
              </tr>
            </BrContextMenuTrigger>
            <BrContextMenuContent>
              <BrContextMenuItem @select="handleAction('View', row)">View Details (View Details)</BrContextMenuItem>
              <BrContextMenuItem @select="handleAction('Edit', row)">Edit (Edit)</BrContextMenuItem>
              <BrContextMenuSeparator />
              <BrContextMenuItem class="text-destructive focus:text-destructive" @select="handleAction('Delete', row)">Delete (Delete)</BrContextMenuItem>
            </BrContextMenuContent>
          </BrContextMenu>
        </template>
      </tbody>
    </table>
  </div>
</template>

主题定制

BrContextMenu 的样式完全接入了 BrConfigProvider 的系统。您可以通过配置 CSS 变量来全局或局部调整其外观。

<script setup lang="ts">
import {
  BrContextMenu,
  BrContextMenuTrigger,
  BrContextMenuContent,
  BrContextMenuItem,
  BrConfigProvider
} from '@breezeui/vue'
</script>

<template>
  <BrConfigProvider
    :theme="{
      radius: '0.25rem',
      popover: '#f8fafc',
      popoverForeground: '#0f172a',
    }"
  >
    <BrContextMenu>
      <BrContextMenuTrigger class="flex h-[150px] w-full items-center justify-center rounded-md border border-dashed text-sm">
        Right-click to view custom theme (Right click to view custom theme)
      </BrContextMenuTrigger>
      <BrContextMenuContent class="w-64">
        <BrContextMenuItem>Custom Theme Item 1 (Custom Theme Item 1)</BrContextMenuItem>
        <BrContextMenuItem>Custom Theme Item 2 (Custom Theme Item 2)</BrContextMenuItem>
        <BrContextMenuItem class="focus:bg-blue-500 focus:text-white">
          Local Override Style Item (Local override style item)
        </BrContextMenuItem>
      </BrContextMenuContent>
    </BrContextMenu>
  </BrConfigProvider>
</template>

API 说明

BrContextMenu

包裹整个右键菜单的根组件,负责管理右键状态和事件。

属性类型默认值说明
dir'ltr' | 'rtl''ltr'菜单阅读方向
modalbooleantrue是否为模态模式(打开时阻止外部交互)
事件类型说明
@update:open(open: boolean) => void菜单打开/关闭时触发

BrContextMenuTrigger

触发菜单的区域。

属性类型默认值说明
asChildbooleanfalse是否将触发器属性传递给子元素,用于替换默认包裹的 span/div
classany-自定义类名

BrContextMenuContent

右键唤起后的菜单内容容器。

属性类型默认值说明
alignOffsetnumber0对齐偏移量
avoidCollisionsbooleantrue是否自动避开屏幕边缘
collisionBoundaryElement | Element[][]碰撞检测边界
collisionPaddingnumber | Record0碰撞检测内边距
loopbooleanfalse键盘导航是否循环
classany-容器类名

BrContextMenuItem

单个菜单项。

属性类型默认值说明
disabledbooleanfalse是否禁用
textValuestring-用于键盘导航的首字母匹配文本
shortcutstring-快捷键展示文本(如 ⌘P
insetbooleanfalse是否内进展示(通常用于对齐带图标的项)
classany-菜单项类名
事件类型说明
@select(event: Event) => void选中时触发,可调用 event.preventDefault() 阻止默认关闭行为

BrContextMenuSub & BrContextMenuSubTrigger & BrContextMenuSubContent

用于构建嵌套的子菜单,其属性基本等同于 Root 对应的容器组件,增加了对 hover 触发展开/收起的支持。

BrContextMenuEmpty & BrContextMenuLoading

用于处理数据为空和异步加载状态的预设占位组件。

属性类型默认值说明
textstring'No items' / 'Loading...'提示文本