@saasmakers/ui 1.4.29 → 1.4.30

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.
@@ -15,9 +15,9 @@ export default {
15
15
  },
16
16
  hideNext: { control: 'boolean' },
17
17
  hidePrevious: { control: 'boolean' },
18
+ loading: { control: 'boolean' },
18
19
  margin: { control: 'number' },
19
20
  navigable: { control: 'boolean' },
20
- loading: { control: 'boolean' },
21
21
  size: {
22
22
  control: 'select',
23
23
  options: ['sm', 'base'] satisfies BaseDividerSize[],
@@ -1,5 +1,6 @@
1
1
  <script lang="ts" setup>
2
2
  import { vOnClickOutside } from '@vueuse/components'
3
+ import { refDebounced } from '@vueuse/core'
3
4
  import type { FieldSelect, FieldSelectOption } from '../../types/fields'
4
5
 
5
6
  const props = withDefaults(defineProps<Omit<FieldSelect, 'modelValue'>>(), {
@@ -18,6 +19,8 @@ const props = withDefaults(defineProps<Omit<FieldSelect, 'modelValue'>>(), {
18
19
  padding: true,
19
20
  placeholder: '',
20
21
  required: false,
22
+ searchable: false,
23
+ searchPlaceholder: '',
21
24
  size: 'base',
22
25
  validation: undefined,
23
26
  })
@@ -30,11 +33,19 @@ const emit = defineEmits<{
30
33
 
31
34
  const { getIcon } = useLayerIcons()
32
35
  const { fadeIn } = useMotion()
36
+ const { normalizeText } = useLayerUtils()
37
+ const { t } = useI18n()
33
38
  const id = useId()
34
39
 
35
40
  const modelValue = defineModel<FieldSelect['modelValue']>({ default: '' })
36
41
 
37
42
  const opened = ref(false)
43
+ const searchRaw = ref('')
44
+ const searchQuery = refDebounced(searchRaw, 250)
45
+
46
+ const searchInput = ref<{
47
+ focus: () => void
48
+ }>()
38
49
 
39
50
  const computedOptions = computed(() => {
40
51
  const options: FieldSelectOption[] = []
@@ -70,6 +81,35 @@ const selectedOption = computed(() => {
70
81
  })
71
82
  })
72
83
 
84
+ const filteredColumns = computed(() => {
85
+ const searchQueryCleaned = normalizeText(searchQuery.value.trim())
86
+
87
+ if (!props.searchable || !searchQueryCleaned) {
88
+ return computedColumns.value
89
+ }
90
+
91
+ return computedColumns.value
92
+ .map(column => ({
93
+ ...column,
94
+ options: column.options.filter(option =>
95
+ normalizeText(option.text).includes(searchQueryCleaned),
96
+ ),
97
+ }))
98
+ .filter(column => column.options.length > 0)
99
+ })
100
+
101
+ const hasFilteredResults = computed(() => {
102
+ return filteredColumns.value.some(column => column.options.length > 0)
103
+ })
104
+
105
+ watch(opened, (isOpened) => {
106
+ if (isOpened && props.searchable) {
107
+ nextTick(() => {
108
+ searchInput.value?.focus()
109
+ })
110
+ }
111
+ })
112
+
73
113
  function onClose() {
74
114
  reset()
75
115
  }
@@ -130,6 +170,7 @@ function onOptionKeyDown(event: KeyboardEvent) {
130
170
 
131
171
  function reset() {
132
172
  opened.value = false
173
+ searchRaw.value = ''
133
174
  }
134
175
 
135
176
  function selectOption(event: MouseEvent, value: string) {
@@ -248,7 +289,30 @@ function selectOption(event: MouseEvent, value: string) {
248
289
  }"
249
290
  >
250
291
  <div
251
- v-for="(column, columnIndex) in computedColumns"
292
+ v-if="searchable"
293
+ class="sticky top-0 z-10 border-b border-gray-200 bg-white dark:border-gray-800 dark:bg-gray-900"
294
+ @click.stop
295
+ >
296
+ <FieldInput
297
+ ref="searchInput"
298
+ v-model="searchRaw"
299
+ border="none"
300
+ class="px-3"
301
+ :placeholder="searchPlaceholder || t('search')"
302
+ size="sm"
303
+ type="search"
304
+ />
305
+ </div>
306
+
307
+ <div
308
+ v-if="searchable && !hasFilteredResults"
309
+ class="px-3 py-2 text-center text-gray-600 dark:text-gray-400"
310
+ >
311
+ {{ t('noResults') }}
312
+ </div>
313
+
314
+ <div
315
+ v-for="(column, columnIndex) in filteredColumns"
252
316
  :key="columnIndex"
253
317
  >
254
318
  <BaseIcon
@@ -298,3 +362,60 @@ function selectOption(event: MouseEvent, value: string) {
298
362
  />
299
363
  </div>
300
364
  </template>
365
+
366
+ <i18n lang="json">
367
+ {
368
+ "de": {
369
+ "noResults": "Keine Ergebnisse",
370
+ "search": "Suchen"
371
+ },
372
+ "en": {
373
+ "noResults": "No results",
374
+ "search": "Search"
375
+ },
376
+ "es": {
377
+ "noResults": "Sin resultados",
378
+ "search": "Buscar"
379
+ },
380
+ "fr": {
381
+ "noResults": "Aucun résultat",
382
+ "search": "Rechercher"
383
+ },
384
+ "it": {
385
+ "noResults": "Nessun risultato",
386
+ "search": "Cerca"
387
+ },
388
+ "ja": {
389
+ "noResults": "結果がありません",
390
+ "search": "検索"
391
+ },
392
+ "ko": {
393
+ "noResults": "결과 없음",
394
+ "search": "검색"
395
+ },
396
+ "nl": {
397
+ "noResults": "Geen resultaten",
398
+ "search": "Zoeken"
399
+ },
400
+ "pl": {
401
+ "noResults": "Brak wyników",
402
+ "search": "Szukaj"
403
+ },
404
+ "pt": {
405
+ "noResults": "Sem resultados",
406
+ "search": "Pesquisar"
407
+ },
408
+ "pt-BR": {
409
+ "noResults": "Nenhum resultado",
410
+ "search": "Pesquisar"
411
+ },
412
+ "id": {
413
+ "noResults": "Tidak ada hasil",
414
+ "search": "Cari"
415
+ },
416
+ "vi": {
417
+ "noResults": "Không có kết quả",
418
+ "search": "Tìm kiếm"
419
+ }
420
+ }
421
+ </i18n>
@@ -109,9 +109,9 @@ export interface BaseDivider {
109
109
  borderStyle?: BaseDividerBorderStyle
110
110
  hideNext?: boolean
111
111
  hidePrevious?: boolean
112
+ loading?: boolean
112
113
  margin?: number
113
114
  navigable?: boolean
114
- loading?: boolean
115
115
  size?: BaseDividerSize
116
116
  title?: string
117
117
  }
@@ -132,6 +132,8 @@ export interface FieldSelect {
132
132
  padding?: boolean
133
133
  placeholder?: string
134
134
  required?: boolean
135
+ searchable?: boolean
136
+ searchPlaceholder?: string
135
137
  size?: FieldSize
136
138
  validation?: VuelidateValidation
137
139
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@saasmakers/ui",
3
- "version": "1.4.29",
3
+ "version": "1.4.30",
4
4
  "private": false,
5
5
  "description": "Reusable Nuxt UI components for SaaS Makers projects",
6
6
  "license": "MIT",