intl-tel-input 27.0.17 → 27.1.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/CHANGELOG.md CHANGED
@@ -1,6 +1,6 @@
1
1
  See the Github Releases page for changelog: https://github.com/jackocnr/intl-tel-input/releases
2
2
 
3
- Or to view a specific version, e.g. v27.0.17, update the URL accordingly, e.g. https://github.com/jackocnr/intl-tel-input/releases/tag/v27.0.17
3
+ Or to view a specific version, e.g. v27.1.1, update the URL accordingly, e.g. https://github.com/jackocnr/intl-tel-input/releases/tag/v27.1.1
4
4
 
5
5
  ## Breaking changes
6
6
 
@@ -58,7 +58,6 @@ declare class IntlTelInput implements AfterViewInit, OnDestroy, OnChanges, Contr
58
58
  private lastEmittedValidity?;
59
59
  private lastEmittedErrorCode?;
60
60
  private pendingWriteValue?;
61
- private countryChangeHandler;
62
61
  private onChange;
63
62
  private onTouched;
64
63
  private onValidatorChange;
@@ -1758,8 +1758,9 @@ var EVENTS = {
1758
1758
  OPEN_COUNTRY_DROPDOWN: "open:countrydropdown",
1759
1759
  CLOSE_COUNTRY_DROPDOWN: "close:countrydropdown",
1760
1760
  COUNTRY_CHANGE: "countrychange",
1761
- INPUT: "input"
1761
+ INPUT: "input",
1762
1762
  // used for synthetic input trigger
1763
+ STRICT_REJECT: "strict:reject"
1763
1764
  };
1764
1765
  var CLASSES = {
1765
1766
  HIDE: "iti__hide",
@@ -2355,6 +2356,7 @@ var UI = class _UI {
2355
2356
  wrapper.appendChild(this.telInputEl);
2356
2357
  this.#updateInputPaddingAndReveal();
2357
2358
  this.#buildHiddenInputs(wrapper);
2359
+ this.ensureDropdownWidthSet();
2358
2360
  }
2359
2361
  #createWrapperAndInsert() {
2360
2362
  const { allowDropdown, showFlags, containerClass, useFullscreenPopup } = this.#options;
@@ -2712,8 +2714,11 @@ var UI = class _UI {
2712
2714
  }
2713
2715
  //* Pre-fill the search input with "+" and show all countries
2714
2716
  //* (used when user types "+" in the phone input to open the dropdown).
2717
+ //* Explicitly focus the search input (openDropdown skips this when
2718
+ //* dropdownAlwaysOpen, but here we need focus to redirect subsequent keystrokes).
2715
2719
  prefillSearchWithPlus() {
2716
2720
  this.#searchInputEl.value = "+";
2721
+ this.#searchInputEl.focus();
2717
2722
  this.#filterCountriesByQuery("");
2718
2723
  }
2719
2724
  // Search input handlers
@@ -2753,24 +2758,23 @@ var UI = class _UI {
2753
2758
  }
2754
2759
  }
2755
2760
  //* Remove highlighting from the previous list item and highlight the new one.
2756
- #highlightListItem(listItem, shouldFocus) {
2757
- const prevItem = this.#highlightedListItemEl;
2758
- if (prevItem) {
2759
- prevItem.classList.remove(CLASSES.HIGHLIGHT);
2760
- }
2761
- this.#highlightedListItemEl = listItem;
2762
- if (this.#highlightedListItemEl) {
2763
- this.#highlightedListItemEl.classList.add(CLASSES.HIGHLIGHT);
2761
+ #highlightListItem(listItem, doScroll = true) {
2762
+ this.#highlightedListItemEl?.classList.remove(CLASSES.HIGHLIGHT);
2763
+ if (listItem) {
2764
+ listItem.classList.add(CLASSES.HIGHLIGHT);
2764
2765
  if (this.#options.countrySearch) {
2765
- const activeDescendant = this.#highlightedListItemEl.getAttribute("id") || "";
2766
+ const activeDescendant = listItem.getAttribute("id") || "";
2766
2767
  this.#searchInputEl.setAttribute(
2767
2768
  ARIA.ACTIVE_DESCENDANT,
2768
2769
  activeDescendant
2769
2770
  );
2770
2771
  }
2771
- if (shouldFocus) {
2772
- this.#highlightedListItemEl.focus();
2772
+ if (doScroll) {
2773
+ this.#scrollCountryListToItem(listItem);
2773
2774
  }
2775
+ this.#highlightedListItemEl = listItem;
2776
+ } else {
2777
+ this.#highlightedListItemEl = null;
2774
2778
  }
2775
2779
  }
2776
2780
  //* Bind a form-submit listener that syncs the hidden inputs with the current phone number
@@ -2861,8 +2865,7 @@ var UI = class _UI {
2861
2865
  this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "true");
2862
2866
  const itemToHighlight = this.#selectedListItemEl ?? this.#countryListEl.firstElementChild;
2863
2867
  if (itemToHighlight) {
2864
- this.#highlightListItem(itemToHighlight, false);
2865
- this.#scrollCountryListToItem(itemToHighlight);
2868
+ this.#highlightListItem(itemToHighlight);
2866
2869
  }
2867
2870
  if (countrySearch && !dropdownAlwaysOpen) {
2868
2871
  this.#searchInputEl.focus();
@@ -3003,8 +3006,7 @@ var UI = class _UI {
3003
3006
  const match = findFirstCountryStartingWith(this.#countries, query);
3004
3007
  if (match) {
3005
3008
  const listItem = this.#listItemByIso2.get(match.iso2);
3006
- this.#highlightListItem(listItem, false);
3007
- this.#scrollCountryListToItem(listItem);
3009
+ this.#highlightListItem(listItem);
3008
3010
  }
3009
3011
  }
3010
3012
  //* Highlight the next/prev item in the list (and ensure it is visible).
@@ -3014,8 +3016,7 @@ var UI = class _UI {
3014
3016
  next = key === KEYS.ARROW_UP ? this.#countryListEl.lastElementChild : this.#countryListEl.firstElementChild;
3015
3017
  }
3016
3018
  if (next) {
3017
- this.#scrollCountryListToItem(next);
3018
- this.#highlightListItem(next, false);
3019
+ this.#highlightListItem(next);
3019
3020
  }
3020
3021
  }
3021
3022
  // Update the selected list item in the dropdown
@@ -3038,6 +3039,9 @@ var UI = class _UI {
3038
3039
  );
3039
3040
  checkIcon.innerHTML = buildCheckIcon();
3040
3041
  this.#selectedListItemEl = newListItem;
3042
+ if (this.#options.dropdownAlwaysOpen) {
3043
+ this.#highlightListItem(newListItem);
3044
+ }
3041
3045
  }
3042
3046
  }
3043
3047
  }
@@ -3056,7 +3060,7 @@ var UI = class _UI {
3056
3060
  }
3057
3061
  }
3058
3062
  if (noCountriesAddedYet) {
3059
- this.#highlightListItem(null, false);
3063
+ this.#highlightListItem(null);
3060
3064
  if (this.#noResultsMessageEl) {
3061
3065
  this.#noResultsMessageEl.classList.remove(CLASSES.HIDE);
3062
3066
  }
@@ -3143,6 +3147,9 @@ var UI = class _UI {
3143
3147
  setLoading(isLoading) {
3144
3148
  this.#selectedFlagEl.classList.toggle(CLASSES.LOADING, isLoading);
3145
3149
  }
3150
+ isLoading() {
3151
+ return this.#selectedFlagEl.classList.contains(CLASSES.LOADING);
3152
+ }
3146
3153
  // Set the disabled state of the input and dropdown.
3147
3154
  setDisabled(disabled) {
3148
3155
  this.telInputEl.disabled = disabled;
@@ -3165,10 +3172,6 @@ var UI = class _UI {
3165
3172
  }
3166
3173
  }
3167
3174
  }
3168
- // Whether the selected flag is currently showing the globe icon (no country selected).
3169
- isShowingGlobe() {
3170
- return this.#selectedFlagEl.classList.contains(CLASSES.GLOBE);
3171
- }
3172
3175
  setCountry(selectedCountryData) {
3173
3176
  const { allowDropdown, showFlags, separateDialCode, i18n } = this.#options;
3174
3177
  const name = selectedCountryData?.name;
@@ -3530,7 +3533,6 @@ var Iti = class _Iti {
3530
3533
  this.#processCountryData();
3531
3534
  this.#ui.buildMarkup(this.#countries);
3532
3535
  this.#setInitialState();
3533
- this.#ui.ensureDropdownWidthSet();
3534
3536
  this.#initListeners();
3535
3537
  this.#startAsyncLoads();
3536
3538
  if (this.#options.dropdownAlwaysOpen) {
@@ -3559,22 +3561,23 @@ var Iti = class _Iti {
3559
3561
  const isRegionlessNanpNumber = isRegionlessNanp(value);
3560
3562
  const { initialCountry, geoIpLookup } = this.#options;
3561
3563
  const isAutoCountry = initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;
3562
- const doingAutoCountryLookup = isAutoCountry && !overrideAutoCountry;
3563
- const isValidInitialCountry = isIso2(initialCountry);
3564
+ const resolvedInitialCountry = isAutoCountry && intlTelInput.autoCountry ? intlTelInput.autoCountry : initialCountry;
3565
+ const doingAutoCountryLookup = isAutoCountry && !overrideAutoCountry && !intlTelInput.autoCountry;
3566
+ const isValidInitialCountry = isIso2(resolvedInitialCountry);
3564
3567
  if (dialCode) {
3565
3568
  if (isRegionlessNanpNumber) {
3566
3569
  if (isValidInitialCountry) {
3567
- this.#setCountry(initialCountry);
3570
+ this.#updateSelectedCountry(resolvedInitialCountry);
3568
3571
  } else if (!doingAutoCountryLookup) {
3569
- this.#setCountry(US.ISO2);
3572
+ this.#updateSelectedCountry(US.ISO2);
3570
3573
  }
3571
3574
  } else {
3572
3575
  this.#updateCountryFromNumber(value);
3573
3576
  }
3574
3577
  } else if (isValidInitialCountry) {
3575
- this.#setCountry(initialCountry);
3578
+ this.#updateSelectedCountry(resolvedInitialCountry);
3576
3579
  } else if (!doingAutoCountryLookup) {
3577
- this.#setCountry("");
3580
+ this.#updateSelectedCountry("");
3578
3581
  }
3579
3582
  if (value) {
3580
3583
  this.#updateValueFromNumber(value);
@@ -3633,7 +3636,6 @@ var Iti = class _Iti {
3633
3636
  intlTelInput.startedLoadingAutoCountry = true;
3634
3637
  if (typeof this.#options.geoIpLookup === "function") {
3635
3638
  const successCallback = (iso2 = "") => {
3636
- this.#ui.setLoading(false);
3637
3639
  const iso2Lower = iso2.toLowerCase();
3638
3640
  if (isIso2(iso2Lower)) {
3639
3641
  intlTelInput.autoCountry = iso2Lower;
@@ -3643,7 +3645,6 @@ var Iti = class _Iti {
3643
3645
  }
3644
3646
  };
3645
3647
  const failureCallback = () => {
3646
- this.#ui.setLoading(false);
3647
3648
  _Iti.forEachInstance("handleAutoCountryFailure");
3648
3649
  };
3649
3650
  this.#options.geoIpLookup(successCallback, failureCallback);
@@ -3653,6 +3654,14 @@ var Iti = class _Iti {
3653
3654
  this.#openDropdown();
3654
3655
  this.#ui.prefillSearchWithPlus();
3655
3656
  }
3657
+ //* Delete the character just typed (the one immediately before the caret). Used by Android workarounds where we can't preventDefault on keydown.
3658
+ #removeJustTypedChar(inputValue) {
3659
+ const currentCaretPos = this.#ui.telInputEl.selectionStart || 0;
3660
+ const valueBeforeCaret = inputValue.substring(0, currentCaretPos - 1);
3661
+ const valueAfterCaret = inputValue.substring(currentCaretPos);
3662
+ this.#setTelInputValue(valueBeforeCaret + valueAfterCaret);
3663
+ return currentCaretPos - 1;
3664
+ }
3656
3665
  //* Initialize the tel input listeners.
3657
3666
  #bindAllTelInputListeners() {
3658
3667
  this.#bindInputListener();
@@ -3672,15 +3681,25 @@ var Iti = class _Iti {
3672
3681
  userOverrideFormatting = true;
3673
3682
  }
3674
3683
  const handleInputEvent = (e) => {
3684
+ if (e?.detail && e.detail["isCountryChange"]) {
3685
+ return;
3686
+ }
3675
3687
  const inputValue = this.#getTelInputValue();
3676
3688
  if (this.#isAndroid && e?.data === "+" && separateDialCode && allowDropdown && countrySearch) {
3677
- const currentCaretPos = this.#ui.telInputEl.selectionStart || 0;
3678
- const valueBeforeCaret = inputValue.substring(0, currentCaretPos - 1);
3679
- const valueAfterCaret = inputValue.substring(currentCaretPos);
3680
- this.#setTelInputValue(valueBeforeCaret + valueAfterCaret);
3689
+ this.#removeJustTypedChar(inputValue);
3681
3690
  this.#openDropdownWithPlus();
3682
3691
  return;
3683
3692
  }
3693
+ if (this.#isAndroid && strictMode && (e?.data === " " || e?.data === "-" || e?.data === ".")) {
3694
+ const newCaretPos = this.#removeJustTypedChar(inputValue);
3695
+ this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
3696
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3697
+ source: "key",
3698
+ rejectedInput: e.data,
3699
+ reason: "invalid"
3700
+ });
3701
+ return;
3702
+ }
3684
3703
  if (this.#updateCountryFromNumber(inputValue)) {
3685
3704
  this.#dispatchCountryChangeEvent();
3686
3705
  }
@@ -3777,6 +3796,11 @@ var Iti = class _Iti {
3777
3796
  const newCountry = this.#resolveCountryChangeFromNumber(newFullNumber);
3778
3797
  const isChangingDialCode = newCountry !== null;
3779
3798
  if (!isAllowedChar || hasExceededMaxLength && !isChangingDialCode && !isInitialPlus) {
3799
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3800
+ source: "key",
3801
+ rejectedInput: e.key,
3802
+ reason: !isAllowedChar ? "invalid" : "max-length"
3803
+ });
3780
3804
  e.preventDefault();
3781
3805
  }
3782
3806
  };
@@ -3806,6 +3830,7 @@ var Iti = class _Iti {
3806
3830
  const numerics = allowedChars.replace(/\+/g, "");
3807
3831
  const sanitised = hasLeadingPlus && allowLeadingPlus ? `+${numerics}` : numerics;
3808
3832
  let newValue = before + sanitised + after;
3833
+ let rejectReason = sanitised !== pasted ? "invalid" : null;
3809
3834
  if (newValue.length > 5 && intlTelInput.utils) {
3810
3835
  let coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
3811
3836
  while (coreNumber.length === 0 && newValue.length > 0) {
@@ -3813,13 +3838,24 @@ var Iti = class _Iti {
3813
3838
  coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
3814
3839
  }
3815
3840
  if (!coreNumber) {
3841
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3842
+ source: "paste",
3843
+ rejectedInput: pastedRaw,
3844
+ reason: "max-length"
3845
+ });
3816
3846
  return;
3817
3847
  }
3818
3848
  if (this.#maxCoreNumberLength && coreNumber.length > this.#maxCoreNumberLength) {
3819
3849
  if (input.selectionEnd === inputValue.length) {
3820
3850
  const trimLength = coreNumber.length - this.#maxCoreNumberLength;
3821
3851
  newValue = newValue.slice(0, newValue.length - trimLength);
3852
+ rejectReason = "max-length";
3822
3853
  } else {
3854
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3855
+ source: "paste",
3856
+ rejectedInput: pastedRaw,
3857
+ reason: "max-length"
3858
+ });
3823
3859
  return;
3824
3860
  }
3825
3861
  }
@@ -3828,6 +3864,13 @@ var Iti = class _Iti {
3828
3864
  const caretPos = selStart + sanitised.length;
3829
3865
  input.setSelectionRange(caretPos, caretPos);
3830
3866
  input.dispatchEvent(new InputEvent("input", { bubbles: true }));
3867
+ if (rejectReason) {
3868
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3869
+ source: "paste",
3870
+ rejectedInput: pastedRaw,
3871
+ reason: rejectReason
3872
+ });
3873
+ }
3831
3874
  };
3832
3875
  this.#ui.telInputEl.addEventListener("paste", handlePasteEvent, {
3833
3876
  signal: this.#abortController.signal
@@ -3879,7 +3922,7 @@ var Iti = class _Iti {
3879
3922
  #updateCountryFromNumber(fullNumber) {
3880
3923
  const iso2 = this.#resolveCountryChangeFromNumber(fullNumber);
3881
3924
  if (iso2 !== null) {
3882
- return this.#setCountry(iso2);
3925
+ return this.#updateSelectedCountry(iso2);
3883
3926
  }
3884
3927
  return false;
3885
3928
  }
@@ -3962,7 +4005,7 @@ var Iti = class _Iti {
3962
4005
  }
3963
4006
  //* Update the selected country, dial code (if separateDialCode), placeholder, title, and selected list item.
3964
4007
  //* Note: called from setInitialState, updateCountryFromNumber, selectListItem, setCountry.
3965
- #setCountry(iso2) {
4008
+ #updateSelectedCountry(iso2) {
3966
4009
  const prevIso2 = this.#selectedCountry?.iso2 || "";
3967
4010
  this.#selectedCountry = iso2 ? this.#countryByIso2.get(iso2) : null;
3968
4011
  if (this.#selectedCountry) {
@@ -4036,7 +4079,7 @@ var Iti = class _Iti {
4036
4079
  return;
4037
4080
  }
4038
4081
  const iso2 = listItem.dataset[DATA_KEYS.ISO2];
4039
- const countryChanged = this.#setCountry(iso2);
4082
+ const countryChanged = this.#updateSelectedCountry(iso2);
4040
4083
  this.#closeDropdown();
4041
4084
  const dialCode = listItem.dataset[DATA_KEYS.DIAL_CODE];
4042
4085
  this.#updateDialCode(dialCode);
@@ -4047,6 +4090,7 @@ var Iti = class _Iti {
4047
4090
  this.#ui.telInputEl.focus();
4048
4091
  if (countryChanged) {
4049
4092
  this.#dispatchCountryChangeEvent();
4093
+ this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
4050
4094
  }
4051
4095
  }
4052
4096
  //* Close the dropdown and unbind any listeners.
@@ -4157,11 +4201,12 @@ var Iti = class _Iti {
4157
4201
  this.#autoCountryDeferred.resolve();
4158
4202
  return;
4159
4203
  }
4160
- this.#fallbackCountryIso2 = intlTelInput.autoCountry;
4161
- const hasSelectedCountryOrGlobe = this.#selectedCountry || this.#ui.isShowingGlobe();
4162
- if (!hasSelectedCountryOrGlobe) {
4163
- this.setCountry(this.#fallbackCountryIso2);
4204
+ if (this.#ui.isLoading()) {
4205
+ this.setCountry(intlTelInput.autoCountry);
4206
+ } else {
4207
+ this.#fallbackCountryIso2 = intlTelInput.autoCountry;
4164
4208
  }
4209
+ this.#ui.setLoading(false);
4165
4210
  this.#autoCountryDeferred.resolve();
4166
4211
  }
4167
4212
  //* Called when the geoip call fails or times out.
@@ -4171,6 +4216,7 @@ var Iti = class _Iti {
4171
4216
  return;
4172
4217
  }
4173
4218
  this.#setInitialState(true);
4219
+ this.#ui.setLoading(false);
4174
4220
  this.#autoCountryDeferred?.reject();
4175
4221
  }
4176
4222
  //* Called when the utils request completes.
@@ -4340,13 +4386,14 @@ var Iti = class _Iti {
4340
4386
  if (!isCountryChange) {
4341
4387
  return;
4342
4388
  }
4343
- this.#setCountry(iso2Lower);
4389
+ this.#updateSelectedCountry(iso2Lower);
4344
4390
  this.#updateDialCode(this.#selectedCountry?.dialCode || "");
4345
4391
  if (this.#options.formatOnDisplay) {
4346
4392
  const inputValue = this.#getTelInputValue();
4347
4393
  this.#updateValueFromNumber(inputValue);
4348
4394
  }
4349
4395
  this.#dispatchCountryChangeEvent();
4396
+ this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
4350
4397
  }
4351
4398
  //* Set the input value and update the country.
4352
4399
  setNumber(number) {
@@ -4470,7 +4517,7 @@ var intlTelInput = Object.assign(
4470
4517
  attachUtils,
4471
4518
  startedLoadingUtils: false,
4472
4519
  startedLoadingAutoCountry: false,
4473
- version: "27.0.17"
4520
+ version: "27.1.1"
4474
4521
  }
4475
4522
  );
4476
4523
  var intlTelInput_default = intlTelInput;
@@ -4539,7 +4586,6 @@ var IntlTelInput = class _IntlTelInput {
4539
4586
  lastEmittedErrorCode;
4540
4587
  // writeValue may be called by Angular forms before utils has loaded; queue it until then
4541
4588
  pendingWriteValue;
4542
- countryChangeHandler = () => this.handleInput();
4543
4589
  // eslint-disable-next-line class-methods-use-this
4544
4590
  onChange = () => {
4545
4591
  };
@@ -4551,7 +4597,6 @@ var IntlTelInput = class _IntlTelInput {
4551
4597
  };
4552
4598
  ngAfterViewInit() {
4553
4599
  this.iti = intlTelInput_default(this.inputRef.nativeElement, this.buildInitOptions());
4554
- this.inputRef.nativeElement.addEventListener("countrychange", this.countryChangeHandler);
4555
4600
  this.applyInputAttrs();
4556
4601
  if (this.disabled) {
4557
4602
  this.iti.setDisabled(this.disabled);
@@ -4685,7 +4730,6 @@ var IntlTelInput = class _IntlTelInput {
4685
4730
  }
4686
4731
  ngOnDestroy() {
4687
4732
  this.iti?.destroy();
4688
- this.inputRef.nativeElement.removeEventListener("countrychange", this.countryChangeHandler);
4689
4733
  }
4690
4734
  ignoredInputAttrs = /* @__PURE__ */ new Set([
4691
4735
  "type",