design-system-next 2.9.1 → 2.9.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.js +5039 -5074
- package/dist/design-system-next.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 +80 -1
- package/src/components/banner/use-banner.ts +1 -1
- package/src/components/calendar/calendar.vue +7 -2
- package/src/components/calendar/use-calendar.ts +3 -1
- package/src/components/select/select-ladderized/use-select-ladderized.ts +164 -164
- package/src/components/select/select-multiple/select-multiple.ts +9 -3
- package/src/components/select/select-multiple/select-multiple.vue +49 -36
- package/src/components/select/select-multiple/use-select-multiple.ts +18 -5
- package/src/components/select/select.ts +8 -0
- package/src/components/select/select.vue +58 -47
- package/src/components/select/use-select.ts +34 -31
|
@@ -118,7 +118,9 @@ export const useCalendar = (props: CalendarPropTypes, emit: SetupContext<Calenda
|
|
|
118
118
|
};
|
|
119
119
|
|
|
120
120
|
const handleSorting = () => {
|
|
121
|
-
state.sort.value
|
|
121
|
+
if (state.sort.value === 'asc') state.sort.value = '';
|
|
122
|
+
else state.sort.value = state.sort.value === 'desc' ? 'asc' : 'desc';
|
|
123
|
+
|
|
122
124
|
emit('update:sort', state.sort.value);
|
|
123
125
|
};
|
|
124
126
|
|
|
@@ -1,164 +1,164 @@
|
|
|
1
|
-
import { ref, toRefs, computed, watch } from 'vue';
|
|
2
|
-
import { useVModel, useDebounceFn, onClickOutside } from '@vueuse/core';
|
|
3
|
-
|
|
4
|
-
import type { SelectLadderizedPropTypes } from './select-ladderized';
|
|
5
|
-
|
|
6
|
-
import type { MenuListType } from '@/components/list/list';
|
|
7
|
-
|
|
8
|
-
export const useSelectLadderized = (
|
|
9
|
-
props: SelectLadderizedPropTypes,
|
|
10
|
-
emit: (event: string, ...args: unknown[]) => void,
|
|
11
|
-
) => {
|
|
12
|
-
const { options, disabled } = toRefs(props);
|
|
13
|
-
|
|
14
|
-
const ladderizedClasses = computed(() => ({
|
|
15
|
-
baseClasses: 'spr-flex spr-flex-col spr-gap-size-spacing-4xs',
|
|
16
|
-
labelClasses: 'spr-body-sm-regular spr-text-color-strong spr-block',
|
|
17
|
-
}));
|
|
18
|
-
|
|
19
|
-
// Popper Variables
|
|
20
|
-
const ladderizedSelectPopperState = ref(false);
|
|
21
|
-
const ladderizedSelectRef = ref(null);
|
|
22
|
-
const isLadderizedSelectPopperDisabled = computed(() => disabled.value);
|
|
23
|
-
|
|
24
|
-
// Ladderized Select Model
|
|
25
|
-
const ladderizedSelectModel = useVModel(props, 'modelValue', emit);
|
|
26
|
-
const ladderizedSelectOptions = computed(() => options.value);
|
|
27
|
-
|
|
28
|
-
// Input Variables
|
|
29
|
-
const inputText = ref<string>('');
|
|
30
|
-
const isSearching = ref(false);
|
|
31
|
-
const wasCleared = ref(false);
|
|
32
|
-
|
|
33
|
-
const isLeafNode = (item: MenuListType): boolean => {
|
|
34
|
-
return !item.sublevel || item.sublevel.length === 0;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Helper to find the path to a selected value in the menu tree
|
|
38
|
-
const findPathToValue = (items: MenuListType[], value: string | number, path: string[] = []): string[] | null => {
|
|
39
|
-
for (const item of items) {
|
|
40
|
-
const newPath = [...path, item.text];
|
|
41
|
-
|
|
42
|
-
if (item.value === value) {
|
|
43
|
-
return newPath;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
if (item.sublevel) {
|
|
47
|
-
const result = findPathToValue(item.sublevel, value, newPath);
|
|
48
|
-
|
|
49
|
-
if (result) return result;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return null;
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const handleSelectedLadderizedItem = (selectedItems: string[], selectedItem?: MenuListType) => {
|
|
57
|
-
wasCleared.value = false;
|
|
58
|
-
ladderizedSelectModel.value = selectedItems;
|
|
59
|
-
|
|
60
|
-
let itemToCheck = selectedItem;
|
|
61
|
-
|
|
62
|
-
// Fallback: if selectedItem is not provided, try to find it from the value
|
|
63
|
-
if (!itemToCheck && selectedItems.length > 0) {
|
|
64
|
-
const findItemByValue = (items: MenuListType[], value: string | number): MenuListType | undefined => {
|
|
65
|
-
for (const item of items) {
|
|
66
|
-
if (item.value === value) return item;
|
|
67
|
-
|
|
68
|
-
if (item.sublevel) {
|
|
69
|
-
const found = findItemByValue(item.sublevel, value);
|
|
70
|
-
|
|
71
|
-
if (found) return found;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return undefined;
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
itemToCheck = findItemByValue(ladderizedSelectOptions.value, selectedItems[selectedItems.length - 1]);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (itemToCheck) {
|
|
82
|
-
const path = findPathToValue(ladderizedSelectOptions.value, itemToCheck.value);
|
|
83
|
-
|
|
84
|
-
inputText.value = path ? path.join(' > ') : itemToCheck.text || '';
|
|
85
|
-
|
|
86
|
-
if (isLeafNode(itemToCheck)) {
|
|
87
|
-
ladderizedSelectPopperState.value = false;
|
|
88
|
-
}
|
|
89
|
-
} else if (selectedItems.length === 0 && !wasCleared.value) {
|
|
90
|
-
inputText.value = '';
|
|
91
|
-
}
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
const handleSearch = () => {
|
|
95
|
-
isSearching.value = true;
|
|
96
|
-
|
|
97
|
-
debouncedEmitSearch();
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const debouncedEmitSearch = useDebounceFn(() => {
|
|
101
|
-
// Optionally emit search event here if needed
|
|
102
|
-
}, 300);
|
|
103
|
-
|
|
104
|
-
const handleClear = () => {
|
|
105
|
-
wasCleared.value = true;
|
|
106
|
-
|
|
107
|
-
inputText.value = '';
|
|
108
|
-
|
|
109
|
-
emit('update:modelValue', []);
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
const handleOptionsToggle = () => {
|
|
113
|
-
ladderizedSelectPopperState.value = true;
|
|
114
|
-
|
|
115
|
-
isSearching.value = false;
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
// Watch for changes in modelValue to update inputText
|
|
119
|
-
watch(
|
|
120
|
-
() => ladderizedSelectModel.value,
|
|
121
|
-
(newVal) => {
|
|
122
|
-
if (wasCleared.value) {
|
|
123
|
-
inputText.value = '';
|
|
124
|
-
wasCleared.value = false;
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (Array.isArray(newVal) && newVal.length > 0) {
|
|
129
|
-
// Treat the array as a single path for ladderized select
|
|
130
|
-
let currentLevel = ladderizedSelectOptions.value;
|
|
131
|
-
|
|
132
|
-
const pathTexts: string[] = [];
|
|
133
|
-
|
|
134
|
-
for (const value of newVal) {
|
|
135
|
-
const found = currentLevel.find((item) => item.value === value);
|
|
136
|
-
if (!found) break;
|
|
137
|
-
pathTexts.push(found.text);
|
|
138
|
-
currentLevel = found.sublevel || [];
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
inputText.value = pathTexts.join(' > ');
|
|
142
|
-
}
|
|
143
|
-
},
|
|
144
|
-
{ immediate: true },
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
onClickOutside(ladderizedSelectRef, () => {
|
|
148
|
-
ladderizedSelectPopperState.value = false;
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
ladderizedClasses,
|
|
153
|
-
ladderizedSelectPopperState,
|
|
154
|
-
ladderizedSelectRef,
|
|
155
|
-
ladderizedSelectOptions,
|
|
156
|
-
isLadderizedSelectPopperDisabled,
|
|
157
|
-
ladderizedSelectModel,
|
|
158
|
-
inputText,
|
|
159
|
-
handleSelectedLadderizedItem,
|
|
160
|
-
handleSearch,
|
|
161
|
-
handleClear,
|
|
162
|
-
handleOptionsToggle,
|
|
163
|
-
};
|
|
164
|
-
};
|
|
1
|
+
import { ref, toRefs, computed, watch } from 'vue';
|
|
2
|
+
import { useVModel, useDebounceFn, onClickOutside } from '@vueuse/core';
|
|
3
|
+
|
|
4
|
+
import type { SelectLadderizedPropTypes } from './select-ladderized';
|
|
5
|
+
|
|
6
|
+
import type { MenuListType } from '@/components/list/list';
|
|
7
|
+
|
|
8
|
+
export const useSelectLadderized = (
|
|
9
|
+
props: SelectLadderizedPropTypes,
|
|
10
|
+
emit: (event: string, ...args: unknown[]) => void,
|
|
11
|
+
) => {
|
|
12
|
+
const { options, disabled } = toRefs(props);
|
|
13
|
+
|
|
14
|
+
const ladderizedClasses = computed(() => ({
|
|
15
|
+
baseClasses: 'spr-flex spr-flex-col spr-gap-size-spacing-4xs',
|
|
16
|
+
labelClasses: 'spr-body-sm-regular spr-text-color-strong spr-block',
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
// Popper Variables
|
|
20
|
+
const ladderizedSelectPopperState = ref(false);
|
|
21
|
+
const ladderizedSelectRef = ref(null);
|
|
22
|
+
const isLadderizedSelectPopperDisabled = computed(() => disabled.value);
|
|
23
|
+
|
|
24
|
+
// Ladderized Select Model
|
|
25
|
+
const ladderizedSelectModel = useVModel(props, 'modelValue', emit);
|
|
26
|
+
const ladderizedSelectOptions = computed(() => options.value);
|
|
27
|
+
|
|
28
|
+
// Input Variables
|
|
29
|
+
const inputText = ref<string>('');
|
|
30
|
+
const isSearching = ref(false);
|
|
31
|
+
const wasCleared = ref(false);
|
|
32
|
+
|
|
33
|
+
const isLeafNode = (item: MenuListType): boolean => {
|
|
34
|
+
return !item.sublevel || item.sublevel.length === 0;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Helper to find the path to a selected value in the menu tree
|
|
38
|
+
const findPathToValue = (items: MenuListType[], value: string | number, path: string[] = []): string[] | null => {
|
|
39
|
+
for (const item of items) {
|
|
40
|
+
const newPath = [...path, item.text];
|
|
41
|
+
|
|
42
|
+
if (item.value === value) {
|
|
43
|
+
return newPath;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (item.sublevel) {
|
|
47
|
+
const result = findPathToValue(item.sublevel, value, newPath);
|
|
48
|
+
|
|
49
|
+
if (result) return result;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return null;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleSelectedLadderizedItem = (selectedItems: string[], selectedItem?: MenuListType) => {
|
|
57
|
+
wasCleared.value = false;
|
|
58
|
+
ladderizedSelectModel.value = selectedItems;
|
|
59
|
+
|
|
60
|
+
let itemToCheck = selectedItem;
|
|
61
|
+
|
|
62
|
+
// Fallback: if selectedItem is not provided, try to find it from the value
|
|
63
|
+
if (!itemToCheck && selectedItems.length > 0) {
|
|
64
|
+
const findItemByValue = (items: MenuListType[], value: string | number): MenuListType | undefined => {
|
|
65
|
+
for (const item of items) {
|
|
66
|
+
if (item.value === value) return item;
|
|
67
|
+
|
|
68
|
+
if (item.sublevel) {
|
|
69
|
+
const found = findItemByValue(item.sublevel, value);
|
|
70
|
+
|
|
71
|
+
if (found) return found;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return undefined;
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
itemToCheck = findItemByValue(ladderizedSelectOptions.value, selectedItems[selectedItems.length - 1]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (itemToCheck) {
|
|
82
|
+
const path = findPathToValue(ladderizedSelectOptions.value, itemToCheck.value);
|
|
83
|
+
|
|
84
|
+
inputText.value = path ? path.join(' > ') : itemToCheck.text || '';
|
|
85
|
+
|
|
86
|
+
if (isLeafNode(itemToCheck)) {
|
|
87
|
+
ladderizedSelectPopperState.value = false;
|
|
88
|
+
}
|
|
89
|
+
} else if (selectedItems.length === 0 && !wasCleared.value) {
|
|
90
|
+
inputText.value = '';
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const handleSearch = () => {
|
|
95
|
+
isSearching.value = true;
|
|
96
|
+
|
|
97
|
+
debouncedEmitSearch();
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const debouncedEmitSearch = useDebounceFn(() => {
|
|
101
|
+
// Optionally emit search event here if needed
|
|
102
|
+
}, 300);
|
|
103
|
+
|
|
104
|
+
const handleClear = () => {
|
|
105
|
+
wasCleared.value = true;
|
|
106
|
+
|
|
107
|
+
inputText.value = '';
|
|
108
|
+
|
|
109
|
+
emit('update:modelValue', []);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const handleOptionsToggle = () => {
|
|
113
|
+
ladderizedSelectPopperState.value = true;
|
|
114
|
+
|
|
115
|
+
isSearching.value = false;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Watch for changes in modelValue to update inputText
|
|
119
|
+
watch(
|
|
120
|
+
() => ladderizedSelectModel.value,
|
|
121
|
+
(newVal) => {
|
|
122
|
+
if (wasCleared.value) {
|
|
123
|
+
inputText.value = '';
|
|
124
|
+
wasCleared.value = false;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (Array.isArray(newVal) && newVal.length > 0) {
|
|
129
|
+
// Treat the array as a single path for ladderized select
|
|
130
|
+
let currentLevel = ladderizedSelectOptions.value;
|
|
131
|
+
|
|
132
|
+
const pathTexts: string[] = [];
|
|
133
|
+
|
|
134
|
+
for (const value of newVal) {
|
|
135
|
+
const found = currentLevel.find((item) => item.value === value);
|
|
136
|
+
if (!found) break;
|
|
137
|
+
pathTexts.push(found.text);
|
|
138
|
+
currentLevel = found.sublevel || [];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
inputText.value = pathTexts.join(' > ');
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
{ immediate: true },
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
onClickOutside(ladderizedSelectRef, () => {
|
|
148
|
+
ladderizedSelectPopperState.value = false;
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
ladderizedClasses,
|
|
153
|
+
ladderizedSelectPopperState,
|
|
154
|
+
ladderizedSelectRef,
|
|
155
|
+
ladderizedSelectOptions,
|
|
156
|
+
isLadderizedSelectPopperDisabled,
|
|
157
|
+
ladderizedSelectModel,
|
|
158
|
+
inputText,
|
|
159
|
+
handleSelectedLadderizedItem,
|
|
160
|
+
handleSearch,
|
|
161
|
+
handleClear,
|
|
162
|
+
handleOptionsToggle,
|
|
163
|
+
};
|
|
164
|
+
};
|
|
@@ -31,9 +31,7 @@ export const multiSelectPropTypes = {
|
|
|
31
31
|
required: true,
|
|
32
32
|
},
|
|
33
33
|
modelValue: {
|
|
34
|
-
type:
|
|
35
|
-
string | number | Record<string, unknown> | (string | number | Record<string, unknown>)[]
|
|
36
|
-
>,
|
|
34
|
+
type: Array as PropType<(string | number | Record<string, unknown>)[]>,
|
|
37
35
|
default: () => [],
|
|
38
36
|
},
|
|
39
37
|
options: {
|
|
@@ -102,10 +100,18 @@ export const multiSelectPropTypes = {
|
|
|
102
100
|
type: String,
|
|
103
101
|
default: '',
|
|
104
102
|
},
|
|
103
|
+
active: {
|
|
104
|
+
type: Boolean,
|
|
105
|
+
default: false,
|
|
106
|
+
},
|
|
105
107
|
disabled: {
|
|
106
108
|
type: Boolean,
|
|
107
109
|
default: false,
|
|
108
110
|
},
|
|
111
|
+
error: {
|
|
112
|
+
type: Boolean,
|
|
113
|
+
default: false,
|
|
114
|
+
},
|
|
109
115
|
clearable: {
|
|
110
116
|
type: Boolean,
|
|
111
117
|
default: false,
|
|
@@ -23,46 +23,59 @@
|
|
|
23
23
|
width: props.width,
|
|
24
24
|
}"
|
|
25
25
|
>
|
|
26
|
-
<div
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
<div ref="multiSelectRef">
|
|
27
|
+
<div @click="handleOptionsToggle">
|
|
28
|
+
<spr-input
|
|
29
|
+
v-model="inputText"
|
|
30
|
+
:class="{
|
|
31
|
+
'spr-cursor-pointer': true,
|
|
32
|
+
}"
|
|
33
|
+
:placeholder="props.placeholder"
|
|
34
|
+
autocomplete="off"
|
|
35
|
+
:helper-text="props.helperText"
|
|
36
|
+
:helper-icon="props.helperIcon"
|
|
37
|
+
:display-helper="props.displayHelper"
|
|
38
|
+
:active="props.active"
|
|
39
|
+
:readonly="true"
|
|
40
|
+
:disabled="props.disabled"
|
|
41
|
+
:error="props.error"
|
|
42
|
+
>
|
|
43
|
+
<template #icon>
|
|
44
|
+
<div class="spr-flex spr-items-center spr-gap-1">
|
|
45
|
+
<Icon
|
|
46
|
+
v-if="props.clearable && inputText"
|
|
47
|
+
class="spr-cursor-pointer"
|
|
48
|
+
icon="ph:x"
|
|
49
|
+
@click.stop="handleClear"
|
|
50
|
+
/>
|
|
51
|
+
<Icon icon="ph:caret-down" />
|
|
52
|
+
</div>
|
|
53
|
+
</template>
|
|
54
|
+
|
|
55
|
+
<template #helperMessage>
|
|
56
|
+
<slot name="helperMessage" />
|
|
57
|
+
</template>
|
|
58
|
+
</spr-input>
|
|
59
|
+
|
|
60
|
+
<!-- Hidden Select for QA automation -->
|
|
61
|
+
<select v-if="multiSelectOptions && multiSelectOptions.length" v-model="multiSelectModel" multiple hidden>
|
|
62
|
+
<option v-for="option in multiSelectOptions" :key="option.value" :value="option.value">
|
|
63
|
+
{{ option.text }}
|
|
64
|
+
</option>
|
|
65
|
+
</select>
|
|
66
|
+
</div>
|
|
67
|
+
|
|
68
|
+
<!-- This div used to poppulate popper menu -->
|
|
69
|
+
<div
|
|
70
|
+
:id="props.id"
|
|
71
|
+
:style="{
|
|
72
|
+
width: props.popperWidth,
|
|
31
73
|
}"
|
|
32
|
-
|
|
33
|
-
:readonly="true"
|
|
34
|
-
:disabled="props.disabled"
|
|
35
|
-
autocomplete="off"
|
|
36
|
-
:helper-text="props.helperText"
|
|
37
|
-
:helper-icon="props.helperIcon"
|
|
38
|
-
:display-helper="props.displayHelper"
|
|
39
|
-
>
|
|
40
|
-
<template #icon>
|
|
41
|
-
<div class="spr-flex spr-items-center spr-gap-1">
|
|
42
|
-
<Icon
|
|
43
|
-
v-if="props.clearable && inputText"
|
|
44
|
-
class="spr-cursor-pointer"
|
|
45
|
-
icon="ph:x"
|
|
46
|
-
@click.stop="handleClear"
|
|
47
|
-
/>
|
|
48
|
-
<Icon icon="ph:caret-down" />
|
|
49
|
-
</div>
|
|
50
|
-
</template>
|
|
51
|
-
</spr-input>
|
|
74
|
+
></div>
|
|
52
75
|
</div>
|
|
53
76
|
|
|
54
|
-
<div
|
|
55
|
-
:id="props.id"
|
|
56
|
-
:style="{
|
|
57
|
-
width: props.popperWidth,
|
|
58
|
-
}"
|
|
59
|
-
></div>
|
|
60
|
-
|
|
61
77
|
<template #popper>
|
|
62
|
-
<div
|
|
63
|
-
ref="multiSelectRef"
|
|
64
|
-
class="spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden spr-p-2"
|
|
65
|
-
>
|
|
78
|
+
<div class="spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden spr-p-2">
|
|
66
79
|
<template v-if="multiSelectOptions.length > 0">
|
|
67
80
|
<spr-list
|
|
68
81
|
v-model="multiSelectedListItems"
|
|
@@ -118,7 +118,7 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
118
118
|
* Opens the multi-select options.
|
|
119
119
|
*/
|
|
120
120
|
const handleOptionsToggle = () => {
|
|
121
|
-
multiSelectPopperState.value =
|
|
121
|
+
multiSelectPopperState.value = !multiSelectPopperState.value;
|
|
122
122
|
};
|
|
123
123
|
|
|
124
124
|
/**
|
|
@@ -140,7 +140,6 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
140
140
|
|
|
141
141
|
hasUserSelected.value = true;
|
|
142
142
|
multiSelectModel.value = selectedValues;
|
|
143
|
-
multiSelectPopperState.value = true;
|
|
144
143
|
inputTextBackup.value =
|
|
145
144
|
multiSelectedItems.length > 3
|
|
146
145
|
? `${multiSelectedItems.length} items selected`
|
|
@@ -224,9 +223,23 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
224
223
|
updateMultiSelectedItemsFromValue();
|
|
225
224
|
});
|
|
226
225
|
|
|
227
|
-
watch(
|
|
228
|
-
|
|
229
|
-
|
|
226
|
+
watch(
|
|
227
|
+
multiSelectOptions,
|
|
228
|
+
() => {
|
|
229
|
+
updateMultiSelectedItemsFromValue();
|
|
230
|
+
},
|
|
231
|
+
{ deep: true },
|
|
232
|
+
);
|
|
233
|
+
|
|
234
|
+
// Add watcher for options prop to re-process options when changed
|
|
235
|
+
watch(
|
|
236
|
+
options,
|
|
237
|
+
() => {
|
|
238
|
+
processOptions();
|
|
239
|
+
updateMultiSelectedItemsFromValue();
|
|
240
|
+
},
|
|
241
|
+
{ deep: true },
|
|
242
|
+
);
|
|
230
243
|
|
|
231
244
|
/**
|
|
232
245
|
* Handles closing the multi-select when clicking outside.
|
|
@@ -102,10 +102,18 @@ export const selectPropTypes = {
|
|
|
102
102
|
type: String,
|
|
103
103
|
default: '',
|
|
104
104
|
},
|
|
105
|
+
active: {
|
|
106
|
+
type: Boolean,
|
|
107
|
+
default: false,
|
|
108
|
+
},
|
|
105
109
|
disabled: {
|
|
106
110
|
type: Boolean,
|
|
107
111
|
default: false,
|
|
108
112
|
},
|
|
113
|
+
error: {
|
|
114
|
+
type: Boolean,
|
|
115
|
+
default: false,
|
|
116
|
+
},
|
|
109
117
|
clearable: {
|
|
110
118
|
type: Boolean,
|
|
111
119
|
default: false,
|
|
@@ -23,58 +23,68 @@
|
|
|
23
23
|
width: props.width,
|
|
24
24
|
}"
|
|
25
25
|
>
|
|
26
|
-
<div
|
|
27
|
-
<
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
26
|
+
<div ref="selectRef">
|
|
27
|
+
<div @click="handleOptionsToggle">
|
|
28
|
+
<spr-input
|
|
29
|
+
v-model="inputText"
|
|
30
|
+
:class="{
|
|
31
|
+
'spr-cursor-pointer': !props.searchable,
|
|
32
|
+
}"
|
|
33
|
+
:placeholder="props.placeholder"
|
|
34
|
+
autocomplete="off"
|
|
35
|
+
:helper-text="props.helperText"
|
|
36
|
+
:helper-icon="props.helperIcon"
|
|
37
|
+
:display-helper="props.displayHelper"
|
|
38
|
+
:active="props.active"
|
|
39
|
+
:readonly="!props.searchable"
|
|
40
|
+
:disabled="props.disabled"
|
|
41
|
+
:error="props.error"
|
|
42
|
+
@keyup="handleSearch"
|
|
43
|
+
>
|
|
44
|
+
<template #icon>
|
|
45
|
+
<div class="spr-flex spr-cursor-pointer spr-items-center">
|
|
46
|
+
<Icon
|
|
47
|
+
v-if="props.clearable && inputText"
|
|
48
|
+
class="spr-cursor-pointer"
|
|
49
|
+
icon="ph:x"
|
|
50
|
+
@click.stop="handleClear"
|
|
51
|
+
/>
|
|
52
|
+
<Icon icon="ph:caret-down" />
|
|
53
|
+
</div>
|
|
54
|
+
</template>
|
|
53
55
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
56
|
+
<template #helperMessage>
|
|
57
|
+
<slot name="helperMessage" />
|
|
58
|
+
</template>
|
|
59
|
+
</spr-input>
|
|
60
|
+
|
|
61
|
+
<!-- Hidden Select for QA automation -->
|
|
62
|
+
<select
|
|
63
|
+
v-if="selectOptions && selectOptions.length"
|
|
64
|
+
:value="Array.isArray(selectModel) ? selectModel[0] : selectModel"
|
|
65
|
+
data-testid="qa-hidden-select"
|
|
66
|
+
tabindex="-1"
|
|
67
|
+
aria-hidden="true"
|
|
68
|
+
hidden
|
|
69
|
+
>
|
|
70
|
+
<option v-for="item in selectOptions" :key="item.value" :value="item.value">
|
|
71
|
+
{{ item.text }}
|
|
72
|
+
</option>
|
|
73
|
+
</select>
|
|
74
|
+
</div>
|
|
67
75
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
76
|
+
<!-- This div used to poppulate popper menu -->
|
|
77
|
+
<div
|
|
78
|
+
:id="props.id"
|
|
79
|
+
:style="{
|
|
80
|
+
width: props.popperWidth,
|
|
81
|
+
}"
|
|
82
|
+
></div>
|
|
83
|
+
</div>
|
|
74
84
|
|
|
75
85
|
<template #popper>
|
|
76
86
|
<div
|
|
77
|
-
ref="
|
|
87
|
+
ref="selectPopperRef"
|
|
78
88
|
class="spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden spr-p-2"
|
|
79
89
|
>
|
|
80
90
|
<template v-if="isSearching">
|
|
@@ -153,6 +163,7 @@ const {
|
|
|
153
163
|
selectClasses,
|
|
154
164
|
selectPopperState,
|
|
155
165
|
selectRef,
|
|
166
|
+
selectPopperRef,
|
|
156
167
|
selectModel,
|
|
157
168
|
selectOptions,
|
|
158
169
|
filteredSelectOptions,
|