mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
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:
@@ -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 () {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -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,
|
||||
{
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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>
|
||||
|
||||
82
packages/components/select-v2/src/useAllowCreate.ts
Normal file
82
packages/components/select-v2/src/useAllowCreate.ts
Normal 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,
|
||||
}
|
||||
}
|
||||
14
packages/components/select-v2/src/useOption.ts
Normal file
14
packages/components/select-v2/src/useOption.ts
Normal 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)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
Reference in New Issue
Block a user