Skip to content

DropdownMenu

Menu component for use inside Dropdown. Displays a list of clickable items with keyboard navigation support.

Import

typescript
import { Dropdown, DropdownMenu } from 'vuiii'

Basic Usage

vue
<script setup>
const menuItems = [
  { label: 'Edit', icon: 'pencil' },
  { label: 'Duplicate', icon: 'document-duplicate' },
  { label: 'Delete', icon: 'trash' },
]
</script>

<template>
  <Dropdown label="Actions">
    <DropdownMenu :items="menuItems" @itemClick="handleClick" />
  </Dropdown>
</template>

Custom Item Rendering

Use the item or itemLabel slot to customize how items are displayed:

vue
<DropdownMenu :items="users">
  <template #item="{ item, index }">
    <div class="user-item">
      <Avatar :src="item.avatar" />
      <span>{{ item.name }}</span>
    </div>
  </template>
</DropdownMenu>

Props

PropTypeDescription
itemsItem[]Array of menu items
cursorIndexnumberIndex of the currently highlighted item (for keyboard navigation)
itemDisabled(item, index) => booleanMarks an item unavailable: renders it disabled and emits no click
itemGroupLabel(item, index) => string | undefinedGroups consecutive items under a heading
listRole'listbox' | 'menu'ARIA role for the list element
listIdstringId of the list element, referenced by a combobox via aria-controls
optionIdPrefixstringPrefix for per-option ids, used for aria-activedescendant

Slots

SlotDescription
itemReplaces the whole item, button included. Props: { item, index, cursorIndex }
itemLabelReplaces only the item's label, keeping the button. Props: { item, index, cursorIndex }
groupLabelReplaces a group heading. Props: { label }

Events

EventPayloadDescription
item-click{ item, index }Emitted when an item is clicked
item-mouseenter{ item, index }Emitted when mouse enters an item
item-mouseleave{ item, index }Emitted when mouse leaves an item

Disabled Items

itemDisabled receives each item and returns whether it is unavailable. Disabled items render as natively disabled buttons, are marked aria-disabled, and emit no item-click.

vue
<DropdownMenu :items="actions" :item-disabled="(action) => !action.allowed" @item-click="run" />

Grouping

itemGroupLabel renders a heading wherever the label changes. Items are expected to arrive already ordered by group — a group split across the list renders its heading twice.

vue
<DropdownMenu :items="options" :item-group-label="(option) => option.category">
  <template #groupLabel="{ label }">
    <Icon name="folder" /> {{ label }}
  </template>
</DropdownMenu>

Indices stay contiguous across the headings, so cursorIndex, optionIdPrefix and the emitted payloads keep pointing at the item's position in items, not at its rendered row.

Released under the MIT License.