@usssa/component-library 1.0.0-alpha.91 → 1.0.0-alpha.93
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.
- package/package.json +1 -1
- package/src/components/core/UMenuSearch.vue +135 -114
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
<script setup>
|
|
2
2
|
import { computed, ref, toRaw, watch } from 'vue'
|
|
3
|
-
import { Notify, SessionStorage } from 'quasar'
|
|
4
3
|
import UAvatar from './UAvatar.vue'
|
|
5
4
|
import UBtnStd from './UBtnStd.vue'
|
|
6
5
|
import UInputTypeaheadAdvanceSearch from './UInputTypeaheadAdvanceSearch.vue'
|
|
7
6
|
import UMenuDropdownAdvancedSearch from './UMenuDropdownAdvancedSearch.vue'
|
|
8
7
|
import UTabBtnStd from './UTabBtnStd.vue'
|
|
9
8
|
import UTooltip from './UTooltip.vue'
|
|
9
|
+
import { useNotify } from '../../composables/useNotify'
|
|
10
10
|
import { parseInitials, categorizeObjects, deepEqual } from '../../utils/data'
|
|
11
11
|
|
|
12
12
|
const props = defineProps({
|
|
@@ -21,11 +21,21 @@ const props = defineProps({
|
|
|
21
21
|
advancedSearchSelectedTab: {
|
|
22
22
|
type: String,
|
|
23
23
|
},
|
|
24
|
+
actionButtonLoading: {
|
|
25
|
+
type: Boolean,
|
|
26
|
+
},
|
|
24
27
|
// Set context for component for special handling
|
|
25
28
|
// context.action (e.g. addContact)
|
|
26
29
|
// context.data (e.g. array of existing contacts)
|
|
27
30
|
context: {
|
|
28
|
-
type:
|
|
31
|
+
type: Array,
|
|
32
|
+
},
|
|
33
|
+
disabledButtonColor: {
|
|
34
|
+
type: String,
|
|
35
|
+
},
|
|
36
|
+
exceedLimitCount: {
|
|
37
|
+
type: Number,
|
|
38
|
+
default: 50,
|
|
29
39
|
},
|
|
30
40
|
fields: {
|
|
31
41
|
type: Array,
|
|
@@ -38,11 +48,6 @@ const props = defineProps({
|
|
|
38
48
|
type: String,
|
|
39
49
|
default: 'fa-kit-duotone fa-filter-search',
|
|
40
50
|
},
|
|
41
|
-
//Number of maximum search result to show
|
|
42
|
-
maxSearchResults: {
|
|
43
|
-
type: Number,
|
|
44
|
-
default: 5,
|
|
45
|
-
},
|
|
46
51
|
menuClass: {
|
|
47
52
|
type: String,
|
|
48
53
|
},
|
|
@@ -52,6 +57,12 @@ const props = defineProps({
|
|
|
52
57
|
options: {
|
|
53
58
|
type: Array,
|
|
54
59
|
},
|
|
60
|
+
recentSearches: {
|
|
61
|
+
type: Array,
|
|
62
|
+
},
|
|
63
|
+
recentSearchesLoading: {
|
|
64
|
+
type: Boolean,
|
|
65
|
+
},
|
|
55
66
|
showCustomMenu: {
|
|
56
67
|
type: Boolean,
|
|
57
68
|
default: false,
|
|
@@ -87,6 +98,9 @@ const props = defineProps({
|
|
|
87
98
|
type: String,
|
|
88
99
|
default: 'Advanced search',
|
|
89
100
|
},
|
|
101
|
+
toolTipDisabledButton: {
|
|
102
|
+
type: String,
|
|
103
|
+
},
|
|
90
104
|
})
|
|
91
105
|
|
|
92
106
|
const emit = defineEmits([
|
|
@@ -98,6 +112,7 @@ const emit = defineEmits([
|
|
|
98
112
|
])
|
|
99
113
|
|
|
100
114
|
const loading = defineModel('loading')
|
|
115
|
+
const { notify } = useNotify()
|
|
101
116
|
|
|
102
117
|
const advancedSearchPendingFilterModel = structuredClone(toRaw(props.model))
|
|
103
118
|
const advancedSearchSelectedTab = ref(props.advancedSearchSelectedTab ?? null)
|
|
@@ -113,11 +128,11 @@ const searchMenuShowing = ref(false)
|
|
|
113
128
|
|
|
114
129
|
const recentSearches = computed({
|
|
115
130
|
get() {
|
|
116
|
-
let arr =
|
|
117
|
-
if (props.context
|
|
131
|
+
let arr = props.recentSearches
|
|
132
|
+
if (props.context) {
|
|
118
133
|
arr = specialContextHandler(arr)
|
|
119
134
|
}
|
|
120
|
-
return arr
|
|
135
|
+
return arr
|
|
121
136
|
},
|
|
122
137
|
set(value) {
|
|
123
138
|
return value
|
|
@@ -173,50 +188,38 @@ const handleTabChange = (val) => {
|
|
|
173
188
|
search()
|
|
174
189
|
}
|
|
175
190
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
191
|
+
const onItemActionClick = (item, isRecentSearchAction) => {
|
|
192
|
+
return emit(
|
|
193
|
+
'onItemActionClick',
|
|
194
|
+
item,
|
|
195
|
+
isRecentSearchAction,
|
|
196
|
+
searchMenuRef.value
|
|
197
|
+
)
|
|
198
|
+
}
|
|
181
199
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
200
|
+
const specialContextHandler = (results) => {
|
|
201
|
+
// Mark result records that are already exits
|
|
202
|
+
results = results.map((result) => {
|
|
203
|
+
const isAlreadyContactSearch = props.context.filter(
|
|
204
|
+
(contact) => contact.id === result.id
|
|
205
|
+
)
|
|
185
206
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
207
|
+
if (isAlreadyContactSearch && isAlreadyContactSearch.length > 0) {
|
|
208
|
+
const tooltip = props.toolTipDisabledButton
|
|
209
|
+
const color = props.disabledButtonColor
|
|
210
|
+
return {
|
|
211
|
+
...result,
|
|
212
|
+
disable: true,
|
|
213
|
+
btnProps: {
|
|
214
|
+
color,
|
|
215
|
+
},
|
|
216
|
+
tooltip,
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return result
|
|
189
220
|
})
|
|
190
221
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
SessionStorage.set('recentSearches', recentSearches.value)
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const onItemActionClick = (item) => {
|
|
197
|
-
recentSearchHandler(item)
|
|
198
|
-
return emit('onItemActionClick', item, searchMenuRef.value)
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
const specialContextHandler = (results) => {
|
|
202
|
-
switch (props.context.action) {
|
|
203
|
-
case 'addContact':
|
|
204
|
-
// Mark result records that are already contacts
|
|
205
|
-
results = results.map((result) => {
|
|
206
|
-
const isAlreadyContactSearch = props.context.data.filter(
|
|
207
|
-
(contact) => contact.userId === result.id
|
|
208
|
-
)
|
|
209
|
-
if (isAlreadyContactSearch && isAlreadyContactSearch.length > 0) {
|
|
210
|
-
return {
|
|
211
|
-
...result,
|
|
212
|
-
disable: true,
|
|
213
|
-
tooltip: 'Person is already a contact',
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
return result
|
|
217
|
-
})
|
|
218
|
-
return results
|
|
219
|
-
}
|
|
222
|
+
return results
|
|
220
223
|
}
|
|
221
224
|
|
|
222
225
|
const convertDate = (inputDate) => {
|
|
@@ -327,7 +330,7 @@ const search = async () => {
|
|
|
327
330
|
|
|
328
331
|
const res = await props.algoliaIndex.search(props.searchText, configPayload)
|
|
329
332
|
|
|
330
|
-
if (res.hits.length >=
|
|
333
|
+
if (res.hits.length >= props.exceedLimitCount) {
|
|
331
334
|
exceedLimit.value = true
|
|
332
335
|
} else {
|
|
333
336
|
exceedLimit.value = false
|
|
@@ -335,7 +338,7 @@ const search = async () => {
|
|
|
335
338
|
|
|
336
339
|
let results = res.hits
|
|
337
340
|
|
|
338
|
-
if (props.context
|
|
341
|
+
if (props.context) {
|
|
339
342
|
results = specialContextHandler(results)
|
|
340
343
|
}
|
|
341
344
|
|
|
@@ -348,9 +351,10 @@ const search = async () => {
|
|
|
348
351
|
}
|
|
349
352
|
return void 0
|
|
350
353
|
} catch (error) {
|
|
351
|
-
|
|
352
|
-
type: '
|
|
353
|
-
|
|
354
|
+
notify({
|
|
355
|
+
type: 'accent',
|
|
356
|
+
label: 'Search broke',
|
|
357
|
+
position: 'top',
|
|
354
358
|
})
|
|
355
359
|
} finally {
|
|
356
360
|
loading.value = false
|
|
@@ -527,6 +531,7 @@ watch(
|
|
|
527
531
|
:color="item.btnProps?.color ?? 'primary'"
|
|
528
532
|
:disable="item.disable"
|
|
529
533
|
:label="actionButtonLabel"
|
|
534
|
+
:loading="actionButtonLoading"
|
|
530
535
|
outline
|
|
531
536
|
size="sm"
|
|
532
537
|
@click.stop="onItemActionClick(item)"
|
|
@@ -572,7 +577,7 @@ watch(
|
|
|
572
577
|
label="Invite"
|
|
573
578
|
outline
|
|
574
579
|
size="sm"
|
|
575
|
-
@click.stop="onInvitePerson(item)"
|
|
580
|
+
@click.stop="onInvitePerson(item, false)"
|
|
576
581
|
/>
|
|
577
582
|
</div>
|
|
578
583
|
</div>
|
|
@@ -596,56 +601,81 @@ watch(
|
|
|
596
601
|
|
|
597
602
|
<!-- Recently selected list -->
|
|
598
603
|
|
|
599
|
-
<q-list
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
<q-item class="list-item" clickable>
|
|
606
|
-
<q-item-section side>
|
|
607
|
-
<UAvatar
|
|
608
|
-
v-if="
|
|
609
|
-
!item.profilePictureUrl ||
|
|
610
|
-
item.profilePictureUrl?.nullValue === 0
|
|
611
|
-
"
|
|
612
|
-
:aria-label="item.name"
|
|
613
|
-
:name="parseInitials(item.name)"
|
|
614
|
-
size="md"
|
|
615
|
-
/>
|
|
616
|
-
<UAvatar
|
|
617
|
-
v-else
|
|
618
|
-
:aria-label="item.name"
|
|
619
|
-
:image="item.profilePictureUrl"
|
|
620
|
-
:round="true"
|
|
621
|
-
size="md"
|
|
622
|
-
:showIndicator="false"
|
|
623
|
-
/>
|
|
624
|
-
</q-item-section>
|
|
625
|
-
|
|
626
|
-
<q-item-section>
|
|
627
|
-
<q-item-label class="text-caption-md wrapped-text">
|
|
628
|
-
{{ item?.name }}
|
|
629
|
-
</q-item-label>
|
|
630
|
-
<q-item-label
|
|
631
|
-
class="text-body-xs text-neutral-9 label wrapped-text"
|
|
632
|
-
>
|
|
633
|
-
{{ item?.description }}
|
|
634
|
-
</q-item-label>
|
|
635
|
-
</q-item-section>
|
|
604
|
+
<q-list class="search-list q-mt-xs">
|
|
605
|
+
<div
|
|
606
|
+
v-if="showRecentSelected && recentSearchesLoading"
|
|
607
|
+
class="row justify-between items-center"
|
|
608
|
+
>
|
|
609
|
+
<span class="text-overline-xs"> Recently Selected </span>
|
|
636
610
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
611
|
+
<q-spinner
|
|
612
|
+
v-if="recentSearchesLoading"
|
|
613
|
+
color="grey-14"
|
|
614
|
+
size="1rem"
|
|
615
|
+
/>
|
|
616
|
+
</div>
|
|
617
|
+
<div
|
|
618
|
+
v-if="showRecentSelected && recentSearches && recentSearches.length"
|
|
619
|
+
>
|
|
620
|
+
<span class="text-overline-xs"> Recently Selected </span>
|
|
621
|
+
|
|
622
|
+
<template v-for="item in recentSearches" :key="item.id">
|
|
623
|
+
<q-item class="list-item" clickable>
|
|
624
|
+
<q-item-section side>
|
|
625
|
+
<UAvatar
|
|
626
|
+
v-if="
|
|
627
|
+
!item.profilePictureUrl ||
|
|
628
|
+
item.profilePictureUrl?.nullValue === 0
|
|
629
|
+
"
|
|
630
|
+
:aria-label="item.name"
|
|
631
|
+
:name="parseInitials(item.name)"
|
|
632
|
+
size="md"
|
|
633
|
+
/>
|
|
634
|
+
<UAvatar
|
|
635
|
+
v-else
|
|
636
|
+
:aria-label="item.name"
|
|
637
|
+
:image="item.profilePictureUrl"
|
|
638
|
+
:round="true"
|
|
639
|
+
size="md"
|
|
640
|
+
:showIndicator="false"
|
|
641
|
+
/>
|
|
642
|
+
</q-item-section>
|
|
643
|
+
|
|
644
|
+
<q-item-section>
|
|
645
|
+
<q-item-label class="text-caption-md wrapped-text">
|
|
646
|
+
{{ item?.name }}
|
|
647
|
+
</q-item-label>
|
|
648
|
+
<q-item-label
|
|
649
|
+
class="text-body-xs text-neutral-9 label wrapped-text"
|
|
650
|
+
>
|
|
651
|
+
{{ item?.description }}
|
|
652
|
+
</q-item-label>
|
|
653
|
+
</q-item-section>
|
|
654
|
+
|
|
655
|
+
<q-item-section side>
|
|
656
|
+
<UBtnStd
|
|
657
|
+
v-bind="item.btnProps"
|
|
658
|
+
:color="item.btnProps?.color ?? 'primary'"
|
|
659
|
+
:disable="item.disable"
|
|
660
|
+
:label="actionButtonLabel"
|
|
661
|
+
:loading="actionButtonLoading"
|
|
662
|
+
outline
|
|
663
|
+
size="sm"
|
|
664
|
+
@click.stop="onItemActionClick(item, true)"
|
|
665
|
+
>
|
|
666
|
+
<template v-if="item.tooltip && item.disable" #tooltip>
|
|
667
|
+
<UTooltip
|
|
668
|
+
anchor="top middle"
|
|
669
|
+
:description="item.tooltip"
|
|
670
|
+
:offset="[0, 4]"
|
|
671
|
+
self="bottom middle"
|
|
672
|
+
/>
|
|
673
|
+
</template>
|
|
674
|
+
</UBtnStd>
|
|
675
|
+
</q-item-section>
|
|
676
|
+
</q-item>
|
|
677
|
+
</template>
|
|
678
|
+
</div>
|
|
649
679
|
</q-list>
|
|
650
680
|
</div>
|
|
651
681
|
</q-list>
|
|
@@ -683,14 +713,6 @@ watch(
|
|
|
683
713
|
display: flex
|
|
684
714
|
flex-direction: column
|
|
685
715
|
gap: $xxs
|
|
686
|
-
.searchText-md
|
|
687
|
-
flex: 1
|
|
688
|
-
word-wrap: break-word
|
|
689
|
-
min-width: 18.5rem
|
|
690
|
-
.searchText-sm
|
|
691
|
-
flex: 1
|
|
692
|
-
word-wrap: break-word
|
|
693
|
-
min-width: 16.5rem
|
|
694
716
|
.list-item
|
|
695
717
|
border-radius: $xs
|
|
696
718
|
padding: $xs
|
|
@@ -706,5 +728,4 @@ watch(
|
|
|
706
728
|
white-space: normal
|
|
707
729
|
overflow: hidden
|
|
708
730
|
word-wrap: break-word
|
|
709
|
-
max-width: 18rem
|
|
710
731
|
</style>
|