design-system-next 2.7.43 → 2.7.44

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.
@@ -0,0 +1,110 @@
1
+ <template>
2
+ <div></div>
3
+ </template>
4
+ <!-- <template>
5
+ <Menu
6
+ :shown="selectPopperState"
7
+ aria-id="select-wrapper"
8
+ distance="4"
9
+ :placement="props.placement"
10
+ :triggers="[]"
11
+ :popper-hide-triggers="[]"
12
+ :auto-hide="false"
13
+ :disabled="isSelectPopperDisabled"
14
+ :container="`#${props.id}`"
15
+ :strategy="
16
+ props.popperStrategy === 'fixed' || props.popperStrategy === 'absolute' ? props.popperStrategy : 'absolute'
17
+ "
18
+ :delay="0"
19
+ :style="{
20
+ position: props.wrapperPosition,
21
+ width: props.width,
22
+ }"
23
+ >
24
+ <div @click="selectPopperState = true">
25
+ {{ selectedListItems }}
26
+
27
+ <spr-input
28
+ v-model="inputText"
29
+ label="Select Numbers"
30
+ :placeholder="props.placeholder"
31
+ :readonly="props.readonly"
32
+ :disabled="props.disabled"
33
+ autocomplete="off"
34
+ />
35
+ </div>
36
+
37
+ <div
38
+ :id="props.id"
39
+ :style="{
40
+ width: props.popperWidth,
41
+ }"
42
+ ></div>
43
+
44
+ <template #popper>
45
+ <div
46
+ ref="selectRef"
47
+ :class="[
48
+ (!props.ladderized || isLadderizedSearch) && 'spr-p-2',
49
+ 'spr-grid spr-max-h-[300px] spr-gap-0.5 spr-overflow-y-auto spr-overflow-x-hidden',
50
+ ]"
51
+ >
52
+ <template v-if="selectMenuList.length > 0">
53
+ <spr-list
54
+ v-if="!props.ladderized || isLadderizedSearch"
55
+ v-model="selectedListItems"
56
+ :menu-list="selectMenuList"
57
+ :group-items-by="props.groupItemsBy"
58
+ :multi-select="props.multiSelect"
59
+ :pre-selected-items="selectValue"
60
+ @update:model-value="handleSelectedItem"
61
+ />
62
+ <spr-ladderized-list
63
+ v-else
64
+ v-model="selectValue"
65
+ :ladderized="props.ladderized"
66
+ :menu-list="selectMenuList"
67
+ :remove-current-level-in-back-label="removeCurrentLevelInBackLabel"
68
+ @update:model-value="handleSelectedLadderizedItem"
69
+ />
70
+ </template>
71
+ <template v-else>
72
+ <div class="spr-flex spr-items-center spr-justify-center spr-p-2 spr-text-center">
73
+ <span class="spr-body-sm-regular spr-m-0">No results found</span>
74
+ </div>
75
+ </template>
76
+ </div>
77
+ </template>
78
+ </Menu>
79
+ </template>
80
+
81
+ <script lang="ts" setup>
82
+ import { Menu } from 'floating-vue';
83
+
84
+ import 'floating-vue/dist/style.css';
85
+
86
+ import SprInput from '../../input/input.vue';
87
+ import SprList from '../../list/list.vue';
88
+ import SprLadderizedList from '../../list/ladderized-list/ladderized-list.vue';
89
+
90
+ import { selectPropTypes, selectEmitTypes } from './select-multiple';
91
+
92
+ import { useSelect } from './use-select-multiple';
93
+
94
+ const props = defineProps(selectPropTypes);
95
+ const emit = defineEmits(selectEmitTypes);
96
+
97
+ const {
98
+ selectPopperState,
99
+ selectRef,
100
+ selectMenuList,
101
+ isSelectPopperDisabled,
102
+ selectedListItems,
103
+ handleSelectedItem,
104
+ handleSelectedLadderizedItem,
105
+ selectValue,
106
+ removeCurrentLevelInBackLabel,
107
+ isLadderizedSearch,
108
+ inputText,
109
+ } = useSelect(props, emit);
110
+ </script> -->
@@ -0,0 +1,499 @@
1
+ // import { ref, toRefs, computed, onMounted, watch } from 'vue';
2
+ // import { onClickOutside, useInfiniteScroll, useVModel } from '@vueuse/core';
3
+
4
+ // import type { SetupContext } from 'vue';
5
+ // import type { SelectPropTypes, SelectEmitTypes } from './select-multiple';
6
+ // import type { MenuListType } from '../list/list';
7
+
8
+ // export const useSelect = (props: SelectPropTypes, emit: SetupContext<SelectEmitTypes>['emit']) => {
9
+ // const {
10
+ // menuList,
11
+ // searchString,
12
+ // disabled,
13
+ // multiSelect,
14
+ // removeCurrentLevelInBackLabel,
15
+ // ladderized,
16
+ // textField,
17
+ // valueField,
18
+ // } = toRefs(props);
19
+
20
+ // // Select component ref variables
21
+ // const selectValue = useVModel(props, 'modelValue', emit); // v-model value of Select component
22
+ // const selectRef = ref<HTMLDivElement | null>(null);
23
+ // const inputText = ref(''); // v-model value of Input component
24
+
25
+ // // List component ref variables
26
+ // const selectedListItems = ref<MenuListType[]>([]); // v-model value of the list component
27
+ // const selectMenuList = ref<MenuListType[]>([]); // menu list for the list component
28
+
29
+ // // Normalized value for internal use - always an array
30
+ // const normalizedValue = computed(() => {
31
+ // // If already an array, use it
32
+ // if (Array.isArray(selectValue.value)) {
33
+ // return selectValue.value;
34
+ // }
35
+ // // If not an array but has a value, make it a single-item array
36
+ // if (selectValue.value !== undefined && selectValue.value !== null) {
37
+ // return [selectValue.value];
38
+ // }
39
+ // // Default empty array
40
+ // return [];
41
+ // });
42
+
43
+ // // Compatibility layer for pre-selected items (List component expects string[] format)
44
+ // const compatPreSelectedItems = computed(() => {
45
+ // // For ladderized Select with search, handle the special format
46
+ // if (props.ladderized && Array.isArray(selectValue.value) && selectValue.value.length === 2) {
47
+ // // We return only the second value from the [subvalue, value] format which is the actual selected value
48
+ // return [selectValue.value[1]?.toString() || ''];
49
+ // }
50
+
51
+ // // For regular arrays (multi-select)
52
+ // if (Array.isArray(selectValue.value)) {
53
+ // return selectValue.value.map((item) => {
54
+ // if (item === undefined || item === null) return '';
55
+
56
+ // // For numbers, preserve the original number value instead of converting to string
57
+ // if (typeof item === 'number') return item;
58
+
59
+ // // For objects, pass the original object reference
60
+ // // This is key for proper multi-select of objects
61
+ // if (typeof item === 'object') return item;
62
+
63
+ // // For strings, pass as is
64
+ // return item.toString();
65
+ // });
66
+ // }
67
+
68
+ // // For single values (single-select)
69
+ // return selectValue.value !== undefined && selectValue.value !== null
70
+ // ? [
71
+ // typeof selectValue.value === 'object'
72
+ // ? // Pass object reference directly instead of stringifying
73
+ // selectValue.value
74
+ // : // For numbers, preserve the original number value
75
+ // typeof selectValue.value === 'number'
76
+ // ? selectValue.value
77
+ // : selectValue.value.toString(),
78
+ // ]
79
+ // : [];
80
+ // });
81
+
82
+ // // Popper state
83
+ // const selectPopperState = ref<boolean>(false);
84
+ // const isSelectPopperDisabled = computed(() => disabled.value);
85
+
86
+ // const isLadderizedSearch = computed(
87
+ // () => ladderized.value && searchString.value !== '' && normalizedValue.value.length === 0,
88
+ // );
89
+
90
+ // const processMenuList = () => {
91
+ // // Handle empty array or non-array values
92
+ // if (!menuList.value || !Array.isArray(menuList.value) || menuList.value.length === 0) {
93
+ // selectMenuList.value = [];
94
+ // return;
95
+ // }
96
+
97
+ // // If ladderized is true and menu list items already conform to MenuListType, don't transform
98
+ // if (ladderized.value) {
99
+ // // Verify the items have the required structure for ladderized lists
100
+ // const allValid = menuList.value.every(
101
+ // (item) => typeof item === 'object' && item !== null && 'text' in item && 'value' in item,
102
+ // );
103
+
104
+ // if (allValid) {
105
+ // selectMenuList.value = menuList.value as MenuListType[];
106
+ // } else {
107
+ // console.warn('Ladderized Select requires menu items in {text, value} format');
108
+ // selectMenuList.value = [];
109
+ // }
110
+ // return;
111
+ // }
112
+
113
+ // const firstItem = menuList.value[0];
114
+
115
+ // // Handle array of strings
116
+ // if (typeof firstItem === 'string') {
117
+ // selectMenuList.value = (menuList.value as string[]).map((item) => ({
118
+ // text: item,
119
+ // value: item,
120
+ // }));
121
+ // return;
122
+ // }
123
+
124
+ // // Handle array of numbers
125
+ // if (typeof firstItem === 'number') {
126
+ // selectMenuList.value = (menuList.value as number[]).map((item) => ({
127
+ // text: item.toString(),
128
+ // value: item, // Keep the value as a number instead of converting to string
129
+ // }));
130
+ // return;
131
+ // }
132
+
133
+ // // Handle array of objects with dynamic attributes
134
+ // if (typeof firstItem === 'object' && firstItem !== null) {
135
+ // // Check if it's already in MenuListType format
136
+ // if ('text' in firstItem && 'value' in firstItem) {
137
+ // selectMenuList.value = menuList.value as MenuListType[];
138
+ // return;
139
+ // }
140
+
141
+ // // Transform to MenuListType format using textField and valueField
142
+ // selectMenuList.value = (menuList.value as Record<string, unknown>[]).map((item) => {
143
+ // const displayText = item[textField.value] || 'Unnamed';
144
+ // // Use the specified value field if available, otherwise use the entire object
145
+ // const itemValue = valueField.value && item[valueField.value] !== undefined ? item[valueField.value] : item;
146
+
147
+ // return {
148
+ // text: displayText,
149
+ // value: typeof itemValue === 'object' ? JSON.stringify(itemValue) : itemValue.toString(),
150
+ // _originalObject: item, // Store the original object for reference
151
+ // };
152
+ // });
153
+ // return;
154
+ // }
155
+
156
+ // // Default fallback
157
+ // selectMenuList.value = menuList.value as MenuListType[];
158
+ // };
159
+
160
+ // watch(menuList, () => {
161
+ // processMenuList();
162
+ // });
163
+
164
+ // const handleSearch = () => {
165
+ // if (menuList.value && menuList.value.length === 0) {
166
+ // return;
167
+ // }
168
+
169
+ // if (!multiSelect.value) {
170
+ // singleSelectSearch();
171
+ // } else {
172
+ // // Process menu list for searching
173
+ // processMenuList();
174
+
175
+ // // Handle multi-select search - filter based on search string
176
+ // if (searchString.value.trim() !== '') {
177
+ // selectMenuList.value = getFilteredMenuList(selectMenuList.value);
178
+ // }
179
+ // }
180
+ // };
181
+
182
+ // const singleSelectSearch = () => {
183
+ // if (props.ladderized) {
184
+ // ladderizedSearch();
185
+ // } else {
186
+ // basicSearch();
187
+ // }
188
+ // };
189
+
190
+ // const basicSearch = () => {
191
+ // // Process menu list first
192
+ // processMenuList();
193
+
194
+ // // Then filter based on search string
195
+ // if (searchString.value.trim() !== '') {
196
+ // selectMenuList.value = getFilteredMenuList(selectMenuList.value);
197
+ // }
198
+ // };
199
+
200
+ // const ladderizedSearch = () => {
201
+ // //revert to initial list if search string is empty or selectValue is not empty
202
+ // if (searchString.value === '' || normalizedValue.value.length > 0) {
203
+ // selectMenuList.value = [...menuList.value] as MenuListType[];
204
+ // return;
205
+ // }
206
+
207
+ // const menuListSubLevels = getAllSublevelItems(menuList.value as MenuListType[]);
208
+
209
+ // const filteredMenuList = getFilteredMenuList(menuList.value as MenuListType[]);
210
+ // const filteredMenuListSubLevels = getFilteredMenuList(menuListSubLevels);
211
+
212
+ // if (filteredMenuList.length > 0) {
213
+ // //if there is a match at the top level of the menuList
214
+ // selectMenuList.value = getAllSublevelItems(filteredMenuList);
215
+ // } else if (filteredMenuListSubLevels.length > 0) {
216
+ // //if there is a match at the 2nd level (sublevel of a menuList item) of the menuList
217
+ // selectMenuList.value = filteredMenuListSubLevels;
218
+ // } else {
219
+ // selectMenuList.value = [];
220
+ // }
221
+ // };
222
+
223
+ // // compile sublevel items from menuList to a single array
224
+ // // and add text and value of the parent item to all sublevel items as subtext and subvalue
225
+ // const getAllSublevelItems = (menuList: MenuListType[]) => {
226
+ // return menuList.reduce<MenuListType[]>((currentValue, currentItem) => {
227
+ // if (currentItem.sublevel) {
228
+ // const mappedSublevel = currentItem.sublevel.map((sublevelItem: MenuListType) => ({
229
+ // ...sublevelItem, //text and value of a sublevel item
230
+ // subtext: currentItem.text, // text of parent of a sublevel item
231
+ // subvalue: typeof currentItem.value === 'string' ? currentItem.value : String(currentItem.value), // value of parent of a sublevel item as string
232
+ // }));
233
+
234
+ // return [...currentValue, ...mappedSublevel];
235
+ // }
236
+
237
+ // return currentValue;
238
+ // }, [] as MenuListType[]);
239
+ // };
240
+
241
+ // // filter list based on search string and menuList/sublevel texts
242
+ // const getFilteredMenuList = (list: MenuListType[]) => {
243
+ // return list.filter((item: MenuListType) => {
244
+ // const searchTerm = searchString.value.toLowerCase().trim();
245
+ // return item.text.toLowerCase().includes(searchTerm);
246
+ // });
247
+ // };
248
+
249
+ // watch(searchString, () => {
250
+ // handleSearch();
251
+ // });
252
+
253
+ // onClickOutside(selectRef, () => {
254
+ // selectPopperState.value = false;
255
+ // });
256
+
257
+ // useInfiniteScroll(
258
+ // selectRef,
259
+ // () => {
260
+ // emit('infinite-scroll-trigger', true);
261
+ // },
262
+ // { distance: 10 },
263
+ // );
264
+
265
+ // // Handle selected item for simple list component
266
+ // const handleSelectedItem = (selectedItems: MenuListType[]) => {
267
+ // if (!props.ladderized) {
268
+ // // Determine the type of value to emit based on the original data type and multiSelect
269
+ // if (multiSelect.value) {
270
+ // // For multi-select, always return an array
271
+ // const values = selectedItems.map((item) => {
272
+ // // If we stored the original object, use it
273
+ // if ('_originalObject' in item) {
274
+ // return item._originalObject;
275
+ // }
276
+
277
+ // // For simple types, handle value type conversion properly
278
+ // const val = item.value;
279
+
280
+ // // If it's already a number, keep it as a number
281
+ // if (typeof val === 'number') {
282
+ // return val;
283
+ // }
284
+
285
+ // // For strings that look like numbers, convert them
286
+ // if (typeof val === 'string' && !isNaN(Number(val)) && val.trim() !== '') {
287
+ // // Only convert if it looks like a proper number format
288
+ // if (/^-?\d+(\.\d+)?$/.test(val)) {
289
+ // return Number(val);
290
+ // }
291
+ // }
292
+
293
+ // // Return the original value for all other cases
294
+ // return val;
295
+ // });
296
+
297
+ // selectValue.value = values as (string | number | Record<string, unknown>)[];
298
+ // } else {
299
+ // // For single-select
300
+ // if (selectedItems.length === 0) {
301
+ // selectValue.value = props.multiSelect ? [] : '';
302
+ // return;
303
+ // }
304
+
305
+ // const item = selectedItems[0];
306
+
307
+ // // If we stored the original object, use it
308
+ // if ('_originalObject' in item) {
309
+ // selectValue.value = item._originalObject as Record<string, unknown>;
310
+ // } else {
311
+ // // For simple types, return the value (try to convert number strings to numbers)
312
+ // const val = item.value;
313
+ // if (typeof val === 'string' && !isNaN(Number(val)) && val.trim() !== '') {
314
+ // selectValue.value = Number(val);
315
+ // } else {
316
+ // selectValue.value = val;
317
+ // }
318
+ // }
319
+ // }
320
+ // } else if (props.ladderized) {
321
+ // if (props.searchString !== '') {
322
+ // // generate select value if ladderized with search string
323
+ // const subvalue = selectedItems[0]?.subvalue;
324
+ // const value = selectedItems[0]?.value;
325
+ // if (subvalue !== undefined && value !== undefined) {
326
+ // selectValue.value = [subvalue, value] as [string, string | number];
327
+ // }
328
+ // } else {
329
+ // // For regular ladderized select selection without search
330
+ // if (selectedItems.length > 0) {
331
+ // const item = selectedItems[0];
332
+ // // Use the value directly for ladderized items
333
+ // if (item && item.value) {
334
+ // selectValue.value = item.value;
335
+ // }
336
+ // }
337
+ // }
338
+ // }
339
+
340
+ // // Always close select for single selection, regardless of value type
341
+ // if (!multiSelect.value) {
342
+ // setTimeout(() => {
343
+ // selectPopperState.value = false;
344
+ // }, 10);
345
+ // }
346
+ // };
347
+
348
+ // // Handle selected item for ladderized list component
349
+ // const handleSelectedLadderizedItem = (selectedItems: string[]) => {
350
+ // // Update the model value with the selected ladderized items
351
+ // if (selectedItems.length > 0) {
352
+ // selectValue.value = selectedItems;
353
+ // }
354
+
355
+ // // If item is from last sublevel, close the select
356
+ // if (checkIfItemFromLastSublevel(selectedItems)) {
357
+ // selectPopperState.value = false;
358
+ // }
359
+ // };
360
+
361
+ // const checkIfItemFromLastSublevel = (selectedItems: string[]) => {
362
+ // let selectedItemsObject = selectMenuList.value;
363
+
364
+ // // Traverse to the last item in the selectedItems array
365
+ // selectedItems.forEach((selectedItem) => {
366
+ // selectedItemsObject = selectedItemsObject.find((item) => selectedItem === item.value)?.sublevel ?? [];
367
+ // });
368
+
369
+ // // If there is a sublevel, return false
370
+ // if (selectedItemsObject.length > 0) {
371
+ // return false;
372
+ // }
373
+
374
+ // return true;
375
+ // };
376
+
377
+ // // Update selected items when model value changes externally
378
+ // const updateSelectedItemsFromValue = () => {
379
+ // if (!selectMenuList.value.length) return;
380
+
381
+ // const values = normalizedValue.value;
382
+ // if (!values || !values.length) {
383
+ // selectedListItems.value = [];
384
+ // return;
385
+ // }
386
+
387
+ // // Store both original values and string versions for flexible matching
388
+ // const valueData = values.map((val) => {
389
+ // if (val === undefined || val === null) return { original: '', string: '' };
390
+
391
+ // // For objects, use JSON string representation
392
+ // if (typeof val === 'object') {
393
+ // return {
394
+ // original: val,
395
+ // string: JSON.stringify(val),
396
+ // isObject: true,
397
+ // id: 'id' in val ? val.id : undefined,
398
+ // };
399
+ // }
400
+
401
+ // // For numbers and strings, keep original and string versions
402
+ // return {
403
+ // original: val,
404
+ // string: val.toString(),
405
+ // isObject: false,
406
+ // };
407
+ // });
408
+
409
+ // // Extract just string values for comparison
410
+ // const valueStrings = valueData.map((v) => v.string);
411
+
412
+ // if (props.ladderized) {
413
+ // // Special handling for ladderized selects
414
+ // if (Array.isArray(selectValue.value) && selectValue.value.length === 2) {
415
+ // // Handle [subvalue, value] format used in ladderized selects with search
416
+ // const subvalue = selectValue.value[0]?.toString() || '';
417
+ // const value = selectValue.value[1]?.toString() || '';
418
+
419
+ // selectedListItems.value = selectMenuList.value.filter((item) => {
420
+ // return item.value === value && (!item.subvalue || item.subvalue === subvalue);
421
+ // });
422
+ // } else {
423
+ // // Regular ladderized select value
424
+ // selectedListItems.value = selectMenuList.value.filter((item) => {
425
+ // // Convert both to strings for comparison or check direct equality for numbers
426
+ // if (typeof item.value === 'number') {
427
+ // return valueData.some((v) => v.original === item.value || v.string === String(item.value));
428
+ // } else {
429
+ // return valueStrings.includes(String(item.value));
430
+ // }
431
+ // });
432
+ // }
433
+ // } else {
434
+ // // Regular Select value
435
+ // selectedListItems.value = selectMenuList.value.filter((item) => {
436
+ // // Handle objects with _originalObject property
437
+ // if ('_originalObject' in item && item._originalObject) {
438
+ // return valueData.some((v) => {
439
+ // // If both are objects, compare by JSON string or by ID
440
+ // if (v.isObject && typeof v.original === 'object') {
441
+ // const originalObj = item._originalObject as Record<string, unknown>;
442
+
443
+ // // First try direct equality comparison
444
+ // if (v.original === originalObj) return true;
445
+
446
+ // // Try JSON string comparison
447
+ // const itemJson = JSON.stringify(originalObj);
448
+ // if (v.string === itemJson) return true;
449
+
450
+ // // Try ID-based comparison if both have ID fields
451
+ // if (v.id !== undefined && 'id' in originalObj) {
452
+ // return v.id === originalObj.id;
453
+ // }
454
+ // }
455
+ // return false;
456
+ // });
457
+ // }
458
+
459
+ // // Handle both numeric and string values correctly
460
+ // if (typeof item.value === 'number') {
461
+ // return valueData.some((v) => v.original === item.value || v.string === String(item.value));
462
+ // } else {
463
+ // return valueStrings.includes(String(item.value));
464
+ // }
465
+ // });
466
+ // }
467
+ // };
468
+
469
+ // watch(selectValue, () => {
470
+ // updateSelectedItemsFromValue();
471
+ // });
472
+
473
+ // watch(selectMenuList, () => {
474
+ // updateSelectedItemsFromValue();
475
+ // });
476
+
477
+ // onMounted(() => {
478
+ // processMenuList();
479
+
480
+ // // Set initial selected items based on model value
481
+ // if (normalizedValue.value.length > 0) {
482
+ // updateSelectedItemsFromValue();
483
+ // }
484
+ // });
485
+
486
+ // return {
487
+ // selectPopperState,
488
+ // selectRef,
489
+ // inputText,
490
+ // selectMenuList,
491
+ // isSelectPopperDisabled,
492
+ // selectedListItems,
493
+ // handleSelectedItem,
494
+ // handleSelectedLadderizedItem,
495
+ // selectValue: compatPreSelectedItems, // Use compatible format for lists
496
+ // removeCurrentLevelInBackLabel,
497
+ // isLadderizedSearch,
498
+ // };
499
+ // };