Enhance FormSelect component by adding search prompt for large lists and implementing debounced search functionality. Introduce new props for search configuration, improving user experience and performance during data selection.

This commit is contained in:
burakovec
2026-03-31 09:46:24 +03:00
parent a10f6997eb
commit 5557ecf52b

View File

@ -59,6 +59,12 @@
<span>Sonuç Bulunamadı</span> <span>Sonuç Bulunamadı</span>
</div> </div>
<template v-else> <template v-else>
<div class="form-select-list-item" v-if="showSearchPrompt">
<span>
İlk {{ selectList.length }} kayıt gösteriliyor. Daha fazla sonuç için en az
{{ props.minSearchLength }} harf yazın.
</span>
</div>
<div <div
class="form-select-list-item" class="form-select-list-item"
v-if="multiple" v-if="multiple"
@ -104,7 +110,7 @@
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, computed, onMounted, watch } from 'vue' import { ref, reactive, computed, onMounted, onUnmounted, watch } from 'vue'
import { useGlobalStore } from '@/stores/globalStore' import { useGlobalStore } from '@/stores/globalStore'
const globalStore = useGlobalStore() const globalStore = useGlobalStore()
@ -129,6 +135,10 @@
multipleText?: string multipleText?: string
view?: Function view?: Function
disabledClass?: string disabledClass?: string
largeListThreshold?: number
initialDisplayLimit?: number
minSearchLength?: number
searchDebounceMs?: number
} }
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
@ -139,7 +149,11 @@
clearable: false, clearable: false,
extraData: '', extraData: '',
multiple: false, multiple: false,
multipleText: 'Tümü' multipleText: 'Tümü',
largeListThreshold: 100,
initialDisplayLimit: 100,
minSearchLength: 2,
searchDebounceMs: 120
}) })
const emit = defineEmits(['update:modelValue', 'change', 'clear']) const emit = defineEmits(['update:modelValue', 'change', 'clear'])
@ -147,9 +161,11 @@
const activated = ref<Boolean>(false) const activated = ref<Boolean>(false)
const multipleAllSelected = ref<boolean>(false) const multipleAllSelected = ref<boolean>(false)
const q = ref<string>('') const q = ref<string>('')
const debouncedQuery = ref<string>('')
const rnd = ref<number>(Number(Math.floor(Math.random() * 1000000000))) const rnd = ref<number>(Number(Math.floor(Math.random() * 1000000000)))
const localValue = ref<string | number | boolean | null | string[]>() const localValue = ref<string | number | boolean | null | string[]>()
const selectedOption = ref<Record<string, any>>({}) const selectedOption = ref<Record<string, any>>({})
let searchDebounceTimer: ReturnType<typeof setTimeout> | undefined
if (props.multiple) { if (props.multiple) {
if (props.modelValue === null || props.modelValue === undefined) { if (props.modelValue === null || props.modelValue === undefined) {
@ -225,12 +241,24 @@
} }
}) })
const isLargeList = computed<boolean>(() => {
return props.listData.length > props.largeListThreshold
})
const showSearchPrompt = computed<boolean>(() => {
return isLargeList.value && debouncedQuery.value.length < props.minSearchLength
})
const selectList = computed<Record<string, any>[]>(() => { const selectList = computed<Record<string, any>[]>(() => {
if (q.value.length > 1) { if (debouncedQuery.value.length >= props.minSearchLength) {
return props.listData.filter((v: Record<string, any>) => { return props.listData.filter((v: Record<string, any>) => {
let val = props.listText !== undefined ? v[props.listText] : v let val = props.listText !== undefined ? v[props.listText] : v
return globalStore.trToUpper(val).includes(globalStore.trToUpper(q.value)) return globalStore
.trToUpper(String(val))
.includes(globalStore.trToUpper(debouncedQuery.value))
}) })
} else if (isLargeList.value) {
return props.listData.slice(0, props.initialDisplayLimit)
} else { } else {
return props.listData return props.listData
} }
@ -308,6 +336,7 @@
if (props.disabled) return if (props.disabled) return
e.stopPropagation() e.stopPropagation()
q.value = '' q.value = ''
debouncedQuery.value = ''
activated.value = true activated.value = true
setTimeout(() => { setTimeout(() => {
@ -368,6 +397,18 @@
SetSelectedOption() SetSelectedOption()
}) })
onUnmounted(() => {
if (searchDebounceTimer !== undefined) clearTimeout(searchDebounceTimer)
document.removeEventListener('click', closeList)
})
watch(q, (value: string) => {
if (searchDebounceTimer !== undefined) clearTimeout(searchDebounceTimer)
searchDebounceTimer = setTimeout(() => {
debouncedQuery.value = value.trim()
}, props.searchDebounceMs)
})
watch( watch(
() => props.invalidText, () => props.invalidText,
() => { () => {