design-system-next 2.7.36 → 2.7.40
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 +5506 -5394
- package/dist/design-system-next.js.gz +0 -0
- package/dist/main.css +1 -1
- package/dist/main.css.gz +0 -0
- package/package.json +1 -1
- package/src/components/date-picker/__tests__/date-picker.test.ts +112 -0
- package/src/components/date-picker/date-picker.ts +5 -0
- package/src/components/date-picker/date-picker.vue +5 -5
- package/src/components/date-picker/use-date-picker.ts +55 -19
- 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/dropdown/dropdown.ts +14 -3
- package/src/components/dropdown/fix-multi-number.ts +92 -0
- package/src/components/dropdown/use-dropdown.ts +328 -23
- package/src/components/filter/filter.vue +3 -3
- package/src/components/filter/use-filter.ts +3 -6
- package/src/components/list/list.ts +4 -3
- package/src/components/list/use-list.ts +158 -7
- package/src/components/table/table-pagination/use-table-pagination.ts +1 -1
- package/src/examples/dropdown-number-multi-select.vue +76 -0
|
@@ -36,7 +36,66 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
36
36
|
|
|
37
37
|
// #region - Helper Methods
|
|
38
38
|
const isItemSelected = (item: MenuListType) => {
|
|
39
|
-
|
|
39
|
+
// First check standard selection via the selectedItems array
|
|
40
|
+
const directSelected = selectedItems.value.some((selectedItem) => {
|
|
41
|
+
// Compare both text and value properties to handle different value types
|
|
42
|
+
if (selectedItem.text === item.text) return true;
|
|
43
|
+
|
|
44
|
+
// Ensure comparison works for both string and number values
|
|
45
|
+
const selectedItemValue = selectedItem.value;
|
|
46
|
+
const itemValue = item.value;
|
|
47
|
+
|
|
48
|
+
// For primitives, use string comparison to handle number-string comparison properly
|
|
49
|
+
if (typeof selectedItemValue !== 'object' && typeof itemValue !== 'object') {
|
|
50
|
+
return String(selectedItemValue) === String(itemValue);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// For objects, use JSON.stringify for comparison (will match for equality)
|
|
54
|
+
if (typeof selectedItemValue === 'object' && selectedItemValue !== null &&
|
|
55
|
+
typeof itemValue === 'object' && itemValue !== null) {
|
|
56
|
+
return JSON.stringify(selectedItemValue) === JSON.stringify(itemValue);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return false;
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (directSelected) return true;
|
|
63
|
+
|
|
64
|
+
// Additional check for objects stored in _originalObject property
|
|
65
|
+
if ('_originalObject' in item && item._originalObject && preSelectedItems.value?.length) {
|
|
66
|
+
return preSelectedItems.value.some(preSelectedValue => {
|
|
67
|
+
// Direct reference comparison (most accurate)
|
|
68
|
+
if (preSelectedValue === item._originalObject) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// If both are objects, compare their serialized forms
|
|
73
|
+
if (typeof preSelectedValue === 'object' && preSelectedValue !== null) {
|
|
74
|
+
const originalObj = item._originalObject as Record<string, unknown>;
|
|
75
|
+
|
|
76
|
+
if (typeof originalObj === 'object') {
|
|
77
|
+
// First try comparing by ID for more reliable object comparison
|
|
78
|
+
if ('id' in preSelectedValue && 'id' in originalObj) {
|
|
79
|
+
return preSelectedValue.id === originalObj.id;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Fallback to full object comparison
|
|
83
|
+
const valString = JSON.stringify(preSelectedValue);
|
|
84
|
+
const itemString = JSON.stringify(originalObj);
|
|
85
|
+
return valString === itemString;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// If object has an id field, check if it matches with the item value
|
|
89
|
+
if ('id' in preSelectedValue) {
|
|
90
|
+
return String(item.value).includes(String(preSelectedValue.id));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return false;
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return false;
|
|
40
99
|
};
|
|
41
100
|
|
|
42
101
|
const setMenuList = () => {
|
|
@@ -96,9 +155,57 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
96
155
|
if (!preSelectedItems.value?.length) return;
|
|
97
156
|
|
|
98
157
|
const selected = preSelectedItems.value
|
|
99
|
-
.map((preSelectedItem: string) =>
|
|
100
|
-
|
|
101
|
-
|
|
158
|
+
.map((preSelectedItem: string | number | Record<string, unknown>) => {
|
|
159
|
+
// For objects, check for matching _originalObject properties
|
|
160
|
+
if (typeof preSelectedItem === 'object' && preSelectedItem !== null) {
|
|
161
|
+
// Try to find an item with a matching _originalObject
|
|
162
|
+
const objectMatch = localizedMenuList.value.find(menuItem => {
|
|
163
|
+
if (!menuItem._originalObject) return false;
|
|
164
|
+
|
|
165
|
+
// Compare serialized versions for deep equality
|
|
166
|
+
return JSON.stringify(menuItem._originalObject) === JSON.stringify(preSelectedItem);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
if (objectMatch) return objectMatch;
|
|
170
|
+
|
|
171
|
+
// If no direct object match, try matching on ID if both have it
|
|
172
|
+
if ('id' in preSelectedItem) {
|
|
173
|
+
const idMatch = localizedMenuList.value.find(menuItem => {
|
|
174
|
+
if (menuItem._originalObject && 'id' in menuItem._originalObject) {
|
|
175
|
+
return menuItem._originalObject.id === preSelectedItem.id;
|
|
176
|
+
}
|
|
177
|
+
// Also check if the value field contains a stringified version that includes the id
|
|
178
|
+
return String(menuItem.value).includes(String(preSelectedItem.id));
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
if (idMatch) return idMatch;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// First try direct value comparison (for exact matches)
|
|
186
|
+
const directMatch = localizedMenuList.value.find(
|
|
187
|
+
(menuItem) => menuItem.value === preSelectedItem
|
|
188
|
+
);
|
|
189
|
+
if (directMatch) return directMatch;
|
|
190
|
+
|
|
191
|
+
// Special handling for number values in the preSelectedItems array
|
|
192
|
+
if (typeof preSelectedItem === 'number') {
|
|
193
|
+
// Find items that match the number value either directly or as a string
|
|
194
|
+
const numericMatch = localizedMenuList.value.find(
|
|
195
|
+
(menuItem) =>
|
|
196
|
+
// Match if menuItem.value is the same number
|
|
197
|
+
(typeof menuItem.value === 'number' && menuItem.value === preSelectedItem) ||
|
|
198
|
+
// Match if menuItem.value is a string representation of the number
|
|
199
|
+
(typeof menuItem.value === 'string' && menuItem.value === String(preSelectedItem))
|
|
200
|
+
);
|
|
201
|
+
if (numericMatch) return numericMatch;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Then try string comparison for cases where types differ (string vs number)
|
|
205
|
+
return localizedMenuList.value.find(
|
|
206
|
+
(menuItem) => String(menuItem.value) === String(preSelectedItem)
|
|
207
|
+
);
|
|
208
|
+
})
|
|
102
209
|
.filter(Boolean) as MenuListType[];
|
|
103
210
|
|
|
104
211
|
if (multiSelect.value) {
|
|
@@ -108,7 +215,11 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
108
215
|
|
|
109
216
|
if (
|
|
110
217
|
firstItem &&
|
|
111
|
-
!selectedItems.value.some((selectedItem) =>
|
|
218
|
+
!selectedItems.value.some((selectedItem) => {
|
|
219
|
+
// Use the same comparison logic as in isItemSelected
|
|
220
|
+
if (selectedItem.text === firstItem.text) return true;
|
|
221
|
+
return String(selectedItem.value) === String(firstItem.value);
|
|
222
|
+
})
|
|
112
223
|
) {
|
|
113
224
|
selectedItems.value = [firstItem];
|
|
114
225
|
}
|
|
@@ -125,17 +236,57 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
125
236
|
if(item.disabled) return;
|
|
126
237
|
|
|
127
238
|
if (multiSelect.value) {
|
|
128
|
-
|
|
239
|
+
// For multi-select, check if item is already selected
|
|
240
|
+
const index = selectedItems.value.findIndex((selectedItem: MenuListType) => {
|
|
241
|
+
// Compare text values first for simple match
|
|
242
|
+
if (selectedItem.text === item.text) return true;
|
|
243
|
+
|
|
244
|
+
// Compare primitive values with string conversion for compatibility
|
|
245
|
+
if (typeof selectedItem.value !== 'object' && typeof item.value !== 'object') {
|
|
246
|
+
return String(selectedItem.value) === String(item.value);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// For objects, compare their JSON string representations
|
|
250
|
+
if (typeof selectedItem.value === 'object' && selectedItem.value !== null &&
|
|
251
|
+
typeof item.value === 'object' && item.value !== null) {
|
|
252
|
+
return JSON.stringify(selectedItem.value) === JSON.stringify(item.value);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Compare _originalObject if available (most reliable for complex objects)
|
|
256
|
+
if ('_originalObject' in selectedItem && selectedItem._originalObject &&
|
|
257
|
+
'_originalObject' in item && item._originalObject) {
|
|
258
|
+
|
|
259
|
+
// Direct reference equality check (fastest)
|
|
260
|
+
if (selectedItem._originalObject === item._originalObject) {
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// ID-based comparison (reliable for objects with IDs)
|
|
265
|
+
const selectedObj = selectedItem._originalObject as Record<string, unknown>;
|
|
266
|
+
const itemObj = item._originalObject as Record<string, unknown>;
|
|
267
|
+
|
|
268
|
+
if ('id' in selectedObj && 'id' in itemObj) {
|
|
269
|
+
return selectedObj.id === itemObj.id;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Full JSON comparison (most comprehensive but slower)
|
|
273
|
+
return JSON.stringify(selectedItem._originalObject) === JSON.stringify(item._originalObject);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return false;
|
|
277
|
+
});
|
|
129
278
|
|
|
130
279
|
if (index === -1) {
|
|
280
|
+
// Add item if not already selected
|
|
131
281
|
selectedItems.value = [...selectedItems.value, item];
|
|
132
282
|
} else {
|
|
283
|
+
// Remove item if already selected
|
|
133
284
|
const updatedItems = [...selectedItems.value];
|
|
134
|
-
|
|
135
285
|
updatedItems.splice(index, 1);
|
|
136
286
|
selectedItems.value = updatedItems;
|
|
137
287
|
}
|
|
138
288
|
} else {
|
|
289
|
+
// For single-select, simply replace the selection
|
|
139
290
|
selectedItems.value = [item];
|
|
140
291
|
}
|
|
141
292
|
};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// Example usage of dropdown with primitive number values in multi-select mode
|
|
2
|
+
|
|
3
|
+
<template>
|
|
4
|
+
<div>
|
|
5
|
+
<h2>Multiple Number Values Demo</h2>
|
|
6
|
+
<p>Selected values: {{ displaySelection }}</p>
|
|
7
|
+
|
|
8
|
+
<div class="dropdown-container">
|
|
9
|
+
<spr-dropdown
|
|
10
|
+
id="number-multi-dropdown"
|
|
11
|
+
v-model="selectedNumbers"
|
|
12
|
+
:menu-list="numberOptions"
|
|
13
|
+
multi-select
|
|
14
|
+
@update:model-value="handleSelectedItems"
|
|
15
|
+
>
|
|
16
|
+
<spr-input
|
|
17
|
+
v-model="displayText"
|
|
18
|
+
label="Select Numbers"
|
|
19
|
+
readonly
|
|
20
|
+
placeholder="Select numbers..."
|
|
21
|
+
/>
|
|
22
|
+
</spr-dropdown>
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<div class="mt-4">
|
|
26
|
+
<h3>Current selection type:</h3>
|
|
27
|
+
<pre>{{ typeof selectedNumbers[0] }}</pre>
|
|
28
|
+
|
|
29
|
+
<h3>Current selection:</h3>
|
|
30
|
+
{{ selectedNumbers }}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</template>
|
|
34
|
+
|
|
35
|
+
<script setup>
|
|
36
|
+
import { ref, computed } from 'vue';
|
|
37
|
+
import SprInput from "@/components/input/input.vue";
|
|
38
|
+
import SprDropdown from "@/components/dropdown/dropdown.vue";
|
|
39
|
+
|
|
40
|
+
// Define number options - raw number values
|
|
41
|
+
const numberOptions = [
|
|
42
|
+
{ text: 'One', value: 1 },
|
|
43
|
+
{ text: 'Two', value: 2 },
|
|
44
|
+
{ text: 'Three', value: 3 },
|
|
45
|
+
{ text: 'Four', value: 4 },
|
|
46
|
+
{ text: 'Five', value: 5 }
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
// Track selected numbers
|
|
50
|
+
const selectedNumbers = ref([]);
|
|
51
|
+
const displayText = ref('');
|
|
52
|
+
|
|
53
|
+
// Display the selection summary
|
|
54
|
+
const displaySelection = computed(() => {
|
|
55
|
+
if (selectedNumbers.value.length === 0) {
|
|
56
|
+
return 'None';
|
|
57
|
+
}
|
|
58
|
+
return selectedNumbers.value.join(', ');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Handle selected items and update display text
|
|
62
|
+
const handleSelectedItems = (items) => {
|
|
63
|
+
// For multi-select, update display text to show selected items
|
|
64
|
+
const selectedTexts = items.map(itemValue => {
|
|
65
|
+
// Find corresponding text for each selected value
|
|
66
|
+
const option = numberOptions.find(opt => opt.value === itemValue);
|
|
67
|
+
return option ? option.text : itemValue;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// Update the input display text
|
|
71
|
+
displayText.value = selectedTexts.join(', ');
|
|
72
|
+
|
|
73
|
+
console.log('Selected values:', items);
|
|
74
|
+
console.log('Type of first value:', typeof items[0]);
|
|
75
|
+
};
|
|
76
|
+
</script>
|