Skip to content

DropdownMenu ​

A contextual menu for actions triggered by clicking an element.

Usage ​

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

Items ​

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

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

Details
vue
<script setup lang="ts">
import { ref } from 'vue'
import PersonIcon from '@bitrix24/b24icons-vue/main/PersonIcon'
import CreditDebitCardIcon from '@bitrix24/b24icons-vue/main/CreditDebitCardIcon'
import Settings2Icon from '@bitrix24/b24icons-vue/actions/Settings2Icon'
import Persons3Icon from '@bitrix24/b24icons-vue/main/Persons3Icon'
import PersonPlus2Icon from '@bitrix24/b24icons-vue/crm/PersonPlus2Icon'
import SendIcon from '@bitrix24/b24icons-vue/main/SendIcon'
import Link3Icon from '@bitrix24/b24icons-vue/main/Link3Icon'
import CirclePlusIcon from '@bitrix24/b24icons-vue/main/CirclePlusIcon'
import SlackIcon from '@bitrix24/b24icons-vue/social/SlackIcon'
import PlusInCircleIcon from '@bitrix24/b24icons-vue/actions/PlusInCircleIcon'
import PulseCircleIcon from '@bitrix24/b24icons-vue/main/PulseCircleIcon'
import ItemIcon from '@bitrix24/b24icons-vue/crm/ItemIcon'
import CrossCircle50Icon from '@bitrix24/b24icons-vue/actions/CrossCircle50Icon'
import CircleCheckThinIcon from '@bitrix24/b24icons-vue/main/CircleCheckThinIcon'
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import Bitrix24Icon from '@bitrix24/b24icons-vue/common-service/Bitrix24Icon'
import LoaderWaitIcon from '@bitrix24/b24icons-vue/animated/LoaderWaitIcon'

const toast = useToast()
const loading = ref(false)

const items = ref([
  [
    {
      label: 'My account',
      avatar: {
        src: '/b24ui/avatar/employee.png'
      },
      type: 'label' as const
    }
  ],
  [
    {
      label: 'Profile',
      icon: PersonIcon,
      slot: 'custom' as const,
      onSelect(e: Event) {
        e.preventDefault()
        toast.add({ title: 'Action', description: 'Profile clicked' })
      }
    },
    {
      label: 'Billing',
      icon: CreditDebitCardIcon,
      kbds: ['meta', 'b'],
      onSelect() {
        toast.add({ title: 'Action', description: 'Billing clicked' })
      }
    },
    {
      label: 'Settings',
      icon: Settings2Icon,
      kbds: ['?'],
      onSelect() {
        toast.add({ title: 'Action', description: 'Settings clicked' })
      }
    }
  ],
  [
    {
      label: 'Team',
      icon: Persons3Icon
    },
    {
      label: 'Invite users',
      icon: PersonPlus2Icon,
      children: [
        [
          {
            label: 'Invite by email',
            icon: SendIcon
          },
          {
            label: 'Invite by link',
            icon: Link3Icon,
            kbds: ['meta', 'i'],
            onSelect(e: Event) {
              e?.preventDefault()
              toast.add({ title: 'Action', description: 'Invite by link clicked' })
            }
          }
        ],
        [
          {
            label: 'More',
            icon: PlusInCircleIcon,
            children: [
              {
                label: 'Import from Bitrix24',
                icon: Bitrix24Icon,
                to: 'https://bitrix24.com',
                target: '_blank',
                onSelect(e: Event) {
                  e.preventDefault()
                  toast.add({ title: 'Action', description: 'Import from Bitrix24 clicked' })
                }
              },
              {
                label: 'Import from Slack',
                icon: SlackIcon,
                to: 'https://slack.com',
                target: '_blank',
                onSelect(e: Event) {
                  e.preventDefault()
                  toast.add({ title: 'Action', description: 'Import from Slack clicked' })
                }
              }
            ]
          }
        ]
      ]
    },
    {
      label: 'New team',
      icon: CirclePlusIcon,
      kbds: ['meta', 'n'],
      loading: loading.value,
      onSelect(e: Event) {
        e?.preventDefault()

        if (loading.value) {
          return
        }

        loading.value = true
        const toastItem = toast.add({
          icon: LoaderWaitIcon,
          title: 'Action loading',
          description: 'New team loading',
          color: 'warning'
        })
        setTimeout(() => {
          loading.value = false
          toast.update(toastItem.id, {
            icon: CircleCheckThinIcon,
            title: 'Action stop',
            description: 'New team loading stops',
            color: 'success'
          })
        }, 2000)
      }
    }
  ],
  [
    {
      label: 'GitHub',
      to: 'https://github.com/bitrix24/b24ui',
      target: '_blank',
      onSelect(e: Event) {
        e.preventDefault()
      }
    },
    {
      label: 'Support',
      icon: PulseCircleIcon,
      to: '/b24ui/components/dropdown-menu.html'
    },
    {
      label: 'API',
      icon: ItemIcon,
      disabled: true
    }
  ],
  [
    {
      label: 'Logout',
      icon: CrossCircle50Icon,
      kbds: ['shift', 'meta', 'q'],
      onSelect() {
        toast.add({ title: 'Action', description: 'Logout clicked' })
      }
    }
  ]
])
</script>

<template>
  <B24DropdownMenu
    :items="items"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />

    <template #custom-trailing>
      <CircleCheckThinIcon class="shrink-0 size-5 text-ai-500" />
    </template>
  </B24DropdownMenu>
</template>

INFO

You can also pass an array of arrays to the items prop to create separated groups of items.

TIP

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.

Details
vue
<script setup lang="ts">
import { computed } from 'vue'
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

export interface ExampleProps {
  contentAlign?: 'start' | 'center' | 'end'
  contentSide?: 'top' | 'right' | 'bottom' | 'left'
  contentSideOffset?: number
}

const props = withDefaults(defineProps<ExampleProps>(), {
  contentAlign: 'start',
  contentSide: 'left',
  contentSideOffset: 8
})

const items = [
  {
    label: 'View',
    icon: OpenedEyeIcon
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon
  }
]

const content = computed(() => {
  return {
    align: props.contentAlign,
    side: props.contentSide,
    sideOffset: props.contentSideOffset
  }
})
</script>

<template>
  <B24DropdownMenu
    :items="items"
    :content="content"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />
  </B24DropdownMenu>
</template>

Arrow ​

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

Details
vue
<script setup lang="ts">
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

const items = [
  {
    label: 'View',
    icon: OpenedEyeIcon
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon
  }
]
</script>

<template>
  <B24DropdownMenu
    arrow
    :items="items"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />
  </B24DropdownMenu>
</template>

Size ​

Use the size prop to control the size of the DropdownMenu.

WARNING

The size prop will not be proxied to the Button, you need to set it yourself.

INFO

When using the same size, the DropdownMenu items will be perfectly aligned with the Button.

Details
vue
<script setup lang="ts">
import type { DropdownMenuProps } from '@bitrix24/b24ui-nuxt'
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

export interface ExampleProps {
  size?: DropdownMenuProps['size']
}

withDefaults(defineProps<ExampleProps>(), {
  size: 'md' as const
})

const items = [
  {
    label: 'View',
    icon: OpenedEyeIcon
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon
  }
]
</script>

<template>
  <B24DropdownMenu
    :size="size"
    :items="items"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />
  </B24DropdownMenu>
</template>

Disabled ​

Use the disabled prop to disable the DropdownMenu.

Details
vue
<script setup lang="ts">
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

export interface ExampleProps {
  isDisabled?: boolean
}

withDefaults(defineProps<ExampleProps>(), {
  isDisabled: true
})

const items = [
  {
    label: 'View',
    icon: OpenedEyeIcon
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon
  }
]
</script>

<template>
  <B24DropdownMenu
    :disabled="isDisabled"
    :items="items"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />
  </B24DropdownMenu>
</template>

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.

INFO

To ensure reactivity for the checked state of items, it's recommended to wrap your items array inside a computed.

Details
vue
<script setup lang="ts">
import { ref, computed } from 'vue'
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

const showCopy = ref(true)
const showEdit = ref(false)

const items = computed(() => [
  {
    label: 'View',
    icon: OpenedEyeIcon,
    type: 'label' as const
  },
  {
    type: 'separator' as const
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon,
    type: 'checkbox' as const,
    checked: showCopy.value,
    onUpdateChecked(checked: boolean) {
      showCopy.value = checked
    },
    onSelect(e: Event) {
      e.preventDefault()
    }
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon,
    type: 'checkbox' as const,
    checked: showEdit.value,
    onUpdateChecked(checked: boolean) {
      showEdit.value = checked
    }
  }
])
</script>

<template>
  <B24DropdownMenu
    :items="items"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />
  </B24DropdownMenu>
</template>

With color items ​

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

Details
vue
<script setup lang="ts">
import TrashBinIcon from '@bitrix24/b24icons-vue/main/TrashBinIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

const items = [
  [
    {
      label: 'View',
      icon: OpenedEyeIcon
    },
    {
      label: 'Copy',
      icon: CopyPlatesIcon
    },
    {
      label: 'Edit',
      icon: PencilDrawIcon
    }
  ],
  [
    {
      label: 'Delete',
      color: 'danger' as const,
      icon: TrashBinIcon
    }
  ]
]
</script>

<template>
  <B24DropdownMenu
    :items="items"
  >
    <B24Button label="Open" color="link" depth="dark" use-dropdown />
  </B24DropdownMenu>
</template>

Control open state ​

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

INFO

In this example, leveraging defineShortcuts, you can toggle the DropdownMenu by pressing O.

Details
vue
<script setup lang="ts">
import { ref } from 'vue'
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'

const open = ref(false)

defineShortcuts({
  o: () => open.value = !open.value
})

const items = [
  {
    label: 'View',
    icon: OpenedEyeIcon
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon
  }
]
</script>

<template>
  <B24DropdownMenu
    v-model:open="open"
    :items="items"
    :b24ui="{ content: 'w-52' }"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />
  </B24DropdownMenu>
</template>

With custom slot ​

Use the slot property to customize a specific item.

You will have access to the following slots:

ts
#{{ item.slot }}
#{{ item.slot }}-leading
#{{ item.slot }}-label
#{{ item.slot }}-trailing

TIP

You can also use the #item, #item-leading, #item-label and #item-trailing slots to customize all items.

Details
vue
<script setup lang="ts">
import MenuIcon from '@bitrix24/b24icons-vue/main/MenuIcon'
import PencilDrawIcon from '@bitrix24/b24icons-vue/actions/PencilDrawIcon'
import CopyPlatesIcon from '@bitrix24/b24icons-vue/actions/CopyPlatesIcon'
import OpenedEyeIcon from '@bitrix24/b24icons-vue/main/OpenedEyeIcon'
import BatteryIcon from '@bitrix24/b24icons-vue/button/BatteryIcon'

const items = [
  {
    label: 'View',
    icon: OpenedEyeIcon,
    slot: 'view'
  },
  {
    label: 'Copy',
    icon: CopyPlatesIcon
  },
  {
    label: 'Edit',
    icon: PencilDrawIcon
  }
]
</script>

<template>
  <B24DropdownMenu
    :items="items"
    :b24ui="{ content: 'w-52' }"
  >
    <B24Button label="Open" color="link" depth="dark" :icon="MenuIcon" />

    <template #view-trailing>
      <BatteryIcon class="shrink-0 size-5 text-ai-500" />
    </template>
  </B24DropdownMenu>
</template>

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.

vue
<script setup lang="ts">
const items = [
  {
    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>

INFO

On macOS in this example, ⌘ E, ⌘ I and ⌘ N would trigger the select function of the corresponding item.

On Windows in this example, ⊞ E, ⊞ I and ⊞ N would trigger the select function of the corresponding item.

API ​

Props ​

Prop Default Type
size"lg" | "md" | "xs" | "sm"
itemsDropdownMenuItem[] | DropdownMenuItem[][]
checkedIconicons.check = `CheckIcon`(props: HTMLAttributes & VNodeProps & {}, ctx: Omit<{ attrs: Data; slots: Readonly<InternalSlots>; emit: (event: string, ...args: any[]) => void; expose: <Exposed extends Record<string, any> = Record<...>>(exposed?: Exposed) => void; }, "expose">): any
The icon displayed when an item is checked.
externalIcontrueboolean | IconComponent
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 }Omit<DropdownMenuContentProps, "as" | "asChild" | "forceMount">
The content of the menu.
arrowfalseboolean | Omit<DropdownMenuArrowProps, "as" | "asChild">
Display an arrow alongside the menu.
portaltrueboolean
Render the menu in a portal.
labelKey"label"string
The key used to get the label from the item.
disabledboolean
b24uiPartialString<{ content: string; arrow: string; group: string; label: string; separator: string; item: string; itemLeadingIcon: string; itemLeadingAvatar: string; itemLeadingAvatarSize: string; itemTrailing: string; ... 4 more ...; itemLabelExternalIcon: string; }>
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.

Slots ​

Slot Type
default{ open: boolean; }
item{ item: DropdownMenuItem; active?: boolean; index: number; }
item-leading{ item: DropdownMenuItem; active?: boolean; index: number; }
item-label{ item: DropdownMenuItem; active?: boolean; index: number; }
item-trailing{ item: DropdownMenuItem; active?: boolean; index: number; }

Emits ​

Event Type

Released under the MIT License.