@usssa/component-library 1.0.0-alpha.1 → 1.0.0-alpha.100

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.
Files changed (128) hide show
  1. package/README.md +18 -37
  2. package/package.json +17 -6
  3. package/src/assets/files.png +0 -0
  4. package/src/assets/logo.svg +19 -0
  5. package/src/assets/no-result.png +0 -0
  6. package/src/assets/quasar-logo-vertical.svg +15 -0
  7. package/src/components/ComponentLink.vue +26 -0
  8. package/src/components/core/UAvatar.vue +179 -0
  9. package/src/components/core/UAvatarGroup.vue +120 -0
  10. package/src/components/core/UBadgeStd.vue +91 -0
  11. package/src/components/core/UBannerStd.vue +142 -0
  12. package/src/components/core/UBreadCrumbs.vue +67 -0
  13. package/src/components/core/UBtnIcon.vue +158 -0
  14. package/src/components/core/UBtnStd.vue +129 -0
  15. package/src/components/core/UBtnToggle.vue +68 -0
  16. package/src/components/core/UCheckboxStd.vue +95 -0
  17. package/src/components/core/UChip.vue +251 -0
  18. package/src/components/core/UDialogStd.vue +244 -0
  19. package/src/components/core/UDrawer.vue +235 -0
  20. package/src/components/core/UInnerLoader.vue +58 -0
  21. package/src/components/core/UInputAddressLookup.vue +470 -0
  22. package/src/components/core/UInputPhoneStd.vue +299 -0
  23. package/src/components/core/UInputTextStd.vue +299 -0
  24. package/src/components/core/UInputTypeaheadAdvanceSearch.vue +59 -0
  25. package/src/components/core/UMenuButtonStd.vue +274 -0
  26. package/src/components/core/UMenuDropdown.vue +72 -0
  27. package/src/components/core/UMenuDropdownAdvancedSearch.vue +301 -0
  28. package/src/components/core/UMenuItem.vue +134 -0
  29. package/src/components/core/UMenuSearch.vue +752 -0
  30. package/src/components/core/UMultiSelectStd.vue +274 -0
  31. package/src/components/core/UPagination.vue +104 -0
  32. package/src/components/core/URadioBtn.vue +116 -0
  33. package/src/components/core/URadioStd.vue +62 -0
  34. package/src/components/core/USelectStd.vue +250 -0
  35. package/src/components/core/UTabBtnStd.vue +176 -0
  36. package/src/components/core/UTable/UTable.vue +93 -0
  37. package/src/components/core/UTable/UTd.vue +63 -0
  38. package/src/components/core/UTable/UTh.vue +48 -0
  39. package/src/components/core/UTable/UTr.vue +20 -0
  40. package/src/components/core/UTableStd.vue +1003 -0
  41. package/src/components/core/UTabsStd.vue +111 -0
  42. package/src/components/core/UToggleStd.vue +159 -0
  43. package/src/components/core/UToolbar.vue +94 -0
  44. package/src/components/core/UTooltip.vue +73 -0
  45. package/src/components/core/UUploader.vue +497 -0
  46. package/src/components/index.js +75 -0
  47. package/src/composables/useNotify.js +79 -0
  48. package/src/composables/useOverlayLoader.js +23 -0
  49. package/src/css/app.sass +159 -0
  50. package/src/css/colors.sass +101 -0
  51. package/src/css/media.sass +1 -0
  52. package/src/css/quasar.variables.sass +121 -0
  53. package/src/css/typography.sass +0 -0
  54. package/src/css/vars/colors.variables.sass +126 -0
  55. package/src/utils/data.ts +146 -0
  56. package/dist/spa/assets/Avatar.45667392.js +0 -9
  57. package/dist/spa/assets/AvatarGroup.99557443.js +0 -9
  58. package/dist/spa/assets/AvatarGroup.bb4cd03a.css +0 -1
  59. package/dist/spa/assets/BadgeStd.ee4129c5.js +0 -6
  60. package/dist/spa/assets/BannerPage.6c6a98c4.js +0 -10
  61. package/dist/spa/assets/BannerPage.d195fcac.css +0 -1
  62. package/dist/spa/assets/BtnIcon.89e50d2e.css +0 -1
  63. package/dist/spa/assets/BtnIcon.934f8c5b.js +0 -10
  64. package/dist/spa/assets/BtnStd.95fc4510.js +0 -11
  65. package/dist/spa/assets/CheckBox.3c204a4b.css +0 -1
  66. package/dist/spa/assets/CheckBox.fbc0bd1a.js +0 -7
  67. package/dist/spa/assets/Chip.49ffc857.js +0 -11
  68. package/dist/spa/assets/Chip.74acaa33.css +0 -1
  69. package/dist/spa/assets/ComponentBase.1ae19b7e.js +0 -1
  70. package/dist/spa/assets/ErrorNotFound.6cccfaee.js +0 -1
  71. package/dist/spa/assets/IndexPage.17d09b76.js +0 -1
  72. package/dist/spa/assets/InputText.19c0d7d1.css +0 -1
  73. package/dist/spa/assets/InputText.3fcd97a4.js +0 -20
  74. package/dist/spa/assets/KFOkCnqEu92Fr1MmgVxIIzQ.34e9582c.woff +0 -0
  75. package/dist/spa/assets/KFOlCnqEu92Fr1MmEU9fBBc-.9ce7f3ac.woff +0 -0
  76. package/dist/spa/assets/KFOlCnqEu92Fr1MmSU5fBBc-.bf14c7d7.woff +0 -0
  77. package/dist/spa/assets/KFOlCnqEu92Fr1MmWUlfBBc-.e0fd57c0.woff +0 -0
  78. package/dist/spa/assets/KFOlCnqEu92Fr1MmYUtfBBc-.f6537e32.woff +0 -0
  79. package/dist/spa/assets/KFOmCnqEu92Fr1Mu4mxM.f2abf7fb.woff +0 -0
  80. package/dist/spa/assets/MainLayout.912bb0f8.js +0 -1
  81. package/dist/spa/assets/MultiSelectStd.644b108c.js +0 -17
  82. package/dist/spa/assets/MultiSelectStd.c7af49eb.css +0 -1
  83. package/dist/spa/assets/QAvatar.5940a79e.js +0 -1
  84. package/dist/spa/assets/QCheckbox.48b43f7d.js +0 -1
  85. package/dist/spa/assets/QPage.ca395ec4.js +0 -1
  86. package/dist/spa/assets/QRadio.0a7f5e2c.js +0 -1
  87. package/dist/spa/assets/QResizeObserver.30c3566c.js +0 -1
  88. package/dist/spa/assets/QSelect.a40eb786.js +0 -1
  89. package/dist/spa/assets/QTabs.72f2507e.js +0 -1
  90. package/dist/spa/assets/QToggle.376c48be.js +0 -1
  91. package/dist/spa/assets/QTooltip.5aa8e325.js +0 -1
  92. package/dist/spa/assets/Radio.0ac35288.js +0 -7
  93. package/dist/spa/assets/Radio.aca54a89.css +0 -1
  94. package/dist/spa/assets/SelectStd.5c6891db.css +0 -1
  95. package/dist/spa/assets/SelectStd.daf1def9.js +0 -16
  96. package/dist/spa/assets/TabButtonPage.20e871dd.js +0 -7
  97. package/dist/spa/assets/TabButtonPage.36ce9f9c.css +0 -1
  98. package/dist/spa/assets/TabsPage.9b94ff39.css +0 -1
  99. package/dist/spa/assets/TabsPage.b92cccee.js +0 -9
  100. package/dist/spa/assets/TogglePage.8129b631.css +0 -1
  101. package/dist/spa/assets/TogglePage.ba6e325c.js +0 -9
  102. package/dist/spa/assets/UAvatar.dda7e478.js +0 -1
  103. package/dist/spa/assets/UAvatar.ea8daa38.css +0 -1
  104. package/dist/spa/assets/UBadgeStd.3965db9a.css +0 -1
  105. package/dist/spa/assets/UBadgeStd.e165ee05.js +0 -1
  106. package/dist/spa/assets/UBtnStd.979ec4e8.js +0 -1
  107. package/dist/spa/assets/UBtnStd.bdd4f5e5.css +0 -1
  108. package/dist/spa/assets/UTooltip.d2e04d0c.js +0 -1
  109. package/dist/spa/assets/UTooltip.efa029a7.css +0 -1
  110. package/dist/spa/assets/dom.e9d4ad51.js +0 -1
  111. package/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.fd84f88b.woff +0 -0
  112. package/dist/spa/assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.4a4dbc62.woff2 +0 -0
  113. package/dist/spa/assets/format.41663636.js +0 -1
  114. package/dist/spa/assets/index.43c62a18.js +0 -21
  115. package/dist/spa/assets/index.4fbd73cf.css +0 -5
  116. package/dist/spa/assets/option-sizes.10cc02d3.js +0 -1
  117. package/dist/spa/assets/render.e67ff27a.js +0 -1
  118. package/dist/spa/assets/tooltip.7c0d0774.js +0 -8
  119. package/dist/spa/assets/touch.9135741d.js +0 -1
  120. package/dist/spa/assets/use-checkbox.44a623c0.js +0 -1
  121. package/dist/spa/favicon.ico +0 -0
  122. package/dist/spa/icons/caret-down.svg +0 -5
  123. package/dist/spa/icons/circle-xmark.svg +0 -6
  124. package/dist/spa/icons/favicon-128x128.png +0 -0
  125. package/dist/spa/icons/favicon-16x16.png +0 -0
  126. package/dist/spa/icons/favicon-32x32.png +0 -0
  127. package/dist/spa/icons/favicon-96x96.png +0 -0
  128. package/dist/spa/index.html +0 -3
@@ -0,0 +1,752 @@
1
+ <script setup>
2
+ import { computed, ref, toRaw, watch } from 'vue'
3
+ import UAvatar from './UAvatar.vue'
4
+ import UBtnStd from './UBtnStd.vue'
5
+ import UInputTypeaheadAdvanceSearch from './UInputTypeaheadAdvanceSearch.vue'
6
+ import UMenuDropdownAdvancedSearch from './UMenuDropdownAdvancedSearch.vue'
7
+ import UTabBtnStd from './UTabBtnStd.vue'
8
+ import UTooltip from './UTooltip.vue'
9
+ import { useNotify } from '../../composables/useNotify'
10
+ import { categorizeObjects, deepEqual, parseInitials } from '../../utils/data'
11
+
12
+ const emit = defineEmits([
13
+ 'onInvitePerson',
14
+ 'onItemActionClick',
15
+ 'updateCountry',
16
+ 'updateModelVal',
17
+ 'updateTab',
18
+ ])
19
+
20
+ const loading = defineModel('loading')
21
+
22
+ const props = defineProps({
23
+ // Label for button to select a record
24
+ actionButtonLabel: {
25
+ type: String,
26
+ },
27
+ actionButtonLoading: {
28
+ type: Boolean,
29
+ },
30
+ // Set default tab
31
+ advancedSearchSelectedTab: {
32
+ type: String,
33
+ },
34
+ algoliaIndex: {
35
+ type: Object,
36
+ },
37
+ // Set context for component for special handling
38
+ // context.action (e.g. addContact)
39
+ // context.data (e.g. array of existing contacts)
40
+ context: {
41
+ type: Array,
42
+ },
43
+ disabledButtonColor: {
44
+ type: String,
45
+ },
46
+ disableActionButtonLabel: {
47
+ type: String,
48
+ default: '',
49
+ },
50
+ exceedLimitCount: {
51
+ type: Number,
52
+ default: 50,
53
+ },
54
+ fields: {
55
+ type: Array,
56
+ },
57
+ headerLabel: {
58
+ type: String,
59
+ default: "I'm looking for",
60
+ },
61
+ labelIcon: {
62
+ type: String,
63
+ default: 'fa-kit-duotone fa-filter-search',
64
+ },
65
+ menuClass: {
66
+ type: String,
67
+ },
68
+ model: {
69
+ type: Object,
70
+ },
71
+ options: {
72
+ type: Array,
73
+ },
74
+ recentSearches: {
75
+ type: Array,
76
+ },
77
+ recentSearchesLoading: {
78
+ type: Boolean,
79
+ },
80
+ searchText: {
81
+ type: String,
82
+ },
83
+ // Show/hide advanced search
84
+ showAdvancedSearch: {
85
+ type: Boolean,
86
+ default: true,
87
+ },
88
+ // Show/hide entity tabs in advanced search
89
+ showAdvancedSearchTabs: {
90
+ type: Boolean,
91
+ default: true,
92
+ },
93
+ showCustomMenu: {
94
+ type: Boolean,
95
+ default: false,
96
+ },
97
+ showInviteBtn: {
98
+ type: Boolean,
99
+ default: false,
100
+ },
101
+ size: {
102
+ type: String,
103
+ default: 'md',
104
+ },
105
+ // Show/hide recent searches
106
+ showRecentSelected: {
107
+ type: Boolean,
108
+ default: true,
109
+ },
110
+ //Advance search title
111
+ title: {
112
+ type: String,
113
+ default: 'Advanced search',
114
+ },
115
+ toolTipDisabledButton: {
116
+ type: String,
117
+ },
118
+ })
119
+
120
+ const { notify } = useNotify()
121
+
122
+ const advancedSearchApplyFilter = ref(false)
123
+ const advancedSearchFilterDirty = ref(false)
124
+ const advancedSearchFilterModelDefault = ref({})
125
+ const advancedSearchSelectedTab = ref(props.advancedSearchSelectedTab ?? null)
126
+ const exceedLimit = ref(false)
127
+ const searchMenuRef = ref(null)
128
+ const searchMenuShowing = ref(false)
129
+ const searchResults = ref(null)
130
+ const selectedItem = ref({})
131
+ const toggle = ref(false)
132
+
133
+ const recentSearches = computed({
134
+ get() {
135
+ let arr = props.recentSearches
136
+ if (props.context) {
137
+ arr = specialContextHandler(arr)
138
+ }
139
+ return arr
140
+ },
141
+ set(value) {
142
+ return value
143
+ },
144
+ })
145
+
146
+ const convertDate = (inputDate) => {
147
+ const [month, day, year] = inputDate.split('/')
148
+ return `${year}-${month}-${day}`
149
+ }
150
+
151
+ const handleTabChange = (val) => {
152
+ if (advancedSearchSelectedTab.value === val) {
153
+ advancedSearchSelectedTab.value = null
154
+ } else {
155
+ advancedSearchSelectedTab.value = val
156
+ }
157
+ Object.assign(
158
+ props.model,
159
+ structuredClone(toRaw(advancedSearchFilterModelDefault.value))
160
+ )
161
+ emit('updateTab', advancedSearchSelectedTab.value)
162
+ search()
163
+ }
164
+
165
+ const onApplyAdvancedSearchFilter = () => {
166
+ advancedSearchApplyFilter.value = true
167
+ advancedSearchFilterDirty.value = false
168
+
169
+ search()
170
+ updateAdvanceSearchToggle()
171
+ }
172
+
173
+ const onClearAdvancedSearchFilter = () => {
174
+ Object.assign(
175
+ props.model,
176
+ structuredClone(toRaw(advancedSearchFilterModelDefault.value))
177
+ )
178
+ advancedSearchApplyFilter.value = false
179
+ advancedSearchFilterDirty.value = false
180
+ search()
181
+ }
182
+
183
+ const onInvitePerson = (item) => {
184
+ emit('onInvitePerson', item)
185
+ }
186
+
187
+ const onItemActionClick = (item, isRecentSearchAction) => {
188
+ selectedItem.value = item
189
+ return emit(
190
+ 'onItemActionClick',
191
+ item,
192
+ isRecentSearchAction,
193
+ searchMenuRef.value
194
+ )
195
+ }
196
+
197
+ const onMenuHide = () => {
198
+ searchMenuShowing.value = false
199
+ }
200
+
201
+ const onMenuShow = () => {
202
+ searchMenuShowing.value = true
203
+ }
204
+
205
+ const search = async () => {
206
+ try {
207
+ if (props.searchText.length < 2) {
208
+ searchResults.value = null
209
+ return void 0
210
+ }
211
+
212
+ loading.value = true
213
+ const configPayload = {}
214
+ const selectedTab = advancedSearchSelectedTab.value
215
+ if (selectedTab) {
216
+ configPayload.facetFilters = [`collection:${selectedTab}`]
217
+ } else {
218
+ let advancedSearchTabOptions = props.options.map((option) => {
219
+ return `collection:${option.id}`
220
+ })
221
+ configPayload.facetFilters = [advancedSearchTabOptions]
222
+ }
223
+
224
+ if (advancedSearchApplyFilter.value) {
225
+ const filters = props.model
226
+ let algoliaFilter = ''
227
+
228
+ const phoneNumber = filters?.selectedCountry?.code + filters?.phoneNumber
229
+
230
+ Object.keys(filters).map((key, index) => {
231
+ let filterValue = filters[key]
232
+
233
+ if (key === 'phoneNumber' && filterValue) {
234
+ filterValue = phoneNumber
235
+ } else if (key === 'startDate' && filterValue) {
236
+ filterValue = convertDate(filters.startDate)
237
+ } else if (key === 'endDate' && filterValue) {
238
+ filterValue = convertDate(filters.endDate)
239
+ }
240
+
241
+ switch (key) {
242
+ case 'city':
243
+ if (selectedTab === 'events' || selectedTab === 'venues') {
244
+ key = 'city'
245
+ } else {
246
+ key = 'address.city'
247
+ }
248
+ break
249
+ case 'state':
250
+ if (selectedTab === 'events' || selectedTab === 'venues') {
251
+ key = 'state'
252
+ } else {
253
+ key = 'address.state'
254
+ }
255
+ break
256
+ case 'postalCode':
257
+ if (selectedTab === 'venues') {
258
+ key = 'postalCode'
259
+ } else {
260
+ key = 'address.postalCode'
261
+ }
262
+ break
263
+ case 'selectedCountry':
264
+ key = 'address.country'
265
+ break
266
+ case 'emailAddress':
267
+ key = 'email'
268
+ break
269
+ case 'teamName':
270
+ key = 'name'
271
+ break
272
+ case 'phoneNumber':
273
+ key = 'mobileNumber'
274
+ break
275
+ case 'venueName':
276
+ key = 'name'
277
+ break
278
+ case 'eventName':
279
+ key = 'name'
280
+ break
281
+ case 'startDate':
282
+ key = 'startsOn'
283
+ break
284
+ case 'endDate':
285
+ key = 'endsOn'
286
+ break
287
+ case 'venueAddress':
288
+ key = 'streetAddress'
289
+ break
290
+ default:
291
+ key = key
292
+ }
293
+
294
+ if (filterValue && filterValue.length) {
295
+ algoliaFilter.length
296
+ ? (algoliaFilter += ` AND ${key}:${filterValue}`)
297
+ : (algoliaFilter += `${key}:"${filterValue}"`)
298
+ }
299
+ })
300
+
301
+ if (algoliaFilter.length) {
302
+ configPayload.filters = algoliaFilter
303
+ }
304
+ }
305
+
306
+ const res = await props.algoliaIndex.search(props.searchText, configPayload)
307
+
308
+ if (res.hits.length >= props.exceedLimitCount) {
309
+ exceedLimit.value = true
310
+ } else {
311
+ exceedLimit.value = false
312
+ }
313
+
314
+ let results = res.hits
315
+
316
+ if (props.context) {
317
+ results = specialContextHandler(results)
318
+ }
319
+
320
+ results = categorizeObjects(results, 'collection')
321
+
322
+ if (Object.keys(results).length) {
323
+ searchResults.value = results
324
+ } else {
325
+ searchResults.value = null
326
+ }
327
+ return void 0
328
+ } catch (error) {
329
+ notify({
330
+ type: 'accent',
331
+ label: 'Search broke',
332
+ position: 'top',
333
+ })
334
+ } finally {
335
+ loading.value = false
336
+ }
337
+ }
338
+
339
+ const specialContextHandler = (results) => {
340
+ // Mark result records that are already exits
341
+ results = results.map((result) => {
342
+ const isAlreadyAddedSearch = props.context.filter(
343
+ (data) => data.id === result.id || data.id === result.docId
344
+ )
345
+ let label = props.actionButtonLabel
346
+
347
+ if (isAlreadyAddedSearch && isAlreadyAddedSearch.length > 0) {
348
+ const tooltip = props.toolTipDisabledButton
349
+ const color = props.disabledButtonColor
350
+ label = props.disableActionButtonLabel
351
+
352
+ return {
353
+ ...result,
354
+ disable: true,
355
+ btnProps: {
356
+ color,
357
+ label,
358
+ },
359
+ tooltip,
360
+ }
361
+ }
362
+ return {
363
+ ...result,
364
+ btnProps: {
365
+ label,
366
+ },
367
+ }
368
+ })
369
+
370
+ return results
371
+ }
372
+
373
+ const transformCategory = (category) => {
374
+ switch (category) {
375
+ case 'users':
376
+ return 'People'
377
+ case 'teams':
378
+ return 'Teams'
379
+ case 'events':
380
+ return 'Events'
381
+ case 'venues':
382
+ return 'Venues'
383
+ default:
384
+ return 'Other'
385
+ }
386
+ }
387
+
388
+ const updateAdvanceSearchToggle = (val) => {
389
+ toggle.value = !toggle.value
390
+ }
391
+
392
+ const updateCountry = (val, label) => {
393
+ emit('updateCountry', val, label)
394
+ }
395
+
396
+ const updateModelVal = (event, label) => {
397
+ emit('updateModelVal', event, label)
398
+ }
399
+
400
+ //Assign default model value on tab change
401
+ watch(
402
+ () => props.model,
403
+ (value) => {
404
+ advancedSearchFilterModelDefault.value = Object.assign(
405
+ {},
406
+ structuredClone(toRaw(value))
407
+ )
408
+ },
409
+ { immediate: true }
410
+ )
411
+
412
+ watch(
413
+ () => props.model,
414
+ (newValue) => {
415
+ if (deepEqual(newValue, advancedSearchFilterModelDefault.value)) {
416
+ advancedSearchFilterDirty.value = false
417
+ } else {
418
+ advancedSearchFilterDirty.value = true
419
+ }
420
+ },
421
+ { deep: true }
422
+ )
423
+
424
+ watch(
425
+ () => props.searchText,
426
+ (value) => {
427
+ if (value.length >= 2) {
428
+ if (!searchMenuShowing.value) searchMenuRef.value.show()
429
+ search()
430
+ } else {
431
+ searchResults.value = null
432
+ }
433
+ }
434
+ )
435
+ </script>
436
+
437
+ <template>
438
+ <q-menu
439
+ v-bind="$attrs"
440
+ v-model="searchMenuShowing"
441
+ :class="`u-search-option-menu ${menuClass}`"
442
+ aria-label="search menu"
443
+ fit
444
+ no-focus
445
+ :offset="[0, 4]"
446
+ ref="searchMenuRef"
447
+ role="menu"
448
+ tabindex="1"
449
+ @hide="onMenuHide"
450
+ @show="onMenuShow"
451
+ >
452
+ <q-list
453
+ :class="`u-typeahead-menu q-px-ba q-pt-ba size-${size}`"
454
+ role="menuitem"
455
+ >
456
+ <div v-if="showAdvancedSearchTabs" class="q-pb-xs">
457
+ <span class="text-overline-xs">{{ headerLabel }}</span>
458
+
459
+ <!-- Tab options -->
460
+
461
+ <div class="tabs-option">
462
+ <UTabBtnStd
463
+ :modelValue="advancedSearchSelectedTab"
464
+ :buttonTabsOptions="options"
465
+ size="sm"
466
+ :standard="false"
467
+ @onTabClick="handleTabChange"
468
+ />
469
+ </div>
470
+ </div>
471
+ <div class="q-mt-xs">
472
+ <!-- Menu dropdown -->
473
+ <UMenuDropdownAdvancedSearch
474
+ v-if="showAdvancedSearch"
475
+ :disbaleApplyFilter="!advancedSearchFilterDirty"
476
+ :fields="fields"
477
+ :labelIcon="labelIcon"
478
+ :model="model"
479
+ :showCustomMenu="showCustomMenu"
480
+ :size="size"
481
+ :title="title"
482
+ :toggle="toggle"
483
+ @onApplyAdvancedSearchFilter="onApplyAdvancedSearchFilter"
484
+ @onClearAdvancedSearchFilter="onClearAdvancedSearchFilter"
485
+ @updateAdvanceSearchToggle="(val) => updateAdvanceSearchToggle(val)"
486
+ @updateCountry="(event, label) => updateCountry(event, label)"
487
+ @updateModelVal="(event, label) => updateModelVal(event, label)"
488
+ >
489
+ </UMenuDropdownAdvancedSearch>
490
+
491
+ <!-- Search Result list -->
492
+
493
+ <q-list
494
+ v-if="searchResults && !exceedLimit && searchText.length"
495
+ class="search-list q-mt-xs"
496
+ >
497
+ <span class="text-overline-xs">Search results</span>
498
+ <template
499
+ v-for="category of Object.keys(searchResults)"
500
+ :key="category"
501
+ >
502
+ <q-item-label caption>
503
+ <span class="text-caption-sm">
504
+ {{ transformCategory(category) }}
505
+ </span>
506
+ </q-item-label>
507
+ <q-item
508
+ v-for="item of searchResults[category]"
509
+ class="q-pa-xs list-item"
510
+ clickable
511
+ :key="item.id"
512
+ >
513
+ <q-item-section side>
514
+ <UAvatar
515
+ v-if="
516
+ !item.profilePictureUrl ||
517
+ item.profilePictureUrl?.nullValue === 0
518
+ "
519
+ :aria-label="item.name"
520
+ :name="parseInitials(item.name)"
521
+ size="md"
522
+ />
523
+ <UAvatar
524
+ v-else
525
+ :aria-label="item.name"
526
+ :image="item.profilePictureUrl"
527
+ :round="true"
528
+ :showIndicator="false"
529
+ size="md"
530
+ />
531
+ </q-item-section>
532
+
533
+ <q-item-section>
534
+ <q-item-label class="text-caption-md wrapped-text">
535
+ {{ item?.name }}
536
+ </q-item-label>
537
+ <q-item-label class="text-body-xs text-neutral-9 wrapped-text">
538
+ {{ item?.description }}
539
+ </q-item-label>
540
+ </q-item-section>
541
+
542
+ <q-item-section side>
543
+ <UBtnStd
544
+ v-bind="item.btnProps"
545
+ :color="item.btnProps?.color ?? 'primary'"
546
+ :disable="
547
+ item.disable ||
548
+ (selectedItem?.id !== item.id && actionButtonLoading)
549
+ "
550
+ :loading="
551
+ selectedItem?.id === item.id ? actionButtonLoading : false
552
+ "
553
+ outline
554
+ size="sm"
555
+ @click.stop="onItemActionClick(item)"
556
+ >
557
+ <template v-if="item.tooltip && item.disable" #tooltip>
558
+ <UTooltip
559
+ anchor="top middle"
560
+ :description="item.tooltip"
561
+ :offset="[0, 4]"
562
+ self="bottom middle"
563
+ />
564
+ </template>
565
+ </UBtnStd>
566
+ </q-item-section>
567
+ </q-item>
568
+ </template>
569
+ </q-list>
570
+
571
+ <!-- No data section -->
572
+
573
+ <div
574
+ v-if="searchText.length && !searchResults && !exceedLimit && !loading"
575
+ >
576
+ <div class="items-center column q-py-ba">
577
+ <img
578
+ src="../../assets/no-result.png"
579
+ alt="No result found"
580
+ aria-label="No result found"
581
+ />
582
+ <span class="text-caption-lg">No results found</span>
583
+ <span class="text-body-sm text-neutral-9 q-mt-xxs q-mb-ba">
584
+ Invite user to join USSSA.
585
+ </span>
586
+ </div>
587
+
588
+ <div v-if="showInviteBtn" class="row items-center full-width">
589
+ <span :class="`searchText-${size} text-caption-md q-mr-xs`">
590
+ {{ searchText }}
591
+ </span>
592
+ <UBtnStd
593
+ color="primary"
594
+ label="Invite"
595
+ outline
596
+ size="sm"
597
+ @click.stop="onInvitePerson(item, false)"
598
+ />
599
+ </div>
600
+ </div>
601
+
602
+ <!-- Exceed limit section -->
603
+
604
+ <div v-if="searchText.length && exceedLimit && searchResults">
605
+ <div class="column q-my-ba items-center">
606
+ <img
607
+ alt="No result found"
608
+ aria-label="No result found"
609
+ src="../../assets/no-result.png"
610
+ />
611
+ <span class="text-caption-lg">Results exceeds limit.</span>
612
+ <span class="text-body-sm text-neutral-9 q-mt-xxs text-center">
613
+ Please select advanced search to provide more details for your
614
+ search.
615
+ </span>
616
+ </div>
617
+ </div>
618
+
619
+ <!-- Recently selected list -->
620
+
621
+ <q-list class="search-list q-mt-xs">
622
+ <div
623
+ v-if="showRecentSelected && recentSearchesLoading"
624
+ class="row justify-between items-center"
625
+ >
626
+ <span class="text-overline-xs"> Recently Selected </span>
627
+
628
+ <q-spinner
629
+ v-if="recentSearchesLoading"
630
+ color="grey-14"
631
+ size="1rem"
632
+ />
633
+ </div>
634
+ <div
635
+ v-if="showRecentSelected && recentSearches && recentSearches.length"
636
+ >
637
+ <span class="text-overline-xs"> Recently Selected </span>
638
+
639
+ <template v-for="item in recentSearches" :key="item.id">
640
+ <q-item class="list-item" clickable>
641
+ <q-item-section side>
642
+ <UAvatar
643
+ v-if="
644
+ !item.profilePictureUrl ||
645
+ item.profilePictureUrl?.nullValue === 0
646
+ "
647
+ :aria-label="item.name"
648
+ :name="parseInitials(item.name)"
649
+ size="md"
650
+ />
651
+ <UAvatar
652
+ v-else
653
+ :aria-label="item.name"
654
+ :image="item.profilePictureUrl"
655
+ :round="true"
656
+ :showIndicator="false"
657
+ size="md"
658
+ />
659
+ </q-item-section>
660
+
661
+ <q-item-section>
662
+ <q-item-label class="text-caption-md wrapped-text">
663
+ {{ item?.name }}
664
+ </q-item-label>
665
+ <q-item-label
666
+ class="text-body-xs text-neutral-9 label wrapped-text"
667
+ >
668
+ {{ item?.description }}
669
+ </q-item-label>
670
+ </q-item-section>
671
+
672
+ <q-item-section side>
673
+ <UBtnStd
674
+ v-bind="item.btnProps"
675
+ :color="item.btnProps?.color ?? 'primary'"
676
+ :disable="
677
+ item.disable ||
678
+ (selectedItem?.id !== item.id && actionButtonLoading)
679
+ "
680
+ :loading="
681
+ selectedItem?.id === item.id ? actionButtonLoading : false
682
+ "
683
+ outline
684
+ size="sm"
685
+ @click.stop="onItemActionClick(item, true)"
686
+ >
687
+ <template v-if="item.tooltip && item.disable" #tooltip>
688
+ <UTooltip
689
+ anchor="top middle"
690
+ :description="item.tooltip"
691
+ :offset="[0, 4]"
692
+ self="bottom middle"
693
+ />
694
+ </template>
695
+ </UBtnStd>
696
+ </q-item-section>
697
+ </q-item>
698
+ </template>
699
+ </div>
700
+ </q-list>
701
+ </div>
702
+ </q-list>
703
+ </q-menu>
704
+ </template>
705
+
706
+ <style lang="sass">
707
+ .u-search-option-menu
708
+ overflow-y: auto
709
+ scrollbar-width: none
710
+ border-radius: $xs
711
+ padding-bottom: $ba
712
+ max-height: 20rem
713
+ box-shadow: 0px 8px 8px 0px rgba(16, 17, 20, 0.16)
714
+ .u-typeahead-menu
715
+ .u-tabs-outer .u-tab-button .truncated-label
716
+ overflow-wrap: unset
717
+ .u-tabs-outer .u-tab-sm
718
+ margin: 0 $xxs
719
+ .u-tabs-outer .u-tab-sm:first-child
720
+ margin-left: 0px
721
+ .tabs-option
722
+ display: flex
723
+ overflow-y: auto
724
+ scrollbar-width: none
725
+ flex-direction: column
726
+ align-items: flex-start
727
+ border-radius: $xs
728
+ background: $neutral-1
729
+ &.size-sm
730
+ width: 24.5625rem
731
+ &.size-md
732
+ width: 26.5rem
733
+ .search-list
734
+ display: flex
735
+ flex-direction: column
736
+ gap: $xxs
737
+ .list-item
738
+ border-radius: $xs
739
+ padding: $xs
740
+ align-items: center
741
+ display: flex
742
+ cursor: default !important
743
+ gap: $xs
744
+ .q-item__section--avatar
745
+ min-width: 0px
746
+ .q-item__section--side
747
+ padding: 0px
748
+ .wrapped-text
749
+ white-space: normal
750
+ overflow: hidden
751
+ word-wrap: break-word
752
+ </style>