design-system-next 2.7.44 → 2.8.0
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 +6416 -5577
- 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/assets/styles/tailwind.css +3 -1
- package/src/components/date-picker/use-date-picker.ts +3 -10
- package/src/components/dropdown/__tests__/dropdown-fixes.spec.ts +106 -0
- package/src/components/dropdown/__tests__/dropdown-value-types.spec.ts +213 -0
- package/src/components/list/ladderized-list/ladderized-list.ts +12 -0
- package/src/components/list/ladderized-list/ladderized-list.vue +6 -0
- package/src/components/list/list.ts +15 -3
- package/src/components/list/list.vue +37 -9
- package/src/components/list/use-list.ts +116 -47
- package/src/components/select/select-ladderized/select-ladderized.ts +108 -0
- package/src/components/select/select-ladderized/select-ladderized.vue +111 -0
- package/src/components/select/select-ladderized/use-select-ladderized.ts +165 -0
- package/src/components/select/select-multiple/select-multiple.ts +128 -115
- package/src/components/select/select-multiple/select-multiple.vue +149 -88
- package/src/components/select/select-multiple/use-select-multiple.ts +367 -499
- package/src/components/select/select.ts +34 -4
- package/src/components/select/select.vue +137 -59
- package/src/components/select/use-select.ts +166 -249
- package/src/components/select/select-laderrized/select-laderrized.ts +0 -122
- package/src/components/select/select-laderrized/select-laderrized.vue +0 -110
- package/src/components/select/select-laderrized/use-select-laderrized.ts +0 -499
- package/src/examples/select-number-multi-select.vue +0 -71
|
@@ -1,21 +1,54 @@
|
|
|
1
|
-
import { ref, toRefs, computed, onMounted, watch } from 'vue';
|
|
2
|
-
import { onClickOutside, useInfiniteScroll, useVModel } from '@vueuse/core';
|
|
1
|
+
import { ref, toRefs, computed, ComputedRef, onMounted, watch } from 'vue';
|
|
2
|
+
import { onClickOutside, useInfiniteScroll, useVModel, useDebounceFn } from '@vueuse/core';
|
|
3
|
+
|
|
4
|
+
import classNames from 'classnames';
|
|
3
5
|
|
|
4
6
|
import type { SetupContext } from 'vue';
|
|
5
7
|
import type { SelectPropTypes, SelectEmitTypes } from './select';
|
|
6
8
|
import type { MenuListType } from '../list/list';
|
|
7
9
|
|
|
10
|
+
interface SelectClasses {
|
|
11
|
+
baseClasses: string;
|
|
12
|
+
labelClasses: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitTypes>['emit']) => {
|
|
9
|
-
const {
|
|
16
|
+
const { displayText, menuList, disabled, textField, valueField, disabledLocalSearch } = toRefs(props);
|
|
17
|
+
|
|
18
|
+
const selectClasses: ComputedRef<SelectClasses> = computed(() => {
|
|
19
|
+
const baseClasses = classNames('spr-flex spr-flex-col spr-gap-size-spacing-4xs');
|
|
20
|
+
|
|
21
|
+
const labelClasses = classNames('spr-body-sm-regular spr-text-color-strong spr-block', {
|
|
22
|
+
'spr-text-color-on-fill-disabled': disabled.value,
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
baseClasses,
|
|
27
|
+
labelClasses,
|
|
28
|
+
};
|
|
29
|
+
});
|
|
10
30
|
|
|
31
|
+
// Popper Variables
|
|
11
32
|
const selectRef = ref<HTMLDivElement | null>(null);
|
|
12
33
|
const selectPopperState = ref<boolean>(false);
|
|
13
34
|
const isSelectPopperDisabled = computed(() => disabled.value);
|
|
14
35
|
|
|
36
|
+
// Select Variables
|
|
15
37
|
const selectModel = useVModel(props, 'modelValue', emit);
|
|
16
|
-
const
|
|
17
|
-
const selectedListItems = ref<MenuListType[]>([]);
|
|
38
|
+
const selectedListItems = ref<MenuListType[]>();
|
|
18
39
|
const selectMenuList = ref<MenuListType[]>([]);
|
|
40
|
+
const hasUserSelected = ref(false);
|
|
41
|
+
|
|
42
|
+
// Input Text Variables
|
|
43
|
+
const inputText = ref<string | number>('');
|
|
44
|
+
const inputTextBackup = ref<string | number>('');
|
|
45
|
+
const isSearching = ref<boolean>(false);
|
|
46
|
+
|
|
47
|
+
const handleMenuToggle = () => {
|
|
48
|
+
selectPopperState.value = true;
|
|
49
|
+
|
|
50
|
+
isSearching.value = false;
|
|
51
|
+
};
|
|
19
52
|
|
|
20
53
|
// Normalized value for internal use - always an array
|
|
21
54
|
const normalizedValue = computed(() => {
|
|
@@ -35,21 +68,20 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
35
68
|
|
|
36
69
|
// Compatibility layer for pre-selected items (List component expects string[] format)
|
|
37
70
|
const compatPreSelectedItems = computed(() => {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
]
|
|
46
|
-
: [];
|
|
71
|
+
if (selectModel.value === undefined || selectModel.value === null) return [];
|
|
72
|
+
|
|
73
|
+
if (Array.isArray(selectModel.value)) {
|
|
74
|
+
return selectModel.value;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return [selectModel.value];
|
|
47
78
|
});
|
|
48
79
|
|
|
49
80
|
const processMenuList = () => {
|
|
50
81
|
// Handle empty array or non-array values
|
|
51
82
|
if (!menuList.value || !Array.isArray(menuList.value) || menuList.value.length === 0) {
|
|
52
83
|
selectMenuList.value = [];
|
|
84
|
+
|
|
53
85
|
return;
|
|
54
86
|
}
|
|
55
87
|
|
|
@@ -71,6 +103,7 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
71
103
|
text: item.toString(),
|
|
72
104
|
value: item, // Keep the value as a number instead of converting to string
|
|
73
105
|
}));
|
|
106
|
+
|
|
74
107
|
return;
|
|
75
108
|
}
|
|
76
109
|
|
|
@@ -99,105 +132,45 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
99
132
|
return;
|
|
100
133
|
}
|
|
101
134
|
|
|
102
|
-
// Default fallback
|
|
103
135
|
selectMenuList.value = menuList.value as MenuListType[];
|
|
104
136
|
};
|
|
105
137
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
const handleSearch = () => {
|
|
111
|
-
if (menuList.value && menuList.value.length === 0) {
|
|
112
|
-
return;
|
|
138
|
+
const filteredSelectMenuList = computed(() => {
|
|
139
|
+
if (disabledLocalSearch.value) {
|
|
140
|
+
return selectMenuList.value;
|
|
113
141
|
}
|
|
114
142
|
|
|
115
|
-
|
|
116
|
-
singleSelectSearch();
|
|
117
|
-
} else {
|
|
118
|
-
// Process menu list for searching
|
|
119
|
-
processMenuList();
|
|
143
|
+
const query = inputText.value.toString().toLowerCase().trim();
|
|
120
144
|
|
|
121
|
-
|
|
122
|
-
if (searchString.value.trim() !== '') {
|
|
123
|
-
selectMenuList.value = getFilteredMenuList(selectMenuList.value);
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
};
|
|
145
|
+
if (!query) return selectMenuList.value;
|
|
127
146
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
ladderizedSearch();
|
|
131
|
-
} else {
|
|
132
|
-
basicSearch();
|
|
133
|
-
}
|
|
134
|
-
};
|
|
147
|
+
return selectMenuList.value.filter((item) => item.text?.toString().toLowerCase().includes(query));
|
|
148
|
+
});
|
|
135
149
|
|
|
136
|
-
|
|
137
|
-
// Process menu list first
|
|
150
|
+
watch(menuList, () => {
|
|
138
151
|
processMenuList();
|
|
152
|
+
});
|
|
139
153
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
const ladderizedSearch = () => {
|
|
147
|
-
//revert to initial list if search string is empty or selectModel is not empty
|
|
148
|
-
if (searchString.value === '' || normalizedValue.value.length > 0) {
|
|
149
|
-
selectMenuList.value = [...menuList.value] as MenuListType[];
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
const menuListSubLevels = getAllSublevelItems(menuList.value as MenuListType[]);
|
|
154
|
-
|
|
155
|
-
const filteredMenuList = getFilteredMenuList(menuList.value as MenuListType[]);
|
|
156
|
-
const filteredMenuListSubLevels = getFilteredMenuList(menuListSubLevels);
|
|
157
|
-
|
|
158
|
-
if (filteredMenuList.length > 0) {
|
|
159
|
-
//if there is a match at the top level of the menuList
|
|
160
|
-
selectMenuList.value = getAllSublevelItems(filteredMenuList);
|
|
161
|
-
} else if (filteredMenuListSubLevels.length > 0) {
|
|
162
|
-
//if there is a match at the 2nd level (sublevel of a menuList item) of the menuList
|
|
163
|
-
selectMenuList.value = filteredMenuListSubLevels;
|
|
164
|
-
} else {
|
|
165
|
-
selectMenuList.value = [];
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
// compile sublevel items from menuList to a single array
|
|
170
|
-
// and add text and value of the parent item to all sublevel items as subtext and subvalue
|
|
171
|
-
const getAllSublevelItems = (menuList: MenuListType[]) => {
|
|
172
|
-
return menuList.reduce<MenuListType[]>((currentValue, currentItem) => {
|
|
173
|
-
if (currentItem.sublevel) {
|
|
174
|
-
const mappedSublevel = currentItem.sublevel.map((sublevelItem: MenuListType) => ({
|
|
175
|
-
...sublevelItem, //text and value of a sublevel item
|
|
176
|
-
subtext: currentItem.text, // text of parent of a sublevel item
|
|
177
|
-
subvalue: typeof currentItem.value === 'string' ? currentItem.value : String(currentItem.value), // value of parent of a sublevel item as string
|
|
178
|
-
}));
|
|
179
|
-
|
|
180
|
-
return [...currentValue, ...mappedSublevel];
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
return currentValue;
|
|
184
|
-
}, [] as MenuListType[]);
|
|
185
|
-
};
|
|
154
|
+
// Search handler: always emit search-string, but only filter locally if local search is enabled
|
|
155
|
+
const handleSearch = () => {
|
|
156
|
+
isSearching.value = true;
|
|
186
157
|
|
|
187
|
-
|
|
188
|
-
const getFilteredMenuList = (list: MenuListType[]) => {
|
|
189
|
-
return list.filter((item: MenuListType) => {
|
|
190
|
-
const searchTerm = searchString.value.toLowerCase().trim();
|
|
191
|
-
return item.text.toLowerCase().includes(searchTerm);
|
|
192
|
-
});
|
|
158
|
+
debouncedEmitSearch();
|
|
193
159
|
};
|
|
194
160
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
});
|
|
161
|
+
const debouncedEmitSearch = useDebounceFn(() => {
|
|
162
|
+
emit('search-string', inputText.value);
|
|
163
|
+
}, 300);
|
|
198
164
|
|
|
199
165
|
onClickOutside(selectRef, () => {
|
|
200
166
|
selectPopperState.value = false;
|
|
167
|
+
|
|
168
|
+
// If user was searching, restore inputText from backup
|
|
169
|
+
if (isSearching.value) {
|
|
170
|
+
inputText.value = inputTextBackup.value;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
isSearching.value = false;
|
|
201
174
|
});
|
|
202
175
|
|
|
203
176
|
useInfiniteScroll(
|
|
@@ -210,114 +183,40 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
210
183
|
|
|
211
184
|
// Handle selected item for simple list component
|
|
212
185
|
const handleSelectedItem = (selectedItems: MenuListType[]) => {
|
|
213
|
-
if (
|
|
214
|
-
|
|
215
|
-
if (multiSelect.value) {
|
|
216
|
-
// For multi-select, always return an array
|
|
217
|
-
const values = selectedItems.map((item) => {
|
|
218
|
-
// If we stored the original object, use it
|
|
219
|
-
if ('_originalObject' in item) {
|
|
220
|
-
return item._originalObject;
|
|
221
|
-
}
|
|
186
|
+
if (selectedItems.length === 0) {
|
|
187
|
+
selectModel.value = '';
|
|
222
188
|
|
|
223
|
-
|
|
224
|
-
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
225
191
|
|
|
226
|
-
|
|
227
|
-
if (typeof val === 'number') {
|
|
228
|
-
return val;
|
|
229
|
-
}
|
|
192
|
+
hasUserSelected.value = true; // User has now made a selection
|
|
230
193
|
|
|
231
|
-
|
|
232
|
-
if (typeof val === 'string' && !isNaN(Number(val)) && val.trim() !== '') {
|
|
233
|
-
// Only convert if it looks like a proper number format
|
|
234
|
-
if (/^-?\d+(\.\d+)?$/.test(val)) {
|
|
235
|
-
return Number(val);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
194
|
+
const item = selectedItems[0];
|
|
238
195
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
196
|
+
// If we stored the original object, use it
|
|
197
|
+
if ('_originalObject' in item) {
|
|
198
|
+
selectModel.value = item._originalObject as Record<string, unknown>;
|
|
199
|
+
} else {
|
|
200
|
+
// For simple types, return the value (try to convert number strings to numbers)
|
|
201
|
+
const itemValue = item.value;
|
|
202
|
+
const itemText = item.text || '';
|
|
242
203
|
|
|
243
|
-
|
|
204
|
+
if (typeof itemValue === 'string' && !isNaN(Number(itemValue)) && itemValue.trim() !== '') {
|
|
205
|
+
selectModel.value = Number(itemValue);
|
|
244
206
|
} else {
|
|
245
|
-
|
|
246
|
-
if (selectedItems.length === 0) {
|
|
247
|
-
selectModel.value = props.multiSelect ? [] : '';
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const item = selectedItems[0];
|
|
252
|
-
|
|
253
|
-
// If we stored the original object, use it
|
|
254
|
-
if ('_originalObject' in item) {
|
|
255
|
-
selectModel.value = item._originalObject as Record<string, unknown>;
|
|
256
|
-
} else {
|
|
257
|
-
// For simple types, return the value (try to convert number strings to numbers)
|
|
258
|
-
const val = item.value;
|
|
259
|
-
if (typeof val === 'string' && !isNaN(Number(val)) && val.trim() !== '') {
|
|
260
|
-
selectModel.value = Number(val);
|
|
261
|
-
} else {
|
|
262
|
-
selectModel.value = val;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
207
|
+
selectModel.value = itemValue;
|
|
265
208
|
}
|
|
266
|
-
} else if (props.ladderized) {
|
|
267
|
-
if (props.searchString !== '') {
|
|
268
|
-
// generate select value if ladderized with search string
|
|
269
|
-
const subvalue = selectedItems[0]?.subvalue;
|
|
270
|
-
const value = selectedItems[0]?.value;
|
|
271
|
-
if (subvalue !== undefined && value !== undefined) {
|
|
272
|
-
selectModel.value = [subvalue, value] as [string, string | number];
|
|
273
|
-
}
|
|
274
|
-
} else {
|
|
275
|
-
// For regular ladderized select selection without search
|
|
276
|
-
if (selectedItems.length > 0) {
|
|
277
|
-
const item = selectedItems[0];
|
|
278
|
-
// Use the value directly for ladderized items
|
|
279
|
-
if (item && item.value) {
|
|
280
|
-
selectModel.value = item.value;
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
209
|
|
|
286
|
-
|
|
287
|
-
if (!multiSelect.value) {
|
|
288
|
-
setTimeout(() => {
|
|
289
|
-
selectPopperState.value = false;
|
|
290
|
-
}, 10);
|
|
210
|
+
inputText.value = itemText;
|
|
291
211
|
}
|
|
292
|
-
};
|
|
293
212
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// Update the model value with the selected ladderized items
|
|
297
|
-
if (selectedItems.length > 0) {
|
|
298
|
-
selectModel.value = selectedItems;
|
|
299
|
-
}
|
|
213
|
+
// Clone inputText to backup after selection
|
|
214
|
+
inputTextBackup.value = inputText.value;
|
|
300
215
|
|
|
301
|
-
//
|
|
302
|
-
|
|
216
|
+
// Always close select for single selection
|
|
217
|
+
setTimeout(() => {
|
|
303
218
|
selectPopperState.value = false;
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
const checkIfItemFromLastSublevel = (selectedItems: string[]) => {
|
|
308
|
-
let selectedItemsObject = selectMenuList.value;
|
|
309
|
-
|
|
310
|
-
// Traverse to the last item in the selectedItems array
|
|
311
|
-
selectedItems.forEach((selectedItem) => {
|
|
312
|
-
selectedItemsObject = selectedItemsObject.find((item) => selectedItem === item.value)?.sublevel ?? [];
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
// If there is a sublevel, return false
|
|
316
|
-
if (selectedItemsObject.length > 0) {
|
|
317
|
-
return false;
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
return true;
|
|
219
|
+
}, 10);
|
|
321
220
|
};
|
|
322
221
|
|
|
323
222
|
// Update selected items when model value changes externally
|
|
@@ -325,8 +224,26 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
325
224
|
if (!selectMenuList.value.length) return;
|
|
326
225
|
|
|
327
226
|
const values = normalizedValue.value;
|
|
227
|
+
|
|
328
228
|
if (!values || !values.length) {
|
|
329
229
|
selectedListItems.value = [];
|
|
230
|
+
|
|
231
|
+
// Only set displayText if user hasn't typed anything
|
|
232
|
+
if (
|
|
233
|
+
displayText.value &&
|
|
234
|
+
!hasUserSelected.value &&
|
|
235
|
+
!isSearching.value &&
|
|
236
|
+
(!inputText.value || inputText.value === '')
|
|
237
|
+
) {
|
|
238
|
+
inputText.value = displayText.value;
|
|
239
|
+
|
|
240
|
+
// Clone displayText to backup if present
|
|
241
|
+
inputTextBackup.value = displayText.value;
|
|
242
|
+
} else if (!hasUserSelected.value && (!inputText.value || inputText.value === '')) {
|
|
243
|
+
inputText.value = '';
|
|
244
|
+
inputTextBackup.value = '';
|
|
245
|
+
}
|
|
246
|
+
|
|
330
247
|
return;
|
|
331
248
|
}
|
|
332
249
|
|
|
@@ -355,63 +272,57 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
355
272
|
// Extract just string values for comparison
|
|
356
273
|
const valueStrings = valueData.map((v) => v.string);
|
|
357
274
|
|
|
358
|
-
|
|
359
|
-
//
|
|
360
|
-
if (
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
275
|
+
selectedListItems.value = selectMenuList.value.filter((item) => {
|
|
276
|
+
// Handle objects with _originalObject property
|
|
277
|
+
if ('_originalObject' in item && item._originalObject) {
|
|
278
|
+
return valueData.some((v) => {
|
|
279
|
+
// If both are objects, compare by JSON string or by ID
|
|
280
|
+
if (v.isObject && typeof v.original === 'object') {
|
|
281
|
+
const originalObj = item._originalObject as Record<string, unknown>;
|
|
364
282
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
} else {
|
|
375
|
-
return valueStrings.includes(String(item.value));
|
|
283
|
+
if (v.original === originalObj) return true;
|
|
284
|
+
|
|
285
|
+
const itemJson = JSON.stringify(originalObj);
|
|
286
|
+
|
|
287
|
+
if (v.string === itemJson) return true;
|
|
288
|
+
|
|
289
|
+
if (v.id !== undefined && 'id' in originalObj) {
|
|
290
|
+
return v.id === originalObj.id;
|
|
291
|
+
}
|
|
376
292
|
}
|
|
293
|
+
return false;
|
|
377
294
|
});
|
|
378
295
|
}
|
|
379
|
-
|
|
380
|
-
//
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
}
|
|
401
|
-
return false;
|
|
402
|
-
});
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
// Handle both numeric and string values correctly
|
|
406
|
-
if (typeof item.value === 'number') {
|
|
407
|
-
return valueData.some((v) => v.original === item.value || v.string === String(item.value));
|
|
408
|
-
} else {
|
|
409
|
-
return valueStrings.includes(String(item.value));
|
|
410
|
-
}
|
|
411
|
-
});
|
|
296
|
+
|
|
297
|
+
// Handle both numeric and string values correctly
|
|
298
|
+
if (typeof item.value === 'number') {
|
|
299
|
+
return valueData.some((v) => v.original === item.value || v.string === String(item.value));
|
|
300
|
+
} else {
|
|
301
|
+
return valueStrings.includes(String(item.value));
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
// Only update inputText if not searching
|
|
306
|
+
if (!isSearching.value) {
|
|
307
|
+
inputText.value = selectedListItems.value.map((item) => item.text).join(', ');
|
|
308
|
+
|
|
309
|
+
// Only use displayText.value if user hasn't selected anything yet
|
|
310
|
+
if (displayText.value && !hasUserSelected.value && (!inputText.value || inputText.value === '')) {
|
|
311
|
+
inputText.value = displayText.value;
|
|
312
|
+
inputTextBackup.value = displayText.value;
|
|
313
|
+
} else {
|
|
314
|
+
// Always update backup to match inputText if not searching
|
|
315
|
+
inputTextBackup.value = inputText.value;
|
|
316
|
+
}
|
|
412
317
|
}
|
|
413
318
|
};
|
|
414
319
|
|
|
320
|
+
const handleClear = () => {
|
|
321
|
+
emit('update:modelValue', '');
|
|
322
|
+
|
|
323
|
+
inputText.value = '';
|
|
324
|
+
};
|
|
325
|
+
|
|
415
326
|
watch(selectModel, () => {
|
|
416
327
|
updateSelectedItemsFromValue();
|
|
417
328
|
});
|
|
@@ -426,20 +337,26 @@ export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitT
|
|
|
426
337
|
// Set initial selected items based on model value
|
|
427
338
|
if (normalizedValue.value.length > 0) {
|
|
428
339
|
updateSelectedItemsFromValue();
|
|
340
|
+
} else if (displayText.value) {
|
|
341
|
+
inputText.value = displayText.value;
|
|
342
|
+
inputTextBackup.value = displayText.value;
|
|
429
343
|
}
|
|
430
344
|
});
|
|
431
345
|
|
|
432
346
|
return {
|
|
347
|
+
selectClasses,
|
|
433
348
|
selectPopperState,
|
|
349
|
+
handleMenuToggle,
|
|
434
350
|
selectRef,
|
|
435
|
-
|
|
351
|
+
selectModel: compatPreSelectedItems, // Use compatible format for lists
|
|
436
352
|
selectMenuList,
|
|
437
|
-
|
|
353
|
+
filteredSelectMenuList,
|
|
438
354
|
selectedListItems,
|
|
355
|
+
inputText,
|
|
356
|
+
isSelectPopperDisabled,
|
|
357
|
+
isSearching,
|
|
439
358
|
handleSelectedItem,
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
removeCurrentLevelInBackLabel,
|
|
443
|
-
isLadderizedSearch,
|
|
359
|
+
handleSearch,
|
|
360
|
+
handleClear,
|
|
444
361
|
};
|
|
445
362
|
};
|
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
// import type { PropType, ExtractPropTypes } from 'vue';
|
|
2
|
-
// import type { MenuListType } from '../list/list';
|
|
3
|
-
|
|
4
|
-
// export const definePropType = <T>(val: unknown): PropType<T> => val as PropType<T>;
|
|
5
|
-
|
|
6
|
-
// const GROUPED_ITEMS_BY_TYPES = ['A-Z', 'Z-A'] as const;
|
|
7
|
-
|
|
8
|
-
// const PLACEMENTS_TYPES = [
|
|
9
|
-
// 'auto',
|
|
10
|
-
// 'auto-start',
|
|
11
|
-
// 'auto-end',
|
|
12
|
-
// 'top',
|
|
13
|
-
// 'top-start',
|
|
14
|
-
// 'top-end',
|
|
15
|
-
// 'right',
|
|
16
|
-
// 'right-start',
|
|
17
|
-
// 'right-end',
|
|
18
|
-
// 'bottom',
|
|
19
|
-
// 'bottom-start',
|
|
20
|
-
// 'bottom-end',
|
|
21
|
-
// 'left',
|
|
22
|
-
// 'left-start',
|
|
23
|
-
// 'left-end',
|
|
24
|
-
// ] as const;
|
|
25
|
-
|
|
26
|
-
// const POPPER_STRATEGY_TYPES = ['fixed', 'absolute'] as const;
|
|
27
|
-
|
|
28
|
-
// export const selectPropTypes = {
|
|
29
|
-
// id: {
|
|
30
|
-
// type: String,
|
|
31
|
-
// required: true,
|
|
32
|
-
// },
|
|
33
|
-
// modelValue: {
|
|
34
|
-
// type: [String, Number, Object, Array] as PropType<
|
|
35
|
-
// string | number | Record<string, unknown> | (string | number | Record<string, unknown>)[]
|
|
36
|
-
// >,
|
|
37
|
-
// default: () => [],
|
|
38
|
-
// },
|
|
39
|
-
// menuList: {
|
|
40
|
-
// type: Array as PropType<MenuListType[] | string[] | Record<string, unknown>[]>,
|
|
41
|
-
// required: true,
|
|
42
|
-
// default: [],
|
|
43
|
-
// },
|
|
44
|
-
// multiSelect: {
|
|
45
|
-
// type: Boolean,
|
|
46
|
-
// default: false,
|
|
47
|
-
// },
|
|
48
|
-
// groupItemsBy: {
|
|
49
|
-
// type: String as PropType<(typeof GROUPED_ITEMS_BY_TYPES)[number]>,
|
|
50
|
-
// validator: (value: (typeof GROUPED_ITEMS_BY_TYPES)[number] | undefined) => {
|
|
51
|
-
// return value === undefined || GROUPED_ITEMS_BY_TYPES.includes(value);
|
|
52
|
-
// },
|
|
53
|
-
// },
|
|
54
|
-
// textField: {
|
|
55
|
-
// type: String,
|
|
56
|
-
// default: 'text',
|
|
57
|
-
// description: 'Field name to use for display text when using dynamic object arrays',
|
|
58
|
-
// },
|
|
59
|
-
// valueField: {
|
|
60
|
-
// type: String,
|
|
61
|
-
// default: 'value',
|
|
62
|
-
// description: 'Field name to use for value when using dynamic object arrays',
|
|
63
|
-
// },
|
|
64
|
-
// placeholder: {
|
|
65
|
-
// type: String,
|
|
66
|
-
// },
|
|
67
|
-
// searchString: {
|
|
68
|
-
// type: String,
|
|
69
|
-
// default: '',
|
|
70
|
-
// },
|
|
71
|
-
// placement: {
|
|
72
|
-
// type: String as PropType<(typeof PLACEMENTS_TYPES)[number]>,
|
|
73
|
-
// validator: (value: (typeof PLACEMENTS_TYPES)[number]) => PLACEMENTS_TYPES.includes(value),
|
|
74
|
-
// default: 'bottom',
|
|
75
|
-
// },
|
|
76
|
-
// popperStrategy: {
|
|
77
|
-
// type: String,
|
|
78
|
-
// validator: (value: 'fixed' | 'absolute') => POPPER_STRATEGY_TYPES.includes(value),
|
|
79
|
-
// default: 'absolute',
|
|
80
|
-
// },
|
|
81
|
-
// popperWidth: {
|
|
82
|
-
// type: String,
|
|
83
|
-
// default: '100%',
|
|
84
|
-
// },
|
|
85
|
-
// width: {
|
|
86
|
-
// type: String,
|
|
87
|
-
// default: '100%',
|
|
88
|
-
// },
|
|
89
|
-
// wrapperPosition: {
|
|
90
|
-
// type: String,
|
|
91
|
-
// default: 'relative',
|
|
92
|
-
// },
|
|
93
|
-
// disabled: {
|
|
94
|
-
// type: Boolean,
|
|
95
|
-
// default: false,
|
|
96
|
-
// },
|
|
97
|
-
// readonly: {
|
|
98
|
-
// type: Boolean,
|
|
99
|
-
// default: false,
|
|
100
|
-
// },
|
|
101
|
-
// ladderized: {
|
|
102
|
-
// type: Boolean,
|
|
103
|
-
// default: false,
|
|
104
|
-
// },
|
|
105
|
-
// removeCurrentLevelInBackLabel: {
|
|
106
|
-
// type: Boolean,
|
|
107
|
-
// default: false,
|
|
108
|
-
// },
|
|
109
|
-
// disabled: {
|
|
110
|
-
// type: Boolean,
|
|
111
|
-
// default: false,
|
|
112
|
-
// },
|
|
113
|
-
// };
|
|
114
|
-
|
|
115
|
-
// export const selectEmitTypes = {
|
|
116
|
-
// 'infinite-scroll-trigger': Boolean,
|
|
117
|
-
// // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
118
|
-
// 'update:modelValue': (_value: unknown) => true, // Accept any type of value
|
|
119
|
-
// };
|
|
120
|
-
|
|
121
|
-
// export type SelectPropTypes = ExtractPropTypes<typeof selectPropTypes>;
|
|
122
|
-
// export type SelectEmitTypes = typeof selectEmitTypes;
|