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:
@ -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,
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user