design-system-next 2.19.3 → 2.19.4
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/dist/design-system-next.es.js +6989 -6837
- package/dist/design-system-next.es.js.gz +0 -0
- package/dist/design-system-next.umd.js +13 -12
- package/dist/design-system-next.umd.js.gz +0 -0
- package/dist/main.css +1 -1
- package/dist/main.css.gz +0 -0
- package/dist/package.json.d.ts +1 -1
- package/package.json +1 -1
- package/src/App.vue +51 -1
- package/src/components/list/list.ts +13 -0
- package/src/components/list/list.vue +106 -32
- package/src/components/list/use-list.ts +21 -5
- package/src/components/select/select-multiple/select-multiple.ts +20 -0
- package/src/components/select/select-multiple/select-multiple.vue +6 -4
- package/src/components/select/select-multiple/use-select-multiple.ts +87 -54
|
@@ -36,6 +36,10 @@ export const listPropTypes = {
|
|
|
36
36
|
type: String,
|
|
37
37
|
default: '',
|
|
38
38
|
},
|
|
39
|
+
supportingDisplayText: {
|
|
40
|
+
type: String,
|
|
41
|
+
default: '',
|
|
42
|
+
},
|
|
39
43
|
menuList: {
|
|
40
44
|
type: Array as PropType<MenuListType[]>,
|
|
41
45
|
required: true,
|
|
@@ -91,15 +95,24 @@ export const listPropTypes = {
|
|
|
91
95
|
type: [String, Number] as PropType<string | number>,
|
|
92
96
|
default: 0,
|
|
93
97
|
},
|
|
98
|
+
displayListItemSelected: {
|
|
99
|
+
type: Boolean,
|
|
100
|
+
default: false,
|
|
101
|
+
},
|
|
94
102
|
itemIcon: {
|
|
95
103
|
type: String,
|
|
96
104
|
default: '',
|
|
97
105
|
},
|
|
106
|
+
disabledUnselectedItems: {
|
|
107
|
+
type: Boolean,
|
|
108
|
+
default: false,
|
|
109
|
+
},
|
|
98
110
|
};
|
|
99
111
|
|
|
100
112
|
export const listEmitTypes = {
|
|
101
113
|
'update:modelValue': (value: MenuListType[]) => value,
|
|
102
114
|
'update:searchValue': (value: string) => typeof value === 'string',
|
|
115
|
+
'get-single-selected-item': (item: MenuListType) => item,
|
|
103
116
|
};
|
|
104
117
|
|
|
105
118
|
export type ListPropTypes = ExtractPropTypes<typeof listPropTypes>;
|
|
@@ -1,21 +1,36 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="spr-font-main">
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
<template v-if="props.searchableMenu || props.displayListItemSelected">
|
|
4
|
+
<div
|
|
5
|
+
:class="[
|
|
6
|
+
'spr-sticky spr-z-20',
|
|
7
|
+
'spr-grid spr-gap-3 spr-bg-white-50 spr-px-size-spacing-3xs spr-py-size-spacing-2xs',
|
|
8
|
+
'spr-border-color-weak spr-border spr-border-x-0 spr-border-b spr-border-t-0 spr-border-solid',
|
|
9
|
+
]"
|
|
10
|
+
:style="{
|
|
11
|
+
top:
|
|
12
|
+
typeof props.stickySearchOffset === 'number'
|
|
13
|
+
? props.stickySearchOffset + 'px'
|
|
14
|
+
: String(props.stickySearchOffset),
|
|
15
|
+
}"
|
|
16
|
+
>
|
|
17
|
+
<spr-input-search
|
|
18
|
+
v-if="props.searchableMenu"
|
|
19
|
+
v-model="searchText"
|
|
20
|
+
:placeholder="props.searchableMenuPlaceholder"
|
|
21
|
+
autocomplete="off"
|
|
22
|
+
/>
|
|
23
|
+
<span
|
|
24
|
+
v-if="props.supportingDisplayText || props.displayListItemSelected"
|
|
25
|
+
class="spr-label-sm-medium spr-text-color-base spr-block"
|
|
26
|
+
>
|
|
27
|
+
<template v-if="props.supportingDisplayText">
|
|
28
|
+
{{ props.supportingDisplayText }}
|
|
29
|
+
</template>
|
|
30
|
+
<template v-else> {{ selectedItems.length }} Selected</template>
|
|
31
|
+
</span>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
19
34
|
|
|
20
35
|
<div class="spr-p-size-spacing-3xs">
|
|
21
36
|
<template v-if="props.groupItemsBy">
|
|
@@ -38,7 +53,11 @@
|
|
|
38
53
|
>
|
|
39
54
|
<template v-if="props.lozenge">
|
|
40
55
|
<div class="spr-flex spr-items-center spr-gap-1">
|
|
41
|
-
<spr-checkbox
|
|
56
|
+
<spr-checkbox
|
|
57
|
+
v-if="props.multiSelect"
|
|
58
|
+
:disabled="item.disabled || (props.disabledUnselectedItems && !isItemSelected(item))"
|
|
59
|
+
:checked="isItemSelected(item)"
|
|
60
|
+
/>
|
|
42
61
|
<spr-lozenge
|
|
43
62
|
v-if="props.lozenge"
|
|
44
63
|
:label="item.text || (item.lozengeProps?.label as string)"
|
|
@@ -52,23 +71,45 @@
|
|
|
52
71
|
</template>
|
|
53
72
|
<template v-else>
|
|
54
73
|
<div class="spr-flex spr-items-center spr-gap-1">
|
|
55
|
-
<spr-checkbox
|
|
74
|
+
<spr-checkbox
|
|
75
|
+
v-if="props.multiSelect"
|
|
76
|
+
:disabled="item.disabled || (props.disabledUnselectedItems && !isItemSelected(item))"
|
|
77
|
+
:checked="isItemSelected(item)"
|
|
78
|
+
/>
|
|
56
79
|
<div :class="[item.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
|
|
57
|
-
<span
|
|
80
|
+
<span
|
|
81
|
+
v-if="props.itemIcon || item.icon"
|
|
82
|
+
:class="[
|
|
83
|
+
item.iconColor,
|
|
84
|
+
'spr-mt-[2px]',
|
|
85
|
+
{
|
|
86
|
+
'spr-text-color-disabled':
|
|
87
|
+
item.disabled || (props.disabledUnselectedItems && !isItemSelected(item)),
|
|
88
|
+
},
|
|
89
|
+
]"
|
|
90
|
+
>
|
|
58
91
|
<icon :icon="(props.itemIcon || item.icon) as string" width="20px" height="20px" />
|
|
59
92
|
</span>
|
|
60
93
|
<div
|
|
61
94
|
:class="[
|
|
62
95
|
'spr-flex spr-flex-auto spr-flex-col spr-justify-start',
|
|
63
|
-
{
|
|
96
|
+
{
|
|
97
|
+
'spr-text-color-disabled':
|
|
98
|
+
item.disabled || (props.disabledUnselectedItems && !isItemSelected(item)),
|
|
99
|
+
},
|
|
64
100
|
]"
|
|
65
101
|
>
|
|
66
|
-
<span class="spr-text-left spr-text-xs">
|
|
102
|
+
<span class="spr-break-words spr-text-left spr-text-xs">
|
|
103
|
+
{{ item.text }}
|
|
104
|
+
</span>
|
|
67
105
|
<span
|
|
68
106
|
v-if="item.subtext"
|
|
69
107
|
:class="[
|
|
70
|
-
'spr-body-xs-regular spr-text-color-base spr-text-left',
|
|
71
|
-
{
|
|
108
|
+
'spr-body-xs-regular spr-text-color-base spr-break-words spr-text-left',
|
|
109
|
+
{
|
|
110
|
+
'spr-text-color-disabled':
|
|
111
|
+
item.disabled || (props.disabledUnselectedItems && !isItemSelected(item)),
|
|
112
|
+
},
|
|
72
113
|
]"
|
|
73
114
|
>
|
|
74
115
|
{{ item.subtext }}
|
|
@@ -128,7 +169,11 @@
|
|
|
128
169
|
>
|
|
129
170
|
<template v-if="props.lozenge">
|
|
130
171
|
<div class="spr-flex spr-items-center spr-gap-1">
|
|
131
|
-
<spr-checkbox
|
|
172
|
+
<spr-checkbox
|
|
173
|
+
v-if="props.multiSelect"
|
|
174
|
+
:disabled="item.disabled || (props.disabledUnselectedItems && !isItemSelected(item))"
|
|
175
|
+
:checked="isItemSelected(item)"
|
|
176
|
+
/>
|
|
132
177
|
<spr-lozenge
|
|
133
178
|
v-if="props.lozenge"
|
|
134
179
|
:label="item.text || (item.lozengeProps?.label as string)"
|
|
@@ -142,23 +187,45 @@
|
|
|
142
187
|
</template>
|
|
143
188
|
<template v-else>
|
|
144
189
|
<div class="spr-flex spr-items-center spr-gap-1">
|
|
145
|
-
<spr-checkbox
|
|
190
|
+
<spr-checkbox
|
|
191
|
+
v-if="props.multiSelect"
|
|
192
|
+
:disabled="item.disabled || (props.disabledUnselectedItems && !isItemSelected(item))"
|
|
193
|
+
:checked="isItemSelected(item)"
|
|
194
|
+
/>
|
|
146
195
|
<div :class="[item.textColor, 'spr-flex spr-flex-row spr-items-center spr-gap-size-spacing-3xs']">
|
|
147
|
-
<span
|
|
196
|
+
<span
|
|
197
|
+
v-if="props.itemIcon || item.icon"
|
|
198
|
+
:class="[
|
|
199
|
+
item.iconColor,
|
|
200
|
+
'spr-mt-[2px]',
|
|
201
|
+
{
|
|
202
|
+
'spr-text-color-disabled':
|
|
203
|
+
item.disabled || (props.disabledUnselectedItems && !isItemSelected(item)),
|
|
204
|
+
},
|
|
205
|
+
]"
|
|
206
|
+
>
|
|
148
207
|
<icon :icon="(props.itemIcon || item.icon) as string" width="20px" height="20px" />
|
|
149
208
|
</span>
|
|
150
209
|
<div
|
|
151
210
|
:class="[
|
|
152
211
|
'spr-flex spr-flex-auto spr-flex-col spr-justify-start',
|
|
153
|
-
{
|
|
212
|
+
{
|
|
213
|
+
'spr-text-color-disabled':
|
|
214
|
+
item.disabled || (props.disabledUnselectedItems && !isItemSelected(item)),
|
|
215
|
+
},
|
|
154
216
|
]"
|
|
155
217
|
>
|
|
156
|
-
<span class="spr-text-left spr-text-xs">
|
|
218
|
+
<span class="spr-break-words spr-text-left spr-text-xs">
|
|
219
|
+
{{ item.text }}
|
|
220
|
+
</span>
|
|
157
221
|
<span
|
|
158
222
|
v-if="item.subtext"
|
|
159
223
|
:class="[
|
|
160
|
-
'spr-body-xs-regular spr-text-color-base spr-text-left',
|
|
161
|
-
{
|
|
224
|
+
'spr-body-xs-regular spr-text-color-base spr-break-words spr-text-left',
|
|
225
|
+
{
|
|
226
|
+
'spr-text-color-disabled':
|
|
227
|
+
item.disabled || (props.disabledUnselectedItems && !isItemSelected(item)),
|
|
228
|
+
},
|
|
162
229
|
]"
|
|
163
230
|
>
|
|
164
231
|
{{ item.subtext }}
|
|
@@ -225,6 +292,13 @@ import { useList } from './use-list';
|
|
|
225
292
|
const props = defineProps(listPropTypes);
|
|
226
293
|
const emit = defineEmits(listEmitTypes);
|
|
227
294
|
|
|
228
|
-
const {
|
|
229
|
-
|
|
295
|
+
const {
|
|
296
|
+
selectedItems,
|
|
297
|
+
searchText,
|
|
298
|
+
localizedMenuList,
|
|
299
|
+
groupedMenuList,
|
|
300
|
+
isItemSelected,
|
|
301
|
+
getListItemClasses,
|
|
302
|
+
handleSelectedItem,
|
|
303
|
+
} = useList(props, emit);
|
|
230
304
|
</script>
|
|
@@ -14,8 +14,16 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
14
14
|
const selectedItems = useVModel(props, 'modelValue', emit);
|
|
15
15
|
const searchString = useVModel(props, 'searchValue', emit);
|
|
16
16
|
|
|
17
|
-
const {
|
|
18
|
-
|
|
17
|
+
const {
|
|
18
|
+
menuList,
|
|
19
|
+
menuLevel,
|
|
20
|
+
groupItemsBy,
|
|
21
|
+
multiSelect,
|
|
22
|
+
preSelectedItems,
|
|
23
|
+
disabledLocalSearch,
|
|
24
|
+
noCheck,
|
|
25
|
+
disabledUnselectedItems,
|
|
26
|
+
} = toRefs(props);
|
|
19
27
|
|
|
20
28
|
const listClasses: ComputedRef<ListClasses> = computed(() => {
|
|
21
29
|
const listItemClasses = classNames(
|
|
@@ -161,6 +169,7 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
161
169
|
|
|
162
170
|
if (previousSelected) {
|
|
163
171
|
handleSelectedItem(item);
|
|
172
|
+
|
|
164
173
|
return true;
|
|
165
174
|
}
|
|
166
175
|
|
|
@@ -341,10 +350,10 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
341
350
|
};
|
|
342
351
|
|
|
343
352
|
const getListItemClasses = (item: MenuListType) => ({
|
|
344
|
-
[listClasses.value.listItemClasses]: !item.disabled,
|
|
353
|
+
[listClasses.value.listItemClasses]: !item.disabled && !(disabledUnselectedItems.value && !isItemSelected(item)),
|
|
345
354
|
'spr-background-color-single-active': isItemSelected(item) && !item.disabled && !noCheck.value,
|
|
346
|
-
'
|
|
347
|
-
item.disabled,
|
|
355
|
+
'spr-cursor-not-allowed spr-flex spr-items-center spr-gap-1.5 spr-rounded-lg':
|
|
356
|
+
item.disabled || (disabledUnselectedItems.value && !isItemSelected(item)),
|
|
348
357
|
'spr-p-size-spacing-3xs': !props.lozenge,
|
|
349
358
|
'spr-py-size-spacing-3xs spr-px-size-spacing-4xs': props.lozenge,
|
|
350
359
|
});
|
|
@@ -420,6 +429,8 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
420
429
|
const handleSelectedItem = (item: MenuListType) => {
|
|
421
430
|
if (item.disabled) return;
|
|
422
431
|
|
|
432
|
+
if (disabledUnselectedItems.value && !isItemSelected(item)) return;
|
|
433
|
+
|
|
423
434
|
if (multiSelect.value) {
|
|
424
435
|
// For multi-select, check if item is already selected
|
|
425
436
|
const index = selectedItems.value.findIndex((selectedItem: MenuListType) => {
|
|
@@ -488,8 +499,11 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
488
499
|
} else {
|
|
489
500
|
// For single-select, simply replace the selection
|
|
490
501
|
selectedItems.value = [item];
|
|
502
|
+
|
|
491
503
|
if (item.onClickFn) item.onClickFn();
|
|
492
504
|
}
|
|
505
|
+
|
|
506
|
+
emit('get-single-selected-item', item);
|
|
493
507
|
};
|
|
494
508
|
// #endregion - Helper Methods
|
|
495
509
|
|
|
@@ -567,11 +581,13 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
567
581
|
|
|
568
582
|
onMounted(() => {
|
|
569
583
|
searchString.value = searchText.value;
|
|
584
|
+
|
|
570
585
|
setMenuList();
|
|
571
586
|
setPreSelectedItems();
|
|
572
587
|
});
|
|
573
588
|
|
|
574
589
|
return {
|
|
590
|
+
selectedItems,
|
|
575
591
|
searchText,
|
|
576
592
|
listClasses,
|
|
577
593
|
localizedMenuList,
|
|
@@ -123,10 +123,22 @@ export const multiSelectPropTypes = {
|
|
|
123
123
|
type: String,
|
|
124
124
|
default: '',
|
|
125
125
|
},
|
|
126
|
+
supportingDisplayText: {
|
|
127
|
+
type: String,
|
|
128
|
+
default: '',
|
|
129
|
+
},
|
|
130
|
+
persistentDisplayText: {
|
|
131
|
+
type: Boolean,
|
|
132
|
+
default: false,
|
|
133
|
+
},
|
|
126
134
|
displaySelectedCountOnly: {
|
|
127
135
|
type: Boolean,
|
|
128
136
|
default: false,
|
|
129
137
|
},
|
|
138
|
+
displayListItemSelected: {
|
|
139
|
+
type: Boolean,
|
|
140
|
+
default: false,
|
|
141
|
+
},
|
|
130
142
|
displayHelper: {
|
|
131
143
|
type: Boolean,
|
|
132
144
|
default: false,
|
|
@@ -179,14 +191,22 @@ export const multiSelectPropTypes = {
|
|
|
179
191
|
type: String,
|
|
180
192
|
default: '',
|
|
181
193
|
},
|
|
194
|
+
disabledUnselectedItems: {
|
|
195
|
+
type: Boolean,
|
|
196
|
+
default: false,
|
|
197
|
+
},
|
|
182
198
|
};
|
|
183
199
|
|
|
184
200
|
export const multiSelectEmitTypes = {
|
|
185
201
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
186
202
|
'update:modelValue': (_value: unknown) => true,
|
|
187
203
|
'update:searchValue': (value: string) => typeof value === 'string',
|
|
204
|
+
'search-string': (value: string) => typeof value === 'string',
|
|
188
205
|
'infinite-scroll-trigger': Boolean,
|
|
189
206
|
'popper-state': Boolean,
|
|
207
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
208
|
+
'get-selected-options': (_value: unknown) => true,
|
|
209
|
+
'get-single-selected-item': (item: MenuListType) => item,
|
|
190
210
|
};
|
|
191
211
|
|
|
192
212
|
export type MultiSelectPropTypes = ExtractPropTypes<typeof multiSelectPropTypes>;
|
|
@@ -49,9 +49,7 @@
|
|
|
49
49
|
class="spr-text-color-supporting spr-px-3"
|
|
50
50
|
:aria-label="`${multiSelectedListItems.length} selected options`"
|
|
51
51
|
>
|
|
52
|
-
{{ multiSelectedListItems.length }} item{{
|
|
53
|
-
multiSelectedListItems.length === 1 ? '' : 's'
|
|
54
|
-
}}
|
|
52
|
+
{{ multiSelectedListItems.length }} item{{ multiSelectedListItems.length === 1 ? '' : 's' }}
|
|
55
53
|
selected
|
|
56
54
|
</span>
|
|
57
55
|
</template>
|
|
@@ -164,9 +162,13 @@
|
|
|
164
162
|
:loading="props.loading"
|
|
165
163
|
:item-icon="props.itemIcon"
|
|
166
164
|
:lozenge="props.lozenge"
|
|
167
|
-
|
|
165
|
+
:supporting-display-text="props.supportingDisplayText"
|
|
166
|
+
:display-list-item-selected="props.displayListItemSelected"
|
|
168
167
|
:disabled-local-search="props.disabledLocalSearch"
|
|
168
|
+
:disabled-unselected-items="props.disabledUnselectedItems"
|
|
169
|
+
multi-select
|
|
169
170
|
@update:model-value="handleMultiSelectedItem"
|
|
171
|
+
@get-single-selected-item="emit('get-single-selected-item', $event)"
|
|
170
172
|
/>
|
|
171
173
|
</div>
|
|
172
174
|
</template>
|
|
@@ -19,7 +19,7 @@ interface MultiSelectClasses {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<MultiSelectEmitTypes>['emit']) => {
|
|
22
|
-
const { displayText, options, textField, valueField, active, disabled, error
|
|
22
|
+
const { displayText, persistentDisplayText, options, textField, valueField, active, disabled, error } = toRefs(props);
|
|
23
23
|
|
|
24
24
|
const multiSelectClasses: ComputedRef<MultiSelectClasses> = computed(() => {
|
|
25
25
|
const baseClasses = classNames('spr-flex spr-flex-col spr-gap-size-spacing-4xs');
|
|
@@ -106,7 +106,6 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
106
106
|
const hasUserSelected = ref(false);
|
|
107
107
|
|
|
108
108
|
const inputText = ref<string | number>('');
|
|
109
|
-
const inputTextBackup = ref<string | number>('');
|
|
110
109
|
const chippedInputTextRef = ref(null);
|
|
111
110
|
|
|
112
111
|
const search = useVModel(props, 'searchValue', emit);
|
|
@@ -201,18 +200,15 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
201
200
|
return item.value;
|
|
202
201
|
}
|
|
203
202
|
}
|
|
204
|
-
|
|
205
203
|
return item.value;
|
|
206
204
|
});
|
|
207
205
|
|
|
208
206
|
hasUserSelected.value = true;
|
|
209
207
|
multiSelectModel.value = selectedValues;
|
|
210
|
-
inputTextBackup.value =
|
|
211
|
-
multiSelectedItems.length > 3
|
|
212
|
-
? `${multiSelectedItems.length} items selected`
|
|
213
|
-
: multiSelectedItems.map((item) => item.text).join(', ');
|
|
214
208
|
|
|
215
209
|
updateMultiSelectedItemsFromValue();
|
|
210
|
+
|
|
211
|
+
emit('get-selected-options', multiSelectedItems);
|
|
216
212
|
};
|
|
217
213
|
|
|
218
214
|
/**
|
|
@@ -256,64 +252,92 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
256
252
|
* Handles stringified objects and updates the input text accordingly.
|
|
257
253
|
*/
|
|
258
254
|
const updateMultiSelectedItemsFromValue = () => {
|
|
259
|
-
|
|
255
|
+
const values = normalizedValue.value;
|
|
260
256
|
|
|
261
|
-
if (!
|
|
262
|
-
|
|
263
|
-
if (!values || !values.length) {
|
|
264
|
-
multiSelectedListItems.value = [];
|
|
265
|
-
inputText.value = '';
|
|
266
|
-
inputTextBackup.value = '';
|
|
257
|
+
if (!values || !values.length) {
|
|
258
|
+
multiSelectedListItems.value = [];
|
|
267
259
|
|
|
268
|
-
|
|
260
|
+
// Always show displayText if provided and no selected items
|
|
261
|
+
if (displayText.value) {
|
|
262
|
+
inputText.value = displayText.value;
|
|
263
|
+
} else if (!persistentDisplayText.value) {
|
|
264
|
+
inputText.value = '';
|
|
269
265
|
}
|
|
270
|
-
multiSelectedListItems.value = multiSelectOptions.value.filter((item) => {
|
|
271
|
-
return values.some((val) => {
|
|
272
|
-
let itemVal = item.value;
|
|
273
|
-
let valToCompare = val;
|
|
274
|
-
|
|
275
|
-
if (typeof itemVal === 'string' && itemVal.startsWith('{') && itemVal.endsWith('}')) {
|
|
276
|
-
try {
|
|
277
|
-
itemVal = JSON.parse(itemVal);
|
|
278
|
-
} catch {
|
|
279
|
-
// ignore
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
266
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
valToCompare = JSON.parse(valToCompare);
|
|
286
|
-
} catch {
|
|
287
|
-
// ignore
|
|
288
|
-
}
|
|
289
|
-
}
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
290
269
|
|
|
291
|
-
|
|
292
|
-
|
|
270
|
+
// Always keep multiSelectedListItems in sync with selected values
|
|
271
|
+
multiSelectedListItems.value = multiSelectOptions.value.filter((item) => {
|
|
272
|
+
return values.some((val) => {
|
|
273
|
+
let itemVal = item.value;
|
|
274
|
+
let valToCompare = val;
|
|
275
|
+
|
|
276
|
+
if (typeof itemVal === 'string' && itemVal.startsWith('{') && itemVal.endsWith('}')) {
|
|
277
|
+
try {
|
|
278
|
+
itemVal = JSON.parse(itemVal);
|
|
279
|
+
} catch {
|
|
280
|
+
// ignore error
|
|
293
281
|
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
282
|
+
}
|
|
283
|
+
if (typeof valToCompare === 'string' && valToCompare.startsWith('{') && valToCompare.endsWith('}')) {
|
|
284
|
+
try {
|
|
285
|
+
valToCompare = JSON.parse(valToCompare);
|
|
286
|
+
} catch {
|
|
287
|
+
// ignore error
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (typeof itemVal === 'object' && typeof valToCompare === 'object') {
|
|
291
|
+
return JSON.stringify(itemVal) === JSON.stringify(valToCompare);
|
|
292
|
+
}
|
|
293
|
+
return itemVal == valToCompare;
|
|
297
294
|
});
|
|
298
|
-
}
|
|
295
|
+
});
|
|
299
296
|
|
|
300
297
|
// Determine input text based on whether count-only mode is enabled
|
|
301
|
-
if (
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
298
|
+
if (!persistentDisplayText.value) {
|
|
299
|
+
if (props.displaySelectedCountOnly && values.length) {
|
|
300
|
+
inputText.value = `${values.length} item${values.length === 1 ? '' : 's'} selected`;
|
|
301
|
+
} else if (values.length > 3) {
|
|
302
|
+
inputText.value = `${values.length} items selected`;
|
|
303
|
+
} else {
|
|
304
|
+
// Safely get display text for each value
|
|
305
|
+
inputText.value = values
|
|
306
|
+
.map((val) => {
|
|
307
|
+
// Try to find the corresponding option for display text
|
|
308
|
+
const found = multiSelectOptions.value.find((opt) => {
|
|
309
|
+
let optVal = opt.value;
|
|
310
|
+
let v = val;
|
|
311
|
+
if (typeof optVal === 'string' && optVal.startsWith('{') && optVal.endsWith('}')) {
|
|
312
|
+
try {
|
|
313
|
+
optVal = JSON.parse(optVal);
|
|
314
|
+
} catch {
|
|
315
|
+
// ignore error
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
if (typeof v === 'string' && v.startsWith('{') && v.endsWith('}')) {
|
|
319
|
+
try {
|
|
320
|
+
v = JSON.parse(v);
|
|
321
|
+
} catch {
|
|
322
|
+
// ignore error
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (typeof optVal === 'object' && typeof v === 'object') {
|
|
326
|
+
return JSON.stringify(optVal) === JSON.stringify(v);
|
|
327
|
+
}
|
|
328
|
+
return optVal == v;
|
|
329
|
+
});
|
|
330
|
+
return found ? found.text : typeof val === 'string' || typeof val === 'number' ? String(val) : '';
|
|
331
|
+
})
|
|
332
|
+
.join(', ');
|
|
333
|
+
}
|
|
309
334
|
}
|
|
310
335
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
336
|
+
if (
|
|
337
|
+
persistentDisplayText.value ||
|
|
338
|
+
(displayText.value && !hasUserSelected.value && (!inputText.value || inputText.value === ''))
|
|
339
|
+
) {
|
|
315
340
|
inputText.value = displayText.value;
|
|
316
|
-
inputTextBackup.value = displayText.value;
|
|
317
341
|
}
|
|
318
342
|
};
|
|
319
343
|
|
|
@@ -354,12 +378,22 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
354
378
|
|
|
355
379
|
watch(searchInput, () => {
|
|
356
380
|
search.value = searchInput.value;
|
|
381
|
+
|
|
382
|
+
emit('search-string', searchInput.value);
|
|
357
383
|
});
|
|
358
384
|
|
|
359
385
|
watch(multiSelectPopperState, (newState) => {
|
|
360
386
|
emit('popper-state', newState);
|
|
361
387
|
});
|
|
362
388
|
|
|
389
|
+
// Watcher for displayText to update inputText when displayText changes and there are no selected items
|
|
390
|
+
watch(displayText, (newVal) => {
|
|
391
|
+
// Only update inputText if there are no selected items
|
|
392
|
+
if (!multiSelectedListItems.value.length) {
|
|
393
|
+
inputText.value = newVal;
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
363
397
|
/**
|
|
364
398
|
* Handles closing the multi-select when clicking outside.
|
|
365
399
|
*/
|
|
@@ -395,7 +429,6 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
395
429
|
updateMultiSelectedItemsFromValue();
|
|
396
430
|
} else if (displayText.value) {
|
|
397
431
|
inputText.value = displayText.value;
|
|
398
|
-
inputTextBackup.value = displayText.value;
|
|
399
432
|
}
|
|
400
433
|
});
|
|
401
434
|
|