下拉菜单 DropDown
通过下拉框展示的小型菜单。
基础使用
通过 options 属性设置菜单内容。选项中字符串值或者对象值的 index 字段会作为选项唯一标识,建议确保它们没有重复。
通过 select 事件获取触发的选项。
options 根据子元素的 href 或 route 字段决定把选项渲染 <a> 标签或者 Vue Router 的 RouterLink 组件。
WARNING
href 和 route 将直接渲染到 <a> 标签中,如果传递类似 javascript:alert(1) 这样的值或恶意 URL,可能会导致 XSS 或开放重定向漏洞。
<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>弹出位置
DropDown 弹出框提供 9 种展示位置。
由 placement 属性决定弹出的位置。该属性值格式为:[方向]-[对齐位置],分别是'top'、'right'、'bottom'、'left'、'top-start'、'top-end'、'right-start'、'right-end'、'bottom-start'、'bottom-end'、'left-start'、'left-end',有四个展示方向,和三种对齐方式,默认的 placement 为 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>菜单分组
选项是可以分组的。
<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>分割按钮
结合 ButtonGroup 和 Button 组件,这里可以使得图标按钮触发弹出。
<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>受控模式
传入 visible 属性进入受控模式;不传或传 undefined 则为非受控模式,可使用 defaultVisible 指定初始显示。组件会触发 update:visible 事件以配合 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 设置禁用状态,此时触发方式将失效,弹出层不会被打开。
<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
| 属性 | 类型 | 可选 | 默认值 | 描述 | 版本 |
|---|---|---|---|---|---|
| options | DropDownOption | DropDownGroupOption | 是 | | 下拉菜单内容。 | 0.1.0 |
| visible | boolean | null | 是 | | 是否显示(受控模式,支持 v-model)。 | 0.1.0 |
| defaultVisible | boolean | null | 是 | | 非受控模式下默认的显示状态。 | 0.1.0 |
| placement | 'top' | 'right' | 'bottom' | 'left' | 'top-start' | 'top-end' | 'right-start' | 'right-end' | 'bottom-start' | 'bottom-end' | 'left-start' | 'left-end' | 是 | 'top' | 弹出位置。 | 0.1.0 |
| trigger | 'hover' | 'click' | 是 | 'hover' | 触发方式。 | 0.1.0 |
| disabled | boolean | 是 | boolean | 是否禁用。 | 0.1.0 |
| offset | number | 是 | 8 | 弹出偏移距离(px)。 | 0.1.0 |
| variant | 'dark' | 'light' | 是 | 'light' | 组件样式变体。 | 0.1.0 |
| arrow | boolean | 是 | true | 是否展示箭头。 | 0.1.0 |
| root | HTMLElement | string | 是 | 'body' | 挂载元素。 | 0.1.0 |
| zIndex | number | 是 | | 弹出层 z-index。 | 0.1.0 |
| destroyOnHide | boolean | 是 | false | 隐藏时是否销毁内容。 | 0.1.0 |
| popoverProps | Omit<PopoverProps, 'visible' | 'content' | 'defaultVisible'> & EmitEvent<PopoverEvents> | 是 | | 透传给内部 Popover 的属性。 | 0.1.0 |
| dividerProps | RestAttrs | 是 | | 透传 DOM 属性到分割线元素。 | 0.1.0 |
DropDownEvents
| 事件 | 参数 | 描述 | 版本 |
|---|---|---|---|
| update:visible | value: boolean | 更新 visible 的回调。 | 0.1.0 |
| close | event: MouseEvent | 弹出层关闭时触发。 | 0.1.0 |
| open | event: MouseEvent | 弹出层打开时触发。 | 0.1.0 |
| select | index: string | number | symbol, option: DropDownOption | string, event: MouseEvent | 选择菜单时触发的回调。 | 0.1.0 |
DropDownSlots
| 插槽 | 参数 | 描述 | 版本 |
|---|---|---|---|
| default | | 触发元素插槽。 | 0.1.0 |
| option | | 选项自定义渲染。 | 0.1.0 |
| group-label | | 0.1.0 |
DropDownExpose
| 属性 | 类型 | 可选 | 默认值 | 描述 | 版本 |
|---|---|---|---|---|---|
| open | () => void | 否 | | 打开确认弹出框。 | 0.1.0 |
| close | () => void | 否 | | 关闭确认弹出框。 | 0.1.0 |
RestAttrs
ts
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
}1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
EmitEvent
ts
export type EmitEvent<T extends Record<string, any>> = {
[K in keyof T as `on${Capitalize<K & string>}`]?: (...args: T[K]) => void
}1
2
3
2
3
DropDownOption, DropDownGroupOption
ts
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
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18