Reka UI 标志Reka
backdrop
组件

DropdownMenu

显示一个菜单给用户——例如一组操作或功能——通过按钮触发。

特性

  • 可受控或不受控。
  • 支持子菜单,可配置阅读方向。
  • 支持项目、标签、项目组。
  • 支持可勾选项目(单选或多选),并可选不确定状态。
  • 支持模态和非模态模式。
  • 自定义侧边、对齐方式、偏移量、碰撞处理。
  • 可选渲染一个指向箭头。
  • 焦点完全受管理。
  • 完整的键盘导航。
  • 即时搜索支持。
  • 关闭和分层行为高度可定制。

安装

从命令行安装此组件。

sh
$ npm add reka-ui

结构

导入所有部分并将其组合。

vue
<script setup lang="ts">
import {
  DropdownMenuArrow,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuItemIndicator,
  DropdownMenuLabel,
  DropdownMenuPortal,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuRoot,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger />

    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuLabel />
        <DropdownMenuItem />

        <DropdownMenuGroup>
          <DropdownMenuItem />
        </DropdownMenuGroup>

        <DropdownMenuCheckboxItem>
          <DropdownMenuItemIndicator />
        </DropdownMenuCheckboxItem>

        <DropdownMenuRadioGroup>
          <DropdownMenuRadioItem>
            <DropdownMenuItemIndicator />
          </DropdownMenuRadioItem>
        </DropdownMenuRadioGroup>

        <DropdownMenuSub>
          <DropdownMenuSubTrigger />
          <DropdownMenuPortal>
            <DropdownMenuSubContent />
          </DropdownMenuPortal>
        </DropdownMenuSub>

        <DropdownMenuSeparator />
        <DropdownMenuArrow />
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

API 参考

根组件

包含下拉菜单的所有部分。

属性默认类型
defaultOpen
布尔值

下拉菜单初始渲染时的打开状态。当您不需要控制其打开状态时使用。

dir
'ltr' | 'rtl'

适用时,组合框的阅读方向。

如果省略,则全局继承自 ConfigProvider 或假定为 LTR(从左到右)阅读模式。

modal
布尔值

下拉菜单的模态。

当设置为 true 时,与外部元素的交互将被禁用,并且只有菜单内容对屏幕阅读器可见。

open
布尔值

菜单的受控打开状态。可用作 v-model:open

事件触发载荷
update:open
[载荷: 布尔值]

子菜单打开状态改变时调用的事件处理程序。

插槽(默认)载荷
open
布尔值

当前打开状态

触发器

切换下拉菜单的按钮。默认情况下,DropdownMenuContent 将根据触发器自身定位。

属性默认类型
as
'button'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

disabled
布尔值

当为 true 时,阻止用户与项目交互

数据属性
[data-state]"打开" | "关闭"
[data-disabled]禁用时存在

传送门

使用时,将内容部分传送到 body 中。

属性默认类型
defer
布尔值

延迟解析 Teleport 目标,直到应用程序的其他部分挂载(需要 Vue 3.5.0+)

reference

disabled
布尔值

禁用传送并内联渲染组件

reference

forceMount
布尔值

当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

to
字符串 | HTMLElement

Vue 原生传送组件属性 :to

reference

内容

下拉菜单打开时弹出的组件。

属性默认类型
align
'start' | 'center' | 'end'

相对于触发器的首选对齐方式。当发生碰撞时可能会改变。

alignOffset
数字

startend 对齐选项的像素偏移量。

arrowPadding
数字

箭头与内容边缘之间的内边距。如果您的内容有 border-radius,这将防止它溢出角落。

as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

avoidCollisions
布尔值

当为 true 时,覆盖侧边和对齐偏好,以防止与边界边缘发生碰撞。

collisionBoundary
Element | (Element | null)[] | null

用作碰撞边界的元素。默认情况下是视口,但您可以提供其他元素以包含在此检查中。

collisionPadding
数字 | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>

碰撞检测应发生的距边界边缘的像素距离。接受一个数字(所有边相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。

disableUpdateOnLayoutShift
布尔值

当布局发生偏移时,是否禁用更新内容位置。

forceMount
布尔值

当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

hideWhenDetached
布尔值

当触发器完全被遮挡时,是否隐藏内容。

loop
布尔值

当为 true 时,键盘导航将从最后一个项目循环到第一个项目,反之亦然。

positionStrategy
'fixed' | 'absolute'

要使用的 CSS position 属性类型。

prioritizePosition
布尔值

强制内容在视口内定位。

可能与参考元素重叠,这可能不是期望的结果。

reference
ReferenceElement

将设置为浮动元素定位参考的自定义元素或虚拟元素。

如果提供,它将替换默认的锚点元素。

side
'top' | 'right' | 'bottom' | 'left'

打开时,相对于触发器渲染的首选侧边。当发生碰撞且启用 avoidCollisions 时,将反转。

sideOffset
数字

与触发器的像素距离。

sticky
'部分' | '始终'

对齐轴上的粘性行为。partial 将使内容保持在边界内,只要触发器至少部分在边界内;而 "always" 则无论如何都会将内容保持在边界内。

updatePositionStrategy
'始终' | '优化'

在每个动画帧更新浮动元素位置的策略。

事件触发载荷
closeAutoFocus
[事件: Event]

关闭时自动聚焦调用的事件处理程序。可阻止其默认行为。

escapeKeyDown
[事件: KeyboardEvent]

按下 Esc 键时调用的事件处理程序。可阻止其默认行为。

focusOutside
[事件: FocusOutsideEvent]

当焦点移出 DismissableLayer 时调用的事件处理程序。可阻止其默认行为。

interactOutside
[事件: PointerDownOutsideEvent | FocusOutsideEvent]

DismissableLayer 外部发生交互时调用的事件处理程序。具体来说,当 pointerdown 事件在外部发生或焦点移出时。可阻止其默认行为。

pointerDownOutside
[事件: PointerDownOutsideEvent]

pointerdown 事件在 DismissableLayer 外部发生时调用的事件处理程序。可阻止其默认行为。

数据属性
[data-state]"打开" | "关闭"
[data-side]"左" | "右" | "下" | "上"
[data-align]"开始" | "结束" | "居中"
[data-orientation]"垂直" | "水平"
CSS 变量描述
--reka-dropdown-menu-content-transform-origin
从内容和箭头位置/偏移量计算的 transform-origin
--reka-dropdown-menu-content-available-width
触发器与边界边缘之间的剩余宽度
--reka-dropdown-menu-content-available-height
触发器与边界边缘之间的剩余高度
--reka-dropdown-menu-trigger-width
触发器的宽度
--reka-dropdown-menu-trigger-height
触发器的高度

箭头

一个可选的箭头元素,用于与下拉菜单一起渲染。这可以帮助在视觉上将触发器与 DropdownMenuContent 连接起来。必须在 DropdownMenuContent 内部渲染。

属性默认类型
as
'svg'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

高度
5
数字

箭头的像素高度。

rounded
布尔值

当为 true 时,渲染圆角版本的箭头。不适用于 as/asChild

宽度
10
数字

箭头的像素宽度。

项目

包含下拉菜单项目的组件。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

disabled
布尔值

当为 true 时,阻止用户与项目交互。

textValue
字符串

用于即时搜索的可选文本。默认情况下,即时搜索行为将使用项目的 .textContent
当内容复杂或包含非文本内容时使用此项。

事件触发载荷
select
[事件: Event]

当用户选择项目(通过鼠标或键盘)时调用的事件处理程序。
在此处理程序中调用 event.preventDefault 将阻止在选择该项目时菜单关闭。

数据属性
[data-orientation]"垂直" | "水平"
[data-highlighted]高亮时存在
[data-disabled]禁用时存在

用于将多个 DropdownMenuItem 分组。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

标签

用于渲染标签。它不能通过箭头键聚焦。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

复选框项目

一个可受控并像复选框一样渲染的项目。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

disabled
布尔值

当为 true 时,阻止用户与项目交互。

modelValue
假 | 真 | '不确定'

项目的受控选中状态。可用作 v-model

textValue
字符串

用于即时搜索的可选文本。默认情况下,即时搜索行为将使用项目的 .textContent
当内容复杂或包含非文本内容时使用此项。

事件触发载荷
select
[事件: Event]

当用户选择项目(通过鼠标或键盘)时调用的事件处理程序。
在此处理程序中调用 event.preventDefault 将阻止在选择该项目时菜单关闭。

update:modelValue
[载荷: 布尔值]

值改变时调用的事件处理程序。

数据属性
[data-state]"已选中" | "未选中" | "不确定"
[data-highlighted]高亮时存在
[data-disabled]禁用时存在

单选组

用于将多个 DropdownMenuRadioItem 分组。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

modelValue
字符串

组中选中项目的值。

事件触发载荷
update:modelValue
[载荷: 字符串]

值改变时调用的事件处理程序。

单选项目

一个可受控并像单选按钮一样渲染的项目。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

disabled
布尔值

当为 true 时,阻止用户与项目交互。

textValue
字符串

用于即时搜索的可选文本。默认情况下,即时搜索行为将使用项目的 .textContent
当内容复杂或包含非文本内容时使用此项。

值*
字符串

项目的唯一值。

事件触发载荷
select
[事件: Event]

当用户选择项目(通过鼠标或键盘)时调用的事件处理程序。
在此处理程序中调用 event.preventDefault 将阻止在选择该项目时菜单关闭。

数据属性
[data-state]"已选中" | "未选中" | "不确定"
[data-highlighted]高亮时存在
[data-disabled]禁用时存在

项目指示器

当父级 DropdownMenuCheckboxItemDropdownMenuRadioItem 被选中时渲染。您可以直接设置此元素的样式,也可以将其用作放置图标的包装器,或两者兼而有之。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

forceMount
布尔值

当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

数据属性
[data-state]"已选中" | "未选中" | "不确定"

分隔符

用于在下拉菜单中视觉上分隔项目。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

子菜单

包含子菜单的所有部分。

属性默认类型
defaultOpen
布尔值

下拉菜单初始渲染时的打开状态。当您不需要控制其打开状态时使用。

open
布尔值

菜单的受控打开状态。可用作 v-model:open

事件触发载荷
update:open
[载荷: 布尔值]

子菜单打开状态改变时调用的事件处理程序。

插槽(默认)载荷
open
布尔值

当前打开状态

子菜单触发器

打开子菜单的项目。必须在 DropdownMenuSub 内部渲染。

属性默认类型
as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

disabled
布尔值

当为 true 时,阻止用户与项目交互。

textValue
字符串

用于即时搜索的可选文本。默认情况下,即时搜索行为将使用项目的 .textContent
当内容复杂或包含非文本内容时使用此项。

数据属性
[data-state]"打开" | "关闭"
[data-highlighted]高亮时存在
[data-disabled]禁用时存在
CSS 变量描述
--reka-dropdown-menu-content-transform-origin
从内容和箭头位置/偏移量计算的 transform-origin
--reka-dropdown-menu-content-available-width
触发器与边界边缘之间的剩余宽度
--reka-dropdown-menu-content-available-height
触发器与边界边缘之间的剩余高度
--reka-dropdown-menu-trigger-width
触发器的宽度
--reka-dropdown-menu-trigger-height
触发器的高度

子菜单内容

子菜单打开时弹出的组件。必须在 DropdownMenuSub 内部渲染。

属性默认类型
alignOffset
数字

startend 对齐选项的像素偏移量。

arrowPadding
数字

箭头与内容边缘之间的内边距。如果您的内容有 border-radius,这将防止它溢出角落。

as
'div'
AsTag | Component

此组件应渲染为的元素或组件。可通过 asChild 覆盖。

asChild
布尔值

更改默认渲染元素为作为子项传递的元素,合并它们的属性和行为。

阅读我们的 组合指南了解更多详情。

avoidCollisions
布尔值

当为 true 时,覆盖侧边和对齐偏好,以防止与边界边缘发生碰撞。

collisionBoundary
Element | (Element | null)[] | null

用作碰撞边界的元素。默认情况下是视口,但您可以提供其他元素以包含在此检查中。

collisionPadding
数字 | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>

碰撞检测应发生的距边界边缘的像素距离。接受一个数字(所有边相同),或一个部分填充对象,例如:{ top: 20, left: 20 }。

disableUpdateOnLayoutShift
布尔值

当布局发生偏移时,是否禁用更新内容位置。

forceMount
布尔值

当需要更多控制时,用于强制挂载。在与 Vue 动画库控制动画时很有用。

hideWhenDetached
布尔值

当触发器完全被遮挡时,是否隐藏内容。

loop
布尔值

当为 true 时,键盘导航将从最后一个项目循环到第一个项目,反之亦然。

positionStrategy
'fixed' | 'absolute'

要使用的 CSS position 属性类型。

prioritizePosition
布尔值

强制内容在视口内定位。

可能与参考元素重叠,这可能不是期望的结果。

reference
ReferenceElement

将设置为浮动元素定位参考的自定义元素或虚拟元素。

如果提供,它将替换默认的锚点元素。

sideOffset
数字

与触发器的像素距离。

sticky
'部分' | '始终'

对齐轴上的粘性行为。partial 将使内容保持在边界内,只要触发器至少部分在边界内;而 "always" 则无论如何都会将内容保持在边界内。

updatePositionStrategy
'始终' | '优化'

在每个动画帧更新浮动元素位置的策略。

事件触发载荷
closeAutoFocus
[事件: Event]

关闭时自动聚焦调用的事件处理程序。可阻止其默认行为。

entryFocus
[事件: Event]

容器获得焦点时调用的事件处理程序。可阻止其默认行为。

escapeKeyDown
[事件: KeyboardEvent]

按下 Esc 键时调用的事件处理程序。可阻止其默认行为。

focusOutside
[事件: FocusOutsideEvent]

当焦点移出 DismissableLayer 时调用的事件处理程序。可阻止其默认行为。

interactOutside
[事件: PointerDownOutsideEvent | FocusOutsideEvent]

DismissableLayer 外部发生交互时调用的事件处理程序。具体来说,当 pointerdown 事件在外部发生或焦点移出时。可阻止其默认行为。

openAutoFocus
[事件: Event]

打开时自动聚焦调用的事件处理程序。可阻止其默认行为。

pointerDownOutside
[事件: PointerDownOutsideEvent]

pointerdown 事件在 DismissableLayer 外部发生时调用的事件处理程序。可阻止其默认行为。

数据属性
[data-state]"打开" | "关闭"
[data-side]"左" | "右" | "下" | "上"
[data-align]"开始" | "结束" | "居中"
[data-orientation]"垂直" | "水平"

示例

带子菜单

您可以使用 DropdownMenuSub 及其部分来创建子菜单。

vue
<script setup lang="ts">
import {
  DropdownMenuArrow,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuRoot,
  DropdownMenuSeparator,
  DropdownMenuSub,
  DropdownMenuSubContent,
  DropdownMenuSubTrigger,
  DropdownMenuTrigger,
} from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuSub>
          <DropdownMenuSubTrigger>Sub menu →</DropdownMenuSubTrigger>
          <DropdownMenuPortal>
            <DropdownMenuSubContent>
              <DropdownMenuItem>Sub menu item</DropdownMenuItem>
              <DropdownMenuItem>Sub menu item</DropdownMenuItem>
              <DropdownMenuArrow />
            </DropdownMenuSubContent>
          </DropdownMenuPortal>
        </DropdownMenuSub>
        <DropdownMenuSeparator />
        <DropdownMenuItem>…</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

带禁用项目

您可以通过 data-disabled 属性为禁用项目添加特殊样式。

vue
<script setup lang="ts">
import {
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuRoot,
  DropdownMenuTrigger,
} from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuItem
          class="DropdownMenuItem"
          disabled
        >

        </DropdownMenuItem>
        <DropdownMenuItem class="DropdownMenuItem">

        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>
css
/* styles.css */
.DropdownMenuItem[data-disabled] {
  color: gainsboro;
}

带分隔符

使用 Separator 部分在项目之间添加分隔符。

vue
<script setup lang="ts">
import {
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuRoot,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuItem>…</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

带标签

使用 Label 部分帮助标记一个区域。

vue
<script setup lang="ts">
import {
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuPortal,
  DropdownMenuRoot,
  DropdownMenuTrigger,
} from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuLabel>Label</DropdownMenuLabel>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuItem>…</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

带复选框项目

使用 CheckboxItem 部分添加一个可勾选的项目。

vue
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuItemIndicator,
  DropdownMenuPortal,
  DropdownMenuRoot,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from 'reka-ui'
import { ref } from 'vue'

const checked = ref(false)
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuItem>…</DropdownMenuItem>
        <DropdownMenuSeparator />
        <DropdownMenuCheckboxItem v-model="checked">
          <DropdownMenuItemIndicator>
            <Icon icon="radix-icons:check" />
          </DropdownMenuItemIndicator>
          Checkbox item
        </DropdownMenuCheckboxItem>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

带单选项目

使用 RadioGroupRadioItem 部分添加一个可在其他项目中选中的项目。

vue
<script setup lang="ts">
import { Icon } from '@iconify/vue'
import {
  DropdownMenuContent,
  DropdownMenuItemIndicator,
  DropdownMenuPortal,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuRoot,
  DropdownMenuTrigger,
} from 'reka-ui'
import { ref } from 'vue'

const color = ref(false)
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuRadioGroup v-model="color">
          <DropdownMenuRadioItem value="red">
            <DropdownMenuItemIndicator>
              <Icon icon="radix-icons:check" />
            </DropdownMenuItemIndicator>
            Red
          </DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="blue">
            <DropdownMenuItemIndicator>
              <Icon icon="radix-icons:check" />
            </DropdownMenuItemIndicator>
            Blue
          </DropdownMenuRadioItem>
          <DropdownMenuRadioItem value="green">
            <DropdownMenuItemIndicator>
              <Icon icon="radix-icons:check" />
            </DropdownMenuItemIndicator>
            Green
          </DropdownMenuRadioItem>
        </DropdownMenuRadioGroup>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

带复杂项目

您可以在 Item 部分添加额外的装饰元素,例如图片。

vue
<script setup lang="ts">
import {
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuPortal,
  DropdownMenuRoot,
  DropdownMenuTrigger,
} from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent>
        <DropdownMenuItem>
          <img src="…">
          Adolfo Hess
        </DropdownMenuItem>
        <DropdownMenuItem>
          <img src="…">
          Miyah Myles
        </DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>

限制内容/子内容大小

您可能希望限制内容(或子内容)的宽度,使其与触发器(或子触发器)的宽度匹配。您可能还希望限制其高度不超过视口。

我们公开了一些 CSS 自定义属性,例如 --reka-dropdown-menu-trigger-width--reka-dropdown-menu-content-available-height 来支持此功能。使用它们来限制内容的尺寸。

vue
<script setup lang="ts">
import { DropdownMenuContent, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuTrigger } from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent
        class="DropdownMenuContent"
        :side-offset="5"
      >

      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>
css
/* styles.css */
.DropdownMenuContent {
  width: var(--reka-dropdown-menu-trigger-width);
  max-height: var(--reka-dropdown-menu-content-available-height);
}

原点感知动画

我们公开了一个 CSS 自定义属性 --reka-dropdown-menu-content-transform-origin。使用它来根据 sidesideOffsetalignalignOffset 和任何碰撞,从其计算出的原点对内容进行动画处理。

vue
<script setup lang="ts">
import { DropdownMenuContent, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuTrigger } from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent class="DropdownMenuContent">

      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>
css
/* styles.css */
.DropdownMenuContent {
  transform-origin: var(--reka-dropdown-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-sidedata-align 属性。它们的值将在运行时改变以反映碰撞。使用它们来创建碰撞和方向感知的动画。

vue
<script setup lang="ts">
import { DropdownMenuContent, DropdownMenuPortal, DropdownMenuRoot, DropdownMenuTrigger } from 'reka-ui'
</script>

<template>
  <DropdownMenuRoot>
    <DropdownMenuTrigger>…</DropdownMenuTrigger>
    <DropdownMenuPortal>
      <DropdownMenuContent class="DropdownMenuContent">

      </DropdownMenuContent>
    </DropdownMenuPortal>
  </DropdownMenuRoot>
</template>
css
/* styles.css */
.DropdownMenuContent {
  animation-duration: 0.6s;
  animation-timing-function: cubic-bezier(0.16, 1, 0.3, 1);
}
.DropdownMenuContent[data-side="top"] {
  animation-name: slideUp;
}
.DropdownMenuContent[data-side="bottom"] {
  animation-name: slideDown;
}

@keyframes slideUp {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes slideDown {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

可访问性

遵循 菜单按钮 WAI-ARIA 设计模式,并使用 漫游 tabindex 来管理菜单项之间的焦点移动。

键盘交互

描述
空格键
当焦点在 DropdownMenuTrigger 上时,打开下拉菜单并聚焦第一个项目。
当焦点在项目上时,激活聚焦的项目。
回车键
当焦点在 DropdownMenuTrigger 上时,打开下拉菜单并聚焦第一个项目。
当焦点在项目上时,激活聚焦的项目。
向下箭头键
当焦点在 DropdownMenuTrigger 上时,打开下拉菜单。
当焦点在项目上时,将焦点移到下一个项目。
向上箭头键
当焦点在项目上时,将焦点移到上一个项目。
向右箭头键向左箭头键
当焦点在 DropdownMenuSubTrigger 上时,根据阅读方向打开或关闭子菜单。
Esc键
关闭下拉菜单并将焦点移到 DropdownMenuTrigger

自定义 API

通过将原始部件抽象到您自己的组件中来创建您自己的 API。

抽象箭头和项目指示器

此示例抽象了 DropdownMenuArrowDropdownMenuItemIndicator 部分。它还封装了 CheckboxItemRadioItem 的实现细节。

用法

vue
<script setup lang="ts">
import {
  DropdownMenu,
  DropdownMenuCheckboxItem,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuRadioItem,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from './your-dropdown-menu'
</script>

<template>
  <DropdownMenu>
    <DropdownMenuTrigger>DropdownMenu trigger</DropdownMenuTrigger>
    <DropdownMenuContent>
      <DropdownMenuItem>Item</DropdownMenuItem>
      <DropdownMenuLabel>Label</DropdownMenuLabel>
      <DropdownMenuGroup>Group</DropdownMenuGroup>
      <DropdownMenuCheckboxItem>CheckboxItem</DropdownMenuCheckboxItem>
      <DropdownMenuSeparator>Separator</DropdownMenuSeparator>
      <DropdownMenuRadioGroup>
        <DropdownMenuRadioItem>RadioItem</DropdownMenuRadioItem>
        <DropdownMenuRadioItem>RadioItem</DropdownMenuRadioItem>
      </DropdownMenuRadioGroup>
    </DropdownMenuContent>
  </DropdownMenu>
</template>

实现

ts
export { default as DropdownMenuCheckboxItem } from 'DropdownMenuCheckboxItem.vue'
// your-dropdown-menu.ts
export { default as DropdownMenuContent } from 'DropdownMenuContent.vue'
export { default as DropdownMenuRadioItem } from 'DropdownMenuRadioItem.vue'

export {
  DropdownMenuRoot as DropdownMenu,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuRadioGroup,
  DropdownMenuSeparator,
  DropdownMenuTrigger
} from 'reka-ui'
vue
<!-- DropdownMenuContent.vue -->
<script setup lang="ts">
import type { DropdownMenuContentEmits, DropdownMenuContentProps } from 'reka-ui'
import { DropdownMenuContent, DropdownMenuPortal, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<DropdownMenuContentProps>()
const emits = defineEmits<DropdownMenuContentEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <DropdownMenuPortal>
    <DropdownMenuContent v-bind="forwarded">
      <slot />
    </DropdownMenuContent>
  </DropdownMenuPortal>
</template>
vue
<!-- DropdownMenuCheckboxItem.vue -->
<script setup lang="ts">
import type { DropdownMenuCheckboxItemEmits, DropdownMenuCheckboxItemProps } from 'reka-ui'
import { CheckIcon } from '@radix-icons/vue'
import { DropdownMenuCheckboxItem, DropdownMenuItemIndicator, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<DropdownMenuCheckboxItemProps>()
const emits = defineEmits<DropdownMenuCheckboxItemEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <DropdownMenuCheckboxItem v-bind="forwarded">
    <span>
      <DropdownMenuItemIndicator>
        <CheckIcon />
      </DropdownMenuItemIndicator>
    </span>
    <slot />
  </DropdownMenuCheckboxItem>
</template>
vue
<!-- DropdownMenuRadioItem.vue -->
<script setup lang="ts">
import type { DropdownMenuRadioItemEmits, DropdownMenuRadioItemProps } from 'reka-ui'
import { DotFilledIcon } from '@radix-icons/vue'
import { DropdownMenuItemIndicator, DropdownMenuRadioItem, useForwardPropsEmits } from 'reka-ui'

const props = defineProps<DropdownMenuRadioItemProps>()
const emits = defineEmits<DropdownMenuRadioItemEmits>()

const forwarded = useForwardPropsEmits(props, emits)
</script>

<template>
  <DropdownMenuRadioItem v-bind="forwarded">
    <span>
      <DropdownMenuItemIndicator>
        <DotFilledIcon />
      </DropdownMenuItemIndicator>
    </span>
    <slot />
  </DropdownMenuRadioItem>
</template>