@descope/web-components-ui 1.44.0 → 1.46.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/cjs/index.cjs.js +58 -24
- package/dist/cjs/index.cjs.js.map +1 -1
- package/dist/index.esm.js +469 -232
- package/dist/index.esm.js.map +1 -1
- package/dist/umd/3620.js +1 -1
- package/dist/umd/3620.js.map +1 -1
- package/dist/umd/5348.js +2 -0
- package/dist/umd/5348.js.map +1 -0
- package/dist/umd/6477.js +149 -0
- package/dist/umd/6477.js.map +1 -0
- package/dist/umd/9365.js +1 -1
- package/dist/umd/9365.js.map +1 -1
- package/dist/umd/DescopeDev.js +1 -1
- package/dist/umd/DescopeDev.js.map +1 -1
- package/dist/umd/descope-hybrid-field-index-js.js +3 -3
- package/dist/umd/descope-hybrid-field-index-js.js.map +1 -1
- package/dist/umd/descope-passcode-index-js.js +1 -1
- package/dist/umd/descope-passcode-index-js.js.map +1 -1
- package/dist/umd/index.js +1 -1
- package/dist/umd/index.js.map +1 -1
- package/dist/umd/phone-fields-descope-phone-field-descope-phone-field-internal-index-js.js +1 -1
- package/dist/umd/phone-fields-descope-phone-field-descope-phone-field-internal-index-js.js.map +1 -1
- package/dist/umd/phone-fields-descope-phone-field-index-js.js +1 -1
- package/dist/umd/phone-fields-descope-phone-field-index-js.js.map +1 -1
- package/dist/umd/phone-fields-descope-phone-input-box-field-descope-phone-input-box-internal-index-js.js +2 -2
- package/dist/umd/phone-fields-descope-phone-input-box-field-descope-phone-input-box-internal-index-js.js.map +1 -1
- package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js +2 -113
- package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js.LICENSE.txt +0 -6
- package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js.map +1 -1
- package/package.json +7 -6
- package/src/components/descope-hybrid-field/HybridFieldClass.js +6 -0
- package/src/components/descope-passcode/PasscodeClass.js +2 -0
- package/src/components/phone-fields/descope-phone-field/PhoneFieldClass.js +10 -2
- package/src/components/phone-fields/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +229 -126
- package/src/components/phone-fields/descope-phone-input-box-field/PhoneFieldInputBoxClass.js +42 -24
- package/src/components/phone-fields/descope-phone-input-box-field/descope-phone-input-box-internal/PhoneFieldInternalInputBox.js +179 -83
- package/src/components/phone-fields/descope-phone-input-box-field/index.js +0 -1
- package/src/components/phone-fields/helpers.js +7 -0
- package/src/mixins/index.js +1 -0
- package/src/mixins/inputOverrideValidConstraints.js +12 -0
- package/dist/umd/6424.js +0 -149
- package/dist/umd/6424.js.map +0 -1
- /package/dist/umd/{6424.js.LICENSE.txt → 6477.js.LICENSE.txt} +0 -0
@@ -1,20 +1,19 @@
|
|
1
1
|
import { createBaseInputClass } from '../../../../baseClasses/createBaseInputClass';
|
2
2
|
import { getComponentName } from '../../../../helpers/componentHelpers';
|
3
|
-
import { getCountryByCodeId } from '../../helpers';
|
3
|
+
import { getCountryByCodeId, matchingParenthesis } from '../../helpers';
|
4
|
+
import parsePhoneNumberFromString, { AsYouType } from 'libphonenumber-js/min';
|
4
5
|
|
5
6
|
export const componentName = getComponentName('phone-field-internal-input-box');
|
6
7
|
|
7
8
|
const observedAttributes = [
|
8
9
|
'disabled',
|
9
10
|
'size',
|
10
|
-
'bordered',
|
11
|
-
'invalid',
|
12
11
|
'readonly',
|
13
12
|
'phone-input-placeholder',
|
14
13
|
'name',
|
14
|
+
'maxlength',
|
15
15
|
'autocomplete',
|
16
16
|
'label-type',
|
17
|
-
'allow-alphanumeric-input',
|
18
17
|
];
|
19
18
|
const mapAttrs = {
|
20
19
|
'phone-input-placeholder': 'placeholder',
|
@@ -27,145 +26,242 @@ class PhoneFieldInternal extends BaseInputClass {
|
|
27
26
|
return [].concat(BaseInputClass.observedAttributes || [], observedAttributes);
|
28
27
|
}
|
29
28
|
|
29
|
+
#ayt;
|
30
|
+
|
30
31
|
constructor() {
|
31
32
|
super();
|
32
33
|
|
33
34
|
this.innerHTML = `
|
34
35
|
<div>
|
35
|
-
<descope-text-field tabindex="1"></descope-text-field>
|
36
|
+
<descope-text-field tabindex="1" type="tel" bordered="false"></descope-text-field>
|
36
37
|
</div>
|
37
38
|
`;
|
38
39
|
|
39
|
-
this.
|
40
|
+
this.textField = this.querySelector('descope-text-field');
|
41
|
+
}
|
42
|
+
|
43
|
+
// notice: this function is exposed in parent component
|
44
|
+
get phoneNumberInputEle() {
|
45
|
+
return this.textField.shadowRoot.querySelector('input');
|
40
46
|
}
|
41
47
|
|
42
|
-
get
|
48
|
+
get defaultDialCode() {
|
43
49
|
return getCountryByCodeId(this.getAttribute('default-code'));
|
44
50
|
}
|
45
51
|
|
46
|
-
get
|
47
|
-
return
|
52
|
+
get defaultCode() {
|
53
|
+
return this.getAttribute('default-code');
|
48
54
|
}
|
49
55
|
|
50
56
|
get allowAlphanumericInput() {
|
51
57
|
return this.getAttribute('allow-alphanumeric-input') === 'true';
|
52
58
|
}
|
53
59
|
|
54
|
-
get
|
55
|
-
|
56
|
-
|
57
|
-
}
|
60
|
+
get minLength() {
|
61
|
+
return parseInt(this.getAttribute('minlength'), 10) || 0;
|
62
|
+
}
|
58
63
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
// +972-12345 => +972-12345
|
63
|
-
// 972-12345 => +972-12345
|
64
|
-
// 12345 => +972-12345
|
65
|
-
//
|
66
|
-
// we also want to handle any extra dash if added in the start of the phone number
|
67
|
-
// e.g.:
|
68
|
-
// +972--12345 => +972-12345
|
69
|
-
const pattern = new RegExp(`\\+?${parseInt(this.defaultCountryCode, 10)}--?`);
|
70
|
-
return `${this.defaultCountryCode}-${this.phoneNumberInput.value.replace(pattern, '')}`;
|
71
|
-
}
|
64
|
+
get maxLength() {
|
65
|
+
return parseInt(this.getAttribute('maxlength'), 10) || 50;
|
66
|
+
}
|
72
67
|
|
73
|
-
|
68
|
+
get restrictCountries() {
|
69
|
+
return this.getAttribute('restrict-countries')?.split(',').filter(Boolean) || [];
|
74
70
|
}
|
75
71
|
|
76
|
-
|
77
|
-
this.
|
72
|
+
get isFormatValue() {
|
73
|
+
return this.getAttribute('format-value') === 'true';
|
78
74
|
}
|
79
75
|
|
80
|
-
get
|
81
|
-
return this.
|
76
|
+
get isStrictValidation() {
|
77
|
+
return this.getAttribute('strict-validation') === 'true';
|
82
78
|
}
|
83
79
|
|
84
|
-
get
|
85
|
-
|
80
|
+
get value() {
|
81
|
+
if (!this.textField.value) return '';
|
82
|
+
|
83
|
+
if (!this.isStrictValidation) {
|
84
|
+
return this.#nonParsedValue();
|
85
|
+
}
|
86
|
+
|
87
|
+
const parsedVal = this.#parseWithCountryCode();
|
88
|
+
|
89
|
+
if (parsedVal?.country && parsedVal?.countryCallingCode && parsedVal?.nationalNumber) {
|
90
|
+
return `+${[parsedVal?.countryCallingCode, parsedVal?.nationalNumber].join('-')}`;
|
91
|
+
}
|
92
|
+
|
93
|
+
// if failed to parse or to find country code return text field value
|
94
|
+
return this.textField.value;
|
86
95
|
}
|
87
96
|
|
88
|
-
|
89
|
-
|
97
|
+
set value(val) {
|
98
|
+
this.textField.value = val;
|
90
99
|
}
|
91
100
|
|
92
|
-
|
93
|
-
|
101
|
+
init() {
|
102
|
+
this.addEventListener('focus', (e) => {
|
103
|
+
// We want to ignore focus events we are dispatching
|
104
|
+
if (e.isTrusted) this.textField.focus();
|
105
|
+
});
|
106
|
+
|
107
|
+
super.init?.();
|
108
|
+
|
109
|
+
this.textField.addEventListener('input', this.#onInput.bind(this));
|
110
|
+
this.handleFocusEventsDispatching([this.textField]);
|
94
111
|
}
|
95
112
|
|
96
113
|
getValidity() {
|
97
114
|
const validPhonePattern = /^\+?\d{1,4}-?(?:\d-?){1,15}$/;
|
98
|
-
const stripValue = this.value
|
115
|
+
const stripValue = this.#sanitizeVal(this.textField.value);
|
99
116
|
|
100
|
-
if (this.isRequired && !this.value) {
|
117
|
+
if (this.isRequired && !this.textField.value) {
|
101
118
|
return { valueMissing: true };
|
102
119
|
}
|
103
120
|
|
104
|
-
if (
|
105
|
-
|
121
|
+
if (this.textField.value) {
|
122
|
+
if (stripValue.length < this.minLength) {
|
123
|
+
return { tooShort: true };
|
124
|
+
}
|
125
|
+
|
126
|
+
if (
|
127
|
+
// has `strict-validation` and not properly parsed
|
128
|
+
(this.isStrictValidation && this.textField.value && !this.#isValidParsedValue()) ||
|
129
|
+
// if no `strict-validation` then conform with naive pattern
|
130
|
+
(!this.isStrictValidation && this.textField.value && !validPhonePattern.test(this.value))
|
131
|
+
) {
|
132
|
+
return { patternMismatch: true };
|
133
|
+
}
|
106
134
|
}
|
107
135
|
|
108
|
-
|
109
|
-
|
136
|
+
return {};
|
137
|
+
}
|
138
|
+
|
139
|
+
setSelectionRange(...args) {
|
140
|
+
this.textField.setSelectionRange(...args);
|
141
|
+
}
|
142
|
+
|
143
|
+
attributeChangedCallback(attrName, oldValue, newValue) {
|
144
|
+
super.attributeChangedCallback(attrName, oldValue, newValue);
|
145
|
+
|
146
|
+
if (oldValue !== newValue && observedAttributes.includes(attrName)) {
|
147
|
+
const attr = mapAttrs[attrName] || attrName;
|
148
|
+
this.textField.setAttribute(attr, newValue);
|
110
149
|
}
|
150
|
+
}
|
151
|
+
|
152
|
+
#onInput(e) {
|
153
|
+
let sanitizedInput = this.#sanitizeInput(e.target.value);
|
111
154
|
|
112
|
-
if (this.
|
113
|
-
|
155
|
+
if (this.isFormatValue && this.#canFormat(sanitizedInput)) {
|
156
|
+
sanitizedInput = this.#formatPhoneNumber(sanitizedInput);
|
114
157
|
}
|
115
158
|
|
116
|
-
|
159
|
+
e.target.value = sanitizedInput;
|
117
160
|
}
|
118
161
|
|
119
|
-
|
120
|
-
this.
|
121
|
-
|
122
|
-
|
123
|
-
});
|
162
|
+
#nonParsedValue() {
|
163
|
+
if (!this.defaultDialCode) {
|
164
|
+
return this.textField.value;
|
165
|
+
}
|
124
166
|
|
125
|
-
|
126
|
-
this
|
167
|
+
const nationalNumber = this.#trimDuplicateCountryCode(this.textField.value);
|
168
|
+
const sanitizedVal = this.#sanitizeVal(nationalNumber);
|
169
|
+
|
170
|
+
return [this.defaultDialCode, sanitizedVal].join('-');
|
171
|
+
}
|
172
|
+
|
173
|
+
#parseWithCountryCode() {
|
174
|
+
if (this.defaultDialCode) {
|
175
|
+
return parsePhoneNumberFromString(
|
176
|
+
[this.defaultDialCode, this.#sanitizeVal(this.textField.value)].filter(Boolean).join('')
|
177
|
+
);
|
178
|
+
}
|
179
|
+
|
180
|
+
// if default-code or not parsed - try to extract country code from value
|
181
|
+
return parsePhoneNumberFromString(this.textField.value);
|
182
|
+
}
|
183
|
+
|
184
|
+
#sanitizeVal(val) {
|
185
|
+
return val.replace(/\D/g, '');
|
127
186
|
}
|
128
187
|
|
129
|
-
|
130
|
-
|
131
|
-
|
188
|
+
#trimDuplicateCountryCode(val) {
|
189
|
+
if (this.textField.value?.[0] === '+') {
|
190
|
+
const dialCodePrefixPattern = new RegExp(`^\\${this.defaultDialCode}`);
|
191
|
+
const trimmed = val.replace(dialCodePrefixPattern, '');
|
192
|
+
return trimmed;
|
193
|
+
}
|
194
|
+
return val;
|
195
|
+
}
|
196
|
+
|
197
|
+
#isValidParsedValue() {
|
198
|
+
const parsed = parsePhoneNumberFromString(this.value);
|
199
|
+
return (
|
200
|
+
parsed && // parsed successfully (not undefined)
|
201
|
+
parsed.isValid?.() && // Parsed object is valid
|
202
|
+
parsed.country && // Parsed object with a country code
|
203
|
+
this.#isAllowedCountry(parsed.country) && // Parsed with allowed country code
|
204
|
+
(this.defaultCode ? this.defaultCode === parsed.country : true) // In case default country code is set validate parsed country matches it
|
132
205
|
);
|
133
206
|
}
|
134
207
|
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
e.target.value = '';
|
140
|
-
}
|
208
|
+
#isAllowedCountry(countryCode) {
|
209
|
+
if (!this.restrictCountries) {
|
210
|
+
return true;
|
211
|
+
}
|
141
212
|
|
142
|
-
|
143
|
-
|
144
|
-
.replace('--', '-')
|
145
|
-
.replace('+-', '+');
|
146
|
-
|
147
|
-
let sanitizedInput = e.target.value;
|
148
|
-
if (!this.allowAlphanumericInput) {
|
149
|
-
const telDigitsRegExp = /^[+\d-]+$/;
|
150
|
-
sanitizedInput = e.target.value
|
151
|
-
.split('')
|
152
|
-
.filter((char) => telDigitsRegExp.test(char))
|
153
|
-
.join('');
|
154
|
-
}
|
213
|
+
return this.restrictCountries.includes(countryCode);
|
214
|
+
}
|
155
215
|
|
156
|
-
|
157
|
-
|
216
|
+
#sanitizeInput(val) {
|
217
|
+
val = val
|
218
|
+
.replace(/^-+/, '') // dash as first char
|
219
|
+
.replace(/(?!^)\+/g, '') // multiple plus symbols
|
220
|
+
.replace('--', '-') // consecutive dashes
|
221
|
+
.replace('+-', '+'); // dash following plus symbol
|
222
|
+
|
223
|
+
if (!this.allowAlphanumericInput) {
|
224
|
+
const telDigitsRegExp = /^[+\d-\(\)]+$/;
|
225
|
+
val = val
|
226
|
+
.split('')
|
227
|
+
.filter((char) => telDigitsRegExp.test(char))
|
228
|
+
.join('');
|
229
|
+
}
|
158
230
|
|
159
|
-
|
231
|
+
return val;
|
160
232
|
}
|
161
233
|
|
162
|
-
|
163
|
-
|
234
|
+
#formatPhoneNumber(phoneNumber = '') {
|
235
|
+
// Get country code from `default-code or` from phone number
|
236
|
+
const countryCode = this.defaultCode || this.#getCountryCodeFromValue(phoneNumber);
|
164
237
|
|
165
|
-
if
|
166
|
-
|
167
|
-
|
238
|
+
// Skip formatting if no country code is available
|
239
|
+
if (!countryCode) {
|
240
|
+
return phoneNumber;
|
241
|
+
}
|
242
|
+
|
243
|
+
// Update AsYouType country code if needed
|
244
|
+
if (!this.#ayt || this.#ayt.country !== countryCode) {
|
245
|
+
this.#ayt = new AsYouType(countryCode);
|
168
246
|
}
|
247
|
+
|
248
|
+
// We need to reset AsYouType instance before setting new input
|
249
|
+
this.#ayt.reset();
|
250
|
+
|
251
|
+
// Set AsYouType input
|
252
|
+
const formattedVal = this.#ayt.input(phoneNumber) || phoneNumber;
|
253
|
+
|
254
|
+
return formattedVal;
|
255
|
+
}
|
256
|
+
|
257
|
+
#getCountryCodeFromValue(val) {
|
258
|
+
const parsed = parsePhoneNumberFromString(val);
|
259
|
+
return parsed?.country || '';
|
260
|
+
}
|
261
|
+
|
262
|
+
#canFormat(val) {
|
263
|
+
if (!matchingParenthesis(val)) return false;
|
264
|
+
return true;
|
169
265
|
}
|
170
266
|
}
|
171
267
|
|
@@ -1,5 +1,12 @@
|
|
1
|
+
import parsePhoneNumberFromString from 'libphonenumber-js/min';
|
1
2
|
import CountryCodes from './CountryCodes';
|
2
3
|
|
3
4
|
export const getCountryByCodeId = (countryCode) => {
|
4
5
|
return CountryCodes.find((c) => c.code === countryCode)?.dialCode;
|
5
6
|
};
|
7
|
+
|
8
|
+
export const matchingParenthesis = (val) => {
|
9
|
+
const openParenMatches = val.match(/\(/g);
|
10
|
+
const closeParenMatches = val.match(/\)/g);
|
11
|
+
return openParenMatches?.length === closeParenMatches?.length;
|
12
|
+
};
|
package/src/mixins/index.js
CHANGED
@@ -11,3 +11,4 @@ export { normalizeBooleanAttributesMixin } from './normalizeBooleanAttributesMix
|
|
11
11
|
export { lifecycleEventsMixin } from './lifecycleEventsMixin';
|
12
12
|
export { inputEventsDispatchingMixin } from './inputEventsDispatchingMixin';
|
13
13
|
export { externalInputMixin } from './externalInputMixin';
|
14
|
+
export { inputOverrideValidConstraintsMixin } from './inputOverrideValidConstraints';
|
@@ -0,0 +1,12 @@
|
|
1
|
+
export const inputOverrideValidConstraintsMixin = (superclass) =>
|
2
|
+
class InputOverrideValidConstraintsMixinClass extends superclass {
|
3
|
+
init() {
|
4
|
+
super.init?.();
|
5
|
+
|
6
|
+
// vaadin uses `validConstraints` (required, pattern, minlength, maxlength) to determine if it should validate
|
7
|
+
// the input or not. We want to override this behavior, so we can enforce validation even if these attributes are not present.
|
8
|
+
if (this.baseElement._hasValidConstraints) {
|
9
|
+
this.baseElement._hasValidConstraints = () => true;
|
10
|
+
}
|
11
|
+
}
|
12
|
+
};
|