上下文菜单
功能特性
- 支持具有可配置阅读方向的子菜单。
- 支持项目、标签、项目组。
- 支持可勾选项目(单选或多选),并带有可选的未定状态。
- 支持模态和非模态模式。
- 可自定义侧边、对齐方式、偏移量和碰撞处理。
- 完全管理焦点。
- 完全键盘导航。
- 支持输入预测。
- 关闭和分层行为高度可自定义。
- 在触摸设备上通过长按触发
安装
从命令行安装该组件。
$ npm add reka-ui结构
导入所有部分并将它们组合在一起。
<script setup lang="ts">
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuGroup,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuSub,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger />
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuLabel />
<ContextMenuItem />
<ContextMenuGroup>
<ContextMenuItem />
</ContextMenuGroup>
<ContextMenuCheckboxItem>
<ContextMenuItemIndicator />
</ContextMenuCheckboxItem>
<ContextMenuRadioGroup>
<ContextMenuRadioItem>
<ContextMenuItemIndicator />
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
<ContextMenuSub>
<ContextMenuSubTrigger />
<ContextMenuPortal>
<ContextMenuSubContent />
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSeparator />
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>API 参考
遵循 菜单 WAI-ARIA 设计模式 并使用 roving tabindex 来管理菜单项之间的焦点移动。
Root
包含上下文菜单的所有部分。
| Prop | Default | Type |
|---|---|---|
dir | 'ltr' | 'rtl'The reading direction of the combobox when applicable. If omitted, inherits globally from | |
modal | true | booleanThe modality of the dropdown menu. When set to |
pressOpenDelay | 700 | numberThe duration from when the trigger is pressed until the menu opens. |
| Emit | Payload |
|---|---|
update:open | [payload: boolean]Event handler called when the open state of the submenu changes. |
Trigger
用于打开上下文菜单的区域。将其包裹在目标元素周围,当右键单击(或使用相关的键盘快捷键)时,上下文菜单将从该目标打开。
| Prop | Default | Type |
|---|---|---|
as | 'span' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | false | booleanWhen Note that this will also restore the native context menu. |
| Data Attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
Portal
当使用时,将内容部分传送到 body 中。
| Prop | Default | Type |
|---|---|---|
defer | booleanDefer the resolving of a Teleport target until other parts of the application have mounted (requires Vue 3.5.0+) | |
disabled | booleanDisable teleport and render the component inline | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
to | string | HTMLElementVue native teleport component prop |
Content
在打开的上下文菜单中弹出的组件。
| Prop | Default | Type |
|---|---|---|
alignFlip | booleanFlip alignment when colliding with boundary.
May only occur when | |
alignOffset | 0 | numberAn offset in pixels from the |
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | true | booleanWhen |
collisionBoundary | [] | Element | (Element | null)[] | nullThe element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. |
collisionPadding | 0 | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. |
disableUpdateOnLayoutShift | booleanWhether to disable the update position for the content when the layout shifted. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideWhenDetached | false | booleanWhether to hide the content when the trigger becomes fully occluded. |
loop | booleanWhen | |
positionStrategy | 'fixed' | 'absolute'The type of CSS position property to use. | |
prioritizePosition | booleanForce content to be position within the viewport. Might overlap the reference element, which may not be desired. | |
reference | ReferenceElementThe custom element or virtual element that will be set as the reference to position the floating element. If provided, it will replace the default anchor element. | |
sideFlip | booleanFlip to the opposite side when colliding with boundary. | |
sticky | 'partial' | 'partial' | 'always'The sticky behavior on the align axis. |
| Emit | Payload |
|---|---|
closeAutoFocus | [event: Event]Event handler called when auto-focusing on close. Can be prevented. |
escapeKeyDown | [event: KeyboardEvent]Event handler called when the escape key is down. Can be prevented. |
focusOutside | [event: FocusOutsideEvent]Event handler called when the focus moves outside of the |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent]Event handler called when an interaction happens outside the |
pointerDownOutside | [event: PointerDownOutsideEvent]Event handler called when a |
<极AttributesTable :data="[ { attribute: '[data-state]', values: ['open', 'closed'], }, { attribute: '[data-side]', values: ['left', 'right', 'bottom', 'top'], }, { attribute: '[data-align]', values: ['start', 'end', 'center'], }, ]" />
| CSS Variable | Description |
|---|---|
--reka-context-menu-content-transform-origin |
根据内容和箭头位置/偏移量计算出的 transform-origin
|
--reka-context-menu-content-available-width |
触发器和边界边缘之间的剩余宽度
|
--reka-context-menu-content-available-height |
触发器和边界边缘之间的剩余高度
|
--reka-context-menu-trigger-width | 触发器的宽度 |
--reka-context-menu-trigger-height | 触发器的高度 |
Arrow
一个可选的箭头元素,与子菜单一起渲染。这可用于在视觉上将触发器项与 ContextMenu.Content 联系起来。必须在 ContextMenu.Content 内部渲染。
| Prop | Default | Type |
|---|---|---|
as | 'svg' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
height | 5 | numberThe height of the arrow in pixels. |
rounded | booleanWhen | |
width | 10 | numberThe width of the arrow in pixels. |
Item
包含上下文菜单项的组件。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the |
| Emit | Payload |
|---|---|
select | [event: Event]Event handler called when the user selects an item (via mouse or keyboard). |
<DataAttributes极able :data="[ { attribute: '[data-highlighted]', values: '高亮时存在', }, { attribute: '[data-disabled]', values: '禁用时存在', }, ]" />
Group
用于分组多个 ContextMenu.Item。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Label
用于渲染标签。无法使用方向键聚焦。
CheckboxItem
一个可以像复选框一样被控制和渲染的项目。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
modelValue | false | true | 'indeterminate'The controlled checked state of the item. Can be used as | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the |
| Emit | Payload |
|---|---|
select | [event: Event]Event handler called when the user selects an item (via mouse or keyboard). |
update:modelValue | [payload: boolean]Event handler called when the value changes. |
| Data Attribute | Value |
|---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | 高亮时存在 |
[data-disabled] | 禁用时存在 |
RadioGroup
用于分组多个 ContextMenu.RadioItem。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
modelValue | stringThe value of the selected item in the group. |
| Emit | Payload |
|---|---|
update:modelValue | [payload: string]Event handler called when the value changes. |
RadioItem
一个可以像单选按钮一样被控制和渲染的项目。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the | |
value* | stringThe unique value of the item. |
| Emit | Payload |
|---|---|
select | [event: Event]Event handler called when the user selects an item (via mouse or keyboard). |
| Data Attribute | Value |
|---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
[data-highlighted] | 高亮时存在 |
[data-disabled] | 禁用时存在 |
ItemIndicator
当父级 ContextMenu.CheckboxItem 或 ContextMenu.RadioItem 被选中时渲染。你可以直接为此元素设置样式,或者将其用作包装器来放入图标,或者两者兼而有之。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
| Data Attribute | Value |
|---|---|
[data-state] | "checked" | "unchecked" | "indeterminate" |
Separator
用于在上下文菜单中视觉上分隔项目。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. |
Sub
包含子菜单的所有部分。
| Prop | Default | Type |
|---|---|---|
defaultOpen | booleanThe open state of the submenu when it is initially rendered. Use when you do not need to control its open state. | |
open | booleanThe controlled open state of the menu. Can be used as |
| Emit | Payload |
|---|---|
update:open | [payload: boolean]Event handler called when the open state of the submenu changes. |
| Slots (default) | Payload |
|---|---|
open | booleanCurrent open state |
SubTrigger
打开子菜单的项目。必须在 ContextMenu.Sub 内部渲染。
| Prop | Default | Type |
|---|---|---|
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
disabled | booleanWhen | |
textValue | stringOptional text used for typeahead purposes. By default the typeahead behavior will use the |
| Data Attribute | Value |
|---|---|
[data极state] | "open" | "closed" |
[data-highlighted] | 高亮时存在 |
[data-disabled] | 禁用时存在 |
SubContent
当子菜单打开时弹出的组件。必须在 ContextMenu.Sub 内部渲染。
| Prop | Default | Type |
|---|---|---|
alignFlip | booleanFlip alignment when colliding with boundary.
May only occur when | |
alignOffset | numberAn offset in pixels from the | |
arrowPadding | numberThe padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. | |
as | 'div' | AsTag | ComponentThe element or component this component should render as. Can be overwritten by |
asChild | booleanChange the default rendered element for the one passed as a child, merging their props and behavior. Read our Composition guide for more details. | |
avoidCollisions | booleanWhen | |
collisionBoundary | Element | (Element | null)[] | nullThe element used as the collision boundary. By default this is the viewport, though you can provide additional element(s) to be included in this check. | |
collisionPadding | number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>The distance in pixels from the boundary edges where collision detection should occur. Accepts a number (same for all sides), or a partial padding object, for example: { top: 20, left: 20 }. | |
disableUpdateOnLayoutShift | booleanWhether to disable the update position for the content when the layout shifted. | |
forceMount | booleanUsed to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. | |
hideWhenDetached | booleanWhether to hide the content when the trigger becomes fully occluded. | |
loop | booleanWhen | |
positionStrategy | 'fixed' | 'absolute'The type of CSS position property to use. | |
prioritizePosition | booleanForce content to be position within the viewport. Might overlap the reference element, which may not be desired. | |
reference | ReferenceElementThe custom element or virtual element that will be set as the reference to position the floating element. If provided, it will replace the default anchor element. | |
sideFlip | booleanFlip to the opposite side when colliding with boundary. | |
sideOffset | numberThe distance in pixels from the trigger. | |
sticky | 'partial' | 'always'The sticky behavior on the align axis. | |
updatePositionStrategy | 'always' | 'optimized'Strategy to update the position of the floating element on every animation frame. |
| Emit | Payload |
|---|---|
closeAutoFocus | [event: Event]Event handler called when auto-focusing on close. Can be prevented. |
entryFocus | [event: Event]Event handler called when container is being focused. Can be prevented. |
escapeKeyDown | [event: KeyboardEvent]Event handler called when the escape key is down. Can be prevented. |
focusOutside | [event: FocusOutsideEvent]Event handler called when the focus moves outside of the |
interactOutside | [event: PointerDownOutsideEvent | FocusOutsideEvent]Event handler called when an interaction happens outside the |
openAutoFocus | [event: Event]Event handler called when auto-focusing on open. Can be prevented. |
pointerDownOutside | [event: PointerDownOutsideEvent]Event handler called when a |
| Data Attribute | Value |
|---|---|
[data-state] | "open" | "closed" |
[data-side] | "left" | "right" | "bottom" | "top" |
[data-align] | "start" | "end" | "center" |
| CSS Variable | Description |
|---|---|
--reka-context-menu-content-transform-origin |
根据内容和箭头位置/偏移量计算出的 transform-origin
|
--reka-context-menu-content-available-width | 触发器和边界边缘之间的剩余宽度 |
--reka-context-menu-content-available-height | 触发器和边界边缘之间的剩余高度 |
--reka-context-menu-trigger-width | 触发器的宽度 |
--reka-context-menu-trigger-height | 触发器的高度 |
示例
带子菜单
你可以通过组合使用 ContextMenuSub 及其部分来创建子菜单。
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenu极,
ContextMenuSubContent,
ContextMenuSubTrigger,
ContextMenuTrigger,
} from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuSub>
<ContextMenuSubTrigger>子菜单 →</ContextMenuSubTrigger>
<ContextMenuPortal>
<ContextMenuSubContent>
<ContextMenuItem>子菜单项</ContextMenuItem>
<ContextMenuItem>子菜单项</ContextMenuItem>
<ContextMenuArrow />
</ContextMenuSubContent>
</ContextMenuPortal>
</ContextMenuSub>
<ContextMenuSepar极 />
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>带禁用项
你可以通过 data-disabled 属性为禁用的项目添加特殊样式。
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem
class="ContextMenuItem"
disabled
>
…
</ContextMenuItem>
<ContextMenuItem class="ContextMenuItem">
…
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>/* styles.css */
.ContextMenuItem[data-disabled] {
color: gainsboro;
}带分隔符
使用 Separator 部分在项目之间添加分隔符。
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from 'reka-ui'
</script>
<template>
<ContextMenu极>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>带标签
使用 Label 部分帮助标记一个区域。
<script setup lang="ts">
import {
ContextMenuContent,
ContextMenuItem,
ContextMenuLabel,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuTrigger,
} from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>极…</ContextMenuTrigger>
<ContextMenuPortal>
极 <ContextMenuContent>
<ContextMenuLabel>标签</ContextMenuLabel>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>带复选框项目
使用 CheckboxItem 部分添加一个可以被勾选的项目。
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenu极ItemIndicator,
ContextMenuPortal,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from 'reka-ui'
const checked = ref(true)
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuItem>…</ContextMenuItem>
<ContextMenuSeparator />
<ContextMenuCheckboxItem v-model="checked">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
复选框项
</ContextMenuCheckboxItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>带单选项目
使用 RadioGroup 和 RadioItem 部分添加一个可以在多个选项中被选中的项目。
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
ContextMenuCheckboxItem,
ContextMenuContent,
ContextMenuItem,
ContextMenuItemIndicator,
ContextMenuPortal,
ContextMenuRadioGroup,
ContextMenuRadioItem,
ContextMenuRoot,
ContextMenuSeparator,
ContextMenuTrigger,
} from 'reka-ui'
const color = ref('blue')
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuRadioGroup v-model="color">
<ContextMenuRadioItem value="red">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
红色
</ContextMenuRadioItem>
<ContextMenuRadioItem value="blue">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
蓝色
</ContextMenuRadioItem>
<ContextMenuRadioItem value="green">
<ContextMenuItemIndicator>
<Icon icon="radix-icons:check" />
</ContextMenuItemIndicator>
绿色
</ContextMenuRadioItem>
</ContextMenuRadioGroup>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>带复杂项目
你可以在 Item 部分添加额外的装饰性元素,例如图像。
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent>
<ContextMenuItem>
<img src="…">
Adolfo Hess
</ContextMenuItem>
<ContextMenuItem>
<img src="…">
Miyah Myles
</ContextMenuItem>
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>限制内容/子内容的尺寸
你可能希望限制内容(或子内容)的宽度,使其与触发器(或子触发器)的宽度匹配。你可能还希望限制其高度,使其不超过视口。
我们暴露了几个 CSS 自定义属性,如 --reka-context-menu-trigger-width 和 --reka-context-menu-content-available-height 来支持这一点。使用它们来限制内容尺寸。
<script setup lang="ts">
import { ContextMenuContent, ContextMenuItem, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>/* styles.css */
.ContextMenuContent {
width: var(--reka-context-menu-trigger-width);
max-height: var(--reka-context-menu-content-available-height);
}基于原点的动画
我们暴露了一个 CSS 自定义属性 --reka-context-menu-content-transform-origin。使用它来根据 side、sideOffset、align、alignOffset 以及任何碰撞,从计算出的原点动画显示内容。
<script setup lang="ts">
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'reka-ui'
</script>
<template>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>/* styles.css */
.ContextMenuContent {
transform-origin: var(--reka-context-menu-content-transform-origin);
animation: scaleIn 0.5s ease-out;
}
@keyframes scaleIn {
from {
opacity: 0;
transform: scale(0);
}
to {
opacity: 1;
transform: scale(1);
}
}基于碰撞的动画
我们暴露了 data-side 和 data-align 属性。它们的值会在运行时根据碰撞情况发生变化。使用它们来创建基于碰撞和方向的动画。
<script setup lang="ts">
import { ContextMenuContent, ContextMenuPortal, ContextMenuRoot, ContextMenuTrigger } from 'reka-ui'
</script>
<极>
<ContextMenuRoot>
<ContextMenuTrigger>…</ContextMenuTrigger>
<ContextMenuPortal>
<ContextMenuContent class="ContextMenuContent">
…
</ContextMenuContent>
</ContextMenuPortal>
</ContextMenuRoot>
</template>/* styles.css */
.ContextMenuContent {
animation-duration: 0.6s;
animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.ContextMenuContent[data-side="top"] {
animation-name: slideUp;
}
.ContextMenuContent[data-side="bottom"] {
animation-name: slideDown;
}
@keyframes slideUp {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@keyframes slide极 {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}无障碍性
使用 roving tabindex 来管理菜单项之间的焦点移动。
键盘交互
| Key | Description |
|---|---|
Space | 激活聚焦的项。 |
Enter | 激活聚焦的项。 |
ArrowDown | 将焦点移动到下一项。 |
ArrowUp | 将焦点移动到前一项。 |
ArrowRightArrowLeft |
当焦点在 ContextMenu.SubTrigger 上时,根据阅读方向打开或关闭子菜单。
|
Esc | 关闭上下文菜单 |
