mirror of
https://github.com/element-plus/element-plus.git
synced 2026-03-13 07:51:17 +08:00
feat(components): [select/select-v2] support debounce prop (#22471)
* feat(components): [select/select-v2] support debounce prop * feat: update * feat: update * feat: update * docs: update version number * feat: update * feat: update * feat: update * feat: update * feat: update * feat: update
This commit is contained in:
@@ -275,6 +275,7 @@ select-v2/custom-width
|
||||
| item-height | The height of the dropdown item | ^[number] | 34 |
|
||||
| scrollbar-always-on | Controls whether the scrollbar is always displayed | ^[boolean] | false |
|
||||
| remote | whether search data from server | ^[boolean] | false |
|
||||
| debounce ^(2.11.6) | debounce delay during remote search, in milliseconds | ^[number] | 300 |
|
||||
| remote-method | function that gets called when the input value changes. Its parameter is the current input value. To use this, `filterable` must be true | ^[Function]`(query: string) => void` | — |
|
||||
| validate-event | whether to trigger form validation | ^[boolean] | true |
|
||||
| offset ^(2.8.8) | offset of the dropdown | ^[number] | 12 |
|
||||
|
||||
@@ -223,6 +223,7 @@ select/custom-label
|
||||
| allow-create | whether creating new items is allowed. To use this, `filterable` must be true | ^[boolean] | false |
|
||||
| filter-method | custom filter method, the first parameter is the current input value. To use this, `filterable` must be true | ^[Function]`(query: string) => void` | — |
|
||||
| remote | whether options are loaded from server | ^[boolean] | false |
|
||||
| debounce ^(2.11.6) | debounce delay during remote search, in milliseconds | ^[number] | 300 |
|
||||
| remote-method | function that gets called when the input value changes. Its parameter is the current input value. To use this, `filterable` must be true | ^[Function]`(query: string) => void` | — |
|
||||
| remote-show-suffix | in remote search method show suffix icon | ^[boolean] | false |
|
||||
| loading | whether Select is loading data from server | ^[boolean] | false |
|
||||
|
||||
@@ -23,6 +23,15 @@ vi.mock('lodash-unified', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@vueuse/core', async () => {
|
||||
return {
|
||||
...((await vi.importActual('@vueuse/core')) as Record<string, any>),
|
||||
useDebounceFn: vi.fn((fn) => {
|
||||
return fn
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
const _mount = makeMountFunc({
|
||||
components: {
|
||||
'el-select': Select,
|
||||
@@ -66,6 +75,7 @@ interface SelectProps {
|
||||
allowCreate?: boolean
|
||||
popperAppendToBody?: boolean
|
||||
placeholder?: string
|
||||
debounce?: number
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
|
||||
@@ -216,6 +216,13 @@ export const selectV2Props = buildProps({
|
||||
* @description whether search data from server
|
||||
*/
|
||||
remote: Boolean,
|
||||
/**
|
||||
* @description debounce delay during remote search, in milliseconds
|
||||
*/
|
||||
debounce: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
/**
|
||||
* @description size of component
|
||||
*/
|
||||
|
||||
@@ -7,19 +7,15 @@ import {
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue'
|
||||
import {
|
||||
findLastIndex,
|
||||
get,
|
||||
isEqual,
|
||||
debounce as lodashDebounce,
|
||||
} from 'lodash-unified'
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { findLastIndex, get, isEqual } from 'lodash-unified'
|
||||
import { useDebounceFn, useResizeObserver } from '@vueuse/core'
|
||||
import {
|
||||
ValidateComponentsMap,
|
||||
debugWarn,
|
||||
escapeStringRegexp,
|
||||
getEventCode,
|
||||
isArray,
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isNumber,
|
||||
isObject,
|
||||
@@ -82,6 +78,7 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
|
||||
// data refs
|
||||
const popperSize = ref(-1)
|
||||
const debouncing = ref(false)
|
||||
|
||||
// DOM & Component refs
|
||||
const selectRef = ref<HTMLElement>()
|
||||
@@ -179,7 +176,7 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
return ValidateComponentsMap[validateState.value]
|
||||
})
|
||||
|
||||
const debounce = computed(() => (props.remote ? 300 : 0))
|
||||
const debounce = computed(() => (props.remote ? props.debounce : 0))
|
||||
|
||||
const isRemoteSearchEmpty = computed(
|
||||
() => props.remote && !states.inputValue && !hasOptions.value
|
||||
@@ -390,7 +387,11 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
|
||||
const dropdownMenuVisible = computed({
|
||||
get() {
|
||||
return expanded.value && (props.loading || !isRemoteSearchEmpty.value)
|
||||
return (
|
||||
expanded.value &&
|
||||
(props.loading || !isRemoteSearchEmpty.value) &&
|
||||
(!debouncing.value || !isEmpty(states.previousQuery))
|
||||
)
|
||||
},
|
||||
set(val: boolean) {
|
||||
expanded.value = val
|
||||
@@ -445,7 +446,10 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
})
|
||||
}
|
||||
|
||||
const debouncedOnInputChange = lodashDebounce(onInputChange, debounce.value)
|
||||
const debouncedOnInputChange = useDebounceFn(() => {
|
||||
onInputChange()
|
||||
debouncing.value = false
|
||||
}, debounce)
|
||||
|
||||
const handleQueryChange = (val: string) => {
|
||||
if (states.previousQuery === val || isComposing.value) {
|
||||
@@ -768,6 +772,7 @@ const useSelect = (props: SelectV2Props, emit: SelectV2EmitFn) => {
|
||||
const onInput = (event: Event) => {
|
||||
states.inputValue = (event.target as HTMLInputElement).value
|
||||
if (props.remote) {
|
||||
debouncing.value = true
|
||||
debouncedOnInputChange()
|
||||
} else {
|
||||
return onInputChange()
|
||||
|
||||
@@ -22,6 +22,15 @@ vi.mock('lodash-unified', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('@vueuse/core', async () => {
|
||||
return {
|
||||
...((await vi.importActual('@vueuse/core')) as Record<string, any>),
|
||||
useDebounceFn: vi.fn((fn) => {
|
||||
return fn
|
||||
}),
|
||||
}
|
||||
})
|
||||
|
||||
interface SelectProps {
|
||||
filterMethod?: any
|
||||
remoteMethod?: any
|
||||
@@ -38,6 +47,7 @@ interface SelectProps {
|
||||
defaultFirstOption?: boolean
|
||||
fitInputWidth?: boolean
|
||||
size?: 'small' | 'default' | 'large'
|
||||
debounce?: number
|
||||
}
|
||||
|
||||
const _mount = (template: string, data: any = () => ({}), otherObj?) =>
|
||||
|
||||
@@ -115,6 +115,13 @@ export const selectProps = buildProps({
|
||||
* @description whether options are loaded from server
|
||||
*/
|
||||
remote: Boolean,
|
||||
/**
|
||||
* @description debounce delay during remote search, in milliseconds
|
||||
*/
|
||||
debounce: {
|
||||
type: Number,
|
||||
default: 300,
|
||||
},
|
||||
/**
|
||||
* @description displayed text while loading data from server, default is 'Loading'
|
||||
*/
|
||||
|
||||
@@ -7,13 +7,8 @@ import {
|
||||
watch,
|
||||
watchEffect,
|
||||
} from 'vue'
|
||||
import {
|
||||
findLastIndex,
|
||||
get,
|
||||
isEqual,
|
||||
debounce as lodashDebounce,
|
||||
} from 'lodash-unified'
|
||||
import { useResizeObserver } from '@vueuse/core'
|
||||
import { findLastIndex, get, isEqual } from 'lodash-unified'
|
||||
import { useDebounceFn, useResizeObserver } from '@vueuse/core'
|
||||
import {
|
||||
ValidateComponentsMap,
|
||||
debugWarn,
|
||||
@@ -21,6 +16,7 @@ import {
|
||||
getEventCode,
|
||||
isArray,
|
||||
isClient,
|
||||
isEmpty,
|
||||
isFunction,
|
||||
isIOS,
|
||||
isNumber,
|
||||
@@ -97,6 +93,7 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
// the controller of the expanded popup
|
||||
const expanded = ref(false)
|
||||
const hoverOption = ref()
|
||||
const debouncing = ref(false)
|
||||
|
||||
const { form, formItem } = useFormItem()
|
||||
const { inputId } = useFormItemInputId(props, {
|
||||
@@ -170,7 +167,7 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
(ValidateComponentsMap[validateState.value] as Component)
|
||||
)
|
||||
|
||||
const debounce = computed(() => (props.remote ? 300 : 0))
|
||||
const debounce = computed(() => (props.remote ? props.debounce : 0))
|
||||
|
||||
const isRemoteSearchEmpty = computed(
|
||||
() => props.remote && !states.inputValue && states.options.size === 0
|
||||
@@ -248,7 +245,11 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
|
||||
const dropdownMenuVisible = computed({
|
||||
get() {
|
||||
return expanded.value && (props.loading || !isRemoteSearchEmpty.value)
|
||||
return (
|
||||
expanded.value &&
|
||||
(props.loading || !isRemoteSearchEmpty.value) &&
|
||||
(!debouncing.value || !isEmpty(states.previousQuery))
|
||||
)
|
||||
},
|
||||
set(val: boolean) {
|
||||
expanded.value = val
|
||||
@@ -490,15 +491,17 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => {
|
||||
const onInput = (event: Event) => {
|
||||
states.inputValue = (event.target as HTMLInputElement).value
|
||||
if (props.remote) {
|
||||
debouncing.value = true
|
||||
debouncedOnInputChange()
|
||||
} else {
|
||||
return onInputChange()
|
||||
}
|
||||
}
|
||||
|
||||
const debouncedOnInputChange = lodashDebounce(() => {
|
||||
const debouncedOnInputChange = useDebounceFn(() => {
|
||||
onInputChange()
|
||||
}, debounce.value)
|
||||
debouncing.value = false
|
||||
}, debounce)
|
||||
|
||||
const emitChange = (val: OptionValue | OptionValue[]) => {
|
||||
if (!isEqual(props.modelValue, val)) {
|
||||
|
||||
Reference in New Issue
Block a user