BaseDropdown is the anchored menu shell (open/close, position, click outside, Escape). Use it for action menus or custom panel content. SelectableDropdown builds on it for picking an item from a list (keyboard ↑↓ Enter + sheet on small viewports).
Anchored panel only. No selection keyboard, no native select, no BottomActionSheet. Close with Escape, click outside, resize, or window blur. SSOT: components/BaseDropdown.vue.
<BaseDropdown force-horizontal-align="right">
<template #button="{ isOpen, toggle }">
<button type="button" @click.stop="toggle">More</button>
</template>
<template #default="{ close }">
<button type="button" @click="doAction(); close()">Action</button>
</template>
</BaseDropdown>| Prop | Type | Description |
|---|---|---|
| forceHorizontalAlign | left | center | right | null | Prefer horizontal alignment (falls back if no space) |
| menuClass | String | Extra classes on the floating panel |
| enablePanel | Boolean | When false, open state toggles but no panel (parent can show a sheet) |
| disabled | Boolean | Ignores toggle/open |
| v-model | Boolean | Optional controlled open state |
#button — { isOpen, toggle, close, open }default — panel content; { isOpen, close }Action menu (same idea as the player ⋯ menu). Enter does not auto-select; only Escape / outside / the action button closes.
Built on BaseDropdown. Menu anchored to the button on lg+ (≥1024px). On max-lg, the same options appear in BottomActionSheet (bottom panel, keyboard ↑ ↓ Esc). Optional: sheet-title prop for the sheet title; by default placeholder is used.
<SelectableDropdown
:items="items"
:selected-item="selectedItem"
placeholder="Select an option"
@select="handleSelect"
/>| Prop | Type | Required | Description |
|---|---|---|---|
| items | Array | Yes | Array of items to display in the dropdown |
| selectedItem | Object | String | Number | No | Currently selected item |
| placeholder | String | No | Placeholder text when no item is selected (default: "Select") |
| sheetTitle | String | No | Title for the bottom sheet at ≤1024px; if empty, placeholder is used |
| buttonClass | String | No | Additional CSS classes for the button |
| getItemKey | Function | No | Function to get unique key for each item |
| getItemLabel | Function | No | Function to get display label for each item |
| isItemSelected | Function | No | Function to check if item is selected |
| getDisplayText | Function | No | Function to get display text for button |
| getItemIcon | Function | No | Function to get icon for each item (optional) |
| Event | Description |
|---|---|
| @select | Emitted when an item is selected. Payload: selected item |
On max-lg the list uses BottomActionSheet; arrow keys move focus between button elements, Esc closes (see the BottomActionSheet page).
Automatically opens up or down based on available space in the viewport (via BaseDropdown).
Common use cases for the dropdown component:
Simple dropdown with string items:
Dropdown with object items (like demo selection):
Dropdown with custom getItemLabel and getItemIcon:
<template>
<SelectableDropdown
:items="items"
:selected-item="selectedItem"
placeholder="Select an option"
@select="handleSelect"
/>
</template>
<script>
export default {
data() {
return {
items: ['Option 1', 'Option 2', 'Option 3'],
selectedItem: null
}
},
methods: {
handleSelect(item) {
this.selectedItem = item
}
}
}
</script><template>
<SelectableDropdown
:items="demos"
:selected-item="currentDemo"
placeholder="Demo"
@select="selectDemo"
/>
</template>
<script>
export default {
data() {
return {
demos: [
{ id: 1, name: 'Master - Extended mix' },
{ id: 2, name: 'Mixdown - Radio edit' },
{ id: 3, name: 'Instrumental' }
],
currentDemo: null
}
},
methods: {
selectDemo(demo) {
this.currentDemo = demo
}
}
}
</script><template>
<SelectableDropdown
:items="items"
:selected-item="selectedItem"
:get-item-label="(item) => item.title"
:get-item-icon="(item) => item.icon"
:get-item-key="(item, index) => item.id"
placeholder="Choose"
@select="handleSelect"
/>
</template>
<script>
export default {
data() {
return {
items: [
{ id: 1, title: 'First Option', icon: 'icon-1' },
{ id: 2, title: 'Second Option', icon: 'icon-2' }
],
selectedItem: null
}
},
methods: {
handleSelect(item) {
this.selectedItem = item
}
}
}
</script>