intl-tel-input 29.1.3 → 29.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.
@@ -1761,6 +1761,29 @@ var EVENTS = {
1761
1761
  // used for synthetic input trigger
1762
1762
  STRICT_REJECT: "strict:reject"
1763
1763
  };
1764
+ var ITI_SLOTS = [
1765
+ "container",
1766
+ "input",
1767
+ "countryContainer",
1768
+ "selectedCountry",
1769
+ "selectedCountryPrimary",
1770
+ "selectedFlag",
1771
+ "arrow",
1772
+ "selectedDialCode",
1773
+ "countrySelector",
1774
+ "countrySelectorContainer",
1775
+ "searchWrapper",
1776
+ "searchIcon",
1777
+ "searchInput",
1778
+ "searchClear",
1779
+ "countryList",
1780
+ "countryListItem",
1781
+ "countryListItemFlag",
1782
+ "countryName",
1783
+ "dialCode",
1784
+ "countryCheck",
1785
+ "noResults"
1786
+ ];
1764
1787
  var CLASSES = {
1765
1788
  HIDE: "iti__hide",
1766
1789
  V_HIDE: "iti__v-hide",
@@ -1937,6 +1960,8 @@ var defaults = {
1937
1960
  allowNumberExtensions: false,
1938
1961
  // Allow alphanumeric "phonewords" (e.g. +1 800 FLOWERS) as valid numbers
1939
1962
  allowPhonewords: false,
1963
+ //* Add custom classes to the elements we generate, keyed by slot name e.g. { selectedCountry: "rounded-l-lg" }.
1964
+ classNames: {},
1940
1965
  //* Add a custom class to the (injected) container element.
1941
1966
  containerClass: "",
1942
1967
  //* Locale for localising country names via Intl.DisplayNames.
@@ -1999,6 +2024,7 @@ var isElLike = (val) => {
1999
2024
  return v.nodeType === 1 && typeof v.tagName === "string" && typeof v.appendChild === "function";
2000
2025
  };
2001
2026
  var placeholderPolicySet = new Set(Object.values(PLACEHOLDER_POLICY));
2027
+ var slotSet = new Set(ITI_SLOTS);
2002
2028
  var warn = (message) => {
2003
2029
  console.warn(`[intl-tel-input] ${message}`);
2004
2030
  };
@@ -2099,6 +2125,26 @@ var validateOptions = (customOptions) => {
2099
2125
  }
2100
2126
  validatedOptions[key] = value;
2101
2127
  break;
2128
+ case "classNames": {
2129
+ if (!isPlainObject(value)) {
2130
+ warnOption("classNames", "an object", value);
2131
+ break;
2132
+ }
2133
+ const validSlots = {};
2134
+ for (const [slot, slotValue] of Object.entries(value)) {
2135
+ if (!slotSet.has(slot)) {
2136
+ warn(
2137
+ `Unknown slot '${slot}' in 'classNames'. Valid slots: ${ITI_SLOTS.join(", ")}. Skipping.`
2138
+ );
2139
+ } else if (typeof slotValue !== "string") {
2140
+ warnOption(`classNames.${slot}`, "a string", slotValue);
2141
+ } else {
2142
+ validSlots[slot] = slotValue.trim().replace(/\s+/g, " ");
2143
+ }
2144
+ }
2145
+ validatedOptions[key] = validSlots;
2146
+ break;
2147
+ }
2102
2148
  case "countryOrder": {
2103
2149
  if (value === null) {
2104
2150
  validatedOptions[key] = value;
@@ -2511,11 +2557,18 @@ var UI = class {
2511
2557
  );
2512
2558
  }
2513
2559
  }
2560
+ //* Append any consumer-supplied classes (via the classNames option) for the given slot to our own classes for that element.
2561
+ #withSlotClass(slot, ourClasses) {
2562
+ const custom = this.#options.classNames[slot];
2563
+ return custom ? `${ourClasses} ${custom}` : ourClasses;
2564
+ }
2514
2565
  //* Generate all of the markup for the core library: the selected country overlay, and the country selector.
2515
2566
  buildMarkup(countries, searchTokens) {
2516
2567
  this.#countries = countries;
2517
2568
  this.#searchTokens = searchTokens;
2518
- this.telInputEl.classList.add("iti__tel-input");
2569
+ this.telInputEl.classList.add(
2570
+ ...this.#withSlotClass("input", "iti__tel-input").split(" ")
2571
+ );
2519
2572
  if (!this.telInputEl.hasAttribute("type")) {
2520
2573
  this.telInputEl.setAttribute("type", "tel");
2521
2574
  }
@@ -2543,7 +2596,9 @@ var UI = class {
2543
2596
  "iti--inline-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN,
2544
2597
  [containerClass]: Boolean(containerClass)
2545
2598
  });
2546
- const wrapper = createEl("div", { class: parentClasses });
2599
+ const wrapper = createEl("div", {
2600
+ class: this.#withSlotClass("container", parentClasses)
2601
+ });
2547
2602
  if (this.#isRTL) {
2548
2603
  wrapper.setAttribute("dir", "ltr");
2549
2604
  }
@@ -2559,7 +2614,12 @@ var UI = class {
2559
2614
  this.#countryContainerEl = createEl(
2560
2615
  "div",
2561
2616
  // visibly hidden until we measure its width to set the input padding correctly
2562
- { class: `iti__country-container ${CLASSES.V_HIDE}` },
2617
+ {
2618
+ class: this.#withSlotClass(
2619
+ "countryContainer",
2620
+ `iti__country-container ${CLASSES.V_HIDE}`
2621
+ )
2622
+ },
2563
2623
  wrapper
2564
2624
  );
2565
2625
  if (enableCountrySelector) {
@@ -2567,7 +2627,7 @@ var UI = class {
2567
2627
  "button",
2568
2628
  {
2569
2629
  type: "button",
2570
- class: "iti__selected-country",
2630
+ class: this.#withSlotClass("selectedCountry", "iti__selected-country"),
2571
2631
  [ARIA.EXPANDED]: "false",
2572
2632
  [ARIA.LABEL]: this.#options.uiTranslations.noCountrySelected,
2573
2633
  [ARIA.HASPOPUP]: "dialog",
@@ -2581,31 +2641,44 @@ var UI = class {
2581
2641
  } else {
2582
2642
  this.#selectedCountryEl = createEl(
2583
2643
  "div",
2584
- { class: "iti__selected-country" },
2644
+ { class: this.#withSlotClass("selectedCountry", "iti__selected-country") },
2585
2645
  this.#countryContainerEl
2586
2646
  );
2587
2647
  }
2588
2648
  const selectedCountryPrimary = createEl(
2589
2649
  "div",
2590
- { class: "iti__selected-country-primary" },
2650
+ {
2651
+ class: this.#withSlotClass(
2652
+ "selectedCountryPrimary",
2653
+ "iti__selected-country-primary"
2654
+ )
2655
+ },
2591
2656
  this.#selectedCountryEl
2592
2657
  );
2593
2658
  this.#selectedFlagEl = createEl(
2594
2659
  "div",
2595
- { class: CLASSES.FLAG },
2660
+ { class: this.#withSlotClass("selectedFlag", CLASSES.FLAG) },
2596
2661
  selectedCountryPrimary
2597
2662
  );
2598
2663
  if (enableCountrySelector) {
2599
2664
  this.#arrowEl = createEl(
2600
2665
  "div",
2601
- { class: "iti__arrow", [ARIA.HIDDEN]: "true" },
2666
+ {
2667
+ class: this.#withSlotClass("arrow", "iti__arrow"),
2668
+ [ARIA.HIDDEN]: "true"
2669
+ },
2602
2670
  selectedCountryPrimary
2603
2671
  );
2604
2672
  }
2605
2673
  if (separateDialCode) {
2606
2674
  this.#selectedDialCodeEl = createEl(
2607
2675
  "div",
2608
- { class: "iti__selected-dial-code" },
2676
+ {
2677
+ class: this.#withSlotClass(
2678
+ "selectedDialCode",
2679
+ "iti__selected-dial-code"
2680
+ )
2681
+ },
2609
2682
  this.#selectedCountryEl
2610
2683
  );
2611
2684
  }
@@ -2636,7 +2709,10 @@ var UI = class {
2636
2709
  const extraClasses = matchDropdownWidth ? "" : "iti--flexible-dropdown-width";
2637
2710
  this.#countrySelectorEl = createEl("div", {
2638
2711
  id: `iti-${this.#id}__country-selector`,
2639
- class: `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`,
2712
+ class: this.#withSlotClass(
2713
+ "countrySelector",
2714
+ `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`
2715
+ ),
2640
2716
  role: "dialog",
2641
2717
  [ARIA.MODAL]: "true"
2642
2718
  });
@@ -2649,7 +2725,7 @@ var UI = class {
2649
2725
  this.#countryListEl = createEl(
2650
2726
  "ul",
2651
2727
  {
2652
- class: "iti__country-list",
2728
+ class: this.#withSlotClass("countryList", "iti__country-list"),
2653
2729
  id: `iti-${this.#id}__country-listbox`,
2654
2730
  role: "listbox",
2655
2731
  [ARIA.LABEL]: uiTranslations.countryListAriaLabel
@@ -2668,7 +2744,9 @@ var UI = class {
2668
2744
  "iti--inline-country-selector": !isFullscreen,
2669
2745
  [containerClass]: Boolean(containerClass)
2670
2746
  });
2671
- this.#detachedCountrySelectorEl = createEl("div", { class: wrapperClasses });
2747
+ this.#detachedCountrySelectorEl = createEl("div", {
2748
+ class: this.#withSlotClass("countrySelectorContainer", wrapperClasses)
2749
+ });
2672
2750
  this.#detachedCountrySelectorEl.appendChild(this.#countrySelectorEl);
2673
2751
  } else {
2674
2752
  this.#countryContainerEl.appendChild(this.#countrySelectorEl);
@@ -2689,13 +2767,13 @@ var UI = class {
2689
2767
  const { uiTranslations, searchInputClass } = this.#options;
2690
2768
  const searchWrapper = createEl(
2691
2769
  "div",
2692
- { class: "iti__search-input-wrapper" },
2770
+ { class: this.#withSlotClass("searchWrapper", "iti__search-input-wrapper") },
2693
2771
  this.#countrySelectorEl
2694
2772
  );
2695
2773
  this.#searchIconEl = createEl(
2696
2774
  "span",
2697
2775
  {
2698
- class: "iti__search-icon",
2776
+ class: this.#withSlotClass("searchIcon", "iti__search-icon"),
2699
2777
  [ARIA.HIDDEN]: "true"
2700
2778
  },
2701
2779
  searchWrapper
@@ -2707,7 +2785,10 @@ var UI = class {
2707
2785
  id: `iti-${this.#id}__search-input`,
2708
2786
  // Chrome says inputs need either a name or an id
2709
2787
  type: "search",
2710
- class: `iti__search-input ${searchInputClass}`,
2788
+ class: this.#withSlotClass(
2789
+ "searchInput",
2790
+ `iti__search-input ${searchInputClass}`
2791
+ ),
2711
2792
  placeholder: uiTranslations.searchPlaceholder,
2712
2793
  // 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
2713
2794
  role: "combobox",
@@ -2723,7 +2804,10 @@ var UI = class {
2723
2804
  "button",
2724
2805
  {
2725
2806
  type: "button",
2726
- class: `iti__search-clear ${CLASSES.HIDE}`,
2807
+ class: this.#withSlotClass(
2808
+ "searchClear",
2809
+ `iti__search-clear ${CLASSES.HIDE}`
2810
+ ),
2727
2811
  [ARIA.LABEL]: uiTranslations.clearSearchAriaLabel,
2728
2812
  tabindex: "-1"
2729
2813
  },
@@ -2738,7 +2822,7 @@ var UI = class {
2738
2822
  this.#noResultsMessageEl = createEl(
2739
2823
  "div",
2740
2824
  {
2741
- class: `iti__no-results ${CLASSES.HIDE}`,
2825
+ class: this.#withSlotClass("noResults", `iti__no-results ${CLASSES.HIDE}`),
2742
2826
  [ARIA.HIDDEN]: "true"
2743
2827
  // all a11y messaging happens in this.#searchResultsLiveRegionEl
2744
2828
  },
@@ -2792,11 +2876,9 @@ var UI = class {
2792
2876
  //* For each country: add a country list item <li> to the countryList <ul> container.
2793
2877
  #appendListItems() {
2794
2878
  const frag = document.createDocumentFragment();
2879
+ const liClass = this.#withSlotClass("countryListItem", CLASSES.COUNTRY_ITEM);
2795
2880
  for (let i = 0; i < this.#countries.length; i++) {
2796
2881
  const c = this.#countries[i];
2797
- const liClass = buildClassNames({
2798
- [CLASSES.COUNTRY_ITEM]: true
2799
- });
2800
2882
  const listItem = createEl("li", {
2801
2883
  id: `iti-${this.#id}__item-${c.iso2}`,
2802
2884
  class: liClass,
@@ -2808,11 +2890,28 @@ var UI = class {
2808
2890
  listItem.dataset[DATA_KEYS.ISO2] = c.iso2;
2809
2891
  this.#listItemByIso2.set(c.iso2, listItem);
2810
2892
  if (this.#options.showFlags) {
2811
- createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
2893
+ createEl(
2894
+ "div",
2895
+ {
2896
+ class: this.#withSlotClass(
2897
+ "countryListItemFlag",
2898
+ `${CLASSES.FLAG} iti__${c.iso2}`
2899
+ )
2900
+ },
2901
+ listItem
2902
+ );
2812
2903
  }
2813
- const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
2904
+ const nameEl = createEl(
2905
+ "span",
2906
+ { class: this.#withSlotClass("countryName", "iti__country-name") },
2907
+ listItem
2908
+ );
2814
2909
  nameEl.textContent = `${c.name} `;
2815
- const dialEl = createEl("span", { class: "iti__dial-code" }, nameEl);
2910
+ const dialEl = createEl(
2911
+ "span",
2912
+ { class: this.#withSlotClass("dialCode", "iti__dial-code") },
2913
+ nameEl
2914
+ );
2816
2915
  if (this.#isRTL) {
2817
2916
  dialEl.setAttribute("dir", "ltr");
2818
2917
  }
@@ -3259,7 +3358,10 @@ var UI = class {
3259
3358
  newListItem.setAttribute(ARIA.SELECTED, "true");
3260
3359
  const checkIcon = createEl(
3261
3360
  "span",
3262
- { class: "iti__country-check", [ARIA.HIDDEN]: "true" },
3361
+ {
3362
+ class: this.#withSlotClass("countryCheck", "iti__country-check"),
3363
+ [ARIA.HIDDEN]: "true"
3364
+ },
3263
3365
  newListItem
3264
3366
  );
3265
3367
  checkIcon.appendChild(buildCheckIcon());
@@ -3446,7 +3548,10 @@ var UI = class {
3446
3548
  this.#updateSelectedListItem(iso2);
3447
3549
  }
3448
3550
  if (this.#selectedCountryEl) {
3449
- const flagClass = iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`;
3551
+ const flagClass = this.#withSlotClass(
3552
+ "selectedFlag",
3553
+ iso2 && showFlags ? `${CLASSES.FLAG} iti__${iso2}` : `${CLASSES.FLAG} ${CLASSES.GLOBE}`
3554
+ );
3450
3555
  let ariaLabel, title;
3451
3556
  let flagContent = null;
3452
3557
  if (iso2) {
@@ -4843,7 +4948,7 @@ var intlTelInput = Object.assign(
4843
4948
  attachUtils,
4844
4949
  startedLoadingUtils: false,
4845
4950
  startedLoadingAutoCountry: false,
4846
- version: "29.1.3",
4951
+ version: "29.2.0",
4847
4952
  NUMBER_FORMAT,
4848
4953
  NUMBER_TYPE,
4849
4954
  VALIDATION_ERROR,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "intl-tel-input",
3
- "version": "29.1.3",
3
+ "version": "29.2.0",
4
4
  "description": "A JavaScript library for entering, formatting, and validating international telephone numbers",
5
5
  "type": "module",
6
6
  "license": "MIT",