Dropdown
A highly customizable and extensible dropdown component that supports flat lists, nested tree navigation, search, selection states, and rich option rendering.
Basic Usage
<template>
<HLDropdown id="basic-dropdown" trigger="click" placement="bottom" :options="simpleOptions" :show-search="false" width="200">
<HLButton size="sm">Basic Dropdown</HLButton>
</HLDropdown>
</template>
<script setup lang="ts">
import { HLDropdown, HLButton } from '@platform-ui/highrise'
const simpleOptions = [
{
key: 'option1',
label: 'option1',
},
{
key: 'option2',
label: 'option2',
},
{
key: 'option3',
label: 'option3',
},
]
</script>
Basic Dropdown with Search
<HLDropdown id="basic-dropdown" trigger="click" placement="bottom" :options="simpleOptions" width="200">
<HLButton size="sm">Basic Dropdown with Search</HLButton>
</HLDropdown>
Close on Select
<HLDropdown
Dropdown Tree
The same options can be displayed in a nested tree structure, which enables hierarchical navigation:
<template>
<HLDropdown id="tree-dropdown" trigger="click" placement="bottom" :options="options" tree-mode show-search :width="280">
<HLButton size="sm">Dropdown Tree</HLButton>
</HLDropdown>
</template>
const options = [
{
key: 'locations',
label: 'Locations',
children: [
{
key: 'usa',
label: 'USA',
type: 'avatar',
src: 'path/to/usa-flag.png',
infoText: '+1',
},
{
key: 'india',
label: 'India',
type: 'avatar',
src: 'path/to/india-flag.png',
infoText: '+91',
},
],
},
]
Custom Search
The dropdown component allows you to implement custom search functionality by handling the search
event. This is useful when you need to perform server-side filtering or apply complex search logic.
<template>
<HLDropdown
id="custom-search-dropdown"
trigger="click"
placement="bottom"
:options="filteredOptions"
show-search
:width="280"
@search="handleSearch"
>
<HLButton size="sm">Custom Search</HLButton>
</HLDropdown>
</template>
const searchQuery = ref('')
const isLoading = ref(false)
const allOptions = [
{
key: 'fruits',
label: 'Fruits',
children: [
{ key: 'apple', label: 'Apple', description: 'Red and sweet' },
{ key: 'banana', label: 'Banana', description: 'Yellow and creamy' },
{ key: 'orange', label: 'Orange', description: 'Citrus fruit' },
],
},
{
key: 'vegetables',
label: 'Vegetables',
children: [
{ key: 'carrot', label: 'Carrot', description: 'Orange and crunchy' },
{ key: 'broccoli', label: 'Broccoli', description: 'Green and healthy' },
{ key: 'potato', label: 'Potato', description: 'Starchy vegetable' },
],
},
]
const filteredOptions = computed(() => {
if (!searchQuery.value) return allOptions
const searchLower = searchQuery.value.toLowerCase()
return allOptions
.map(group => {
const matchingChildren = group.children?.filter(
item => item.label.toLowerCase().includes(searchLower) || item.description?.toLowerCase().includes(searchLower)
)
if (!matchingChildren?.length) return null
return {
...group,
children: matchingChildren,
}
})
.filter(Boolean)
})
let searchTimeout
const handleSearch = value => {
clearTimeout(searchTimeout)
isLoading.value = true
searchTimeout = setTimeout(() => {
searchQuery.value = value
isLoading.value = false
}, 300)
}
Custom Render
The dropdown component supports custom rendering of options using the render
type. This is useful for creating highly customized option displays:
<template>
<HLDropdown id="custom-render-dropdown" trigger="click" placement="bottom" :options="options">
<HLButton size="sm">Custom Render</HLButton>
</HLDropdown>
</template>
const options = [
{
key: 'custom-content',
label: 'Custom Content',
type: 'render',
render: () =>
h(
'div',
{
class: 'flex flex-col gap-2 p-2',
},
[
h('div', { class: 'text-sm font-medium' }, 'Custom Content'),
h('div', { class: 'text-xs text-gray-500' }, 'This is a fully custom rendered option'),
h('div', { class: 'mt-2 flex items-center gap-2' }, [
h('div', { class: 'w-2 h-2 rounded-full bg-green-500' }),
h('span', { class: 'text-xs' }, 'Active'),
]),
]
),
},
]
Multiple Selection
The dropdown component supports multiple selection mode, which allows users to select multiple options. You can also control whether the dropdown should close after selection.
<template>
<HLDropdown
id="multiple-dropdown"
trigger="click"
placement="bottom"
:options="options"
multiple
:closeOnSelect="false"
show-selected-mark
:width="280"
>
<HLButton size="sm">Multiple Selection</HLButton>
</HLDropdown>
</template>
<script setup>
const options = [
{
key: 'fruits',
label: 'Fruits',
children: [
{ key: 'apple', label: 'Apple' },
{ key: 'banana', label: 'Banana' },
{ key: 'orange', label: 'Orange' },
],
},
{
key: 'vegetables',
label: 'Vegetables',
children: [
{ key: 'carrot', label: 'Carrot' },
{ key: 'broccoli', label: 'Broccoli' },
{ key: 'potato', label: 'Potato' },
],
},
]
</script>
Props
Dropdown Props
Prop | Type | Default | Description |
---|---|---|---|
id * | string | undefined | undefined | Unique identifier for the dropdown. Required for accessibility and testing. |
options | DropdownOption[] | [] | Array of options to display in the dropdown menu. See Option Properties for details. |
trigger | 'click' | 'hover' | 'focus' | 'click' | Event that triggers the dropdown menu. |
placement | 'top-start' | 'top' | 'top-end' | 'right-start' | 'right' | 'right-end' | 'bottom-start' | 'bottom' | 'bottom-end' | 'left-start' | 'left' | 'left-end' | 'bottom' | Position of the dropdown menu relative to the trigger element. |
width | number | undefined | 182 | Width of the dropdown menu in pixels. |
show | boolean | undefined | undefined | Controlled visibility state. Use with v-model:show for two-way binding. |
showArrow | boolean | true | Shows a pointing arrow from the menu to the trigger. |
showSelectedMark | boolean | false | Shows a checkmark next to the selected option. |
treeMode | boolean | false | Enables hierarchical navigation for nested options. |
showSearch | boolean | true | Shows a search input at the top of the dropdown. |
multiple | boolean | false | Enables multiple selection mode. |
closeOnSelect | boolean | false | Controls whether the dropdown should close after an option is selected. |
DropdownOption Properties
Property | Type | Description | Example |
---|---|---|---|
key | string | Unique identifier for the option. | 'edit' |
label | string | Display text for the option. | 'Edit Post' |
type | 'default' | 'header' | 'divider' | 'avatar' | 'icon' | 'render' | 'search' | Type of menu item to render. | 'header' |
description | string | Secondary text shown below the label. | 'Modify post content' |
descriptionIcon | () => VNode | Icon rendered next to description. | () => h(InfoIcon) |
icon | () => VNode | Icon for the option. | () => h(EditIcon) |
iconPlacement | 'left' | 'right' | Position of the icon. | 'left' |
children | DropdownOption[] | Nested options for tree navigation. | [{ key: 'sub1', label: 'Sub Option' }] |
src | string | Image URL for avatar type options. | '/path/to/image.png' |
infoText | string | Additional text shown to the right. | '+1' |
titleRightSlot | () => VNode | Custom content for the right side. | () => h(HLTag, { ... }) |
render | () => VNode | Custom render function for the entire option. | () => h('div', { ... }) |
Emits
Dropdown Emits
Event | Arguments | Description |
---|---|---|
@select | (key: string | number, option: DropdownOption) | Fired when an option is selected. |
@update:show | (show: boolean) | Fired when dropdown visibility changes. |
@search | (value: string) | Fired when search input changes. |
@clickoutside | (e: MouseEvent ) |
Slots
Name | Parameters | Description |
---|---|---|
default | () | The default content slot |