design-system-next 2.26.9 → 2.26.12
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 +5727 -5681
- package/dist/design-system-next.es.js.gz +0 -0
- package/dist/design-system-next.umd.js +12 -12
- package/dist/design-system-next.umd.js.gz +0 -0
- package/package.json +1 -1
- package/src/components/input/input-contact-number/use-input-contact-number.ts +55 -0
- package/src/components/input/input-currency/input-currency.ts +1 -1
- package/src/components/input/input-currency/use-input-currency.ts +12 -14
- package/src/components/list/use-list.ts +7 -0
- package/src/components/select/select-ladderized/use-select-ladderized.ts +38 -23
- package/src/components/select/select-multiple/use-select-multiple.ts +28 -4
|
Binary file
|
package/package.json
CHANGED
|
@@ -91,7 +91,37 @@ export const useInputContactNumber = (
|
|
|
91
91
|
emit('getContactNumberErrors', []);
|
|
92
92
|
|
|
93
93
|
if (value.length > 0) {
|
|
94
|
+
// Try to parse the number as user types
|
|
95
|
+
const original = value.trim();
|
|
96
|
+
const hasPlus = original.startsWith('+');
|
|
97
|
+
const normalizedNumber = hasPlus ? `+${original.replace(/[^0-9]/g, '')}` : original.replace(/\D/g, '');
|
|
98
|
+
|
|
99
|
+
let phoneNumber;
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
phoneNumber = hasPlus
|
|
103
|
+
? parsePhoneNumber(normalizedNumber)
|
|
104
|
+
: parsePhoneNumber(normalizedNumber, {
|
|
105
|
+
defaultCountry: selectedCountry.value.countryCode as CountryCode,
|
|
106
|
+
extract: false,
|
|
107
|
+
});
|
|
108
|
+
} catch {
|
|
109
|
+
phoneNumber = undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Emit parsed number even if incomplete, if it was parsed
|
|
113
|
+
if (phoneNumber && phoneNumber.isValid()) {
|
|
114
|
+
emit('getParsedInternationalNumber', phoneNumber.formatInternational());
|
|
115
|
+
} else if (normalizedNumber) {
|
|
116
|
+
// For incomplete numbers, emit what we have in international format prefix
|
|
117
|
+
const prefix = `+${selectedCountry.value.countryCallingCode}`;
|
|
118
|
+
emit('getParsedInternationalNumber', `${prefix} ${value.replace(/[^0-9]/g, '')}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
94
121
|
formatContactNumber();
|
|
122
|
+
} else {
|
|
123
|
+
// Emit empty string when input is cleared
|
|
124
|
+
emit('getParsedInternationalNumber', '');
|
|
95
125
|
}
|
|
96
126
|
};
|
|
97
127
|
|
|
@@ -109,6 +139,30 @@ export const useInputContactNumber = (
|
|
|
109
139
|
countryCode: selectedCountry.value.countryCode,
|
|
110
140
|
countryCallingCode: selectedCountry.value.countryCallingCode,
|
|
111
141
|
});
|
|
142
|
+
|
|
143
|
+
// Emit parsed number with new country code if there's a value
|
|
144
|
+
if (formattedValue.value) {
|
|
145
|
+
const original = formattedValue.value.trim();
|
|
146
|
+
const normalizedNumber = original.replace(/\D/g, '');
|
|
147
|
+
|
|
148
|
+
let phoneNumber;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
phoneNumber = parsePhoneNumber(normalizedNumber, {
|
|
152
|
+
defaultCountry: selectedCountry.value.countryCode as CountryCode,
|
|
153
|
+
extract: false,
|
|
154
|
+
});
|
|
155
|
+
} catch {
|
|
156
|
+
phoneNumber = undefined;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
if (phoneNumber && phoneNumber.isValid()) {
|
|
160
|
+
emit('getParsedInternationalNumber', phoneNumber.formatInternational().replace(/\s/g, ''));
|
|
161
|
+
} else if (normalizedNumber) {
|
|
162
|
+
const prefix = `+${selectedCountry.value.countryCallingCode}`;
|
|
163
|
+
emit('getParsedInternationalNumber', `${prefix}${normalizedNumber}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
112
166
|
};
|
|
113
167
|
|
|
114
168
|
const formatContactNumber = () => {
|
|
@@ -146,6 +200,7 @@ export const useInputContactNumber = (
|
|
|
146
200
|
formattedValue.value = formattedNumber;
|
|
147
201
|
|
|
148
202
|
emit('getContactNumberErrors', []);
|
|
203
|
+
emit('getParsedInternationalNumber', phoneNumber.formatInternational());
|
|
149
204
|
} else {
|
|
150
205
|
emit('getContactNumberErrors', [
|
|
151
206
|
{
|
|
@@ -17,16 +17,11 @@ interface InputCurrencyClasses {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const useInputCurrency = (props: InputCurrencyPropTypes, emit: SetupContext<InputCurrencyEmitTypes>['emit']) => {
|
|
20
|
-
const {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
maxDecimals,
|
|
26
|
-
minDecimals,
|
|
27
|
-
disableRounding,
|
|
28
|
-
baseValue,
|
|
29
|
-
} = toRefs(props);
|
|
20
|
+
const { id, currency, disabledCountryCurrency, disabled, maxDecimals, minDecimals, disableRounding, baseValue } =
|
|
21
|
+
toRefs(props);
|
|
22
|
+
|
|
23
|
+
// Normalize currency to uppercase for case-insensitive support
|
|
24
|
+
const normalizedCurrency = computed(() => currency.value.toUpperCase());
|
|
30
25
|
|
|
31
26
|
const inputCurrencyClasses: ComputedRef<InputCurrencyClasses> = computed(() => {
|
|
32
27
|
const dropdownBaseClasses = classNames(
|
|
@@ -442,20 +437,23 @@ export const useInputCurrency = (props: InputCurrencyPropTypes, emit: SetupConte
|
|
|
442
437
|
|
|
443
438
|
// Initialization of selected currency must occur AFTER currencyOptions is defined.
|
|
444
439
|
const initCurrency = () => {
|
|
445
|
-
const fallback =
|
|
440
|
+
const fallback =
|
|
441
|
+
currencyOptions.find((c) => c.value === 'PHP') ||
|
|
442
|
+
currencyOptions.find((c) => c.value === 'USD') ||
|
|
443
|
+
currencyOptions[0];
|
|
446
444
|
|
|
447
|
-
return currencyOptions.find((c) => c.value ===
|
|
445
|
+
return currencyOptions.find((c) => c.value === normalizedCurrency.value) || fallback;
|
|
448
446
|
};
|
|
449
447
|
|
|
450
448
|
const selected = ref(initCurrency());
|
|
451
449
|
// #endregion - Set Currency Options
|
|
452
450
|
|
|
453
|
-
watch(
|
|
451
|
+
watch(normalizedCurrency, (code) => {
|
|
454
452
|
if (code) handleSelectedCurrency(code);
|
|
455
453
|
});
|
|
456
454
|
|
|
457
455
|
onMounted(() => {
|
|
458
|
-
handleSelectedCurrency(
|
|
456
|
+
handleSelectedCurrency(normalizedCurrency.value);
|
|
459
457
|
if (modelValue.value && numericValue.value !== null) {
|
|
460
458
|
modelValue.value = formatNumberForBlur(numericValue.value);
|
|
461
459
|
emit('getCurrencyValue', numericValue.value);
|
|
@@ -613,6 +613,13 @@ export const useList = (props: ListPropTypes, emit: SetupContext<ListEmitTypes>[
|
|
|
613
613
|
handleSearch();
|
|
614
614
|
});
|
|
615
615
|
|
|
616
|
+
// Sync searchString (from prop) back to searchText (local state)
|
|
617
|
+
watch(searchString, (newVal) => {
|
|
618
|
+
if (searchText.value !== newVal) {
|
|
619
|
+
searchText.value = newVal;
|
|
620
|
+
}
|
|
621
|
+
});
|
|
622
|
+
|
|
616
623
|
watch(
|
|
617
624
|
apiSelectedList,
|
|
618
625
|
() => {
|
|
@@ -153,48 +153,63 @@ export const useSelectLadderized = (
|
|
|
153
153
|
if (disabled.value) return;
|
|
154
154
|
|
|
155
155
|
wasCleared.value = true;
|
|
156
|
-
|
|
157
156
|
inputText.value = '';
|
|
157
|
+
ladderizedSelectModel.value = [];
|
|
158
158
|
|
|
159
159
|
emit('update:modelValue', []);
|
|
160
160
|
};
|
|
161
161
|
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
(newVal) => {
|
|
166
|
-
if (isCustomInput.value) return;
|
|
167
|
-
|
|
168
|
-
if (wasCleared.value) {
|
|
169
|
-
inputText.value = '';
|
|
170
|
-
wasCleared.value = false;
|
|
162
|
+
// Helper function to update input text from model value
|
|
163
|
+
const updateInputTextFromModel = (newVal: (string | number)[]) => {
|
|
164
|
+
if (isCustomInput.value) return;
|
|
171
165
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
let currentLevel = ladderizedSelectOptions.value;
|
|
166
|
+
if (wasCleared.value) {
|
|
167
|
+
inputText.value = '';
|
|
168
|
+
wasCleared.value = false;
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
178
171
|
|
|
179
|
-
|
|
172
|
+
if (Array.isArray(newVal) && newVal.length > 0) {
|
|
173
|
+
// Treat the array as a single path for ladderized select
|
|
174
|
+
let currentLevel = ladderizedSelectOptions.value;
|
|
175
|
+
const pathTexts: string[] = [];
|
|
180
176
|
|
|
181
|
-
|
|
182
|
-
|
|
177
|
+
for (const value of newVal) {
|
|
178
|
+
const found = currentLevel.find((item) => item.value === value);
|
|
183
179
|
|
|
184
|
-
|
|
180
|
+
if (!found) break;
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
182
|
+
pathTexts.push(found.text);
|
|
183
|
+
currentLevel = found.sublevel || [];
|
|
184
|
+
}
|
|
189
185
|
|
|
186
|
+
// Only update if we successfully resolved all values in the path
|
|
187
|
+
if (pathTexts.length === newVal.length) {
|
|
190
188
|
inputText.value = prependText.value
|
|
191
189
|
? pathTexts.slice().reverse().join(textSeperator.value)
|
|
192
190
|
: pathTexts.join(textSeperator.value);
|
|
193
191
|
}
|
|
192
|
+
} else if (Array.isArray(newVal) && newVal.length === 0) {
|
|
193
|
+
inputText.value = '';
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Watch for changes in modelValue to update inputText
|
|
198
|
+
watchDeep(
|
|
199
|
+
ladderizedSelectModel,
|
|
200
|
+
(newVal) => {
|
|
201
|
+
updateInputTextFromModel(newVal);
|
|
194
202
|
},
|
|
195
203
|
{ immediate: true },
|
|
196
204
|
);
|
|
197
205
|
|
|
206
|
+
// Watch for changes in options to re-render text when options load
|
|
207
|
+
watch(ladderizedSelectOptions, () => {
|
|
208
|
+
if (!wasCleared.value && ladderizedSelectModel.value && ladderizedSelectModel.value.length > 0) {
|
|
209
|
+
updateInputTextFromModel(ladderizedSelectModel.value);
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
|
|
198
213
|
watch(ladderizedSelectPopperState, (newState) => {
|
|
199
214
|
emit('popper-state', newState);
|
|
200
215
|
});
|
|
@@ -107,6 +107,7 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
107
107
|
|
|
108
108
|
const inputText = ref<string | number>('');
|
|
109
109
|
const chippedInputTextRef = ref(null);
|
|
110
|
+
const isSearching = ref<boolean>(false);
|
|
110
111
|
|
|
111
112
|
const search = useVModel(props, 'searchValue', emit);
|
|
112
113
|
const searchInput = ref<string>('');
|
|
@@ -204,9 +205,17 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
204
205
|
});
|
|
205
206
|
|
|
206
207
|
hasUserSelected.value = true;
|
|
208
|
+
|
|
209
|
+
// Temporarily disable searching flag to allow inputText update
|
|
210
|
+
const wasSearching = isSearching.value;
|
|
211
|
+
isSearching.value = false;
|
|
212
|
+
|
|
207
213
|
multiSelectModel.value = selectedValues;
|
|
208
214
|
|
|
209
|
-
|
|
215
|
+
// Restore searching state if user was searching
|
|
216
|
+
if (wasSearching) {
|
|
217
|
+
isSearching.value = true;
|
|
218
|
+
}
|
|
210
219
|
|
|
211
220
|
emit('get-selected-options', multiSelectedItems);
|
|
212
221
|
};
|
|
@@ -252,6 +261,11 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
252
261
|
* Handles stringified objects and updates the input text accordingly.
|
|
253
262
|
*/
|
|
254
263
|
const updateMultiSelectedItemsFromValue = () => {
|
|
264
|
+
// Don't update inputText if user is actively searching
|
|
265
|
+
if (isSearching.value) {
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
255
269
|
const values = normalizedValue.value;
|
|
256
270
|
|
|
257
271
|
if (!values || !values.length) {
|
|
@@ -313,7 +327,7 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
313
327
|
inputText.value = `${values.length} items selected`;
|
|
314
328
|
} else {
|
|
315
329
|
// Safely get display text for each value
|
|
316
|
-
|
|
330
|
+
const displayTexts = values
|
|
317
331
|
.map((val) => {
|
|
318
332
|
// Try to find the corresponding option for display text
|
|
319
333
|
const found = multiSelectOptions.value.find((opt) => {
|
|
@@ -338,9 +352,12 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
338
352
|
}
|
|
339
353
|
return optVal == v;
|
|
340
354
|
});
|
|
341
|
-
return
|
|
355
|
+
// Only return text if found, otherwise return empty string
|
|
356
|
+
return found ? found.text : '';
|
|
342
357
|
})
|
|
343
|
-
.
|
|
358
|
+
.filter((text) => text !== ''); // Filter out empty strings from not found items
|
|
359
|
+
|
|
360
|
+
inputText.value = displayTexts.length > 0 ? displayTexts.join(', ') : '';
|
|
344
361
|
}
|
|
345
362
|
}
|
|
346
363
|
|
|
@@ -390,6 +407,9 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
390
407
|
watch(searchInput, (newVal, oldVal) => {
|
|
391
408
|
search.value = newVal;
|
|
392
409
|
|
|
410
|
+
// Track if user is actively searching
|
|
411
|
+
isSearching.value = newVal.length > 0;
|
|
412
|
+
|
|
393
413
|
// Only emit search-string if value actually changed (not just modifier keys)
|
|
394
414
|
// Modifier key presses alone won't change the input value
|
|
395
415
|
if (newVal !== oldVal) {
|
|
@@ -422,6 +442,10 @@ export const useMultiSelect = (props: MultiSelectPropTypes, emit: SetupContext<M
|
|
|
422
442
|
|
|
423
443
|
multiSelectPopperState.value = false;
|
|
424
444
|
|
|
445
|
+
// Clear search state when closing
|
|
446
|
+
isSearching.value = false;
|
|
447
|
+
searchInput.value = '';
|
|
448
|
+
|
|
425
449
|
updateMultiSelectedItemsFromValue();
|
|
426
450
|
},
|
|
427
451
|
{
|