20260720172840

This commit is contained in:
oneao committed 2026-07-20 17:28:47 +08:00
1 parent cda7f16d3a
commit 94207c77ff
3117 files changed
+201407 -1236

No files matched your search

@@ -0,0 +1,86 @@
# _semantic
## Source
```vue
<script setup lang="ts">
import { computed, ref } from 'vue'
import { SemanticPreview } from '@/components/semantic'
import { useComponentLocale } from '@/composables/use-locale'
import { locales } from '../locales'
const mode = ref<'single' | 'multiple'>('single')
const options = [
{ value: 'aojunhao123', label: 'aojunhao123' },
{ value: 'thinkasany', label: 'thinkasany' },
{ value: 'meet-student', label: 'meet-student' },
]
const value = computed(() => {
if (mode.value !== 'multiple')
return []
return options.length > 0
? [options[0]!.value]
: []
})
const { t } = useComponentLocale(locales)
const semantics = computed(() => {
const base = [
{ name: 'root', desc: t('root') },
{ name: 'prefix', desc: t('prefix') },
{ name: 'content', desc: t('content') },
{ name: 'placeholder', desc: t('placeholder') },
{ name: 'clear', desc: t('clear') },
{ name: 'input', desc: t('input') },
{ name: 'suffix', desc: t('suffix') },
{ name: 'popup.root', desc: t('popup.root') },
{ name: 'popup.list', desc: t('popup.list') },
{ name: 'popup.listItem', desc: t('popup.listItem') },
]
if (mode.value === 'multiple') {
return [
...base,
{ name: 'item', desc: t('item') },
{ name: 'itemContent', desc: t('itemContent') },
{ name: 'itemRemove', desc: t('itemRemove') },
]
}
return base
})
const divRef = ref<HTMLDivElement | null>(null)
</script>
<template>
<SemanticPreview
component-name="Select"
:semantics="semantics"
>
<template #default="{ classes }">
<div ref="divRef" :style="{ position: 'absolute', height: '200px' }">
<div :style="{ display: 'flex', justifyContent: 'center', marginBottom: '12px' }">
<a-segmented v-model:value="mode" :options="['single', 'multiple']" />
</div>
<div :style="{ display: 'flex', flexDirection: 'column', gap: '12px' }">
<a-select
prefix="prefix"
:style="{ width: '300px' }"
:options="options"
:value="value"
:mode="mode === 'multiple' ? 'multiple' : undefined"
open
:get-popup-container="() => divRef!"
:classes="classes"
/>
</div>
</div>
</template>
</SemanticPreview>
</template>
```
@@ -0,0 +1,34 @@
# Automatic tokenization
## Description (en-US)
Try to copy `Lucy,Jack` and paste to the input. Only available in tags and multiple mode.
## Source
```vue
<script setup lang="ts">
import { ref } from 'vue'
const options = Array.from({ length: 26 }, (_, i) => {
const value = (i + 10).toString(36) + (i + 10)
return { value, label: value }
})
function handleChange(value: string[]) {
console.log(`selected ${value}`)
}
const value = ref()
</script>
<template>
<a-select
v-model:value="value"
mode="tags"
style="width: 100%"
:token-separators="[',']"
:options="options"
@change="handleChange"
/>
</template>
```
@@ -0,0 +1,32 @@
# Basic Usage
## Description (en-US)
Basic Usage.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = [
{ value: 'jack', label: 'Jack' },
{ value: 'lucy', label: 'Lucy' },
{ value: 'Yiminghe', label: 'yiminghe' },
{ value: 'disabled', label: 'Disabled', disabled: true },
]
const value1 = shallowRef('lucy')
const value2 = shallowRef('lucy')
</script>
<template>
<a-space wrap>
<div>
{{ value1 }}
</div>
<a-select v-model:value="value1" style="width: 120px" :options="options" />
<a-select v-model:value="value2" style="width: 120px" disabled :options="[{ value: 'lucy', label: 'lucy' }]" />
</a-space>
</template>
```
@@ -0,0 +1,36 @@
# Big Data
## Description (en-US)
Select use [virtual scroll](https://github.com/react-component/virtual-list) which get better performance, turn off it by setting `:virtual="false"`.
## Source
```vue
<script setup lang="ts">
import type { SelectProps } from 'antdv-next'
import { shallowRef } from 'vue'
const options: SelectProps['options'] = []
for (let i = 0; i < 100000; i++) {
const value = `${i.toString(36)}${i}`
options.push({
label: value,
value,
disabled: i === 10,
})
}
const value = shallowRef(['a10', 'c12'])
</script>
<template>
<a-typography-title :level="4">
{{ options?.length }} Items
</a-typography-title>
<a-select v-model:value="value" mode="multiple" style="width: 100%" placeholder="Please select" :options="options" />
</template>
<style scoped>
</style>
```
@@ -0,0 +1,46 @@
# Coordinate
## Description (en-US)
Coordinating the selection of provinces and cities is a common use case and demonstrates how selection can be coordinated. [Cascader](../../cascader/docs.md) component is strongly recommended in this case.
## Source
```vue
<script setup lang="ts">
import { computed, shallowRef } from 'vue'
const cityData: Record<string, string[]> = {
Zhejiang: ['Hangzhou', 'Ningbo', 'Wenzhou'],
Jiangsu: ['Nanjing', 'Suzhou', 'Zhenjiang'],
}
const provinceData: string[] = ['Zhejiang', 'Jiangsu']
const province = shallowRef(provinceData[0]!)
const secondCity = shallowRef(cityData[provinceData[0]!]![0])
const cities = computed(() => cityData[province.value] ?? [])
function handleProvinceChange(value: string) {
province.value = value
secondCity.value = cityData[value]?.[0] ?? ''
}
</script>
<template>
<a-space wrap>
<a-select
v-model:value="province"
style="width: 120px"
:options="provinceData.map((p) => ({ label: p, value: p }))"
@change="handleProvinceChange"
/>
<a-select
v-model:value="secondCity"
style="width: 120px"
:options="cities.map((c) => ({ label: c, value: c }))"
/>
</a-space>
</template>
```
@@ -0,0 +1,51 @@
# Custom dropdown
## Description (en-US)
Customize the dropdown menu via `popupRender`. If you want to close the dropdown after clicking the custom content, you need to control `open` prop.
## Source
```vue
<script setup lang="ts">
import { PlusOutlined } from '@antdv-next/icons'
import { shallowRef } from 'vue'
let index = 0
const items = shallowRef(['jack', 'lucy'])
const name = shallowRef('')
function addItem(e: MouseEvent) {
e.preventDefault()
items.value = [...items.value, name.value || `New item ${index++}`]
name.value = ''
}
</script>
<template>
<a-select
style="width: 300px"
placeholder="custom dropdown render"
:options="items.map((item) => ({ label: item, value: item }))"
>
<template #popupRender="menu">
<component :is="menu" />
<a-divider style="margin: 8px 0" />
<a-space style="padding: 0 8px 4px">
<a-input
v-model:value="name"
placeholder="Please enter item"
@keydown.stop
/>
<a-button type="text" @click="addItem">
<template #icon>
<PlusOutlined />
</template>
Add item
</a-button>
</a-space>
</template>
</a-select>
</template>
```
@@ -0,0 +1,37 @@
# Custom Selected Label Render
## Description (en-US)
Allows custom rendering of the currently selected label, which can be used for value backfill but the corresponding option is missing and does not want to directly render the value.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = [
{ label: 'gold', value: 'gold' },
{ label: 'lime', value: 'lime' },
{ label: 'green', value: 'green' },
{ label: 'cyan', value: 'cyan' },
]
const value = shallowRef('1')
</script>
<template>
<a-select
v-model:value="value"
style="width: 100%"
:options="options"
>
<template #labelRender="{ label, value: val }">
<template v-if="label">
{{ val }}
</template>
<span v-else>No option match</span>
</template>
</a-select>
</template>
```
@@ -0,0 +1,48 @@
# Custom Tag Render
## Description (en-US)
Allows for custom rendering of tags.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = [
{ value: 'gold' },
{ value: 'lime' },
{ value: 'green' },
{ value: 'cyan' },
]
const value = shallowRef(['gold', 'cyan'])
function onPreventMouseDown(event: MouseEvent) {
event.preventDefault()
event.stopPropagation()
}
</script>
<template>
<a-select
v-model:value="value"
mode="multiple"
style="width: 100%"
:options="options"
>
<template #tagRender="{ label, value: tagValue, closable, onClose }">
<a-tag
:color="tagValue"
:closable="closable"
style="margin-inline-end: 4px"
@mousedown="onPreventMouseDown"
@close="onClose"
>
{{ label }}
</a-tag>
</template>
</a-select>
</template>
```
@@ -0,0 +1,34 @@
# Hide Already Selected
## Description (en-US)
Hide already selected options in the dropdown.
## Source
```vue
<script setup lang="ts">
import { computed, shallowRef } from 'vue'
const OPTIONS = ['Apples', 'Nails', 'Bananas', 'Helicopters']
const selectedItems = shallowRef<string[]>([])
const filteredOptions = computed(() =>
OPTIONS.filter(o => !selectedItems.value.includes(o)).map(item => ({
value: item,
label: item,
})),
)
</script>
<template>
<a-select
v-model:value="selectedItems"
mode="multiple"
placeholder="Inserted are removed"
style="width: 100%"
:options="filteredOptions"
/>
</template>
```
@@ -0,0 +1,36 @@
# Get value of selected item
## Description (en-US)
As a default behavior, the `onChange` callback can only get the `value` of the selected item. The `labelInValue` prop can be used to get the `label` property of the selected item.
The `label` of the selected item will be packed as an object for passing to the `onChange` callback.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const value = shallowRef({ value: 'lucy', label: 'Lucy (101)' })
const options = [
{ value: 'jack', label: 'Jack (100)' },
{ value: 'lucy', label: 'Lucy (101)' },
]
function handleChange(val: { value: string, label: string }) {
console.log(val) // { value: "lucy", key: "lucy", label: "Lucy (101)" }
}
</script>
<template>
<a-select
v-model:value="value"
label-in-value
style="width: 120px"
:options="options"
@change="handleChange"
/>
</template>
```
@@ -0,0 +1,46 @@
# Max Count
## Description (en-US)
You can set the `maxCount` prop to control the max number of items can be selected. When the limit is exceeded, the options will become disabled.
## Source
```vue
<script setup lang="ts">
import { DownOutlined } from '@antdv-next/icons'
import { shallowRef } from 'vue'
const MAX_COUNT = 3
const value = shallowRef<string[]>(['Ava Swift'])
const options = [
{ value: 'Ava Swift', label: 'Ava Swift' },
{ value: 'Cole Reed', label: 'Cole Reed' },
{ value: 'Mia Blake', label: 'Mia Blake' },
{ value: 'Jake Stone', label: 'Jake Stone' },
{ value: 'Lily Lane', label: 'Lily Lane' },
{ value: 'Ryan Chase', label: 'Ryan Chase' },
{ value: 'Zoe Fox', label: 'Zoe Fox' },
{ value: 'Alex Grey', label: 'Alex Grey' },
{ value: 'Elle Blair', label: 'Elle Blair' },
]
</script>
<template>
<a-select
v-model:value="value"
mode="multiple"
:max-count="MAX_COUNT"
style="width: 100%"
placeholder="Please select"
:options="options"
>
<template #suffixIcon>
<span>{{ value.length }} / {{ MAX_COUNT }}</span>
<DownOutlined />
</template>
</a-select>
</template>
```
@@ -0,0 +1,48 @@
# Multiple selection
## Description (en-US)
Multiple selection, selecting from existing items.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = Array.from({ length: 26 }, (_, i) => {
const value = (i + 10).toString(36) + (i + 10)
return { label: value, value }
})
const value1 = shallowRef(['a10', 'c12'])
const value2 = shallowRef(['a10', 'c12'])
function handleChange(value: string[]) {
console.log(`selected ${value}`)
}
</script>
<template>
<a-space style="width: 100%" vertical>
<a-select
v-model:value="value1"
mode="multiple"
allow-clear
style="width: 100%"
placeholder="Please select"
:options="options"
@change="handleChange"
/>
<a-select
v-model:value="value2"
mode="multiple"
disabled
style="width: 100%"
placeholder="Please select"
:options="options"
@change="handleChange"
/>
</a-space>
</template>
```
@@ -0,0 +1,51 @@
# Option Group
## Description (en-US)
Using `OptGroup` to group the options.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const value = shallowRef('Lucy')
const options = [
{
label: 'manager',
title: 'manager',
options: [
{ label: 'Jack', value: 'Jack' },
{ label: 'Lucy', value: 'Lucy' },
],
},
{
label: 'engineer',
title: 'engineer',
options: [
{ label: 'Chloe', value: 'Chloe' },
{ label: 'Lucas', value: 'Lucas' },
],
},
]
function handleChange(val: string) {
console.log(`selected ${val}`)
}
</script>
<template>
<a-select
v-model:value="value"
style="width: 200px"
:options="options"
@change="handleChange"
/>
</template>
<style scoped>
</style>
```
@@ -0,0 +1,66 @@
# Custom dropdown options
## Description (en-US)
Use `optionRender` to customize the rendering dropdown options.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const value = shallowRef(['china'])
const options = [
{
label: 'China',
value: 'china',
emoji: '🇨🇳',
desc: 'China (中国)',
},
{
label: 'USA',
value: 'usa',
emoji: '🇺🇸',
desc: 'USA (美国)',
},
{
label: 'Japan',
value: 'japan',
emoji: '🇯🇵',
desc: 'Japan (日本)',
},
{
label: 'Korea',
value: 'korea',
emoji: '🇰🇷',
desc: 'Korea (韩国)',
},
]
function handleChange(val: string[]) {
console.log(`selected ${val}`)
}
</script>
<template>
<a-select
v-model:value="value"
mode="multiple"
style="width: 100%"
placeholder="select one country"
:options="options"
@change="handleChange"
>
<template #optionRender="{ option }">
<a-space>
<span role="img" :aria-label="option.data.label">
{{ option.data.emoji }}
</span>
{{ option.data.desc }}
</a-space>
</template>
</a-select>
</template>
```
@@ -0,0 +1,50 @@
# Placement
## Description (en-US)
You can manually specify the position of the popup via `placement`.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
type Placement = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
const placement = shallowRef<Placement>('topLeft')
const value = shallowRef('HangZhou')
const options = [
{ value: 'HangZhou', label: 'HangZhou #310000' },
{ value: 'NingBo', label: 'NingBo #315000' },
{ value: 'WenZhou', label: 'WenZhou #325000' },
]
</script>
<template>
<a-radio-group v-model:value="placement">
<a-radio-button value="topLeft">
topLeft
</a-radio-button>
<a-radio-button value="topRight">
topRight
</a-radio-button>
<a-radio-button value="bottomLeft">
bottomLeft
</a-radio-button>
<a-radio-button value="bottomRight">
bottomRight
</a-radio-button>
</a-radio-group>
<br>
<br>
<a-select
v-model:value="value"
style="width: 120px"
:popup-match-select-width="false"
:placement="placement"
:options="options"
/>
</template>
```
@@ -0,0 +1,59 @@
# Responsive maxTagCount
## Description (en-US)
Auto collapse to tag with responsive case. Not recommend use in large form case since responsive calculation has a perf cost.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = Array.from({ length: 26 }, (_, i) => {
const value = (i + 10).toString(36) + (i + 10)
return { label: `Long Label: ${value}`, value }
})
const value = shallowRef(['a10', 'c12', 'h17', 'j19', 'k20'])
const value1 = shallowRef(['a10', 'c12', 'h17', 'j19'])
</script>
<template>
<a-space vertical style="width: 100%">
<a-select
v-model:value="value"
mode="multiple"
style="width: 100%"
:options="options"
placeholder="Select Item..."
max-tag-count="responsive"
/>
<a-select
mode="multiple"
style="width: 100%"
disabled
:options="options"
placeholder="Select Item..."
max-tag-count="responsive"
/>
<a-select
v-model:value="value1"
mode="multiple"
style="width: 100%"
:options="options"
placeholder="Select Item..."
max-tag-count="responsive"
>
<template #maxTagPlaceholder="omittedValues">
<a-tooltip>
<template #title>
{{ omittedValues.map((v: any) => v.label).join(', ') }}
</template>
<span>Hover Me</span>
</a-tooltip>
</template>
</a-select>
</a-space>
</template>
```
@@ -0,0 +1,76 @@
# Search Box
## Description (en-US)
Search with remote data.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
interface OptionItem {
value: string
label: string
}
let timeout: ReturnType<typeof setTimeout> | null = null
let currentValue = ''
const data = shallowRef<OptionItem[]>([])
const value = shallowRef<string>()
function fetchData(val: string, callback: (data: OptionItem[]) => void) {
if (timeout) {
clearTimeout(timeout)
timeout = null
}
currentValue = val
const params = new URLSearchParams({ code: 'utf-8', q: val })
const fake = () => {
fetch(`https://suggest.taobao.com/sug?${params.toString()}`)
.then(response => response.json())
.then(({ result }) => {
if (currentValue === val) {
const options = result.map((item: string[]) => ({
value: item[0],
label: item[0],
}))
callback(options)
}
})
}
if (val) {
timeout = setTimeout(fake, 300)
}
else {
callback([])
}
}
function handleSearch(val: string) {
fetchData(val, (options) => {
data.value = options
})
}
</script>
<template>
<a-select
v-model:value="value"
show-search
:filter-option="false"
placeholder="input search text"
style="width: 200px"
:default-active-first-option="false"
:suffix-icon="null"
:not-found-content="null"
:options="data"
@search="handleSearch"
/>
</template>
```
@@ -0,0 +1,35 @@
# Custom Search
## Description (en-US)
Customize search using `filterOption`.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const value = shallowRef<string>()
const options = [
{ value: '1', label: 'Jack' },
{ value: '2', label: 'Lucy' },
{ value: '3', label: 'Tom' },
]
function filterOption(input: string, option: { label?: string }) {
return (option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
</script>
<template>
<a-select
v-model:value="value"
show-search
:filter-option="filterOption"
placeholder="Select a person"
:options="options"
/>
</template>
```
@@ -0,0 +1,40 @@
# Search with sort
## Description (en-US)
Search the options with sorting.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const value = shallowRef<string>()
const options = [
{ value: '1', label: 'Not Identified' },
{ value: '2', label: 'Closed' },
{ value: '3', label: 'Communicated' },
{ value: '4', label: 'Identified' },
{ value: '5', label: 'Resolved' },
{ value: '6', label: 'Cancelled' },
]
function filterSort(optionA: { label?: string }, optionB: { label?: string }) {
return (optionA?.label ?? '').toLowerCase().localeCompare((optionB?.label ?? '').toLowerCase())
}
</script>
<template>
<a-select
v-model:value="value"
show-search
option-filter-prop="label"
:filter-sort="filterSort"
style="width: 200px"
placeholder="Search to Select"
:options="options"
/>
</template>
```
@@ -0,0 +1,84 @@
# Search and Select Users
## Description (en-US)
A complete multiple select sample with remote search, debounce fetch, ajax callback order flow, and loading state.
## Source
```vue
<script setup lang="ts">
import { debounce } from 'es-toolkit'
import { shallowRef } from 'vue'
interface UserValue {
label: string
value: string
avatar?: string
}
const value = shallowRef<UserValue[]>([])
const options = shallowRef<UserValue[]>([])
const fetching = shallowRef(false)
let fetchId = 0
async function fetchUserList(username: string): Promise<UserValue[]> {
console.log('fetching user', username)
return fetch(`https://660d2bd96ddfa2943b33731c.mockapi.io/api/users/?search=${username}`)
.then(res => res.json())
.then((res) => {
const results = Array.isArray(res) ? res : []
return results.map((user: { name: string, id: string, avatar: string }) => ({
label: user.name,
value: user.id,
avatar: user.avatar,
}))
})
}
const debounceFetcher = debounce((val: string) => {
fetchId += 1
const currentFetchId = fetchId
options.value = []
fetching.value = true
fetchUserList(val).then((newOptions) => {
if (currentFetchId !== fetchId) {
return
}
options.value = newOptions
fetching.value = false
})
}, 300)
function handleSearch(val: string) {
debounceFetcher(val)
}
</script>
<template>
<a-select
v-model:value="value"
mode="multiple"
label-in-value
show-search
:filter-option="false"
placeholder="Select users"
style="width: 100%"
:options="options"
@search="handleSearch"
>
<template #notFoundContent>
<a-spin v-if="fetching" size="small" />
<span v-else>No results found</span>
</template>
<template #optionRender="{ option }">
<div style="display: flex; align-items: center">
<a-avatar v-if="option.data.avatar" :src="option.data.avatar" style="margin-right: 8px" />
{{ option.data.label }}
</div>
</template>
</a-select>
</template>
```
@@ -0,0 +1,41 @@
# Select with search field
## Description (en-US)
Search the options while expanded.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const value = shallowRef<string>()
const options = [
{ value: 'jack', label: 'Jack' },
{ value: 'lucy', label: 'Lucy' },
{ value: 'tom', label: 'Tom' },
]
function onChange(val: string) {
console.log(`selected ${val}`)
}
function onSearch(val: string) {
console.log('search:', val)
}
</script>
<template>
<a-select
v-model:value="value"
show-search
option-filter-prop="label"
placeholder="Select a person"
:options="options"
@change="onChange"
@search="onSearch"
/>
</template>
```
@@ -0,0 +1,72 @@
# Sizes
## Description (en-US)
The height of the input field for the select defaults to 32px. If size is set to large, the height will be 40px, and if set to small, 24px.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
type SizeType = 'large' | 'middle' | 'small'
const options = Array.from({ length: 26 }, (_, i) => {
const value = (i + 10).toString(36) + (i + 10)
return { value, label: value }
})
const size = shallowRef<SizeType>('middle')
const value1 = shallowRef('a10')
const value2 = shallowRef(['a10', 'c12'])
const value3 = shallowRef(['a10', 'c12'])
function handleChange(val: string | string[]) {
console.log(`Selected: ${val}`)
}
</script>
<template>
<a-radio-group v-model:value="size">
<a-radio-button value="large">
Large
</a-radio-button>
<a-radio-button value="middle">
Default
</a-radio-button>
<a-radio-button value="small">
Small
</a-radio-button>
</a-radio-group>
<br>
<br>
<a-space vertical style="width: 100%">
<a-select
v-model:value="value1"
:size="size"
style="width: 200px"
:options="options"
@change="handleChange"
/>
<a-select
v-model:value="value2"
mode="multiple"
:size="size"
placeholder="Please select"
style="width: 100%"
:options="options"
@change="handleChange"
/>
<a-select
v-model:value="value3"
mode="tags"
:size="size"
placeholder="Please select"
style="width: 100%"
:options="options"
@change="handleChange"
/>
</a-space>
</template>
```
@@ -0,0 +1,19 @@
# Status
## Description (en-US)
Add status to Select with `status`, which could be `error` or `warning`.
## Source
```vue
<script setup lang="ts">
</script>
<template>
<a-space vertical style="width: 100%">
<a-select status="error" style="width: 100%" />
<a-select status="warning" style="width: 100%" />
</a-space>
</template>
```
@@ -0,0 +1,74 @@
# Custom semantic dom styling
## Description (en-US)
You can customize the semantic dom style of Select by passing objects/functions through `classes` and `styles`.
## Source
```vue
<script setup lang="ts">
import { MehOutlined } from '@antdv-next/icons'
const options = [
{ value: 'GuangZhou', label: 'GuangZhou' },
{ value: 'ShenZhen', label: 'ShenZhen' },
]
const stylesObject = {
prefix: {
color: '#1890ff',
},
suffix: {
color: '#1890ff',
},
}
const stylesFilled = {
prefix: {
color: '#722ed1',
},
suffix: {
color: '#722ed1',
},
popup: {
root: {
border: '1px solid #722ed1',
},
},
}
</script>
<template>
<a-flex vertical gap="middle">
<a-select
:options="options"
:classes="{ root: 'custom-select' }"
:styles="stylesObject"
placeholder="Object"
>
<template #prefix>
<MehOutlined />
</template>
</a-select>
<a-select
:options="options"
:classes="{ root: 'custom-select' }"
:styles="stylesFilled"
placeholder="Function"
variant="filled"
>
<template #prefix>
<MehOutlined />
</template>
</a-select>
</a-flex>
</template>
<style>
.custom-select {
border-radius: 8px;
width: 300px;
}
</style>
```
@@ -0,0 +1,103 @@
# Prefix and Suffix
## Description (en-US)
Custom `prefix` and `suffixIcon`.
## Source
```vue
<script setup lang="ts">
import { MehOutlined, SmileOutlined } from '@antdv-next/icons'
import { shallowRef } from 'vue'
const options = [
{ value: 'jack', label: 'Jack' },
{ value: 'lucy', label: 'Lucy' },
{ value: 'Yiminghe', label: 'yiminghe' },
{ value: 'disabled', label: 'Disabled', disabled: true },
]
const value1 = shallowRef('lucy')
const value2 = shallowRef('lucy')
const value3 = shallowRef('lucy')
const value4 = shallowRef(['lucy'])
const value5 = shallowRef(['lucy'])
const value6 = shallowRef(['lucy'])
function handleChange(val: string | string[]) {
console.log(`selected ${val}`)
}
</script>
<template>
<a-space wrap>
<a-select
v-model:value="value1"
prefix="User"
placeholder="Select User"
style="width: 200px"
:options="options"
allow-clear
show-search
@change="handleChange"
/>
<a-select
v-model:value="value2"
placeholder="Select"
style="width: 120px"
:options="options"
@change="handleChange"
>
<template #suffixIcon>
<SmileOutlined />
</template>
</a-select>
<a-select
v-model:value="value3"
placeholder="Select"
style="width: 120px"
disabled
:options="[{ value: 'lucy', label: 'Lucy' }]"
>
<template #suffixIcon>
<MehOutlined />
</template>
</a-select>
<br>
<a-select
v-model:value="value4"
prefix="User"
placeholder="Select"
mode="multiple"
style="width: 200px"
:options="options"
@change="handleChange"
/>
<a-select
v-model:value="value5"
placeholder="Select"
mode="multiple"
style="width: 120px"
:options="options"
@change="handleChange"
>
<template #suffixIcon>
<SmileOutlined />
</template>
</a-select>
<a-select
v-model:value="value6"
placeholder="Select"
mode="multiple"
style="width: 120px"
disabled
:options="[{ value: 'lucy', label: 'Lucy' }]"
>
<template #suffixIcon>
<MehOutlined />
</template>
</a-select>
</a-space>
</template>
```
@@ -0,0 +1,35 @@
# Tags
## Description (en-US)
Allow user to select tags from list or input custom tag.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = Array.from({ length: 26 }, (_, i) => {
const value = (i + 10).toString(36) + (i + 10)
return { value, label: value }
})
const value = shallowRef<string[]>([])
function handleChange(val: string[]) {
console.log(`selected ${val}`)
}
</script>
<template>
<a-select
v-model:value="value"
mode="tags"
style="width: 100%"
placeholder="Tags Mode"
:options="options"
@change="handleChange"
/>
</template>
```
@@ -0,0 +1,99 @@
# Variants
## Description (en-US)
Variants of Select, there are four variants: `outlined` `filled` `borderless` and `underlined`.
## Source
```vue
<script setup lang="ts">
import { shallowRef } from 'vue'
const options = [
{ value: 'jack', label: 'Jack' },
{ value: 'lucy', label: 'Lucy' },
{ value: 'Yiminghe', label: 'yiminghe' },
]
const value1 = shallowRef<string>()
const value2 = shallowRef(['lucy'])
const value3 = shallowRef<string>()
const value4 = shallowRef(['lucy'])
const value5 = shallowRef<string>()
const value6 = shallowRef(['lucy'])
const value7 = shallowRef<string>()
const value8 = shallowRef(['lucy'])
</script>
<template>
<a-flex :gap="12" vertical>
<a-flex :gap="8">
<a-select
v-model:value="value1"
placeholder="Outlined"
style="flex: 1"
:options="options"
/>
<a-select
v-model:value="value2"
mode="multiple"
placeholder="Outlined"
style="flex: 1"
:options="options"
/>
</a-flex>
<a-flex :gap="8">
<a-select
v-model:value="value3"
placeholder="Filled"
variant="filled"
style="flex: 1"
:options="options"
/>
<a-select
v-model:value="value4"
mode="multiple"
placeholder="Filled"
variant="filled"
style="flex: 1"
:options="options"
/>
</a-flex>
<a-flex :gap="8">
<a-select
v-model:value="value5"
placeholder="Borderless"
variant="borderless"
style="flex: 1"
:options="options"
/>
<a-select
v-model:value="value6"
mode="multiple"
placeholder="Borderless"
variant="borderless"
style="flex: 1"
:options="options"
/>
</a-flex>
<a-flex :gap="8">
<a-select
v-model:value="value7"
placeholder="Underlined"
variant="underlined"
style="flex: 1"
:options="options"
/>
<a-select
v-model:value="value8"
mode="multiple"
placeholder="Underlined"
variant="underlined"
style="flex: 1"
:options="options"
/>
</a-flex>
</a-flex>
</template>
```
@@ -0,0 +1,220 @@
---
title: Select
description: A dropdown menu for displaying choices.
---
## When To Use
- A dropdown menu for displaying choices - an elegant alternative to the native `<select>` element.
- Utilizing [Radio](../radio/docs.md) is recommended when there are fewer total options (less than 5).
- You probably need [AutoComplete](../auto-complete/docs.md) if you're looking for an input box that can be typed or selected.
## Demos
<!-- prettier-ignore -->
| Demo | Path |
| --- | --- |
| Basic Usage | demo/basic.md |
| Select with search field | demo/search.md |
| Custom Search | demo/search-filter-option.md |
| Multiple selection | demo/multiple.md |
| Sizes | demo/size.md |
| Custom dropdown options | demo/option-render.md |
| Search with sort | demo/search-sorts.md |
| Tags | demo/tags.md |
| Option Group | demo/optgroup.md |
| Coordinate | demo/coordinate.md |
| Search Box | demo/search-box.md |
| Get value of selected item | demo/label-in-value.md |
| Automatic tokenization | demo/automatic-tokenization.md |
| Search and Select Users | demo/search-users.md |
| Prefix and Suffix | demo/suffix.md |
| Custom dropdown | demo/custom-dropdown-menu.md |
| Hide Already Selected | demo/hide-selected.md |
| Variants | demo/variant.md |
| Custom Tag Render | demo/custom-tag-render.md |
| Custom Selected Label Render | demo/custom-label-render.md |
| Responsive maxTagCount | demo/responsive.md |
| Big Data | demo/big-data.md |
| Status | demo/status.md |
| Placement | demo/placement.md |
| Max Count | demo/maxCount.md |
| Custom semantic dom styling | demo/style-class.md |
## API
### Property
Common props ref:[Common props](../../docs/vue/common-props.md)
| Property | Description | Type | Default | Version | [Global Config](../config-provider/docs.md#component-config) |
| --- | --- | --- | --- | --- | --- |
| allowClear | Show clear button | boolean \| &#123; clearIcon?: VueNode &#125; | false | - | ✓ |
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags` | boolean | true | - | × |
| defaultActiveFirstOption | Whether active first option by default | boolean | true | - | × |
| disabled | Whether disabled select | boolean | false | - | × |
| dropdownClassName | The className of dropdown menu, **Deprecated. Use `classes.popup.root` instead** | string | - | - | × |
| dropdownMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width, **Deprecated. Use `popupMatchSelectWidth` instead** | boolean \| number | - | - | × |
| dropdownRender | Customize dropdown content, **Deprecated. Use `popupRender` instead** | (originNode: VueNode) =&gt; VueNode | - | - | × |
| dropdownStyle | The style of dropdown menu, **Deprecated. Use `styles.popup.root` instead** | CSSProperties | - | - | × |
| fieldNames | Customize node label, value, options, groupLabel field name | object | &#123; label: 'label', value: 'value', options: 'options', groupLabel: 'label' &#125; | - | × |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| (inputValue: string, option?: Option) =&gt; boolean | true | - | × |
| filterSort | Sort function for search options sorting, see Array.sort's compareFunction | (optionA: Option, optionB: Option, info: &#123; searchValue: string &#125;) =&gt; number | - | - | × |
| getPopupContainer | Parent Node which the selector should be rendered to. Default to body. When position issues happen, try to modify it into scrollable content and position it relative | (triggerNode: HTMLElement) =&gt; HTMLElement | () =&gt; document.body | - | × |
| labelInValue | Whether to embed label in value, turn the format of value from `string` to `&#123; value: string, label: VueNode &#125;` | boolean | false | - | × |
| listHeight | Config popup height | number | 256 | - | × |
| loading | Indicate loading state | boolean | false | - | × |
| maxCount | The max number of items can be selected, only applies when `mode` is `multiple` or `tags` | number | - | - | × |
| maxTagCount | Max tag count to show. `responsive` will cost render performance | number \| 'responsive' | - | - | × |
| maxTagPlaceholder | Placeholder for not showing tags | VueNode \| (omittedValues: LabeledValue[]) =&gt; VueNode | - | - | × |
| maxTagTextLength | Max tag text length to show | number | - | - | × |
| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | VueNode | - | - | ✓ |
| mode | Set mode of Select | 'multiple' \| 'tags' | - | - | × |
| notFoundContent | Specify content to show when no result matches | VueNode | `Not Found` | - | × |
| open | Controlled open state of dropdown | boolean | - | - | × |
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true. If options is set, it should be set to label. When a string[] is provided, multiple fields are searched using OR matching | string \| string[] | value | - | × |
| options | Select options. Will get better perf than jsx definition | &#123; label: VueNode; value: string &#125;[] | - | - | × |
| optionRender | Customize the rendering dropdown options | (option: FlattenOptionData&lt;BaseOptionType&gt;, info: &#123; index: number &#125;) =&gt; VueNode | - | - | × |
| placeholder | The placeholder of select | string | - | - | × |
| placement | The position where the selection box pops up | `bottomLeft` `bottomRight` `topLeft` `topRight` | bottomLeft | - | × |
| popupClassName | The className of dropdown menu, use `classes.popup.root` instead | string | - | - | × |
| popupMatchSelectWidth | Determine whether the dropdown menu and the select input are the same width. Default set `min-width` same as input. Will ignore when value less than select width. `false` will disable virtual scroll | boolean \| number | true | - | × |
| popupRender | Customize dropdown content | (originNode: VueNode) =&gt; VueNode | - | - | × |
| prefix | The custom prefix | VueNode | - | - | × |
| removeIcon | The custom remove icon | VueNode | - | - | ✓ |
| searchValue | The current input "search" text | string | - | - | × |
| showSearch | Whether select is searchable | boolean \| Object | single: false, multiple: true | - | ✓ |
| size | Size of Select input | `large` \| `middle` \| `small` | - | - | × |
| status | Set validation status | 'error' \| 'warning' | - | - | × |
| suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | VueNode | `<DownOutlined />` | - | ✓ |
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) =&gt; VueNode | - | - | × |
| labelRender | Customize selected label render | (props: LabelInValueType) =&gt; VueNode | - | - | × |
| tokenSeparators | Separator used to tokenize, only applies when `mode="tags"` | string[] | - | - | × |
| value | Current selected option (considered as a immutable array), support `v-model:value` | string \| string[] \| number \| number[] | - | - | × |
| variant | Variants of selector | `outlined` \| `borderless` \| `filled` \| `underlined` | `outlined` | - | ✓ |
| virtual | Disable virtual scroll when set to false | boolean | true | - | × |
### Events
| Event | Description | Type | Version |
| --- | --- | --- | --- |
| active | Called when keyboard or mouse interaction occurs | (value: string \| number) =&gt; void | - |
| blur | Called when blur | (event: FocusEvent) =&gt; void | - |
| change | Called when select an option or input value change | (value, option: Option \| Array&lt;Option&gt;) =&gt; void | - |
| clear | Called when clear | () =&gt; void | - |
| deselect | Called when an option is deselected, param is the selected option's value. Only called for `multiple` or `tags`, effective in multiple or tags mode only | (value: string \| number) =&gt; void | - |
| dropdownVisibleChange | Called when dropdown open, **Deprecated. Use `openChange` instead** | (open: boolean) =&gt; void | - |
| focus | Called when focus | (event: FocusEvent) =&gt; void | - |
| inputKeydown | Called when key pressed | (event: KeyboardEvent) =&gt; void | - |
| openChange | Called when dropdown open | (open: boolean) =&gt; void | - |
| popupScroll | Called when dropdown scrolls | (event: UIEvent) =&gt; void | - |
| search | Callback function that is fired when input changed | (value: string) =&gt; void | - |
| select | Called when an option is selected, the params are option's value (or key) and option instance | (value: string \| number, option: Option) =&gt; void | - |
### Slots
| Slot | Description | Type | Version |
| --- | --- | --- | --- |
| labelRender | Customize selected label render | (props: LabelInValueType) =&gt; VueNode | - |
| maxTagPlaceholder | Placeholder for not showing tags | (omittedValues: LabeledValue[]) =&gt; VueNode | - |
| menuItemSelectedIcon | The custom menuItemSelected icon with multiple options | VueNode | - |
| notFoundContent | Specify content to show when no result matches | VueNode | - |
| optionRender | Customize the rendering dropdown options | (option: FlattenOptionData&lt;BaseOptionType&gt;, info: &#123; index: number &#125;) =&gt; VueNode | - |
| popupRender | Customize dropdown content | (originNode: VueNode) =&gt; VueNode | - |
| prefix | The custom prefix | VueNode | - |
| removeIcon | The custom remove icon | VueNode | - |
| suffixIcon | The custom suffix icon. Customize icon will not response click open to avoid icon designed to do other interactive. You can use `pointer-events: none` style to bypass | VueNode | - |
| tagRender | Customize tag render, only applies when `mode` is set to `multiple` or `tags` | (props) =&gt; VueNode | - |
### Select Methods
| Name | Description | Version |
| --- | --- | --- |
| blur() | Remove focus | - |
| focus() | Get focus | - |
### showSearch
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| autoClearSearchValue | Whether the current search will be cleared on selecting an item. Only applies when `mode` is set to `multiple` or `tags` | boolean | true | - |
| filterOption | If true, filter options by input, if function, filter options against it. The function will receive two arguments, `inputValue` and `option`, if the function returns true, the option will be included in the filtered set; Otherwise, it will be excluded | boolean \| (inputValue: string, option?: Option) =&gt; boolean | true | - |
| filterSort | Sort function for search options sorting, see Array.sort's compareFunction | (optionA: Option, optionB: Option, info: &#123; searchValue: string &#125;) =&gt; number | - | - |
| optionFilterProp | Which prop value of option will be used for filter if filterOption is true. If options is set, it should be set to label. When a string[] is provided, multiple fields are searched using OR matching | string \| string[] | value | - |
| searchValue | The current input "search" text | string | - | - |
| onSearch | Callback function that is fired when input changed | (value: string) =&gt; void | - | - |
### Option props
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| disabled | Disable this option | boolean | false | - |
| title | title attribute of Select Option | string | - | - |
| value | Default to filter with this property | string \| number | - | - |
### OptGroup props
| Property | Description | Type | Default | Version |
| --- | --- | --- | --- | --- |
| key | Group key | string | - | - |
| label | Group label | string \| VueNode | - | - |
| title | title attribute of Select Option | string | - | - |
## Semantic DOM
| _semantic | demo/_semantic.md |
## FAQ
### Why sometimes search will show 2 same option when in `tags` mode?
It's caused by option with different `label` and `value`. You can use `optionFilterProp="label"` to change filter logic instead.
### When I click elements in popupRender, the select dropdown will not be closed?
You can control it by `open` prop.
### I don't want dropdown close when click inside `popupRender`?
Select will close when it lose focus. You can prevent event to handle this:
```html
<a-select>
<template #popupRender>
<div
@mousedown="(e) => {
e.preventDefault()
e.stopPropagation()
}"
>
Some Content
</div>
</template>
</a-select>
```
### Why sometimes customize Option cause scroll break?
Virtual scroll internal set item height as `24px`. You need to adjust `listItemHeight` when your option height is less and `listHeight` config list container height:
```html
<a-select :list-item-height="10" :list-height="250" />
```
Note: `listItemHeight` and `listHeight` are internal props. Please only modify when necessary.
### Why a11y test report missing `aria-` props?
Select only create a11y auxiliary node when operating on. Please open Select and retry. For `aria-label` & `aria-labelledby` miss warning, please add related prop to Select with your own requirement.
Default virtual scrolling will create a mock element to simulate an accessible binding. If a screen reader needs to fully access the entire list, you can set `:virtual="false"` to disable virtual scrolling and the accessibility option will be bound to the actual element.
### Custom tags generated using `tagRender` slot will pop up a drop-down box when clicked to close
If you don't want a drop-down menu to appear automatically after clicking on an element (such as a close icon), you can prevent the `mousedown` event from propagating on it.
```html
<a-select>
<template #tagRender="{ closable, label, onClose }">
<span class="border">
&#123;&#123; label &#125;&#125;
<span
v-if="closable"
class="cursor-pointer"
@mousedown.stop
@click="onClose"
>
❎
</span>
</span>
</template>
</a-select>
```
@@ -0,0 +1,49 @@
# select Token
Use these variables through `theme.components.Select`. This document lists definitions only and does not include values.
```vue
<a-config-provider
:theme="{
token: {
// Global token configuration
},
components: {
Select: {
// Token name: value
},
},
}"
>
...
</a-config-provider>
```
## Token List
| Token | Type | Description |
| --- | --- | --- |
| `zIndexPopup` | `number` | z-index of dropdown |
| `optionSelectedColor` | `string` | Text color when option is selected |
| `optionSelectedFontWeight` | `CSSProperties` | Font weight when option is selected |
| `optionSelectedBg` | `string` | Background color when option is selected |
| `optionActiveBg` | `string` | Background color when option is active |
| `optionPadding` | `CSSProperties` | Padding of option |
| `optionFontSize` | `number` | Font size of option |
| `optionLineHeight` | `CSSProperties` | Line height of option |
| `optionHeight` | `number` | Height of option |
| `selectorBg` | `string` | Background color of selector |
| `clearBg` | `string` | Background color of clear button |
| `singleItemHeightLG` | `number` | Height of single selected item with large size |
| `showArrowPaddingInlineEnd` | `number` | Inline end padding of arrow |
| `hoverBorderColor` | `string` | Hover border color |
| `activeBorderColor` | `string` | Active border color |
| `activeOutlineColor` | `string` | Active outline color |
| `multipleItemBg` | `string` | Background color of multiple tag |
| `multipleItemBorderColor` | `string` | Border color of multiple tag |
| `multipleItemHeight` | `number` | Height of multiple tag |
| `multipleItemHeightSM` | `number` | Height of multiple tag with small size |
| `multipleItemHeightLG` | `number` | Height of multiple tag with large size |
| `multipleSelectorBgDisabled` | `string` | Background color of multiple selector when disabled |
| `multipleItemColorDisabled` | `string` | Text color of multiple tag when disabled |
| `multipleItemBorderColorDisabled` | `string` | Border color of multiple tag when disabled |