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