安裝
Terminal
pnpm dlx shadcn-vue add @admin/dropdown-menuimport { AdminDropdownMenu, AdminDropdownMenuCheckboxItem, AdminDropdownMenuContent, AdminDropdownMenuGroup, AdminDropdownMenuItem, AdminDropdownMenuLabel, AdminDropdownMenuRadioGroup, AdminDropdownMenuRadioItem, AdminDropdownMenuSeparator, AdminDropdownMenuShortcut, AdminDropdownMenuSub, AdminDropdownMenuSubContent, AdminDropdownMenuSubTrigger, AdminDropdownMenuTrigger,} from '@/components/admin/dropdown-menu'用法
點 trigger 顯示 content;items 由鍵盤 ArrowUp / ArrowDown 切換 highlight、Enter 觸發 select、Escape 關閉。
<script setup lang="ts">import { AdminButton } from '@/components/admin/button'import { AdminDropdownMenu, AdminDropdownMenuContent, AdminDropdownMenuItem, AdminDropdownMenuLabel, AdminDropdownMenuSeparator, AdminDropdownMenuTrigger} from '@/components/admin/dropdown-menu'</script><template> <admin-dropdown-menu> <admin-dropdown-menu-trigger as-child> <admin-button variant="outline">開啟選單</admin-button> </admin-dropdown-menu-trigger> <admin-dropdown-menu-content align="start"> <admin-dropdown-menu-label>我的帳號</admin-dropdown-menu-label> <admin-dropdown-menu-separator /> <admin-dropdown-menu-item>個人資料</admin-dropdown-menu-item> <admin-dropdown-menu-item>帳號設定</admin-dropdown-menu-item> <admin-dropdown-menu-item disabled>付費方案(停用)</admin-dropdown-menu-item> <admin-dropdown-menu-separator /> <admin-dropdown-menu-item>登出</admin-dropdown-menu-item> </admin-dropdown-menu-content> </admin-dropdown-menu></template>場景
操作選單
行內 action menu,含 disabled item 與 separator 分組;常用於列表 row 右側「⋯」按鈕。
<script setup lang="ts">import { AdminButton } from '@/components/admin/button'import { AdminDropdownMenu, AdminDropdownMenuContent, AdminDropdownMenuItem, AdminDropdownMenuSeparator, AdminDropdownMenuTrigger} from '@/components/admin/dropdown-menu'function onAction(name: string) { // eslint-disable-next-line no-console console.log('action:', name)}</script><template> <admin-dropdown-menu> <admin-dropdown-menu-trigger as-child> <admin-button aria-label="更多操作" size="small" variant="ghost">⋯</admin-button> </admin-dropdown-menu-trigger> <admin-dropdown-menu-content align="end"> <admin-dropdown-menu-item @select="onAction('edit')">編輯</admin-dropdown-menu-item> <admin-dropdown-menu-item @select="onAction('duplicate')">複製</admin-dropdown-menu-item> <admin-dropdown-menu-item @select="onAction('move')">移動到...</admin-dropdown-menu-item> <admin-dropdown-menu-separator /> <admin-dropdown-menu-item disabled>歸檔(停用)</admin-dropdown-menu-item> <admin-dropdown-menu-separator /> <admin-dropdown-menu-item @select="onAction('delete')">刪除</admin-dropdown-menu-item> </admin-dropdown-menu-content> </admin-dropdown-menu></template>進階選單
checkbox item(多選)、radio group(單選)、巢狀 sub-menu、快捷鍵提示。
<script setup lang="ts">import type { AcceptableValue } from 'reka-ui'import { ref } from 'vue'import { AdminButton } from '@/components/admin/button'import { AdminDropdownMenu, AdminDropdownMenuCheckboxItem, AdminDropdownMenuContent, AdminDropdownMenuGroup, AdminDropdownMenuItem, AdminDropdownMenuLabel, AdminDropdownMenuRadioGroup, AdminDropdownMenuRadioItem, AdminDropdownMenuSeparator, AdminDropdownMenuShortcut, AdminDropdownMenuSub, AdminDropdownMenuSubContent, AdminDropdownMenuSubTrigger, AdminDropdownMenuTrigger} from '@/components/admin/dropdown-menu'const showStatusBar = ref(true)const showActivityBar = ref(false)const position = ref<AcceptableValue>('bottom')</script><template> <admin-dropdown-menu> <admin-dropdown-menu-trigger as-child> <admin-button variant="outline">檢視選項</admin-button> </admin-dropdown-menu-trigger> <admin-dropdown-menu-content align="start" class="w-56"> <admin-dropdown-menu-label>外觀</admin-dropdown-menu-label> <admin-dropdown-menu-separator /> <admin-dropdown-menu-checkbox-item v-model="showStatusBar"> 狀態列 </admin-dropdown-menu-checkbox-item> <admin-dropdown-menu-checkbox-item v-model="showActivityBar"> 活動列 </admin-dropdown-menu-checkbox-item> <admin-dropdown-menu-separator /> <admin-dropdown-menu-label>面板位置</admin-dropdown-menu-label> <admin-dropdown-menu-radio-group v-model="position"> <admin-dropdown-menu-radio-item value="top">上方</admin-dropdown-menu-radio-item> <admin-dropdown-menu-radio-item value="bottom">下方</admin-dropdown-menu-radio-item> <admin-dropdown-menu-radio-item value="right">右側</admin-dropdown-menu-radio-item> </admin-dropdown-menu-radio-group> <admin-dropdown-menu-separator /> <admin-dropdown-menu-group> <admin-dropdown-menu-item> 新增檔案 <admin-dropdown-menu-shortcut>⌘N</admin-dropdown-menu-shortcut> </admin-dropdown-menu-item> <admin-dropdown-menu-sub> <admin-dropdown-menu-sub-trigger>更多工具</admin-dropdown-menu-sub-trigger> <admin-dropdown-menu-sub-content> <admin-dropdown-menu-item>移動到…</admin-dropdown-menu-item> <admin-dropdown-menu-item>複製連結</admin-dropdown-menu-item> <admin-dropdown-menu-separator /> <admin-dropdown-menu-sub> <admin-dropdown-menu-sub-trigger>匯出為</admin-dropdown-menu-sub-trigger> <admin-dropdown-menu-sub-content> <admin-dropdown-menu-item>PDF</admin-dropdown-menu-item> <admin-dropdown-menu-item>PNG</admin-dropdown-menu-item> </admin-dropdown-menu-sub-content> </admin-dropdown-menu-sub> </admin-dropdown-menu-sub-content> </admin-dropdown-menu-sub> </admin-dropdown-menu-group> </admin-dropdown-menu-content> </admin-dropdown-menu></template>鍵盤導航
| 快捷鍵 | 行為 |
|---|---|
ArrowDown / ArrowUp | 移動 highlight 到下一個 / 上一個 item |
Enter / Space | 觸發 highlighted item 的 select event 並關閉 menu |
Escape | 關閉 menu,focus 回 trigger |
Tab | 關閉 menu,focus 移到下一個 tabbable 元素 |
Home / End | 跳到第一個 / 最後一個 item |
ArrowRight | 在 sub-trigger 上展開子選單並 focus 第一個 item(RTL 相反) |
ArrowLeft | 收合子選單,focus 回 sub-trigger(RTL 相反) |
| 任意可印字元 | type-ahead 跳到 textValue 開頭符合的 item |
行為由 reka-ui DropdownMenu primitive 提供,本元件薄包一層樣式。
與其他元件的差別
| 元件 | 適用情境 |
|---|---|
| admin-dropdown-menu | 觸發後彈出的「選項列表」,含鍵盤導航;典型用例:navigation menu、action menu、heading dropdown |
admin-popover | 任意 內容浮層(不限選項列表);沒有 roving tabindex / arrow key 導航 |
admin-select | 表單欄位用的 單選,會把選定值寫回 <input> |
API 參考
組成
<template> <admin-dropdown-menu> <admin-dropdown-menu-trigger>開啟選單</admin-dropdown-menu-trigger> <admin-dropdown-menu-content> <admin-dropdown-menu-label>群組標題</admin-dropdown-menu-label> <admin-dropdown-menu-separator /> <admin-dropdown-menu-item> 選項一 <admin-dropdown-menu-shortcut>⌘K</admin-dropdown-menu-shortcut> </admin-dropdown-menu-item> <admin-dropdown-menu-checkbox-item v-model="checked">可勾選項</admin-dropdown-menu-checkbox-item> <admin-dropdown-menu-radio-group v-model="value"> <admin-dropdown-menu-radio-item value="a">單選 A</admin-dropdown-menu-radio-item> <admin-dropdown-menu-radio-item value="b">單選 B</admin-dropdown-menu-radio-item> </admin-dropdown-menu-radio-group> <admin-dropdown-menu-sub> <admin-dropdown-menu-sub-trigger>更多</admin-dropdown-menu-sub-trigger> <admin-dropdown-menu-sub-content> <admin-dropdown-menu-item>子選項</admin-dropdown-menu-item> </admin-dropdown-menu-sub-content> </admin-dropdown-menu-sub> </admin-dropdown-menu-content> </admin-dropdown-menu></template>admin-dropdown-menu
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
defaultOpen | boolean | undefined | — | The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | undefined | — | The controlled open state of the menu. Can be used as `v-model:open`. |
dir | Direction$4 | undefined | — | The reading direction of the combobox when applicable. If omitted, inherits globally from `ConfigProvider` or assumes LTR (left-to-right) reading mode. |
modal | boolean | undefined | — | The modality of the dropdown menu. When set to `true`, interaction with outside elements will be disabled and only menu content will be visible to screen readers. |
class | unknown | — | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
update:open | boolean | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | { open: boolean; } | — |
admin-dropdown-menu-trigger
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
disabled | boolean | undefined | — | When `true`, prevents the user from interacting with item |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-content
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
forceMount | boolean | undefined | — | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
loop | boolean | undefined | false | When `true`, keyboard navigation will loop from last item to first, and vice versa. |
align | "start" | "end" | "center" | undefined | "center" | The preferred alignment against the trigger. May change when collisions occur. |
sideOffset | number | undefined | 4 | The distance in pixels from the trigger. |
memoDependencies | unknown[] | undefined | — | Reactive dependencies that should invalidate the memoized content subtree. |
side | "top" | "right" | "bottom" | "left" | undefined | "bottom" | The preferred side of the trigger to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. |
sideFlip | boolean | undefined | true | Flip to the opposite side when colliding with boundary. |
alignOffset | number | undefined | 0 | An offset in pixels from the `start` or `end` alignment options. |
alignFlip | boolean | undefined | true | Flip alignment when colliding with boundary. May only occur when `prioritizePosition` is true. |
avoidCollisions | boolean | undefined | true | When `true`, overrides the side and align preferences to prevent collisions with boundary edges. |
collisionBoundary | Element | (Element | null)[] | null | undefined | [] | The 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>> | undefined | 0 | 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 }. |
arrowPadding | number | undefined | 0 | The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. |
hideShiftedArrow | boolean | undefined | true | When `true`, hides the arrow when it cannot be centered to the reference element. |
sticky | "partial" | "always" | undefined | "partial" | The sticky behavior on the align axis. `partial` will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. |
hideWhenDetached | boolean | undefined | false | Whether to hide the content when the trigger becomes fully occluded. |
positionStrategy | "fixed" | "absolute" | undefined | — | The type of CSS position property to use. |
updatePositionStrategy | "always" | "optimized" | undefined | 'optimized' | Strategy to update the position of the floating element on every animation frame. |
disableUpdateOnLayoutShift | boolean | undefined | false | Whether to disable the update position for the content when the layout shifted. |
prioritizePosition | boolean | undefined | false | Force content to be position within the viewport. Might overlap the reference element, which may not be desired. |
reference | ReferenceElement | undefined | — | The 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. |
class | ClassValue | — | — |
to | string | HTMLElement | undefined | — | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
escapeKeyDown | KeyboardEvent | — |
pointerDownOutside | PointerDownOutsideEvent | — |
focusOutside | FocusOutsideEvent | — |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | — |
closeAutoFocus | Event | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-item
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
disabled | boolean | undefined | — | When `true`, prevents the user from interacting with the item. |
textValue | string | undefined | — | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item. Use this when the content is complex, or you have non-textual content inside. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
inset | boolean | undefined | false | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
select | Event | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-separator
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
admin-dropdown-menu-label
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
inset | boolean | undefined | false | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-group
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | unknown | — | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-checkbox-item
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
modelValue | CheckedState | undefined | — | The controlled checked state of the item. Can be used as `v-model`. |
disabled | boolean | undefined | — | When `true`, prevents the user from interacting with the item. |
textValue | string | undefined | — | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item. Use this when the content is complex, or you have non-textual content inside. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
select | Event | — |
update:modelValue | boolean | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-radio-group
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
modelValue | AcceptableValue | undefined | — | The value of the selected item in the group. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
update:modelValue | AcceptableValue | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-radio-item
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
value* | AcceptableValue | — | The unique value of the item. |
disabled | boolean | undefined | — | When `true`, prevents the user from interacting with the item. |
textValue | string | undefined | — | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item. Use this when the content is complex, or you have non-textual content inside. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
* 代表必填
Events
| 名稱 | Payload | 說明 |
|---|---|---|
select | Event | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-sub
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
defaultOpen | boolean | undefined | — | The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. |
open | boolean | undefined | — | The controlled open state of the menu. Can be used as `v-model:open`. |
class | unknown | — | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
update:open | boolean | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-sub-trigger
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
disabled | boolean | undefined | — | When `true`, prevents the user from interacting with the item. |
textValue | string | undefined | — | Optional text used for typeahead purposes. By default the typeahead behavior will use the `.textContent` of the item. Use this when the content is complex, or you have non-textual content inside. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
class | ClassValue | — | — |
inset | boolean | undefined | false | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-sub-content
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
forceMount | boolean | undefined | — | Used to force mounting when more control is needed. Useful when controlling animation with Vue animation libraries. |
as | globalThis.Component | AsTag | undefined | "div" | The element or component this component should render as. Can be overwritten by `asChild`. |
asChild | boolean | undefined | — | Change the default rendered element for the one passed as a child, merging their props and behavior. Read our [Composition](https://www.reka-ui.com/docs/guides/composition) guide for more details. |
loop | boolean | undefined | false | When `true`, keyboard navigation will loop from last item to first, and vice versa. |
sideOffset | number | undefined | 0 | The distance in pixels from the trigger. |
memoDependencies | unknown[] | undefined | — | Reactive dependencies that should invalidate the memoized content subtree. |
sideFlip | boolean | undefined | true | Flip to the opposite side when colliding with boundary. |
alignOffset | number | undefined | 0 | An offset in pixels from the `start` or `end` alignment options. |
alignFlip | boolean | undefined | true | Flip alignment when colliding with boundary. May only occur when `prioritizePosition` is true. |
avoidCollisions | boolean | undefined | true | When `true`, overrides the side and align preferences to prevent collisions with boundary edges. |
collisionBoundary | Element | (Element | null)[] | null | undefined | [] | The 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>> | undefined | 0 | 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 }. |
arrowPadding | number | undefined | 0 | The padding between the arrow and the edges of the content. If your content has border-radius, this will prevent it from overflowing the corners. |
hideShiftedArrow | boolean | undefined | true | When `true`, hides the arrow when it cannot be centered to the reference element. |
sticky | "partial" | "always" | undefined | "partial" | The sticky behavior on the align axis. `partial` will keep the content in the boundary as long as the trigger is at least partially in the boundary whilst "always" will keep the content in the boundary regardless. |
hideWhenDetached | boolean | undefined | false | Whether to hide the content when the trigger becomes fully occluded. |
positionStrategy | "fixed" | "absolute" | undefined | — | The type of CSS position property to use. |
updatePositionStrategy | "always" | "optimized" | undefined | 'optimized' | Strategy to update the position of the floating element on every animation frame. |
disableUpdateOnLayoutShift | boolean | undefined | false | Whether to disable the update position for the content when the layout shifted. |
prioritizePosition | boolean | undefined | false | Force content to be position within the viewport. Might overlap the reference element, which may not be desired. |
reference | ReferenceElement | undefined | — | The 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. |
class | ClassValue | — | — |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
escapeKeyDown | KeyboardEvent | — |
pointerDownOutside | PointerDownOutsideEvent | — |
focusOutside | FocusOutsideEvent | — |
interactOutside | PointerDownOutsideEvent | FocusOutsideEvent | — |
openAutoFocus | Event | — |
closeAutoFocus | Event | — |
entryFocus | Event | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |
admin-dropdown-menu-shortcut
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | ClassValue | — | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | {} | — |