@descope/web-components-ui 1.43.0 → 1.45.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.
Files changed (47) hide show
  1. package/dist/cjs/index.cjs.js +124 -55
  2. package/dist/cjs/index.cjs.js.map +1 -1
  3. package/dist/index.esm.js +530 -256
  4. package/dist/index.esm.js.map +1 -1
  5. package/dist/umd/3620.js +1 -1
  6. package/dist/umd/3620.js.map +1 -1
  7. package/dist/umd/5348.js +2 -0
  8. package/dist/umd/5348.js.map +1 -0
  9. package/dist/umd/6477.js +149 -0
  10. package/dist/umd/6477.js.map +1 -0
  11. package/dist/umd/9365.js +1 -1
  12. package/dist/umd/9365.js.map +1 -1
  13. package/dist/umd/DescopeDev.js +1 -1
  14. package/dist/umd/DescopeDev.js.map +1 -1
  15. package/dist/umd/descope-hybrid-field-index-js.js +3 -3
  16. package/dist/umd/descope-hybrid-field-index-js.js.map +1 -1
  17. package/dist/umd/descope-passcode-index-js.js +1 -1
  18. package/dist/umd/descope-passcode-index-js.js.map +1 -1
  19. package/dist/umd/descope-timer-button.js +1 -1
  20. package/dist/umd/descope-timer-button.js.map +1 -1
  21. package/dist/umd/descope-timer.js +1 -1
  22. package/dist/umd/descope-timer.js.map +1 -1
  23. package/dist/umd/index.js +1 -1
  24. package/dist/umd/index.js.map +1 -1
  25. package/dist/umd/phone-fields-descope-phone-field-descope-phone-field-internal-index-js.js +1 -1
  26. package/dist/umd/phone-fields-descope-phone-field-descope-phone-field-internal-index-js.js.map +1 -1
  27. package/dist/umd/phone-fields-descope-phone-field-index-js.js +1 -1
  28. package/dist/umd/phone-fields-descope-phone-field-index-js.js.map +1 -1
  29. package/dist/umd/phone-fields-descope-phone-input-box-field-descope-phone-input-box-internal-index-js.js +2 -2
  30. package/dist/umd/phone-fields-descope-phone-input-box-field-descope-phone-input-box-internal-index-js.js.map +1 -1
  31. package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js +2 -113
  32. package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js.LICENSE.txt +0 -6
  33. package/dist/umd/phone-fields-descope-phone-input-box-field-index-js.js.map +1 -1
  34. package/package.json +7 -6
  35. package/src/components/descope-hybrid-field/HybridFieldClass.js +6 -0
  36. package/src/components/descope-passcode/PasscodeClass.js +2 -0
  37. package/src/components/phone-fields/descope-phone-field/PhoneFieldClass.js +10 -2
  38. package/src/components/phone-fields/descope-phone-field/descope-phone-field-internal/PhoneFieldInternal.js +229 -125
  39. package/src/components/phone-fields/descope-phone-input-box-field/PhoneFieldInputBoxClass.js +42 -24
  40. package/src/components/phone-fields/descope-phone-input-box-field/descope-phone-input-box-internal/PhoneFieldInternalInputBox.js +176 -79
  41. package/src/components/phone-fields/descope-phone-input-box-field/index.js +0 -1
  42. package/src/components/phone-fields/helpers.js +7 -0
  43. package/src/mixins/index.js +1 -0
  44. package/src/mixins/inputOverrideValidConstraints.js +12 -0
  45. package/dist/umd/6424.js +0 -149
  46. package/dist/umd/6424.js.map +0 -1
  47. /package/dist/umd/{6424.js.LICENSE.txt → 6477.js.LICENSE.txt} +0 -0
package/dist/index.esm.js CHANGED
@@ -15,7 +15,7 @@ import '@vaadin/number-field';
15
15
  import '@vaadin/password-field';
16
16
  import MarkdownIt from 'markdown-it';
17
17
  import '@vaadin/text-area';
18
- import { parsePhoneNumberFromString } from 'libphonenumber-js/min';
18
+ import parsePhoneNumberFromString$1, { parsePhoneNumberFromString, AsYouType } from 'libphonenumber-js/min';
19
19
  import '@vaadin/grid';
20
20
  import { GridSortColumn } from '@vaadin/grid/vaadin-grid-sort-column';
21
21
  import { GridSelectionColumn } from '@vaadin/grid/vaadin-grid-selection-column';
@@ -2244,6 +2244,19 @@ const externalInputMixin =
2244
2244
  }
2245
2245
  };
2246
2246
 
2247
+ const inputOverrideValidConstraintsMixin = (superclass) =>
2248
+ class InputOverrideValidConstraintsMixinClass extends superclass {
2249
+ init() {
2250
+ super.init?.();
2251
+
2252
+ // vaadin uses `validConstraints` (required, pattern, minlength, maxlength) to determine if it should validate
2253
+ // the input or not. We want to override this behavior, so we can enforce validation even if these attributes are not present.
2254
+ if (this.baseElement._hasValidConstraints) {
2255
+ this.baseElement._hasValidConstraints = () => true;
2256
+ }
2257
+ }
2258
+ };
2259
+
2247
2260
  const createBaseInputClass$1 = (...args) =>
2248
2261
  compose$1(
2249
2262
  inputValidationMixin$1,
@@ -8163,6 +8176,7 @@ const PasscodeClass = compose$1(
8163
8176
  },
8164
8177
  }),
8165
8178
  draggableMixin$1,
8179
+ inputOverrideValidConstraintsMixin,
8166
8180
  composedProxyInputMixin$1({ proxyProps: ['value', 'selectionStart'] }),
8167
8181
  componentNameValidationMixin$1,
8168
8182
  customMixin$c
@@ -10210,7 +10224,7 @@ const parsePhoneNumber = (val) => {
10210
10224
 
10211
10225
  const componentName$P = getComponentName$1('phone-field-internal');
10212
10226
 
10213
- const commonAttrs$1 = ['disabled', 'size', 'bordered', 'readonly', 'allow-alphanumeric-input'];
10227
+ const commonAttrs$1 = ['disabled', 'size', 'bordered', 'readonly'];
10214
10228
  const countryAttrs = ['country-input-placeholder', 'default-code', 'restrict-countries'];
10215
10229
  const phoneAttrs = ['phone-input-placeholder', 'maxlength', 'autocomplete', 'name'];
10216
10230
  const labelTypeAttrs = ['label-type', 'country-input-label', 'label'];
@@ -10229,6 +10243,8 @@ let PhoneFieldInternal$1 = class PhoneFieldInternal extends BaseInputClass$8 {
10229
10243
  return [].concat(BaseInputClass$8.observedAttributes || [], inputRelatedAttrs$1);
10230
10244
  }
10231
10245
 
10246
+ #ayt;
10247
+
10232
10248
  constructor() {
10233
10249
  super();
10234
10250
 
@@ -10245,200 +10261,329 @@ let PhoneFieldInternal$1 = class PhoneFieldInternal extends BaseInputClass$8 {
10245
10261
  </div>
10246
10262
  `;
10247
10263
 
10248
- this.countryCodeInput = this.querySelector('descope-combo-box');
10249
- this.phoneNumberInput = this.querySelector('descope-text-field');
10250
- this.inputs = [this.countryCodeInput, this.phoneNumberInput];
10264
+ this.comboBox = this.querySelector('descope-combo-box');
10265
+ this.textField = this.querySelector('descope-text-field');
10251
10266
 
10252
- forwardAttrs$1(this, this.countryCodeInput, { includeAttrs: ['label-type'] });
10253
- forwardAttrs$1(this, this.phoneNumberInput, { includeAttrs: ['label-type', 'required'] });
10267
+ this.inputs = [this.comboBox, this.textField];
10268
+
10269
+ forwardAttrs$1(this, this.comboBox, { includeAttrs: ['label-type'] });
10270
+ forwardAttrs$1(this, this.textField, { includeAttrs: ['label-type', 'required'] });
10254
10271
 
10255
10272
  // override combo box setter to display dialCode value in input
10256
- this.countryCodeInput.customValueTransformFn = (val) => {
10273
+ this.comboBox.customValueTransformFn = (val) => {
10257
10274
  const [, dialCode] = val?.split?.(' ') || [];
10258
10275
  return dialCode;
10259
10276
  };
10260
10277
  }
10261
10278
 
10279
+ // exposed from main component
10262
10280
  get countryCodeInputData() {
10263
- return this.countryCodeInput.items;
10281
+ return this.comboBox.items;
10282
+ }
10283
+
10284
+ // exposed from main component
10285
+ get countryCodeValue() {
10286
+ return this.comboBox.shadowRoot.querySelector('input').value;
10287
+ }
10288
+
10289
+ // exposed from main component
10290
+ get phoneNumberInputEle() {
10291
+ return this.textField.shadowRoot.querySelector('input');
10264
10292
  }
10265
10293
 
10266
10294
  get allowAlphanumericInput() {
10267
10295
  return this.getAttribute('allow-alphanumeric-input') === 'true';
10268
10296
  }
10269
10297
 
10270
- get value() {
10271
- if (!this.phoneNumberValue) {
10272
- return '';
10273
- }
10274
- return `${this.countryCodeInput.value}-${this.phoneNumberInput.value}`;
10298
+ get defaultCode() {
10299
+ return this.getAttribute('default-code');
10275
10300
  }
10276
10301
 
10277
- set value(val) {
10278
- const [countryCode, phoneNumber] = parsePhoneNumber(val);
10302
+ get selectionStart() {
10303
+ return this.textField.selectionStart;
10304
+ }
10279
10305
 
10280
- this.#setCountryCode(countryCode);
10281
- this.#setPhoneNumber(phoneNumber);
10306
+ get minLength() {
10307
+ return parseInt(this.getAttribute('minlength'), 10) || 0;
10282
10308
  }
10283
10309
 
10284
- setSelectionRange(...args) {
10285
- this.phoneNumberInput.setSelectionRange(...args);
10310
+ get selectedCountryCode() {
10311
+ return this.comboBox?.selectedItem?.getAttribute('data-country-code');
10286
10312
  }
10287
10313
 
10288
- get selectionStart() {
10289
- return this.phoneNumberInput.selectionStart;
10314
+ get restrictCountries() {
10315
+ const attr = this.getAttribute('restrict-countries');
10316
+ return attr?.split(',').filter(Boolean) || [];
10290
10317
  }
10291
10318
 
10292
- #setCountryCode(val) {
10293
- if (val) {
10294
- const countryCodeItem = this.getCountryByDialCode(val);
10295
- if (countryCodeItem) {
10296
- this.countryCodeInput.selectedItem = countryCodeItem;
10297
- }
10298
- } else {
10299
- this.countryCodeInput.selectedItem = undefined;
10300
- }
10319
+ get isFormatValue() {
10320
+ return this.getAttribute('format-value') === 'true';
10301
10321
  }
10302
10322
 
10303
- #setPhoneNumber(val) {
10304
- if (this.phoneNumberInput.value === val) {
10305
- return;
10323
+ // `strict validation` enforces value parsing with libphonenumber-js
10324
+ get isStrictValidation() {
10325
+ return this.getAttribute('strict-validation') === 'true';
10326
+ }
10327
+
10328
+ get value() {
10329
+ if (!this.comboBox.value || !this.textField.value) {
10330
+ return '';
10306
10331
  }
10307
10332
 
10308
- this.phoneNumberInput.value = val;
10309
- }
10333
+ const [countryCode, phoneNumber] = parsePhoneNumber(
10334
+ `${this.comboBox.value}-${this.textField.value}`
10335
+ );
10310
10336
 
10311
- get phoneNumberValue() {
10312
- return this.phoneNumberInput.value;
10337
+ return `${countryCode || this.comboBox.value}-${phoneNumber || this.textField.value}`;
10313
10338
  }
10314
10339
 
10315
- get countryCodeValue() {
10316
- return this.countryCodeInput.shadowRoot.querySelector('input').value;
10317
- }
10340
+ set value(val) {
10341
+ const [countryCode, nationalNumber] = parsePhoneNumber(val);
10318
10342
 
10319
- get phoneNumberInputEle() {
10320
- return this.phoneNumberInput.shadowRoot.querySelector('input');
10343
+ this.#setCountryCode(countryCode);
10344
+ this.#setNationalNumber(nationalNumber);
10321
10345
  }
10322
10346
 
10323
- get minLength() {
10324
- return parseInt(this.getAttribute('minlength'), 10) || 0;
10347
+ init() {
10348
+ this.addEventListener('focus', (e) => {
10349
+ // we want to ignore focus events we are dispatching
10350
+ if (e.isTrusted) this.inputs[1].focus();
10351
+ });
10352
+
10353
+ super.init?.();
10354
+
10355
+ this.#initInputs();
10325
10356
  }
10326
10357
 
10327
10358
  getValidity() {
10328
- const hasCode = this.countryCodeInput.value;
10329
- const hasPhone = this.phoneNumberInput.value;
10330
- const emptyValue = !hasCode || !hasPhone;
10331
- const hasMinPhoneLength =
10332
- this.phoneNumberInput.value?.length && this.phoneNumberInput.value.length < this.minLength;
10359
+ const countryCode = this.comboBox.value;
10360
+ const nationalNumer = this.textField.value;
10361
+
10362
+ const hasCountryCode = !!countryCode.length;
10363
+ const hasNationalNumber = !!nationalNumer.length;
10333
10364
 
10334
- if (this.isRequired && emptyValue) {
10365
+ const isEmpty = !hasCountryCode || !hasNationalNumber;
10366
+ const isValidLength = hasNationalNumber && nationalNumer.length >= this.minLength;
10367
+
10368
+ if (this.isRequired && isEmpty) {
10335
10369
  return { valueMissing: true };
10336
10370
  }
10337
- if (hasMinPhoneLength) {
10371
+
10372
+ if (!isValidLength) {
10338
10373
  return { tooShort: true };
10339
10374
  }
10340
- if (hasPhone && !hasCode) {
10341
- return { valueMissing: true };
10375
+
10376
+ if (this.value && this.isStrictValidation && !this.#isValidParsedValue()) {
10377
+ return { patternMismatch: true };
10342
10378
  }
10379
+
10343
10380
  return {};
10344
10381
  }
10345
10382
 
10346
- init() {
10347
- this.addEventListener('focus', (e) => {
10348
- // we want to ignore focus events we are dispatching
10349
- if (e.isTrusted) this.inputs[1].focus();
10383
+ setSelectionRange(...args) {
10384
+ this.textField.setSelectionRange(...args);
10385
+ }
10386
+
10387
+ attributeChangedCallback(attrName, oldValue, newValue) {
10388
+ super.attributeChangedCallback(attrName, oldValue, newValue);
10389
+
10390
+ if (oldValue !== newValue) {
10391
+ if (attrName === 'default-code' && newValue) {
10392
+ this.#handleDefaultCountryCode(newValue);
10393
+ } else if (inputRelatedAttrs$1.includes(attrName)) {
10394
+ const attr = mapAttrs$1[attrName] || attrName;
10395
+
10396
+ if (commonAttrs$1.includes(attrName)) {
10397
+ this.inputs.forEach((input) => input.setAttribute(attr, newValue));
10398
+ } else if (countryAttrs.includes(attrName)) {
10399
+ this.comboBox.setAttribute(attr, newValue);
10400
+ } else if (phoneAttrs.includes(attrName)) {
10401
+ this.textField.setAttribute(attr, newValue);
10402
+ }
10403
+ }
10404
+
10405
+ if (labelTypeAttrs.includes(attrName)) {
10406
+ this.#handleLabelTypeAttrs(attrName, newValue);
10407
+ }
10408
+
10409
+ if (attrName === 'restrict-countries') {
10410
+ this.#updateComboBoxItems(this.restrictCountries);
10411
+ }
10412
+ }
10413
+ }
10414
+
10415
+ #initInputs() {
10416
+ // Sanitize phone input value to filter everything but digits
10417
+ this.textField.addEventListener('input', (e) => {
10418
+ if (!this.allowAlphanumericInput) {
10419
+ const telDigitsRegExp = /^\d$/;
10420
+ const sanitizedInput = e.target.value
10421
+ .split('')
10422
+ .filter((char) => telDigitsRegExp.test(char))
10423
+ .join('');
10424
+ e.target.value = sanitizedInput;
10425
+ }
10350
10426
  });
10351
10427
 
10352
- super.init?.();
10353
- this.initInputs();
10428
+ this.handleFocusEventsDispatching(this.inputs);
10429
+ this.handleInputEventDispatching();
10430
+
10431
+ // verify country code item against phone number pattern and replace if needed and country is allowed
10432
+ // (e.g. +1 can be US or CA, depending on the pattern)
10433
+ this.addEventListener('input', this.#handleSameCountryCodes.bind(this));
10354
10434
  }
10355
10435
 
10356
- getRestrictedCountries() {
10357
- const attr = this.getAttribute('restrict-countries');
10358
- return attr ? attr.split(',') : [];
10436
+ #setCountryCode(val) {
10437
+ if (!val || val === this.selectedCountryCode) return;
10438
+
10439
+ let countryCodeItem = undefined;
10440
+
10441
+ if (this.value) {
10442
+ // try to parse the phone number, and set country code item according to actual dial code (e.g. `+1` can be `US` or `CA`)
10443
+ const code = this.#getCountryCodeByPhoneNumber(`${val}-${this.textField.value}`);
10444
+ countryCodeItem = this.#getCountryByCodeId(code);
10445
+ }
10446
+
10447
+ // in case country code item does not exist (for example: Parsed code is CA for +1 - but Canada is not allowed)
10448
+ // then use the first option with same dial code (e.g. in that case - `US` for +1)
10449
+ if (!countryCodeItem) {
10450
+ countryCodeItem = this.#getCountryByDialCode(val);
10451
+ }
10452
+
10453
+ // set country code item; in it doesn't exist in list - set `undefined`
10454
+ this.comboBox.selectedItem = countryCodeItem;
10359
10455
  }
10360
10456
 
10361
- getCountryByDialCode(countryDialCode) {
10362
- return this.countryCodeInput.items?.find((c) => c.getAttribute('data-id') === countryDialCode);
10457
+ #setNationalNumber(val) {
10458
+ if (this.isFormatValue) {
10459
+ val = this.#formatNationalNumber(val);
10460
+ }
10461
+
10462
+ if (this.textField.value !== val) {
10463
+ this.textField.value = val;
10464
+ }
10465
+ }
10466
+
10467
+ #formatNationalNumber(nationalNumber = '') {
10468
+ // re-initialize AsYouType if country code is outdated
10469
+ if (!this.#ayt || this.#ayt.country !== this.selectedCountryCode) {
10470
+ this.#ayt = new AsYouType(this.selectedCountryCode);
10471
+ }
10472
+
10473
+ // reset previous AsYouType input
10474
+ this.#ayt.reset();
10475
+
10476
+ const formattedVal = this.#ayt.input(nationalNumber);
10477
+
10478
+ return formattedVal || nationalNumber;
10363
10479
  }
10364
10480
 
10365
- getCountryByCodeId(countryCode) {
10366
- return this.countryCodeInput.items?.find(
10367
- (c) => c.getAttribute('data-country-code') === countryCode
10481
+ #isValidParsedValue() {
10482
+ const parsed = parsePhoneNumberFromString(this.value);
10483
+
10484
+ return (
10485
+ parsed && // Parsed successfully (not undefined)
10486
+ parsed.isValid?.() && // Parsed object is valid
10487
+ parsed.country && // Parsed object with a country code
10488
+ this.#isAllowedCountry(parsed.country) // Parsed with allowed country code
10368
10489
  );
10369
10490
  }
10370
10491
 
10371
- updateCountryCodeItems(restrictCountries) {
10492
+ #isAllowedCountry(countryCode) {
10493
+ if (!this.restrictCountries) {
10494
+ return true;
10495
+ }
10496
+
10497
+ return this.restrictCountries.includes(countryCode);
10498
+ }
10499
+
10500
+ // return country item by dial code `data-id` (e.g. `+1`)
10501
+ #getCountryByDialCode(dialCode) {
10502
+ return this.comboBox.items?.find((c) => c.getAttribute('data-id') === dialCode) || undefined;
10503
+ }
10504
+
10505
+ // return country item by country code `data-country-code` (e.g. `US`)
10506
+ #getCountryByCodeId(countryCode) {
10507
+ return this.comboBox.items?.find((c) => c.getAttribute('data-country-code') === countryCode);
10508
+ }
10509
+
10510
+ #getCountryCodeByPhoneNumber(val) {
10511
+ if (!val) return undefined;
10512
+ const parsed = parsePhoneNumberFromString(val);
10513
+ if (!parsed?.country) return undefined;
10514
+ const foundCountryItem = this.#getCountryByCodeId(parsed.country);
10515
+ return foundCountryItem?.getAttribute('data-country-code');
10516
+ }
10517
+
10518
+ #updateComboBoxItems(restrictCountries) {
10372
10519
  const items = restrictCountries.length
10373
10520
  ? CountryCodes.filter((c) => restrictCountries.includes(c.code))
10374
10521
  : CountryCodes;
10522
+
10375
10523
  this.querySelector('descope-combo-box').innerHTML = items
10376
10524
  .map((item) => comboBoxItem(item))
10377
10525
  .join('');
10378
10526
  }
10379
10527
 
10380
- handleDefaultCountryCode(countryCode) {
10381
- if (!this.countryCodeInput.value) {
10382
- const countryCodeItem = this.getCountryByCodeId(countryCode);
10528
+ #handleDefaultCountryCode(countryCode) {
10529
+ if (!this.comboBox.value) {
10530
+ const countryCodeItem = this.#getCountryByCodeId(countryCode);
10383
10531
  // When replacing the input component (inserting internal component into text-field) -
10384
10532
  // Vaadin resets the input's value. We use setTimeout in order to make sure this happens
10385
10533
  // after the reset.
10386
10534
  if (countryCodeItem) {
10387
10535
  setTimeout(() => {
10388
- this.countryCodeInput.selectedItem = countryCodeItem;
10536
+ this.comboBox.selectedItem = countryCodeItem;
10389
10537
  });
10390
10538
  }
10391
10539
  }
10392
10540
  }
10393
10541
 
10394
- initInputs() {
10395
- // Sanitize phone input value to filter everything but digits
10396
- this.phoneNumberInput.addEventListener('input', (e) => {
10397
- // we want to update only phoneNumberInput, and avoid triggering `set value`
10398
- e.stopPropagation();
10542
+ #handleSameCountryCodes() {
10543
+ if (!this.value) return;
10399
10544
 
10400
- if (!this.allowAlphanumericInput) {
10401
- const telDigitsRegExp = /^\d$/;
10402
- const sanitizedInput = e.target.value
10403
- .split('')
10404
- .filter((char) => telDigitsRegExp.test(char))
10405
- .join('');
10406
- e.target.value = sanitizedInput;
10407
- }
10408
- });
10545
+ const country = this.#getCountryCodeByPhoneNumber(this.value);
10409
10546
 
10410
- this.handleFocusEventsDispatching(this.inputs);
10411
- this.handleInputEventDispatching();
10547
+ if (!country) return;
10548
+
10549
+ // re-set country code if needed (same coutnry code for different countries, e.g. +1 for US or CA)
10550
+ if (this.selectedCountryCode !== country) {
10551
+ const foundCountryItem = this.#getCountryByCodeId(country);
10552
+ // if found country is defined in country list then set it, otherwise - clear phone number
10553
+ if (foundCountryItem) {
10554
+ this.comboBox.selectedItem = foundCountryItem;
10555
+ }
10556
+ }
10412
10557
  }
10413
10558
 
10414
- handleLabelTypeAttrs(attrName, newValue) {
10559
+ #handleLabelTypeAttrs(attrName, newValue) {
10415
10560
  // set or remove label attributes from inner text/combo components on `label-type` change
10416
10561
  const attr = mapAttrs$1[attrName] || attrName;
10417
10562
 
10418
10563
  if (attrName === 'label-type') {
10419
- this.onLabelTypeChange(newValue);
10564
+ this.#handleLabelTypeChange(newValue);
10420
10565
  }
10421
10566
  // on inner components label attr change - set label attributes for text/combo components
10422
10567
  // only if label-type is `floating`
10423
10568
  else if (this.getAttribute('label-type') === 'floating') {
10424
10569
  if (attrName === 'country-input-label') {
10425
- this.countryCodeInput.setAttribute(attr, newValue);
10570
+ this.comboBox.setAttribute(attr, newValue);
10426
10571
  } else if (attrName === 'label') {
10427
- this.phoneNumberInput.setAttribute(attr, newValue);
10572
+ this.textField.setAttribute(attr, newValue);
10428
10573
  }
10429
10574
  }
10430
10575
  }
10431
10576
 
10432
- onLabelTypeChange(newValue) {
10577
+ #handleLabelTypeChange(newValue) {
10433
10578
  if (newValue === 'floating') {
10434
10579
  // on change to `floating` label - set inner components `label` and `placeholder`
10435
- this.countryCodeInput.setAttribute('label', this.getAttribute('country-input-label') || '');
10436
- this.countryCodeInput.setAttribute(
10580
+ this.comboBox.setAttribute('label', this.getAttribute('country-input-label') || '');
10581
+ this.comboBox.setAttribute(
10437
10582
  'placeholder',
10438
10583
  this.getAttribute('country-input-placeholder') || ''
10439
10584
  );
10440
- this.phoneNumberInput.setAttribute('label', this.getAttribute('label') || '');
10441
- this.phoneNumberInput.setAttribute(
10585
+ this.textField.setAttribute('label', this.getAttribute('label') || '');
10586
+ this.textField.setAttribute(
10442
10587
  'placeholder',
10443
10588
  this.getAttribute('phone-input-placeholder') || ''
10444
10589
  );
@@ -10447,34 +10592,6 @@ let PhoneFieldInternal$1 = class PhoneFieldInternal extends BaseInputClass$8 {
10447
10592
  this.inputs.forEach((input) => input.removeAttribute('label'));
10448
10593
  }
10449
10594
  }
10450
-
10451
- attributeChangedCallback(attrName, oldValue, newValue) {
10452
- super.attributeChangedCallback(attrName, oldValue, newValue);
10453
-
10454
- if (oldValue !== newValue) {
10455
- if (attrName === 'default-code' && newValue) {
10456
- this.handleDefaultCountryCode(newValue);
10457
- } else if (inputRelatedAttrs$1.includes(attrName)) {
10458
- const attr = mapAttrs$1[attrName] || attrName;
10459
-
10460
- if (commonAttrs$1.includes(attrName)) {
10461
- this.inputs.forEach((input) => input.setAttribute(attr, newValue));
10462
- } else if (countryAttrs.includes(attrName)) {
10463
- this.countryCodeInput.setAttribute(attr, newValue);
10464
- } else if (phoneAttrs.includes(attrName)) {
10465
- this.phoneNumberInput.setAttribute(attr, newValue);
10466
- }
10467
- }
10468
-
10469
- if (labelTypeAttrs.includes(attrName)) {
10470
- this.handleLabelTypeAttrs(attrName, newValue);
10471
- }
10472
-
10473
- if (attrName === 'restrict-countries') {
10474
- this.updateCountryCodeItems(this.getRestrictedCountries());
10475
- }
10476
- }
10477
- }
10478
10595
  };
10479
10596
 
10480
10597
  customElements.define(componentName$P, PhoneFieldInternal$1);
@@ -10510,7 +10627,6 @@ const customMixin$a = (superclass) =>
10510
10627
  includeAttrs: [
10511
10628
  'size',
10512
10629
  'bordered',
10513
- 'invalid',
10514
10630
  'minlength',
10515
10631
  'maxlength',
10516
10632
  'default-code',
@@ -10523,6 +10639,8 @@ const customMixin$a = (superclass) =>
10523
10639
  'label',
10524
10640
  'label-type',
10525
10641
  'allow-alphanumeric-input',
10642
+ 'format-value',
10643
+ 'strict-validation',
10526
10644
  ],
10527
10645
  });
10528
10646
  }
@@ -10671,6 +10789,7 @@ const PhoneFieldClass = compose$1(
10671
10789
  },
10672
10790
  }),
10673
10791
  draggableMixin$1,
10792
+ inputOverrideValidConstraintsMixin,
10674
10793
  composedProxyInputMixin$1({ proxyProps: ['value', 'selectionStart'] }),
10675
10794
  customMixin$a
10676
10795
  )(
@@ -10783,19 +10902,22 @@ const getCountryByCodeId = (countryCode) => {
10783
10902
  return CountryCodes.find((c) => c.code === countryCode)?.dialCode;
10784
10903
  };
10785
10904
 
10905
+ const matchingParenthesis = (val) => {
10906
+ const openParenMatches = val.match(/\(/g);
10907
+ const closeParenMatches = val.match(/\)/g);
10908
+ return openParenMatches?.length === closeParenMatches?.length;
10909
+ };
10910
+
10786
10911
  const componentName$N = getComponentName$1('phone-field-internal-input-box');
10787
10912
 
10788
10913
  const observedAttributes$3 = [
10789
10914
  'disabled',
10790
10915
  'size',
10791
- 'bordered',
10792
- 'invalid',
10793
10916
  'readonly',
10794
10917
  'phone-input-placeholder',
10795
10918
  'name',
10796
10919
  'autocomplete',
10797
10920
  'label-type',
10798
- 'allow-alphanumeric-input',
10799
10921
  ];
10800
10922
  const mapAttrs = {
10801
10923
  'phone-input-placeholder': 'placeholder',
@@ -10808,77 +10930,95 @@ class PhoneFieldInternal extends BaseInputClass$7 {
10808
10930
  return [].concat(BaseInputClass$7.observedAttributes || [], observedAttributes$3);
10809
10931
  }
10810
10932
 
10933
+ #ayt;
10934
+
10811
10935
  constructor() {
10812
10936
  super();
10813
10937
 
10814
10938
  this.innerHTML = `
10815
10939
  <div>
10816
- <descope-text-field tabindex="1"></descope-text-field>
10940
+ <descope-text-field tabindex="1" type="tel" bordered="false"></descope-text-field>
10817
10941
  </div>
10818
10942
  `;
10819
10943
 
10820
- this.phoneNumberInput = this.querySelector('descope-text-field');
10944
+ this.textField = this.querySelector('descope-text-field');
10821
10945
  }
10822
10946
 
10823
- get defaultCountryCode() {
10947
+ // notice: this function is exposed in parent component
10948
+ get phoneNumberInputEle() {
10949
+ return this.textField.shadowRoot.querySelector('input');
10950
+ }
10951
+
10952
+ get defaultDialCode() {
10824
10953
  return getCountryByCodeId(this.getAttribute('default-code'));
10825
10954
  }
10826
10955
 
10827
- get hasDefaultCode() {
10828
- return !!this.getAttribute('default-code');
10956
+ get defaultCode() {
10957
+ return this.getAttribute('default-code');
10829
10958
  }
10830
10959
 
10831
10960
  get allowAlphanumericInput() {
10832
10961
  return this.getAttribute('allow-alphanumeric-input') === 'true';
10833
10962
  }
10834
10963
 
10835
- get value() {
10836
- if (!this.phoneNumberValue) {
10837
- return '';
10838
- }
10964
+ get minLength() {
10965
+ return parseInt(this.getAttribute('minlength'), 10) || 0;
10966
+ }
10839
10967
 
10840
- if (this.hasDefaultCode) {
10841
- // we want to transform phone numbers to a valid {dialCode}-{phoneNumber} format
10842
- // e.g.:
10843
- // +972-12345 => +972-12345
10844
- // 972-12345 => +972-12345
10845
- // 12345 => +972-12345
10846
- //
10847
- // we also want to handle any extra dash if added in the start of the phone number
10848
- // e.g.:
10849
- // +972--12345 => +972-12345
10850
- const pattern = new RegExp(`\\+?${parseInt(this.defaultCountryCode, 10)}--?`);
10851
- return `${this.defaultCountryCode}-${this.phoneNumberInput.value.replace(pattern, '')}`;
10852
- }
10968
+ get maxLength() {
10969
+ return parseInt(this.getAttribute('maxlength'), 10) || 50;
10970
+ }
10853
10971
 
10854
- return this.phoneNumberInput.value;
10972
+ get restrictCountries() {
10973
+ return this.getAttribute('restrict-countries')?.split(',').filter(Boolean) || [];
10855
10974
  }
10856
10975
 
10857
- set value(val) {
10858
- this.phoneNumberInput.value = val;
10976
+ get isFormatValue() {
10977
+ return this.getAttribute('format-value') === 'true';
10859
10978
  }
10860
10979
 
10861
- get phoneNumberValue() {
10862
- return this.phoneNumberInput.value;
10980
+ get isStrictValidation() {
10981
+ return this.getAttribute('strict-validation') === 'true';
10863
10982
  }
10864
10983
 
10865
- get phoneNumberInputEle() {
10866
- return this.phoneNumberInput.shadowRoot.querySelector('input');
10984
+ get value() {
10985
+ if (!this.textField.value) return '';
10986
+
10987
+ if (!this.isStrictValidation) {
10988
+ return this.#nonParsedValue();
10989
+ }
10990
+
10991
+ const parsedVal = this.#parseWithCountryCode();
10992
+
10993
+ if (parsedVal?.country && parsedVal?.countryCallingCode && parsedVal?.nationalNumber) {
10994
+ return `+${[parsedVal?.countryCallingCode, parsedVal?.nationalNumber].join('-')}`;
10995
+ }
10996
+
10997
+ // if failed to parse or to find country code return text field value
10998
+ return this.textField.value;
10867
10999
  }
10868
11000
 
10869
- get minLength() {
10870
- return parseInt(this.getAttribute('minlength'), 10) || 0;
11001
+ set value(val) {
11002
+ this.textField.value = val;
10871
11003
  }
10872
11004
 
10873
- get maxLength() {
10874
- return parseInt(this.getAttribute('maxlength'), 10) || 50;
11005
+ init() {
11006
+ this.addEventListener('focus', (e) => {
11007
+ // We want to ignore focus events we are dispatching
11008
+ if (e.isTrusted) this.textField.focus();
11009
+ });
11010
+
11011
+ super.init?.();
11012
+
11013
+ this.textField.addEventListener('input', this.#onInput.bind(this));
11014
+ this.handleFocusEventsDispatching([this.textField]);
10875
11015
  }
10876
11016
 
10877
11017
  getValidity() {
10878
11018
  const validPhonePattern = /^\+?\d{1,4}-?(?:\d-?){1,15}$/;
10879
- const stripValue = this.value.replace(/\D/g, '');
11019
+ const stripValue = this.#sanitizeVal(this.textField.value);
10880
11020
 
10881
- if (this.isRequired && !this.value) {
11021
+ if (this.isRequired && !this.textField.value) {
10882
11022
  return { valueMissing: true };
10883
11023
  }
10884
11024
 
@@ -10890,63 +11030,144 @@ class PhoneFieldInternal extends BaseInputClass$7 {
10890
11030
  return { tooLong: true };
10891
11031
  }
10892
11032
 
10893
- if (this.value && !validPhonePattern.test(this.value)) {
11033
+ if (
11034
+ // has `strict-validation` and not properly parsed
11035
+ (this.isStrictValidation && this.textField.value && !this.#isValidParsedValue()) ||
11036
+ // if no `strict-validation` then conform with naive pattern
11037
+ (!this.isStrictValidation && this.textField.value && !validPhonePattern.test(this.value))
11038
+ ) {
10894
11039
  return { patternMismatch: true };
10895
11040
  }
10896
11041
 
10897
11042
  return {};
10898
11043
  }
10899
11044
 
10900
- init() {
10901
- this.addEventListener('focus', (e) => {
10902
- // we want to ignore focus events we are dispatching
10903
- if (e.isTrusted) this.phoneNumberInput.focus();
10904
- });
11045
+ setSelectionRange(...args) {
11046
+ this.textField.setSelectionRange(...args);
11047
+ }
10905
11048
 
10906
- super.init?.();
10907
- this.initInputs();
11049
+ attributeChangedCallback(attrName, oldValue, newValue) {
11050
+ super.attributeChangedCallback(attrName, oldValue, newValue);
11051
+
11052
+ if (oldValue !== newValue && observedAttributes$3.includes(attrName)) {
11053
+ const attr = mapAttrs[attrName] || attrName;
11054
+ this.textField.setAttribute(attr, newValue);
11055
+ }
11056
+ }
11057
+
11058
+ #onInput(e) {
11059
+ let sanitizedInput = this.#sanitizeInput(e.target.value);
11060
+
11061
+ if (this.isFormatValue && this.#canFormat(sanitizedInput)) {
11062
+ sanitizedInput = this.#formatPhoneNumber(sanitizedInput);
11063
+ }
11064
+
11065
+ e.target.value = sanitizedInput;
11066
+ }
11067
+
11068
+ #nonParsedValue() {
11069
+ if (!this.defaultDialCode) {
11070
+ return this.textField.value;
11071
+ }
11072
+
11073
+ const nationalNumber = this.#trimDuplicateCountryCode(this.textField.value);
11074
+ const sanitizedVal = this.#sanitizeVal(nationalNumber);
11075
+
11076
+ return [this.defaultDialCode, sanitizedVal].join('-');
11077
+ }
11078
+
11079
+ #parseWithCountryCode() {
11080
+ if (this.defaultDialCode) {
11081
+ return parsePhoneNumberFromString$1(
11082
+ [this.defaultDialCode, this.#sanitizeVal(this.textField.value)].filter(Boolean).join('')
11083
+ );
11084
+ }
11085
+
11086
+ // if default-code or not parsed - try to extract country code from value
11087
+ return parsePhoneNumberFromString$1(this.textField.value);
11088
+ }
11089
+
11090
+ #sanitizeVal(val) {
11091
+ return val.replace(/\D/g, '');
11092
+ }
11093
+
11094
+ #trimDuplicateCountryCode(val) {
11095
+ if (this.textField.value?.[0] === '+') {
11096
+ const dialCodePrefixPattern = new RegExp(`^\\${this.defaultDialCode}`);
11097
+ const trimmed = val.replace(dialCodePrefixPattern, '');
11098
+ return trimmed;
11099
+ }
11100
+ return val;
10908
11101
  }
10909
11102
 
10910
- getCountryByDialCode(countryDialCode) {
10911
- return this.countryCodeInput.items?.find(
10912
- (c) => c.getAttribute('data-country-code') === countryDialCode
11103
+ #isValidParsedValue() {
11104
+ const parsed = parsePhoneNumberFromString$1(this.value);
11105
+ return (
11106
+ parsed && // parsed successfully (not undefined)
11107
+ parsed.isValid?.() && // Parsed object is valid
11108
+ parsed.country && // Parsed object with a country code
11109
+ this.#isAllowedCountry(parsed.country) && // Parsed with allowed country code
11110
+ (this.defaultCode ? this.defaultCode === parsed.country : true) // In case default country code is set validate parsed country matches it
10913
11111
  );
10914
11112
  }
10915
11113
 
10916
- initInputs() {
10917
- // Sanitize phone input value to filter everything but digits
10918
- this.phoneNumberInput.addEventListener('input', (e) => {
10919
- if (e.target.value.length === 1 && e.target.value === '-') {
10920
- e.target.value = '';
10921
- }
11114
+ #isAllowedCountry(countryCode) {
11115
+ if (!this.restrictCountries) {
11116
+ return true;
11117
+ }
10922
11118
 
10923
- e.target.value = e.target.value
10924
- .replace(/(?!^)\+/g, '')
10925
- .replace('--', '-')
10926
- .replace('+-', '+');
11119
+ return this.restrictCountries.includes(countryCode);
11120
+ }
10927
11121
 
10928
- let sanitizedInput = e.target.value;
10929
- if (!this.allowAlphanumericInput) {
10930
- const telDigitsRegExp = /^[+\d-]+$/;
10931
- sanitizedInput = e.target.value
10932
- .split('')
10933
- .filter((char) => telDigitsRegExp.test(char))
10934
- .join('');
10935
- }
11122
+ #sanitizeInput(val) {
11123
+ val = val
11124
+ .replace(/^-+/, '') // dash as first char
11125
+ .replace(/(?!^)\+/g, '') // multiple plus symbols
11126
+ .replace('--', '-') // consecutive dashes
11127
+ .replace('+-', '+'); // dash following plus symbol
10936
11128
 
10937
- e.target.value = sanitizedInput;
10938
- });
11129
+ if (!this.allowAlphanumericInput) {
11130
+ const telDigitsRegExp = /^[+\d-\(\)]+$/;
11131
+ val = val
11132
+ .split('')
11133
+ .filter((char) => telDigitsRegExp.test(char))
11134
+ .join('');
11135
+ }
10939
11136
 
10940
- this.handleFocusEventsDispatching([this.phoneNumberInput]);
11137
+ return val;
10941
11138
  }
10942
11139
 
10943
- attributeChangedCallback(attrName, oldValue, newValue) {
10944
- super.attributeChangedCallback(attrName, oldValue, newValue);
11140
+ #formatPhoneNumber(phoneNumber = '') {
11141
+ // Get country code from `default-code or` from phone number
11142
+ const countryCode = this.defaultCode || this.#getCountryCodeFromValue(phoneNumber);
10945
11143
 
10946
- if (oldValue !== newValue && observedAttributes$3.includes(attrName)) {
10947
- const attr = mapAttrs[attrName] || attrName;
10948
- this.phoneNumberInput.setAttribute(attr, newValue);
11144
+ // Skip formatting if no country code is available
11145
+ if (!countryCode) {
11146
+ return phoneNumber;
10949
11147
  }
11148
+
11149
+ // Update AsYouType country code if needed
11150
+ if (!this.#ayt || this.#ayt.country !== countryCode) {
11151
+ this.#ayt = new AsYouType(countryCode);
11152
+ }
11153
+
11154
+ // We need to reset AsYouType instance before setting new input
11155
+ this.#ayt.reset();
11156
+
11157
+ // Set AsYouType input
11158
+ const formattedVal = this.#ayt.input(phoneNumber) || phoneNumber;
11159
+
11160
+ return formattedVal;
11161
+ }
11162
+
11163
+ #getCountryCodeFromValue(val) {
11164
+ const parsed = parsePhoneNumberFromString$1(val);
11165
+ return parsed?.country || '';
11166
+ }
11167
+
11168
+ #canFormat(val) {
11169
+ if (!matchingParenthesis(val)) return false;
11170
+ return true;
10950
11171
  }
10951
11172
  }
10952
11173
 
@@ -10957,7 +11178,7 @@ const textVars = TextFieldClass.cssVarList;
10957
11178
  const componentName$M = getComponentName$1('phone-input-box-field');
10958
11179
 
10959
11180
  const customMixin$9 = (superclass) =>
10960
- class PhoneInputBoxFieldMixinClass extends superclass {
11181
+ class PhoneFieldInputBoxMixinClass extends superclass {
10961
11182
  static get CountryCodes() {
10962
11183
  return CountryCodes;
10963
11184
  }
@@ -10981,8 +11202,6 @@ const customMixin$9 = (superclass) =>
10981
11202
  forwardAttrs$1(this.shadowRoot.host, this.inputElement, {
10982
11203
  includeAttrs: [
10983
11204
  'size',
10984
- 'bordered',
10985
- 'invalid',
10986
11205
  'minlength',
10987
11206
  'maxlength',
10988
11207
  'default-code',
@@ -10991,6 +11210,10 @@ const customMixin$9 = (superclass) =>
10991
11210
  'label',
10992
11211
  'label-type',
10993
11212
  'allow-alphanumeric-input',
11213
+ 'restrict-countries',
11214
+ 'format-value',
11215
+ 'strict-validation',
11216
+ 'data-errormessage-type-mismatch',
10994
11217
  ],
10995
11218
  });
10996
11219
  }
@@ -11006,7 +11229,8 @@ const {
11006
11229
  inputElement: inputElement$1,
11007
11230
  requiredIndicator: requiredIndicator$3,
11008
11231
  inputField: inputField$1,
11009
- inputFieldInternal,
11232
+ internalComponent,
11233
+ internalComponentAfter,
11010
11234
  phoneInput,
11011
11235
  errorMessage: errorMessage$5,
11012
11236
  helperText: helperText$3,
@@ -11017,8 +11241,11 @@ const {
11017
11241
  inputElement: { selector: 'input' },
11018
11242
  requiredIndicator: { selector: '[required]::part(required-indicator)::after' },
11019
11243
  inputField: { selector: () => 'vaadin-text-field::part(input-field)' },
11020
- inputFieldInternal: {
11021
- selector: () => 'descope-phone-field-internal-input-box vaadin-text-field::part(input-field)',
11244
+ internalComponent: {
11245
+ selector: 'descope-phone-field-internal-input-box',
11246
+ },
11247
+ internalComponentAfter: {
11248
+ selector: 'descope-phone-field-internal-input-box::after',
11022
11249
  },
11023
11250
  phoneInput: { selector: () => 'descope-text-field' },
11024
11251
  helperText: { selector: '::part(helper-text)' },
@@ -11041,14 +11268,6 @@ const PhoneFieldInputBoxClass = compose$1(
11041
11268
  hostMinWidth: { ...host$f, property: 'min-width' },
11042
11269
  hostDirection: { ...host$f, property: 'direction' },
11043
11270
 
11044
- inputBorderStyle: { ...inputFieldInternal, property: 'border-style' },
11045
- inputBorderWidth: { ...inputFieldInternal, property: 'border-width' },
11046
- inputBorderColor: { ...inputFieldInternal, property: 'border-color' },
11047
- inputBorderRadius: [
11048
- { ...inputField$1, property: 'border-radius' },
11049
- { ...inputFieldInternal, property: 'border-radius' },
11050
- ],
11051
-
11052
11271
  inputHorizontalPadding: [
11053
11272
  { ...phoneInput, property: 'padding-left' },
11054
11273
  { ...phoneInput, property: 'padding-right' },
@@ -11073,10 +11292,18 @@ const PhoneFieldInputBoxClass = compose$1(
11073
11292
 
11074
11293
  inputPlaceholderTextColor: { ...phoneInput, property: textVars.inputPlaceholderColor },
11075
11294
 
11076
- inputOutlineStyle: { ...inputField$1, property: 'outline-style' },
11077
- inputOutlineColor: { ...inputField$1, property: 'outline-color' },
11078
- inputOutlineWidth: { ...inputField$1, property: 'outline-width' },
11079
- inputOutlineOffset: { ...inputField$1, property: 'outline-offset' },
11295
+ inputBorderStyle: { ...internalComponentAfter, property: 'border-style' },
11296
+ inputBorderWidth: { ...internalComponentAfter, property: 'border-width' },
11297
+ inputBorderColor: { ...internalComponentAfter, property: 'border-color' },
11298
+ inputBorderRadius: [
11299
+ { ...internalComponent, property: 'border-radius' },
11300
+ { ...internalComponentAfter, property: 'border-radius' },
11301
+ ],
11302
+
11303
+ inputOutlineStyle: { ...internalComponent, property: 'outline-style' },
11304
+ inputOutlineColor: { ...internalComponent, property: 'outline-color' },
11305
+ inputOutlineWidth: { ...internalComponent, property: 'outline-width' },
11306
+ inputOutlineOffset: { ...internalComponent, property: 'outline-offset' },
11080
11307
 
11081
11308
  labelPosition: { ...label$3, property: 'position' },
11082
11309
  labelTopPosition: { ...label$3, property: 'top' },
@@ -11095,6 +11322,7 @@ const PhoneFieldInputBoxClass = compose$1(
11095
11322
  },
11096
11323
  }),
11097
11324
  draggableMixin$1,
11325
+ inputOverrideValidConstraintsMixin,
11098
11326
  composedProxyInputMixin$1({ proxyProps: ['value', 'selectionStart'] }),
11099
11327
  customMixin$9
11100
11328
  )(
@@ -11126,7 +11354,6 @@ const PhoneFieldInputBoxClass = compose$1(
11126
11354
  vaadin-text-field::part(input-field) {
11127
11355
  padding: 0;
11128
11356
  background: transparent;
11129
- overflow: hidden;
11130
11357
  -webkit-mask-image: none;
11131
11358
  }
11132
11359
  descope-phone-field-internal-input-box {
@@ -11137,14 +11364,20 @@ const PhoneFieldInputBoxClass = compose$1(
11137
11364
  descope-phone-field-internal-input-box > div {
11138
11365
  width: 100%;
11139
11366
  }
11140
- descope-phone-field-internal-input-box .separator {
11141
- flex: 0;
11142
- border: none;
11143
- }
11144
11367
  descope-phone-field-internal-input-box descope-text-field {
11145
11368
  ${textVars.inputOutlineWidth}: 0;
11146
11369
  ${textVars.inputOutlineOffset}: 0;
11147
11370
  }
11371
+ descope-phone-field-internal-input-box::after {
11372
+ content: '';
11373
+ position: absolute;
11374
+ width: 100%;
11375
+ height: 100%;
11376
+ top: 0;
11377
+ left: 0;
11378
+ box-sizing: border-box;
11379
+ pointer-events: none;
11380
+ }
11148
11381
  descope-text-field {
11149
11382
  flex-grow: 1;
11150
11383
  width: 100%;
@@ -17714,6 +17947,8 @@ const attrs = {
17714
17947
  'restrict-countries',
17715
17948
  'default-code',
17716
17949
  'phone-minlength',
17950
+ 'phone-format-value',
17951
+ 'phone-strict-validation',
17717
17952
  'data-errormessage-value-missing-phone',
17718
17953
  ],
17719
17954
  inputBox: [
@@ -17721,6 +17956,8 @@ const attrs = {
17721
17956
  'restrict-countries',
17722
17957
  'default-code',
17723
17958
  'phone-minlength',
17959
+ 'phone-format-value',
17960
+ 'phone-strict-validation',
17724
17961
  'data-errormessage-value-missing-phone',
17725
17962
  ],
17726
17963
  },
@@ -17734,6 +17971,8 @@ const attrMap = {
17734
17971
  phone: {
17735
17972
  'phone-input-label': 'label',
17736
17973
  'phone-minlength': 'minlength',
17974
+ 'phone-format-value': 'format-value',
17975
+ 'phone-strict-validation': 'strict-validation',
17737
17976
  'data-errormessage-value-missing-phone': 'data-errormessage-value-missing',
17738
17977
  },
17739
17978
  };
@@ -22327,7 +22566,7 @@ var addressField$1 = /*#__PURE__*/Object.freeze({
22327
22566
  vars: vars$3
22328
22567
  });
22329
22568
 
22330
- var clockIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIiIGhlaWdodD0iMTIiIHZpZXdCb3g9IjAgMCAxMiAxMiIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8cGF0aCBkPSJNNiAwQzIuNyAwIDAgMi43IDAgNkMwIDkuMyAyLjcgMTIgNiAxMkM5LjMgMTIgMTIgOS4zIDEyIDZDMTIgMi43IDkuMyAwIDYgMFpNNiAxMC41QzMuNTI1IDEwLjUgMS41IDguNDc1IDEuNSA2QzEuNSAzLjUyNSAzLjUyNSAxLjUgNiAxLjVDOC40NzUgMS41IDEwLjUgMy41MjUgMTAuNSA2QzEwLjUgOC40NzUgOC40NzUgMTAuNSA2IDEwLjVaIiBmaWxsPSIjMTgxQTFDIi8+CiAgPHBhdGggZD0iTTYgMi4yNUg1LjI1VjYuNzVIOVY2SDZWMi4yNVoiIGZpbGw9IiMxODFBMUMiLz4KPC9zdmc+Cg==";
22569
+ var clockIcon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMWVtIiBoZWlnaHQ9IjFlbSIgdmlld0JveD0iMCAwIDEwMSAxMDEiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik01MC4zMzM0IDAuMzMzMjUyQzIyLjgzMzQgMC4zMzMyNTIgMC4zMzMzNzQgMjIuODMzMyAwLjMzMzM3NCA1MC4zMzMzQzAuMzMzMzc0IDc3LjgzMzMgMjIuODMzNCAxMDAuMzMzIDUwLjMzMzQgMTAwLjMzM0M3Ny44MzM0IDEwMC4zMzMgMTAwLjMzMyA3Ny44MzMzIDEwMC4zMzMgNTAuMzMzM0MxMDAuMzMzIDIyLjgzMzMgNzcuODMzNCAwLjMzMzI1MiA1MC4zMzM0IDAuMzMzMjUyWk01MC4zMzM0IDg3LjgzMzNDMjkuNzA4NCA4Ny44MzMzIDEyLjgzMzQgNzAuOTU4MyAxMi44MzM0IDUwLjMzMzNDMTIuODMzNCAyOS43MDgzIDI5LjcwODQgMTIuODMzMyA1MC4zMzM0IDEyLjgzMzNDNzAuOTU4NCAxMi44MzMzIDg3LjgzMzQgMjkuNzA4MyA4Ny44MzM0IDUwLjMzMzNDODcuODMzNCA3MC45NTgzIDcwLjk1ODQgODcuODMzMyA1MC4zMzM0IDg3LjgzMzNaIiBmaWxsPSIjMTgxQTFDIi8+CjxwYXRoIGQ9Ik01MC4zMzI4IDE5LjA4MzNINDQuMDgyOFY1Ni41ODMySDc1LjMzMjhWNTAuMzMzMkg1MC4zMzI4VjE5LjA4MzNaIiBmaWxsPSIjMTgxQTFDIi8+Cjwvc3ZnPgo=";
22331
22570
 
22332
22571
  const fmt = (val, pad = 2) => String(val).padStart(pad, '0');
22333
22572
 
@@ -22348,7 +22587,7 @@ const formatTime = (ms = 0) => {
22348
22587
 
22349
22588
  const componentName$1 = getComponentName('timer');
22350
22589
 
22351
- const observedAttributes = ['seconds', 'hide-icon'];
22590
+ const observedAttributes = ['seconds', 'hide-icon', 'paused'];
22352
22591
 
22353
22592
  const BaseClass$1 = createBaseClass({
22354
22593
  componentName: componentName$1,
@@ -22406,57 +22645,63 @@ class RawTimer extends BaseClass$1 {
22406
22645
  return Math.max(0, secs);
22407
22646
  }
22408
22647
 
22648
+ // we use `pause` attribute for allowing preview mode by executing `pause`
22649
+ get isPaused() {
22650
+ return this.getAttribute('paused') === 'true';
22651
+ }
22652
+
22409
22653
  init() {
22410
22654
  super.init?.();
22411
22655
 
22412
22656
  this.#timeRemains = this.seconds;
22413
22657
  }
22414
22658
 
22415
- start() {
22416
- this.dispatchEvent(new CustomEvent('timer-started', { bubbles: true }));
22417
-
22418
- this.startInterval();
22419
- }
22420
-
22421
- startInterval() {
22659
+ startInterval() {
22422
22660
  const intervalCycle = () => {
22661
+ this.#decreaseInterval();
22662
+ if (!this.#timeRemains) this.stop();
22423
22663
  this.#updateDisplay(this.#timeRemains);
22424
-
22425
- if (!this.#timeRemains) {
22426
- this.stop();
22427
- } else {
22428
- this.#decreaseInterval();
22429
- }
22430
22664
  };
22431
22665
 
22432
- intervalCycle();
22433
-
22434
22666
  if (this.#timeRemains) {
22435
22667
  this.#intervalId = setInterval(intervalCycle, DEFAULT_INTERVAL);
22668
+ } else {
22669
+ this.stop();
22436
22670
  }
22437
22671
  }
22438
22672
 
22673
+ stopInterval() {
22674
+ clearInterval(this.#intervalId);
22675
+ }
22676
+
22439
22677
  #decreaseInterval() {
22440
- this.#timeRemains = Math.max(this.#timeRemains - DEFAULT_INTERVAL, 0);
22678
+ this.#timeRemains = this.#timeRemains - DEFAULT_INTERVAL;
22441
22679
  }
22442
22680
 
22443
22681
  reset() {
22444
22682
  this.#timeRemains = this.seconds;
22445
- this.start();
22683
+ this.#updateDisplay(this.#timeRemains);
22684
+
22685
+ if (this.isPaused) return;
22686
+
22687
+ this.dispatchEvent(new CustomEvent('timer-started', { bubbles: true }));
22688
+ this.startInterval();
22446
22689
  }
22447
22690
 
22448
22691
  stop() {
22449
- this.pause();
22692
+ this.stopInterval();
22450
22693
  this.#timeRemains = 0;
22451
22694
  this.#updateDisplay(this.#timeRemains);
22452
22695
  this.dispatchEvent(new CustomEvent('timer-ended', { bubbles: true }));
22453
22696
  }
22454
22697
 
22455
22698
  pause() {
22456
- clearInterval(this.#intervalId);
22699
+ this.setAttribute('paused', 'true');
22700
+ this.stopInterval();
22457
22701
  }
22458
22702
 
22459
22703
  resume() {
22704
+ this.removeAttribute('paused');
22460
22705
  this.startInterval();
22461
22706
  }
22462
22707
 
@@ -22468,6 +22713,19 @@ class RawTimer extends BaseClass$1 {
22468
22713
  this.icon.classList.toggle('hidden', val === 'true');
22469
22714
  }
22470
22715
 
22716
+ #handlePause(paused) {
22717
+ setTimeout(() => {
22718
+ if (paused) {
22719
+ this.pause();
22720
+ } else {
22721
+ // we want to prevent a another interval from starting in case previous interval was not yet cleared
22722
+ if (!this.#intervalId) {
22723
+ this.resume();
22724
+ }
22725
+ }
22726
+ });
22727
+ }
22728
+
22471
22729
  attributeChangedCallback(attrName, oldValue, newValue) {
22472
22730
  super.attributeChangedCallback?.(attrName, oldValue, newValue);
22473
22731
 
@@ -22478,28 +22736,31 @@ class RawTimer extends BaseClass$1 {
22478
22736
  if (attrName === 'hide-icon') {
22479
22737
  this.#toggleIcon(newValue);
22480
22738
  }
22739
+ if (attrName === 'paused') {
22740
+ this.#handlePause(newValue === 'true');
22741
+ }
22481
22742
  }
22482
22743
  }
22483
22744
  }
22484
22745
 
22485
22746
  const { host: host$1, icon, timer: timer$2 } = {
22486
22747
  host: { selector: () => ':host' },
22487
- icon: { selector: '.icon' },
22488
- timer: { selector: '.timer' },
22748
+ icon: { selector: () => '.icon' },
22749
+ timer: { selector: () => '.timer' },
22489
22750
  };
22490
22751
 
22491
22752
  const TimerClass = compose(
22492
22753
  createStyleMixin({
22493
22754
  mappings: {
22494
- backgroundColor: {},
22495
- fontSize: [{}, { ...icon }],
22755
+ fontSize: {},
22496
22756
  iconSize: [
22497
22757
  { ...icon, property: 'width' },
22498
22758
  { ...icon, property: 'height' },
22499
22759
  ],
22500
22760
  fontFamily: {},
22761
+ minHeight: {},
22501
22762
  fontWeight: { ...timer$2 },
22502
- lineHeight: { property: 'min-height' },
22763
+ lineHeight: { ...timer$2 },
22503
22764
  textColor: { ...timer$2, property: 'color' },
22504
22765
  gap: {},
22505
22766
  textAlign: { property: 'justify-content' },
@@ -22519,7 +22780,8 @@ const timer = {
22519
22780
  [vars$2.hostDirection]: globalRefs$1.direction,
22520
22781
  [vars$2.gap]: '0.25em',
22521
22782
  [vars$2.fontFamily]: globalRefs$1.fonts.font1.family,
22522
- [vars$2.lineHeight]: '2em',
22783
+ [vars$2.minHeight]: '1.5em',
22784
+ [vars$2.lineHeight]: '1em',
22523
22785
  [vars$2.fontWeight]: globalRefs$1.fonts.font1.fontWeight,
22524
22786
  [vars$2.textColor]: globalRefs$1.colors.surface.contrast,
22525
22787
  [vars$2.iconColor]: globalRefs$1.colors.surface.contrast,
@@ -22567,6 +22829,7 @@ const mapButtonAttrs = {
22567
22829
  const timerAttrs = [
22568
22830
  'timer-seconds',
22569
22831
  'timer-hide-icon',
22832
+ 'timer-paused',
22570
22833
  'size',
22571
22834
  'text-align',
22572
22835
  'full-width',
@@ -22575,6 +22838,7 @@ const timerAttrs = [
22575
22838
  const mapTimerAttrs = {
22576
22839
  'timer-seconds': 'seconds',
22577
22840
  'timer-hide-icon': 'hide-icon',
22841
+ 'timer-paused': 'paused',
22578
22842
  };
22579
22843
 
22580
22844
  const BaseClass = createBaseClass({
@@ -22589,7 +22853,7 @@ class RawTimerButton extends BaseClass {
22589
22853
  this.attachShadow({ mode: 'open' }).innerHTML = `
22590
22854
  <div class="wrapper">
22591
22855
  <descope-timer class="timer"></descope-timer>
22592
- <descope-button class="button">
22856
+ <descope-button class="button" disabled="true">
22593
22857
  <slot></slot>
22594
22858
  </descope-button>
22595
22859
  </div>
@@ -22635,6 +22899,14 @@ class RawTimerButton extends BaseClass {
22635
22899
  includeAttrs: timerAttrs,
22636
22900
  mapAttrs: mapTimerAttrs,
22637
22901
  });
22902
+
22903
+ // When we sync attributes, the `disabled` attribute is being compared to the attribute
22904
+ // on Vaadin component; since the Vaadin component has no `disabled` attribute, we sync
22905
+ // that back to our component.
22906
+ // This is pending a more generic fix in the way we sync attibutes.
22907
+ this.button.shadowRoot
22908
+ .querySelector('vaadin-button')
22909
+ .setAttribute('disabled', 'true');
22638
22910
  }
22639
22911
 
22640
22912
  onTimerStarted() {
@@ -22650,9 +22922,11 @@ class RawTimerButton extends BaseClass {
22650
22922
  }
22651
22923
 
22652
22924
  toggleButtonDisable(isDisabled) {
22653
- isDisabled
22654
- ? this.button.setAttribute('disabled', 'true')
22655
- : this.button.removeAttribute('disabled');
22925
+ setTimeout(() => {
22926
+ isDisabled
22927
+ ? this.button.setAttribute('disabled', 'true')
22928
+ : this.button.removeAttribute('disabled');
22929
+ });
22656
22930
  }
22657
22931
  }
22658
22932