intl-tel-input 29.1.3 → 29.2.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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * International Telephone Input v29.1.3
2
+ * International Telephone Input v29.2.1
3
3
  * git+https://github.com/jackocnr/intl-tel-input.git
4
4
  * Licensed under the MIT license
5
5
  */
@@ -1792,6 +1792,29 @@ var _factory = (() => {
1792
1792
  // used for synthetic input trigger
1793
1793
  STRICT_REJECT: "strict:reject"
1794
1794
  };
1795
+ var ITI_SLOTS = [
1796
+ "container",
1797
+ "input",
1798
+ "countryContainer",
1799
+ "selectedCountry",
1800
+ "selectedCountryPrimary",
1801
+ "selectedFlag",
1802
+ "arrow",
1803
+ "selectedDialCode",
1804
+ "countrySelector",
1805
+ "countrySelectorContainer",
1806
+ "searchWrapper",
1807
+ "searchIcon",
1808
+ "searchInput",
1809
+ "searchClear",
1810
+ "countryList",
1811
+ "countryListItem",
1812
+ "countryListItemFlag",
1813
+ "countryName",
1814
+ "dialCode",
1815
+ "countryCheck",
1816
+ "noResults"
1817
+ ];
1795
1818
  var CLASSES = {
1796
1819
  HIDE: "iti__hide",
1797
1820
  V_HIDE: "iti__v-hide",
@@ -1849,6 +1872,7 @@ var _factory = (() => {
1849
1872
  NANP: "1"
1850
1873
  // North American Numbering Plan
1851
1874
  };
1875
+ var E164_MAX_DIGITS = 15;
1852
1876
  var UK = {
1853
1877
  ISO2: "gb",
1854
1878
  DIAL_CODE: "44",
@@ -1968,6 +1992,8 @@ var _factory = (() => {
1968
1992
  allowNumberExtensions: false,
1969
1993
  // Allow alphanumeric "phonewords" (e.g. +1 800 FLOWERS) as valid numbers
1970
1994
  allowPhonewords: false,
1995
+ //* Add custom classes to the elements we generate, keyed by slot name e.g. { selectedCountry: "rounded-l-lg" }.
1996
+ classNames: {},
1971
1997
  //* Add a custom class to the (injected) container element.
1972
1998
  containerClass: "",
1973
1999
  //* Locale for localising country names via Intl.DisplayNames.
@@ -2030,6 +2056,7 @@ var _factory = (() => {
2030
2056
  return v.nodeType === 1 && typeof v.tagName === "string" && typeof v.appendChild === "function";
2031
2057
  };
2032
2058
  var placeholderPolicySet = new Set(Object.values(PLACEHOLDER_POLICY));
2059
+ var slotSet = new Set(ITI_SLOTS);
2033
2060
  var warn = (message) => {
2034
2061
  console.warn(`[intl-tel-input] ${message}`);
2035
2062
  };
@@ -2130,6 +2157,26 @@ var _factory = (() => {
2130
2157
  }
2131
2158
  validatedOptions[key] = value;
2132
2159
  break;
2160
+ case "classNames": {
2161
+ if (!isPlainObject(value)) {
2162
+ warnOption("classNames", "an object", value);
2163
+ break;
2164
+ }
2165
+ const validSlots = {};
2166
+ for (const [slot, slotValue] of Object.entries(value)) {
2167
+ if (!slotSet.has(slot)) {
2168
+ warn(
2169
+ `Unknown slot '${slot}' in 'classNames'. Valid slots: ${ITI_SLOTS.join(", ")}. Skipping.`
2170
+ );
2171
+ } else if (typeof slotValue !== "string") {
2172
+ warnOption(`classNames.${slot}`, "a string", slotValue);
2173
+ } else {
2174
+ validSlots[slot] = slotValue.trim().replace(/\s+/g, " ");
2175
+ }
2176
+ }
2177
+ validatedOptions[key] = validSlots;
2178
+ break;
2179
+ }
2133
2180
  case "countryOrder": {
2134
2181
  if (value === null) {
2135
2182
  validatedOptions[key] = value;
@@ -2542,11 +2589,18 @@ var _factory = (() => {
2542
2589
  );
2543
2590
  }
2544
2591
  }
2592
+ //* Append any consumer-supplied classes (via the classNames option) for the given slot to our own classes for that element.
2593
+ #withSlotClass(slot, ourClasses) {
2594
+ const custom = this.#options.classNames[slot];
2595
+ return custom ? `${ourClasses} ${custom}` : ourClasses;
2596
+ }
2545
2597
  //* Generate all of the markup for the core library: the selected country overlay, and the country selector.
2546
2598
  buildMarkup(countries, searchTokens) {
2547
2599
  this.#countries = countries;
2548
2600
  this.#searchTokens = searchTokens;
2549
- this.telInputEl.classList.add("iti__tel-input");
2601
+ this.telInputEl.classList.add(
2602
+ ...this.#withSlotClass("input", "iti__tel-input").split(" ")
2603
+ );
2550
2604
  if (!this.telInputEl.hasAttribute("type")) {
2551
2605
  this.telInputEl.setAttribute("type", "tel");
2552
2606
  }
@@ -2574,7 +2628,9 @@ var _factory = (() => {
2574
2628
  "iti--inline-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN,
2575
2629
  [containerClass]: Boolean(containerClass)
2576
2630
  });
2577
- const wrapper = createEl("div", { class: parentClasses });
2631
+ const wrapper = createEl("div", {
2632
+ class: this.#withSlotClass("container", parentClasses)
2633
+ });
2578
2634
  if (this.#isRTL) {
2579
2635
  wrapper.setAttribute("dir", "ltr");
2580
2636
  }
@@ -2590,7 +2646,12 @@ var _factory = (() => {
2590
2646
  this.#countryContainerEl = createEl(
2591
2647
  "div",
2592
2648
  // visibly hidden until we measure its width to set the input padding correctly
2593
- { class: `iti__country-container ${CLASSES.V_HIDE}` },
2649
+ {
2650
+ class: this.#withSlotClass(
2651
+ "countryContainer",
2652
+ `iti__country-container ${CLASSES.V_HIDE}`
2653
+ )
2654
+ },
2594
2655
  wrapper
2595
2656
  );
2596
2657
  if (enableCountrySelector) {
@@ -2598,7 +2659,7 @@ var _factory = (() => {
2598
2659
  "button",
2599
2660
  {
2600
2661
  type: "button",
2601
- class: "iti__selected-country",
2662
+ class: this.#withSlotClass("selectedCountry", "iti__selected-country"),
2602
2663
  [ARIA.EXPANDED]: "false",
2603
2664
  [ARIA.LABEL]: this.#options.uiTranslations.noCountrySelected,
2604
2665
  [ARIA.HASPOPUP]: "dialog",
@@ -2612,31 +2673,44 @@ var _factory = (() => {
2612
2673
  } else {
2613
2674
  this.#selectedCountryEl = createEl(
2614
2675
  "div",
2615
- { class: "iti__selected-country" },
2676
+ { class: this.#withSlotClass("selectedCountry", "iti__selected-country") },
2616
2677
  this.#countryContainerEl
2617
2678
  );
2618
2679
  }
2619
2680
  const selectedCountryPrimary = createEl(
2620
2681
  "div",
2621
- { class: "iti__selected-country-primary" },
2682
+ {
2683
+ class: this.#withSlotClass(
2684
+ "selectedCountryPrimary",
2685
+ "iti__selected-country-primary"
2686
+ )
2687
+ },
2622
2688
  this.#selectedCountryEl
2623
2689
  );
2624
2690
  this.#selectedFlagEl = createEl(
2625
2691
  "div",
2626
- { class: CLASSES.FLAG },
2692
+ { class: this.#withSlotClass("selectedFlag", CLASSES.FLAG) },
2627
2693
  selectedCountryPrimary
2628
2694
  );
2629
2695
  if (enableCountrySelector) {
2630
2696
  this.#arrowEl = createEl(
2631
2697
  "div",
2632
- { class: "iti__arrow", [ARIA.HIDDEN]: "true" },
2698
+ {
2699
+ class: this.#withSlotClass("arrow", "iti__arrow"),
2700
+ [ARIA.HIDDEN]: "true"
2701
+ },
2633
2702
  selectedCountryPrimary
2634
2703
  );
2635
2704
  }
2636
2705
  if (separateDialCode) {
2637
2706
  this.#selectedDialCodeEl = createEl(
2638
2707
  "div",
2639
- { class: "iti__selected-dial-code" },
2708
+ {
2709
+ class: this.#withSlotClass(
2710
+ "selectedDialCode",
2711
+ "iti__selected-dial-code"
2712
+ )
2713
+ },
2640
2714
  this.#selectedCountryEl
2641
2715
  );
2642
2716
  }
@@ -2667,7 +2741,10 @@ var _factory = (() => {
2667
2741
  const extraClasses = matchDropdownWidth ? "" : "iti--flexible-dropdown-width";
2668
2742
  this.#countrySelectorEl = createEl("div", {
2669
2743
  id: `iti-${this.#id}__country-selector`,
2670
- class: `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`,
2744
+ class: this.#withSlotClass(
2745
+ "countrySelector",
2746
+ `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`
2747
+ ),
2671
2748
  role: "dialog",
2672
2749
  [ARIA.MODAL]: "true"
2673
2750
  });
@@ -2680,7 +2757,7 @@ var _factory = (() => {
2680
2757
  this.#countryListEl = createEl(
2681
2758
  "ul",
2682
2759
  {
2683
- class: "iti__country-list",
2760
+ class: this.#withSlotClass("countryList", "iti__country-list"),
2684
2761
  id: `iti-${this.#id}__country-listbox`,
2685
2762
  role: "listbox",
2686
2763
  [ARIA.LABEL]: uiTranslations.countryListAriaLabel
@@ -2699,7 +2776,9 @@ var _factory = (() => {
2699
2776
  "iti--inline-country-selector": !isFullscreen,
2700
2777
  [containerClass]: Boolean(containerClass)
2701
2778
  });
2702
- this.#detachedCountrySelectorEl = createEl("div", { class: wrapperClasses });
2779
+ this.#detachedCountrySelectorEl = createEl("div", {
2780
+ class: this.#withSlotClass("countrySelectorContainer", wrapperClasses)
2781
+ });
2703
2782
  this.#detachedCountrySelectorEl.appendChild(this.#countrySelectorEl);
2704
2783
  } else {
2705
2784
  this.#countryContainerEl.appendChild(this.#countrySelectorEl);
@@ -2720,13 +2799,13 @@ var _factory = (() => {
2720
2799
  const { uiTranslations, searchInputClass } = this.#options;
2721
2800
  const searchWrapper = createEl(
2722
2801
  "div",
2723
- { class: "iti__search-input-wrapper" },
2802
+ { class: this.#withSlotClass("searchWrapper", "iti__search-input-wrapper") },
2724
2803
  this.#countrySelectorEl
2725
2804
  );
2726
2805
  this.#searchIconEl = createEl(
2727
2806
  "span",
2728
2807
  {
2729
- class: "iti__search-icon",
2808
+ class: this.#withSlotClass("searchIcon", "iti__search-icon"),
2730
2809
  [ARIA.HIDDEN]: "true"
2731
2810
  },
2732
2811
  searchWrapper
@@ -2738,7 +2817,10 @@ var _factory = (() => {
2738
2817
  id: `iti-${this.#id}__search-input`,
2739
2818
  // Chrome says inputs need either a name or an id
2740
2819
  type: "search",
2741
- class: `iti__search-input ${searchInputClass}`,
2820
+ class: this.#withSlotClass(
2821
+ "searchInput",
2822
+ `iti__search-input ${searchInputClass}`
2823
+ ),
2742
2824
  placeholder: uiTranslations.searchPlaceholder,
2743
2825
  // role=combobox + aria-autocomplete=list + aria-activedescendant allows maintaining focus on the search input while allowing users to navigate search results with up/down keyboard keys
2744
2826
  role: "combobox",
@@ -2754,7 +2836,10 @@ var _factory = (() => {
2754
2836
  "button",
2755
2837
  {
2756
2838
  type: "button",
2757
- class: `iti__search-clear ${CLASSES.HIDE}`,
2839
+ class: this.#withSlotClass(
2840
+ "searchClear",
2841
+ `iti__search-clear ${CLASSES.HIDE}`
2842
+ ),
2758
2843
  [ARIA.LABEL]: uiTranslations.clearSearchAriaLabel,
2759
2844
  tabindex: "-1"
2760
2845
  },
@@ -2769,7 +2854,7 @@ var _factory = (() => {
2769
2854
  this.#noResultsMessageEl = createEl(
2770
2855
  "div",
2771
2856
  {
2772
- class: `iti__no-results ${CLASSES.HIDE}`,
2857
+ class: this.#withSlotClass("noResults", `iti__no-results ${CLASSES.HIDE}`),
2773
2858
  [ARIA.HIDDEN]: "true"
2774
2859
  // all a11y messaging happens in this.#searchResultsLiveRegionEl
2775
2860
  },
@@ -2823,11 +2908,9 @@ var _factory = (() => {
2823
2908
  //* For each country: add a country list item <li> to the countryList <ul> container.
2824
2909
  #appendListItems() {
2825
2910
  const frag = document.createDocumentFragment();
2911
+ const liClass = this.#withSlotClass("countryListItem", CLASSES.COUNTRY_ITEM);
2826
2912
  for (let i = 0; i < this.#countries.length; i++) {
2827
2913
  const c = this.#countries[i];
2828
- const liClass = buildClassNames({
2829
- [CLASSES.COUNTRY_ITEM]: true
2830
- });
2831
2914
  const listItem = createEl("li", {
2832
2915
  id: `iti-${this.#id}__item-${c.iso2}`,
2833
2916
  class: liClass,
@@ -2839,11 +2922,28 @@ var _factory = (() => {
2839
2922
  listItem.dataset[DATA_KEYS.ISO2] = c.iso2;
2840
2923
  this.#listItemByIso2.set(c.iso2, listItem);
2841
2924
  if (this.#options.showFlags) {
2842
- createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
2925
+ createEl(
2926
+ "div",
2927
+ {
2928
+ class: this.#withSlotClass(
2929
+ "countryListItemFlag",
2930
+ `${CLASSES.FLAG} iti__${c.iso2}`
2931
+ )
2932
+ },
2933
+ listItem
2934
+ );
2843
2935
  }
2844
- const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
2936
+ const nameEl = createEl(
2937
+ "span",
2938
+ { class: this.#withSlotClass("countryName", "iti__country-name") },
2939
+ listItem
2940
+ );
2845
2941
  nameEl.textContent = `${c.name} `;
2846
- const dialEl = createEl("span", { class: "iti__dial-code" }, nameEl);
2942
+ const dialEl = createEl(
2943
+ "span",
2944
+ { class: this.#withSlotClass("dialCode", "iti__dial-code") },
2945
+ nameEl
2946
+ );
2847
2947
  if (this.#isRTL) {
2848
2948
  dialEl.setAttribute("dir", "ltr");
2849
2949
  }
@@ -3290,7 +3390,10 @@ var _factory = (() => {
3290
3390
  newListItem.setAttribute(ARIA.SELECTED, "true");
3291
3391
  const checkIcon = createEl(
3292
3392
  "span",
3293
- { class: "iti__country-check", [ARIA.HIDDEN]: "true" },
3393
+ {
3394
+ class: this.#withSlotClass("countryCheck", "iti__country-check"),
3395
+ [ARIA.HIDDEN]: "true"
3396
+ },
3294
3397
  newListItem
3295
3398
  );
3296
3399
  checkIcon.appendChild(buildCheckIcon());
@@ -3477,7 +3580,10 @@ var _factory = (() => {
3477
3580
  this.#updateSelectedListItem(iso2);
3478
3581
  }
3479
3582
  if (this.#selectedCountryEl) {
3480
- const flagClass = iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`;
3583
+ const flagClass = this.#withSlotClass(
3584
+ "selectedFlag",
3585
+ iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`
3586
+ );
3481
3587
  let ariaLabel, title;
3482
3588
  let flagContent = null;
3483
3589
  if (iso2) {
@@ -4107,8 +4213,8 @@ var _factory = (() => {
4107
4213
  const after = inputValue.slice(selEnd ?? void 0);
4108
4214
  const newValue = before + normalisedKey + after;
4109
4215
  const newFullNumber = this.#buildFullNumber(newValue);
4110
- let hasExceededMaxLength = false;
4111
- if (intlTelInput.utils && this.#maxCoreNumberLength) {
4216
+ let hasExceededMaxLength = getNumeric(newFullNumber).length > E164_MAX_DIGITS;
4217
+ if (!hasExceededMaxLength && intlTelInput.utils && this.#maxCoreNumberLength) {
4112
4218
  const coreNumber = intlTelInput.utils.getCoreNumber(
4113
4219
  newFullNumber,
4114
4220
  this.#selectedCountry?.iso2
@@ -4173,44 +4279,35 @@ var _factory = (() => {
4173
4279
  let newValue = before + sanitised + after;
4174
4280
  let rejectReason = sanitised !== pasted ? "invalid" : null;
4175
4281
  if (newValue.length > 30) {
4176
- this.#ui.playStrictRejectAnimation();
4177
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4178
- source: "paste",
4179
- rejectedInput: pastedRaw,
4180
- reason: "max-length"
4181
- });
4182
- this.#restoreValueBeforeStrictPaste(pasteSnapshot);
4282
+ this.#rejectStrictPasteAsTooLong(pasteSnapshot);
4183
4283
  return true;
4184
4284
  }
4185
- if (newValue.length > 5 && intlTelInput.utils) {
4285
+ const excessDigits = getNumeric(this.#buildFullNumber(newValue)).length - E164_MAX_DIGITS;
4286
+ if (excessDigits > 0) {
4287
+ if (selEnd !== originalValue.length) {
4288
+ this.#rejectStrictPasteAsTooLong(pasteSnapshot);
4289
+ return true;
4290
+ }
4291
+ newValue = newValue.slice(0, newValue.length - excessDigits);
4292
+ rejectReason = "max-length";
4293
+ }
4294
+ if (this.#maxCoreNumberLength && newValue.length > 5 && intlTelInput.utils) {
4186
4295
  let coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
4187
4296
  while (coreNumber.length === 0 && newValue.length > 0) {
4188
4297
  newValue = newValue.slice(0, -1);
4189
4298
  coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
4190
4299
  }
4191
4300
  if (!coreNumber) {
4192
- this.#ui.playStrictRejectAnimation();
4193
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4194
- source: "paste",
4195
- rejectedInput: pastedRaw,
4196
- reason: "max-length"
4197
- });
4198
- this.#restoreValueBeforeStrictPaste(pasteSnapshot);
4301
+ this.#rejectStrictPasteAsTooLong(pasteSnapshot);
4199
4302
  return true;
4200
4303
  }
4201
- if (this.#maxCoreNumberLength && coreNumber.length > this.#maxCoreNumberLength) {
4304
+ if (coreNumber.length > this.#maxCoreNumberLength) {
4202
4305
  if (selEnd === originalValue.length) {
4203
4306
  const trimLength = coreNumber.length - this.#maxCoreNumberLength;
4204
4307
  newValue = newValue.slice(0, newValue.length - trimLength);
4205
4308
  rejectReason = "max-length";
4206
4309
  } else {
4207
- this.#ui.playStrictRejectAnimation();
4208
- this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4209
- source: "paste",
4210
- rejectedInput: pastedRaw,
4211
- reason: "max-length"
4212
- });
4213
- this.#restoreValueBeforeStrictPaste(pasteSnapshot);
4310
+ this.#rejectStrictPasteAsTooLong(pasteSnapshot);
4214
4311
  return true;
4215
4312
  }
4216
4313
  }
@@ -4230,6 +4327,16 @@ var _factory = (() => {
4230
4327
  }
4231
4328
  return false;
4232
4329
  }
4330
+ // Reject a paste entirely because it would exceed the max length, restoring the previous value.
4331
+ #rejectStrictPasteAsTooLong(pasteSnapshot) {
4332
+ this.#ui.playStrictRejectAnimation();
4333
+ this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4334
+ source: "paste",
4335
+ rejectedInput: pasteSnapshot.pastedRaw,
4336
+ reason: "max-length"
4337
+ });
4338
+ this.#restoreValueBeforeStrictPaste(pasteSnapshot);
4339
+ }
4233
4340
  #restoreValueBeforeStrictPaste(pasteSnapshot) {
4234
4341
  this.#setTelInputValue(pasteSnapshot.value);
4235
4342
  this.#ui.telInputEl.setSelectionRange(
@@ -4338,6 +4445,9 @@ var _factory = (() => {
4338
4445
  if (currentDial && currentDial.startsWith(numeric)) {
4339
4446
  return null;
4340
4447
  }
4448
+ if (!selectedIso2) {
4449
+ return null;
4450
+ }
4341
4451
  return "";
4342
4452
  } else if ((!number || number === "+") && !selectedIso2 && this.#fallbackCountryIso2) {
4343
4453
  return this.#fallbackCountryIso2;
@@ -4874,7 +4984,7 @@ var _factory = (() => {
4874
4984
  attachUtils,
4875
4985
  startedLoadingUtils: false,
4876
4986
  startedLoadingAutoCountry: false,
4877
- version: "29.1.3",
4987
+ version: "29.2.1",
4878
4988
  NUMBER_FORMAT,
4879
4989
  NUMBER_TYPE,
4880
4990
  VALIDATION_ERROR,