v2.1.8
/
  • Get Started
  • Components
  • Composables
  • Typography
  • GitHub
  • Layout
  • App
  • Container
  • Error
  • SidebarLayout
  • Element
  • Advice
  • Alert
  • Avatar
  • AvatarGroup
  • Badge
  • Banner
  • Button
  • Calendar
  • Card
  • Chip
  • Collapsible
  • Countdown
  • FieldGroup
  • Kbd
  • Progress
  • Separator
  • Skeleton
  • Form
  • Checkbox
  • CheckboxGroup
  • ColorPicker
  • FileUpload
  • Form
  • FormField
  • Input
  • InputDate
  • InputMenu
  • InputNumber
  • InputTags
  • InputTime
  • PinInput
  • RadioGroup
  • Range
  • Select
  • SelectMenu
  • Switch
  • Textarea
  • Data
  • Accordion
  • DescriptionList
  • Empty
  • Table
  • TableWrapper
  • Timeline
  • User
  • Navigation
  • Breadcrumb
  • CommandPalette
  • Link
  • NavigationMenu
  • Pagination
  • Stepper
  • Tabs
  • Overlay
  • ContextMenu
  • DropdownMenu
  • Modal
  • Popover
  • Slideover
  • Toast
  • Tooltip
  • Page
  • PageCard
  • PageColumns
  • PageGrid
  • PageLinks
  • PageList
  • Dashboard
  • DashboardGroup
  • DashboardSearch
  • DashboardSearchButton
  • AI Chat
  • soonChatMessage
  • soonChatMessages
  • soonChatPalette
  • soonChatPrompt
  • soonChatPromptSubmit
  • Content
  • ContentSearch
  • ContentSearchButton
  • ContentSurround
  • ContentToc
  • Color Mode
  • ColorModeAvatar
  • ColorModeButton
  • ColorModeImage
  • ColorModeSelect
  • ColorModeSwitch
  • i18n
  • LocaleSelect
  • b24icons
  • b24jssdk
Use our Nuxt starter
v2.1.8
  • Docs
  • Components
  • Composables
  • Typography

DropdownMenu

A contextual menu for actions triggered by clicking an element.
GitHub
Demo
Nuxt UI
DropdownMenuDropdownMenu

Usage

Use a Button or any other component in the default slot of the DropdownMenu.

{
  "wait": "Loading client-side content..."
}

Items

Use the items prop as an array of objects with the following properties:

  • label?: string
  • icon?: IconComponent
  • avatar?: AvatarProps
  • kbds?: string[] | KbdProps[]
  • type?: "link" | "label" | "separator" | "checkbox"
  • color?: "air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-copilot" | "air-primary-warning"
  • checked?: boolean
  • disabled?: boolean
  • slot?: string
  • onSelect?: (e: Event) => void
  • onUpdateChecked?: (checked: boolean) => void
  • children?: DropdownMenuItem[] | DropdownMenuItem[][]
  • class?: any
  • b24ui?: { item?: ClassNameValue, label?: ClassNameValue, separator?: ClassNameValue, itemLeadingIcon?: ClassNameValue, itemLeadingAvatarSize?: ClassNameValue, itemLeadingAvatar?: ClassNameValue, itemLabel?: ClassNameValue, itemLabelExternalIcon?: ClassNameValue, itemTrailing?: ClassNameValue, itemTrailingIcon?: ClassNameValue, itemTrailingKbds?: ClassNameValue, itemTrailingKbdsSize?: ClassNameValue }

You can pass any property from the Link component such as to, target, etc.

{
  "wait": "Loading client-side content..."
}
You can also pass an array of arrays to the items prop to create separated groups of items.
Each item can take a children array of objects with the same properties as the items prop to create a nested menu which can be controlled using the open, defaultOpen and content properties.

Content

Use the content prop to control how the DropdownMenu content is rendered, like its align or side for example.

{
  "wait": "Loading client-side content..."
}

Arrow

Use the arrow prop to display an arrow on the DropdownMenu.

{
  "wait": "Loading client-side content..."
}

Modal

Use the modal prop to control whether the DropdownMenu blocks interaction with outside content. Defaults to true.

{
  "wait": "Loading client-side content..."
}

Disabled

Use the disabled prop to disable the DropdownMenu.

{
  "wait": "Loading client-side content..."
}

Examples

With checkbox items

You can use the type property with checkbox and use the checked / onUpdateChecked properties to control the checked state of the item.

{
  "wait": "Loading client-side content..."
}
To ensure reactivity for the checked state of items, it's recommended to wrap your items array inside a computed.

With color items

You can use the color property to highlight certain items with a color.

{
  "wait": "Loading client-side content..."
}

Control open state

You can control the open state by using the default-open prop or the v-model:open directive.

{
  "wait": "Loading client-side content..."
}
In this example, leveraging defineShortcuts, you can toggle the DropdownMenu by pressing O.

With custom slot

Use the slot property to customize a specific item.

You will have access to the following slots:

  • #{{ item.slot }}
  • #{{ item.slot }}-leading
  • #{{ item.slot }}-label
  • #{{ item.slot }}-trailing
{
  "wait": "Loading client-side content..."
}
You can also use the #item, #item-leading, #item-label and #item-trailing slots to customize all items.

With trigger content width

You can expand the content to the full width of its button by adding the w-(--reka-dropdown-menu-trigger-width) class on the b24ui.content and b24ui.viewport slot.

{
  "wait": "Loading client-side content..."
}

Extract shortcuts

When you have some items with kbds property (displaying some Kbd), you can easily make them work with the defineShortcuts composable.

Inside the defineShortcuts composable, there is an extractShortcuts utility that will extract the shortcuts recursively from the items and return an object that you can pass to defineShortcuts. It will automatically call the select function of the item when the shortcut is pressed.

<script setup lang="ts">
import type { DropdownMenuItem } from '@bitrix24/b24ui-nuxt'

const items: DropdownMenuItem[] = [{
  label: 'Invite users',
  children: [{
    label: 'Invite by email',
    kbds: ['meta', 'e'],
    onSelect() {
      console.log('Invite by email clicked')
    }
  }, {
    label: 'Invite by link',
    kbds: ['meta', 'i'],
    onSelect() {
      console.log('Invite by link clicked')
    }
  }]
}, {
  label: 'New team',
  kbds: ['meta', 'n'],
  onSelect() {
    console.log('New team clicked')
  }
}]

defineShortcuts(extractShortcuts(items))
</script>
In this example, E, I and N would trigger the select function of the corresponding item.

API

Props

Prop Default Type
items DropdownMenuItem[] | DropdownMenuItem[][]
  • label?: string
  • description?: string
  • icon?: IconComponent
  • color?: "air-primary" | "air-primary-success" | "air-primary-alert" | "air-primary-warning" | "air-primary-copilot"
  • avatar?: AvatarProps
  • content?: (DropdownMenuContentProps & { class?: any; } & Partial<EmitsToProps<MenuContentEmits>>)
  • kbds?: (string )[] | KbdProps[] | undefined
  • type?: "label" | "link" | "separator" | "checkbox"

    The item type. Defaults to 'link'.

  • slot?: string
  • loading?: boolean
  • disabled?: boolean
  • checked?: boolean
  • open?: boolean
  • defaultOpen?: boolean
  • children?: ArrayOrNested<DropdownMenuItem>
  • onSelect?: ((e: Event) => void)
  • onUpdateChecked?: ((checked: boolean) => void)
  • class?: any
  • b24ui?: Pick<{ content?: ClassNameValue; viewport?: ClassNameValue; arrow?: ClassNameValue; group?: ClassNameValue; label?: ClassNameValue; separator?: ClassNameValue; item?: ClassNameValue; itemLeadingIcon?: ClassNameValue; itemLeadingAvatar?: ClassNameValue; itemLeadingAvatarSize?: ClassNameValue; itemTrailing?: ClassNameValue; itemTrailingIcon?: ClassNameValue; itemTrailingKbds?: ClassNameValue; itemTrailingKbdsSize?: ClassNameValue; itemWrapper?: ClassNameValue; itemLabel?: ClassNameValue; itemDescription?: ClassNameValue; itemLabelExternalIcon?: ClassNameValue; }, "label" | "content" | "separator" | "item" | "itemLeadingIcon" | "itemLeadingAvatar" | "itemLeadingAvatarSize" | "itemTrailing" | "itemTrailingIcon" | "itemTrailingKbds" | "itemTrailingKbdsSize" | "itemWrapper" | "itemLabel" | "itemDescription" | "itemLabelExternalIcon">
  • to?: string | RouteLocationAsRelativeGeneric | RouteLocationAsPathGeneric

    Route Location the link should navigate to when clicked on.

  • autofocus?: Booleanish
  • name?: string
  • download?: any
  • hreflang?: string
  • media?: string
  • ping?: string
  • target?: (string & {}) | "_blank" | "_parent" | "_self" | "_top" | null

    Where to display the linked URL, as the name for a browsing context.

  • referrerpolicy?: HTMLAttributeReferrerPolicy
  • as?: any

    The element or component this component should render as when not a link. Defaults to 'button'.

  • active?: boolean

    Force the link to be active independent of the current route.

  • isAction?: boolean

    When true, uses special underlined styling.

  • trailingSlash?: "append" | "remove"

    An option to either add or remove trailing slashes in the href for this specific link. Overrides the global trailingSlash option if provided.

checkedIconicons.checkIconComponent

The icon displayed when an item is checked.

externalIcontrueIconComponent

The icon displayed when the item is an external link. Set to false to hide the external icon.

content{ side: 'bottom', sideOffset: 8, collisionPadding: 8 } DropdownMenuContentProps & Partial<EmitsToProps<MenuContentEmits>>

The content of the menu.

  • side?: "top" | "right" | "bottom" | "left"

    The preferred side of the trigger to render against when open. Will be reversed when collisions occur and avoidCollisions is enabled. Defaults to "top".

  • sideOffset?: number

    The distance in pixels from the trigger. Defaults to 0.

  • sideFlip?: boolean

    Flip to the opposite side when colliding with boundary. Defaults to true.

  • align?: "end" | "start" | "center"

    The preferred alignment against the trigger. May change when collisions occur. Defaults to "center".

  • alignOffset?: number

    An offset in pixels from the start or end alignment options. Defaults to 0.

  • alignFlip?: boolean

    Flip alignment when colliding with boundary. May only occur when prioritizePosition is true. Defaults to true.

  • avoidCollisions?: boolean

    When true, overrides the side and align preferences to prevent collisions with boundary edges. Defaults to true.

  • collisionBoundary?: Element | (Element | null)[] | null

    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. Defaults to [].

  • 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 }. Defaults to 0.

  • arrowPadding?: number

    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. Defaults to 0.

  • sticky?: "partial" | "always"

    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. Defaults to "partial".

  • hideWhenDetached?: boolean

    Whether to hide the content when the trigger becomes fully occluded. Defaults to false.

  • positionStrategy?: "fixed" | "absolute"

    The type of CSS position property to use.

  • updatePositionStrategy?: "always" | "optimized"

    Strategy to update the position of the floating element on every animation frame. Defaults to 'optimized'.

  • disableUpdateOnLayoutShift?: boolean

    Whether to disable the update position for the content when the layout shifted. Defaults to false.

  • prioritizePosition?: boolean

    Force content to be position within the viewport.

    Might overlap the reference element, which may not be desired. Defaults to false.

  • reference?: ReferenceElement

    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.

  • loop?: boolean

    When true, keyboard navigation will loop from last item to first, and vice versa. Defaults to false.

  • onEscapeKeyDown?: ((event: KeyboardEvent) => void)
  • onPointerDownOutside?: ((event: PointerDownOutsideEvent) => void)
  • onFocusOutside?: ((event: FocusOutsideEvent) => void)
  • onInteractOutside?: ((event: PointerDownOutsideEvent | FocusOutsideEvent) => void)
  • onCloseAutoFocus?: ((event: Event) => void)
arrowfalseboolean | DropdownMenuArrowProps

Display an arrow alongside the menu.

portaltrue string | false | true | HTMLElement

Render the menu in a portal.

labelKey'label' string | number

The key used to get the label from the item.

descriptionKey'description' string | number

The key used to get the description from the item.

disabledfalseboolean
defaultOpenboolean

The open state of the dropdown menu when it is initially rendered. Use when you do not need to control its open state.

openboolean

The controlled open state of the menu. Can be used as v-model:open.

modaltrueboolean

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.

b24ui { content?: ClassNameValue; viewport?: ClassNameValue; arrow?: ClassNameValue; group?: ClassNameValue; label?: ClassNameValue; separator?: ClassNameValue; item?: ClassNameValue; itemLeadingIcon?: ClassNameValue; itemLeadingAvatar?: ClassNameValue; itemLeadingAvatarSize?: ClassNameValue; itemTrailing?: ClassNameValue; itemTrailingIcon?: ClassNameValue; itemTrailingKbds?: ClassNameValue; itemTrailingKbdsSize?: ClassNameValue; itemWrapper?: ClassNameValue; itemLabel?: ClassNameValue; itemDescription?: ClassNameValue; itemLabelExternalIcon?: ClassNameValue; }

Slots

Slot Type
default{ open: boolean; }
item{ item: DropdownMenuItem; active?: boolean | undefined; index: number; b24ui: object; }
item-leading{ item: DropdownMenuItem; active?: boolean | undefined; index: number; b24ui: object; }
item-label{ item: DropdownMenuItem; active?: boolean | undefined; index: number; }
item-description{ item: DropdownMenuItem; active?: boolean | undefined; index: number; }
item-trailing{ item: DropdownMenuItem; active?: boolean | undefined; index: number; b24ui: object; }
content-top{}
content-bottom{}

Emits

Event Type
update:open[payload: boolean]

Theme

app.config.ts
export default defineAppConfig({
  b24ui: {
    dropdownMenu: {
      slots: {
        content: 'light bg-(--popup-window-background-color) shadow-(--popup-window-box-shadow) rounded-(--popup-window-border-radius) will-change-[opacity] motion-safe:data-[state=open]:animate-[scale-in_100ms_ease-out] motion-safe:data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-(--reka-dropdown-menu-content-transform-origin) font-[family-name:var(--ui-font-family-primary)] relative isolate px-0 py-(--menu-popup-padding) pointer-events-auto',
        viewport: 'relative w-full max-h-[40vh] min-w-[120px] overflow-x-hidden overflow-y-auto scrollbar-thin',
        arrow: 'fill-(--popup-window-background-color)',
        group: 'grid',
        label: 'w-full h-(--popup-window-delimiter-section-height) px-[18px] mt-(--menu-item-block-stack-space) flex flex-row rtl:flex-row-reverse items-center select-none outline-none whitespace-nowrap text-start text-(length:--popup-window-delimiter-font-size) text-(--popup-window-delimiter-text-color) font-(--popup-window-delimiter-font-weight) after:ms-[10px] after:block after:flex-1 after:min-w-[15px] after:h-px after:bg-(--popup-window-delimiter-bg-color)',
        separator: 'my-[8px] mx-[18px] h-[1px] bg-(--popup-window-delimiter-bg-color)',
        item: 'group w-full h-[36px] px-[18px] mt-(--menu-item-block-stack-space) relative flex flex-row rtl:flex-row-reverse items-center select-none outline-none whitespace-nowrap cursor-pointer data-disabled:cursor-not-allowed data-disabled:opacity-30 text-start text-(length:--menu-popup-item-font-size) text-(--menu-popup-item-color) hover:text-(--menu-popup-item-color-hover) data-highlighted:text-(--menu-popup-item-color-hover) data-[state=open]:text-(--menu-popup-item-color-hover) hover:bg-(--menu-popup-item-bg-color-hover) data-highlighted:bg-(--menu-popup-item-bg-color-hover) data-[state=open]:bg-(--menu-popup-item-bg-color-hover) transition-colors',
        itemLeadingIcon: 'shrink-0 size-[18px] text-(--ui-color-design-plain-content-icon-secondary) group-data-highlighted:text-(--ui-color-accent-main-primary) group-data-[state=open]:text-(--ui-color-accent-main-primary) group-data-[state=checked]:text-(--ui-color-accent-main-primary) transition-colors',
        itemLeadingAvatar: 'shrink-0 size-[16px] me-[8px]',
        itemLeadingAvatarSize: '2xs',
        itemTrailing: 'ml-auto rtl:ml-0 rtl:mr-auto inline-flex gap-1.5 items-center',
        itemTrailingIcon: 'shrink-0 size-[24px] text-(--ui-color-accent-main-primary)',
        itemTrailingKbds: 'shrink-0 hidden lg:inline-flex items-center gap-0.5',
        itemTrailingKbdsSize: 'md',
        itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
        itemLabel: 'max-w-[240px] truncate -mt-px group-data-[state=checked]:text-(--ui-color-accent-main-primary)',
        itemDescription: 'max-w-[240px] truncate -mt-[6px] text-(--b24ui-typography-description-color) text-(length:--ui-font-size-sm)',
        itemLabelExternalIcon: 'inline-block size-[16px] text-(--ui-color-design-plain-content-icon-secondary)'
      },
      variants: {
        color: {
          'air-primary': {
            item: 'style-filled'
          },
          'air-primary-success': {
            item: 'style-filled-success'
          },
          'air-primary-alert': {
            item: 'style-filled-alert'
          },
          'air-primary-copilot': {
            item: 'style-filled-copilot'
          },
          'air-primary-warning': {
            item: 'style-filled-warning'
          },
          default: {
            item: 'style-old-default'
          },
          danger: {
            item: 'style-old-danger'
          },
          success: {
            item: 'style-old-success'
          },
          warning: {
            item: 'style-old-warning'
          },
          primary: {
            item: 'style-old-primary'
          },
          secondary: {
            item: 'style-old-secondary'
          },
          collab: {
            item: 'style-old-collab'
          },
          ai: {
            item: 'style-old-ai'
          }
        },
        active: {
          true: {
            item: 'text-(--ui-color-accent-main-primary) hover:text-(--ui-color-accent-main-primary)',
            itemLeadingIcon: 'text-(--ui-color-accent-main-primary) hover:text-(--ui-color-accent-main-primary) group-data-[state=open]:text-(--ui-color-accent-main-primary)'
          },
          false: {}
        },
        loading: {
          true: {
            itemLeadingIcon: 'animate-spin'
          }
        }
      },
      compoundVariants: [],
      defaultVariants: {}
    }
  }
})
vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import bitrix24UIPluginVite from '@bitrix24/b24ui-nuxt/vite'

export default defineConfig({
  plugins: [
    vue(),
    bitrix24UIPluginVite({
      b24ui: {
        dropdownMenu: {
          slots: {
            content: 'light bg-(--popup-window-background-color) shadow-(--popup-window-box-shadow) rounded-(--popup-window-border-radius) will-change-[opacity] motion-safe:data-[state=open]:animate-[scale-in_100ms_ease-out] motion-safe:data-[state=closed]:animate-[scale-out_100ms_ease-in] origin-(--reka-dropdown-menu-content-transform-origin) font-[family-name:var(--ui-font-family-primary)] relative isolate px-0 py-(--menu-popup-padding) pointer-events-auto',
            viewport: 'relative w-full max-h-[40vh] min-w-[120px] overflow-x-hidden overflow-y-auto scrollbar-thin',
            arrow: 'fill-(--popup-window-background-color)',
            group: 'grid',
            label: 'w-full h-(--popup-window-delimiter-section-height) px-[18px] mt-(--menu-item-block-stack-space) flex flex-row rtl:flex-row-reverse items-center select-none outline-none whitespace-nowrap text-start text-(length:--popup-window-delimiter-font-size) text-(--popup-window-delimiter-text-color) font-(--popup-window-delimiter-font-weight) after:ms-[10px] after:block after:flex-1 after:min-w-[15px] after:h-px after:bg-(--popup-window-delimiter-bg-color)',
            separator: 'my-[8px] mx-[18px] h-[1px] bg-(--popup-window-delimiter-bg-color)',
            item: 'group w-full h-[36px] px-[18px] mt-(--menu-item-block-stack-space) relative flex flex-row rtl:flex-row-reverse items-center select-none outline-none whitespace-nowrap cursor-pointer data-disabled:cursor-not-allowed data-disabled:opacity-30 text-start text-(length:--menu-popup-item-font-size) text-(--menu-popup-item-color) hover:text-(--menu-popup-item-color-hover) data-highlighted:text-(--menu-popup-item-color-hover) data-[state=open]:text-(--menu-popup-item-color-hover) hover:bg-(--menu-popup-item-bg-color-hover) data-highlighted:bg-(--menu-popup-item-bg-color-hover) data-[state=open]:bg-(--menu-popup-item-bg-color-hover) transition-colors',
            itemLeadingIcon: 'shrink-0 size-[18px] text-(--ui-color-design-plain-content-icon-secondary) group-data-highlighted:text-(--ui-color-accent-main-primary) group-data-[state=open]:text-(--ui-color-accent-main-primary) group-data-[state=checked]:text-(--ui-color-accent-main-primary) transition-colors',
            itemLeadingAvatar: 'shrink-0 size-[16px] me-[8px]',
            itemLeadingAvatarSize: '2xs',
            itemTrailing: 'ml-auto rtl:ml-0 rtl:mr-auto inline-flex gap-1.5 items-center',
            itemTrailingIcon: 'shrink-0 size-[24px] text-(--ui-color-accent-main-primary)',
            itemTrailingKbds: 'shrink-0 hidden lg:inline-flex items-center gap-0.5',
            itemTrailingKbdsSize: 'md',
            itemWrapper: 'flex-1 flex flex-col text-start min-w-0',
            itemLabel: 'max-w-[240px] truncate -mt-px group-data-[state=checked]:text-(--ui-color-accent-main-primary)',
            itemDescription: 'max-w-[240px] truncate -mt-[6px] text-(--b24ui-typography-description-color) text-(length:--ui-font-size-sm)',
            itemLabelExternalIcon: 'inline-block size-[16px] text-(--ui-color-design-plain-content-icon-secondary)'
          },
          variants: {
            color: {
              'air-primary': {
                item: 'style-filled'
              },
              'air-primary-success': {
                item: 'style-filled-success'
              },
              'air-primary-alert': {
                item: 'style-filled-alert'
              },
              'air-primary-copilot': {
                item: 'style-filled-copilot'
              },
              'air-primary-warning': {
                item: 'style-filled-warning'
              },
              default: {
                item: 'style-old-default'
              },
              danger: {
                item: 'style-old-danger'
              },
              success: {
                item: 'style-old-success'
              },
              warning: {
                item: 'style-old-warning'
              },
              primary: {
                item: 'style-old-primary'
              },
              secondary: {
                item: 'style-old-secondary'
              },
              collab: {
                item: 'style-old-collab'
              },
              ai: {
                item: 'style-old-ai'
              }
            },
            active: {
              true: {
                item: 'text-(--ui-color-accent-main-primary) hover:text-(--ui-color-accent-main-primary)',
                itemLeadingIcon: 'text-(--ui-color-accent-main-primary) hover:text-(--ui-color-accent-main-primary) group-data-[state=open]:text-(--ui-color-accent-main-primary)'
              },
              false: {}
            },
            loading: {
              true: {
                itemLeadingIcon: 'animate-spin'
              }
            }
          },
          compoundVariants: [],
          defaultVariants: {}
        }
      }
    })
  ]
})
Some colors in compoundVariants are omitted for readability. Check out the source code on GitHub.

ContextMenu

A pop-up menu that appears upon right-clicking an element to present relevant actions.

Modal

A popup window for showing messages or gathering user input.

On this page

  • Usage
    • Items
    • Content
    • Arrow
    • Modal
    • Disabled
  • Examples
    • With checkbox items
    • With color items
    • Control open state
    • With custom slot
    • With trigger content width
    • Extract shortcuts
  • API
    • Props
    • Slots
    • Emits
  • Theme
Releases
Published under MIT License.

Copyright © 2024-present Bitrix24