DropDown
A compact menu displayed via a dropdown.
Basic Usage
Set menu content using the options prop. When an option is a string or an object, its index field will be used as the option's unique identifier — make sure they are not duplicated.
Use the select event to get the triggered option.
options determines whether to render the options as <a> tags or Vue Router RouterLink components based on the href or route field of the child elements.
WARNING
The href and route will be directly rendered into the <a> tag. If values such as javascript:alert(1) or malicious URLs are passed, this may lead to XSS or open redirect vulnerabilities.
<template>
<px-space>
<px-drop-down :options="options1" @select="selectHandler">
<px-button>String Option</px-button>
</px-drop-down>
<px-drop-down :options="options2" @select="selectHandler">
<px-button>Object Option</px-button>
</px-drop-down>
</px-space>
</template>
<script setup lang="ts">
import type { DropDownListOption } from '@pixelium/web-vue'
// On-demand import
// import type { DropDownListOption } from '@pixelium/web-vue/es';
import { ref } from 'vue'
const options1 = ref(['Action 1', 'Action 2', 'Action 3'])
const options2 = ref([
{ label: 'Home', index: 'Home', href: '/pixelium-design/' },
{
label: 'Github',
index: 'Github',
target: '_blank',
href: 'https://github.com/shika-works/pixelium-design'
},
{ label: 'Action 1', index: 'Action 1' },
{ label: 'Action 2', index: 'Action 2', disabled: true },
{ label: 'Action 3', index: 'Action 3' }
])
const selectHandler = (index: string, option: string | DropDownListOption) => {
$message.info(
`Selected index: ${index}, option: ${
typeof option === 'string' ? option : JSON.stringify(option)
}`
)
}
</script>Placement
The DropDown popup supports multiple placements.
The placement prop determines where the popup appears. The value format is [direction]-[alignment], such as 'top', 'right', 'bottom', 'left', 'top-start', 'top-end', 'right-start', 'right-end', 'bottom-start', 'bottom-end', 'left-start', 'left-end'. There are four directions and three alignment variations. Default placement is top.
<template>
<div class="box">
<div class="left">
<px-drop-down
v-for="item in ['left-start', 'left', 'left-end']"
:key="item"
:placement="item"
:options="getOptions(item)"
>
<px-button>{{ getLabel(item) }}</px-button>
</px-drop-down>
</div>
<div class="center">
<div class="top">
<px-drop-down
v-for="item in ['top-start', 'top', 'top-end']"
:key="item"
:placement="item"
:options="getOptions(item)"
>
<px-button>{{ getLabel(item) }}</px-button>
</px-drop-down>
</div>
<div class="bottom">
<px-drop-down
v-for="item in ['bottom-start', 'bottom', 'bottom-end']"
:key="item"
:placement="item"
:options="getOptions(item)"
>
<px-button>{{ getLabel(item) }}</px-button>
</px-drop-down>
</div>
</div>
<div class="right">
<px-drop-down
v-for="item in ['right-start', 'right', 'right-end']"
:key="item"
:placement="item"
:options="getOptions(item)"
>
<px-button>{{ getLabel(item) }}</px-button>
</px-drop-down>
</div>
</div>
</template>
<script setup lang="ts">
const getLabel = (placement: string) => {
return placement.replace(/-/g, ' ').replace(/\b\w/g, (l) => l.toUpperCase())
}
const getOptions = (placement: string) => {
let contentArr: string[] = []
if (placement.includes('-')) {
const [pos, sub] = placement.split('-')
contentArr = [capitalize(pos), capitalize(sub), 'Popover']
} else {
contentArr = [capitalize(placement), 'Popover']
}
return contentArr
}
const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1)
</script>
<style lang="css" scoped>
.box {
display: flex;
width: 600px;
height: 350px;
margin-left: 100px;
margin-top: 64px;
margin-bottom: 64px;
}
.left,
.right {
flex-shrink: 0;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.left {
align-items: flex-start;
}
.right {
align-items: flex-end;
}
.center {
flex: 1;
display: flex;
flex-direction: column;
}
.top,
.bottom {
display: flex;
justify-content: space-around;
}
.top {
flex: 1;
}
</style>Grouped Menus
Options can be grouped.
<template>
<px-space>
<px-drop-down :options="options1">
<px-button>DropDown Group</px-button>
</px-drop-down>
<px-drop-down :options="options2">
<px-button>DropDown Divider</px-button>
</px-drop-down>
</px-space>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const options1 = ref([
'Action 1',
'Action 2',
'Action 3',
{
label: 'Action More',
index: 'Action More',
children: ['Action More 1', 'Action More 2', 'Action More 3'],
type: 'group'
}
])
const options2 = ref([
'Action 1',
'Action 2',
'Action 3',
{
label: 'Action More 1',
index: 'Action More 1',
divider: true
},
'Action More 2',
'Action More 3'
])
</script>Split Button
Combine ButtonGroup and Button components so an icon button can trigger the popup.
<template>
<px-space>
<px-drop-down :options="options">
<px-button
>DropDown <IconChevronDown style="margin-left: 8px"></IconChevronDown
></px-button>
</px-drop-down>
<px-button-group>
<px-button>DropDown</px-button>
<px-drop-down :options="options">
<px-button shape="square"><IconChevronDown /></px-button>
</px-drop-down>
</px-button-group>
</px-space>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { IconChevronDown } from '@pixelium/web-vue/icon-hn/es'
const options = ref(['Action 1', 'Action 2', 'Action 3'])
</script>Controlled Mode
Passing visible switches the component to controlled mode. If omitted or set to undefined, the component is uncontrolled and defaultVisible can be used to set the initial state. The component emits update:visible to support v-model.
<template>
<px-drop-down :options="options" v-model:visible="visible">
<px-button variant="text">Controlled {{ visible }}</px-button>
</px-drop-down>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const visible = ref(false)
const options = ref(['Action 1', 'Action 2', 'Action 3'])
</script>Disabled State
Set disabled to disable the component. Trigger actions will be ignored and the popup will not open.
<template>
<px-drop-down :options="options" disabled>
<px-button theme="info">Disabled</px-button>
</px-drop-down>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const options = ref(['Action 1', 'Action 2', 'Action 3'])
</script>API
DropDownProps
| Attribute | Type | Optional | Default | Description | Version |
|---|---|---|---|---|---|
| options | DropDownOption | DropDownGroupOption | True | | Dropdown menu contents. | 0.1.0 |
| visible | boolean | null | True | | Whether it is shown (controlled mode, supports v-model). | 0.1.0 |
| defaultVisible | boolean | null | True | | Default visibility in uncontrolled mode. | 0.1.0 |
| placement | 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | True | 'top' | Popup placement. | 0.1.0 |
| trigger | 'hover' | 'click' | True | 'hover' | Trigger action. | 0.1.0 |
| disabled | boolean | True | boolean | Whether the component is disabled. | 0.1.0 |
| offset | number | True | 8 | Popup offset in pixels. | 0.1.0 |
| variant | 'dark' | 'light' | True | 'light' | Component style variant. | 0.1.0 |
| arrow | boolean | True | true | Whether to show an arrow. | 0.1.0 |
| root | HTMLElement | string | True | 'body' | Mount element. | 0.1.0 |
| zIndex | number | True | | z-index for the popup. | 0.1.0 |
| destroyOnHide | boolean | True | false | Whether to destroy content when hidden. | 0.1.0 |
| popoverProps | Omit<PopoverProps, 'visible' | 'content' | 'defaultVisible'> & EmitEvent<PopoverEvents> | True | | Props forwarded to the internal Popover. | 0.1.0 |
| dividerProps | RestAttrs | True | | DOM attributes forwarded to the divider element. | 0.1.0 |
DropDownEvents
| Event | Parameter | Description | Version |
|---|---|---|---|
| update:visible | value: boolean | Callback for updating visible. | 0.1.0 |
| close | event: MouseEvent | Fired when the popup closes. | 0.1.0 |
| open | event: MouseEvent | Fired when the popup opens. | 0.1.0 |
| select | index: string | number | symbol, option: DropDownOption | string, event: MouseEvent | Fired when a menu option is selected. | 0.1.0 |
DropDownSlots
| Slot | Parameter | Description | Version |
|---|---|---|---|
| default | | Trigger element slot. | 0.1.0 |
| option | | Custom rendering for an option. | 0.1.0 |
| group-label | | Custom rendering for the group header. | 0.1.0 |
DropDownExpose
| Attribute | Type | Optional | Default | Description | Version |
|---|---|---|---|---|---|
| open | () => void | False | | Opens the popup. | 0.1.0 |
| close | () => void | False | | Closes the popup. | 0.1.0 |
RestAttrs
import type { StyleValue } from 'vue'
export type VueClassValue = string | Record<string, any> | VueClassValue[]
export type VueStyleValue = StyleValue
export type RestAttrs = {
style?: VueStyleValue | null
class?: VueClassValue | null
[x: string]: any
}2
3
4
5
6
7
8
9
10
EmitEvent
export type EmitEvent<T extends Record<string, any>> = {
[K in keyof T as `on${Capitalize<K & string>}`]?: (...args: T[K]) => void
}2
3
DropDownOption, DropDownGroupOption
export interface DropDownOption extends NavigationOption {
divider?: boolean
disabled?: boolean
href?: string
route?: string | object
target?: string
}
export interface DropDownGroupOption extends NavigationOption {
children: (DropDownOption | string)[]
type: 'group'
}
export interface NavigationOption {
index: string | number | symbol
label?: string
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18