intl-tel-input 25.7.0 → 25.8.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.
package/README.md CHANGED
@@ -77,16 +77,16 @@ _Note: We have now dropped support for all versions of Internet Explorer because
77
77
  ## Getting Started (Using a CDN)
78
78
  1. Add the CSS
79
79
  ```html
80
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@25.7.0/build/css/intlTelInput.css">
80
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/intl-tel-input@25.8.0/build/css/intlTelInput.css">
81
81
  ```
82
82
 
83
83
  2. Add the plugin script and initialise it on your input element
84
84
  ```html
85
- <script src="https://cdn.jsdelivr.net/npm/intl-tel-input@25.7.0/build/js/intlTelInput.min.js"></script>
85
+ <script src="https://cdn.jsdelivr.net/npm/intl-tel-input@25.8.0/build/js/intlTelInput.min.js"></script>
86
86
  <script>
87
87
  const input = document.querySelector("#phone");
88
88
  window.intlTelInput(input, {
89
- loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.7.0/build/js/utils.js"),
89
+ loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.8.0/build/js/utils.js"),
90
90
  });
91
91
  </script>
92
92
  ```
@@ -322,7 +322,7 @@ The `loadUtils` option takes a function which returns a Promise which resolves t
322
322
  ```js
323
323
  // (A) import utils module from a CDN
324
324
  intlTelInput(htmlInputElement, {
325
- loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.7.0/build/js/utils.js"),
325
+ loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.8.0/build/js/utils.js"),
326
326
  });
327
327
 
328
328
  // (B) import utils module from your own hosted version of utils.js
@@ -464,6 +464,12 @@ Change the selected country. It should be rare, if ever, that you need to do thi
464
464
  iti.setCountry("gb");
465
465
  ```
466
466
 
467
+ **setDisabled**
468
+ Updates the disabled attribute of both the telephone input and the selected country button. Accepts a boolean value. _Note: we recommend using this instead of updating the disabled attribute of the input directly._
469
+ ```js
470
+ iti.setDisabled(true);
471
+ ```
472
+
467
473
  **setNumber**
468
474
  Insert a number, and update the selected country accordingly. _Note that if `formatOnDisplay` is enabled, this will attempt to format the number to either national or international format according to the `nationalMode` option._
469
475
  ```js
@@ -476,12 +482,6 @@ Change the placeholderNumberType option.
476
482
  iti.setPlaceholderNumberType("FIXED_LINE");
477
483
  ```
478
484
 
479
- **setDisabled**
480
- Updates the disabled attribute of both the telephone input and the selected country button. Accepts a boolean value. _Note: we recommend using this instead of updating the disabled attribute of the input directly._
481
- ```js
482
- iti.setDisabled(true);
483
- ```
484
-
485
485
  ## Static Methods
486
486
 
487
487
  **getCountryData**
@@ -606,7 +606,7 @@ The `loadUtils` option takes a function which returns a Promise which resolves t
606
606
  ```js
607
607
  // (A) import utils module from a CDN
608
608
  intlTelInput(htmlInputElement, {
609
- loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.7.0/build/js/utils.js"),
609
+ loadUtils: () => import("https://cdn.jsdelivr.net/npm/intl-tel-input@25.8.0/build/js/utils.js"),
610
610
  });
611
611
 
612
612
  // (B) import utils module from your own hosted version of utils.js
package/angular/README.md CHANGED
@@ -29,7 +29,7 @@ import "intl-tel-input/styles";
29
29
 
30
30
  See the [Validation demo](https://github.com/jackocnr/intl-tel-input/blob/master/angular/demo/validation/validation.component.ts) for a more fleshed-out example of how to handle validation.
31
31
 
32
- A note on the utils script (~260KB): if you're lazy loading the IntlTelInput chunk (and so less worried about filesize) then you can just import IntlTelInput from `"intl-tel-input/angularWithUtils"`, to include the utils script. Alternatively, if you use the main `"intl-tel-input/angular"` import, then you should couple this with the `loadUtils` initialisation option - you will need to host the [utils.js](https://github.com/jackocnr/intl-tel-input/blob/master/build/js/utils.js) file, and then set the `loadUtils` option to that URL, or alternatively just point it to a CDN hosted version e.g. `"https://cdn.jsdelivr.net/npm/intl-tel-input@25.7.0/build/js/utils.js"`.
32
+ A note on the utils script (~260KB): if you're lazy loading the IntlTelInput chunk (and so less worried about filesize) then you can just import IntlTelInput from `"intl-tel-input/angularWithUtils"`, to include the utils script. Alternatively, if you use the main `"intl-tel-input/angular"` import, then you should couple this with the `loadUtils` initialisation option - you will need to host the [utils.js](https://github.com/jackocnr/intl-tel-input/blob/master/build/js/utils.js) file, and then set the `loadUtils` option to that URL, or alternatively just point it to a CDN hosted version e.g. `"https://cdn.jsdelivr.net/npm/intl-tel-input@25.8.0/build/js/utils.js"`.
33
33
 
34
34
  ## Props
35
35
  Here's a list of all of the current props you can pass to the IntlTelInput Angular component.
@@ -1592,6 +1592,7 @@ var interfaceTranslations = {
1592
1592
  noCountrySelected: "No country selected",
1593
1593
  countryListAriaLabel: "List of countries",
1594
1594
  searchPlaceholder: "Search",
1595
+ clearSearchAriaLabel: "Clear search",
1595
1596
  zeroSearchResults: "No results found",
1596
1597
  oneSearchResult: "1 result found",
1597
1598
  multipleSearchResults: "${count} results found",
@@ -1720,8 +1721,8 @@ var translateCursorPosition = (relevantChars, formattedValue, prevCaretPos, isDe
1720
1721
  }
1721
1722
  return formattedValue.length;
1722
1723
  };
1723
- var createEl = (name, attrs, container) => {
1724
- const el = document.createElement(name);
1724
+ var createEl = (tagName, attrs, container) => {
1725
+ const el = document.createElement(tagName);
1725
1726
  if (attrs) {
1726
1727
  Object.entries(attrs).forEach(([key, value]) => el.setAttribute(key, value));
1727
1728
  }
@@ -1734,7 +1735,14 @@ var forEachInstance = (method, ...args) => {
1734
1735
  const { instances } = intlTelInput;
1735
1736
  Object.values(instances).forEach((instance) => instance[method](...args));
1736
1737
  };
1737
- var Iti = class {
1738
+ var Iti = class _Iti {
1739
+ /**
1740
+ * Build a space-delimited class string from an object map of className -> truthy/falsey.
1741
+ * Only keys with truthy values are included.
1742
+ */
1743
+ static _buildClassNames(flags) {
1744
+ return Object.keys(flags).filter((k) => Boolean(flags[k])).join(" ");
1745
+ }
1738
1746
  constructor(input, customOptions = {}) {
1739
1747
  this.id = id++;
1740
1748
  this.telInput = input;
@@ -1901,20 +1909,14 @@ var Iti = class {
1901
1909
  this.telInput.setAttribute("autocomplete", "off");
1902
1910
  }
1903
1911
  const { allowDropdown, separateDialCode, showFlags, containerClass, hiddenInput, dropdownContainer, fixDropdownWidth, useFullscreenPopup, countrySearch, i18n } = this.options;
1904
- let parentClass = "iti";
1905
- if (allowDropdown) {
1906
- parentClass += " iti--allow-dropdown";
1907
- }
1908
- if (showFlags) {
1909
- parentClass += " iti--show-flags";
1910
- }
1911
- if (containerClass) {
1912
- parentClass += ` ${containerClass}`;
1913
- }
1914
- if (!useFullscreenPopup) {
1915
- parentClass += " iti--inline-dropdown";
1916
- }
1917
- const wrapper = createEl("div", { class: parentClass });
1912
+ const parentClasses = _Iti._buildClassNames({
1913
+ "iti": true,
1914
+ "iti--allow-dropdown": allowDropdown,
1915
+ "iti--show-flags": showFlags,
1916
+ "iti--inline-dropdown": !useFullscreenPopup,
1917
+ [containerClass]: Boolean(containerClass)
1918
+ });
1919
+ const wrapper = createEl("div", { class: parentClasses });
1918
1920
  (_a = this.telInput.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(wrapper, this.telInput);
1919
1921
  if (allowDropdown || showFlags || separateDialCode) {
1920
1922
  this.countryContainer = createEl("div", { class: "iti__country-container" }, wrapper);
@@ -1929,9 +1931,8 @@ var Iti = class {
1929
1931
  class: "iti__selected-country",
1930
1932
  "aria-expanded": "false",
1931
1933
  "aria-label": this.options.i18n.selectedCountryAriaLabel,
1932
- "aria-haspopup": "true",
1933
- "aria-controls": `iti-${this.id}__dropdown-content`,
1934
- "role": "combobox"
1934
+ "aria-haspopup": "dialog",
1935
+ "aria-controls": `iti-${this.id}__dropdown-content`
1935
1936
  }, this.countryContainer);
1936
1937
  if (this.telInput.disabled) {
1937
1938
  this.selectedCountry.setAttribute("disabled", "true");
@@ -1952,21 +1953,57 @@ var Iti = class {
1952
1953
  const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
1953
1954
  this.dropdownContent = createEl("div", {
1954
1955
  id: `iti-${this.id}__dropdown-content`,
1955
- class: `iti__dropdown-content iti__hide ${extraClasses}`
1956
+ class: `iti__dropdown-content iti__hide ${extraClasses}`,
1957
+ role: "dialog",
1958
+ "aria-modal": "true"
1956
1959
  });
1957
1960
  if (countrySearch) {
1961
+ const searchWrapper = createEl("div", { class: "iti__search-input-wrapper" }, this.dropdownContent);
1962
+ this.searchIcon = createEl("span", {
1963
+ class: "iti__search-icon",
1964
+ "aria-hidden": "true"
1965
+ }, searchWrapper);
1966
+ this.searchIcon.innerHTML = `
1967
+ <svg class="iti__search-icon-svg" width="14" height="14" viewBox="0 0 24 24" focusable="false" aria-hidden="true">
1968
+ <circle cx="11" cy="11" r="7" />
1969
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
1970
+ </svg>`;
1958
1971
  this.searchInput = createEl("input", {
1959
- type: "text",
1972
+ id: `iti-${this.id}__search-input`,
1973
+ // Chrome says inputs need either a name or an id
1974
+ type: "search",
1960
1975
  class: "iti__search-input",
1961
1976
  placeholder: i18n.searchPlaceholder,
1977
+ // 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
1962
1978
  role: "combobox",
1963
1979
  "aria-expanded": "true",
1964
1980
  "aria-label": i18n.searchPlaceholder,
1965
1981
  "aria-controls": `iti-${this.id}__country-listbox`,
1966
1982
  "aria-autocomplete": "list",
1967
1983
  "autocomplete": "off"
1968
- }, this.dropdownContent);
1984
+ }, searchWrapper);
1985
+ this.searchClearButton = createEl("button", {
1986
+ type: "button",
1987
+ class: "iti__search-clear iti__hide",
1988
+ "aria-label": i18n.clearSearchAriaLabel,
1989
+ tabindex: "-1"
1990
+ }, searchWrapper);
1991
+ const maskId = `iti-${this.id}-clear-mask`;
1992
+ this.searchClearButton.innerHTML = `
1993
+ <svg class="iti__search-clear-svg" width="12" height="12" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
1994
+ <mask id="${maskId}" maskUnits="userSpaceOnUse">
1995
+ <rect width="16" height="16" fill="white" />
1996
+ <path d="M5.2 5.2 L10.8 10.8 M10.8 5.2 L5.2 10.8" stroke="black" stroke-linecap="round" class="iti__search-clear-x" />
1997
+ </mask>
1998
+ <circle cx="8" cy="8" r="8" class="iti__search-clear-bg" mask="url(#${maskId})" />
1999
+ </svg>`;
1969
2000
  this.searchResultsA11yText = createEl("span", { class: "iti__a11y-text" }, this.dropdownContent);
2001
+ this.searchNoResults = createEl("div", {
2002
+ class: "iti__no-results iti__hide",
2003
+ "aria-hidden": "true"
2004
+ // all a11y messaging happens in this.searchResultsA11yText
2005
+ }, this.dropdownContent);
2006
+ this.searchNoResults.textContent = i18n.zeroSearchResults;
1970
2007
  }
1971
2008
  this.countryList = createEl("ul", {
1972
2009
  class: "iti__country-list",
@@ -1976,18 +2013,16 @@ var Iti = class {
1976
2013
  }, this.dropdownContent);
1977
2014
  this._appendListItems();
1978
2015
  if (countrySearch) {
1979
- this._updateSearchResultsText();
2016
+ this._updateSearchResultsA11yText();
1980
2017
  }
1981
2018
  if (dropdownContainer) {
1982
- let dropdownClasses = "iti iti--container";
1983
- if (containerClass) {
1984
- dropdownClasses += ` ${containerClass}`;
1985
- }
1986
- if (useFullscreenPopup) {
1987
- dropdownClasses += " iti--fullscreen-popup";
1988
- } else {
1989
- dropdownClasses += " iti--inline-dropdown";
1990
- }
2019
+ const dropdownClasses = _Iti._buildClassNames({
2020
+ "iti": true,
2021
+ "iti--container": true,
2022
+ "iti--fullscreen-popup": useFullscreenPopup,
2023
+ "iti--inline-dropdown": !useFullscreenPopup,
2024
+ [containerClass]: Boolean(containerClass)
2025
+ });
1991
2026
  this.dropdown = createEl("div", { class: dropdownClasses });
1992
2027
  this.dropdown.appendChild(this.dropdownContent);
1993
2028
  } else {
@@ -2370,6 +2405,11 @@ var Iti = class {
2370
2405
  } else {
2371
2406
  this._filterCountries("", true);
2372
2407
  }
2408
+ if (this.searchInput.value) {
2409
+ this.searchClearButton.classList.remove("iti__hide");
2410
+ } else {
2411
+ this.searchClearButton.classList.add("iti__hide");
2412
+ }
2373
2413
  };
2374
2414
  let keyupTimer = null;
2375
2415
  this._handleSearchChange = () => {
@@ -2382,6 +2422,13 @@ var Iti = class {
2382
2422
  }, 100);
2383
2423
  };
2384
2424
  this.searchInput.addEventListener("input", this._handleSearchChange);
2425
+ this._handleSearchClear = (e) => {
2426
+ e.stopPropagation();
2427
+ this.searchInput.value = "";
2428
+ this.searchInput.focus();
2429
+ doFilter();
2430
+ };
2431
+ this.searchClearButton.addEventListener("click", this._handleSearchClear);
2385
2432
  this.searchInput.addEventListener("click", (e) => e.stopPropagation());
2386
2433
  }
2387
2434
  }
@@ -2446,12 +2493,17 @@ var Iti = class {
2446
2493
  }
2447
2494
  if (noCountriesAddedYet) {
2448
2495
  this._highlightListItem(null, false);
2496
+ if (this.searchNoResults) {
2497
+ this.searchNoResults.classList.remove("iti__hide");
2498
+ }
2499
+ } else if (this.searchNoResults) {
2500
+ this.searchNoResults.classList.add("iti__hide");
2449
2501
  }
2450
2502
  this.countryList.scrollTop = 0;
2451
- this._updateSearchResultsText();
2503
+ this._updateSearchResultsA11yText();
2452
2504
  }
2453
2505
  //* Update search results text (for a11y).
2454
- _updateSearchResultsText() {
2506
+ _updateSearchResultsA11yText() {
2455
2507
  const { i18n } = this.options;
2456
2508
  const count = this.countryList.childElementCount;
2457
2509
  let searchText;
@@ -2563,9 +2615,8 @@ var Iti = class {
2563
2615
  if (this.highlightedItem) {
2564
2616
  this.highlightedItem.classList.add("iti__highlight");
2565
2617
  this.highlightedItem.setAttribute("aria-selected", "true");
2566
- const activeDescendant = this.highlightedItem.getAttribute("id") || "";
2567
- this.selectedCountry.setAttribute("aria-activedescendant", activeDescendant);
2568
2618
  if (this.options.countrySearch) {
2619
+ const activeDescendant = this.highlightedItem.getAttribute("id") || "";
2569
2620
  this.searchInput.setAttribute("aria-activedescendant", activeDescendant);
2570
2621
  }
2571
2622
  }
@@ -2713,7 +2764,6 @@ var Iti = class {
2713
2764
  _closeDropdown() {
2714
2765
  this.dropdownContent.classList.add("iti__hide");
2715
2766
  this.selectedCountry.setAttribute("aria-expanded", "false");
2716
- this.selectedCountry.removeAttribute("aria-activedescendant");
2717
2767
  if (this.highlightedItem) {
2718
2768
  this.highlightedItem.setAttribute("aria-selected", "false");
2719
2769
  }
@@ -2721,9 +2771,9 @@ var Iti = class {
2721
2771
  this.searchInput.removeAttribute("aria-activedescendant");
2722
2772
  }
2723
2773
  this.dropdownArrow.classList.remove("iti__arrow--up");
2724
- document.removeEventListener("keydown", this._handleKeydownOnDropdown);
2725
2774
  if (this.options.countrySearch) {
2726
2775
  this.searchInput.removeEventListener("input", this._handleSearchChange);
2776
+ this.searchClearButton.removeEventListener("click", this._handleSearchClear);
2727
2777
  }
2728
2778
  document.documentElement.removeEventListener("click", this._handleClickOffToClose);
2729
2779
  this.countryList.removeEventListener("mouseover", this._handleMouseoverCountryList);
@@ -3058,7 +3108,7 @@ var intlTelInput = Object.assign((input, options) => {
3058
3108
  attachUtils,
3059
3109
  startedLoadingUtilsScript: false,
3060
3110
  startedLoadingAutoCountry: false,
3061
- version: "25.7.0"
3111
+ version: "25.8.0"
3062
3112
  });
3063
3113
  var intl_tel_input_default = intlTelInput;
3064
3114
 
@@ -1592,6 +1592,7 @@ var interfaceTranslations = {
1592
1592
  noCountrySelected: "No country selected",
1593
1593
  countryListAriaLabel: "List of countries",
1594
1594
  searchPlaceholder: "Search",
1595
+ clearSearchAriaLabel: "Clear search",
1595
1596
  zeroSearchResults: "No results found",
1596
1597
  oneSearchResult: "1 result found",
1597
1598
  multipleSearchResults: "${count} results found",
@@ -1720,8 +1721,8 @@ var translateCursorPosition = (relevantChars, formattedValue, prevCaretPos, isDe
1720
1721
  }
1721
1722
  return formattedValue.length;
1722
1723
  };
1723
- var createEl = (name, attrs, container) => {
1724
- const el = document.createElement(name);
1724
+ var createEl = (tagName, attrs, container) => {
1725
+ const el = document.createElement(tagName);
1725
1726
  if (attrs) {
1726
1727
  Object.entries(attrs).forEach(([key, value]) => el.setAttribute(key, value));
1727
1728
  }
@@ -1734,7 +1735,14 @@ var forEachInstance = (method, ...args) => {
1734
1735
  const { instances } = intlTelInput;
1735
1736
  Object.values(instances).forEach((instance) => instance[method](...args));
1736
1737
  };
1737
- var Iti = class {
1738
+ var Iti = class _Iti {
1739
+ /**
1740
+ * Build a space-delimited class string from an object map of className -> truthy/falsey.
1741
+ * Only keys with truthy values are included.
1742
+ */
1743
+ static _buildClassNames(flags) {
1744
+ return Object.keys(flags).filter((k) => Boolean(flags[k])).join(" ");
1745
+ }
1738
1746
  constructor(input, customOptions = {}) {
1739
1747
  this.id = id++;
1740
1748
  this.telInput = input;
@@ -1901,20 +1909,14 @@ var Iti = class {
1901
1909
  this.telInput.setAttribute("autocomplete", "off");
1902
1910
  }
1903
1911
  const { allowDropdown, separateDialCode, showFlags, containerClass, hiddenInput, dropdownContainer, fixDropdownWidth, useFullscreenPopup, countrySearch, i18n } = this.options;
1904
- let parentClass = "iti";
1905
- if (allowDropdown) {
1906
- parentClass += " iti--allow-dropdown";
1907
- }
1908
- if (showFlags) {
1909
- parentClass += " iti--show-flags";
1910
- }
1911
- if (containerClass) {
1912
- parentClass += ` ${containerClass}`;
1913
- }
1914
- if (!useFullscreenPopup) {
1915
- parentClass += " iti--inline-dropdown";
1916
- }
1917
- const wrapper = createEl("div", { class: parentClass });
1912
+ const parentClasses = _Iti._buildClassNames({
1913
+ "iti": true,
1914
+ "iti--allow-dropdown": allowDropdown,
1915
+ "iti--show-flags": showFlags,
1916
+ "iti--inline-dropdown": !useFullscreenPopup,
1917
+ [containerClass]: Boolean(containerClass)
1918
+ });
1919
+ const wrapper = createEl("div", { class: parentClasses });
1918
1920
  (_a = this.telInput.parentNode) === null || _a === void 0 ? void 0 : _a.insertBefore(wrapper, this.telInput);
1919
1921
  if (allowDropdown || showFlags || separateDialCode) {
1920
1922
  this.countryContainer = createEl("div", { class: "iti__country-container" }, wrapper);
@@ -1929,9 +1931,8 @@ var Iti = class {
1929
1931
  class: "iti__selected-country",
1930
1932
  "aria-expanded": "false",
1931
1933
  "aria-label": this.options.i18n.selectedCountryAriaLabel,
1932
- "aria-haspopup": "true",
1933
- "aria-controls": `iti-${this.id}__dropdown-content`,
1934
- "role": "combobox"
1934
+ "aria-haspopup": "dialog",
1935
+ "aria-controls": `iti-${this.id}__dropdown-content`
1935
1936
  }, this.countryContainer);
1936
1937
  if (this.telInput.disabled) {
1937
1938
  this.selectedCountry.setAttribute("disabled", "true");
@@ -1952,21 +1953,57 @@ var Iti = class {
1952
1953
  const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
1953
1954
  this.dropdownContent = createEl("div", {
1954
1955
  id: `iti-${this.id}__dropdown-content`,
1955
- class: `iti__dropdown-content iti__hide ${extraClasses}`
1956
+ class: `iti__dropdown-content iti__hide ${extraClasses}`,
1957
+ role: "dialog",
1958
+ "aria-modal": "true"
1956
1959
  });
1957
1960
  if (countrySearch) {
1961
+ const searchWrapper = createEl("div", { class: "iti__search-input-wrapper" }, this.dropdownContent);
1962
+ this.searchIcon = createEl("span", {
1963
+ class: "iti__search-icon",
1964
+ "aria-hidden": "true"
1965
+ }, searchWrapper);
1966
+ this.searchIcon.innerHTML = `
1967
+ <svg class="iti__search-icon-svg" width="14" height="14" viewBox="0 0 24 24" focusable="false" aria-hidden="true">
1968
+ <circle cx="11" cy="11" r="7" />
1969
+ <line x1="21" y1="21" x2="16.65" y2="16.65" />
1970
+ </svg>`;
1958
1971
  this.searchInput = createEl("input", {
1959
- type: "text",
1972
+ id: `iti-${this.id}__search-input`,
1973
+ // Chrome says inputs need either a name or an id
1974
+ type: "search",
1960
1975
  class: "iti__search-input",
1961
1976
  placeholder: i18n.searchPlaceholder,
1977
+ // 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
1962
1978
  role: "combobox",
1963
1979
  "aria-expanded": "true",
1964
1980
  "aria-label": i18n.searchPlaceholder,
1965
1981
  "aria-controls": `iti-${this.id}__country-listbox`,
1966
1982
  "aria-autocomplete": "list",
1967
1983
  "autocomplete": "off"
1968
- }, this.dropdownContent);
1984
+ }, searchWrapper);
1985
+ this.searchClearButton = createEl("button", {
1986
+ type: "button",
1987
+ class: "iti__search-clear iti__hide",
1988
+ "aria-label": i18n.clearSearchAriaLabel,
1989
+ tabindex: "-1"
1990
+ }, searchWrapper);
1991
+ const maskId = `iti-${this.id}-clear-mask`;
1992
+ this.searchClearButton.innerHTML = `
1993
+ <svg class="iti__search-clear-svg" width="12" height="12" viewBox="0 0 16 16" aria-hidden="true" focusable="false">
1994
+ <mask id="${maskId}" maskUnits="userSpaceOnUse">
1995
+ <rect width="16" height="16" fill="white" />
1996
+ <path d="M5.2 5.2 L10.8 10.8 M10.8 5.2 L5.2 10.8" stroke="black" stroke-linecap="round" class="iti__search-clear-x" />
1997
+ </mask>
1998
+ <circle cx="8" cy="8" r="8" class="iti__search-clear-bg" mask="url(#${maskId})" />
1999
+ </svg>`;
1969
2000
  this.searchResultsA11yText = createEl("span", { class: "iti__a11y-text" }, this.dropdownContent);
2001
+ this.searchNoResults = createEl("div", {
2002
+ class: "iti__no-results iti__hide",
2003
+ "aria-hidden": "true"
2004
+ // all a11y messaging happens in this.searchResultsA11yText
2005
+ }, this.dropdownContent);
2006
+ this.searchNoResults.textContent = i18n.zeroSearchResults;
1970
2007
  }
1971
2008
  this.countryList = createEl("ul", {
1972
2009
  class: "iti__country-list",
@@ -1976,18 +2013,16 @@ var Iti = class {
1976
2013
  }, this.dropdownContent);
1977
2014
  this._appendListItems();
1978
2015
  if (countrySearch) {
1979
- this._updateSearchResultsText();
2016
+ this._updateSearchResultsA11yText();
1980
2017
  }
1981
2018
  if (dropdownContainer) {
1982
- let dropdownClasses = "iti iti--container";
1983
- if (containerClass) {
1984
- dropdownClasses += ` ${containerClass}`;
1985
- }
1986
- if (useFullscreenPopup) {
1987
- dropdownClasses += " iti--fullscreen-popup";
1988
- } else {
1989
- dropdownClasses += " iti--inline-dropdown";
1990
- }
2019
+ const dropdownClasses = _Iti._buildClassNames({
2020
+ "iti": true,
2021
+ "iti--container": true,
2022
+ "iti--fullscreen-popup": useFullscreenPopup,
2023
+ "iti--inline-dropdown": !useFullscreenPopup,
2024
+ [containerClass]: Boolean(containerClass)
2025
+ });
1991
2026
  this.dropdown = createEl("div", { class: dropdownClasses });
1992
2027
  this.dropdown.appendChild(this.dropdownContent);
1993
2028
  } else {
@@ -2370,6 +2405,11 @@ var Iti = class {
2370
2405
  } else {
2371
2406
  this._filterCountries("", true);
2372
2407
  }
2408
+ if (this.searchInput.value) {
2409
+ this.searchClearButton.classList.remove("iti__hide");
2410
+ } else {
2411
+ this.searchClearButton.classList.add("iti__hide");
2412
+ }
2373
2413
  };
2374
2414
  let keyupTimer = null;
2375
2415
  this._handleSearchChange = () => {
@@ -2382,6 +2422,13 @@ var Iti = class {
2382
2422
  }, 100);
2383
2423
  };
2384
2424
  this.searchInput.addEventListener("input", this._handleSearchChange);
2425
+ this._handleSearchClear = (e) => {
2426
+ e.stopPropagation();
2427
+ this.searchInput.value = "";
2428
+ this.searchInput.focus();
2429
+ doFilter();
2430
+ };
2431
+ this.searchClearButton.addEventListener("click", this._handleSearchClear);
2385
2432
  this.searchInput.addEventListener("click", (e) => e.stopPropagation());
2386
2433
  }
2387
2434
  }
@@ -2446,12 +2493,17 @@ var Iti = class {
2446
2493
  }
2447
2494
  if (noCountriesAddedYet) {
2448
2495
  this._highlightListItem(null, false);
2496
+ if (this.searchNoResults) {
2497
+ this.searchNoResults.classList.remove("iti__hide");
2498
+ }
2499
+ } else if (this.searchNoResults) {
2500
+ this.searchNoResults.classList.add("iti__hide");
2449
2501
  }
2450
2502
  this.countryList.scrollTop = 0;
2451
- this._updateSearchResultsText();
2503
+ this._updateSearchResultsA11yText();
2452
2504
  }
2453
2505
  //* Update search results text (for a11y).
2454
- _updateSearchResultsText() {
2506
+ _updateSearchResultsA11yText() {
2455
2507
  const { i18n } = this.options;
2456
2508
  const count = this.countryList.childElementCount;
2457
2509
  let searchText;
@@ -2563,9 +2615,8 @@ var Iti = class {
2563
2615
  if (this.highlightedItem) {
2564
2616
  this.highlightedItem.classList.add("iti__highlight");
2565
2617
  this.highlightedItem.setAttribute("aria-selected", "true");
2566
- const activeDescendant = this.highlightedItem.getAttribute("id") || "";
2567
- this.selectedCountry.setAttribute("aria-activedescendant", activeDescendant);
2568
2618
  if (this.options.countrySearch) {
2619
+ const activeDescendant = this.highlightedItem.getAttribute("id") || "";
2569
2620
  this.searchInput.setAttribute("aria-activedescendant", activeDescendant);
2570
2621
  }
2571
2622
  }
@@ -2713,7 +2764,6 @@ var Iti = class {
2713
2764
  _closeDropdown() {
2714
2765
  this.dropdownContent.classList.add("iti__hide");
2715
2766
  this.selectedCountry.setAttribute("aria-expanded", "false");
2716
- this.selectedCountry.removeAttribute("aria-activedescendant");
2717
2767
  if (this.highlightedItem) {
2718
2768
  this.highlightedItem.setAttribute("aria-selected", "false");
2719
2769
  }
@@ -2721,9 +2771,9 @@ var Iti = class {
2721
2771
  this.searchInput.removeAttribute("aria-activedescendant");
2722
2772
  }
2723
2773
  this.dropdownArrow.classList.remove("iti__arrow--up");
2724
- document.removeEventListener("keydown", this._handleKeydownOnDropdown);
2725
2774
  if (this.options.countrySearch) {
2726
2775
  this.searchInput.removeEventListener("input", this._handleSearchChange);
2776
+ this.searchClearButton.removeEventListener("click", this._handleSearchClear);
2727
2777
  }
2728
2778
  document.documentElement.removeEventListener("click", this._handleClickOffToClose);
2729
2779
  this.countryList.removeEventListener("mouseover", this._handleMouseoverCountryList);
@@ -3058,7 +3108,7 @@ var intlTelInput = Object.assign((input, options) => {
3058
3108
  attachUtils,
3059
3109
  startedLoadingUtilsScript: false,
3060
3110
  startedLoadingAutoCountry: false,
3061
- version: "25.7.0"
3111
+ version: "25.8.0"
3062
3112
  });
3063
3113
  var intl_tel_input_default = intlTelInput;
3064
3114
 
@@ -245,6 +245,7 @@ export type I18n = {
245
245
  ax?: string;
246
246
  selectedCountryAriaLabel?: string;
247
247
  searchPlaceholder?: string;
248
+ clearSearchAriaLabel?: string;
248
249
  countryListAriaLabel?: string;
249
250
  oneSearchResult?: string;
250
251
  multipleSearchResults?: string;
@@ -104,6 +104,9 @@ export declare class Iti {
104
104
  private dropdownArrow;
105
105
  private dropdownContent;
106
106
  private searchInput;
107
+ private searchIcon;
108
+ private searchClearButton;
109
+ private searchNoResults;
107
110
  private searchResultsA11yText;
108
111
  private countryList;
109
112
  private dropdown;
@@ -125,11 +128,17 @@ export declare class Iti {
125
128
  private _handleClickOffToClose;
126
129
  private _handleKeydownOnDropdown;
127
130
  private _handleSearchChange;
131
+ private _handleSearchClear;
128
132
  private _handlePageLoad;
129
133
  private resolveAutoCountryPromise;
130
134
  private rejectAutoCountryPromise;
131
135
  private resolveUtilsScriptPromise;
132
136
  private rejectUtilsScriptPromise;
137
+ /**
138
+ * Build a space-delimited class string from an object map of className -> truthy/falsey.
139
+ * Only keys with truthy values are included.
140
+ */
141
+ private static _buildClassNames;
133
142
  constructor(input: HTMLInputElement, customOptions?: SomeOptions);
134
143
  _init(): void;
135
144
  private _processCountryData;
@@ -156,7 +165,7 @@ export declare class Iti {
156
165
  private _bindDropdownListeners;
157
166
  private _searchForCountry;
158
167
  private _filterCountries;
159
- private _updateSearchResultsText;
168
+ private _updateSearchResultsA11yText;
160
169
  private _handleUpDownKey;
161
170
  private _handleEnterKey;
162
171
  private _updateValFromNumber;