mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
feat(components): [select-v2] add props attribute (#14536)
* feat(components): [select-v2] add props attribute * docs: updata * docs: updata * docs: updata
This commit is contained in:
@@ -3,7 +3,7 @@ title: Virtualized Select
|
||||
lang: en-US
|
||||
---
|
||||
|
||||
# <ElBadge value="beta">Select V2 virtualized selector</ElBadge>
|
||||
# Select V2 virtualized selector ^(beta)
|
||||
|
||||
:::tip
|
||||
|
||||
@@ -131,19 +131,39 @@ select-v2/remote-search
|
||||
|
||||
:::
|
||||
|
||||
## use value-key
|
||||
## Use value-key attribute
|
||||
|
||||
:::demo when `options.value` is an object, you should set a unique identity key name for value
|
||||
when `options.value` is an object, you should set a unique identity key name for value
|
||||
|
||||
::: tip
|
||||
|
||||
Before ^(2.4.0), `value-key` was used both as the unique value of the selected object and as an alias for the value in `options`. Now `value-key` is only used as the unique value of the selected object, and the alias for the value in options is `props.value`.
|
||||
|
||||
:::
|
||||
|
||||
:::demo
|
||||
|
||||
select-v2/use-valueKey
|
||||
|
||||
:::
|
||||
|
||||
## Aliases for custom options ^(2.4.2)
|
||||
|
||||
When your `options` format is different from the default format, you can customize the alias of the `options` through the `props` attribute
|
||||
|
||||
:::demo
|
||||
|
||||
select-v2/props
|
||||
|
||||
:::
|
||||
|
||||
## SelectV2 Attributes
|
||||
|
||||
| Name | Description | Type | Accepted Values | Default |
|
||||
| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------- | --------------------------------------------------------------------------------------------------------- | ------------- |
|
||||
| model-value / v-model | biding value | string / number / boolean / object | — | — |
|
||||
| options | data of the options, the key of `value` and `label` can be customize by `props` | Array | — | — |
|
||||
| props ^(2.4.2) | configuration options, see the following table | object | — | — |
|
||||
| multiple | is multiple | boolean | — | false |
|
||||
| disabled | is disabled | boolean | — | false |
|
||||
| value-key | unique identity key name for value, required when value is an object | string | — | value |
|
||||
@@ -183,6 +203,15 @@ select-v2/use-valueKey
|
||||
| loading-text | 远程加载时显示的文字 | string | — | 加载中 | -->
|
||||
</span>
|
||||
|
||||
## props
|
||||
|
||||
| Attribute | Description | Type | Default |
|
||||
| --------- | --------------------------------------------------------------- | ------ | -------- |
|
||||
| value | specify which key of node object is used as the node's value | string | value |
|
||||
| label | specify which key of node object is used as the node's label | string | label |
|
||||
| options | specify which key of node object is used as the node's children | string | options |
|
||||
| disabled | specify which key of node object is used as the node's disabled | string | disabled |
|
||||
|
||||
## SelectV2 Events
|
||||
|
||||
| Name | Description | Params |
|
||||
|
||||
25
docs/examples/select-v2/props.vue
Normal file
25
docs/examples/select-v2/props.vue
Normal file
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<el-select-v2
|
||||
v-model="value"
|
||||
:options="options"
|
||||
:props="props"
|
||||
placeholder="Please select"
|
||||
style="width: 240px"
|
||||
filterable
|
||||
multiple
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue'
|
||||
const initials = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
|
||||
const props = {
|
||||
label: 'name',
|
||||
value: 'id',
|
||||
}
|
||||
const value = ref([])
|
||||
const options = Array.from({ length: 1000 }).map((_, idx) => ({
|
||||
id: `Option ${idx + 1}`,
|
||||
name: `${initials[idx % 10]}${idx}`,
|
||||
}))
|
||||
</script>
|
||||
@@ -1,26 +1,28 @@
|
||||
import { placements } from '@popperjs/core'
|
||||
import { definePropType, isValidComponentSize } from '@element-plus/utils'
|
||||
import { useSizeProp } from '@element-plus/hooks'
|
||||
import { buildProps, definePropType, iconPropType } from '@element-plus/utils'
|
||||
import { useTooltipContentProps } from '@element-plus/components/tooltip'
|
||||
import { CircleClose } from '@element-plus/icons-vue'
|
||||
import type { Component, PropType } from 'vue'
|
||||
import type { ComponentSize } from '@element-plus/constants'
|
||||
import type { OptionType } from './select.types'
|
||||
import { defaultProps } from './useProps'
|
||||
|
||||
import type { Option, OptionType } from './select.types'
|
||||
import type { Props } from './useProps'
|
||||
import type { Options, Placement } from '@element-plus/components/popper'
|
||||
|
||||
export const SelectProps = {
|
||||
export const SelectProps = buildProps({
|
||||
allowCreate: Boolean,
|
||||
autocomplete: {
|
||||
type: String as PropType<'none' | 'both' | 'list' | 'inline'>,
|
||||
type: definePropType<'none' | 'both' | 'list' | 'inline'>(String),
|
||||
default: 'none',
|
||||
},
|
||||
automaticDropdown: Boolean,
|
||||
clearable: Boolean,
|
||||
clearIcon: {
|
||||
type: [String, Object] as PropType<string | Component>,
|
||||
type: iconPropType,
|
||||
default: CircleClose,
|
||||
},
|
||||
effect: {
|
||||
type: String as PropType<'light' | 'dark' | string>,
|
||||
type: definePropType<'light' | 'dark' | string>(String),
|
||||
default: 'light',
|
||||
},
|
||||
collapseTags: Boolean,
|
||||
@@ -52,9 +54,11 @@ export const SelectProps = {
|
||||
loading: Boolean,
|
||||
loadingText: String,
|
||||
label: String,
|
||||
modelValue: [Array, String, Number, Boolean, Object] as PropType<
|
||||
any[] | string | number | boolean | Record<string, any> | any
|
||||
>,
|
||||
modelValue: {
|
||||
type: definePropType<
|
||||
any[] | string | number | boolean | Record<string, any> | any
|
||||
>([Array, String, Number, Boolean, Object]),
|
||||
},
|
||||
multiple: Boolean,
|
||||
multipleLimit: {
|
||||
type: Number,
|
||||
@@ -69,7 +73,7 @@ export const SelectProps = {
|
||||
default: true,
|
||||
},
|
||||
options: {
|
||||
type: Array as PropType<OptionType[]>,
|
||||
type: definePropType<OptionType[]>(Array),
|
||||
required: true,
|
||||
},
|
||||
placeholder: {
|
||||
@@ -85,13 +89,14 @@ export const SelectProps = {
|
||||
default: '',
|
||||
},
|
||||
popperOptions: {
|
||||
type: Object as PropType<Partial<Options>>,
|
||||
type: definePropType<Partial<Options>>(Object),
|
||||
default: () => ({} as Partial<Options>),
|
||||
},
|
||||
remote: Boolean,
|
||||
size: {
|
||||
type: String as PropType<ComponentSize>,
|
||||
validator: isValidComponentSize,
|
||||
size: useSizeProp,
|
||||
props: {
|
||||
type: definePropType<Props>(Object),
|
||||
default: () => defaultProps,
|
||||
},
|
||||
valueKey: {
|
||||
type: String,
|
||||
@@ -110,15 +115,18 @@ export const SelectProps = {
|
||||
values: placements,
|
||||
default: 'bottom-start',
|
||||
},
|
||||
}
|
||||
} as const)
|
||||
|
||||
export const OptionProps = {
|
||||
export const OptionProps = buildProps({
|
||||
data: Array,
|
||||
disabled: Boolean,
|
||||
hovering: Boolean,
|
||||
item: Object,
|
||||
item: {
|
||||
type: definePropType<Option>(Object),
|
||||
required: true,
|
||||
},
|
||||
index: Number,
|
||||
style: Object,
|
||||
selected: Boolean,
|
||||
created: Boolean,
|
||||
}
|
||||
} as const)
|
||||
|
||||
@@ -13,28 +13,33 @@
|
||||
@click.stop="selectOptionClick"
|
||||
>
|
||||
<slot :item="item" :index="index" :disabled="disabled">
|
||||
<span>{{ item.label }}</span>
|
||||
<span>{{ getLabel(item) }}</span>
|
||||
</slot>
|
||||
</li>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
// @ts-nocheck
|
||||
import { defineComponent } from 'vue'
|
||||
import { defineComponent, inject } from 'vue'
|
||||
import { useNamespace } from '@element-plus/hooks'
|
||||
import { useOption } from './useOption'
|
||||
import { useProps } from './useProps'
|
||||
import { OptionProps } from './defaults'
|
||||
import { selectV2InjectionKey } from './token'
|
||||
|
||||
export default defineComponent({
|
||||
props: OptionProps,
|
||||
emits: ['select', 'hover'],
|
||||
setup(props, { emit }) {
|
||||
const select = inject(selectV2InjectionKey)!
|
||||
const ns = useNamespace('select')
|
||||
const { hoverItem, selectOptionClick } = useOption(props, { emit })
|
||||
const { getLabel } = useProps(select.props)
|
||||
|
||||
return {
|
||||
ns,
|
||||
hoverItem,
|
||||
selectOptionClick,
|
||||
getLabel,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
@@ -17,6 +17,7 @@ import { useNamespace } from '@element-plus/hooks'
|
||||
import { EVENT_CODE } from '@element-plus/constants'
|
||||
import GroupItem from './group-item.vue'
|
||||
import OptionItem from './option-item.vue'
|
||||
import { useProps } from './useProps'
|
||||
|
||||
import { selectV2InjectionKey } from './token'
|
||||
|
||||
@@ -37,6 +38,8 @@ export default defineComponent({
|
||||
setup(props, { slots, expose }) {
|
||||
const select = inject(selectV2InjectionKey)!
|
||||
const ns = useNamespace('select')
|
||||
const { getLabel, getValue, getDisabled } = useProps(select.props)
|
||||
|
||||
const cachedHeights = ref<Array<number>>([])
|
||||
|
||||
const listRef = ref()
|
||||
@@ -92,9 +95,9 @@ export default defineComponent({
|
||||
|
||||
const isItemSelected = (modelValue: any[] | any, target: Option) => {
|
||||
if (select.props.multiple) {
|
||||
return contains(modelValue, target.value)
|
||||
return contains(modelValue, getValue(target))
|
||||
}
|
||||
return isEqual(modelValue, target.value)
|
||||
return isEqual(modelValue, getValue(target))
|
||||
}
|
||||
|
||||
const isItemDisabled = (modelValue: any[] | any, selected: boolean) => {
|
||||
@@ -159,7 +162,7 @@ export default defineComponent({
|
||||
<OptionItem
|
||||
{...itemProps}
|
||||
selected={isSelected}
|
||||
disabled={item.disabled || isDisabled}
|
||||
disabled={getDisabled(item) || isDisabled}
|
||||
created={!!item.created}
|
||||
hovering={isHovering}
|
||||
item={item}
|
||||
@@ -168,7 +171,7 @@ export default defineComponent({
|
||||
>
|
||||
{{
|
||||
default: (props: OptionItemProps) =>
|
||||
slots.default?.(props) || <span>{item.label}</span>,
|
||||
slots.default?.(props) || <span>{getLabel(item)}</span>,
|
||||
}}
|
||||
</OptionItem>
|
||||
)
|
||||
|
||||
@@ -1,25 +1,13 @@
|
||||
export type OptionCommon = {
|
||||
label: string
|
||||
}
|
||||
type OptionCommon = Record<string, any>
|
||||
|
||||
export type Option<T = any> = OptionCommon & {
|
||||
export type Option = OptionCommon & {
|
||||
created?: boolean
|
||||
value: T
|
||||
// reserve for option
|
||||
[prop: string]: any
|
||||
}
|
||||
|
||||
export type OptionGroup<T = any> = OptionCommon & {
|
||||
options: Array<T>
|
||||
export type OptionGroup = OptionCommon
|
||||
|
||||
// reserve for flexibility
|
||||
[prop: string]: any
|
||||
}
|
||||
export type OptionType = Option | OptionGroup
|
||||
|
||||
export type OptionType<T = any> = Option<T> | OptionGroup<T>
|
||||
|
||||
// maybe adding T for type restriction is better here, but not sure this is going to work for
|
||||
// template rendering
|
||||
export type OptionItemProps = {
|
||||
item: any
|
||||
index: number
|
||||
|
||||
@@ -43,11 +43,11 @@
|
||||
<template v-if="collapseTags && modelValue.length > 0">
|
||||
<div
|
||||
v-for="item in showTagList"
|
||||
:key="getValueKey(item)"
|
||||
:key="getValueKey(getValue(item))"
|
||||
:class="nsSelectV2.e('selected-item')"
|
||||
>
|
||||
<el-tag
|
||||
:closable="!selectDisabled && !item?.disable"
|
||||
:closable="!selectDisabled && !getDisabled(item)"
|
||||
:size="collapseTagSize"
|
||||
type="info"
|
||||
disable-transitions
|
||||
@@ -58,8 +58,9 @@
|
||||
:style="{
|
||||
maxWidth: `${tagMaxWidth}px`,
|
||||
}"
|
||||
>{{ item?.label }}</span
|
||||
>
|
||||
{{ getLabel(item) }}
|
||||
</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
<div :class="nsSelectV2.e('selected-item')">
|
||||
@@ -84,18 +85,21 @@
|
||||
:style="{
|
||||
maxWidth: `${tagMaxWidth}px`,
|
||||
}"
|
||||
>+ {{ modelValue.length - maxCollapseTags }}</span
|
||||
>
|
||||
+ {{ modelValue.length - maxCollapseTags }}
|
||||
</span>
|
||||
</template>
|
||||
<template #content>
|
||||
<div :class="nsSelectV2.e('selection')">
|
||||
<div
|
||||
v-for="selected in collapseTagList"
|
||||
:key="getValueKey(selected)"
|
||||
:key="getValueKey(getValue(selected))"
|
||||
:class="nsSelectV2.e('selected-item')"
|
||||
>
|
||||
<el-tag
|
||||
:closable="!selectDisabled && !selected.disabled"
|
||||
:closable="
|
||||
!selectDisabled && !getDisabled(selected)
|
||||
"
|
||||
:size="collapseTagSize"
|
||||
class="in-tooltip"
|
||||
type="info"
|
||||
@@ -107,8 +111,9 @@
|
||||
:style="{
|
||||
maxWidth: `${tagMaxWidth}px`,
|
||||
}"
|
||||
>{{ getLabel(selected) }}</span
|
||||
>
|
||||
{{ getLabel(selected) }}
|
||||
</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
@@ -120,8 +125,9 @@
|
||||
:style="{
|
||||
maxWidth: `${tagMaxWidth}px`,
|
||||
}"
|
||||
>+ {{ modelValue.length - maxCollapseTags }}</span
|
||||
>
|
||||
+ {{ modelValue.length - maxCollapseTags }}
|
||||
</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
@@ -129,11 +135,11 @@
|
||||
<template v-else>
|
||||
<div
|
||||
v-for="selected in states.cachedOptions"
|
||||
:key="getValueKey(selected)"
|
||||
:key="getValueKey(getValue(selected))"
|
||||
:class="nsSelectV2.e('selected-item')"
|
||||
>
|
||||
<el-tag
|
||||
:closable="!selectDisabled && !selected.disabled"
|
||||
:closable="!selectDisabled && !getDisabled(selected)"
|
||||
:size="collapseTagSize"
|
||||
type="info"
|
||||
disable-transitions
|
||||
@@ -144,8 +150,9 @@
|
||||
:style="{
|
||||
maxWidth: `${tagMaxWidth}px`,
|
||||
}"
|
||||
>{{ getLabel(selected) }}</span
|
||||
>
|
||||
{{ getLabel(selected) }}
|
||||
</span>
|
||||
</el-tag>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -7,7 +7,7 @@ export interface SelectV2Context {
|
||||
props: ExtractPropTypes<typeof SelectProps>
|
||||
expanded: boolean
|
||||
popper: Ref<TooltipInstance>
|
||||
onSelect: (option: Option<any>, index: number, byClick?: boolean) => void
|
||||
onSelect: (option: Option, index: number, byClick?: boolean) => void
|
||||
onHover: (idx: number) => void
|
||||
onKeyboardNavigate: (direction: 'forward' | 'backward') => void
|
||||
onKeyboardSelect: () => void
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
// @ts-nocheck
|
||||
import { computed, ref } from 'vue'
|
||||
import { useProps } from './useProps'
|
||||
import type { ISelectProps } from './token'
|
||||
import type { Option } from './select.types'
|
||||
|
||||
export function useAllowCreate(props: ISelectProps, states) {
|
||||
const { aliasProps, getLabel, getValue } = useProps(props)
|
||||
|
||||
const createOptionCount = ref(0)
|
||||
const cachedSelectedOption = ref<Option>(null)
|
||||
|
||||
@@ -12,7 +15,7 @@ export function useAllowCreate(props: ISelectProps, states) {
|
||||
})
|
||||
|
||||
function hasExistingOption(query: string) {
|
||||
const hasValue = (option) => option.value === query
|
||||
const hasValue = (option) => getValue(option) === query
|
||||
return (
|
||||
(props.options && props.options.some(hasValue)) ||
|
||||
states.createdOptions.some(hasValue)
|
||||
@@ -34,10 +37,10 @@ export function useAllowCreate(props: ISelectProps, states) {
|
||||
if (enableAllowCreateMode.value) {
|
||||
if (query && query.length > 0 && !hasExistingOption(query)) {
|
||||
const newOption = {
|
||||
value: query,
|
||||
label: query,
|
||||
[aliasProps.value.value]: query,
|
||||
[aliasProps.value.label]: query,
|
||||
created: true,
|
||||
disabled: false,
|
||||
[aliasProps.value.disabled]: false,
|
||||
}
|
||||
if (states.createdOptions.length >= createOptionCount.value) {
|
||||
states.createdOptions[createOptionCount.value] = newOption
|
||||
@@ -65,12 +68,12 @@ export function useAllowCreate(props: ISelectProps, states) {
|
||||
!option.created ||
|
||||
(option.created &&
|
||||
props.reserveKeyword &&
|
||||
states.inputValue === option.label)
|
||||
states.inputValue === getLabel(option))
|
||||
) {
|
||||
return
|
||||
}
|
||||
const idx = states.createdOptions.findIndex(
|
||||
(it) => it.value === option.value
|
||||
(it) => getValue(it) === getValue(option)
|
||||
)
|
||||
if (~idx) {
|
||||
states.createdOptions.splice(idx, 1)
|
||||
|
||||
36
packages/components/select-v2/src/useProps.ts
Normal file
36
packages/components/select-v2/src/useProps.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { computed } from 'vue'
|
||||
import { get } from 'lodash-unified'
|
||||
|
||||
import type { ISelectProps } from './token'
|
||||
import type { Option } from './select.types'
|
||||
|
||||
export interface Props {
|
||||
label?: string
|
||||
value?: string
|
||||
disabled?: string
|
||||
options?: string
|
||||
}
|
||||
|
||||
export const defaultProps: Required<Props> = {
|
||||
label: 'label',
|
||||
value: 'value',
|
||||
disabled: 'disabled',
|
||||
options: 'options',
|
||||
}
|
||||
|
||||
export function useProps(props: Pick<ISelectProps, 'props'>) {
|
||||
const aliasProps = computed(() => ({ ...defaultProps, ...props.props }))
|
||||
|
||||
const getLabel = (option: Option) => get(option, aliasProps.value.label)
|
||||
const getValue = (option: Option) => get(option, aliasProps.value.value)
|
||||
const getDisabled = (option: Option) => get(option, aliasProps.value.disabled)
|
||||
const getOptions = (option: Option) => get(option, aliasProps.value.options)
|
||||
|
||||
return {
|
||||
aliasProps,
|
||||
getLabel,
|
||||
getValue,
|
||||
getDisabled,
|
||||
getOptions,
|
||||
}
|
||||
}
|
||||
@@ -14,14 +14,13 @@ import { useFormItem, useFormSize } from '@element-plus/components/form'
|
||||
|
||||
import { ArrowUp } from '@element-plus/icons-vue'
|
||||
import { useAllowCreate } from './useAllowCreate'
|
||||
|
||||
import { flattenOptions } from './util'
|
||||
|
||||
import { useInput } from './useInput'
|
||||
import { useProps } from './useProps'
|
||||
|
||||
import type { CSSProperties } from 'vue'
|
||||
import type ElTooltip from '@element-plus/components/tooltip'
|
||||
import type { SelectProps } from './defaults'
|
||||
import type { CSSProperties, ExtractPropTypes } from 'vue'
|
||||
import type { Option, OptionType } from './select.types'
|
||||
import type { ISelectProps } from './token'
|
||||
|
||||
const DEFAULT_INPUT_PLACEHOLDER = ''
|
||||
const MINIMUM_INPUT_WIDTH = 11
|
||||
@@ -31,12 +30,13 @@ const TAG_BASE_WIDTH = {
|
||||
small: 33,
|
||||
}
|
||||
|
||||
const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
const useSelect = (props: ISelectProps, emit) => {
|
||||
// inject
|
||||
const { t } = useLocale()
|
||||
const nsSelectV2 = useNamespace('select-v2')
|
||||
const nsInput = useNamespace('input')
|
||||
const { form: elForm, formItem: elFormItem } = useFormItem()
|
||||
const { getLabel, getValue, getDisabled, getOptions } = useProps(props)
|
||||
|
||||
const states = reactive({
|
||||
inputValue: DEFAULT_INPUT_PLACEHOLDER,
|
||||
@@ -143,46 +143,49 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
const query = states.inputValue
|
||||
// when query was given, we should test on the label see whether the label contains the given query
|
||||
const regexp = new RegExp(escapeStringRegexp(query), 'i')
|
||||
const containsQueryString = query ? regexp.test(o.label || '') : true
|
||||
const containsQueryString = query ? regexp.test(getLabel(o) || '') : true
|
||||
return containsQueryString
|
||||
}
|
||||
if (props.loading) {
|
||||
return []
|
||||
}
|
||||
return flattenOptions(
|
||||
(props.options as OptionType[])
|
||||
.concat(states.createdOptions)
|
||||
.map((v) => {
|
||||
if (isArray(v.options)) {
|
||||
const filtered = v.options.filter(isValidOption)
|
||||
if (filtered.length > 0) {
|
||||
return {
|
||||
...v,
|
||||
options: filtered,
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (props.remote || isValidOption(v as Option)) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return null
|
||||
})
|
||||
.filter((v) => v !== null)
|
||||
)
|
||||
|
||||
return [...props.options, ...states.createdOptions].reduce((all, item) => {
|
||||
const options = getOptions(item)
|
||||
|
||||
if (isArray(options)) {
|
||||
const filtered = options.filter(isValidOption)
|
||||
|
||||
if (filtered.length > 0) {
|
||||
all.push(
|
||||
{
|
||||
label: getLabel(item),
|
||||
isTitle: true,
|
||||
type: 'Group',
|
||||
},
|
||||
...filtered,
|
||||
{ type: 'Group' }
|
||||
)
|
||||
}
|
||||
} else if (props.remote || isValidOption(item)) {
|
||||
all.push(item)
|
||||
}
|
||||
|
||||
return all
|
||||
}, []) as OptionType[]
|
||||
})
|
||||
|
||||
const filteredOptionsValueMap = computed(() => {
|
||||
const valueMap = new Map()
|
||||
|
||||
filteredOptions.value.forEach((option, index) => {
|
||||
valueMap.set(getValueKey(option.value), { option, index })
|
||||
valueMap.set(getValueKey(getValue(option)), { option, index })
|
||||
})
|
||||
return valueMap
|
||||
})
|
||||
|
||||
const optionsAllDisabled = computed(() =>
|
||||
filteredOptions.value.every((option) => option.disabled)
|
||||
filteredOptions.value.every((option) => getDisabled(option))
|
||||
)
|
||||
|
||||
const selectSize = useFormSize()
|
||||
@@ -348,7 +351,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
const update = (val: any) => {
|
||||
emit(UPDATE_MODEL_EVENT, val)
|
||||
emitChange(val)
|
||||
states.previousValue = val?.toString()
|
||||
states.previousValue = String(val)
|
||||
}
|
||||
|
||||
const getValueIndex = (arr = [], value: unknown) => {
|
||||
@@ -371,12 +374,6 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
return isObject(item) ? get(item, props.valueKey) : item
|
||||
}
|
||||
|
||||
// if the selected item is item then we get label via indexing
|
||||
// otherwise it should be string we simply return the item itself.
|
||||
const getLabel = (item: unknown) => {
|
||||
return isObject(item) ? item.label : item
|
||||
}
|
||||
|
||||
const resetInputHeight = () => {
|
||||
return nextTick(() => {
|
||||
if (!inputRef.value) return
|
||||
@@ -409,7 +406,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
if (props.multiple) {
|
||||
let selectedOptions = (props.modelValue as any[]).slice()
|
||||
|
||||
const index = getValueIndex(selectedOptions, option.value)
|
||||
const index = getValueIndex(selectedOptions, getValue(option))
|
||||
if (index > -1) {
|
||||
selectedOptions = [
|
||||
...selectedOptions.slice(0, index),
|
||||
@@ -421,7 +418,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
props.multipleLimit <= 0 ||
|
||||
selectedOptions.length < props.multipleLimit
|
||||
) {
|
||||
selectedOptions = [...selectedOptions, option.value]
|
||||
selectedOptions = [...selectedOptions, getValue(option)]
|
||||
states.cachedOptions.push(option)
|
||||
selectNewOption(option)
|
||||
updateHoveringIndex(idx)
|
||||
@@ -444,8 +441,8 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
setSoftFocus()
|
||||
} else {
|
||||
selectedIndex.value = idx
|
||||
states.selectedLabel = option.label
|
||||
update(option.value)
|
||||
states.selectedLabel = getLabel(option)
|
||||
update(getValue(option))
|
||||
expanded.value = false
|
||||
states.isComposing = false
|
||||
states.isSilentBlur = byClick
|
||||
@@ -460,7 +457,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
const deleteTag = (event: MouseEvent, option: Option) => {
|
||||
let selectedOptions = (props.modelValue as any[]).slice()
|
||||
|
||||
const index = getValueIndex(selectedOptions, option.value)
|
||||
const index = getValueIndex(selectedOptions, getValue(option))
|
||||
|
||||
if (index > -1 && !selectDisabled.value) {
|
||||
selectedOptions = [
|
||||
@@ -469,7 +466,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
]
|
||||
states.cachedOptions.splice(index, 1)
|
||||
update(selectedOptions)
|
||||
emit('remove-tag', option.value)
|
||||
emit('remove-tag', getValue(option))
|
||||
states.softFocus = true
|
||||
removeNewOption(option)
|
||||
return nextTick(focusAndUpdatePopup)
|
||||
@@ -589,7 +586,7 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
}
|
||||
}
|
||||
const option = options[newIndex]
|
||||
if (option.disabled || option.type === 'Group') {
|
||||
if (getDisabled(option) || option.type === 'Group') {
|
||||
// prevent dispatching multiple nextTick callbacks.
|
||||
return onKeyboardNavigate(direction, newIndex)
|
||||
} else {
|
||||
@@ -697,10 +694,10 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
const options = filteredOptions.value
|
||||
const selectedItemIndex = options.findIndex(
|
||||
(option) =>
|
||||
getValueKey(option.value) === getValueKey(props.modelValue)
|
||||
getValueKey(getValue(option)) === getValueKey(props.modelValue)
|
||||
)
|
||||
if (~selectedItemIndex) {
|
||||
states.selectedLabel = options[selectedItemIndex].label
|
||||
states.selectedLabel = getLabel(options[selectedItemIndex])
|
||||
updateHoveringIndex(selectedItemIndex)
|
||||
} else {
|
||||
states.selectedLabel = `${props.modelValue}`
|
||||
@@ -823,6 +820,8 @@ const useSelect = (props: ExtractPropTypes<typeof SelectProps>, emit) => {
|
||||
debouncedOnInputChange,
|
||||
deleteTag,
|
||||
getLabel,
|
||||
getValue,
|
||||
getDisabled,
|
||||
getValueKey,
|
||||
handleBlur,
|
||||
handleClear,
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { isArray } from '@vue/shared'
|
||||
|
||||
import type { Option, OptionGroup } from './select.types'
|
||||
|
||||
export const flattenOptions = (options: Array<Option | OptionGroup>) => {
|
||||
const flattened = []
|
||||
options.forEach((option) => {
|
||||
if (isArray(option.options)) {
|
||||
flattened.push({
|
||||
label: option.label,
|
||||
isTitle: true,
|
||||
type: 'Group',
|
||||
})
|
||||
|
||||
option.options.forEach((o: Option) => {
|
||||
flattened.push(o)
|
||||
})
|
||||
flattened.push({
|
||||
type: 'Group',
|
||||
})
|
||||
} else {
|
||||
flattened.push(option)
|
||||
}
|
||||
})
|
||||
|
||||
return flattened
|
||||
}
|
||||
Reference in New Issue
Block a user