intl-tel-input 28.0.9 → 29.0.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.
Files changed (73) hide show
  1. package/dist/css/intlTelInput-no-assets.css +20 -11
  2. package/dist/css/intlTelInput-no-assets.min.css +1 -1
  3. package/dist/css/intlTelInput.css +20 -11
  4. package/dist/css/intlTelInput.min.css +1 -1
  5. package/dist/js/data.js +1 -1
  6. package/dist/js/data.min.js +1 -1
  7. package/dist/js/intlTelInput.d.ts +38 -23
  8. package/dist/js/intlTelInput.js +392 -324
  9. package/dist/js/intlTelInput.min.js +2 -2
  10. package/dist/js/intlTelInput.mjs +391 -323
  11. package/dist/js/intlTelInputWithUtils.js +680 -614
  12. package/dist/js/intlTelInputWithUtils.min.js +2 -2
  13. package/dist/js/intlTelInputWithUtils.mjs +679 -613
  14. package/dist/js/{i18n → locale}/ar.js +3 -3
  15. package/dist/js/{i18n → locale}/bg.js +2 -2
  16. package/dist/js/{i18n → locale}/bn.js +2 -2
  17. package/dist/js/{i18n → locale}/bs.js +2 -2
  18. package/dist/js/{i18n → locale}/ca.js +2 -2
  19. package/dist/js/{i18n → locale}/cs.js +2 -2
  20. package/dist/js/{i18n → locale}/da.js +2 -2
  21. package/dist/js/{i18n → locale}/de.js +2 -2
  22. package/dist/js/{i18n → locale}/el.js +2 -2
  23. package/dist/js/{i18n → locale}/es.js +2 -2
  24. package/dist/js/{i18n → locale}/et.js +2 -2
  25. package/dist/js/{i18n → locale}/fa.js +2 -5
  26. package/dist/js/{i18n → locale}/fi.js +2 -2
  27. package/dist/js/locale/fil.js +16 -0
  28. package/dist/js/{i18n → locale}/fr.js +2 -2
  29. package/dist/js/locale/he.js +19 -0
  30. package/dist/js/{i18n → locale}/hi.js +2 -2
  31. package/dist/js/{i18n → locale}/hr.js +2 -2
  32. package/dist/js/{i18n → locale}/hu.js +2 -5
  33. package/dist/js/locale/hy.js +19 -0
  34. package/dist/js/{i18n → locale}/id.js +2 -5
  35. package/dist/js/{i18n → locale}/index.js +9 -0
  36. package/dist/js/locale/is.js +21 -0
  37. package/dist/js/{i18n → locale}/it.js +2 -2
  38. package/dist/js/{i18n → locale}/ja.js +2 -5
  39. package/dist/js/{i18n → locale}/kn.js +2 -2
  40. package/dist/js/{i18n → locale}/ko.js +2 -5
  41. package/dist/js/{i18n → locale}/lt.js +2 -2
  42. package/dist/js/locale/lv.js +24 -0
  43. package/dist/js/locale/mk.js +21 -0
  44. package/dist/js/{i18n → locale}/mr.js +2 -2
  45. package/dist/js/locale/ms.js +16 -0
  46. package/dist/js/{i18n → locale}/nl.js +2 -2
  47. package/dist/js/{i18n → locale}/no.js +2 -2
  48. package/dist/js/{i18n → locale}/pl.js +2 -2
  49. package/dist/js/{i18n → locale}/pt.js +2 -2
  50. package/dist/js/{i18n → locale}/ro.js +4 -4
  51. package/dist/js/{i18n → locale}/ru.js +2 -2
  52. package/dist/js/{i18n → locale}/sk.js +5 -8
  53. package/dist/js/{i18n → locale}/sl.js +2 -2
  54. package/dist/js/{i18n → locale}/sq.js +2 -2
  55. package/dist/js/{i18n → locale}/sr.js +2 -2
  56. package/dist/js/{i18n → locale}/sv.js +2 -5
  57. package/dist/js/locale/sw.js +19 -0
  58. package/dist/js/locale/ta.js +19 -0
  59. package/dist/js/{i18n → locale}/te.js +2 -2
  60. package/dist/js/{i18n → locale}/th.js +2 -5
  61. package/dist/js/{i18n → locale}/tr.js +2 -5
  62. package/dist/js/{i18n → locale}/uk.js +2 -2
  63. package/dist/js/{i18n → locale}/ur.js +2 -2
  64. package/dist/js/{i18n → locale}/uz.js +2 -5
  65. package/dist/js/{i18n → locale}/vi.js +2 -5
  66. package/dist/js/{i18n → locale}/zh-hk.js +2 -5
  67. package/dist/js/{i18n → locale}/zh.js +2 -5
  68. package/dist/js/locale.d.ts +124 -0
  69. package/dist/js/utils.js +51 -51
  70. package/package.json +7 -7
  71. package/dist/js/i18n.d.ts +0 -106
  72. /package/dist/js/{i18n → locale}/en.js +0 -0
  73. /package/dist/js/{i18n → locale}/types.js +0 -0
@@ -1752,8 +1752,8 @@ var data_default = allCountries;
1752
1752
 
1753
1753
  // packages/core/src/js/constants.ts
1754
1754
  var EVENTS = {
1755
- OPEN_COUNTRY_DROPDOWN: "open:countrydropdown",
1756
- CLOSE_COUNTRY_DROPDOWN: "close:countrydropdown",
1755
+ OPEN_COUNTRY_SELECTOR: "open:countryselector",
1756
+ CLOSE_COUNTRY_SELECTOR: "close:countryselector",
1757
1757
  COUNTRY_CHANGE: "countrychange",
1758
1758
  INPUT: "input",
1759
1759
  // used for synthetic input trigger
@@ -1767,7 +1767,8 @@ var CLASSES = {
1767
1767
  FLAG: "iti__flag",
1768
1768
  LOADING: "iti__loading",
1769
1769
  COUNTRY_ITEM: "iti__country",
1770
- HIGHLIGHT: "iti__highlight"
1770
+ HIGHLIGHT: "iti__highlight",
1771
+ STRICT_REJECT_ANIMATION: "iti__strict-reject-animation"
1771
1772
  };
1772
1773
  var KEYS = {
1773
1774
  ARROW_UP: "ArrowUp",
@@ -1799,9 +1800,9 @@ var TIMINGS = {
1799
1800
  var LAYOUT = {
1800
1801
  NARROW_VIEWPORT_WIDTH: 500,
1801
1802
  // keep in sync with .iti__country-list CSS media query
1802
- FALLBACK_SELECTED_WITH_DIAL_WIDTH: 78,
1803
+ FALLBACK_SELECTED_COUNTRY_WITH_DIAL_WIDTH: 78,
1803
1804
  // px width fallback when separateDialCode enabled
1804
- FALLBACK_SELECTED_NO_DIAL_WIDTH: 42,
1805
+ FALLBACK_SELECTED_COUNTRY_NO_DIAL_WIDTH: 42,
1805
1806
  // px width fallback when no separate dial code
1806
1807
  INPUT_PADDING_EXTRA_LEFT: 6,
1807
1808
  // px gap between selected country container and input text
@@ -1829,14 +1830,17 @@ var US = {
1829
1830
  DIAL_CODE: "1"
1830
1831
  // +1 United States
1831
1832
  };
1832
- var PLACEHOLDER_MODES = {
1833
- AGGRESSIVE: "aggressive",
1834
- POLITE: "polite",
1835
- OFF: "off"
1836
- };
1837
- var INITIAL_COUNTRY = {
1838
- AUTO: "auto"
1833
+ var PLACEHOLDER_POLICY = {
1834
+ AGGRESSIVE: "AGGRESSIVE",
1835
+ POLITE: "POLITE",
1836
+ OFF: "OFF"
1839
1837
  };
1838
+ var COUNTRY_SELECTOR_MODES = [
1839
+ "OFF",
1840
+ "DROPDOWN",
1841
+ "FULLSCREEN",
1842
+ "AUTO"
1843
+ ];
1840
1844
  var NUMBER_FORMATS = [
1841
1845
  "E164",
1842
1846
  "INTERNATIONAL",
@@ -1869,8 +1873,9 @@ var toEnumObject = (arr) => Object.fromEntries(arr.map((v) => [v, v]));
1869
1873
  var NUMBER_FORMAT = toEnumObject(NUMBER_FORMATS);
1870
1874
  var NUMBER_TYPE = toEnumObject(NUMBER_TYPES);
1871
1875
  var VALIDATION_ERROR = toEnumObject(VALIDATION_ERRORS);
1876
+ var COUNTRY_SELECTOR_MODE = toEnumObject(COUNTRY_SELECTOR_MODES);
1872
1877
  var DATA_KEYS = {
1873
- // e.g. <li data-iso2="us"> for country items in dropdown
1878
+ // e.g. <li data-iso2="us"> for country items in the country list
1874
1879
  ISO2: "iso2",
1875
1880
  DIAL_CODE: "dialCode",
1876
1881
  // e.g. <input data-intl-tel-input-id="0"> on the input element
@@ -1888,7 +1893,7 @@ var ARIA = {
1888
1893
  MODAL: "aria-modal"
1889
1894
  };
1890
1895
 
1891
- // packages/core/src/js/i18n/en.ts
1896
+ // packages/core/src/js/locale/en.ts
1892
1897
  var interfaceTranslations = {
1893
1898
  selectedCountryAriaLabel: "Change country for phone number, currently selected ${countryName} (${dialCode})",
1894
1899
  noCountrySelected: "Select country for phone number",
@@ -1911,63 +1916,63 @@ var en_default = interfaceTranslations;
1911
1916
  // packages/core/src/js/core/options.ts
1912
1917
  var mediaQuery = (q) => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(q).matches;
1913
1918
  var isNarrowViewport = () => mediaQuery(`(max-width: ${LAYOUT.NARROW_VIEWPORT_WIDTH}px)`);
1914
- var computeDefaultUseFullscreenPopup = () => {
1919
+ var resolveAutoCountrySelectorMode = () => {
1915
1920
  if (typeof navigator !== "undefined" && typeof window !== "undefined") {
1916
1921
  const isShortViewport = mediaQuery("(max-height: 600px)");
1917
1922
  const isCoarsePointer = mediaQuery("(pointer: coarse)");
1918
- return isNarrowViewport() || isCoarsePointer && isShortViewport;
1923
+ if (isNarrowViewport() || isCoarsePointer && isShortViewport) {
1924
+ return COUNTRY_SELECTOR_MODE.FULLSCREEN;
1925
+ }
1919
1926
  }
1920
- return false;
1927
+ return COUNTRY_SELECTOR_MODE.DROPDOWN;
1921
1928
  };
1922
1929
  var defaults = {
1923
- //* Whether or not to allow the dropdown.
1924
- allowDropdown: true,
1930
+ //* How the country selector is displayed. "DROPDOWN" vs "FULLSCREEN", or "AUTO" to decide itself, or "OFF".
1931
+ countrySelectorMode: COUNTRY_SELECTOR_MODE.AUTO,
1925
1932
  //* The number type to enforce during validation.
1926
1933
  allowedNumberTypes: [NUMBER_TYPE.MOBILE, NUMBER_TYPE.FIXED_LINE],
1927
1934
  //* Whether or not to allow extensions after the main number.
1928
1935
  allowNumberExtensions: false,
1929
1936
  // Allow alphanumeric "phonewords" (e.g. +1 800 FLOWERS) as valid numbers
1930
1937
  allowPhonewords: false,
1931
- //* Add a placeholder in the input with an example number for the selected country.
1932
- autoPlaceholder: PLACEHOLDER_MODES.POLITE,
1933
1938
  //* Add a custom class to the (injected) container element.
1934
1939
  containerClass: "",
1935
1940
  //* Locale for localising country names via Intl.DisplayNames.
1936
1941
  countryNameLocale: "en",
1937
1942
  //* Override individual country names by iso2 code.
1938
1943
  countryNameOverrides: {},
1939
- //* The order of the countries in the dropdown. Defaults to alphabetical.
1944
+ //* The order of the countries in the country list. Defaults to alphabetical.
1940
1945
  countryOrder: null,
1941
- //* Add a country search input at the top of the dropdown.
1946
+ //* Add a country search input at the top of the country selector.
1942
1947
  countrySearch: true,
1943
1948
  //* Modify the auto placeholder.
1944
1949
  customPlaceholder: null,
1945
1950
  //* Always show the dropdown
1946
1951
  dropdownAlwaysOpen: false,
1947
- //* Append menu to specified element.
1948
- dropdownContainer: null,
1952
+ //* Optional DOM element to append the dropdown to (used to escape ancestors with overflow:hidden, or to mount in a custom container). Only consulted in dropdown rendering; ignored when the country selector renders as a fullscreen popup.
1953
+ dropdownParent: null,
1949
1954
  //* Don't display these countries.
1950
1955
  excludeCountries: null,
1951
1956
  //* Fix the dropdown width to the input width (rather than being as wide as the longest country name).
1952
- fixDropdownWidth: true,
1957
+ matchDropdownWidth: true,
1953
1958
  //* Format the number as the user types
1954
1959
  formatAsYouType: true,
1955
- //* Format the input value during initialisation and on setNumber.
1956
- formatOnDisplay: true,
1957
- //* geoIp lookup function.
1958
- geoIpLookup: null,
1959
- //* Inject a hidden input with the name returned from this function, and on submit, populate it with the result of getNumber.
1960
- hiddenInput: null,
1961
- //* Internationalise the core library text e.g. search input placeholder, country names.
1962
- i18n: {},
1960
+ //* Inject hidden inputs with the names returned from this function, and on submit, populate them with the full number and selected country iso2.
1961
+ hiddenInputs: null,
1962
+ //* Translations for the core library UI strings e.g. search input placeholder, country names.
1963
+ uiTranslations: {},
1963
1964
  //* Initial country.
1964
1965
  initialCountry: "",
1966
+ //* Async lookup function used to determine the initial country (e.g. via IP). Ignored if initialCountry is set.
1967
+ initialCountryLookup: null,
1965
1968
  //* A function to load the utils script.
1966
1969
  loadUtils: null,
1967
- //* National vs international formatting for numbers e.g. placeholders and displaying existing numbers.
1968
- nationalMode: false,
1970
+ //* Format used when displaying numbers (placeholder examples and stored values). One of "E164", "INTERNATIONAL", "NATIONAL".
1971
+ numberDisplayFormat: NUMBER_FORMAT.INTERNATIONAL,
1969
1972
  //* Display only these countries.
1970
1973
  onlyCountries: null,
1974
+ //* When to set the placeholder to an example number for the selected country: "POLITE" only when the input has no manually-set placeholder, "AGGRESSIVE" always, "OFF" never.
1975
+ placeholderNumberPolicy: PLACEHOLDER_POLICY.POLITE,
1971
1976
  //* Number type to use for placeholders.
1972
1977
  placeholderNumberType: NUMBER_TYPE.MOBILE,
1973
1978
  //* Add custom classes to the search input element.
@@ -1976,12 +1981,10 @@ var defaults = {
1976
1981
  separateDialCode: true,
1977
1982
  //* When strictMode rejects a key (etc), play a short feedback animation
1978
1983
  strictRejectAnimation: true,
1979
- //* Show flags - for both the selected country, and in the country dropdown
1984
+ //* Show flags - for both the selected country, and in the country list
1980
1985
  showFlags: true,
1981
1986
  //* Only allow certain chars e.g. a plus followed by numeric digits, and cap at max valid length.
1982
- strictMode: true,
1983
- //* Use full screen popup instead of dropdown for country list.
1984
- useFullscreenPopup: computeDefaultUseFullscreenPopup()
1987
+ strictMode: true
1985
1988
  };
1986
1989
  var toString = (val) => JSON.stringify(val);
1987
1990
  var isPlainObject = (val) => Boolean(val) && typeof val === "object" && !Array.isArray(val);
@@ -1993,7 +1996,7 @@ var isElLike = (val) => {
1993
1996
  const v = val;
1994
1997
  return v.nodeType === 1 && typeof v.tagName === "string" && typeof v.appendChild === "function";
1995
1998
  };
1996
- var placeholderModeSet = new Set(Object.values(PLACEHOLDER_MODES));
1999
+ var placeholderPolicySet = new Set(Object.values(PLACEHOLDER_POLICY));
1997
2000
  var warn = (message) => {
1998
2001
  console.warn(`[intl-tel-input] ${message}`);
1999
2002
  };
@@ -2039,30 +2042,48 @@ var validateOptions = (customOptions) => {
2039
2042
  continue;
2040
2043
  }
2041
2044
  switch (key) {
2042
- case "allowDropdown":
2043
2045
  case "allowNumberExtensions":
2044
2046
  case "allowPhonewords":
2045
2047
  case "countrySearch":
2046
2048
  case "dropdownAlwaysOpen":
2047
- case "fixDropdownWidth":
2049
+ case "matchDropdownWidth":
2048
2050
  case "formatAsYouType":
2049
- case "formatOnDisplay":
2050
- case "nationalMode":
2051
2051
  case "showFlags":
2052
2052
  case "separateDialCode":
2053
2053
  case "strictMode":
2054
2054
  case "strictRejectAnimation":
2055
- case "useFullscreenPopup":
2056
2055
  if (typeof value !== "boolean") {
2057
2056
  warnOption(key, "a boolean", value);
2058
2057
  break;
2059
2058
  }
2060
2059
  validatedOptions[key] = value;
2061
2060
  break;
2062
- case "autoPlaceholder":
2063
- if (typeof value !== "string" || !placeholderModeSet.has(value)) {
2064
- const validModes = Array.from(placeholderModeSet).join(", ");
2065
- warnOption("autoPlaceholder", `one of ${validModes}`, value);
2061
+ case "countrySelectorMode":
2062
+ if (typeof value !== "string" || !COUNTRY_SELECTOR_MODES.includes(value)) {
2063
+ warnOption(
2064
+ "countrySelectorMode",
2065
+ `one of ${COUNTRY_SELECTOR_MODES.map((m) => `"${m}"`).join(", ")}`,
2066
+ value
2067
+ );
2068
+ break;
2069
+ }
2070
+ validatedOptions[key] = value;
2071
+ break;
2072
+ case "numberDisplayFormat":
2073
+ if (typeof value !== "string" || value === NUMBER_FORMAT.RFC3966 || !(value === NUMBER_FORMAT.E164 || value === NUMBER_FORMAT.INTERNATIONAL || value === NUMBER_FORMAT.NATIONAL)) {
2074
+ warnOption(
2075
+ "numberDisplayFormat",
2076
+ 'one of "E164", "INTERNATIONAL", "NATIONAL"',
2077
+ value
2078
+ );
2079
+ break;
2080
+ }
2081
+ validatedOptions[key] = value;
2082
+ break;
2083
+ case "placeholderNumberPolicy":
2084
+ if (typeof value !== "string" || !placeholderPolicySet.has(value)) {
2085
+ const validPolicies = Array.from(placeholderPolicySet).join(", ");
2086
+ warnOption("placeholderNumberPolicy", `one of ${validPolicies}`, value);
2066
2087
  break;
2067
2088
  }
2068
2089
  validatedOptions[key] = value;
@@ -2088,8 +2109,8 @@ var validateOptions = (customOptions) => {
2088
2109
  break;
2089
2110
  }
2090
2111
  case "customPlaceholder":
2091
- case "geoIpLookup":
2092
- case "hiddenInput":
2112
+ case "hiddenInputs":
2113
+ case "initialCountryLookup":
2093
2114
  case "loadUtils":
2094
2115
  if (value !== null && !isFunction(value)) {
2095
2116
  warnOption(key, "a function or null", value);
@@ -2097,9 +2118,9 @@ var validateOptions = (customOptions) => {
2097
2118
  }
2098
2119
  validatedOptions[key] = value;
2099
2120
  break;
2100
- case "dropdownContainer":
2121
+ case "dropdownParent":
2101
2122
  if (value !== null && !isElLike(value)) {
2102
- warnOption("dropdownContainer", "an HTMLElement or null", value);
2123
+ warnOption("dropdownParent", "an HTMLElement or null", value);
2103
2124
  break;
2104
2125
  }
2105
2126
  validatedOptions[key] = value;
@@ -2116,9 +2137,9 @@ var validateOptions = (customOptions) => {
2116
2137
  }
2117
2138
  break;
2118
2139
  }
2119
- case "i18n":
2140
+ case "uiTranslations":
2120
2141
  if (value && !isPlainObject(value)) {
2121
- warnOption("i18n", "an object", value);
2142
+ warnOption("uiTranslations", "an object", value);
2122
2143
  break;
2123
2144
  }
2124
2145
  validatedOptions[key] = value;
@@ -2136,12 +2157,8 @@ var validateOptions = (customOptions) => {
2136
2157
  break;
2137
2158
  }
2138
2159
  const lower = value.toLowerCase();
2139
- if (lower && lower !== INITIAL_COUNTRY.AUTO && !isIso2(lower)) {
2140
- warnOption(
2141
- "initialCountry",
2142
- "a valid iso2 country code or 'auto'",
2143
- value
2144
- );
2160
+ if (lower && !isIso2(lower)) {
2161
+ warnOption("initialCountry", "a valid iso2 country code", value);
2145
2162
  break;
2146
2163
  }
2147
2164
  validatedOptions[key] = value;
@@ -2204,30 +2221,29 @@ var normaliseOptions = (o) => {
2204
2221
  }
2205
2222
  };
2206
2223
  var applyOptionSideEffects = (o) => {
2224
+ if (o.countrySelectorMode === COUNTRY_SELECTOR_MODE.AUTO) {
2225
+ o.countrySelectorMode = resolveAutoCountrySelectorMode();
2226
+ }
2207
2227
  if (o.dropdownAlwaysOpen) {
2208
- o.useFullscreenPopup = false;
2209
- o.allowDropdown = true;
2228
+ o.countrySelectorMode = COUNTRY_SELECTOR_MODE.DROPDOWN;
2210
2229
  }
2211
- if (o.useFullscreenPopup) {
2212
- o.fixDropdownWidth = false;
2230
+ if (o.countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN) {
2231
+ o.matchDropdownWidth = false;
2213
2232
  } else {
2214
2233
  if (isNarrowViewport()) {
2215
- o.fixDropdownWidth = true;
2234
+ o.matchDropdownWidth = true;
2216
2235
  }
2217
2236
  }
2218
2237
  if (o.onlyCountries?.length === 1) {
2219
2238
  o.initialCountry = o.onlyCountries[0];
2220
2239
  }
2221
- if (o.separateDialCode) {
2222
- o.nationalMode = false;
2240
+ if (o.separateDialCode && o.numberDisplayFormat === NUMBER_FORMAT.NATIONAL) {
2241
+ o.numberDisplayFormat = NUMBER_FORMAT.INTERNATIONAL;
2223
2242
  }
2224
- if (o.allowDropdown && !o.showFlags && !o.separateDialCode) {
2225
- o.nationalMode = false;
2243
+ if (o.countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF && !o.showFlags && !o.separateDialCode && o.numberDisplayFormat === NUMBER_FORMAT.NATIONAL) {
2244
+ o.numberDisplayFormat = NUMBER_FORMAT.INTERNATIONAL;
2226
2245
  }
2227
- if (o.useFullscreenPopup && !o.dropdownContainer) {
2228
- o.dropdownContainer = document.body;
2229
- }
2230
- o.i18n = { ...en_default, ...o.i18n };
2246
+ o.uiTranslations = { ...en_default, ...o.uiTranslations };
2231
2247
  };
2232
2248
 
2233
2249
  // packages/core/src/js/helpers/string.ts
@@ -2438,6 +2454,7 @@ var Numerals = class _Numerals {
2438
2454
  };
2439
2455
 
2440
2456
  // packages/core/src/js/core/ui.ts
2457
+ var supportsCssAnchor = typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("anchor-name: --x");
2441
2458
  var UI = class _UI {
2442
2459
  // private
2443
2460
  #options;
@@ -2452,8 +2469,8 @@ var UI = class _UI {
2452
2469
  #selectedCountryEl;
2453
2470
  #selectedFlagEl;
2454
2471
  #selectedDialCodeEl;
2455
- #dropdownArrowEl;
2456
- #dropdownContentEl;
2472
+ #arrowEl;
2473
+ #countrySelectorEl;
2457
2474
  #searchIconEl;
2458
2475
  #searchInputEl;
2459
2476
  #searchClearButtonEl;
@@ -2462,11 +2479,11 @@ var UI = class _UI {
2462
2479
  #hiddenInputCountryEl;
2463
2480
  #noResultsMessageEl;
2464
2481
  #searchResultsLiveRegionEl;
2465
- #detachedDropdownEl;
2482
+ #detachedCountrySelectorEl;
2466
2483
  #selectedListItemEl = null;
2467
2484
  #highlightedListItemEl = null;
2468
2485
  #listItemByIso2 = /* @__PURE__ */ new Map();
2469
- #dropdownAbortController = null;
2486
+ #countrySelectorAbortController = null;
2470
2487
  #resizeObserver;
2471
2488
  // public
2472
2489
  telInputEl;
@@ -2491,7 +2508,7 @@ var UI = class _UI {
2491
2508
  );
2492
2509
  }
2493
2510
  }
2494
- //* Generate all of the markup for the core library: the selected country overlay, and the dropdown.
2511
+ //* Generate all of the markup for the core library: the selected country overlay, and the country selector.
2495
2512
  buildMarkup(countries, searchTokens) {
2496
2513
  this.#countries = countries;
2497
2514
  this.#searchTokens = searchTokens;
@@ -2514,13 +2531,13 @@ var UI = class _UI {
2514
2531
  this.ensureDropdownWidthSet();
2515
2532
  }
2516
2533
  #createWrapperAndInsert() {
2517
- const { allowDropdown, showFlags, containerClass, useFullscreenPopup } = this.#options;
2534
+ const { countrySelectorMode, showFlags, containerClass } = this.#options;
2518
2535
  const parentClasses = buildClassNames({
2519
2536
  iti: true,
2520
2537
  "iti--input-container": true,
2521
- "iti--allow-dropdown": allowDropdown,
2538
+ "iti--has-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF,
2522
2539
  "iti--show-flags": showFlags,
2523
- "iti--inline-dropdown": !useFullscreenPopup,
2540
+ "iti--inline-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN,
2524
2541
  [containerClass]: Boolean(containerClass)
2525
2542
  });
2526
2543
  const wrapper = createEl("div", { class: parentClasses });
@@ -2531,8 +2548,9 @@ var UI = class _UI {
2531
2548
  return wrapper;
2532
2549
  }
2533
2550
  #buildCountryContainer(wrapper) {
2534
- const { allowDropdown, separateDialCode, showFlags } = this.#options;
2535
- if (!allowDropdown && !showFlags && !separateDialCode) {
2551
+ const { countrySelectorMode, separateDialCode, showFlags } = this.#options;
2552
+ const enableCountrySelector = countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF;
2553
+ if (!enableCountrySelector && !showFlags && !separateDialCode) {
2536
2554
  return;
2537
2555
  }
2538
2556
  this.#countryContainerEl = createEl(
@@ -2541,16 +2559,16 @@ var UI = class _UI {
2541
2559
  { class: `iti__country-container ${CLASSES.V_HIDE}` },
2542
2560
  wrapper
2543
2561
  );
2544
- if (allowDropdown) {
2562
+ if (enableCountrySelector) {
2545
2563
  this.#selectedCountryEl = createEl(
2546
2564
  "button",
2547
2565
  {
2548
2566
  type: "button",
2549
2567
  class: "iti__selected-country",
2550
2568
  [ARIA.EXPANDED]: "false",
2551
- [ARIA.LABEL]: this.#options.i18n.noCountrySelected,
2569
+ [ARIA.LABEL]: this.#options.uiTranslations.noCountrySelected,
2552
2570
  [ARIA.HASPOPUP]: "dialog",
2553
- [ARIA.CONTROLS]: `iti-${this.#id}__dropdown-content`
2571
+ [ARIA.CONTROLS]: `iti-${this.#id}__country-selector`
2554
2572
  },
2555
2573
  this.#countryContainerEl
2556
2574
  );
@@ -2574,8 +2592,8 @@ var UI = class _UI {
2574
2592
  { class: CLASSES.FLAG },
2575
2593
  selectedCountryPrimary
2576
2594
  );
2577
- if (allowDropdown) {
2578
- this.#dropdownArrowEl = createEl(
2595
+ if (enableCountrySelector) {
2596
+ this.#arrowEl = createEl(
2579
2597
  "div",
2580
2598
  { class: "iti__arrow", [ARIA.HIDDEN]: "true" },
2581
2599
  selectedCountryPrimary
@@ -2588,38 +2606,39 @@ var UI = class _UI {
2588
2606
  this.#selectedCountryEl
2589
2607
  );
2590
2608
  }
2591
- if (allowDropdown) {
2592
- this.#buildDropdownContent();
2609
+ if (enableCountrySelector) {
2610
+ this.#buildCountrySelector();
2593
2611
  }
2594
2612
  }
2595
2613
  ensureDropdownWidthSet() {
2596
- const { fixDropdownWidth, allowDropdown } = this.#options;
2597
- if (!allowDropdown || !fixDropdownWidth || this.#dropdownContentEl.style.width) {
2614
+ const { matchDropdownWidth, countrySelectorMode } = this.#options;
2615
+ if (countrySelectorMode === COUNTRY_SELECTOR_MODE.OFF || !matchDropdownWidth || this.#countrySelectorEl.style.width) {
2598
2616
  return;
2599
2617
  }
2600
2618
  const inputWidth = this.telInputEl.offsetWidth;
2601
2619
  if (inputWidth > 0) {
2602
- this.#dropdownContentEl.style.width = `${inputWidth}px`;
2620
+ this.#countrySelectorEl.style.width = `${inputWidth}px`;
2603
2621
  }
2604
2622
  }
2605
- #buildDropdownContent() {
2623
+ #buildCountrySelector() {
2606
2624
  const {
2607
- fixDropdownWidth,
2608
- useFullscreenPopup,
2625
+ matchDropdownWidth,
2626
+ countrySelectorMode,
2609
2627
  countrySearch,
2610
- i18n,
2611
- dropdownContainer,
2628
+ uiTranslations,
2612
2629
  containerClass
2613
2630
  } = this.#options;
2614
- const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
2615
- this.#dropdownContentEl = createEl("div", {
2616
- id: `iti-${this.#id}__dropdown-content`,
2617
- class: `iti__dropdown-content ${CLASSES.HIDE} ${extraClasses}`,
2631
+ const isFullscreen = countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN;
2632
+ const detachedParent = this.#getDetachedParent();
2633
+ const extraClasses = matchDropdownWidth ? "" : "iti--flexible-dropdown-width";
2634
+ this.#countrySelectorEl = createEl("div", {
2635
+ id: `iti-${this.#id}__country-selector`,
2636
+ class: `iti__country-selector ${CLASSES.HIDE} ${extraClasses}`,
2618
2637
  role: "dialog",
2619
2638
  [ARIA.MODAL]: "true"
2620
2639
  });
2621
2640
  if (this.#isRTL) {
2622
- this.#dropdownContentEl.setAttribute("dir", "rtl");
2641
+ this.#countrySelectorEl.setAttribute("dir", "rtl");
2623
2642
  }
2624
2643
  if (countrySearch) {
2625
2644
  this.#buildSearchUI();
@@ -2630,40 +2649,54 @@ var UI = class _UI {
2630
2649
  class: "iti__country-list",
2631
2650
  id: `iti-${this.#id}__country-listbox`,
2632
2651
  role: "listbox",
2633
- [ARIA.LABEL]: i18n.countryListAriaLabel
2652
+ [ARIA.LABEL]: uiTranslations.countryListAriaLabel
2634
2653
  },
2635
- this.#dropdownContentEl
2654
+ this.#countrySelectorEl
2636
2655
  );
2637
2656
  this.#appendListItems();
2638
2657
  if (countrySearch) {
2639
2658
  this.#updateSearchResultsA11yText();
2640
2659
  }
2641
- if (!useFullscreenPopup) {
2660
+ if (!isFullscreen) {
2642
2661
  this.#inlineDropdownHeight = this.#getHiddenInlineDropdownHeight();
2643
2662
  if (countrySearch) {
2644
- this.#dropdownContentEl.style.height = `${this.#inlineDropdownHeight}px`;
2663
+ this.#countrySelectorEl.style.height = `${this.#inlineDropdownHeight}px`;
2645
2664
  }
2646
2665
  }
2647
- if (dropdownContainer) {
2648
- const dropdownClasses = buildClassNames({
2666
+ if (detachedParent) {
2667
+ const wrapperClasses = buildClassNames({
2649
2668
  iti: true,
2650
- "iti--container": true,
2651
- "iti--fullscreen-popup": useFullscreenPopup,
2652
- "iti--inline-dropdown": !useFullscreenPopup,
2669
+ "iti--detached-country-selector": true,
2670
+ "iti--fullscreen-popup": isFullscreen,
2671
+ "iti--inline-country-selector": !isFullscreen,
2653
2672
  [containerClass]: Boolean(containerClass)
2654
2673
  });
2655
- this.#detachedDropdownEl = createEl("div", { class: dropdownClasses });
2656
- this.#detachedDropdownEl.appendChild(this.#dropdownContentEl);
2674
+ this.#detachedCountrySelectorEl = createEl("div", { class: wrapperClasses });
2675
+ this.#detachedCountrySelectorEl.appendChild(this.#countrySelectorEl);
2676
+ if (!isFullscreen) {
2677
+ this.#setupCssAnchorPositioning();
2678
+ }
2657
2679
  } else {
2658
- this.#countryContainerEl.appendChild(this.#dropdownContentEl);
2680
+ this.#countryContainerEl.appendChild(this.#countrySelectorEl);
2659
2681
  }
2660
2682
  }
2683
+ //* Resolve the DOM element to attach the country selector to. Fullscreen always uses document.body; dropdown uses the consumer-supplied dropdownParent (if any); otherwise the country selector renders inline within the input wrapper (no detached element).
2684
+ #getDetachedParent() {
2685
+ const { countrySelectorMode, dropdownParent } = this.#options;
2686
+ if (countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN) {
2687
+ return document.body;
2688
+ }
2689
+ if (countrySelectorMode === COUNTRY_SELECTOR_MODE.DROPDOWN) {
2690
+ return dropdownParent;
2691
+ }
2692
+ return null;
2693
+ }
2661
2694
  #buildSearchUI() {
2662
- const { i18n, searchInputClass } = this.#options;
2695
+ const { uiTranslations, searchInputClass } = this.#options;
2663
2696
  const searchWrapper = createEl(
2664
2697
  "div",
2665
2698
  { class: "iti__search-input-wrapper" },
2666
- this.#dropdownContentEl
2699
+ this.#countrySelectorEl
2667
2700
  );
2668
2701
  this.#searchIconEl = createEl(
2669
2702
  "span",
@@ -2681,11 +2714,11 @@ var UI = class _UI {
2681
2714
  // Chrome says inputs need either a name or an id
2682
2715
  type: "search",
2683
2716
  class: `iti__search-input ${searchInputClass}`,
2684
- placeholder: i18n.searchPlaceholder,
2717
+ placeholder: uiTranslations.searchPlaceholder,
2685
2718
  // 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
2686
2719
  role: "combobox",
2687
2720
  [ARIA.EXPANDED]: "true",
2688
- [ARIA.LABEL]: i18n.searchPlaceholder,
2721
+ [ARIA.LABEL]: uiTranslations.searchPlaceholder,
2689
2722
  [ARIA.CONTROLS]: `iti-${this.#id}__country-listbox`,
2690
2723
  [ARIA.AUTOCOMPLETE]: "list",
2691
2724
  autocomplete: "off"
@@ -2697,7 +2730,7 @@ var UI = class _UI {
2697
2730
  {
2698
2731
  type: "button",
2699
2732
  class: `iti__search-clear ${CLASSES.HIDE}`,
2700
- [ARIA.LABEL]: i18n.clearSearchAriaLabel,
2733
+ [ARIA.LABEL]: uiTranslations.clearSearchAriaLabel,
2701
2734
  tabindex: "-1"
2702
2735
  },
2703
2736
  searchWrapper
@@ -2706,7 +2739,7 @@ var UI = class _UI {
2706
2739
  this.#searchResultsLiveRegionEl = createEl(
2707
2740
  "span",
2708
2741
  { class: "iti__a11y-text" },
2709
- this.#dropdownContentEl
2742
+ this.#countrySelectorEl
2710
2743
  );
2711
2744
  this.#noResultsMessageEl = createEl(
2712
2745
  "div",
@@ -2715,9 +2748,9 @@ var UI = class _UI {
2715
2748
  [ARIA.HIDDEN]: "true"
2716
2749
  // all a11y messaging happens in this.#searchResultsLiveRegionEl
2717
2750
  },
2718
- this.#dropdownContentEl
2751
+ this.#countrySelectorEl
2719
2752
  );
2720
- this.#noResultsMessageEl.textContent = i18n.searchEmptyState ?? null;
2753
+ this.#noResultsMessageEl.textContent = uiTranslations.searchEmptyState ?? null;
2721
2754
  }
2722
2755
  #updateInputPaddingAndReveal() {
2723
2756
  if (!this.#countryContainerEl) {
@@ -2727,12 +2760,12 @@ var UI = class _UI {
2727
2760
  this.#countryContainerEl.classList.remove(CLASSES.V_HIDE);
2728
2761
  }
2729
2762
  #buildHiddenInputs(wrapper) {
2730
- const { hiddenInput } = this.#options;
2731
- if (!hiddenInput) {
2763
+ const { hiddenInputs } = this.#options;
2764
+ if (!hiddenInputs) {
2732
2765
  return;
2733
2766
  }
2734
2767
  const telInputName = this.telInputEl.getAttribute("name") || "";
2735
- const names = hiddenInput(telInputName);
2768
+ const names = hiddenInputs(telInputName);
2736
2769
  if (names.phone) {
2737
2770
  const existingInput = this.telInputEl.form?.querySelector(
2738
2771
  `input[name="${names.phone}"]`
@@ -2797,7 +2830,7 @@ var UI = class _UI {
2797
2830
  //* Update the input padding to make space for (1) the selected country/globe, (2) the arrow, and (3) the separate dial code, all of which are optional, hence handling this in the JS rather than CSS.
2798
2831
  #updateInputPadding() {
2799
2832
  if (this.#selectedCountryEl) {
2800
- const fallbackWidth = this.#options.separateDialCode ? LAYOUT.FALLBACK_SELECTED_WITH_DIAL_WIDTH : LAYOUT.FALLBACK_SELECTED_NO_DIAL_WIDTH;
2833
+ const fallbackWidth = this.#options.separateDialCode ? LAYOUT.FALLBACK_SELECTED_COUNTRY_WITH_DIAL_WIDTH : LAYOUT.FALLBACK_SELECTED_COUNTRY_NO_DIAL_WIDTH;
2801
2834
  const selectedCountryWidth = this.#selectedCountryEl.offsetWidth || this.#getHiddenSelectedCountryWidth() || fallbackWidth;
2802
2835
  const inputPadding = selectedCountryWidth + LAYOUT.INPUT_PADDING_EXTRA_LEFT;
2803
2836
  this.telInputEl.style.paddingLeft = `${inputPadding}px`;
@@ -2851,24 +2884,24 @@ var UI = class _UI {
2851
2884
  // Get the dropdown height (before it is added to the DOM)
2852
2885
  #getHiddenInlineDropdownHeight() {
2853
2886
  const body = _UI.#getBody();
2854
- this.#dropdownContentEl.classList.remove(CLASSES.HIDE);
2887
+ this.#countrySelectorEl.classList.remove(CLASSES.HIDE);
2855
2888
  const tempContainer = createEl("div", {
2856
- class: "iti iti--inline-dropdown"
2889
+ class: "iti iti--inline-country-selector"
2857
2890
  });
2858
- tempContainer.appendChild(this.#dropdownContentEl);
2891
+ tempContainer.appendChild(this.#countrySelectorEl);
2859
2892
  tempContainer.style.visibility = "hidden";
2860
2893
  body.appendChild(tempContainer);
2861
- const height = this.#dropdownContentEl.offsetHeight;
2894
+ const height = this.#countrySelectorEl.offsetHeight;
2862
2895
  body.removeChild(tempContainer);
2863
2896
  tempContainer.style.visibility = "";
2864
- this.#dropdownContentEl.classList.add(CLASSES.HIDE);
2897
+ this.#countrySelectorEl.classList.add(CLASSES.HIDE);
2865
2898
  return height > 0 ? height : LAYOUT.FALLBACK_DROPDOWN_HEIGHT;
2866
2899
  }
2867
2900
  //* Update search results text (for a11y).
2868
2901
  #updateSearchResultsA11yText() {
2869
- const { i18n } = this.#options;
2902
+ const { uiTranslations } = this.#options;
2870
2903
  const count = this.#countryListEl.childElementCount;
2871
- this.#searchResultsLiveRegionEl.textContent = i18n.searchSummaryAria(count);
2904
+ this.#searchResultsLiveRegionEl.textContent = uiTranslations.searchSummaryAria(count);
2872
2905
  }
2873
2906
  //* Country search: Filter the countries according to the search query.
2874
2907
  #filterCountriesByQuery(query) {
@@ -2886,8 +2919,8 @@ var UI = class _UI {
2886
2919
  this.#showFilteredCountries(matchedCountries);
2887
2920
  }
2888
2921
  //* Pre-fill the search input with "+" and show all countries
2889
- //* (used when user types "+" in the phone input to open the dropdown).
2890
- //* Explicitly focus the search input (openDropdown skips this when
2922
+ //* (used when user types "+" in the phone input to open the country selector).
2923
+ //* Explicitly focus the search input (openCountrySelector skips this when
2891
2924
  //* dropdownAlwaysOpen, but here we need focus to redirect subsequent keystrokes).
2892
2925
  prefillSearchWithPlus() {
2893
2926
  this.#searchInputEl.value = "+";
@@ -2970,15 +3003,15 @@ var UI = class _UI {
2970
3003
  { signal }
2971
3004
  );
2972
3005
  }
2973
- //* Wire up triggers that open/close the dropdown: label click (focus input or swallow repeat click),
3006
+ //* Wire up triggers that open/close the country selector: label click (focus input or swallow repeat click),
2974
3007
  //* selected-country click (open), and keydown on countryContainer (open on arrow/space/enter, close on tab).
2975
- bindAllInitialDropdownListeners(signal, onOpen, onClose) {
3008
+ bindAllInitialCountrySelectorListeners(signal, onOpen, onClose) {
2976
3009
  const label = this.telInputEl.closest("label");
2977
3010
  if (label) {
2978
3011
  label.addEventListener(
2979
3012
  "click",
2980
3013
  (e) => {
2981
- if (!this.isDropdownOpen()) {
3014
+ if (!this.isCountrySelectorOpen()) {
2982
3015
  this.telInputEl.focus();
2983
3016
  } else {
2984
3017
  e.preventDefault();
@@ -2990,7 +3023,7 @@ var UI = class _UI {
2990
3023
  this.#selectedCountryEl.addEventListener(
2991
3024
  "click",
2992
3025
  () => {
2993
- if (!this.isDropdownOpen() && !this.telInputEl.disabled && !this.telInputEl.readOnly) {
3026
+ if (!this.isCountrySelectorOpen() && !this.telInputEl.disabled && !this.telInputEl.readOnly) {
2994
3027
  onOpen();
2995
3028
  }
2996
3029
  },
@@ -3005,7 +3038,7 @@ var UI = class _UI {
3005
3038
  KEYS.SPACE,
3006
3039
  KEYS.ENTER
3007
3040
  ];
3008
- if (!this.isDropdownOpen() && openKeys.includes(e.key)) {
3041
+ if (!this.isCountrySelectorOpen() && openKeys.includes(e.key)) {
3009
3042
  e.preventDefault();
3010
3043
  e.stopPropagation();
3011
3044
  onOpen();
@@ -3017,24 +3050,24 @@ var UI = class _UI {
3017
3050
  { signal }
3018
3051
  );
3019
3052
  }
3020
- //* Open the dropdown: create a fresh AbortController, do the DOM work, and wire up all
3021
- //* dropdown-open listeners (which invoke the caller's onSelect / onClose callbacks).
3022
- openDropdown(onSelect, onClose) {
3023
- const { countrySearch, dropdownAlwaysOpen, dropdownContainer } = this.#options;
3024
- this.#dropdownAbortController = new AbortController();
3053
+ //* Open the country selector: create a fresh AbortController, do the DOM work, and wire up all
3054
+ //* open-state listeners (which invoke the caller's onSelect / onClose callbacks).
3055
+ openCountrySelector(onSelect, onClose) {
3056
+ const { countrySearch, dropdownAlwaysOpen } = this.#options;
3057
+ this.#countrySelectorAbortController = new AbortController();
3025
3058
  this.ensureDropdownWidthSet();
3026
- if (dropdownContainer) {
3027
- this.#injectAndPositionDetachedDropdown();
3059
+ if (this.#detachedCountrySelectorEl) {
3060
+ this.#injectAndPositionDetachedCountrySelector();
3028
3061
  } else {
3029
- const positionBelow = this.#shouldPositionInlineDropdownBelowInput();
3062
+ const positionBelow = this.#shouldPositionDropdownBelowInput();
3030
3063
  const distance = this.telInputEl.offsetHeight + LAYOUT.DROPDOWN_MARGIN;
3031
3064
  if (positionBelow) {
3032
- this.#dropdownContentEl.style.top = `${distance}px`;
3065
+ this.#countrySelectorEl.style.top = `${distance}px`;
3033
3066
  } else {
3034
- this.#dropdownContentEl.style.bottom = `${distance}px`;
3067
+ this.#countrySelectorEl.style.bottom = `${distance}px`;
3035
3068
  }
3036
3069
  }
3037
- this.#dropdownContentEl.classList.remove(CLASSES.HIDE);
3070
+ this.#countrySelectorEl.classList.remove(CLASSES.HIDE);
3038
3071
  this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "true");
3039
3072
  const itemToHighlight = this.#selectedListItemEl ?? this.#countryListEl.firstElementChild;
3040
3073
  if (itemToHighlight) {
@@ -3043,7 +3076,7 @@ var UI = class _UI {
3043
3076
  if (countrySearch && !dropdownAlwaysOpen) {
3044
3077
  this.#searchInputEl.focus();
3045
3078
  }
3046
- if (this.#options.useFullscreenPopup && this.#detachedDropdownEl && window.visualViewport) {
3079
+ if (this.#options.countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN && this.#detachedCountrySelectorEl && window.visualViewport) {
3047
3080
  window.visualViewport.addEventListener(
3048
3081
  "resize",
3049
3082
  () => {
@@ -3052,29 +3085,29 @@ var UI = class _UI {
3052
3085
  this.#scrollCountryListToItem(this.#highlightedListItemEl);
3053
3086
  }
3054
3087
  },
3055
- { signal: this.#dropdownAbortController.signal }
3088
+ { signal: this.#countrySelectorAbortController.signal }
3056
3089
  );
3057
3090
  }
3058
- this.#dropdownArrowEl.classList.add(CLASSES.ARROW_UP);
3059
- this.#bindDropdownOpenListeners(onSelect, onClose);
3091
+ this.#arrowEl.classList.add(CLASSES.ARROW_UP);
3092
+ this.#bindCountrySelectorOpenListeners(onSelect, onClose);
3060
3093
  }
3061
- //* Wire up all listeners needed while the dropdown is open: list-item hover (highlight),
3094
+ //* Wire up all listeners needed while the country selector is open: list-item hover (highlight),
3062
3095
  //* list-item click & enter key (select), click-off & escape (close), search input (filter),
3063
- //* (when countrySearch disabled) typed-char hidden search, and (when dropdown is in an external
3064
- //* container) close on window scroll.
3065
- #bindDropdownOpenListeners(onSelect, onClose) {
3066
- const signal = this.#dropdownAbortController.signal;
3096
+ //* (when countrySearch disabled) typed-char hidden search, and (when the country selector is in an
3097
+ //* external container) update (fixed) position on scroll/resize.
3098
+ #bindCountrySelectorOpenListeners(onSelect, onClose) {
3099
+ const signal = this.#countrySelectorAbortController.signal;
3067
3100
  this.#bindListItemHover(signal);
3068
3101
  this.#bindListItemClick(signal, onSelect);
3069
3102
  if (!this.#options.dropdownAlwaysOpen) {
3070
3103
  this.#bindOutsideClickToClose(signal, onClose);
3071
3104
  }
3072
- this.#bindDropdownKeydownListener(signal, onSelect, onClose);
3105
+ this.#bindCountrySelectorKeydownListener(signal, onSelect, onClose);
3073
3106
  if (this.#options.countrySearch) {
3074
3107
  this.#bindSearchInputListener(signal);
3075
3108
  }
3076
- if (!this.#options.useFullscreenPopup && this.#options.dropdownContainer) {
3077
- window.addEventListener("scroll", onClose, { signal });
3109
+ if (this.#options.countrySelectorMode === COUNTRY_SELECTOR_MODE.DROPDOWN && this.#options.dropdownParent && !supportsCssAnchor) {
3110
+ document.addEventListener("scroll", onClose, { signal, capture: true, passive: true });
3078
3111
  }
3079
3112
  }
3080
3113
  //* When mouse over a list item, just highlight that one (so if they hit "enter" we know which to select).
@@ -3107,13 +3140,13 @@ var UI = class _UI {
3107
3140
  { signal }
3108
3141
  );
3109
3142
  }
3110
- //* Invoke onClickOff when the user clicks anywhere outside the dropdown.
3143
+ //* Invoke onClickOff when the user clicks anywhere outside the country selector.
3111
3144
  #bindOutsideClickToClose(signal, onClickOff) {
3112
3145
  setTimeout(() => {
3113
3146
  document.documentElement.addEventListener(
3114
3147
  "click",
3115
3148
  (e) => {
3116
- if (!this.#dropdownContentEl.contains(e.target)) {
3149
+ if (!this.#countrySelectorEl.contains(e.target)) {
3117
3150
  onClickOff();
3118
3151
  }
3119
3152
  },
@@ -3121,11 +3154,10 @@ var UI = class _UI {
3121
3154
  );
3122
3155
  }, 0);
3123
3156
  }
3124
- //* Keyboard navigation while the dropdown is open: arrow keys navigate, hidden-search keys filter,
3125
- //* and enter/escape invoke the caller's callbacks (which handle country selection / dropdown close).
3126
- //* Listens on document because key events go there when no input has focus.
3157
+ //* Keyboard navigation while the country selector is open: arrow keys navigate, hidden-search keys filter,
3158
+ //* and enter/escape invoke the caller's callbacks (which handle country selection / close).
3127
3159
  //* Uses keydown rather than keypress so non-char keys (arrow, esc) fire and so holding a key repeats.
3128
- #bindDropdownKeydownListener(signal, onEnter, onEscape) {
3160
+ #bindCountrySelectorKeydownListener(signal, onEnter, onEscape) {
3129
3161
  let query = "";
3130
3162
  let queryTimer = null;
3131
3163
  const handleKeydown = (e) => {
@@ -3147,7 +3179,7 @@ var UI = class _UI {
3147
3179
  this.#selectedCountryEl.focus();
3148
3180
  }
3149
3181
  }
3150
- if (!this.#options.countrySearch && e.target !== this.telInputEl && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {
3182
+ if (!this.#options.countrySearch && REGEX.HIDDEN_SEARCH_CHAR.test(e.key)) {
3151
3183
  e.stopPropagation();
3152
3184
  if (queryTimer) {
3153
3185
  clearTimeout(queryTimer);
@@ -3159,7 +3191,8 @@ var UI = class _UI {
3159
3191
  }, TIMINGS.HIDDEN_SEARCH_RESET_MS);
3160
3192
  }
3161
3193
  };
3162
- document.addEventListener("keydown", handleKeydown, { signal });
3194
+ this.#selectedCountryEl?.addEventListener("keydown", handleKeydown, { signal });
3195
+ this.#countrySelectorEl?.addEventListener("keydown", handleKeydown, { signal });
3163
3196
  }
3164
3197
  //* Wire up country search input listener: typing filters the list, the clear button resets it.
3165
3198
  #bindSearchInputListener(signal) {
@@ -3196,7 +3229,7 @@ var UI = class _UI {
3196
3229
  this.#highlightListItem(next);
3197
3230
  }
3198
3231
  }
3199
- // Update the selected list item in the dropdown
3232
+ // Update the selected list item in the country list
3200
3233
  #updateSelectedListItem(iso2) {
3201
3234
  if (this.#selectedListItemEl && this.#selectedListItemEl.dataset[DATA_KEYS.ISO2] !== iso2) {
3202
3235
  this.#selectedListItemEl.setAttribute(ARIA.SELECTED, "false");
@@ -3247,12 +3280,12 @@ var UI = class _UI {
3247
3280
  this.#countryListEl.scrollTop = 0;
3248
3281
  this.#updateSearchResultsA11yText();
3249
3282
  }
3250
- // UI: Close the dropdown (DOM + abort dropdown-scoped listeners).
3251
- closeDropdown() {
3252
- const { countrySearch, dropdownContainer } = this.#options;
3253
- this.#dropdownAbortController.abort();
3254
- this.#dropdownAbortController = null;
3255
- this.#dropdownContentEl.classList.add(CLASSES.HIDE);
3283
+ // UI: Close the country selector (DOM + abort scoped listeners).
3284
+ closeCountrySelector() {
3285
+ const { countrySearch } = this.#options;
3286
+ this.#countrySelectorAbortController.abort();
3287
+ this.#countrySelectorAbortController = null;
3288
+ this.#countrySelectorEl.classList.add(CLASSES.HIDE);
3256
3289
  this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "false");
3257
3290
  if (countrySearch) {
3258
3291
  this.#searchInputEl.removeAttribute(ARIA.ACTIVE_DESCENDANT);
@@ -3263,19 +3296,19 @@ var UI = class _UI {
3263
3296
  this.#highlightedListItemEl = null;
3264
3297
  }
3265
3298
  }
3266
- this.#dropdownArrowEl.classList.remove(CLASSES.ARROW_UP);
3267
- if (dropdownContainer) {
3268
- this.#detachedDropdownEl.remove();
3269
- this.#detachedDropdownEl.style.top = "";
3270
- this.#detachedDropdownEl.style.bottom = "";
3271
- this.#detachedDropdownEl.style.paddingLeft = "";
3272
- this.#detachedDropdownEl.style.paddingRight = "";
3299
+ this.#arrowEl.classList.remove(CLASSES.ARROW_UP);
3300
+ if (this.#detachedCountrySelectorEl) {
3301
+ this.#detachedCountrySelectorEl.remove();
3302
+ this.#detachedCountrySelectorEl.style.top = "";
3303
+ this.#detachedCountrySelectorEl.style.bottom = "";
3304
+ this.#detachedCountrySelectorEl.style.paddingLeft = "";
3305
+ this.#detachedCountrySelectorEl.style.paddingRight = "";
3273
3306
  } else {
3274
- this.#dropdownContentEl.style.top = "";
3275
- this.#dropdownContentEl.style.bottom = "";
3307
+ this.#countrySelectorEl.style.top = "";
3308
+ this.#countrySelectorEl.style.bottom = "";
3276
3309
  }
3277
3310
  }
3278
- #shouldPositionInlineDropdownBelowInput() {
3311
+ #shouldPositionDropdownBelowInput() {
3279
3312
  if (this.#options.dropdownAlwaysOpen) {
3280
3313
  return true;
3281
3314
  }
@@ -3284,50 +3317,83 @@ var UI = class _UI {
3284
3317
  const spaceBelow = window.innerHeight - inputPos.bottom;
3285
3318
  return spaceBelow >= this.#inlineDropdownHeight || spaceBelow >= spaceAbove;
3286
3319
  }
3287
- // inject dropdown into container and apply positioning styles
3288
- #injectAndPositionDetachedDropdown() {
3289
- const { dropdownContainer, useFullscreenPopup } = this.#options;
3290
- if (useFullscreenPopup) {
3320
+ // inject the country selector into its detached wrapper and apply positioning styles
3321
+ #injectAndPositionDetachedCountrySelector() {
3322
+ const isFullscreen = this.#options.countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN;
3323
+ const detachedParent = this.#getDetachedParent();
3324
+ if (isFullscreen) {
3291
3325
  if (window.innerWidth >= LAYOUT.NARROW_VIEWPORT_WIDTH) {
3292
3326
  const inputPos = this.telInputEl.getBoundingClientRect();
3293
- this.#detachedDropdownEl.style.paddingLeft = `${inputPos.left}px`;
3294
- this.#detachedDropdownEl.style.paddingRight = `${window.innerWidth - inputPos.right}px`;
3327
+ this.#detachedCountrySelectorEl.style.paddingLeft = `${inputPos.left}px`;
3328
+ this.#detachedCountrySelectorEl.style.paddingRight = `${window.innerWidth - inputPos.right}px`;
3295
3329
  }
3296
- } else {
3330
+ } else if (!supportsCssAnchor) {
3297
3331
  const inputPos = this.telInputEl.getBoundingClientRect();
3298
- this.#detachedDropdownEl.style.left = `${inputPos.left}px`;
3299
- const positionBelow = this.#shouldPositionInlineDropdownBelowInput();
3300
- if (positionBelow) {
3301
- this.#detachedDropdownEl.style.top = `${inputPos.bottom + LAYOUT.DROPDOWN_MARGIN}px`;
3332
+ this.#detachedCountrySelectorEl.style.left = `${inputPos.left}px`;
3333
+ if (this.#shouldPositionDropdownBelowInput()) {
3334
+ this.#detachedCountrySelectorEl.style.top = `${inputPos.bottom + LAYOUT.DROPDOWN_MARGIN}px`;
3302
3335
  } else {
3303
- this.#detachedDropdownEl.style.top = "unset";
3304
- this.#detachedDropdownEl.style.bottom = `${window.innerHeight - inputPos.top + LAYOUT.DROPDOWN_MARGIN}px`;
3336
+ this.#detachedCountrySelectorEl.style.top = "unset";
3337
+ this.#detachedCountrySelectorEl.style.bottom = `${window.innerHeight - inputPos.top + LAYOUT.DROPDOWN_MARGIN}px`;
3305
3338
  }
3306
3339
  }
3307
- dropdownContainer.appendChild(this.#detachedDropdownEl);
3340
+ detachedParent.appendChild(this.#detachedCountrySelectorEl);
3341
+ }
3342
+ //* Wire up CSS Anchor Positioning between the input and the detached country selector using a
3343
+ //* unique anchor name per instance. Called once at build time — the matching styles in
3344
+ //* intlTelInput.css only take effect in browsers that support anchor(); elsewhere these
3345
+ //* properties are inert. We append our name to any existing anchor-name (read via
3346
+ //* getComputedStyle so we pick up CSS-defined values), so consumer-set anchors on the input
3347
+ //* are preserved. Caveat: this snapshots the consumer's value once — if they later change
3348
+ //* anchor-name via CSS (e.g. a class swap), our inline write will shadow the change.
3349
+ #setupCssAnchorPositioning() {
3350
+ const anchorName = `--iti-anchor-${this.#id}`;
3351
+ const existing = getComputedStyle(this.telInputEl).anchorName;
3352
+ this.telInputEl.style.anchorName = existing && existing !== "none" ? `${existing}, ${anchorName}` : anchorName;
3353
+ this.#detachedCountrySelectorEl.style.positionAnchor = anchorName;
3308
3354
  }
3309
3355
  // Adjust the fullscreen popup dimensions to match the visual viewport,
3310
3356
  // so it stays above the virtual keyboard on mobile devices.
3311
3357
  #adjustFullscreenPopupToViewport() {
3312
3358
  const vv = window.visualViewport;
3313
- if (!vv || !this.#detachedDropdownEl) {
3359
+ if (!vv || !this.#detachedCountrySelectorEl) {
3314
3360
  return;
3315
3361
  }
3316
3362
  const virtualKeyboardHeight = window.innerHeight - vv.height;
3317
- this.#detachedDropdownEl.style.bottom = `${virtualKeyboardHeight}px`;
3363
+ this.#detachedCountrySelectorEl.style.bottom = `${virtualKeyboardHeight}px`;
3318
3364
  }
3319
- // UI: Whether the dropdown is currently open (visible).
3320
- isDropdownOpen() {
3321
- return !this.#dropdownContentEl.classList.contains(CLASSES.HIDE);
3365
+ // UI: Whether the country selector is currently open (visible).
3366
+ isCountrySelectorOpen() {
3367
+ return !this.#countrySelectorEl.classList.contains(CLASSES.HIDE);
3322
3368
  }
3323
3369
  // Toggle the loading spinner on the selected flag (used during auto-country geoIP lookup).
3324
3370
  setLoading(isLoading) {
3325
3371
  this.#selectedFlagEl.classList.toggle(CLASSES.LOADING, isLoading);
3326
3372
  }
3373
+ //* Play the strict-reject animation (shake, or background-colour flash under prefers-reduced-motion) on the wrapper.
3374
+ //* Called when strictMode rejects the whole input (keystroke, or whole paste).
3375
+ //* Uses the wrapper (not the input) so any separateDialCode / country button move together with the input.
3376
+ playStrictRejectAnimation() {
3377
+ if (!this.#options.strictRejectAnimation) {
3378
+ return;
3379
+ }
3380
+ const wrapperEl = this.telInputEl.parentElement;
3381
+ if (!wrapperEl) {
3382
+ return;
3383
+ }
3384
+ wrapperEl.classList.remove(CLASSES.STRICT_REJECT_ANIMATION);
3385
+ void wrapperEl.offsetWidth;
3386
+ wrapperEl.classList.add(CLASSES.STRICT_REJECT_ANIMATION);
3387
+ wrapperEl.addEventListener(
3388
+ "animationend",
3389
+ () => wrapperEl.classList.remove(CLASSES.STRICT_REJECT_ANIMATION),
3390
+ { once: true }
3391
+ );
3392
+ }
3327
3393
  isLoading() {
3328
3394
  return this.#selectedFlagEl.classList.contains(CLASSES.LOADING);
3329
3395
  }
3330
- // Set the disabled state of the input and dropdown.
3396
+ // Set the disabled state of the input and country selector.
3331
3397
  setDisabled(disabled) {
3332
3398
  this.telInputEl.disabled = disabled;
3333
3399
  if (this.#selectedCountryEl) {
@@ -3338,7 +3404,7 @@ var UI = class _UI {
3338
3404
  }
3339
3405
  }
3340
3406
  }
3341
- // Set the readonly state of the input and dropdown.
3407
+ // Set the readonly state of the input and country selector.
3342
3408
  setReadonly(readonly) {
3343
3409
  this.telInputEl.readOnly = readonly;
3344
3410
  if (this.#selectedCountryEl) {
@@ -3349,12 +3415,12 @@ var UI = class _UI {
3349
3415
  }
3350
3416
  }
3351
3417
  }
3352
- setCountry(selectedCountryData) {
3353
- const { allowDropdown, showFlags, separateDialCode, i18n } = this.#options;
3354
- const name = selectedCountryData?.name;
3355
- const dialCode = selectedCountryData?.dialCode;
3356
- const iso2 = selectedCountryData?.iso2 ?? "";
3357
- if (allowDropdown) {
3418
+ setSelectedCountry(selectedCountry) {
3419
+ const { countrySelectorMode, showFlags, separateDialCode, uiTranslations } = this.#options;
3420
+ const name = selectedCountry?.name;
3421
+ const dialCode = selectedCountry?.dialCode;
3422
+ const iso2 = selectedCountry?.iso2 ?? "";
3423
+ if (countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF) {
3358
3424
  this.#updateSelectedListItem(iso2);
3359
3425
  }
3360
3426
  if (this.#selectedCountryEl) {
@@ -3363,13 +3429,13 @@ var UI = class _UI {
3363
3429
  let flagContent = null;
3364
3430
  if (iso2) {
3365
3431
  title = name;
3366
- ariaLabel = i18n.selectedCountryAriaLabel.replace("${countryName}", name).replace("${dialCode}", `+${dialCode}`);
3432
+ ariaLabel = uiTranslations.selectedCountryAriaLabel.replace("${countryName}", name).replace("${dialCode}", `+${dialCode}`);
3367
3433
  if (!showFlags) {
3368
3434
  flagContent = buildGlobeIcon();
3369
3435
  }
3370
3436
  } else {
3371
- title = i18n.noCountrySelected;
3372
- ariaLabel = i18n.noCountrySelected;
3437
+ title = uiTranslations.noCountrySelected;
3438
+ ariaLabel = uiTranslations.noCountrySelected;
3373
3439
  flagContent = buildGlobeIcon();
3374
3440
  }
3375
3441
  this.#selectedFlagEl.className = flagClass;
@@ -3518,17 +3584,17 @@ var hasRegionlessDialCode = (number) => {
3518
3584
  };
3519
3585
 
3520
3586
  // packages/core/src/js/format/formatting.ts
3521
- var stripSeparateDialCode = (fullNumber, hasValidDialCode, separateDialCode, selectedCountryData) => {
3587
+ var stripSeparateDialCode = (fullNumber, hasValidDialCode, separateDialCode, selectedCountry) => {
3522
3588
  if (!separateDialCode || !hasValidDialCode) {
3523
3589
  return fullNumber;
3524
3590
  }
3525
- const dialCode = `+${selectedCountryData.dialCode}`;
3591
+ const dialCode = `+${selectedCountry.dialCode}`;
3526
3592
  const start = fullNumber[dialCode.length] === " " || fullNumber[dialCode.length] === "-" ? dialCode.length + 1 : dialCode.length;
3527
3593
  return fullNumber.substring(start);
3528
3594
  };
3529
- var formatNumberAsYouType = (fullNumber, telInputValue, utils, selectedCountryData, separateDialCode) => {
3530
- const result = utils ? utils.formatNumberAsYouType(fullNumber, selectedCountryData?.iso2) : fullNumber;
3531
- const dialCode = selectedCountryData?.dialCode;
3595
+ var formatNumberAsYouType = (fullNumber, telInputValue, utils, selectedCountry, separateDialCode) => {
3596
+ const result = utils ? utils.formatNumberAsYouType(fullNumber, selectedCountry?.iso2) : fullNumber;
3597
+ const dialCode = selectedCountry?.dialCode;
3532
3598
  if (separateDialCode && telInputValue.charAt(0) !== "+" && result.includes(`+${dialCode}`)) {
3533
3599
  const afterDialCode = result.split(`+${dialCode}`)[1] || "";
3534
3600
  return afterDialCode.trim();
@@ -3659,8 +3725,8 @@ var Iti = class _Iti {
3659
3725
  this.#ui.telInputEl.value = this.#numerals.denormalise(asciiValue);
3660
3726
  }
3661
3727
  #createInitPromise(options) {
3662
- const { initialCountry, geoIpLookup, loadUtils } = options;
3663
- const needsAutoCountryDeferred = initialCountry === INITIAL_COUNTRY.AUTO && Boolean(geoIpLookup);
3728
+ const { initialCountry, initialCountryLookup, loadUtils } = options;
3729
+ const needsAutoCountryDeferred = !initialCountry && Boolean(initialCountryLookup);
3664
3730
  const needsUtilsDeferred = Boolean(loadUtils) && !intlTelInput.utils;
3665
3731
  if (needsAutoCountryDeferred) {
3666
3732
  this.#autoCountryDeferred = createDeferred();
@@ -3682,7 +3748,7 @@ var Iti = class _Iti {
3682
3748
  this.#initListeners();
3683
3749
  this.#startAsyncLoads();
3684
3750
  if (this.#options.dropdownAlwaysOpen) {
3685
- this.#openDropdown();
3751
+ this.openCountrySelector();
3686
3752
  }
3687
3753
  }
3688
3754
  //********************
@@ -3705,8 +3771,8 @@ var Iti = class _Iti {
3705
3771
  const value = useAttribute ? attributeValue : inputValue;
3706
3772
  const dialCode = this.#getDialCode(value);
3707
3773
  const isRegionlessNanpNumber = isRegionlessNanp(value);
3708
- const { initialCountry, geoIpLookup } = this.#options;
3709
- const isAutoCountry = initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;
3774
+ const { initialCountry, initialCountryLookup } = this.#options;
3775
+ const isAutoCountry = !initialCountry && Boolean(initialCountryLookup);
3710
3776
  const resolvedInitialCountry = isAutoCountry && intlTelInput.autoCountry ? intlTelInput.autoCountry : initialCountry;
3711
3777
  const doingAutoCountryLookup = isAutoCountry && !overrideAutoCountry && !intlTelInput.autoCountry;
3712
3778
  const isValidInitialCountry = isIso2(resolvedInitialCountry);
@@ -3735,11 +3801,11 @@ var Iti = class _Iti {
3735
3801
  //* Initialise the main event listeners: input keyup, and click selected country.
3736
3802
  #initListeners() {
3737
3803
  this.#bindAllTelInputListeners();
3738
- if (this.#options.allowDropdown) {
3739
- this.#ui.bindAllInitialDropdownListeners(
3804
+ if (this.#options.countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF) {
3805
+ this.#ui.bindAllInitialCountrySelectorListeners(
3740
3806
  this.#abortController.signal,
3741
- () => this.#openDropdown(),
3742
- () => this.#closeDropdown()
3807
+ () => this.openCountrySelector(),
3808
+ () => this.#closeCountrySelectorInternal()
3743
3809
  );
3744
3810
  }
3745
3811
  this.#ui.bindHiddenInputSubmitListener(
@@ -3748,7 +3814,7 @@ var Iti = class _Iti {
3748
3814
  () => this.#selectedCountry?.iso2 || ""
3749
3815
  );
3750
3816
  }
3751
- //* Init requests: utils script / geo ip lookup.
3817
+ //* Init requests: utils script / initial country lookup.
3752
3818
  #startAsyncLoads() {
3753
3819
  if (this.#utilsDeferred) {
3754
3820
  const { loadUtils } = this.#options;
@@ -3772,7 +3838,7 @@ var Iti = class _Iti {
3772
3838
  }
3773
3839
  }
3774
3840
  }
3775
- //* Perform the geo ip lookup.
3841
+ //* Perform the initial country lookup.
3776
3842
  async #loadAutoCountry() {
3777
3843
  if (intlTelInput.autoCountry) {
3778
3844
  this.#handleAutoCountryLoaded();
@@ -3783,14 +3849,14 @@ var Iti = class _Iti {
3783
3849
  return;
3784
3850
  }
3785
3851
  intlTelInput.startedLoadingAutoCountry = true;
3786
- if (typeof this.#options.geoIpLookup === "function") {
3852
+ if (typeof this.#options.initialCountryLookup === "function") {
3787
3853
  let timeoutId;
3788
3854
  try {
3789
3855
  const iso2 = await Promise.race([
3790
- this.#options.geoIpLookup(),
3856
+ this.#options.initialCountryLookup(),
3791
3857
  new Promise((_, reject) => {
3792
3858
  timeoutId = setTimeout(
3793
- () => reject(new Error("intl-tel-input: geoIpLookup timed out after 10s")),
3859
+ () => reject(new Error("intl-tel-input: initialCountryLookup timed out after 10s")),
3794
3860
  1e4
3795
3861
  );
3796
3862
  })
@@ -3813,8 +3879,8 @@ var Iti = class _Iti {
3813
3879
  }
3814
3880
  }
3815
3881
  }
3816
- #openDropdownWithPlus() {
3817
- this.#openDropdown();
3882
+ #openCountrySelectorWithPlus() {
3883
+ this.openCountrySelector();
3818
3884
  this.#ui.prefillSearchWithPlus();
3819
3885
  }
3820
3886
  //* Delete the character just typed (the one immediately before the caret). Used by Android workarounds where we can't preventDefault on keydown.
@@ -3834,13 +3900,13 @@ var Iti = class _Iti {
3834
3900
  //* 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)
3835
3901
  #handleAndroidPlusKey(inputValue) {
3836
3902
  this.#removeJustTypedChar(inputValue);
3837
- this.#openDropdownWithPlus();
3903
+ this.#openCountrySelectorWithPlus();
3838
3904
  }
3839
3905
  //* 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.
3840
3906
  #handleAndroidStrictReject(inputValue, rejectedInput) {
3841
3907
  const newCaretPos = this.#removeJustTypedChar(inputValue);
3842
3908
  this.#ui.telInputEl.setSelectionRange(newCaretPos, newCaretPos);
3843
- this.#playStrictRejectAnimation();
3909
+ this.#ui.playStrictRejectAnimation();
3844
3910
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
3845
3911
  source: "key",
3846
3912
  rejectedInput,
@@ -3904,7 +3970,7 @@ var Iti = class _Iti {
3904
3970
  strictMode,
3905
3971
  formatAsYouType,
3906
3972
  separateDialCode,
3907
- allowDropdown,
3973
+ countrySelectorMode,
3908
3974
  countrySearch
3909
3975
  } = this.#options;
3910
3976
  const detail = e?.detail;
@@ -3914,7 +3980,7 @@ var Iti = class _Iti {
3914
3980
  let inputValue = this.#getTelInputValue();
3915
3981
  const isPaste = e?.inputType === INPUT_TYPES.PASTE;
3916
3982
  const isStrictPaste = strictMode && isPaste;
3917
- if (this.#isAndroid && !isPaste && e?.data === "+" && separateDialCode && allowDropdown && countrySearch) {
3983
+ if (this.#isAndroid && !isPaste && e?.data === "+" && separateDialCode && countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF && countrySearch) {
3918
3984
  this.#handleAndroidPlusKey(inputValue);
3919
3985
  return;
3920
3986
  }
@@ -3961,13 +4027,13 @@ var Iti = class _Iti {
3961
4027
  //* On keydown event: (1) if strictMode then prevent invalid characters, (2) if separateDialCode then handle plus key
3962
4028
  //* Note that this fires BEFORE the input is updated.
3963
4029
  #handleKeydownEvent = (e) => {
3964
- const { strictMode, separateDialCode, allowDropdown, countrySearch } = this.#options;
4030
+ const { strictMode, separateDialCode, countrySelectorMode, countrySearch } = this.#options;
3965
4031
  if (!e.key || e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) {
3966
4032
  return;
3967
4033
  }
3968
- if (separateDialCode && allowDropdown && countrySearch && e.key === "+") {
4034
+ if (separateDialCode && countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF && countrySearch && e.key === "+") {
3969
4035
  e.preventDefault();
3970
- this.#openDropdownWithPlus();
4036
+ this.#openCountrySelectorWithPlus();
3971
4037
  return;
3972
4038
  }
3973
4039
  if (!strictMode) {
@@ -3997,7 +4063,7 @@ var Iti = class _Iti {
3997
4063
  const newCountry = this.#resolveCountryChangeFromNumber(newFullNumber);
3998
4064
  const isChangingDialCode = newCountry !== null;
3999
4065
  if (!isAllowedChar || hasExceededMaxLength && !isChangingDialCode && !isInitialPlus) {
4000
- this.#playStrictRejectAnimation();
4066
+ this.#ui.playStrictRejectAnimation();
4001
4067
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4002
4068
  source: "key",
4003
4069
  rejectedInput: e.key,
@@ -4052,7 +4118,7 @@ var Iti = class _Iti {
4052
4118
  let newValue = before + sanitised + after;
4053
4119
  let rejectReason = sanitised !== pasted ? "invalid" : null;
4054
4120
  if (newValue.length > 30) {
4055
- this.#playStrictRejectAnimation();
4121
+ this.#ui.playStrictRejectAnimation();
4056
4122
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4057
4123
  source: "paste",
4058
4124
  rejectedInput: pastedRaw,
@@ -4068,7 +4134,7 @@ var Iti = class _Iti {
4068
4134
  coreNumber = intlTelInput.utils.getCoreNumber(newValue, iso2);
4069
4135
  }
4070
4136
  if (!coreNumber) {
4071
- this.#playStrictRejectAnimation();
4137
+ this.#ui.playStrictRejectAnimation();
4072
4138
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4073
4139
  source: "paste",
4074
4140
  rejectedInput: pastedRaw,
@@ -4083,7 +4149,7 @@ var Iti = class _Iti {
4083
4149
  newValue = newValue.slice(0, newValue.length - trimLength);
4084
4150
  rejectReason = "max-length";
4085
4151
  } else {
4086
- this.#playStrictRejectAnimation();
4152
+ this.#ui.playStrictRejectAnimation();
4087
4153
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4088
4154
  source: "paste",
4089
4155
  rejectedInput: pastedRaw,
@@ -4099,7 +4165,7 @@ var Iti = class _Iti {
4099
4165
  input.setSelectionRange(caretPos, caretPos);
4100
4166
  if (rejectReason) {
4101
4167
  if (pasted.length > 0 && sanitised.length === 0) {
4102
- this.#playStrictRejectAnimation();
4168
+ this.#ui.playStrictRejectAnimation();
4103
4169
  }
4104
4170
  this.#dispatchEvent(EVENTS.STRICT_REJECT, {
4105
4171
  source: "paste",
@@ -4121,21 +4187,6 @@ var Iti = class _Iti {
4121
4187
  const max = Number(this.#ui.telInputEl.getAttribute("maxlength"));
4122
4188
  return max && number.length > max ? number.substring(0, max) : number;
4123
4189
  }
4124
- //* Play the strict-reject animation (shake, or background-colour flash under prefers-reduced-motion) on the wrapper.
4125
- //* Called when strictMode rejects the whole input (keystroke, or whole paste).
4126
- //* Uses the wrapper (not the input) so any separateDialCode / country button move together with the input.
4127
- #playStrictRejectAnimation() {
4128
- if (!this.#options.strictRejectAnimation) {
4129
- return;
4130
- }
4131
- const wrapperEl = this.#ui.telInputEl.parentElement;
4132
- if (!wrapperEl) {
4133
- return;
4134
- }
4135
- wrapperEl.classList.remove("iti__strict-reject-animation");
4136
- void wrapperEl.offsetWidth;
4137
- wrapperEl.classList.add("iti__strict-reject-animation");
4138
- }
4139
4190
  //* Trigger a custom event on the input (typed via ItiEventMap).
4140
4191
  #dispatchEvent(name, detailProps = {}) {
4141
4192
  const e = new CustomEvent(name, {
@@ -4145,27 +4196,36 @@ var Iti = class _Iti {
4145
4196
  });
4146
4197
  this.#ui.telInputEl.dispatchEvent(e);
4147
4198
  }
4148
- //* Open the dropdown. Bail if already open — otherwise the existing AbortController gets overwritten
4149
- //* and its listeners leak. Reachable via openDropdownWithPlus when dropdownAlwaysOpen is set
4150
- #openDropdown() {
4151
- if (this.#ui.isDropdownOpen()) {
4199
+ //* Open the country selector. Bail if already open — otherwise the existing AbortController gets overwritten
4200
+ //* and its listeners leak. Reachable via openCountrySelectorWithPlus when dropdownAlwaysOpen is set.
4201
+ //* Public so consumers can programmatically open the country selector.
4202
+ openCountrySelector() {
4203
+ if (this.#ui.isCountrySelectorOpen()) {
4152
4204
  return;
4153
4205
  }
4154
- this.#ui.openDropdown(
4206
+ this.#ui.openCountrySelector(
4155
4207
  (li) => this.#selectListItem(li),
4156
- () => this.#closeDropdown()
4208
+ () => this.#closeCountrySelectorInternal()
4157
4209
  );
4158
- this.#dispatchEvent(EVENTS.OPEN_COUNTRY_DROPDOWN);
4210
+ this.#dispatchEvent(EVENTS.OPEN_COUNTRY_SELECTOR);
4159
4211
  }
4160
4212
  //* Update the input's value to the given number (format first if possible)
4161
4213
  //* NOTE: this is called from setInitialState, handleUtilsLoaded and setNumber.
4162
4214
  #updateValueFromNumber(fullNumber) {
4163
- const { formatOnDisplay, nationalMode, separateDialCode } = this.#options;
4215
+ const { numberDisplayFormat, separateDialCode } = this.#options;
4164
4216
  let number = fullNumber;
4165
- if (formatOnDisplay && intlTelInput.utils && this.#selectedCountry) {
4217
+ if (intlTelInput.utils && this.#selectedCountry) {
4166
4218
  const isRegionless = hasRegionlessDialCode(fullNumber);
4167
- const useNational = nationalMode && !isRegionless || !number.startsWith("+") && !separateDialCode;
4168
- const format = useNational ? NUMBER_FORMAT.NATIONAL : NUMBER_FORMAT.INTERNATIONAL;
4219
+ const preserveUserNational = !number.startsWith("+") && !separateDialCode;
4220
+ const useNational = numberDisplayFormat === NUMBER_FORMAT.NATIONAL && !isRegionless || preserveUserNational;
4221
+ let format;
4222
+ if (useNational) {
4223
+ format = NUMBER_FORMAT.NATIONAL;
4224
+ } else if (numberDisplayFormat === NUMBER_FORMAT.E164 && !isRegionless) {
4225
+ format = NUMBER_FORMAT.E164;
4226
+ } else {
4227
+ format = NUMBER_FORMAT.INTERNATIONAL;
4228
+ }
4169
4229
  number = intlTelInput.utils.formatNumber(
4170
4230
  number,
4171
4231
  this.#selectedCountry?.iso2,
@@ -4262,14 +4322,14 @@ var Iti = class _Iti {
4262
4322
  return null;
4263
4323
  }
4264
4324
  //* Update the selected country, dial code (if separateDialCode), placeholder, title, and selected list item.
4265
- //* Note: called from setInitialState, updateCountryFromNumber, selectListItem, setCountry.
4325
+ //* Note: called from setInitialState, updateCountryFromNumber, selectListItem, setSelectedCountry.
4266
4326
  #updateSelectedCountry(iso2) {
4267
4327
  const prevIso2 = this.#selectedCountry?.iso2 || "";
4268
4328
  this.#selectedCountry = iso2 ? this.#countryByIso2.get(iso2) : null;
4269
4329
  if (this.#selectedCountry) {
4270
4330
  this.#fallbackCountryIso2 = this.#selectedCountry.iso2;
4271
4331
  }
4272
- this.#ui.setCountry(this.#selectedCountry);
4332
+ this.#ui.setSelectedCountry(this.#selectedCountry);
4273
4333
  this.#updatePlaceholder();
4274
4334
  this.#updateMaxCoreNumberLength();
4275
4335
  return prevIso2 !== iso2;
@@ -4287,12 +4347,11 @@ var Iti = class _Iti {
4287
4347
  }
4288
4348
  let exampleNumber = intlTelInput.utils.getExampleNumber(
4289
4349
  iso2,
4290
- false,
4291
4350
  placeholderNumberType,
4292
- true
4351
+ NUMBER_FORMAT.E164
4293
4352
  );
4294
4353
  let validNumber = exampleNumber;
4295
- while (intlTelInput.utils.isPossibleNumber(
4354
+ while (intlTelInput.utils.isValidNumber(
4296
4355
  exampleNumber,
4297
4356
  iso2,
4298
4357
  allowedNumberTypes
@@ -4309,19 +4368,19 @@ var Iti = class _Iti {
4309
4368
  //* Update the input placeholder to an example number from the currently selected country.
4310
4369
  #updatePlaceholder() {
4311
4370
  const {
4312
- autoPlaceholder,
4371
+ placeholderNumberPolicy,
4313
4372
  placeholderNumberType,
4314
- nationalMode,
4373
+ numberDisplayFormat,
4315
4374
  customPlaceholder
4316
4375
  } = this.#options;
4317
- const shouldSetPlaceholder = autoPlaceholder === PLACEHOLDER_MODES.AGGRESSIVE || !this.#ui.hadInitialPlaceholder && autoPlaceholder === PLACEHOLDER_MODES.POLITE;
4376
+ const shouldSetPlaceholder = placeholderNumberPolicy === PLACEHOLDER_POLICY.AGGRESSIVE || !this.#ui.hadInitialPlaceholder && placeholderNumberPolicy === PLACEHOLDER_POLICY.POLITE;
4318
4377
  if (!intlTelInput.utils || !shouldSetPlaceholder) {
4319
4378
  return;
4320
4379
  }
4321
4380
  let placeholder = this.#selectedCountry ? intlTelInput.utils.getExampleNumber(
4322
4381
  this.#selectedCountry.iso2,
4323
- nationalMode,
4324
- placeholderNumberType
4382
+ placeholderNumberType,
4383
+ numberDisplayFormat
4325
4384
  ) : "";
4326
4385
  placeholder = this.#prepareNumberForInput(placeholder);
4327
4386
  if (typeof customPlaceholder === "function") {
@@ -4329,36 +4388,40 @@ var Iti = class _Iti {
4329
4388
  }
4330
4389
  this.#ui.telInputEl.setAttribute("placeholder", placeholder);
4331
4390
  }
4332
- //* Called when the user selects a list item from the dropdown (no-op if listItem is null).
4391
+ //* Called when the user selects a list item from the country list (no-op if listItem is null).
4333
4392
  #selectListItem(listItem) {
4334
4393
  if (!listItem) {
4335
4394
  return;
4336
4395
  }
4337
4396
  const iso2 = listItem.dataset[DATA_KEYS.ISO2];
4338
4397
  const countryChanged = this.#updateSelectedCountry(iso2);
4339
- this.#closeDropdown();
4398
+ this.#closeCountrySelectorInternal();
4340
4399
  const dialCode = listItem.dataset[DATA_KEYS.DIAL_CODE];
4341
4400
  this.#updateDialCode(dialCode);
4342
- if (this.#options.formatOnDisplay) {
4343
- const inputValue = this.#getTelInputValue();
4344
- this.#updateValueFromNumber(inputValue);
4345
- }
4401
+ const inputValue = this.#getTelInputValue();
4402
+ this.#updateValueFromNumber(inputValue);
4346
4403
  this.#ui.telInputEl.focus();
4347
4404
  if (countryChanged) {
4348
4405
  this.#dispatchCountryChangeEvent();
4349
4406
  this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
4350
4407
  }
4351
4408
  }
4352
- //* Close the dropdown and unbind any listeners.
4353
- #closeDropdown(isDestroy) {
4354
- if (!this.#ui.isDropdownOpen() || this.#options.dropdownAlwaysOpen && !isDestroy) {
4409
+ //* Public: close the country selector (consumer-callable; delegates to the internal helper
4410
+ //* without the destroy-specific path).
4411
+ closeCountrySelector() {
4412
+ this.#closeCountrySelectorInternal();
4413
+ }
4414
+ //* Close the country selector and unbind any listeners. The isDestroy flag forces close even
4415
+ //* when dropdownAlwaysOpen is set, so destroy() can fully tear down.
4416
+ #closeCountrySelectorInternal(isDestroy) {
4417
+ if (!this.#ui.isCountrySelectorOpen() || this.#options.dropdownAlwaysOpen && !isDestroy) {
4355
4418
  return;
4356
4419
  }
4357
- this.#ui.closeDropdown();
4358
- this.#dispatchEvent(EVENTS.CLOSE_COUNTRY_DROPDOWN);
4420
+ this.#ui.closeCountrySelector();
4421
+ this.#dispatchEvent(EVENTS.CLOSE_COUNTRY_SELECTOR);
4359
4422
  }
4360
4423
  //* Replace any existing dial code with the new one
4361
- //* Note: called from selectListItem and setCountry
4424
+ //* Note: called from selectListItem and setSelectedCountry
4362
4425
  #updateDialCode(newDialCodeDigits) {
4363
4426
  const inputValue = this.#getTelInputValue();
4364
4427
  if (!inputValue.startsWith("+")) {
@@ -4438,7 +4501,7 @@ var Iti = class _Iti {
4438
4501
  //**************************
4439
4502
  //* INTERNAL METHODS
4440
4503
  //**************************
4441
- //* Called when the geoip call returns.
4504
+ //* Called when the initial country lookup returns.
4442
4505
  #handleAutoCountryLoaded() {
4443
4506
  if (!this.#autoCountryDeferred || !intlTelInput.autoCountry) {
4444
4507
  return;
@@ -4447,15 +4510,17 @@ var Iti = class _Iti {
4447
4510
  this.#autoCountryDeferred.resolve();
4448
4511
  return;
4449
4512
  }
4450
- if (this.#ui.isLoading()) {
4451
- this.setCountry(intlTelInput.autoCountry);
4513
+ const isFocused = document.activeElement === this.#ui.telInputEl;
4514
+ const hasTypedValue = Boolean(this.#getTelInputValue());
4515
+ if (this.#ui.isLoading() && !(isFocused && hasTypedValue)) {
4516
+ this.setSelectedCountry(intlTelInput.autoCountry);
4452
4517
  } else {
4453
4518
  this.#fallbackCountryIso2 = intlTelInput.autoCountry;
4454
4519
  }
4455
4520
  this.#ui.setLoading(false);
4456
4521
  this.#autoCountryDeferred.resolve();
4457
4522
  }
4458
- //* Called when the geoip call fails or times out.
4523
+ //* Called when the initial country lookup fails or times out.
4459
4524
  #handleAutoCountryFailure() {
4460
4525
  if (!this.#isActive) {
4461
4526
  this.#autoCountryDeferred?.reject();
@@ -4476,7 +4541,8 @@ var Iti = class _Iti {
4476
4541
  return;
4477
4542
  }
4478
4543
  const inputValue = this.#getTelInputValue();
4479
- if (inputValue) {
4544
+ const isFocused = document.activeElement === this.#ui.telInputEl;
4545
+ if (inputValue && !isFocused) {
4480
4546
  this.#updateValueFromNumber(inputValue);
4481
4547
  }
4482
4548
  if (this.#selectedCountry) {
@@ -4502,8 +4568,8 @@ var Iti = class _Iti {
4502
4568
  return;
4503
4569
  }
4504
4570
  this.#isActive = false;
4505
- if (this.#options.allowDropdown) {
4506
- this.#closeDropdown(true);
4571
+ if (this.#options.countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF) {
4572
+ this.#closeCountrySelectorInternal(true);
4507
4573
  }
4508
4574
  this.#abortController.abort();
4509
4575
  this.#ui.destroy();
@@ -4551,7 +4617,7 @@ var Iti = class _Iti {
4551
4617
  );
4552
4618
  }
4553
4619
  //* Get the country data for the currently selected country.
4554
- getSelectedCountryData() {
4620
+ getSelectedCountry() {
4555
4621
  return this.#selectedCountry ?? null;
4556
4622
  }
4557
4623
  //* Get the validation error e.g. "TOO_SHORT" / "TOO_LONG", or null if it can't be determined / instance is destroyed.
@@ -4606,7 +4672,7 @@ var Iti = class _Iti {
4606
4672
  if (!this.#selectedCountry && !hasRegionlessDialCode(value)) {
4607
4673
  return false;
4608
4674
  }
4609
- const check = mode === "precise" ? intlTelInput.utils.isValidNumber : intlTelInput.utils.isPossibleNumber;
4675
+ const check = mode === "precise" ? intlTelInput.utils.isValidNumberPrecise : intlTelInput.utils.isValidNumber;
4610
4676
  if (!check(value, iso2, allowedNumberTypes)) {
4611
4677
  return false;
4612
4678
  }
@@ -4619,7 +4685,7 @@ var Iti = class _Iti {
4619
4685
  return true;
4620
4686
  }
4621
4687
  //* Update the selected country, and update the input value accordingly.
4622
- setCountry(iso2) {
4688
+ setSelectedCountry(iso2) {
4623
4689
  if (!this.#isActive) {
4624
4690
  return;
4625
4691
  }
@@ -4634,10 +4700,8 @@ var Iti = class _Iti {
4634
4700
  }
4635
4701
  this.#updateSelectedCountry(iso2Lower);
4636
4702
  this.#updateDialCode(this.#selectedCountry?.dialCode || "");
4637
- if (this.#options.formatOnDisplay) {
4638
- const inputValue = this.#getTelInputValue();
4639
- this.#updateValueFromNumber(inputValue);
4640
- }
4703
+ const inputValue = this.#getTelInputValue();
4704
+ this.#updateValueFromNumber(inputValue);
4641
4705
  this.#dispatchCountryChangeEvent();
4642
4706
  this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
4643
4707
  }
@@ -4662,14 +4726,14 @@ var Iti = class _Iti {
4662
4726
  this.#options.placeholderNumberType = type;
4663
4727
  this.#updatePlaceholder();
4664
4728
  }
4665
- // Set the disabled state of the input and dropdown.
4729
+ // Set the disabled state of the input and country selector.
4666
4730
  setDisabled(disabled) {
4667
4731
  if (!this.#isActive) {
4668
4732
  return;
4669
4733
  }
4670
4734
  this.#ui.setDisabled(disabled);
4671
4735
  }
4672
- // Set the readonly state of the input and dropdown.
4736
+ // Set the readonly state of the input and country selector.
4673
4737
  setReadonly(readonly) {
4674
4738
  if (!this.#isActive) {
4675
4739
  return;
@@ -4679,7 +4743,7 @@ var Iti = class _Iti {
4679
4743
  //********************
4680
4744
  //* STATIC METHODS
4681
4745
  //********************
4682
- // Internal instance notification used by utils/geoip loaders.
4746
+ // Internal instance notification used by utils/initial-country loaders.
4683
4747
  // Kept public so module-level helpers (e.g. attachUtils) can call it, while still allowing
4684
4748
  // access to private instance methods.
4685
4749
  static forEachInstance(method, ...args) {
@@ -4743,8 +4807,8 @@ var intlTelInput = Object.assign(
4743
4807
  defaults,
4744
4808
  //* Using a static var like this allows us to mock it in the tests.
4745
4809
  documentReady: () => document.readyState === "complete",
4746
- //* Get the country data object.
4747
- getCountryData: () => data_default,
4810
+ //* Get the full list of all countries the library knows about.
4811
+ getAllCountries: () => data_default,
4748
4812
  //* A getter for the core library instance.
4749
4813
  getInstance: (input) => {
4750
4814
  const id = input.dataset[DATA_KEYS.INSTANCE_ID];
@@ -4755,17 +4819,21 @@ var intlTelInput = Object.assign(
4755
4819
  attachUtils,
4756
4820
  startedLoadingUtils: false,
4757
4821
  startedLoadingAutoCountry: false,
4758
- version: "28.0.9",
4822
+ version: "29.0.0",
4759
4823
  NUMBER_FORMAT,
4760
4824
  NUMBER_TYPE,
4761
- VALIDATION_ERROR
4825
+ VALIDATION_ERROR,
4826
+ PLACEHOLDER_POLICY,
4827
+ COUNTRY_SELECTOR_MODE
4762
4828
  }
4763
4829
  );
4764
4830
  var intlTelInput_default = intlTelInput;
4765
4831
  export {
4832
+ COUNTRY_SELECTOR_MODE,
4766
4833
  Iti,
4767
4834
  NUMBER_FORMAT,
4768
4835
  NUMBER_TYPE,
4836
+ PLACEHOLDER_POLICY,
4769
4837
  VALIDATION_ERROR,
4770
4838
  intlTelInput_default as default
4771
4839
  };