From bd7a0956fe22f153f135cfd0aaa8b3b1f6fe06e5 Mon Sep 17 00:00:00 2001 From: Dsaquel <291874700n@gmail.com> Date: Sun, 6 Jul 2025 20:45:46 +0200 Subject: [PATCH] chore: wip --- .../components/select/src/select-dropdown.vue | 5 +- .../components/select/src/useFlatSelect.ts | 47 +---- packages/components/select/src/useSelect.ts | 199 +++++++----------- 3 files changed, 89 insertions(+), 162 deletions(-) diff --git a/packages/components/select/src/select-dropdown.vue b/packages/components/select/src/select-dropdown.vue index 1caf974d93..c1de80f7e1 100644 --- a/packages/components/select/src/select-dropdown.vue +++ b/packages/components/select/src/select-dropdown.vue @@ -36,12 +36,13 @@ export default defineComponent({ const minWidth = ref('') function updateMinWidth() { - minWidth.value = `${select!.selectRef?.offsetWidth}px` } + minWidth.value = `${select!.selectRef?.offsetWidth}px` + } onMounted(() => { // TODO: updatePopper // popper.value.update() - if(select) { + if (select) { updateMinWidth() useResizeObserver(select.selectRef, updateMinWidth) } diff --git a/packages/components/select/src/useFlatSelect.ts b/packages/components/select/src/useFlatSelect.ts index ee20c8f105..7833140f0b 100644 --- a/packages/components/select/src/useFlatSelect.ts +++ b/packages/components/select/src/useFlatSelect.ts @@ -6,7 +6,6 @@ import { reactive, ref, watch, - watchEffect, } from 'vue' import { get, isEqual } from 'lodash-unified' import { @@ -37,19 +36,21 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => { const nsSelect = useNamespace('select') const states = reactive({ - inputValue: '', options: new Map(), cachedOptions: new Map(), optionValues: [], // sorted value of options selected: [], - selectionWidth: 0, collapseItemWidth: 0, - selectedLabel: '', hoveringIndex: -1, + + //TODO: maybe move this to useSelect + selectionWidth: 0, + selectedLabel: '', + inputValue: '', + isBeforeHide: false, previousQuery: null, inputHovering: false, menuVisibleOnFocus: false, - isBeforeHide: false, }) // template refs @@ -129,24 +130,6 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => { }) } - //TODO: maybe remove - //watch( - // () => props.modelValue, - // (val, oldVal) => { - // if (props.multiple) { - // if (props.filterable && !props.reserveKeyword) { - // states.inputValue = '' - // handleQueryChange('') - // } - // } - // setSelected() - // }, - // { - // flush: 'post', - // deep: true, - // } - //) - watch( // fix `Array.prototype.push/splice/..` cannot trigger non-deep watcher // https://github.com/vuejs/vue-next/issues/2116 @@ -167,17 +150,7 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => { } ) - watchEffect(() => { - // Anything could cause options changed, then update options - // If you want to control it by condition, write here - if (states.isBeforeHide) return - updateOptions() - }) - const handleQueryChange = (val: string) => { - //if (states.previousQuery === val || isComposing.value) { - // return - //} states.previousQuery = val if (props.filterable && isFunction(props.filterMethod)) { props.filterMethod(val) @@ -309,13 +282,7 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => { } else { emit(UPDATE_MODEL_EVENT, option.value) emitChange(option.value) - //TODO: handle in useSelect - //expanded.value = false } - //if (expanded.value) return - nextTick(() => { - scrollToOption(option) - }) } const getValueIndex = (arr: OptionValue[], option: OptionPublicInstance) => { @@ -397,6 +364,7 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => { optionsArray, filteredOptionsCount, handleOptionSelect, + handleQueryChange, // internal scrollToOption, hasModelValue, updateOptions, @@ -427,7 +395,6 @@ export const useFlatSelect = (props: SelectProps, emit: FlatSelectEmits) => { }) ) - //TODO: provide('flat-select', FLAT_SELECT_API) return FLAT_SELECT_API diff --git a/packages/components/select/src/useSelect.ts b/packages/components/select/src/useSelect.ts index ac591222c7..68a4cc278b 100644 --- a/packages/components/select/src/useSelect.ts +++ b/packages/components/select/src/useSelect.ts @@ -21,7 +21,6 @@ import { debugWarn, ensureArray, isArray, - isClient, isFunction, isIOS, isObject, @@ -58,8 +57,17 @@ import type { export const useSelect = (props: SelectProps, emit: SelectEmits) => { const FLAT_SELECT_API = useFlatSelect(props, emit) - const { t, contentId, nsSelect, setSelected, states, menuRef, scrollbarRef } = - FLAT_SELECT_API + const { + t, + contentId, + nsSelect, + setSelected, + states, + menuRef, + scrollbarRef, + handleOptionSelect: _handleOptionSelect, + handleQueryChange: _handleQueryChange, + } = FLAT_SELECT_API const nsInput = useNamespace('input') // template refs @@ -243,7 +251,6 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => { } } setSelected() - //TODO: maybe remove if (!isEqual(val, oldVal) && props.validateEvent) { formItem?.validate('change').catch((err) => debugWarn(err)) } @@ -268,26 +275,26 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => { } ) - watch( - // fix `Array.prototype.push/splice/..` cannot trigger non-deep watcher - // https://github.com/vuejs/vue-next/issues/2116 - () => states.options.entries(), - () => { - if (!isClient) return - // tooltipRef.value?.updatePopper?.() - setSelected() - if ( - props.defaultFirstOption && - (props.filterable || props.remote) && - filteredOptionsCount.value - ) { - checkDefaultFirstOption() - } - }, - { - flush: 'post', - } - ) + //watch( + // // fix `Array.prototype.push/splice/..` cannot trigger non-deep watcher + // // https://github.com/vuejs/vue-next/issues/2116 + // () => states.options.entries(), + // () => { + // if (!isClient) return + // // tooltipRef.value?.updatePopper?.() + // setSelected() + // if ( + // props.defaultFirstOption && + // (props.filterable || props.remote) && + // filteredOptionsCount.value + // ) { + // checkDefaultFirstOption() + // } + // }, + // { + // flush: 'post', + // } + //) //TODO: maybe delete watch([() => states.hoveringIndex, optionsArray], ([val]) => { @@ -308,62 +315,6 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => { updateOptions() }) - const handleQueryChange = (val: string) => { - if (states.previousQuery === val || isComposing.value) { - return - } - states.previousQuery = val - if (props.filterable && isFunction(props.filterMethod)) { - props.filterMethod(val) - } else if ( - props.filterable && - props.remote && - isFunction(props.remoteMethod) - ) { - props.remoteMethod(val) - } - if ( - props.defaultFirstOption && - (props.filterable || props.remote) && - filteredOptionsCount.value - ) { - nextTick(checkDefaultFirstOption) - } else { - nextTick(updateHoveringIndex) - } - } - - /** - * find and highlight first option as default selected - * @remark - * - if the first option in dropdown list is user-created, - * it would be at the end of the optionsArray - * so find it and set hover. - * (NOTE: there must be only one user-created option in dropdown list with query) - * - if there's no user-created option in list, just find the first one as usual - * (NOTE: exclude options that are disabled or in disabled-group) - */ - const checkDefaultFirstOption = () => { - const optionsInDropdown = optionsArray.value.filter( - (n) => n.visible && !n.disabled && !n.states.groupDisabled - ) - const userCreatedOption = optionsInDropdown.find((n) => n.created) - const firstOriginOption = optionsInDropdown[0] - const valueList = optionsArray.value.map((item) => item.value) - states.hoveringIndex = getValueIndex( - valueList, - userCreatedOption || firstOriginOption - ) - } - - const updateHoveringIndex = () => { - states.hoveringIndex = optionsArray.value.findIndex((item) => - states.selected.some( - (selected) => getValueKey(selected) === getValueKey(item) - ) - ) - } - const resetSelectionWidth = () => { states.selectionWidth = Number.parseFloat( window.getComputedStyle(selectionRef.value!).width @@ -462,46 +413,37 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => { focus() } - const handleOptionSelect = (option: OptionPublicInstance) => { - if (props.multiple) { - const value = ensureArray(props.modelValue ?? []).slice() - const optionIndex = getValueIndex(value, option) - if (optionIndex > -1) { - value.splice(optionIndex, 1) - } else if ( - props.multipleLimit <= 0 || - value.length < props.multipleLimit - ) { - value.push(option.value) - } - emit(UPDATE_MODEL_EVENT, value) - emitChange(value) - if (option.created) { - handleQueryChange('') - } - if (props.filterable && !props.reserveKeyword) { - states.inputValue = '' - } - } else { - emit(UPDATE_MODEL_EVENT, option.value) - emitChange(option.value) - expanded.value = false - } - focus() - if (expanded.value) return - nextTick(() => { - scrollToOption(option) - }) - } - - const getValueIndex = (arr: OptionValue[], option: OptionPublicInstance) => { - if (isUndefined(option)) return -1 - if (!isObject(option.value)) return arr.indexOf(option.value) - - return arr.findIndex((item) => { - return isEqual(get(item, props.valueKey), getValueKey(option)) - }) - } + //const handleOptionSelect = (option: OptionPublicInstance) => { + // if (props.multiple) { + // const value = ensureArray(props.modelValue ?? []).slice() + // const optionIndex = getValueIndex(value, option) + // if (optionIndex > -1) { + // value.splice(optionIndex, 1) + // } else if ( + // props.multipleLimit <= 0 || + // value.length < props.multipleLimit + // ) { + // value.push(option.value) + // } + // emit(UPDATE_MODEL_EVENT, value) + // emitChange(value) + // if (option.created) { + // handleQueryChange('') + // } + // if (props.filterable && !props.reserveKeyword) { + // states.inputValue = '' + // } + // } else { + // emit(UPDATE_MODEL_EVENT, option.value) + // emitChange(option.value) + // expanded.value = false + // } + // focus() + // if (expanded.value) return + // nextTick(() => { + // scrollToOption(option) + // }) + //} const scrollToOption = ( option: @@ -685,6 +627,23 @@ export const useSelect = (props: SelectProps, emit: SelectEmits) => { const collapseTagStyle = computed(() => { return { maxWidth: `${states.selectionWidth}px` } }) + function handleQueryChange(val: string) { + if (states.previousQuery === val || isComposing.value) { + return + } + _handleQueryChange(val) + } + + function handleOptionSelect(option: OptionPublicInstance) { + _handleOptionSelect(option) + if (!props.multiple) { + expanded.value = false + } + if (expanded.value) return + nextTick(() => { + scrollToOption(option) + }) + } useResizeObserver(selectionRef, resetSelectionWidth) useResizeObserver(menuRef, updateTooltip)