design-system-next 2.14.3 → 2.15.1
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 +7037 -6519
- package/dist/design-system-next.es.js.gz +0 -0
- package/dist/design-system-next.umd.js +13 -13
- package/dist/design-system-next.umd.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 +9 -1
- package/src/assets/styles/tailwind.css +0 -11
- package/src/components/dropdown/dropdown.ts +8 -1
- package/src/components/dropdown/dropdown.vue +3 -1
- package/src/components/input/input-contact-number/input-contact-number.ts +6 -8
- package/src/components/input/input-contact-number/input-contact-number.vue +6 -5
- package/src/components/input/input-contact-number/use-input-contact-number.ts +61 -26
- package/src/components/input/input-currency/input-currency.ts +100 -0
- package/src/components/input/input-currency/input-currency.vue +60 -0
- package/src/components/input/input-currency/use-input-currency.ts +538 -0
- package/src/components/select/select-ladderized/select-ladderized.ts +5 -1
- package/src/components/select/select-ladderized/select-ladderized.vue +1 -1
- package/src/components/select/select-multiple/select-multiple.ts +4 -0
- package/src/components/select/select-multiple/select-multiple.vue +1 -1
- package/src/components/select/select.ts +6 -2
- package/src/components/select/select.vue +51 -48
|
@@ -23,9 +23,13 @@ export const COUNTRY_OPTIONS: CountryOption[] = getCountries().map((countryCode)
|
|
|
23
23
|
});
|
|
24
24
|
|
|
25
25
|
export const inputContactNumberPropTypes = {
|
|
26
|
+
id: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: '',
|
|
29
|
+
},
|
|
26
30
|
modelValue: {
|
|
27
31
|
type: String,
|
|
28
|
-
|
|
32
|
+
default: '',
|
|
29
33
|
},
|
|
30
34
|
placeholder: {
|
|
31
35
|
type: String,
|
|
@@ -69,13 +73,7 @@ export const inputContactNumberEmitTypes = {
|
|
|
69
73
|
|
|
70
74
|
export interface InputContactNumberEmit {
|
|
71
75
|
(event: 'update:modelValue', value: string): void;
|
|
72
|
-
(
|
|
73
|
-
event: 'getSelectedCountryCallingCode',
|
|
74
|
-
value: {
|
|
75
|
-
countryCode: string[];
|
|
76
|
-
countryCallingCode: string[];
|
|
77
|
-
},
|
|
78
|
-
): void;
|
|
76
|
+
(event: 'getSelectedCountryCallingCode', value: { countryCode: string; countryCallingCode: string }): void;
|
|
79
77
|
(event: 'getContactNumberErrors', value: Array<{ title: string; message: string }>): void;
|
|
80
78
|
}
|
|
81
79
|
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
>
|
|
13
13
|
<template #prefix>
|
|
14
14
|
<spr-dropdown
|
|
15
|
-
id="
|
|
15
|
+
:id="dropdownId"
|
|
16
16
|
v-model="selectedCountry.countryCode"
|
|
17
|
-
class="
|
|
17
|
+
:class="inputContactNumberClasses.dropdownBaseClasses"
|
|
18
18
|
:menu-list="COUNTRY_OPTIONS"
|
|
19
19
|
placement="bottom-start"
|
|
20
20
|
:width="!props.disabledCountryCallingCode ? '45px' : '35px'"
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
@update:model-value="handleSelectedCountryCode"
|
|
24
24
|
@get-popper-state="handlePopperState"
|
|
25
25
|
>
|
|
26
|
-
<
|
|
27
|
-
|
|
26
|
+
<div :class="inputContactNumberClasses.dropdownWrappertClasses">
|
|
27
|
+
<span>+{{ selectedCountry.countryCallingCode }}</span>
|
|
28
28
|
<icon v-if="!props.disabledCountryCallingCode" icon="ph:caret-down" width="16px" height="16px" />
|
|
29
|
-
</
|
|
29
|
+
</div>
|
|
30
30
|
</spr-dropdown>
|
|
31
31
|
</template>
|
|
32
32
|
</spr-input>
|
|
@@ -48,6 +48,7 @@ const emit = defineEmits(inputContactNumberEmitTypes);
|
|
|
48
48
|
|
|
49
49
|
const {
|
|
50
50
|
inputContactNumberClasses,
|
|
51
|
+
dropdownId,
|
|
51
52
|
formattedValue,
|
|
52
53
|
selectedCountry,
|
|
53
54
|
popperState,
|
|
@@ -8,17 +8,26 @@ import parsePhoneNumber, { getCountries, getCountryCallingCode, CountryCode } fr
|
|
|
8
8
|
import { type InputContactNumberEmitTypes, type InputContactNumberPropTypes } from './input-contact-number';
|
|
9
9
|
|
|
10
10
|
interface InputContactNumberClasses {
|
|
11
|
-
|
|
11
|
+
dropdownBaseClasses: string;
|
|
12
|
+
dropdownWrappertClasses: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export const useInputContactNumber = (
|
|
15
16
|
props: InputContactNumberPropTypes,
|
|
16
17
|
emit: SetupContext<InputContactNumberEmitTypes>['emit'],
|
|
17
18
|
) => {
|
|
18
|
-
const { preSelectedCountryCode, disabledCountryCallingCode, disabled } = toRefs(props);
|
|
19
|
+
const { id, preSelectedCountryCode, disabledCountryCallingCode, disabled } = toRefs(props);
|
|
19
20
|
|
|
20
21
|
const inputContactNumberClasses: ComputedRef<InputContactNumberClasses> = computed(() => {
|
|
21
|
-
const
|
|
22
|
+
const dropdownBaseClasses = classNames(
|
|
23
|
+
'[&_#dropdown-wrapper]:spr-my-1',
|
|
24
|
+
'[&_#dropdown-wrapper[data-popper-placement="bottom-start"]]:spr-ml-[-10px]',
|
|
25
|
+
'[&_#dropdown-wrapper[data-popper-placement="bottom-start"]]:spr-mt-[6px]',
|
|
26
|
+
'[&_#dropdown-wrapper[data-popper-placement="top-start"]]:spr-ml-[-10px]',
|
|
27
|
+
'[&_#dropdown-wrapper[data-popper-placement="top-start"]]:spr-mt-[-6px]',
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
const dropdownWrappertClasses = classNames(
|
|
22
31
|
'spr-font-weight-regular spr-font-size-200 spr-line-height-500 spr-letter-spacing-none spr-font-main',
|
|
23
32
|
'spr-flex spr-items-center spr-gap-size-spacing-5xs',
|
|
24
33
|
{
|
|
@@ -29,20 +38,25 @@ export const useInputContactNumber = (
|
|
|
29
38
|
);
|
|
30
39
|
|
|
31
40
|
return {
|
|
32
|
-
|
|
41
|
+
dropdownBaseClasses,
|
|
42
|
+
dropdownWrappertClasses,
|
|
33
43
|
};
|
|
34
44
|
});
|
|
35
45
|
|
|
46
|
+
// fallback random id if user does not provide one (stable per component instance)
|
|
47
|
+
const fallbackId = ref(`currency-${Math.random().toString(36).slice(2, 8)}-dropdown`);
|
|
48
|
+
const dropdownId = computed(() => (id.value ? `${id.value}-dropdown` : fallbackId.value));
|
|
49
|
+
|
|
36
50
|
const formattedValue = useVModel(props, 'modelValue', emit);
|
|
37
51
|
|
|
38
52
|
const selectedCountry = ref({
|
|
39
|
-
countryCode:
|
|
40
|
-
countryCallingCode:
|
|
53
|
+
countryCode: 'PH',
|
|
54
|
+
countryCallingCode: '63',
|
|
41
55
|
});
|
|
42
56
|
|
|
43
57
|
const popperState = ref(false);
|
|
44
58
|
|
|
45
|
-
const
|
|
59
|
+
const setSelectedCountry = (selectedCountryCode: string) => {
|
|
46
60
|
const countryCallingCode = getCountryCallingCode(selectedCountryCode as CountryCode);
|
|
47
61
|
|
|
48
62
|
const countryCode = getCountries().find((country) => {
|
|
@@ -51,8 +65,8 @@ export const useInputContactNumber = (
|
|
|
51
65
|
|
|
52
66
|
if (countryCode && countryCallingCode) {
|
|
53
67
|
selectedCountry.value = {
|
|
54
|
-
countryCode:
|
|
55
|
-
countryCallingCode:
|
|
68
|
+
countryCode: countryCode,
|
|
69
|
+
countryCallingCode: countryCallingCode,
|
|
56
70
|
};
|
|
57
71
|
|
|
58
72
|
formatContactNumber();
|
|
@@ -81,10 +95,10 @@ export const useInputContactNumber = (
|
|
|
81
95
|
}
|
|
82
96
|
};
|
|
83
97
|
|
|
84
|
-
const handleSelectedCountryCode = (countryCode: string
|
|
98
|
+
const handleSelectedCountryCode = (countryCode: string) => {
|
|
85
99
|
selectedCountry.value = {
|
|
86
|
-
countryCode:
|
|
87
|
-
countryCallingCode:
|
|
100
|
+
countryCode: countryCode,
|
|
101
|
+
countryCallingCode: getCountryCallingCode(countryCode as CountryCode),
|
|
88
102
|
};
|
|
89
103
|
|
|
90
104
|
emit('getContactNumberErrors', []);
|
|
@@ -92,27 +106,46 @@ export const useInputContactNumber = (
|
|
|
92
106
|
formatContactNumber();
|
|
93
107
|
|
|
94
108
|
emit('getSelectedCountryCallingCode', {
|
|
95
|
-
countryCode: selectedCountry.value.countryCode
|
|
96
|
-
countryCallingCode: selectedCountry.value.countryCallingCode
|
|
109
|
+
countryCode: selectedCountry.value.countryCode,
|
|
110
|
+
countryCallingCode: selectedCountry.value.countryCallingCode,
|
|
97
111
|
});
|
|
98
112
|
};
|
|
99
113
|
|
|
100
114
|
const formatContactNumber = () => {
|
|
101
|
-
if (!formattedValue.value)
|
|
102
|
-
|
|
103
|
-
|
|
115
|
+
if (!formattedValue.value) {
|
|
116
|
+
emit('getContactNumberErrors', []);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
104
119
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
120
|
+
const original = formattedValue.value.trim();
|
|
121
|
+
const hasPlus = original.startsWith('+');
|
|
122
|
+
const normalizedNumber = hasPlus ? `+${original.replace(/[^0-9]/g, '')}` : original.replace(/\D/g, '');
|
|
123
|
+
|
|
124
|
+
let phoneNumber;
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
phoneNumber = hasPlus
|
|
128
|
+
? parsePhoneNumber(normalizedNumber)
|
|
129
|
+
: parsePhoneNumber(normalizedNumber, {
|
|
130
|
+
defaultCountry: selectedCountry.value.countryCode as CountryCode,
|
|
131
|
+
extract: false,
|
|
132
|
+
});
|
|
133
|
+
} catch {
|
|
134
|
+
phoneNumber = undefined;
|
|
135
|
+
}
|
|
109
136
|
|
|
110
137
|
if (phoneNumber && phoneNumber.isValid()) {
|
|
111
138
|
let formattedNumber = phoneNumber.formatInternational();
|
|
112
139
|
|
|
113
|
-
|
|
140
|
+
const prefix = `+${selectedCountry.value.countryCallingCode} `;
|
|
141
|
+
|
|
142
|
+
if (formattedNumber.startsWith(prefix)) {
|
|
143
|
+
formattedNumber = formattedNumber.slice(prefix.length);
|
|
144
|
+
}
|
|
114
145
|
|
|
115
146
|
formattedValue.value = formattedNumber;
|
|
147
|
+
|
|
148
|
+
emit('getContactNumberErrors', []);
|
|
116
149
|
} else {
|
|
117
150
|
emit('getContactNumberErrors', [
|
|
118
151
|
{
|
|
@@ -133,23 +166,24 @@ export const useInputContactNumber = (
|
|
|
133
166
|
|
|
134
167
|
watch(preSelectedCountryCode, (newValue) => {
|
|
135
168
|
if (newValue) {
|
|
136
|
-
|
|
169
|
+
setSelectedCountry(newValue);
|
|
137
170
|
}
|
|
138
171
|
});
|
|
139
172
|
|
|
140
173
|
onMounted(() => {
|
|
141
174
|
emit('getSelectedCountryCallingCode', {
|
|
142
|
-
countryCode: selectedCountry.value.countryCode
|
|
143
|
-
countryCallingCode: selectedCountry.value.countryCallingCode
|
|
175
|
+
countryCode: selectedCountry.value.countryCode,
|
|
176
|
+
countryCallingCode: selectedCountry.value.countryCallingCode,
|
|
144
177
|
});
|
|
145
178
|
|
|
146
179
|
if (preSelectedCountryCode.value) {
|
|
147
|
-
|
|
180
|
+
setSelectedCountry(preSelectedCountryCode.value);
|
|
148
181
|
}
|
|
149
182
|
});
|
|
150
183
|
|
|
151
184
|
return {
|
|
152
185
|
inputContactNumberClasses,
|
|
186
|
+
dropdownId,
|
|
153
187
|
formattedValue,
|
|
154
188
|
selectedCountry,
|
|
155
189
|
popperState,
|
|
@@ -158,5 +192,6 @@ export const useInputContactNumber = (
|
|
|
158
192
|
formatContactNumber,
|
|
159
193
|
handleUpdateModelValue,
|
|
160
194
|
handlePopperState,
|
|
195
|
+
setSelectedCountry,
|
|
161
196
|
};
|
|
162
197
|
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { ExtractPropTypes } from 'vue';
|
|
2
|
+
|
|
3
|
+
export interface CurrencyOption {
|
|
4
|
+
text: string;
|
|
5
|
+
value: string;
|
|
6
|
+
currency: string;
|
|
7
|
+
symbol: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const inputCurrencyPropTypes = {
|
|
11
|
+
id: {
|
|
12
|
+
type: String,
|
|
13
|
+
default: '',
|
|
14
|
+
},
|
|
15
|
+
modelValue: {
|
|
16
|
+
type: String,
|
|
17
|
+
default: '',
|
|
18
|
+
},
|
|
19
|
+
placeholder: {
|
|
20
|
+
type: String,
|
|
21
|
+
default: '0.00',
|
|
22
|
+
},
|
|
23
|
+
preSelectedCurrency: {
|
|
24
|
+
type: String,
|
|
25
|
+
default: 'PHP',
|
|
26
|
+
},
|
|
27
|
+
disabled: {
|
|
28
|
+
type: Boolean,
|
|
29
|
+
default: false,
|
|
30
|
+
},
|
|
31
|
+
disabledCountryCurrency: {
|
|
32
|
+
type: Boolean,
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
autoFormat: {
|
|
36
|
+
type: Boolean,
|
|
37
|
+
default: false,
|
|
38
|
+
},
|
|
39
|
+
maxDecimals: {
|
|
40
|
+
type: Number,
|
|
41
|
+
default: 2,
|
|
42
|
+
},
|
|
43
|
+
minDecimals: {
|
|
44
|
+
type: Number,
|
|
45
|
+
default: 2,
|
|
46
|
+
},
|
|
47
|
+
displayAsCode: {
|
|
48
|
+
type: Boolean,
|
|
49
|
+
default: true,
|
|
50
|
+
},
|
|
51
|
+
disableRounding: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: false,
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
export const inputCurrencyEmitTypes = {
|
|
58
|
+
'update:modelValue': (value: string): value is string => typeof value === 'string',
|
|
59
|
+
getSelectedCurrencyMeta: (value: {
|
|
60
|
+
currency: string;
|
|
61
|
+
symbol: string;
|
|
62
|
+
numericValue: number | null;
|
|
63
|
+
rawValue: string | null;
|
|
64
|
+
}): value is { currency: string; symbol: string; numericValue: number | null; rawValue: string | null } =>
|
|
65
|
+
typeof value.currency === 'string' &&
|
|
66
|
+
typeof value.symbol === 'string' &&
|
|
67
|
+
(value.numericValue === null || (typeof value.numericValue === 'number' && !isNaN(value.numericValue))) &&
|
|
68
|
+
(value.rawValue === null || typeof value.rawValue === 'string'),
|
|
69
|
+
getCurrencyErrors: (value: Array<{ title: string; message: string }>) => {
|
|
70
|
+
return (
|
|
71
|
+
Array.isArray(value) &&
|
|
72
|
+
value.every(
|
|
73
|
+
(item) =>
|
|
74
|
+
item !== null &&
|
|
75
|
+
typeof item === 'object' &&
|
|
76
|
+
typeof item.title === 'string' &&
|
|
77
|
+
typeof item.message === 'string',
|
|
78
|
+
)
|
|
79
|
+
);
|
|
80
|
+
},
|
|
81
|
+
getNumericValue: (value: number): value is number => typeof value === 'number' && !isNaN(value),
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export interface InputCurrencyEmit {
|
|
85
|
+
(event: 'update:modelValue', value: string): void;
|
|
86
|
+
(
|
|
87
|
+
event: 'getSelectedCurrencyMeta',
|
|
88
|
+
value: {
|
|
89
|
+
currency: string;
|
|
90
|
+
symbol: string;
|
|
91
|
+
numericValue: number | null;
|
|
92
|
+
rawValue: string | null;
|
|
93
|
+
},
|
|
94
|
+
): void;
|
|
95
|
+
(event: 'getCurrencyErrors', value: Array<{ title: string; message: string }>): void;
|
|
96
|
+
(event: 'getNumericValue', value: number): void;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export type InputCurrencyPropTypes = ExtractPropTypes<typeof inputCurrencyPropTypes>;
|
|
100
|
+
export type InputCurrencyEmitTypes = typeof inputCurrencyEmitTypes;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<spr-input
|
|
3
|
+
v-bind="$attrs"
|
|
4
|
+
v-model="modelValue"
|
|
5
|
+
type="text"
|
|
6
|
+
:placeholder="props.placeholder"
|
|
7
|
+
:active="popperState"
|
|
8
|
+
:disabled="props.disabled"
|
|
9
|
+
data-testid="input-currency-text"
|
|
10
|
+
@input="handleCurrencyInput"
|
|
11
|
+
@blur="handleBlur"
|
|
12
|
+
>
|
|
13
|
+
<template #prefix>
|
|
14
|
+
<spr-dropdown
|
|
15
|
+
:id="dropdownId"
|
|
16
|
+
v-model="selected.value"
|
|
17
|
+
:class="inputCurrencyClasses.dropdownBaseClasses"
|
|
18
|
+
:menu-list="currencyOptions"
|
|
19
|
+
placement="bottom-start"
|
|
20
|
+
:width="!props.disabledCountryCurrency ? '45px' : '35px'"
|
|
21
|
+
popper-width="300px"
|
|
22
|
+
:disabled="props.disabled || props.disabledCountryCurrency"
|
|
23
|
+
data-testid="input-currency-dropdown"
|
|
24
|
+
@update:model-value="handleSelectedCurrency"
|
|
25
|
+
@get-popper-state="handlePopperState"
|
|
26
|
+
>
|
|
27
|
+
<span :class="inputCurrencyClasses.dropdownWrappertClasses">
|
|
28
|
+
<span>{{ dropdownDisplayText }}</span>
|
|
29
|
+
<icon v-if="!props.disabledCountryCurrency" icon="ph:caret-down" width="16px" height="16px" />
|
|
30
|
+
</span>
|
|
31
|
+
</spr-dropdown>
|
|
32
|
+
</template>
|
|
33
|
+
</spr-input>
|
|
34
|
+
</template>
|
|
35
|
+
|
|
36
|
+
<script setup lang="ts">
|
|
37
|
+
import { Icon } from '@iconify/vue';
|
|
38
|
+
|
|
39
|
+
import SprInput from '@/components/input/input.vue';
|
|
40
|
+
import SprDropdown from '@/components/dropdown/dropdown.vue';
|
|
41
|
+
import { useInputCurrency } from './use-input-currency';
|
|
42
|
+
import { inputCurrencyPropTypes, inputCurrencyEmitTypes } from './input-currency';
|
|
43
|
+
|
|
44
|
+
const props = defineProps(inputCurrencyPropTypes);
|
|
45
|
+
const emit = defineEmits(inputCurrencyEmitTypes);
|
|
46
|
+
|
|
47
|
+
const {
|
|
48
|
+
inputCurrencyClasses,
|
|
49
|
+
dropdownId,
|
|
50
|
+
modelValue,
|
|
51
|
+
currencyOptions,
|
|
52
|
+
selected,
|
|
53
|
+
dropdownDisplayText,
|
|
54
|
+
popperState,
|
|
55
|
+
handleCurrencyInput,
|
|
56
|
+
handleBlur,
|
|
57
|
+
handleSelectedCurrency,
|
|
58
|
+
handlePopperState,
|
|
59
|
+
} = useInputCurrency(props, emit);
|
|
60
|
+
</script>
|