intl-tel-input 19.0.1 → 19.1.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.
@@ -34,6 +34,8 @@ const defaults = {
34
34
  excludeCountries: [],
35
35
  // fix the dropdown width to the input width (rather than being as wide as the longest country name)
36
36
  fixDropdownWidth: true,
37
+ // format the number as the user types
38
+ formatAsYouType: true,
37
39
  // format the input value during initialisation and on setNumber
38
40
  formatOnDisplay: true,
39
41
  // geoIp lookup function
@@ -756,23 +758,72 @@ class Iti {
756
758
 
757
759
  // initialize any key listeners
758
760
  _initKeyListeners() {
759
- // update flag on keyup
760
- this._handleKeyupEvent = () => {
761
+ let userOverrideFormatting = false;
762
+ // update flag on input event
763
+ this._handleKeyEvent = (e) => {
761
764
  if (this._updateFlagFromNumber(this.telInput.value)) {
762
765
  this._triggerCountryChange();
763
766
  }
767
+
768
+ // if user types their own formatting char (not a plus or a numeric), then set the override
769
+ if (e && e.data && /[^+0-9]/.test(e.data)) {
770
+ userOverrideFormatting = true;
771
+ }
772
+ // if user removes all formatting chars, then reset the override
773
+ else if (!/[^+0-9]/.test(this.telInput.value)) {
774
+ userOverrideFormatting = false;
775
+ }
776
+
777
+ if (this.options.formatAsYouType && !userOverrideFormatting) {
778
+ // maintain caret position after reformatting
779
+ const currentCaretPos = this.telInput.selectionStart;
780
+ const valueBeforeCaret = this.telInput.value.substring(0, currentCaretPos);
781
+ const relevantCharsBeforeCaret = valueBeforeCaret.replace(/[^+0-9]/g, "").length;
782
+ const isDeleteForwards = e && e.inputType === "deleteContentForward";
783
+ const formattedValue = this.formatNumberAsYouType();
784
+ const newCaretPos = this._translateCursorPosition(relevantCharsBeforeCaret, formattedValue, currentCaretPos, isDeleteForwards);
785
+ this.telInput.value = formattedValue
786
+ this.telInput.setSelectionRange(newCaretPos, newCaretPos);
787
+ }
764
788
  };
765
- this.telInput.addEventListener("keyup", this._handleKeyupEvent);
789
+ this.telInput.addEventListener("input", this._handleKeyEvent);
766
790
 
767
791
  // update flag on cut/paste events (now supported in all major browsers)
768
792
  this._handleClipboardEvent = () => {
769
793
  // hack because "paste" event is fired before input is updated
770
- setTimeout(this._handleKeyupEvent);
794
+ setTimeout(this._handleKeyEvent);
771
795
  };
772
796
  this.telInput.addEventListener("cut", this._handleClipboardEvent);
773
797
  this.telInput.addEventListener("paste", this._handleClipboardEvent);
774
798
  }
775
799
 
800
+ // iterate through the formattedValue until hit the right number of relevant chars
801
+ _translateCursorPosition(relevantChars, formattedValue, prevCaretPos, isDeleteForwards) {
802
+ // if the first char is a formatting char, and they backspace delete it:
803
+ // cursor should stay at the start (pos 0), rather than stick to the first digit (pos 1)
804
+ if (prevCaretPos === 0 && !isDeleteForwards) {
805
+ return 0;
806
+ }
807
+ let count = 0;
808
+ for (let i = 0; i < formattedValue.length; i++) {
809
+ if (/[+0-9]/.test(formattedValue[i])) {
810
+ count++;
811
+ }
812
+
813
+ // normal case: stop when you hit the right number of relevant chars
814
+ // (cursor will be just after the final relevant char)
815
+ if (count === relevantChars && !isDeleteForwards) {
816
+ return i + 1;
817
+ }
818
+ // spacial case: delete forwards (fn + delete on a mac):
819
+ // wait until hit one extra relevant char, and put the cursor just before it (after any formatting chars)
820
+ if (isDeleteForwards && count === relevantChars + 1) {
821
+ return i;
822
+ }
823
+ }
824
+ return formattedValue.length;
825
+ }
826
+
776
827
  // adhere to the input's maxlength attr
777
828
  _cap(number) {
778
829
  const max = this.telInput.getAttribute("maxlength");
@@ -1666,7 +1717,7 @@ class Iti {
1666
1717
  }
1667
1718
 
1668
1719
  // unbind key events, and cut/paste events
1669
- this.telInput.removeEventListener("keyup", this._handleKeyupEvent);
1720
+ this.telInput.removeEventListener("input", this._handleKeyEvent);
1670
1721
  this.telInput.removeEventListener("cut", this._handleClipboardEvent);
1671
1722
  this.telInput.removeEventListener("paste", this._handleClipboardEvent);
1672
1723
 
@@ -1746,6 +1797,14 @@ class Iti {
1746
1797
  : null;
1747
1798
  }
1748
1799
 
1800
+ // format the number as the user types
1801
+ formatNumberAsYouType() {
1802
+ const val = this._getFullNumber().trim();
1803
+ return window.intlTelInputUtils
1804
+ ? intlTelInputUtils.formatNumberAsYouType(val, this.selectedCountryData.iso2)
1805
+ : val;
1806
+ }
1807
+
1749
1808
  // update the selected flag, and update the input val accordingly
1750
1809
  setCountry(originalCountryCode) {
1751
1810
  const countryCode = originalCountryCode.toLowerCase();
package/src/js/utils.js CHANGED
@@ -3,6 +3,24 @@ goog.provide("i18n.phonenumbers.demo");
3
3
  goog.require("i18n.phonenumbers.PhoneNumberFormat");
4
4
  goog.require("i18n.phonenumbers.PhoneNumberUtil");
5
5
  goog.require("i18n.phonenumbers.Error");
6
+ goog.require('i18n.phonenumbers.AsYouTypeFormatter');
7
+
8
+ // format the number as the user types
9
+ const formatNumberAsYouType = (number, countryCode) => {
10
+ try {
11
+ // have to clean it first, as AYTF stops formatting as soon as it hits any formatting char (even it's own)
12
+ // (it's designed to be fed one char at a time, as opposed to every char every time)
13
+ const clean = number.replace(/[^+0-9]/g, "");
14
+ const formatter = new i18n.phonenumbers.AsYouTypeFormatter(countryCode);
15
+ let result = "";
16
+ for (let i = 0; i < clean.length; i++) {
17
+ result = formatter.inputDigit(clean.charAt(i));
18
+ }
19
+ return result;
20
+ } catch (e) {
21
+ return number;
22
+ }
23
+ };
6
24
 
7
25
  // format the given number to the given format
8
26
  const formatNumber = (number, countryCode, formatArg) => {
@@ -158,6 +176,7 @@ const validationError = {
158
176
 
159
177
  // exports
160
178
  goog.exportSymbol("intlTelInputUtils", {});
179
+ goog.exportSymbol("intlTelInputUtils.formatNumberAsYouType", formatNumberAsYouType);
161
180
  goog.exportSymbol("intlTelInputUtils.formatNumber", formatNumber);
162
181
  goog.exportSymbol("intlTelInputUtils.getExampleNumber", getExampleNumber);
163
182
  goog.exportSymbol("intlTelInputUtils.getExtension", getExtension);
@@ -127,7 +127,7 @@ var triggerInputEvent = function(type) {
127
127
  }
128
128
 
129
129
  var triggerKey = function(el, type, key) {
130
- var e = new KeyboardEvent(type, { key: key });
130
+ var e = new KeyboardEvent(type, { key: key, data: key });
131
131
  el.dispatchEvent(e);
132
132
  };
133
133