@saasmakers/ui 1.4.30 → 1.4.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<script lang="ts" setup>
|
|
2
|
+
import type { FieldInput } from '#components'
|
|
2
3
|
import { vOnClickOutside } from '@vueuse/components'
|
|
3
|
-
import { refDebounced } from '@vueuse/core'
|
|
4
4
|
import type { FieldSelect, FieldSelectOption } from '../../types/fields'
|
|
5
5
|
|
|
6
6
|
const props = withDefaults(defineProps<Omit<FieldSelect, 'modelValue'>>(), {
|
|
@@ -41,11 +41,16 @@ const modelValue = defineModel<FieldSelect['modelValue']>({ default: '' })
|
|
|
41
41
|
|
|
42
42
|
const opened = ref(false)
|
|
43
43
|
const searchRaw = ref('')
|
|
44
|
-
const searchQuery = refDebounced(searchRaw, 250)
|
|
45
44
|
|
|
46
|
-
const searchInput = ref<
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
const searchInput = ref<InstanceType<typeof FieldInput>>()
|
|
46
|
+
|
|
47
|
+
const computedColumns = computed(() => {
|
|
48
|
+
if (props.columns.length > 0) {
|
|
49
|
+
return props.columns
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return [{ options: computedOptions.value }]
|
|
53
|
+
})
|
|
49
54
|
|
|
50
55
|
const computedOptions = computed(() => {
|
|
51
56
|
const options: FieldSelectOption[] = []
|
|
@@ -67,33 +72,22 @@ const computedOptions = computed(() => {
|
|
|
67
72
|
return options
|
|
68
73
|
})
|
|
69
74
|
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
return props.columns
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return [{ options: computedOptions.value }]
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
const selectedOption = computed(() => {
|
|
79
|
-
return computedOptions.value.find((option) => {
|
|
80
|
-
return option.value === modelValue.value
|
|
81
|
-
})
|
|
75
|
+
const searchQueryCleaned = computed(() => {
|
|
76
|
+
return normalizeText(searchRaw.value.trim())
|
|
82
77
|
})
|
|
83
78
|
|
|
84
79
|
const filteredColumns = computed(() => {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (!props.searchable || !searchQueryCleaned) {
|
|
80
|
+
if (!props.searchable || !searchQueryCleaned.value) {
|
|
88
81
|
return computedColumns.value
|
|
89
82
|
}
|
|
90
83
|
|
|
91
84
|
return computedColumns.value
|
|
92
85
|
.map(column => ({
|
|
93
86
|
...column,
|
|
94
|
-
options: column.options.filter(option =>
|
|
95
|
-
normalizeText(option.text).includes(searchQueryCleaned)
|
|
96
|
-
|
|
87
|
+
options: column.options.filter((option) => {
|
|
88
|
+
return normalizeText(option.text).includes(searchQueryCleaned.value)
|
|
89
|
+
|| normalizeText(option.value).includes(searchQueryCleaned.value)
|
|
90
|
+
}),
|
|
97
91
|
}))
|
|
98
92
|
.filter(column => column.options.length > 0)
|
|
99
93
|
})
|
|
@@ -102,6 +96,16 @@ const hasFilteredResults = computed(() => {
|
|
|
102
96
|
return filteredColumns.value.some(column => column.options.length > 0)
|
|
103
97
|
})
|
|
104
98
|
|
|
99
|
+
const selectedOption = computed(() => {
|
|
100
|
+
return computedOptions.value.find((option) => {
|
|
101
|
+
return option.value === modelValue.value
|
|
102
|
+
})
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const showNoResults = computed(() => {
|
|
106
|
+
return props.searchable && !!searchQueryCleaned.value && !hasFilteredResults.value
|
|
107
|
+
})
|
|
108
|
+
|
|
105
109
|
watch(opened, (isOpened) => {
|
|
106
110
|
if (isOpened && props.searchable) {
|
|
107
111
|
nextTick(() => {
|
|
@@ -168,6 +172,12 @@ function onOptionKeyDown(event: KeyboardEvent) {
|
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
|
|
175
|
+
function onSearchKeyup(event: KeyboardEvent) {
|
|
176
|
+
if (event.key === 'Escape') {
|
|
177
|
+
reset()
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
171
181
|
function reset() {
|
|
172
182
|
opened.value = false
|
|
173
183
|
searchRaw.value = ''
|
|
@@ -296,16 +306,19 @@ function selectOption(event: MouseEvent, value: string) {
|
|
|
296
306
|
<FieldInput
|
|
297
307
|
ref="searchInput"
|
|
298
308
|
v-model="searchRaw"
|
|
309
|
+
:autocomplete="false"
|
|
310
|
+
background="white"
|
|
299
311
|
border="none"
|
|
300
312
|
class="px-3"
|
|
301
313
|
:placeholder="searchPlaceholder || t('search')"
|
|
302
|
-
size="
|
|
314
|
+
:size="size"
|
|
303
315
|
type="search"
|
|
316
|
+
@keyup="onSearchKeyup"
|
|
304
317
|
/>
|
|
305
318
|
</div>
|
|
306
319
|
|
|
307
320
|
<div
|
|
308
|
-
v-if="
|
|
321
|
+
v-if="showNoResults"
|
|
309
322
|
class="px-3 py-2 text-center text-gray-600 dark:text-gray-400"
|
|
310
323
|
>
|
|
311
324
|
{{ t('noResults') }}
|