Dropdown Menu
Displays a menu to the user — such as a set of actions or functions — triggered by a button.
Examples
Basic
Prop | Default | Type | Description |
---|---|---|---|
items | [] | DropdownMenuItemProps[] | The items to display in the dropdown-menu. |
label | - | string | The label to display in the dropdown-menu. |
defaultOpen | false | boolean | The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state. |
dir | ltr | ltr , rtl | The reading direction of the combobox when applicable. If omitted, inherits globally from ConfigProvider or assumes LTR (left-to-right) reading mode. |
modal | true | boolean | 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. |
open | false | boolean | The controlled open state of the menu. Can be used as v-model:open . |
Preview
Code
<script setup lang="ts">
const items = [
{
label: 'Profile',
shortcut: '⇧⌘P',
onclick: () => {
// eslint-disable-next-line no-alert
alert('Profile clicked')
},
},
{
label: 'Billing',
shortcut: '⌘B',
},
{
label: 'Settings',
shortcut: '⌘S',
},
{
label: 'Shortcuts',
shortcut: '⌘K',
},
{}, // to add a separator between items (label or items should be null).
{
label: 'Teams',
},
{
label: 'Invite users',
items: [
{
label: 'Email',
shortcut: '⌘E',
},
{
label: 'Message',
shortcut: '⌘M',
},
{},
{
label: 'More',
items: [
{
label: 'Slack',
shortcut: '⌘S',
},
{
label: 'Discord',
shortcut: '⌘D',
},
{},
{
label: 'More',
items: [
{
label: 'Telegram',
shortcut: '⌘T',
},
{
label: 'WhatsApp',
shortcut: '⌘W',
},
],
},
],
},
],
},
{
label: 'New team',
shortcut: '⌘T',
},
{},
{
label: 'GitHub',
items: [
{
label: 'Personal',
shortcut: '⌘P',
},
{
label: 'Organization',
shortcut: '⌘O',
},
],
},
{
label: 'Support',
},
{
label: 'API',
disabled: true,
},
{},
{
label: 'Logout',
shortcut: '⇧⌘Q',
},
]
</script>
<template>
<div class="grid h-50 place-items-center">
<NDropdownMenu
:items
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
align: 'end',
side: 'bottom',
}"
label="Open"
/>
</div>
</template>
Read more in Radix Dropdown Menu Root API.
Inset
Prop | Default | Type | Description |
---|---|---|---|
inset | false | boolean | Set the dropdown-menu to be inset. |
Preview
Code
<script setup lang="ts">
const items = [
{
label: 'Profile',
shortcut: '⇧⌘P',
onclick: () => {
// eslint-disable-next-line no-alert
alert('Profile clicked')
},
leading: 'i-lucide-user',
},
{
label: 'Billing',
shortcut: '⌘B',
leading: 'i-lucide-banknote',
},
{
label: 'Settings',
shortcut: '⌘S',
leading: 'i-lucide-settings',
},
{
label: 'Shortcuts',
shortcut: '⌘K',
leading: 'i-lucide-keyboard',
},
{}, // to add a separator between items (label or items should be null).
{
label: 'Teams',
leading: 'i-lucide-users',
},
{
label: 'Invite users',
leading: 'i-lucide-plus',
items: [
{
label: 'Email',
shortcut: '⌘E',
leading: 'i-lucide-mail',
},
{
label: 'Message',
shortcut: '⌘M',
leading: 'i-lucide-message-circle',
},
{},
{
label: 'More',
leading: 'i-lucide-more-horizontal',
items: [
{
label: 'Slack',
shortcut: '⌘S',
leading: 'i-logos-slack-icon',
},
{
label: 'Discord',
shortcut: '⌘D',
leading: 'i-logos-discord-icon',
},
{
label: 'Telegram',
shortcut: '⌘T',
leading: 'i-logos-telegram',
},
{
label: 'WhatsApp',
shortcut: '⌘W',
leading: 'i-logos-whatsapp-icon',
},
],
},
],
},
{
label: 'New team',
shortcut: '⌘T',
},
{},
{
label: 'GitHub',
items: [
{
label: 'Personal',
shortcut: '⌘P',
},
{
label: 'Organization',
shortcut: '⌘O',
},
],
},
{
label: 'Support',
},
{
label: 'API',
disabled: true,
},
{},
{
label: 'Logout',
shortcut: '⇧⌘Q',
},
]
</script>
<template>
<div class="grid h-50 place-items-center">
<NDropdownMenu
:items
menu-label="My Account"
inset
:_dropdown-menu-content="{
class: 'w-60',
}"
:_dropdown-menu-trigger="{
icon: true,
square: true,
rounded: 'full',
label: 'i-lucide-ellipsis-vertical',
}"
/>
</div>
</template>
Variant and Color
Prop | Default | Type | Description |
---|---|---|---|
dropdown-menu | solid-white | {variant}-{color} | Change the color of the dropdown-menu. |
dropdown-menu-item | gray | {color} | Change the color of the dropdown-menu item. |
_dropdown-menu-trigger.dropdown-menu | solid-white | {variant}-{color} | Change the color of the dropdown-menu trigger. |
_dropdown-menu-item.dropdown-menu-item | gray | {color} | Change the color of the dropdown-menu item. |
Preview
Code
<script setup lang="ts">
const items = [
{
label: 'Profile',
shortcut: '⇧⌘P',
onclick: () => {
// eslint-disable-next-line no-alert
alert('Profile clicked')
},
},
{
label: 'Billing',
shortcut: '⌘B',
},
{
label: 'Settings',
shortcut: '⌘S',
},
{
label: 'Shortcuts',
shortcut: '⌘K',
},
{}, // to add a separator between items (label or items should be null).
{
label: 'Teams',
},
{
label: 'Invite users',
items: [
{
label: 'Email',
shortcut: '⌘E',
},
{
label: 'Message',
shortcut: '⌘M',
},
{},
{
label: 'More',
items: [
{
label: 'Slack',
shortcut: '⌘S',
},
{
label: 'Discord',
shortcut: '⌘D',
},
{},
{
label: 'More',
items: [
{
label: 'Telegram',
shortcut: '⌘T',
},
{
label: 'WhatsApp',
shortcut: '⌘W',
},
],
},
],
},
],
},
{
label: 'New team',
shortcut: '⌘T',
},
{},
{
label: 'GitHub',
items: [
{
label: 'Personal',
shortcut: '⌘P',
},
{
label: 'Organization',
shortcut: '⌘O',
},
],
},
{
label: 'Support',
},
{
label: 'API',
disabled: true,
},
{},
{
label: 'Logout',
shortcut: '⇧⌘Q',
},
]
</script>
<template>
<div class="h-50 flex items-center justify-around">
<NDropdownMenu
:items
dropdown-menu="ghost-pink"
dropdown-menu-item="pink"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open"
/>
<NDropdownMenu
:items
dropdown-menu="outline-gray"
dropdown-menu-item="gray"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open"
/>
<NDropdownMenu
:items
dropdown-menu="solid-primary"
dropdown-menu-item="primary"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open"
/>
</div>
</template>
Read more in Button variant and color section
Size
Adjust the dropdown-menu size without limits. Use breakpoints
(e.g., sm:sm
, xs:lg
) for responsive sizes or states
(e.g., hover:lg
, focus:3xl
) for state-based sizes.
Prop | Default | Type | Description |
---|---|---|---|
size | sm | string | Adjusts the overall size of the dropdown-menu component. |
_dropdownMenuItem.size | sm | string | Customizes the size of each item within the dropdown-menu dropdown. |
_dropdownMenuTrigger.size | sm | string | Modifies the size of the dropdown-menu trigger element. |
_dropdownMenuLabel.size | sm | string | Adjusts the size of the dropdown-menu label. |
Preview
Code
<script setup lang="ts">
const items = [
{
label: 'Profile',
shortcut: '⇧⌘P',
onclick: () => {
// eslint-disable-next-line no-alert
alert('Profile clicked')
},
},
{
label: 'Billing',
shortcut: '⌘B',
},
{
label: 'Settings',
shortcut: '⌘S',
},
{
label: 'Shortcuts',
shortcut: '⌘K',
},
{}, // to add a separator between items (label or items should be null).
{
label: 'Teams',
},
{
label: 'Invite users',
items: [
{
label: 'Email',
shortcut: '⌘E',
},
{
label: 'Message',
shortcut: '⌘M',
},
{},
{
label: 'More',
items: [
{
label: 'Slack',
shortcut: '⌘S',
},
{
label: 'Discord',
shortcut: '⌘D',
},
{},
{
label: 'More',
items: [
{
label: 'Telegram',
shortcut: '⌘T',
},
{
label: 'WhatsApp',
shortcut: '⌘W',
},
],
},
],
},
],
},
{
label: 'New team',
shortcut: '⌘T',
},
{},
{
label: 'GitHub',
items: [
{
label: 'Personal',
shortcut: '⌘P',
},
{
label: 'Organization',
shortcut: '⌘O',
},
],
},
{
label: 'Support',
},
{
label: 'API',
disabled: true,
},
{},
{
label: 'Logout',
shortcut: '⇧⌘Q',
},
]
</script>
<template>
<div class="h-50 flex items-center justify-around">
<NDropdownMenu
:items
size="xs"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open XS"
/>
<NDropdownMenu
:items
size="sm"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open SM/Default"
/>
<NDropdownMenu
:items
size="md"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open MD"
/>
<NDropdownMenu
:items
size="lg"
menu-label="My Account"
:_dropdown-menu-content="{
class: 'w-52',
}"
label="Open LG"
/>
</div>
</template>
Slots
Name | Props | Description |
---|---|---|
trigger | - | The trigger slot. |
item | item | The item slot. |
sub-trigger | - | The sub-trigger slot. |
content | items | The content slot. |
label | label | The label slot. |
group | items | The group slot. |
Preview
Code
<template>
<div>
<NDropdownMenu
:modal="false"
:_dropdown-menu-content="{
class: 'w-60',
align: 'start',
side: 'right',
}"
>
<NAvatar
src="https://avatars.githubusercontent.com/u/499550?v=4"
/>
<template #menu-label>
<div class="flex flex-col items-start">
<span class="text-accent font-semibold leading-5">Evan You</span>
<span class="text-xs text-muted">evan@vuejs.org</span>
</div>
</template>
<template #items>
<NDropdownMenuGroup>
<NDropdownMenuItem
dropdown-menu-item="primary"
label="Join or create a workspace"
leading="i-lucide-plus"
/>
<NDropdownMenuSeparator />
<NDropdownMenuSub>
<NDropdownMenuSubTrigger
inset
dropdown-menu-item="primary"
>
<span>Invite users</span>
</NDropdownMenuSubTrigger>
<NDropdownMenuPortal>
<NDropdownMenuSubContent>
<NDropdownMenuItem
dropdown-menu-item="primary"
>
<span>Email</span>
</NDropdownMenuItem>
<NDropdownMenuItem
dropdown-menu-item="primary"
>
<span>Message</span>
</NDropdownMenuItem>
<NDropdownMenuSeparator />
<NDropdownMenuItem
dropdown-menu-item="primary"
>
<span>More...</span>
</NDropdownMenuItem>
</NDropdownMenuSubContent>
</NDropdownMenuPortal>
</NDropdownMenuSub>
</NDropdownMenuGroup>
<NDropdownMenuSeparator />
<NDropdownMenuGroup>
<NDropdownMenuItem
dropdown-menu-item="primary"
label="Profile"
leading="i-lucide-user-round"
shortcut="⌘P"
/>
<NDropdownMenuItem
dropdown-menu-item="primary"
label="Settings"
leading="i-lucide-settings"
shortcut="⌘S"
/>
</NDropdownMenuGroup>
<NDropdownMenuSeparator />
<NDropdownMenuGroup>
<NDropdownMenuItem
dropdown-menu-item="primary"
label="Logout"
leading="i-lucide-log-out"
shortcut="⌘L"
/>
</NDropdownMenuGroup>
</template>
</NDropdownMenu>
</div>
</template>
Presets
shortcuts/dropdown-menu.ts
type DropdownMenuPrefix = 'dropdown-menu'
export const staticDropdownMenu: Record<`${DropdownMenuPrefix}-${string}` | DropdownMenuPrefix, string> = {
// configurations
'dropdown-menu': '',
'dropdown-menu-default-variant': 'btn-solid-white',
// dropdown-menu-trigger
'dropdown-menu-trigger': '',
'dropdown-menu-trigger-leading': '',
'dropdown-menu-trigger-trailing': 'ml-auto',
// dropdown-menu-content
'dropdown-menu-content': 'z-50 min-w-32 overflow-hidden rounded-md border border-base bg-popover p-1 text-popover shadow-md',
// dropdown-menu-item
'dropdown-menu-item-base': 'text-left transition-color focus-visible:outline-0',
'dropdown-menu-item-leading': 'text-1em',
'dropdown-menu-item-trailing': 'ml-auto opacity-75 text-1em',
// dropdown-menu-label
'dropdown-menu-label': 'px-2 py-1.5 text-1em font-semibold',
// dropdown-menu-separator
'dropdown-menu-separator-root': 'relative -mx-1',
'dropdown-menu-separator': '',
// dropdown-menu-shortcut
'dropdown-menu-shortcut': 'pl-10 ml-auto text-0.875em tracking-widest n-disabled space-x-0.5',
// dropdown-menu-group
'dropdown-menu-group': '',
// dropdown-menu-sub
'dropdown-menu-sub-trigger': 'transition-color focus-visible:outline-0',
'dropdown-menu-sub-trigger-leading': 'text-1em',
'dropdown-menu-sub-trigger-trailing': 'ml-auto opacity-75 text-1em',
'dropdown-menu-sub-trigger-trailing-icon': 'i-lucide-chevron-right',
'dropdown-menu-sub-content': 'z-50 min-w-32 overflow-hidden rounded-md border border-base bg-popover p-1 text-popover shadow-lg',
}
export const dynamicDropdownMenu = [
[/^dropdown-menu-([^-]+)-([^-]+)$/, ([, v = 'solid', c = 'white']) => `btn-${v}-${c}`],
[/^dropdown-menu-item(?:-(\S+))?$/, ([, c = 'gray']) => `focus:bg-${c}-100 focus:text-${c}-800 dark:focus:bg-${c}-800 dark:focus:text-${c}-100 data-[state=open]:bg-${c}-100 dark:data-[state=open]:bg-${c}-800`],
]
export const dropdowMenu = [
...dynamicDropdownMenu,
staticDropdownMenu,
]
Props
types/dropdown-menu.ts
import type {
DropdownMenuContentProps,
DropdownMenuGroupProps,
DropdownMenuLabelProps,
DropdownMenuRootProps,
DropdownMenuSeparatorProps,
DropdownMenuSubContentProps,
DropdownMenuSubTriggerProps,
DropdownMenuTriggerProps,
} from 'radix-vue'
import type { HTMLAttributes } from 'vue'
import type { NButtonProps } from './button'
import type { NSeparatorProps } from './separator'
/**
* Base extensions for dropdown menu components.
*/
interface BaseExtensions {
/** CSS class for the component */
class?: HTMLAttributes['class']
/** Size of the component */
size?: HTMLAttributes['class']
}
/**
* Props for the NDropdownMenu component.
*/
export interface NDropdownMenuProps extends
Omit<NDropdownMenuRootProps, 'class' | 'size'>,
Omit<NDropdownMenuTriggerProps, 'una'>,
Pick<NDropdownMenuItemProps, 'shortcut' | 'dropdownMenuItem'> {
/** Label for the menu */
menuLabel?: string
/** Items in the dropdown menu */
items?: NDropdownMenuProps[]
/** Whether the menu is inset */
inset?: boolean
// Subcomponents
/** Props for the dropdown menu root */
_dropdownMenuRoot?: Partial<NDropdownMenuRootProps>
/** Props for the dropdown menu item */
_dropdownMenuItem?: Partial<NDropdownMenuItemProps>
/** Props for the dropdown menu trigger */
_dropdownMenuTrigger?: Partial<NDropdownMenuTriggerProps>
/** Props for the dropdown menu content */
_dropdownMenuContent?: Partial<NDropdownMenuContentProps>
/** Props for the dropdown menu sub-content */
_dropdownMenuSubContent?: Partial<NDropdownMenuSubContentProps>
/** Props for the dropdown menu label */
_dropdownMenuLabel?: Partial<NDropdownMenuLabelProps>
/** Props for the dropdown menu separator */
_dropdownMenuSeparator?: Partial<NDropdownMenuSeparatorProps>
/** Props for the dropdown menu group */
_dropdownMenuGroup?: Partial<NDropdownMenuGroupProps>
/** Props for the dropdown menu sub-trigger */
_dropdownMenuSubTrigger?: Partial<NDropdownMenuSubTriggerProps>
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps & NButtonProps['una']
}
/**
* Props for the NDropdownMenuRoot component.
*/
export interface NDropdownMenuRootProps extends BaseExtensions, DropdownMenuRootProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuRoot']
}
/**
* Props for the NDropdownMenuTrigger component.
*/
export interface NDropdownMenuTriggerProps extends NButtonProps, DropdownMenuTriggerProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuTrigger'] & NButtonProps['una']
}
/**
* Props for the NDropdownMenuContent component.
*/
export interface NDropdownMenuContentProps extends BaseExtensions, DropdownMenuContentProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuContent']
}
/**
* Props for the NDropdownMenuLabel component.
*/
export interface NDropdownMenuLabelProps extends BaseExtensions, DropdownMenuLabelProps {
/** Whether the label is inset */
inset?: boolean
/** Size of the label */
size?: HTMLAttributes['class']
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuLabel']
}
/**
* Props for the NDropdownMenuSeparator component.
*/
export interface NDropdownMenuSeparatorProps extends DropdownMenuSeparatorProps, NSeparatorProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuSeparator'] & NSeparatorProps['una']
}
/**
* Props for the NDropdownMenuGroup component.
*/
export interface NDropdownMenuGroupProps extends BaseExtensions, DropdownMenuGroupProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuGroup']
}
/**
* Props for the NDropdownMenuSubContent component.
*/
export interface NDropdownMenuSubContentProps extends BaseExtensions, DropdownMenuSubContentProps {
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuSubContent']
}
/**
* Props for the NDropdownMenuItem component.
*/
export interface NDropdownMenuItemProps extends NButtonProps {
/** Dropdown menu item */
dropdownMenuItem?: HTMLAttributes['class']
/** Whether the item is inset */
inset?: boolean
/** Shortcut key for the item */
shortcut?: string
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuItem'] & NButtonProps['una']
}
/**
* Props for the NDropdownMenuSubTrigger component.
*/
export interface NDropdownMenuSubTriggerProps extends NButtonProps, DropdownMenuSubTriggerProps {
/** Dropdown menu item */
dropdownMenuItem?: HTMLAttributes['class']
/** Whether the sub-trigger is inset */
inset?: boolean
}
/**
* Props for the NDropdownMenuShortcut component.
*/
export interface NDropdownMenuShortcutProps extends BaseExtensions {
/** Shortcut key for the item */
value?: string
/** Additional properties for the una component */
una?: NDropdownMenuUnaProps['dropdownMenuShortcut']
}
/**
* Props for the NDropdownMenuUna component.
*/
interface NDropdownMenuUnaProps {
/** CSS class for the dropdown menu content */
dropdownMenuContent?: HTMLAttributes['class']
/** CSS class for the dropdown menu sub-content */
dropdownMenuSubContent?: HTMLAttributes['class']
/** CSS class for the dropdown menu sub-trigger */
dropdownMenuSubTrigger?: HTMLAttributes['class']
/** CSS class for the dropdown menu trigger */
dropdownMenuTrigger?: HTMLAttributes['class']
/** CSS class for the dropdown menu label */
dropdownMenuLabel?: HTMLAttributes['class']
/** CSS class for the dropdown menu separator */
dropdownMenuSeparator?: HTMLAttributes['class']
/** CSS class for the dropdown menu group */
dropdownMenuGroup?: HTMLAttributes['class']
/** CSS class for the dropdown menu item */
dropdownMenuItem?: HTMLAttributes['class']
/** CSS class for the dropdown menu root */
dropdownMenuRoot?: HTMLAttributes['class']
/** CSS class for the dropdown menu shortcut */
dropdownMenuShortcut?: HTMLAttributes['class']
}
Components
DropdownMenu.vue
DropdownMenuTrigger.vue
DropdownMenuItem.vue
DropdownMenuGroup.vue
DropdownMenuLabel.vue
DropdownMenuSeparator.vue
DropdownMenuContent.vue
DropdownMenuSub.vue
DropdownMenuSubTrigger
DropdownMenuSubContent
<script setup lang="ts">
import type { DropdownMenuContentEmits, DropdownMenuRootEmits } from 'radix-vue'
import type { NDropdownMenuProps } from '../../../types'
import { createReusableTemplate, reactivePick } from '@vueuse/core'
import { DropdownMenuPortal, useForwardPropsEmits } from 'radix-vue'
import { omitProps } from '../../../utils'
import DropdownMenuContent from './DropdownMenuContent.vue'
import DropdownMenuGroup from './DropdownMenuGroup.vue'
import DropdownMenuItem from './DropdownMenuItem.vue'
import DropdownMenuLabel from './DropdownMenuLabel.vue'
import DropdownMenuRoot from './DropdownMenuRoot.vue'
import DropdownMenuSeparator from './DropdownMenuSeparator.vue'
import DropdownMenuSub from './DropdownMenuSub.vue'
import DropdownMenuSubContent from './DropdownMenuSubContent.vue'
import DropdownMenuSubTrigger from './DropdownMenuSubTrigger.vue'
import DropdownMenuTrigger from './DropdownMenuTrigger.vue'
const props = defineProps<NDropdownMenuProps>()
const emits = defineEmits<DropdownMenuRootEmits & DropdownMenuContentEmits>()
const forwarded = useForwardPropsEmits(props, emits)
const [DefineMenuSub, ReuseMenuSub] = createReusableTemplate<NDropdownMenuProps>()
</script>
<template>
<DropdownMenuRoot
v-bind="reactivePick(forwarded, ['defaultOpen', 'open', 'modal', 'dir'])"
>
<DropdownMenuTrigger
v-bind="omitProps({ ...forwarded, ...forwarded._dropdownMenuTrigger }, [
'dropdownMenuItem',
'items',
'menuLabel',
'_dropdownMenuItem',
'_dropdownMenuContent',
'_dropdownMenuLabel',
'_dropdownMenuSeparator',
'_dropdownMenuGroup',
'_dropdownMenuTrigger',
'_dropdownMenuSubTrigger',
'_dropdownMenuSubContent',
])"
>
<slot />
</DropdownMenuTrigger>
<DropdownMenuContent
v-bind="forwarded._dropdownMenuContent"
>
<slot name="content">
<template
v-if="menuLabel || $slots['menu-label']"
>
<DropdownMenuLabel
:size
:inset
:una="forwarded.una?.dropdownMenuLabel"
v-bind="forwarded._dropdownMenuLabel"
>
<slot name="menu-label">
{{ menuLabel }}
</slot>
</DropdownMenuLabel>
<DropdownMenuSeparator
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="forwarded._dropdownMenuSeparator"
/>
</template>
<slot name="items" :items>
<DropdownMenuGroup
:una="forwarded.una?.dropdownMenuGroup"
v-bind="forwarded._dropdownMenuGroup"
>
<template
v-for="item in items"
:key="item.label"
>
<slot
v-if="!item.items && item.label"
:name="`item-${item.label}`"
>
<DropdownMenuItem
:size
:inset
:dropdown-menu-item
:una="forwarded.una?.dropdownMenuItem"
v-bind="{ ...item, ...forwarded._dropdownMenuItem, ...item._dropdownMenuItem }"
/>
</slot>
<DropdownMenuSeparator
v-else-if="!item.label && !item.items"
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="{ ...forwarded._dropdownMenuSeparator, ...item._dropdownMenuSeparator }"
/>
<ReuseMenuSub
v-else
v-bind="item"
/>
</template>
</DropdownMenuGroup>
</slot>
</slot>
</DropdownMenuContent>
</DropdownMenuRoot>
<DefineMenuSub
v-slot="subProps"
as="div"
>
<template
v-if="subProps.menuLabel"
>
<DropdownMenuLabel
:size
:inset
:una="forwarded.una?.dropdownMenuLabel"
v-bind="{ ...forwarded._dropdownMenuLabel, ...subProps._dropdownMenuLabel }"
>
{{ subProps.menuLabel }}
</DropdownMenuLabel>
<DropdownMenuSeparator
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="{ ...forwarded._dropdownMenuSeparator, ...subProps._dropdownMenuSeparator }"
/>
</template>
<DropdownMenuGroup
:una="forwarded.una?.dropdownMenuGroup"
v-bind="{ ...forwarded._dropdownMenuGroup, ...subProps._dropdownMenuGroup }"
>
<DropdownMenuSub>
<DropdownMenuSubTrigger
:size
:inset
:una="forwarded.una?.dropdownMenuSubTrigger"
:dropdown-menu-item
v-bind="omitProps({
...subProps,
...forwarded._dropdownMenuSubTrigger,
...subProps._dropdownMenuSubTrigger,
}, ['$slots'])"
>
<slot name="sub-trigger" :label="subProps.label" />
</DropdownMenuSubTrigger>
<DropdownMenuPortal>
<DropdownMenuSubContent
v-bind="subProps._dropdownMenuSubContent"
:una="forwarded.una?.dropdownMenuSubContent"
>
<template
v-for="subItem in subProps.items"
:key="subItem.label"
>
<DropdownMenuItem
v-if="!subItem.items && subItem.label"
:size
:inset
:dropdown-menu-item
:una="forwarded.una?.dropdownMenuItem"
v-bind="{ ...subItem, ...forwarded._dropdownMenuItem, ...subItem._dropdownMenuItem }"
>
{{ subItem.label }}
</DropdownMenuItem>
<DropdownMenuSeparator
v-else-if="!subItem.label && !subItem.items"
:una="forwarded.una?.dropdownMenuSeparator"
v-bind="{ ...forwarded._dropdownMenuSeparator, ...subItem._dropdownMenuSeparator }"
/>
<ReuseMenuSub
v-else
v-bind="subItem"
/>
</template>
</DropdownMenuSubContent>
</DropdownMenuPortal>
</DropdownMenuSub>
</DropdownMenuGroup>
</DefineMenuSub>
</template>
<script setup lang="ts">
import type { NDropdownMenuTriggerProps } from '../../../types'
import { DropdownMenuTrigger, useForwardProps } from 'radix-vue'
import { cn, randomId } from '../../../utils'
import Button from '../Button.vue'
const props = defineProps<NDropdownMenuTriggerProps>()
const forwardedProps = useForwardProps(props)
</script>
<template>
<DropdownMenuTrigger
as-child
>
<slot>
<Button
v-bind="forwardedProps"
:id="randomId('dropdown-menu-trigger')"
:class="cn(
'dropdown-menu-trigger',
props.class,
)"
:una="{
btnDefaultVariant: 'dropdown-menu-default-variant',
btnLeading: cn('dropdown-menu-trigger-leading', forwardedProps.una?.btnLeading),
btnTrailing: cn('dropdown-menu-trigger-trailing', forwardedProps.una?.btnTrailing),
...props.una,
}"
/>
</slot>
</DropdownMenuTrigger>
</template>
<script setup lang="ts">
import type { NDropdownMenuItemProps } from '../../../types'
import { DropdownMenuItem, useForwardProps } from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
import Button from '../Button.vue'
import DropdownMenuShortcut from './DropdownMenuShortcut.vue'
const props = withDefaults(defineProps<NDropdownMenuItemProps>(), {
dropdownMenuItem: '~',
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<div>
<DropdownMenuItem
as-child
>
<Button
v-bind="forwardedProps"
:dropdown-menu-item
:class="cn(
'dropdown-menu-item-base w-full justify-start font-normal rounded-sm px-2',
forwardedProps.inset && !(forwardedProps.leading || $slots.leading) && 'pl-8',
props.class,
)"
btn="~"
:una="{
btnLeading: cn('dropdown-menu-item-leading', forwardedProps.una?.btnLeading),
btnTrailing: cn('dropdown-menu-item-trailing', forwardedProps.una?.btnTrailing),
...forwardedProps.una,
}"
>
<template v-for="(_, name) in $slots" #[name]="slotData">
<slot :name="name" v-bind="slotData" />
</template>
<template
v-if="forwardedProps.shortcut"
#trailing
>
<DropdownMenuShortcut>
{{ forwardedProps.shortcut }}
</DropdownMenuShortcut>
</template>
</Button>
</DropdownMenuItem>
</div>
</template>
<script setup lang="ts">
import type { NDropdownMenuGroupProps } from '../../../types'
import { DropdownMenuGroup } from 'radix-vue'
import { cn } from '../../../utils'
const props = defineProps<NDropdownMenuGroupProps>()
</script>
<template>
<DropdownMenuGroup
v-bind="props"
:class="cn(
'dropdown-menu-group',
props.una?.dropdownMenuGroup,
props.class,
)"
>
<slot />
</DropdownMenuGroup>
</template>
<script setup lang="ts">
import type { NDropdownMenuLabelProps } from '../../../types'
import { DropdownMenuLabel, useForwardProps } from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
const props = withDefaults(defineProps<NDropdownMenuLabelProps>(), {
size: 'sm',
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<DropdownMenuLabel
v-bind="forwardedProps"
:class="cn(
'dropdown-menu-label',
forwardedProps.inset && 'pl-8',
props.class,
props.una?.dropdownMenuLabel,
)"
>
<slot />
</DropdownMenuLabel>
</template>
<script setup lang="ts">
import type { NDropdownMenuSeparatorProps } from '../../../types'
import {
DropdownMenuSeparator,
} from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
import Separator from '../Separator.vue'
const props = defineProps<NDropdownMenuSeparatorProps>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
</script>
<template>
<DropdownMenuSeparator
as-child
>
<div class="dropdown-menu-separator-root">
<Separator
v-bind="delegatedProps"
:class="cn(
'dropdown-menu-separator my-1',
props.class,
props.una?.dropdownMenuSeparator,
)"
/>
</div>
</DropdownMenuSeparator>
</template>
<script setup lang="ts">
import type { NDropdownMenuContentProps } from '../../../types'
import {
DropdownMenuContent,
type DropdownMenuContentEmits,
DropdownMenuPortal,
useForwardPropsEmits,
} from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
const props = withDefaults(
defineProps<NDropdownMenuContentProps>(),
{
sideOffset: 4,
},
)
const emits = defineEmits<DropdownMenuContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<DropdownMenuPortal>
<DropdownMenuContent
v-bind="forwarded"
:class="cn(
'dropdown-menu-content data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
props.una?.dropdownMenuContent,
)"
>
<slot />
</DropdownMenuContent>
</DropdownMenuPortal>
</template>
<script setup lang="ts">
import type {
DropdownMenuSubEmits,
DropdownMenuSubProps,
} from 'radix-vue'
import {
DropdownMenuSub,
useForwardPropsEmits,
} from 'radix-vue'
const props = defineProps<DropdownMenuSubProps>()
const emits = defineEmits<DropdownMenuSubEmits>()
const forwarded = useForwardPropsEmits(props, emits)
</script>
<template>
<DropdownMenuSub
v-bind="forwarded"
>
<slot />
</DropdownMenuSub>
</template>
<script setup lang="ts">
import type { NDropdownMenuSubTriggerProps } from '../../../types'
import {
DropdownMenuSubTrigger,
useForwardProps,
} from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
import Button from '../Button.vue'
const props = withDefaults(defineProps<NDropdownMenuSubTriggerProps>(), {
dropdownMenuItem: '~',
})
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwardedProps = useForwardProps(delegatedProps)
</script>
<template>
<DropdownMenuSubTrigger
as-child
>
<slot>
<Button
v-bind="forwardedProps"
:dropdown-menu-item
:class="cn(
'dropdown-menu-sub-trigger w-full justify-start font-normal rounded-sm px-2',
forwardedProps.inset && !(forwardedProps.leading || $slots.leading) && 'pl-8',
props.class,
)"
btn="~"
:una="{
btnLeading: cn('dropdown-menu-sub-trigger-leading', forwardedProps.una?.btnLeading),
btnTrailing: cn('dropdown-menu-sub-trigger-trailing', forwardedProps.una?.btnTrailing),
...forwardedProps.una,
}"
trailing="dropdown-menu-sub-trigger-trailing-icon"
/>
</slot>
</dropdownmenusubtrigger>
</template>
<script setup lang="ts">
import type { NDropdownMenuSubContentProps } from '../../../types'
import {
DropdownMenuSubContent,
type DropdownMenuSubContentEmits,
useForwardPropsEmits,
} from 'radix-vue'
import { computed } from 'vue'
import { cn } from '../../../utils'
const props = defineProps<NDropdownMenuSubContentProps>()
const emits = defineEmits<DropdownMenuSubContentEmits>()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
return delegated
})
const forwarded = useForwardPropsEmits(delegatedProps, emits)
</script>
<template>
<DropdownMenuSubContent
v-bind="forwarded"
:class="cn(
'dropdown-menu-sub-content data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
props.class,
forwarded.una?.dropdownMenuSubContent,
)"
>
<slot />
</DropdownMenuSubContent>
</template>
Dialog
A window overlaid on either the primary window or another dialog window, rendering the content underneath inert.
Form Group
A versatile wrapper for various form components such as `Input`, `Textarea`, `Select`, and more. It offers a comprehensive set of features, including label, description, hint, message, status, and others.