feat(select-v2): support allow-create feature (#3017)

* feat(select-v2): support allow-create feature

support allow-create feature and refactored part of select-v2's code

* fix: optimized code
This commit is contained in:
msidolphin
2021-08-25 17:23:13 +08:00
committed by GitHub
parent 3aff7bbbd5
commit d6975c61df
18 changed files with 557 additions and 72 deletions

View File

@@ -20,6 +20,16 @@ const createData = (count = 1000) => {
}))
}
const clickClearButton = async wrapper => {
const select = wrapper.findComponent(Select)
const selectVm = select.vm as any
selectVm.states.comboBoxHovering = true
await nextTick
const clearBtn = wrapper.find(`.${selectVm.clearIcon}`)
expect(clearBtn.exists()).toBeTruthy()
await clearBtn.trigger('click')
}
interface SelectProps {
popperClass?: string
value?: string | string[] | number | number[]
@@ -29,6 +39,7 @@ interface SelectProps {
multiple?: boolean
filterable?: boolean
multipleLimit?: number
allowCreate?: boolean
popperAppendToBody?: boolean
placeholder?: string
[key: string]: any
@@ -64,6 +75,7 @@ const createSelect = (options: {
:multiple-limit="multipleLimit"
:popper-append-to-body="popperAppendToBody"
:placeholder="placeholder"
:allow-create="allowCreate"
@change="onChange"
@visible-change="onVisibleChange"
@remove-tah="onRemoveTag"
@@ -79,6 +91,7 @@ const createSelect = (options: {
options: createData(),
value: '',
popperClass: '',
allowCreate: false,
disabled: false,
clearable: false,
multiple: false,
@@ -313,13 +326,7 @@ describe('Select', () => {
const vm = wrapper.vm as any
vm.value = vm.options[1].value
await nextTick
const select = wrapper.findComponent(Select)
const selectVm = select.vm as any
selectVm.states.comboBoxHovering = true
await nextTick
const clearBtn = wrapper.find(`.${selectVm.clearIcon}`)
expect(clearBtn.exists()).toBeTruthy()
await clearBtn.trigger('click')
await clickClearButton(wrapper)
expect(vm.value).toBe('')
const placeholder = wrapper.find(`.${PLACEHOLDER_CLASS_NAME}`)
expect(placeholder.text()).toBe(DEFAULT_PLACEHOLDER)
@@ -492,6 +499,113 @@ describe('Select', () => {
})
})
describe('allow-create', () => {
it('single select', async() => {
const wrapper = createSelect({
data: () => {
return {
allowCreate: true,
filterable: true,
clearable: true,
options: [
{
value: '1',
label: 'option 1',
},
{
value: '2',
label: 'option 2',
},
{
value: '3',
label: 'option 3',
},
],
}
},
})
await nextTick
const vm = wrapper.vm as any
const input = wrapper.find('input')
await wrapper.trigger('click')
// create a new option
await input.trigger('compositionupdate', {
data: '1111',
})
const options = getOptions()
const select = wrapper.findComponent(Select)
const selectVm = select.vm as any
expect(selectVm.filteredOptions.length).toBe(1)
// selected the new option
await options[0].click()
expect(vm.value).toBe('1111')
// closed the menu
await wrapper.trigger('click')
expect(selectVm.filteredOptions.length).toBe(4)
selectVm.handleClear()
expect(selectVm.filteredOptions.length).toBe(3)
})
it('multiple', async () => {
const wrapper = createSelect({
data: () => {
return {
allowCreate: true,
filterable: true,
clearable: true,
multiple: true,
options: [
{
value: '1',
label: 'option 1',
},
{
value: '2',
label: 'option 2',
},
{
value: '3',
label: 'option 3',
},
],
}
},
})
await nextTick
const vm = wrapper.vm as any
await wrapper.trigger('click')
await wrapper.find('input').trigger('compositionupdate', {
data: '1111',
})
const options = getOptions()
const select = wrapper.findComponent(Select)
const selectVm = select.vm as any
expect(selectVm.filteredOptions.length).toBe(1)
// selected the new option
await options[0].click()
// closed the menu
await wrapper.trigger('click')
await wrapper.find('input').trigger('compositionupdate', {
data: '2222',
})
await getOptions()[0].click()
expect(JSON.stringify(vm.value)).toBe(JSON.stringify(['1111', '2222']))
await wrapper.trigger('click')
expect(selectVm.filteredOptions.length).toBe(5)
// remove tag
const tagCloseIcons = wrapper.findAll('.el-tag__close')
await tagCloseIcons[1].trigger('click')
expect(selectVm.filteredOptions.length).toBe(4)
// simulate backspace
await wrapper.find('input').trigger('keydown', {
key: EVENT_CODE.backspace,
})
expect(selectVm.filteredOptions.length).toBe(3)
})
})
it('render empty slot', async () => {
const wrapper = createSelect({
data () {

View File

@@ -77,3 +77,14 @@ export const SelectProps = {
default: 'value',
},
}
export const OptionProps = {
data: Array,
disabled: Boolean,
hovering: Boolean,
item: Object,
index: Number,
style: Object,
selected: Boolean,
created: Boolean,
}

View File

@@ -6,6 +6,7 @@
'el-select-dropdown__option-item': true,
'is-selected': selected,
'is-disabled': disabled,
'is-craeted': created,
'hover': hovering
}"
@mouseenter="hoverItem"
@@ -19,32 +20,16 @@
<script lang="ts">
import { defineComponent } from 'vue'
import { useOption } from './useOption'
import { OptionProps } from './defaults'
export default defineComponent({
props: {
data: Array,
disabled: Boolean,
hovering: Boolean,
item: Object,
index: Number,
style: Object,
selected: Boolean,
},
props: OptionProps,
emits: ['select', 'hover'],
setup(props, { emit }) {
// fill in hooks.
// change these to variables
const { hoverItem, selectOptionClick } = useOption(props, { emit })
return {
hoverItem: () => {
emit('hover', props.index)
},
selectOptionClick: () => {
if (!props.disabled) {
emit('select', props.item, props.index)
}
},
hoverItem,
selectOptionClick,
}
},
})

View File

@@ -93,7 +93,17 @@ export default defineComponent({
const isItemHovering = (target: number) => props.hoveringIndex === target
const scrollToItem = (index: number) => {
listRef.value.scrollToItem(index)
const list = listRef.value
if (list) {
listRef.value.scrollToItem(index)
}
}
const resetScrollTop = () => {
const list = listRef.value
if (list) {
listRef.value.resetScrollTop()
}
}
// computed
@@ -108,6 +118,7 @@ export default defineComponent({
isItemSelected,
scrollToItem,
resetScrollTop,
}
},
@@ -164,6 +175,7 @@ export default defineComponent({
...scoped,
selected,
disabled: item.disabled || itemDisabled,
created: !!item.created,
hovering: isItemHovering(index),
item,
onSelect,
@@ -176,7 +188,6 @@ export default defineComponent({
},
)
const List = h(
Comp,
{

View File

@@ -100,6 +100,7 @@
:aria-expanded="expanded"
:aria-labelledby="label"
class="el-select-v2__combobox-input"
:class="[selectSize ? `is-${ selectSize }` : '']"
:disabled="disabled"
role="combobox"
:readonly="!filterable"
@@ -227,7 +228,6 @@ import ElSelectMenu from './select-dropdown.vue'
import useSelect from './useSelect'
import { selectV2InjectionKey } from './token'
import { SelectProps } from './defaults'
export default defineComponent({
name: 'ElSelectV2',
components: {

View File

@@ -1,5 +1,5 @@
import type { ExtractPropTypes, InjectionKey } from 'vue'
import { SelectProps } from './defaults'
import { OptionProps, SelectProps } from './defaults'
import type { Option } from './select.types'
export interface SelectV2Context {
@@ -11,3 +11,5 @@ export interface SelectV2Context {
}
export const selectV2InjectionKey = 'ElSelectV2Injection' as any as InjectionKey<SelectV2Context>
export type IOptionProps = ExtractPropTypes<typeof OptionProps>
export type ISelectProps = ExtractPropTypes<typeof SelectProps>

View File

@@ -0,0 +1,82 @@
import { computed, ref } from 'vue'
import type { ISelectProps } from './token'
import type { Option } from './select.types'
export function useAllowCreate(props: ISelectProps, states) {
const createOptionCount = ref(0)
const cachedSelectedOption = ref<Option>(null)
const enableAllowCreateMode = computed(() => {
return props.allowCreate && props.filterable
})
function hasExistingOption(query: string) {
const hasValue = option => option.value === query
return props.options && props.options.some(hasValue) || states.createdOptions.some(hasValue)
}
function selectNewOption(option: Option) {
if (!enableAllowCreateMode.value) {
return
}
if (props.multiple && option.created) {
createOptionCount.value++
} else {
cachedSelectedOption.value = option
}
}
function createNewOption(query: string) {
if (enableAllowCreateMode.value) {
if (query && query.length > 0 && !hasExistingOption(query)) {
const newOption = {
value: query,
label: query,
created: true,
disabled: false,
}
if (states.createdOptions.length >= createOptionCount.value) {
states.createdOptions[createOptionCount.value] = newOption
} else {
states.createdOptions.push(newOption)
}
} else {
if (props.multiple) {
states.createdOptions.length = createOptionCount.value
} else {
const selectedOption = cachedSelectedOption.value
states.createdOptions.length = 0
if (selectedOption && selectedOption.created) {
states.createdOptions.push(selectedOption)
}
}
}
}
}
function removeNewOption(option: Option) {
if (!enableAllowCreateMode.value || !option || !option.created) {
return
}
const idx = states.createdOptions.findIndex(it => it.value === option.value)
if (~idx) {
states.createdOptions.splice(idx, 1)
createOptionCount.value--
}
}
function clearAllNewOption() {
if (enableAllowCreateMode.value) {
states.createdOptions.length = 0
createOptionCount.value = 0
}
}
return {
createNewOption,
removeNewOption,
selectNewOption,
clearAllNewOption,
}
}

View File

@@ -0,0 +1,14 @@
import type { IOptionProps } from './token'
export function useOption(props: IOptionProps, { emit }) {
return {
hoverItem: () => {
emit('hover', props.index)
},
selectOptionClick: () => {
if (!props.disabled) {
emit('select', props.item, props.index)
}
},
}
}

View File

@@ -24,6 +24,8 @@ import {
useGlobalConfig,
} from '@element-plus/utils/util'
import { useAllowCreate } from './useAllowCreate'
import { SelectProps } from './defaults'
import { flattenOptions } from './util'
@@ -154,7 +156,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
const selectSize = computed(() => props.size || elFormItem.size || $ELEMENT.size)
const collapseTagSize = computed(() => selectSize.value)
const collapseTagSize = computed(() => ['small', 'mini'].indexOf(selectSize.value) > -1 ? 'mini' : 'small')
const calculatePopperSize = () => {
popperSize.value = selectRef.value?.getBoundingClientRect?.()?.width || 200
@@ -207,6 +209,9 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
return -1
})
// hooks
const { createNewOption, removeNewOption, selectNewOption, clearAllNewOption } = useAllowCreate(props, states)
// methods
const focusAndUpdatePopup = () => {
inputRef.value.focus?.()
@@ -300,6 +305,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
emitChange(val)
}
// TODO 提取
const getValueIndex = (arr = [], value: unknown) => {
if (!isObject(value)) return arr.indexOf(value)
@@ -366,10 +372,11 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
...selectedOptions.slice(index + 1),
]
states.cachedOptions.splice(index, 1)
removeNewOption(option)
} else if (props.multipleLimit <= 0 || selectedOptions.length < props.multipleLimit) {
selectedOptions = [...selectedOptions, option.value]
states.cachedOptions.push(option)
selectNewOption(option)
}
update(selectedOptions)
if (option.created) {
@@ -392,6 +399,10 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
expanded.value = false
states.isComposing = false
states.isSilentBlur = byClick
selectNewOption(option)
if (!option.created) {
clearAllNewOption()
}
}
}
@@ -409,6 +420,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
emit('remove-tag', tag.value)
states.softFocus = true
nextTick(focusAndUpdatePopup)
removeNewOption(tag)
}
event.stopPropagation()
}
@@ -434,12 +446,6 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
}
const handleBlur = () => {
if (props.filterable) {
if (props.allowCreate) {
// create new item to the list
}
}
states.softFocus = false
// reset input value when blurred
@@ -475,7 +481,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
e.preventDefault()
const selected = (props.modelValue as Array<any>).slice()
selected.pop()
states.cachedOptions.pop()
removeNewOption(states.cachedOptions.pop())
update(selected)
}
}
@@ -497,6 +503,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
expanded.value = false
update(emptyValue)
emit('clear')
clearAllNewOption()
nextTick(focusAndUpdatePopup)
}
@@ -568,6 +575,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
resetInputHeight()
}
debouncedOnInputChange()
createNewOption(states.displayInputValue)
}
const onCompositionUpdate = (e: CompositionEvent) => {
@@ -630,6 +638,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
// the purpose of this function is to differ the blur event trigger mechanism
} else {
states.displayInputValue = ''
createNewOption('')
}
})
@@ -639,6 +648,11 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
deep: true,
})
// fix the problem that scrollTop is not reset in filterable mode
watch(filteredOptions, () => {
return nextTick(menuRef.value.resetScrollTop)
})
onMounted(() => {
initStates()
addResizeListener(selectRef.value, handleResize)

View File

@@ -332,6 +332,13 @@ const createList = ({
}
const resetScrollTop = () => {
const window = windowRef.value
if (window) {
window.scrollTop = 0
}
}
// life cycles
onMounted(() => {
if (isServer) return
@@ -401,6 +408,7 @@ const createList = ({
onWheel,
scrollTo,
scrollToItem,
resetScrollTop,
}
expose({
@@ -409,6 +417,7 @@ const createList = ({
getItemStyleCache,
scrollTo,
scrollToItem,
resetScrollTop,
states,
})

View File

@@ -37,7 +37,7 @@ const ScrollBar = defineComponent({
display: props.visible ? null : 'none',
position: 'absolute',
width: HORIZONTAL === props.layout ? '100%' : '6px',
height: HORIZONTAL === props.layout ? '6px' : '100%',
height: HORIZONTAL === props.layout ? '6px' : 'auto',
[ScrollbarDirKey[props.layout]]: '2px',
right: '2px',
bottom: '2px',

View File

@@ -27,7 +27,7 @@
@include b(select-dropdown__list) {
list-style: none;
margin: map.get($--select-dropdown, 'padding');
padding: 0;
margin: map.get($--select-dropdown, 'padding')!important;
padding: 0!important;
box-sizing: border-box;
}

View File

@@ -4,7 +4,7 @@
@import 'mixins/utils';
@import 'common/var';
$--input-inline-start: 7px !default;
$--input-inline-start: 15px !default;
@include b(select-v2) {
display: inline-block;
@@ -14,11 +14,7 @@ $--input-inline-start: 7px !default;
@include e(wrapper) {
box-sizing: border-box;
cursor: pointer;
padding-left: 15px;
padding-right: 30px;
padding-top: 1px;
padding-bottom: 1px;
padding: 5px 30px 5px 0px;
border: 1px solid var(--el-border-color-base);
border-radius: var(--el-border-radius-base);
transition: border-color var(--el-transition-duration-fast)
@@ -76,8 +72,8 @@ $--input-inline-start: 7px !default;
}
#{$selector}__input-wrapper input {
line-height: map.get($--input-height, 'medium');
height: map.get($--input-height, 'medium');
line-height: map.get($--input-height, 'medium') - 8px;
height: map.get($--input-height, 'medium') - 8px;
min-width: 4px;
width: 100%;
@@ -111,21 +107,59 @@ $--input-inline-start: 7px !default;
);
}
@each $size in (medium, small, mini) {
@include m($size) {
font-size: map.get($--input-font-size, $size);
@include e(wrapper) {
&,
#{$selector}__input-wrapper {
line-height: map.get($--input-height, $size);
}
@include m(medium) {
@include e(wrapper) {
padding: 3px 30px 3px 0px;
&, #{$selector}__input-wrapper {
line-height: map.get($--input-height, 'medium');
}
}
@include e(caret) {
line-height: map.get($--input-height, 'medium');
}
@include e(suffix) {
height: map.get($--input-height, 'medium');
}
}
#{$selector}__input-wrapper input {
line-height: map.get($--input-height, $size);
height: map.get($--input-height, $size);
@include m(small) {
@include e(wrapper) {
padding: 3px 30px 3px 0px;
line-height: map.get($--input-height, 'small');
#{$selector}__input-wrapper {
line-height: map.get($--input-height, 'small') - 8px;
input {
line-height: map.get($--input-height, 'small') - 8px;
height: map.get($--input-height, 'small') - 8px;
}
}
}
@include e(caret) {
line-height: map.get($--input-height, 'small');
}
@include e(suffix) {
height: map.get($--input-height, 'small');
}
}
@include m(mini) {
@include e(wrapper) {
padding: 1px 30px 1px 0px;
line-height: map.get($--input-height, 'mini');
#{$selector}__input-wrapper {
line-height: map.get($--input-height, 'mini') - 4px;
input {
line-height: map.get($--input-height, 'mini') - 4px;
height: map.get($--input-height, 'mini') - 4px;
}
}
}
@include e(caret) {
line-height: map.get($--input-height, 'mini');
}
@include e(suffix) {
height: map.get($--input-height, 'mini');
}
}
#{$selector}__selection > span {

View File

@@ -320,8 +320,48 @@ We can clear all the selected options at once, also applicable for single select
:::
### Create Option
Create and select new items that are not included in select options
:::demo By using the `allow-create` attribute, users can create new items by typing in the input box. Note that for `allow-create` to work, `filterable` must be `true`.
```html
<template>
<el-select-v2
v-model="value1"
:options="options"
placeholder="Please select"
style="width: 200px; margin-right: 16px; vertical-align: middle;"
allow-create
filterable
multiple
clearable
/>
<el-select-v2
v-model="value2"
:options="options"
placeholder="Please select"
style="width: 200px; vertical-align: middle;"
allow-create
filterable
clearable
/>
</template>
WIP👷
<script>
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
export default {
data() {
return {
options: Array.from({ length: 1000 }).map((_, idx) => ({
value: `Option ${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
})),
value1: [],
value2: '',
}
},
}
</script>
```
:::
### Remote search
@@ -350,6 +390,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus
| autocomplete | select input 的 autocomplete 属性 | string | — | off |
| placeholder | the autocomplete attribute of select input | string | — | Please select |
| filterable | is filterable | boolean | — | false |
| allow-create | whether creating new items is allowed. To use this, `filterable` must be true | boolean | — | false |
| no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data |
| popper-class | custom class name for Select's dropdown | string | — | — |
| popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false |

View File

@@ -319,9 +319,50 @@ We can clear all the selected options at once, also applicable for single select
:::
### Create Option
### Crear nuevos items
Crear y seleccionar nuevos items que no están incluidas en las opciones de selección.
WIP👷
:::demo Al utilizar el atributo `allow-create`, los usuarios pueden crear nuevos elementos escribiendo en el cuadro del input. Tenga en cuenta que para que `allow-create` funcione, `filterable` debe ser `true`.
```html
<template>
<el-select-v2
v-model="value1"
:options="options"
placeholder="Please select"
style="width: 200px; margin-right: 16px; vertical-align: middle;"
allow-create
filterable
multiple
clearable
/>
<el-select-v2
v-model="value2"
:options="options"
placeholder="Please select"
style="width: 200px; vertical-align: middle;"
allow-create
filterable
clearable
/>
</template>
<script>
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
export default {
data() {
return {
options: Array.from({ length: 1000 }).map((_, idx) => ({
value: `Option ${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
})),
value1: [],
value2: '',
}
},
}
</script>
```
:::
### Remote search
@@ -350,6 +391,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus
| autocomplete | select input 的 autocomplete 属性 | string | — | off |
| placeholder | the autocomplete attribute of select input | string | — | Please select |
| filterable | is filterable | boolean | — | false |
| allow-create | si esta permitido crear nuevos items. Para usar esto, `filterable` debe ser `true`. | boolean | — | false |
| no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data |
| popper-class | custom class name for Select's dropdown | string | — | — |
| popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false |

View File

@@ -319,9 +319,51 @@ We can clear all the selected options at once, also applicable for single select
:::
### Create Option
### Créer des options
WIP👷
Vous pouvez entrer des choix dans le champ de sélection qui ne sont pas incluses dans le menu.
:::demo En utilisant `allow-create`, peuvent créer de nouveaux choix en les entrant dans le champ d'input. Cette option ne marche que si `filterable` est activé.
```html
<template>
<el-select-v2
v-model="value1"
:options="options"
placeholder="Please select"
style="width: 200px; margin-right: 16px; vertical-align: middle;"
allow-create
filterable
multiple
clearable
/>
<el-select-v2
v-model="value2"
:options="options"
placeholder="Please select"
style="width: 200px; vertical-align: middle;"
allow-create
filterable
clearable
/>
</template>
<script>
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
export default {
data() {
return {
options: Array.from({ length: 1000 }).map((_, idx) => ({
value: `Option ${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
})),
value1: [],
value2: '',
}
},
}
</script>
```
:::
### Remote search
@@ -350,6 +392,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus
| autocomplete | select input 的 autocomplete 属性 | string | — | off |
| placeholder | the autocomplete attribute of select input | string | — | Please select |
| filterable | is filterable | boolean | — | false |
| allow-create | Si l'utilisateur peut créer des options. Dans ce cas `filterable` doit être activé. | boolean | — | false |
| no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data |
| popper-class | custom class name for Select's dropdown | string | — | — |
| popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false |

View File

@@ -319,9 +319,49 @@ We can clear all the selected options at once, also applicable for single select
:::
### Create Option
### 新規アイテムの作成
セレクトオプションに含まれないアイテムを新規に作成してセレクトする
:::demo `allow-create`属性を使うことで、ユーザは入力ボックスに入力することで新しいアイテムを作成することができます。なお、`allow-create` が動作するためには、`filterable``true` でなければならない。
```html
<template>
<el-select-v2
v-model="value1"
:options="options"
placeholder="Please select"
style="width: 200px; margin-right: 16px; vertical-align: middle;"
allow-create
filterable
multiple
clearable
/>
<el-select-v2
v-model="value2"
:options="options"
placeholder="Please select"
style="width: 200px; vertical-align: middle;"
allow-create
filterable
clearable
/>
</template>
WIP👷
<script>
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
export default {
data() {
return {
options: Array.from({ length: 1000 }).map((_, idx) => ({
value: `Option ${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
})),
value1: [],
value2: '',
}
},
}
</script>
```
:::
### Remote search
@@ -350,6 +390,7 @@ Some APIs are still undergoing (comparing to the non-virtualized select), becaus
| autocomplete | select input 的 autocomplete 属性 | string | — | off |
| placeholder | the autocomplete attribute of select input | string | — | Please select |
| filterable | is filterable | boolean | — | false |
| allow-create | 新しいアイテムの作成を許可するかどうかを指定します。これを使うには、`filterable` がtrueでなければなりません。 | boolean | — | false |
| no-data-text | displayed text when there is no options, you can also use slot empty | string | — | No Data |
| popper-class | custom class name for Select's dropdown | string | — | — |
| popper-append-to-body | whether to append the popper menu to body. If the positioning of the popper is wrong, you can try to set this prop to false | boolean | - | false |

View File

@@ -321,7 +321,48 @@
### 创建临时选项
WIP (该功能还在施工中👷‍♀️)
可以创建并选中选项中不存在的条目
:::demo 使用`allow-create`属性即可通过在输入框中输入文字来创建新的条目。注意此时`filterable`必须为真。
```html
<template>
<el-select-v2
v-model="value1"
:options="options"
placeholder="请选择"
style="width: 200px; margin-right: 16px; vertical-align: middle;"
allow-create
filterable
multiple
clearable
/>
<el-select-v2
v-model="value2"
:options="options"
placeholder="请选择"
style="width: 200px; vertical-align: middle;"
allow-create
filterable
clearable
/>
</template>
<script>
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
export default {
data() {
return {
options: Array.from({ length: 1000 }).map((_, idx) => ({
value: `选项${idx + 1}`,
label: `${initials[idx % 10]}${idx}`,
})),
value1: [],
value2: '',
}
},
}
</script>
```
:::
### 远程搜索
@@ -350,6 +391,7 @@ WIP (该功能还在施工中👷‍♀️)
| autocomplete | select input 的 autocomplete 属性 | string | — | off |
| placeholder | 占位符 | string | — | 请选择 |
| filterable | 是否可搜索 | boolean | — | false |
| allow-create | 是否允许用户创建新条目,需配合 `filterable` 使用 | boolean | — | false |
| no-data-text | 选项为空时显示的文字,也可以使用`#empty`设置 | string | — | 无数据 |
| popper-class | Select 下拉框的类名 | string | — | — |
| popper-append-to-body | 是否将弹出框插入至 body 元素。在弹出框的定位出现问题时,可将该属性设置为 false | boolean | - | false |