chore: wip

This commit is contained in:
Dsaquel
2025-07-06 20:45:46 +02:00
parent 8740130eaa
commit bd7a0956fe
3 changed files with 89 additions and 162 deletions

View File

@@ -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)
}

View File

@@ -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<SelectStates>({
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

View File

@@ -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)