intl-tel-input 27.1.2 → 27.2.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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * International Telephone Input v27.1.2
2
+ * International Telephone Input v27.2.0
3
3
  * git+https://github.com/jackocnr/intl-tel-input.git
4
4
  * Licensed under the MIT license
5
5
  */
@@ -3022,7 +3022,7 @@ var _factory = (() => {
3022
3022
  this.#selectedCountryEl.focus();
3023
3023
  }
3024
3024
  }
3025
- if (!this.#options.countrySearch && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {
3025
+ if (!this.#options.countrySearch && e.target !== this.telInputEl && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {
3026
3026
  e.stopPropagation();
3027
3027
  if (queryTimer) {
3028
3028
  clearTimeout(queryTimer);
@@ -3544,6 +3544,8 @@ var _factory = (() => {
3544
3544
  #isActive = true;
3545
3545
  #abortController;
3546
3546
  #numerals;
3547
+ //* Tracks whether the user has typed/pasted their own formatting chars, so AYT-formatting should back off.
3548
+ #userOverrideFormatting = false;
3547
3549
  #autoCountryDeferred;
3548
3550
  #utilsDeferred;
3549
3551
  constructor(input, customOptions = {}) {
@@ -3733,176 +3735,211 @@ var _factory = (() => {
3733
3735
  this.#bindKeydownListener();
3734
3736
  this.#bindPasteListener();
3735
3737
  }
3736
- #bindInputListener() {
3737
- const {
3738
- strictMode,
3739
- formatAsYouType,
3740
- separateDialCode,
3741
- allowDropdown,
3742
- countrySearch
3743
- } = this.#options;
3744
- let userOverrideFormatting = false;
3745
- if (REGEX.ALPHA_UNICODE.test(this.#getTelInputValue())) {
3746
- userOverrideFormatting = true;
3738
+ //* Android workaround for handling plus when separateDialCode enabled (as impossible to handle with keydown/keyup, for which e.key always returns "Unidentified", see https://stackoverflow.com/q/59584061/217866)
3739
+ #handleAndroidPlusKey(inputValue) {
3740
+ this.#removeJustTypedChar(inputValue);
3741
+ this.#openDropdownWithPlus();
3742
+ }
3743
+ //* Android strictMode workaround: the keydown-based filter can't block these because e.key is "Unidentified" on Android virtual keyboards, so strip them here on input.
3744
+ #handleAndroidStrictReject(inputValue, rejectedInput) {
3745
+ const newCaretPos = this.#removeJustTypedChar(inputValue);
3746
+ this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
3747
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3748
+ source: "key",
3749
+ rejectedInput,
3750
+ reason: "invalid"
3751
+ });
3752
+ }
3753
+ //* Format the input value using libphonenumber's AYT formatter, preserving caret position (called after an input event).
3754
+ #formatAsYouType(inputValue, isDeleteForwards) {
3755
+ const currentCaretPos = this.#ui.telInputEl.selectionStart || 0;
3756
+ const valueBeforeCaret = inputValue.substring(0, currentCaretPos);
3757
+ const relevantCharsBeforeCaret = valueBeforeCaret.replace(
3758
+ REGEX.NON_PLUS_NUMERIC_GLOBAL,
3759
+ ""
3760
+ ).length;
3761
+ const fullNumber = this.#getFullNumber();
3762
+ const formattedValue = formatNumberAsYouType(
3763
+ fullNumber,
3764
+ inputValue,
3765
+ intlTelInput.utils,
3766
+ this.#selectedCountry,
3767
+ this.#options.separateDialCode
3768
+ );
3769
+ const newCaretPos = computeNewCaretPosition(
3770
+ relevantCharsBeforeCaret,
3771
+ formattedValue,
3772
+ currentCaretPos,
3773
+ isDeleteForwards
3774
+ );
3775
+ this.#setTelInputValue(formattedValue);
3776
+ this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
3777
+ }
3778
+ //* If separateDialCode AND typed dial code (e.g. from paste or autofill, or from typing a dial code when countrySearch disabled), then remove the typed dial code.
3779
+ //* Only strip when a full dial code is actually present — otherwise a lone typed "+" (or partial prefix) would get erased.
3780
+ #stripTypedDialCode(inputValue) {
3781
+ if (inputValue.startsWith("+") && this.#selectedCountry && this.#getDialCode(inputValue)) {
3782
+ const cleanNumber = stripSeparateDialCode(
3783
+ inputValue,
3784
+ true,
3785
+ true,
3786
+ this.#selectedCountry
3787
+ );
3788
+ this.#setTelInputValue(cleanNumber);
3747
3789
  }
3748
- const handleInputEvent = (e) => {
3749
- if (e?.detail && e.detail["isCountryChange"]) {
3750
- return;
3751
- }
3752
- const inputValue = this.#getTelInputValue();
3753
- if (this.#isAndroid && e?.data === "+" && separateDialCode && allowDropdown && countrySearch) {
3754
- this.#removeJustTypedChar(inputValue);
3755
- this.#openDropdownWithPlus();
3756
- return;
3757
- }
3758
- if (this.#isAndroid && strictMode && (e?.data === " " || e?.data === "-" || e?.data === ".")) {
3759
- const newCaretPos = this.#removeJustTypedChar(inputValue);
3760
- this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
3761
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3762
- source: "key",
3763
- rejectedInput: e.data,
3764
- reason: "invalid"
3765
- });
3766
- return;
3767
- }
3768
- if (this.#updateCountryFromNumber(inputValue)) {
3769
- this.#dispatchCountryChangeEvent();
3770
- }
3771
- const isFormattingChar = e?.data && REGEX.NON_PLUS_NUMERIC.test(e.data);
3772
- const isPaste = e?.inputType === INPUT_TYPES.PASTE && inputValue;
3773
- if (isFormattingChar || isPaste && !strictMode) {
3774
- userOverrideFormatting = true;
3775
- } else if (!REGEX.NON_PLUS_NUMERIC.test(inputValue)) {
3776
- userOverrideFormatting = false;
3777
- }
3778
- const isSetNumber = e?.detail && e.detail["isSetNumber"];
3779
- const isAscii = this.#numerals.isAscii();
3780
- if (formatAsYouType && !userOverrideFormatting && !isSetNumber && isAscii) {
3781
- const currentCaretPos = this.#ui.telInputEl.selectionStart || 0;
3782
- const valueBeforeCaret = inputValue.substring(0, currentCaretPos);
3783
- const relevantCharsBeforeCaret = valueBeforeCaret.replace(
3784
- REGEX.NON_PLUS_NUMERIC_GLOBAL,
3785
- ""
3786
- ).length;
3787
- const isDeleteForwards = e?.inputType === INPUT_TYPES.DELETE_FORWARD;
3788
- const fullNumber = this.#getFullNumber();
3789
- const formattedValue = formatNumberAsYouType(
3790
- fullNumber,
3791
- inputValue,
3792
- intlTelInput.utils,
3793
- this.#selectedCountry,
3794
- separateDialCode
3795
- );
3796
- const newCaretPos = computeNewCaretPosition(
3797
- relevantCharsBeforeCaret,
3798
- formattedValue,
3799
- currentCaretPos,
3800
- isDeleteForwards
3801
- );
3802
- this.#setTelInputValue(formattedValue);
3803
- this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
3804
- }
3805
- if (separateDialCode && inputValue.startsWith("+") && this.#selectedCountry?.dialCode) {
3806
- const cleanNumber = stripSeparateDialCode(
3807
- inputValue,
3808
- true,
3809
- separateDialCode,
3810
- this.#selectedCountry
3811
- );
3812
- this.#setTelInputValue(cleanNumber);
3813
- }
3814
- };
3790
+ }
3791
+ #bindInputListener() {
3792
+ this.#userOverrideFormatting = REGEX.ALPHA_UNICODE.test(
3793
+ this.#getTelInputValue()
3794
+ );
3815
3795
  this.#ui.telInputEl.addEventListener(
3816
3796
  "input",
3817
- handleInputEvent,
3797
+ this.#handleInputEvent,
3818
3798
  {
3819
3799
  signal: this.#abortController.signal
3820
3800
  }
3821
3801
  );
3822
3802
  }
3803
+ //* On input event: (1) Update selected country, (2) Format-as-you-type.
3804
+ //* Note that this fires AFTER the input is updated.
3805
+ #handleInputEvent = (e) => {
3806
+ const { strictMode, formatAsYouType, separateDialCode, allowDropdown, countrySearch } = this.#options;
3807
+ const detail = e?.detail;
3808
+ if (detail?.["isCountryChange"]) {
3809
+ return;
3810
+ }
3811
+ const inputValue = this.#getTelInputValue();
3812
+ if (this.#isAndroid && e?.data === "+" && separateDialCode && allowDropdown && countrySearch) {
3813
+ this.#handleAndroidPlusKey(inputValue);
3814
+ return;
3815
+ }
3816
+ if (this.#isAndroid && strictMode && (e?.data === " " || e?.data === "-" || e?.data === ".")) {
3817
+ this.#handleAndroidStrictReject(inputValue, e.data);
3818
+ return;
3819
+ }
3820
+ if (this.#updateCountryFromNumber(inputValue)) {
3821
+ this.#dispatchCountryChangeEvent();
3822
+ }
3823
+ const isFormattingChar = e?.data && REGEX.NON_PLUS_NUMERIC.test(e.data);
3824
+ const isPaste = e?.inputType === INPUT_TYPES.PASTE && inputValue;
3825
+ if (isFormattingChar || isPaste && !strictMode) {
3826
+ this.#userOverrideFormatting = true;
3827
+ } else if (!REGEX.NON_PLUS_NUMERIC.test(inputValue)) {
3828
+ this.#userOverrideFormatting = false;
3829
+ }
3830
+ if (formatAsYouType && !this.#userOverrideFormatting && !detail?.["isSetNumber"] && this.#numerals.isAscii()) {
3831
+ this.#formatAsYouType(
3832
+ inputValue,
3833
+ e?.inputType === INPUT_TYPES.DELETE_FORWARD
3834
+ );
3835
+ }
3836
+ if (separateDialCode) {
3837
+ this.#stripTypedDialCode(inputValue);
3838
+ }
3839
+ };
3823
3840
  #bindKeydownListener() {
3824
- const { strictMode, separateDialCode, allowDropdown, countrySearch } = this.#options;
3841
+ const { strictMode, separateDialCode } = this.#options;
3825
3842
  if (!strictMode && !separateDialCode) {
3826
3843
  return;
3827
3844
  }
3828
- const handleKeydownEvent = (e) => {
3829
- if (!e.key || e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) {
3830
- return;
3831
- }
3832
- if (separateDialCode && allowDropdown && countrySearch && e.key === "+") {
3833
- e.preventDefault();
3834
- this.#openDropdownWithPlus();
3835
- return;
3836
- }
3837
- if (!strictMode) {
3838
- return;
3839
- }
3840
- const inputValue = this.#getTelInputValue();
3841
- const alreadyHasPlus = inputValue.startsWith("+");
3842
- const isInitialPlus = !alreadyHasPlus && this.#ui.telInputEl.selectionStart === 0 && e.key === "+";
3843
- const normalisedKey = this.#numerals.normalise(e.key);
3844
- const isNumeric = /^[0-9]$/.test(normalisedKey);
3845
- const isAllowedChar = separateDialCode ? isNumeric : isInitialPlus || isNumeric;
3846
- const input = this.#ui.telInputEl;
3847
- const selStart = input.selectionStart;
3848
- const selEnd = input.selectionEnd;
3849
- const before = inputValue.slice(0, selStart ?? void 0);
3850
- const after = inputValue.slice(selEnd ?? void 0);
3851
- const newValue = before + normalisedKey + after;
3852
- const newFullNumber = this.#buildFullNumber(newValue);
3853
- let hasExceededMaxLength = false;
3854
- if (intlTelInput.utils && this.#maxCoreNumberLength) {
3855
- const coreNumber = intlTelInput.utils.getCoreNumber(
3856
- newFullNumber,
3857
- this.#selectedCountry?.iso2
3858
- );
3859
- hasExceededMaxLength = coreNumber.length > this.#maxCoreNumberLength;
3860
- }
3861
- const newCountry = this.#resolveCountryChangeFromNumber(newFullNumber);
3862
- const isChangingDialCode = newCountry !== null;
3863
- if (!isAllowedChar || hasExceededMaxLength && !isChangingDialCode && !isInitialPlus) {
3864
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3865
- source: "key",
3866
- rejectedInput: e.key,
3867
- reason: !isAllowedChar ? "invalid" : "max-length"
3868
- });
3869
- e.preventDefault();
3870
- }
3871
- };
3872
- this.#ui.telInputEl.addEventListener("keydown", handleKeydownEvent, {
3845
+ this.#ui.telInputEl.addEventListener("keydown", this.#handleKeydownEvent, {
3873
3846
  signal: this.#abortController.signal
3874
3847
  });
3875
3848
  }
3849
+ //* On keydown event: (1) if strictMode then prevent invalid characters, (2) if separateDialCode then handle plus key
3850
+ //* Note that this fires BEFORE the input is updated.
3851
+ #handleKeydownEvent = (e) => {
3852
+ const { strictMode, separateDialCode, allowDropdown, countrySearch } = this.#options;
3853
+ if (!e.key || e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) {
3854
+ return;
3855
+ }
3856
+ if (separateDialCode && allowDropdown && countrySearch && e.key === "+") {
3857
+ e.preventDefault();
3858
+ this.#openDropdownWithPlus();
3859
+ return;
3860
+ }
3861
+ if (!strictMode) {
3862
+ return;
3863
+ }
3864
+ const inputValue = this.#getTelInputValue();
3865
+ const alreadyHasPlus = inputValue.startsWith("+");
3866
+ const isInitialPlus = !alreadyHasPlus && this.#ui.telInputEl.selectionStart === 0 && e.key === "+";
3867
+ const normalisedKey = this.#numerals.normalise(e.key);
3868
+ const isNumeric = /^[0-9]$/.test(normalisedKey);
3869
+ const isAllowedChar = separateDialCode ? isNumeric : isInitialPlus || isNumeric;
3870
+ const input = this.#ui.telInputEl;
3871
+ const selStart = input.selectionStart;
3872
+ const selEnd = input.selectionEnd;
3873
+ const before = inputValue.slice(0, selStart ?? void 0);
3874
+ const after = inputValue.slice(selEnd ?? void 0);
3875
+ const newValue = before + normalisedKey + after;
3876
+ const newFullNumber = this.#buildFullNumber(newValue);
3877
+ let hasExceededMaxLength = false;
3878
+ if (intlTelInput.utils && this.#maxCoreNumberLength) {
3879
+ const coreNumber = intlTelInput.utils.getCoreNumber(
3880
+ newFullNumber,
3881
+ this.#selectedCountry?.iso2
3882
+ );
3883
+ hasExceededMaxLength = coreNumber.length > this.#maxCoreNumberLength;
3884
+ }
3885
+ const newCountry = this.#resolveCountryChangeFromNumber(newFullNumber);
3886
+ const isChangingDialCode = newCountry !== null;
3887
+ if (!isAllowedChar || hasExceededMaxLength && !isChangingDialCode && !isInitialPlus) {
3888
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3889
+ source: "key",
3890
+ rejectedInput: e.key,
3891
+ reason: !isAllowedChar ? "invalid" : "max-length"
3892
+ });
3893
+ e.preventDefault();
3894
+ }
3895
+ };
3876
3896
  #bindPasteListener() {
3877
3897
  if (!this.#options.strictMode) {
3878
3898
  return;
3879
3899
  }
3880
- const handlePasteEvent = (e) => {
3881
- e.preventDefault();
3882
- const input = this.#ui.telInputEl;
3883
- const selStart = input.selectionStart;
3884
- const selEnd = input.selectionEnd;
3885
- const inputValue = this.#getTelInputValue();
3886
- const before = inputValue.slice(0, selStart ?? void 0);
3887
- const after = inputValue.slice(selEnd ?? void 0);
3888
- const iso2 = this.#selectedCountry?.iso2;
3889
- const pastedRaw = e.clipboardData.getData("text");
3890
- const pasted = this.#numerals.normalise(pastedRaw);
3891
- const initialCharSelected = selStart === 0 && selEnd > 0;
3892
- const allowLeadingPlus = !inputValue.startsWith("+") || initialCharSelected;
3893
- const allowedChars = pasted.replace(REGEX.NON_PLUS_NUMERIC_GLOBAL, "");
3894
- const hasLeadingPlus = allowedChars.startsWith("+");
3895
- const numerics = allowedChars.replace(/\+/g, "");
3896
- const sanitised = hasLeadingPlus && allowLeadingPlus ? `+${numerics}` : numerics;
3897
- let newValue = before + sanitised + after;
3898
- let rejectReason = sanitised !== pasted ? "invalid" : null;
3899
- if (newValue.length > 5 && intlTelInput.utils) {
3900
- let coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
3901
- while (coreNumber.length === 0 && newValue.length > 0) {
3902
- newValue = newValue.slice(0, -1);
3903
- coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
3904
- }
3905
- if (!coreNumber) {
3900
+ this.#ui.telInputEl.addEventListener("paste", this.#handlePasteEvent, {
3901
+ signal: this.#abortController.signal
3902
+ });
3903
+ }
3904
+ #handlePasteEvent = (e) => {
3905
+ e.preventDefault();
3906
+ const input = this.#ui.telInputEl;
3907
+ const selStart = input.selectionStart;
3908
+ const selEnd = input.selectionEnd;
3909
+ const inputValue = this.#getTelInputValue();
3910
+ const before = inputValue.slice(0, selStart ?? void 0);
3911
+ const after = inputValue.slice(selEnd ?? void 0);
3912
+ const iso2 = this.#selectedCountry?.iso2;
3913
+ const pastedRaw = e.clipboardData.getData("text");
3914
+ const pasted = this.#numerals.normalise(pastedRaw);
3915
+ const initialCharSelected = selStart === 0 && selEnd > 0;
3916
+ const allowLeadingPlus = !inputValue.startsWith("+") || initialCharSelected;
3917
+ const allowedChars = pasted.replace(REGEX.NON_PLUS_NUMERIC_GLOBAL, "");
3918
+ const hasLeadingPlus = allowedChars.startsWith("+");
3919
+ const numerics = allowedChars.replace(/\+/g, "");
3920
+ const sanitised = hasLeadingPlus && allowLeadingPlus ? `+${numerics}` : numerics;
3921
+ let newValue = before + sanitised + after;
3922
+ let rejectReason = sanitised !== pasted ? "invalid" : null;
3923
+ if (newValue.length > 5 && intlTelInput.utils) {
3924
+ let coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
3925
+ while (coreNumber.length === 0 && newValue.length > 0) {
3926
+ newValue = newValue.slice(0, -1);
3927
+ coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
3928
+ }
3929
+ if (!coreNumber) {
3930
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3931
+ source: "paste",
3932
+ rejectedInput: pastedRaw,
3933
+ reason: "max-length"
3934
+ });
3935
+ return;
3936
+ }
3937
+ if (this.#maxCoreNumberLength && coreNumber.length > this.#maxCoreNumberLength) {
3938
+ if (input.selectionEnd === inputValue.length) {
3939
+ const trimLength = coreNumber.length - this.#maxCoreNumberLength;
3940
+ newValue = newValue.slice(0, newValue.length - trimLength);
3941
+ rejectReason = "max-length";
3942
+ } else {
3906
3943
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3907
3944
  source: "paste",
3908
3945
  rejectedInput: pastedRaw,
@@ -3910,37 +3947,20 @@ var _factory = (() => {
3910
3947
  });
3911
3948
  return;
3912
3949
  }
3913
- if (this.#maxCoreNumberLength && coreNumber.length > this.#maxCoreNumberLength) {
3914
- if (input.selectionEnd === inputValue.length) {
3915
- const trimLength = coreNumber.length - this.#maxCoreNumberLength;
3916
- newValue = newValue.slice(0, newValue.length - trimLength);
3917
- rejectReason = "max-length";
3918
- } else {
3919
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3920
- source: "paste",
3921
- rejectedInput: pastedRaw,
3922
- reason: "max-length"
3923
- });
3924
- return;
3925
- }
3926
- }
3927
3950
  }
3928
- this.#setTelInputValue(newValue);
3929
- const caretPos = selStart + sanitised.length;
3930
- input.setSelectionRange(caretPos, caretPos);
3931
- input.dispatchEvent(new InputEvent("input", { bubbles: true }));
3932
- if (rejectReason) {
3933
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3934
- source: "paste",
3935
- rejectedInput: pastedRaw,
3936
- reason: rejectReason
3937
- });
3938
- }
3939
- };
3940
- this.#ui.telInputEl.addEventListener("paste", handlePasteEvent, {
3941
- signal: this.#abortController.signal
3942
- });
3943
- }
3951
+ }
3952
+ this.#setTelInputValue(newValue);
3953
+ const caretPos = selStart + sanitised.length;
3954
+ input.setSelectionRange(caretPos, caretPos);
3955
+ input.dispatchEvent(new InputEvent("input", { bubbles: true }));
3956
+ if (rejectReason) {
3957
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3958
+ source: "paste",
3959
+ rejectedInput: pastedRaw,
3960
+ reason: rejectReason
3961
+ });
3962
+ }
3963
+ };
3944
3964
  //* Adhere to the input's maxlength attr.
3945
3965
  #truncateToMaxLength(number) {
3946
3966
  const max = Number(this.#ui.telInputEl.getAttribute("maxlength"));
@@ -4582,7 +4602,7 @@ var _factory = (() => {
4582
4602
  attachUtils,
4583
4603
  startedLoadingUtils: false,
4584
4604
  startedLoadingAutoCountry: false,
4585
- version: "27.1.2"
4605
+ version: "27.2.0"
4586
4606
  }
4587
4607
  );
4588
4608
  var intlTelInput_default = intlTelInput;