intl-tel-input 28.1.0 → 29.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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 +362 -300
  9. package/dist/js/intlTelInput.min.js +2 -2
  10. package/dist/js/intlTelInput.mjs +361 -299
  11. package/dist/js/intlTelInputWithUtils.js +734 -726
  12. package/dist/js/intlTelInputWithUtils.min.js +2 -2
  13. package/dist/js/intlTelInputWithUtils.mjs +733 -725
  14. package/dist/js/locale.d.ts +124 -0
  15. package/dist/js/utils.js +83 -83
  16. package/package.json +7 -7
  17. package/dist/js/i18n.d.ts +0 -124
  18. /package/dist/js/{i18n → locale}/ar.js +0 -0
  19. /package/dist/js/{i18n → locale}/bg.js +0 -0
  20. /package/dist/js/{i18n → locale}/bn.js +0 -0
  21. /package/dist/js/{i18n → locale}/bs.js +0 -0
  22. /package/dist/js/{i18n → locale}/ca.js +0 -0
  23. /package/dist/js/{i18n → locale}/cs.js +0 -0
  24. /package/dist/js/{i18n → locale}/da.js +0 -0
  25. /package/dist/js/{i18n → locale}/de.js +0 -0
  26. /package/dist/js/{i18n → locale}/el.js +0 -0
  27. /package/dist/js/{i18n → locale}/en.js +0 -0
  28. /package/dist/js/{i18n → locale}/es.js +0 -0
  29. /package/dist/js/{i18n → locale}/et.js +0 -0
  30. /package/dist/js/{i18n → locale}/fa.js +0 -0
  31. /package/dist/js/{i18n → locale}/fi.js +0 -0
  32. /package/dist/js/{i18n → locale}/fil.js +0 -0
  33. /package/dist/js/{i18n → locale}/fr.js +0 -0
  34. /package/dist/js/{i18n → locale}/he.js +0 -0
  35. /package/dist/js/{i18n → locale}/hi.js +0 -0
  36. /package/dist/js/{i18n → locale}/hr.js +0 -0
  37. /package/dist/js/{i18n → locale}/hu.js +0 -0
  38. /package/dist/js/{i18n → locale}/hy.js +0 -0
  39. /package/dist/js/{i18n → locale}/id.js +0 -0
  40. /package/dist/js/{i18n → locale}/index.js +0 -0
  41. /package/dist/js/{i18n → locale}/is.js +0 -0
  42. /package/dist/js/{i18n → locale}/it.js +0 -0
  43. /package/dist/js/{i18n → locale}/ja.js +0 -0
  44. /package/dist/js/{i18n → locale}/kn.js +0 -0
  45. /package/dist/js/{i18n → locale}/ko.js +0 -0
  46. /package/dist/js/{i18n → locale}/lt.js +0 -0
  47. /package/dist/js/{i18n → locale}/lv.js +0 -0
  48. /package/dist/js/{i18n → locale}/mk.js +0 -0
  49. /package/dist/js/{i18n → locale}/mr.js +0 -0
  50. /package/dist/js/{i18n → locale}/ms.js +0 -0
  51. /package/dist/js/{i18n → locale}/nl.js +0 -0
  52. /package/dist/js/{i18n → locale}/no.js +0 -0
  53. /package/dist/js/{i18n → locale}/pl.js +0 -0
  54. /package/dist/js/{i18n → locale}/pt.js +0 -0
  55. /package/dist/js/{i18n → locale}/ro.js +0 -0
  56. /package/dist/js/{i18n → locale}/ru.js +0 -0
  57. /package/dist/js/{i18n → locale}/sk.js +0 -0
  58. /package/dist/js/{i18n → locale}/sl.js +0 -0
  59. /package/dist/js/{i18n → locale}/sq.js +0 -0
  60. /package/dist/js/{i18n → locale}/sr.js +0 -0
  61. /package/dist/js/{i18n → locale}/sv.js +0 -0
  62. /package/dist/js/{i18n → locale}/sw.js +0 -0
  63. /package/dist/js/{i18n → locale}/ta.js +0 -0
  64. /package/dist/js/{i18n → locale}/te.js +0 -0
  65. /package/dist/js/{i18n → locale}/th.js +0 -0
  66. /package/dist/js/{i18n → locale}/tr.js +0 -0
  67. /package/dist/js/{i18n → locale}/types.js +0 -0
  68. /package/dist/js/{i18n → locale}/uk.js +0 -0
  69. /package/dist/js/{i18n → locale}/ur.js +0 -0
  70. /package/dist/js/{i18n → locale}/uz.js +0 -0
  71. /package/dist/js/{i18n → locale}/vi.js +0 -0
  72. /package/dist/js/{i18n → locale}/zh-hk.js +0 -0
  73. /package/dist/js/{i18n → locale}/zh.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
@@ -1800,9 +1800,9 @@ var TIMINGS = {
1800
1800
  var LAYOUT = {
1801
1801
  NARROW_VIEWPORT_WIDTH: 500,
1802
1802
  // keep in sync with .iti__country-list CSS media query
1803
- FALLBACK_SELECTED_WITH_DIAL_WIDTH: 78,
1803
+ FALLBACK_SELECTED_COUNTRY_WITH_DIAL_WIDTH: 78,
1804
1804
  // px width fallback when separateDialCode enabled
1805
- FALLBACK_SELECTED_NO_DIAL_WIDTH: 42,
1805
+ FALLBACK_SELECTED_COUNTRY_NO_DIAL_WIDTH: 42,
1806
1806
  // px width fallback when no separate dial code
1807
1807
  INPUT_PADDING_EXTRA_LEFT: 6,
1808
1808
  // px gap between selected country container and input text
@@ -1830,14 +1830,17 @@ var US = {
1830
1830
  DIAL_CODE: "1"
1831
1831
  // +1 United States
1832
1832
  };
1833
- var PLACEHOLDER_MODES = {
1834
- AGGRESSIVE: "aggressive",
1835
- POLITE: "polite",
1836
- OFF: "off"
1837
- };
1838
- var INITIAL_COUNTRY = {
1839
- AUTO: "auto"
1833
+ var PLACEHOLDER_POLICY = {
1834
+ AGGRESSIVE: "AGGRESSIVE",
1835
+ POLITE: "POLITE",
1836
+ OFF: "OFF"
1840
1837
  };
1838
+ var COUNTRY_SELECTOR_MODES = [
1839
+ "OFF",
1840
+ "DROPDOWN",
1841
+ "FULLSCREEN",
1842
+ "AUTO"
1843
+ ];
1841
1844
  var NUMBER_FORMATS = [
1842
1845
  "E164",
1843
1846
  "INTERNATIONAL",
@@ -1870,8 +1873,9 @@ var toEnumObject = (arr) => Object.fromEntries(arr.map((v) => [v, v]));
1870
1873
  var NUMBER_FORMAT = toEnumObject(NUMBER_FORMATS);
1871
1874
  var NUMBER_TYPE = toEnumObject(NUMBER_TYPES);
1872
1875
  var VALIDATION_ERROR = toEnumObject(VALIDATION_ERRORS);
1876
+ var COUNTRY_SELECTOR_MODE = toEnumObject(COUNTRY_SELECTOR_MODES);
1873
1877
  var DATA_KEYS = {
1874
- // 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
1875
1879
  ISO2: "iso2",
1876
1880
  DIAL_CODE: "dialCode",
1877
1881
  // e.g. <input data-intl-tel-input-id="0"> on the input element
@@ -1889,7 +1893,7 @@ var ARIA = {
1889
1893
  MODAL: "aria-modal"
1890
1894
  };
1891
1895
 
1892
- // packages/core/src/js/i18n/en.ts
1896
+ // packages/core/src/js/locale/en.ts
1893
1897
  var interfaceTranslations = {
1894
1898
  selectedCountryAriaLabel: "Change country for phone number, currently selected ${countryName} (${dialCode})",
1895
1899
  noCountrySelected: "Select country for phone number",
@@ -1912,63 +1916,63 @@ var en_default = interfaceTranslations;
1912
1916
  // packages/core/src/js/core/options.ts
1913
1917
  var mediaQuery = (q) => typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia(q).matches;
1914
1918
  var isNarrowViewport = () => mediaQuery(`(max-width: ${LAYOUT.NARROW_VIEWPORT_WIDTH}px)`);
1915
- var computeDefaultUseFullscreenPopup = () => {
1919
+ var resolveAutoCountrySelectorMode = () => {
1916
1920
  if (typeof navigator !== "undefined" && typeof window !== "undefined") {
1917
1921
  const isShortViewport = mediaQuery("(max-height: 600px)");
1918
1922
  const isCoarsePointer = mediaQuery("(pointer: coarse)");
1919
- return isNarrowViewport() || isCoarsePointer && isShortViewport;
1923
+ if (isNarrowViewport() || isCoarsePointer && isShortViewport) {
1924
+ return COUNTRY_SELECTOR_MODE.FULLSCREEN;
1925
+ }
1920
1926
  }
1921
- return false;
1927
+ return COUNTRY_SELECTOR_MODE.DROPDOWN;
1922
1928
  };
1923
1929
  var defaults = {
1924
- //* Whether or not to allow the dropdown.
1925
- 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,
1926
1932
  //* The number type to enforce during validation.
1927
1933
  allowedNumberTypes: [NUMBER_TYPE.MOBILE, NUMBER_TYPE.FIXED_LINE],
1928
1934
  //* Whether or not to allow extensions after the main number.
1929
1935
  allowNumberExtensions: false,
1930
1936
  // Allow alphanumeric "phonewords" (e.g. +1 800 FLOWERS) as valid numbers
1931
1937
  allowPhonewords: false,
1932
- //* Add a placeholder in the input with an example number for the selected country.
1933
- autoPlaceholder: PLACEHOLDER_MODES.POLITE,
1934
1938
  //* Add a custom class to the (injected) container element.
1935
1939
  containerClass: "",
1936
1940
  //* Locale for localising country names via Intl.DisplayNames.
1937
1941
  countryNameLocale: "en",
1938
1942
  //* Override individual country names by iso2 code.
1939
1943
  countryNameOverrides: {},
1940
- //* The order of the countries in the dropdown. Defaults to alphabetical.
1944
+ //* The order of the countries in the country list. Defaults to alphabetical.
1941
1945
  countryOrder: null,
1942
- //* Add a country search input at the top of the dropdown.
1946
+ //* Add a country search input at the top of the country selector.
1943
1947
  countrySearch: true,
1944
1948
  //* Modify the auto placeholder.
1945
1949
  customPlaceholder: null,
1946
1950
  //* Always show the dropdown
1947
1951
  dropdownAlwaysOpen: false,
1948
- //* Append menu to specified element.
1949
- 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,
1950
1954
  //* Don't display these countries.
1951
1955
  excludeCountries: null,
1952
1956
  //* Fix the dropdown width to the input width (rather than being as wide as the longest country name).
1953
- fixDropdownWidth: true,
1957
+ matchDropdownWidth: true,
1954
1958
  //* Format the number as the user types
1955
1959
  formatAsYouType: true,
1956
- //* Format the input value during initialisation and on setNumber.
1957
- formatOnDisplay: true,
1958
- //* geoIp lookup function.
1959
- geoIpLookup: null,
1960
- //* Inject a hidden input with the name returned from this function, and on submit, populate it with the result of getNumber.
1961
- hiddenInput: null,
1962
- //* Internationalise the core library text e.g. search input placeholder, country names.
1963
- 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: {},
1964
1964
  //* Initial country.
1965
1965
  initialCountry: "",
1966
+ //* Async lookup function used to determine the initial country (e.g. via IP). Ignored if initialCountry is set.
1967
+ initialCountryLookup: null,
1966
1968
  //* A function to load the utils script.
1967
1969
  loadUtils: null,
1968
- //* National vs international formatting for numbers e.g. placeholders and displaying existing numbers.
1969
- nationalMode: false,
1970
+ //* Format used when displaying numbers (placeholder examples and stored values). One of "E164", "INTERNATIONAL", "NATIONAL".
1971
+ numberDisplayFormat: NUMBER_FORMAT.INTERNATIONAL,
1970
1972
  //* Display only these countries.
1971
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,
1972
1976
  //* Number type to use for placeholders.
1973
1977
  placeholderNumberType: NUMBER_TYPE.MOBILE,
1974
1978
  //* Add custom classes to the search input element.
@@ -1977,12 +1981,10 @@ var defaults = {
1977
1981
  separateDialCode: true,
1978
1982
  //* When strictMode rejects a key (etc), play a short feedback animation
1979
1983
  strictRejectAnimation: true,
1980
- //* 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
1981
1985
  showFlags: true,
1982
1986
  //* Only allow certain chars e.g. a plus followed by numeric digits, and cap at max valid length.
1983
- strictMode: true,
1984
- //* Use full screen popup instead of dropdown for country list.
1985
- useFullscreenPopup: computeDefaultUseFullscreenPopup()
1987
+ strictMode: true
1986
1988
  };
1987
1989
  var toString = (val) => JSON.stringify(val);
1988
1990
  var isPlainObject = (val) => Boolean(val) && typeof val === "object" && !Array.isArray(val);
@@ -1994,7 +1996,7 @@ var isElLike = (val) => {
1994
1996
  const v = val;
1995
1997
  return v.nodeType === 1 && typeof v.tagName === "string" && typeof v.appendChild === "function";
1996
1998
  };
1997
- var placeholderModeSet = new Set(Object.values(PLACEHOLDER_MODES));
1999
+ var placeholderPolicySet = new Set(Object.values(PLACEHOLDER_POLICY));
1998
2000
  var warn = (message) => {
1999
2001
  console.warn(`[intl-tel-input] ${message}`);
2000
2002
  };
@@ -2040,30 +2042,48 @@ var validateOptions = (customOptions) => {
2040
2042
  continue;
2041
2043
  }
2042
2044
  switch (key) {
2043
- case "allowDropdown":
2044
2045
  case "allowNumberExtensions":
2045
2046
  case "allowPhonewords":
2046
2047
  case "countrySearch":
2047
2048
  case "dropdownAlwaysOpen":
2048
- case "fixDropdownWidth":
2049
+ case "matchDropdownWidth":
2049
2050
  case "formatAsYouType":
2050
- case "formatOnDisplay":
2051
- case "nationalMode":
2052
2051
  case "showFlags":
2053
2052
  case "separateDialCode":
2054
2053
  case "strictMode":
2055
2054
  case "strictRejectAnimation":
2056
- case "useFullscreenPopup":
2057
2055
  if (typeof value !== "boolean") {
2058
2056
  warnOption(key, "a boolean", value);
2059
2057
  break;
2060
2058
  }
2061
2059
  validatedOptions[key] = value;
2062
2060
  break;
2063
- case "autoPlaceholder":
2064
- if (typeof value !== "string" || !placeholderModeSet.has(value)) {
2065
- const validModes = Array.from(placeholderModeSet).join(", ");
2066
- 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);
2067
2087
  break;
2068
2088
  }
2069
2089
  validatedOptions[key] = value;
@@ -2089,8 +2109,8 @@ var validateOptions = (customOptions) => {
2089
2109
  break;
2090
2110
  }
2091
2111
  case "customPlaceholder":
2092
- case "geoIpLookup":
2093
- case "hiddenInput":
2112
+ case "hiddenInputs":
2113
+ case "initialCountryLookup":
2094
2114
  case "loadUtils":
2095
2115
  if (value !== null && !isFunction(value)) {
2096
2116
  warnOption(key, "a function or null", value);
@@ -2098,9 +2118,9 @@ var validateOptions = (customOptions) => {
2098
2118
  }
2099
2119
  validatedOptions[key] = value;
2100
2120
  break;
2101
- case "dropdownContainer":
2121
+ case "dropdownParent":
2102
2122
  if (value !== null && !isElLike(value)) {
2103
- warnOption("dropdownContainer", "an HTMLElement or null", value);
2123
+ warnOption("dropdownParent", "an HTMLElement or null", value);
2104
2124
  break;
2105
2125
  }
2106
2126
  validatedOptions[key] = value;
@@ -2117,9 +2137,9 @@ var validateOptions = (customOptions) => {
2117
2137
  }
2118
2138
  break;
2119
2139
  }
2120
- case "i18n":
2140
+ case "uiTranslations":
2121
2141
  if (value && !isPlainObject(value)) {
2122
- warnOption("i18n", "an object", value);
2142
+ warnOption("uiTranslations", "an object", value);
2123
2143
  break;
2124
2144
  }
2125
2145
  validatedOptions[key] = value;
@@ -2137,12 +2157,8 @@ var validateOptions = (customOptions) => {
2137
2157
  break;
2138
2158
  }
2139
2159
  const lower = value.toLowerCase();
2140
- if (lower && lower !== INITIAL_COUNTRY.AUTO && !isIso2(lower)) {
2141
- warnOption(
2142
- "initialCountry",
2143
- "a valid iso2 country code or 'auto'",
2144
- value
2145
- );
2160
+ if (lower && !isIso2(lower)) {
2161
+ warnOption("initialCountry", "a valid iso2 country code", value);
2146
2162
  break;
2147
2163
  }
2148
2164
  validatedOptions[key] = value;
@@ -2205,30 +2221,29 @@ var normaliseOptions = (o) => {
2205
2221
  }
2206
2222
  };
2207
2223
  var applyOptionSideEffects = (o) => {
2224
+ if (o.countrySelectorMode === COUNTRY_SELECTOR_MODE.AUTO) {
2225
+ o.countrySelectorMode = resolveAutoCountrySelectorMode();
2226
+ }
2208
2227
  if (o.dropdownAlwaysOpen) {
2209
- o.useFullscreenPopup = false;
2210
- o.allowDropdown = true;
2228
+ o.countrySelectorMode = COUNTRY_SELECTOR_MODE.DROPDOWN;
2211
2229
  }
2212
- if (o.useFullscreenPopup) {
2213
- o.fixDropdownWidth = false;
2230
+ if (o.countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN) {
2231
+ o.matchDropdownWidth = false;
2214
2232
  } else {
2215
2233
  if (isNarrowViewport()) {
2216
- o.fixDropdownWidth = true;
2234
+ o.matchDropdownWidth = true;
2217
2235
  }
2218
2236
  }
2219
2237
  if (o.onlyCountries?.length === 1) {
2220
2238
  o.initialCountry = o.onlyCountries[0];
2221
2239
  }
2222
- if (o.separateDialCode) {
2223
- o.nationalMode = false;
2224
- }
2225
- if (o.allowDropdown && !o.showFlags && !o.separateDialCode) {
2226
- o.nationalMode = false;
2240
+ if (o.separateDialCode && o.numberDisplayFormat === NUMBER_FORMAT.NATIONAL) {
2241
+ o.numberDisplayFormat = NUMBER_FORMAT.INTERNATIONAL;
2227
2242
  }
2228
- if (o.useFullscreenPopup && !o.dropdownContainer) {
2229
- o.dropdownContainer = document.body;
2243
+ if (o.countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF && !o.showFlags && !o.separateDialCode && o.numberDisplayFormat === NUMBER_FORMAT.NATIONAL) {
2244
+ o.numberDisplayFormat = NUMBER_FORMAT.INTERNATIONAL;
2230
2245
  }
2231
- o.i18n = { ...en_default, ...o.i18n };
2246
+ o.uiTranslations = { ...en_default, ...o.uiTranslations };
2232
2247
  };
2233
2248
 
2234
2249
  // packages/core/src/js/helpers/string.ts
@@ -2439,6 +2454,7 @@ var Numerals = class _Numerals {
2439
2454
  };
2440
2455
 
2441
2456
  // packages/core/src/js/core/ui.ts
2457
+ var supportsCssAnchor = typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("anchor-name: --x");
2442
2458
  var UI = class _UI {
2443
2459
  // private
2444
2460
  #options;
@@ -2453,8 +2469,8 @@ var UI = class _UI {
2453
2469
  #selectedCountryEl;
2454
2470
  #selectedFlagEl;
2455
2471
  #selectedDialCodeEl;
2456
- #dropdownArrowEl;
2457
- #dropdownContentEl;
2472
+ #arrowEl;
2473
+ #countrySelectorEl;
2458
2474
  #searchIconEl;
2459
2475
  #searchInputEl;
2460
2476
  #searchClearButtonEl;
@@ -2463,11 +2479,11 @@ var UI = class _UI {
2463
2479
  #hiddenInputCountryEl;
2464
2480
  #noResultsMessageEl;
2465
2481
  #searchResultsLiveRegionEl;
2466
- #detachedDropdownEl;
2482
+ #detachedCountrySelectorEl;
2467
2483
  #selectedListItemEl = null;
2468
2484
  #highlightedListItemEl = null;
2469
2485
  #listItemByIso2 = /* @__PURE__ */ new Map();
2470
- #dropdownAbortController = null;
2486
+ #countrySelectorAbortController = null;
2471
2487
  #resizeObserver;
2472
2488
  // public
2473
2489
  telInputEl;
@@ -2492,7 +2508,7 @@ var UI = class _UI {
2492
2508
  );
2493
2509
  }
2494
2510
  }
2495
- //* 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.
2496
2512
  buildMarkup(countries, searchTokens) {
2497
2513
  this.#countries = countries;
2498
2514
  this.#searchTokens = searchTokens;
@@ -2515,13 +2531,13 @@ var UI = class _UI {
2515
2531
  this.ensureDropdownWidthSet();
2516
2532
  }
2517
2533
  #createWrapperAndInsert() {
2518
- const { allowDropdown, showFlags, containerClass, useFullscreenPopup } = this.#options;
2534
+ const { countrySelectorMode, showFlags, containerClass } = this.#options;
2519
2535
  const parentClasses = buildClassNames({
2520
2536
  iti: true,
2521
2537
  "iti--input-container": true,
2522
- "iti--allow-dropdown": allowDropdown,
2538
+ "iti--has-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF,
2523
2539
  "iti--show-flags": showFlags,
2524
- "iti--inline-dropdown": !useFullscreenPopup,
2540
+ "iti--inline-country-selector": countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN,
2525
2541
  [containerClass]: Boolean(containerClass)
2526
2542
  });
2527
2543
  const wrapper = createEl("div", { class: parentClasses });
@@ -2532,8 +2548,9 @@ var UI = class _UI {
2532
2548
  return wrapper;
2533
2549
  }
2534
2550
  #buildCountryContainer(wrapper) {
2535
- const { allowDropdown, separateDialCode, showFlags } = this.#options;
2536
- if (!allowDropdown && !showFlags && !separateDialCode) {
2551
+ const { countrySelectorMode, separateDialCode, showFlags } = this.#options;
2552
+ const enableCountrySelector = countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF;
2553
+ if (!enableCountrySelector && !showFlags && !separateDialCode) {
2537
2554
  return;
2538
2555
  }
2539
2556
  this.#countryContainerEl = createEl(
@@ -2542,16 +2559,16 @@ var UI = class _UI {
2542
2559
  { class: `iti__country-container ${CLASSES.V_HIDE}` },
2543
2560
  wrapper
2544
2561
  );
2545
- if (allowDropdown) {
2562
+ if (enableCountrySelector) {
2546
2563
  this.#selectedCountryEl = createEl(
2547
2564
  "button",
2548
2565
  {
2549
2566
  type: "button",
2550
2567
  class: "iti__selected-country",
2551
2568
  [ARIA.EXPANDED]: "false",
2552
- [ARIA.LABEL]: this.#options.i18n.noCountrySelected,
2569
+ [ARIA.LABEL]: this.#options.uiTranslations.noCountrySelected,
2553
2570
  [ARIA.HASPOPUP]: "dialog",
2554
- [ARIA.CONTROLS]: `iti-${this.#id}__dropdown-content`
2571
+ [ARIA.CONTROLS]: `iti-${this.#id}__country-selector`
2555
2572
  },
2556
2573
  this.#countryContainerEl
2557
2574
  );
@@ -2575,8 +2592,8 @@ var UI = class _UI {
2575
2592
  { class: CLASSES.FLAG },
2576
2593
  selectedCountryPrimary
2577
2594
  );
2578
- if (allowDropdown) {
2579
- this.#dropdownArrowEl = createEl(
2595
+ if (enableCountrySelector) {
2596
+ this.#arrowEl = createEl(
2580
2597
  "div",
2581
2598
  { class: "iti__arrow", [ARIA.HIDDEN]: "true" },
2582
2599
  selectedCountryPrimary
@@ -2589,38 +2606,39 @@ var UI = class _UI {
2589
2606
  this.#selectedCountryEl
2590
2607
  );
2591
2608
  }
2592
- if (allowDropdown) {
2593
- this.#buildDropdownContent();
2609
+ if (enableCountrySelector) {
2610
+ this.#buildCountrySelector();
2594
2611
  }
2595
2612
  }
2596
2613
  ensureDropdownWidthSet() {
2597
- const { fixDropdownWidth, allowDropdown } = this.#options;
2598
- 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) {
2599
2616
  return;
2600
2617
  }
2601
2618
  const inputWidth = this.telInputEl.offsetWidth;
2602
2619
  if (inputWidth > 0) {
2603
- this.#dropdownContentEl.style.width = `${inputWidth}px`;
2620
+ this.#countrySelectorEl.style.width = `${inputWidth}px`;
2604
2621
  }
2605
2622
  }
2606
- #buildDropdownContent() {
2623
+ #buildCountrySelector() {
2607
2624
  const {
2608
- fixDropdownWidth,
2609
- useFullscreenPopup,
2625
+ matchDropdownWidth,
2626
+ countrySelectorMode,
2610
2627
  countrySearch,
2611
- i18n,
2612
- dropdownContainer,
2628
+ uiTranslations,
2613
2629
  containerClass
2614
2630
  } = this.#options;
2615
- const extraClasses = fixDropdownWidth ? "" : "iti--flexible-dropdown-width";
2616
- this.#dropdownContentEl = createEl("div", {
2617
- id: `iti-${this.#id}__dropdown-content`,
2618
- 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}`,
2619
2637
  role: "dialog",
2620
2638
  [ARIA.MODAL]: "true"
2621
2639
  });
2622
2640
  if (this.#isRTL) {
2623
- this.#dropdownContentEl.setAttribute("dir", "rtl");
2641
+ this.#countrySelectorEl.setAttribute("dir", "rtl");
2624
2642
  }
2625
2643
  if (countrySearch) {
2626
2644
  this.#buildSearchUI();
@@ -2631,40 +2649,54 @@ var UI = class _UI {
2631
2649
  class: "iti__country-list",
2632
2650
  id: `iti-${this.#id}__country-listbox`,
2633
2651
  role: "listbox",
2634
- [ARIA.LABEL]: i18n.countryListAriaLabel
2652
+ [ARIA.LABEL]: uiTranslations.countryListAriaLabel
2635
2653
  },
2636
- this.#dropdownContentEl
2654
+ this.#countrySelectorEl
2637
2655
  );
2638
2656
  this.#appendListItems();
2639
2657
  if (countrySearch) {
2640
2658
  this.#updateSearchResultsA11yText();
2641
2659
  }
2642
- if (!useFullscreenPopup) {
2660
+ if (!isFullscreen) {
2643
2661
  this.#inlineDropdownHeight = this.#getHiddenInlineDropdownHeight();
2644
2662
  if (countrySearch) {
2645
- this.#dropdownContentEl.style.height = `${this.#inlineDropdownHeight}px`;
2663
+ this.#countrySelectorEl.style.height = `${this.#inlineDropdownHeight}px`;
2646
2664
  }
2647
2665
  }
2648
- if (dropdownContainer) {
2649
- const dropdownClasses = buildClassNames({
2666
+ if (detachedParent) {
2667
+ const wrapperClasses = buildClassNames({
2650
2668
  iti: true,
2651
- "iti--container": true,
2652
- "iti--fullscreen-popup": useFullscreenPopup,
2653
- "iti--inline-dropdown": !useFullscreenPopup,
2669
+ "iti--detached-country-selector": true,
2670
+ "iti--fullscreen-popup": isFullscreen,
2671
+ "iti--inline-country-selector": !isFullscreen,
2654
2672
  [containerClass]: Boolean(containerClass)
2655
2673
  });
2656
- this.#detachedDropdownEl = createEl("div", { class: dropdownClasses });
2657
- 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
+ }
2658
2679
  } else {
2659
- this.#countryContainerEl.appendChild(this.#dropdownContentEl);
2680
+ this.#countryContainerEl.appendChild(this.#countrySelectorEl);
2681
+ }
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;
2660
2688
  }
2689
+ if (countrySelectorMode === COUNTRY_SELECTOR_MODE.DROPDOWN) {
2690
+ return dropdownParent;
2691
+ }
2692
+ return null;
2661
2693
  }
2662
2694
  #buildSearchUI() {
2663
- const { i18n, searchInputClass } = this.#options;
2695
+ const { uiTranslations, searchInputClass } = this.#options;
2664
2696
  const searchWrapper = createEl(
2665
2697
  "div",
2666
2698
  { class: "iti__search-input-wrapper" },
2667
- this.#dropdownContentEl
2699
+ this.#countrySelectorEl
2668
2700
  );
2669
2701
  this.#searchIconEl = createEl(
2670
2702
  "span",
@@ -2682,11 +2714,11 @@ var UI = class _UI {
2682
2714
  // Chrome says inputs need either a name or an id
2683
2715
  type: "search",
2684
2716
  class: `iti__search-input ${searchInputClass}`,
2685
- placeholder: i18n.searchPlaceholder,
2717
+ placeholder: uiTranslations.searchPlaceholder,
2686
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
2687
2719
  role: "combobox",
2688
2720
  [ARIA.EXPANDED]: "true",
2689
- [ARIA.LABEL]: i18n.searchPlaceholder,
2721
+ [ARIA.LABEL]: uiTranslations.searchPlaceholder,
2690
2722
  [ARIA.CONTROLS]: `iti-${this.#id}__country-listbox`,
2691
2723
  [ARIA.AUTOCOMPLETE]: "list",
2692
2724
  autocomplete: "off"
@@ -2698,7 +2730,7 @@ var UI = class _UI {
2698
2730
  {
2699
2731
  type: "button",
2700
2732
  class: `iti__search-clear ${CLASSES.HIDE}`,
2701
- [ARIA.LABEL]: i18n.clearSearchAriaLabel,
2733
+ [ARIA.LABEL]: uiTranslations.clearSearchAriaLabel,
2702
2734
  tabindex: "-1"
2703
2735
  },
2704
2736
  searchWrapper
@@ -2707,7 +2739,7 @@ var UI = class _UI {
2707
2739
  this.#searchResultsLiveRegionEl = createEl(
2708
2740
  "span",
2709
2741
  { class: "iti__a11y-text" },
2710
- this.#dropdownContentEl
2742
+ this.#countrySelectorEl
2711
2743
  );
2712
2744
  this.#noResultsMessageEl = createEl(
2713
2745
  "div",
@@ -2716,9 +2748,9 @@ var UI = class _UI {
2716
2748
  [ARIA.HIDDEN]: "true"
2717
2749
  // all a11y messaging happens in this.#searchResultsLiveRegionEl
2718
2750
  },
2719
- this.#dropdownContentEl
2751
+ this.#countrySelectorEl
2720
2752
  );
2721
- this.#noResultsMessageEl.textContent = i18n.searchEmptyState ?? null;
2753
+ this.#noResultsMessageEl.textContent = uiTranslations.searchEmptyState ?? null;
2722
2754
  }
2723
2755
  #updateInputPaddingAndReveal() {
2724
2756
  if (!this.#countryContainerEl) {
@@ -2728,12 +2760,12 @@ var UI = class _UI {
2728
2760
  this.#countryContainerEl.classList.remove(CLASSES.V_HIDE);
2729
2761
  }
2730
2762
  #buildHiddenInputs(wrapper) {
2731
- const { hiddenInput } = this.#options;
2732
- if (!hiddenInput) {
2763
+ const { hiddenInputs } = this.#options;
2764
+ if (!hiddenInputs) {
2733
2765
  return;
2734
2766
  }
2735
2767
  const telInputName = this.telInputEl.getAttribute("name") || "";
2736
- const names = hiddenInput(telInputName);
2768
+ const names = hiddenInputs(telInputName);
2737
2769
  if (names.phone) {
2738
2770
  const existingInput = this.telInputEl.form?.querySelector(
2739
2771
  `input[name="${names.phone}"]`
@@ -2798,7 +2830,7 @@ var UI = class _UI {
2798
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.
2799
2831
  #updateInputPadding() {
2800
2832
  if (this.#selectedCountryEl) {
2801
- 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;
2802
2834
  const selectedCountryWidth = this.#selectedCountryEl.offsetWidth || this.#getHiddenSelectedCountryWidth() || fallbackWidth;
2803
2835
  const inputPadding = selectedCountryWidth + LAYOUT.INPUT_PADDING_EXTRA_LEFT;
2804
2836
  this.telInputEl.style.paddingLeft = `${inputPadding}px`;
@@ -2852,24 +2884,24 @@ var UI = class _UI {
2852
2884
  // Get the dropdown height (before it is added to the DOM)
2853
2885
  #getHiddenInlineDropdownHeight() {
2854
2886
  const body = _UI.#getBody();
2855
- this.#dropdownContentEl.classList.remove(CLASSES.HIDE);
2887
+ this.#countrySelectorEl.classList.remove(CLASSES.HIDE);
2856
2888
  const tempContainer = createEl("div", {
2857
- class: "iti iti--inline-dropdown"
2889
+ class: "iti iti--inline-country-selector"
2858
2890
  });
2859
- tempContainer.appendChild(this.#dropdownContentEl);
2891
+ tempContainer.appendChild(this.#countrySelectorEl);
2860
2892
  tempContainer.style.visibility = "hidden";
2861
2893
  body.appendChild(tempContainer);
2862
- const height = this.#dropdownContentEl.offsetHeight;
2894
+ const height = this.#countrySelectorEl.offsetHeight;
2863
2895
  body.removeChild(tempContainer);
2864
2896
  tempContainer.style.visibility = "";
2865
- this.#dropdownContentEl.classList.add(CLASSES.HIDE);
2897
+ this.#countrySelectorEl.classList.add(CLASSES.HIDE);
2866
2898
  return height > 0 ? height : LAYOUT.FALLBACK_DROPDOWN_HEIGHT;
2867
2899
  }
2868
2900
  //* Update search results text (for a11y).
2869
2901
  #updateSearchResultsA11yText() {
2870
- const { i18n } = this.#options;
2902
+ const { uiTranslations } = this.#options;
2871
2903
  const count = this.#countryListEl.childElementCount;
2872
- this.#searchResultsLiveRegionEl.textContent = i18n.searchSummaryAria(count);
2904
+ this.#searchResultsLiveRegionEl.textContent = uiTranslations.searchSummaryAria(count);
2873
2905
  }
2874
2906
  //* Country search: Filter the countries according to the search query.
2875
2907
  #filterCountriesByQuery(query) {
@@ -2887,8 +2919,8 @@ var UI = class _UI {
2887
2919
  this.#showFilteredCountries(matchedCountries);
2888
2920
  }
2889
2921
  //* Pre-fill the search input with "+" and show all countries
2890
- //* (used when user types "+" in the phone input to open the dropdown).
2891
- //* 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
2892
2924
  //* dropdownAlwaysOpen, but here we need focus to redirect subsequent keystrokes).
2893
2925
  prefillSearchWithPlus() {
2894
2926
  this.#searchInputEl.value = "+";
@@ -2971,15 +3003,15 @@ var UI = class _UI {
2971
3003
  { signal }
2972
3004
  );
2973
3005
  }
2974
- //* 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),
2975
3007
  //* selected-country click (open), and keydown on countryContainer (open on arrow/space/enter, close on tab).
2976
- bindAllInitialDropdownListeners(signal, onOpen, onClose) {
3008
+ bindAllInitialCountrySelectorListeners(signal, onOpen, onClose) {
2977
3009
  const label = this.telInputEl.closest("label");
2978
3010
  if (label) {
2979
3011
  label.addEventListener(
2980
3012
  "click",
2981
3013
  (e) => {
2982
- if (!this.isDropdownOpen()) {
3014
+ if (!this.isCountrySelectorOpen()) {
2983
3015
  this.telInputEl.focus();
2984
3016
  } else {
2985
3017
  e.preventDefault();
@@ -2991,7 +3023,7 @@ var UI = class _UI {
2991
3023
  this.#selectedCountryEl.addEventListener(
2992
3024
  "click",
2993
3025
  () => {
2994
- if (!this.isDropdownOpen() && !this.telInputEl.disabled && !this.telInputEl.readOnly) {
3026
+ if (!this.isCountrySelectorOpen() && !this.telInputEl.disabled && !this.telInputEl.readOnly) {
2995
3027
  onOpen();
2996
3028
  }
2997
3029
  },
@@ -3006,7 +3038,7 @@ var UI = class _UI {
3006
3038
  KEYS.SPACE,
3007
3039
  KEYS.ENTER
3008
3040
  ];
3009
- if (!this.isDropdownOpen() && openKeys.includes(e.key)) {
3041
+ if (!this.isCountrySelectorOpen() && openKeys.includes(e.key)) {
3010
3042
  e.preventDefault();
3011
3043
  e.stopPropagation();
3012
3044
  onOpen();
@@ -3018,24 +3050,24 @@ var UI = class _UI {
3018
3050
  { signal }
3019
3051
  );
3020
3052
  }
3021
- //* Open the dropdown: create a fresh AbortController, do the DOM work, and wire up all
3022
- //* dropdown-open listeners (which invoke the caller's onSelect / onClose callbacks).
3023
- openDropdown(onSelect, onClose) {
3024
- const { countrySearch, dropdownAlwaysOpen, dropdownContainer } = this.#options;
3025
- 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();
3026
3058
  this.ensureDropdownWidthSet();
3027
- if (dropdownContainer) {
3028
- this.#injectAndPositionDetachedDropdown();
3059
+ if (this.#detachedCountrySelectorEl) {
3060
+ this.#injectAndPositionDetachedCountrySelector();
3029
3061
  } else {
3030
- const positionBelow = this.#shouldPositionInlineDropdownBelowInput();
3062
+ const positionBelow = this.#shouldPositionDropdownBelowInput();
3031
3063
  const distance = this.telInputEl.offsetHeight + LAYOUT.DROPDOWN_MARGIN;
3032
3064
  if (positionBelow) {
3033
- this.#dropdownContentEl.style.top = `${distance}px`;
3065
+ this.#countrySelectorEl.style.top = `${distance}px`;
3034
3066
  } else {
3035
- this.#dropdownContentEl.style.bottom = `${distance}px`;
3067
+ this.#countrySelectorEl.style.bottom = `${distance}px`;
3036
3068
  }
3037
3069
  }
3038
- this.#dropdownContentEl.classList.remove(CLASSES.HIDE);
3070
+ this.#countrySelectorEl.classList.remove(CLASSES.HIDE);
3039
3071
  this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "true");
3040
3072
  const itemToHighlight = this.#selectedListItemEl ?? this.#countryListEl.firstElementChild;
3041
3073
  if (itemToHighlight) {
@@ -3044,7 +3076,7 @@ var UI = class _UI {
3044
3076
  if (countrySearch && !dropdownAlwaysOpen) {
3045
3077
  this.#searchInputEl.focus();
3046
3078
  }
3047
- if (this.#options.useFullscreenPopup && this.#detachedDropdownEl && window.visualViewport) {
3079
+ if (this.#options.countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN && this.#detachedCountrySelectorEl && window.visualViewport) {
3048
3080
  window.visualViewport.addEventListener(
3049
3081
  "resize",
3050
3082
  () => {
@@ -3053,29 +3085,29 @@ var UI = class _UI {
3053
3085
  this.#scrollCountryListToItem(this.#highlightedListItemEl);
3054
3086
  }
3055
3087
  },
3056
- { signal: this.#dropdownAbortController.signal }
3088
+ { signal: this.#countrySelectorAbortController.signal }
3057
3089
  );
3058
3090
  }
3059
- this.#dropdownArrowEl.classList.add(CLASSES.ARROW_UP);
3060
- this.#bindDropdownOpenListeners(onSelect, onClose);
3091
+ this.#arrowEl.classList.add(CLASSES.ARROW_UP);
3092
+ this.#bindCountrySelectorOpenListeners(onSelect, onClose);
3061
3093
  }
3062
- //* 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),
3063
3095
  //* list-item click & enter key (select), click-off & escape (close), search input (filter),
3064
- //* (when countrySearch disabled) typed-char hidden search, and (when dropdown is in an external
3065
- //* container) close on window scroll.
3066
- #bindDropdownOpenListeners(onSelect, onClose) {
3067
- 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;
3068
3100
  this.#bindListItemHover(signal);
3069
3101
  this.#bindListItemClick(signal, onSelect);
3070
3102
  if (!this.#options.dropdownAlwaysOpen) {
3071
3103
  this.#bindOutsideClickToClose(signal, onClose);
3072
3104
  }
3073
- this.#bindDropdownKeydownListener(signal, onSelect, onClose);
3105
+ this.#bindCountrySelectorKeydownListener(signal, onSelect, onClose);
3074
3106
  if (this.#options.countrySearch) {
3075
3107
  this.#bindSearchInputListener(signal);
3076
3108
  }
3077
- if (!this.#options.useFullscreenPopup && this.#options.dropdownContainer) {
3078
- 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 });
3079
3111
  }
3080
3112
  }
3081
3113
  //* When mouse over a list item, just highlight that one (so if they hit "enter" we know which to select).
@@ -3108,13 +3140,13 @@ var UI = class _UI {
3108
3140
  { signal }
3109
3141
  );
3110
3142
  }
3111
- //* Invoke onClickOff when the user clicks anywhere outside the dropdown.
3143
+ //* Invoke onClickOff when the user clicks anywhere outside the country selector.
3112
3144
  #bindOutsideClickToClose(signal, onClickOff) {
3113
3145
  setTimeout(() => {
3114
3146
  document.documentElement.addEventListener(
3115
3147
  "click",
3116
3148
  (e) => {
3117
- if (!this.#dropdownContentEl.contains(e.target)) {
3149
+ if (!this.#countrySelectorEl.contains(e.target)) {
3118
3150
  onClickOff();
3119
3151
  }
3120
3152
  },
@@ -3122,10 +3154,10 @@ var UI = class _UI {
3122
3154
  );
3123
3155
  }, 0);
3124
3156
  }
3125
- //* Keyboard navigation while the dropdown is open: arrow keys navigate, hidden-search keys filter,
3126
- //* and enter/escape invoke the caller's callbacks (which handle country selection / dropdown close).
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) => {
@@ -3160,7 +3192,7 @@ var UI = class _UI {
3160
3192
  }
3161
3193
  };
3162
3194
  this.#selectedCountryEl?.addEventListener("keydown", handleKeydown, { signal });
3163
- this.#dropdownContentEl?.addEventListener("keydown", handleKeydown, { signal });
3195
+ this.#countrySelectorEl?.addEventListener("keydown", handleKeydown, { signal });
3164
3196
  }
3165
3197
  //* Wire up country search input listener: typing filters the list, the clear button resets it.
3166
3198
  #bindSearchInputListener(signal) {
@@ -3197,7 +3229,7 @@ var UI = class _UI {
3197
3229
  this.#highlightListItem(next);
3198
3230
  }
3199
3231
  }
3200
- // Update the selected list item in the dropdown
3232
+ // Update the selected list item in the country list
3201
3233
  #updateSelectedListItem(iso2) {
3202
3234
  if (this.#selectedListItemEl && this.#selectedListItemEl.dataset[DATA_KEYS.ISO2] !== iso2) {
3203
3235
  this.#selectedListItemEl.setAttribute(ARIA.SELECTED, "false");
@@ -3248,12 +3280,12 @@ var UI = class _UI {
3248
3280
  this.#countryListEl.scrollTop = 0;
3249
3281
  this.#updateSearchResultsA11yText();
3250
3282
  }
3251
- // UI: Close the dropdown (DOM + abort dropdown-scoped listeners).
3252
- closeDropdown() {
3253
- const { countrySearch, dropdownContainer } = this.#options;
3254
- this.#dropdownAbortController.abort();
3255
- this.#dropdownAbortController = null;
3256
- 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);
3257
3289
  this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "false");
3258
3290
  if (countrySearch) {
3259
3291
  this.#searchInputEl.removeAttribute(ARIA.ACTIVE_DESCENDANT);
@@ -3264,19 +3296,19 @@ var UI = class _UI {
3264
3296
  this.#highlightedListItemEl = null;
3265
3297
  }
3266
3298
  }
3267
- this.#dropdownArrowEl.classList.remove(CLASSES.ARROW_UP);
3268
- if (dropdownContainer) {
3269
- this.#detachedDropdownEl.remove();
3270
- this.#detachedDropdownEl.style.top = "";
3271
- this.#detachedDropdownEl.style.bottom = "";
3272
- this.#detachedDropdownEl.style.paddingLeft = "";
3273
- 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 = "";
3274
3306
  } else {
3275
- this.#dropdownContentEl.style.top = "";
3276
- this.#dropdownContentEl.style.bottom = "";
3307
+ this.#countrySelectorEl.style.top = "";
3308
+ this.#countrySelectorEl.style.bottom = "";
3277
3309
  }
3278
3310
  }
3279
- #shouldPositionInlineDropdownBelowInput() {
3311
+ #shouldPositionDropdownBelowInput() {
3280
3312
  if (this.#options.dropdownAlwaysOpen) {
3281
3313
  return true;
3282
3314
  }
@@ -3285,41 +3317,54 @@ var UI = class _UI {
3285
3317
  const spaceBelow = window.innerHeight - inputPos.bottom;
3286
3318
  return spaceBelow >= this.#inlineDropdownHeight || spaceBelow >= spaceAbove;
3287
3319
  }
3288
- // inject dropdown into container and apply positioning styles
3289
- #injectAndPositionDetachedDropdown() {
3290
- const { dropdownContainer, useFullscreenPopup } = this.#options;
3291
- 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) {
3292
3325
  if (window.innerWidth >= LAYOUT.NARROW_VIEWPORT_WIDTH) {
3293
3326
  const inputPos = this.telInputEl.getBoundingClientRect();
3294
- this.#detachedDropdownEl.style.paddingLeft = `${inputPos.left}px`;
3295
- 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`;
3296
3329
  }
3297
- } else {
3330
+ } else if (!supportsCssAnchor) {
3298
3331
  const inputPos = this.telInputEl.getBoundingClientRect();
3299
- this.#detachedDropdownEl.style.left = `${inputPos.left}px`;
3300
- const positionBelow = this.#shouldPositionInlineDropdownBelowInput();
3301
- if (positionBelow) {
3302
- 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`;
3303
3335
  } else {
3304
- this.#detachedDropdownEl.style.top = "unset";
3305
- 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`;
3306
3338
  }
3307
3339
  }
3308
- 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;
3309
3354
  }
3310
3355
  // Adjust the fullscreen popup dimensions to match the visual viewport,
3311
3356
  // so it stays above the virtual keyboard on mobile devices.
3312
3357
  #adjustFullscreenPopupToViewport() {
3313
3358
  const vv = window.visualViewport;
3314
- if (!vv || !this.#detachedDropdownEl) {
3359
+ if (!vv || !this.#detachedCountrySelectorEl) {
3315
3360
  return;
3316
3361
  }
3317
3362
  const virtualKeyboardHeight = window.innerHeight - vv.height;
3318
- this.#detachedDropdownEl.style.bottom = `${virtualKeyboardHeight}px`;
3363
+ this.#detachedCountrySelectorEl.style.bottom = `${virtualKeyboardHeight}px`;
3319
3364
  }
3320
- // UI: Whether the dropdown is currently open (visible).
3321
- isDropdownOpen() {
3322
- 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);
3323
3368
  }
3324
3369
  // Toggle the loading spinner on the selected flag (used during auto-country geoIP lookup).
3325
3370
  setLoading(isLoading) {
@@ -3348,7 +3393,7 @@ var UI = class _UI {
3348
3393
  isLoading() {
3349
3394
  return this.#selectedFlagEl.classList.contains(CLASSES.LOADING);
3350
3395
  }
3351
- // Set the disabled state of the input and dropdown.
3396
+ // Set the disabled state of the input and country selector.
3352
3397
  setDisabled(disabled) {
3353
3398
  this.telInputEl.disabled = disabled;
3354
3399
  if (this.#selectedCountryEl) {
@@ -3359,7 +3404,7 @@ var UI = class _UI {
3359
3404
  }
3360
3405
  }
3361
3406
  }
3362
- // Set the readonly state of the input and dropdown.
3407
+ // Set the readonly state of the input and country selector.
3363
3408
  setReadonly(readonly) {
3364
3409
  this.telInputEl.readOnly = readonly;
3365
3410
  if (this.#selectedCountryEl) {
@@ -3370,12 +3415,12 @@ var UI = class _UI {
3370
3415
  }
3371
3416
  }
3372
3417
  }
3373
- setCountry(selectedCountryData) {
3374
- const { allowDropdown, showFlags, separateDialCode, i18n } = this.#options;
3375
- const name = selectedCountryData?.name;
3376
- const dialCode = selectedCountryData?.dialCode;
3377
- const iso2 = selectedCountryData?.iso2 ?? "";
3378
- 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) {
3379
3424
  this.#updateSelectedListItem(iso2);
3380
3425
  }
3381
3426
  if (this.#selectedCountryEl) {
@@ -3384,13 +3429,13 @@ var UI = class _UI {
3384
3429
  let flagContent = null;
3385
3430
  if (iso2) {
3386
3431
  title = name;
3387
- ariaLabel = i18n.selectedCountryAriaLabel.replace("${countryName}", name).replace("${dialCode}", `+${dialCode}`);
3432
+ ariaLabel = uiTranslations.selectedCountryAriaLabel.replace("${countryName}", name).replace("${dialCode}", `+${dialCode}`);
3388
3433
  if (!showFlags) {
3389
3434
  flagContent = buildGlobeIcon();
3390
3435
  }
3391
3436
  } else {
3392
- title = i18n.noCountrySelected;
3393
- ariaLabel = i18n.noCountrySelected;
3437
+ title = uiTranslations.noCountrySelected;
3438
+ ariaLabel = uiTranslations.noCountrySelected;
3394
3439
  flagContent = buildGlobeIcon();
3395
3440
  }
3396
3441
  this.#selectedFlagEl.className = flagClass;
@@ -3539,17 +3584,17 @@ var hasRegionlessDialCode = (number) => {
3539
3584
  };
3540
3585
 
3541
3586
  // packages/core/src/js/format/formatting.ts
3542
- var stripSeparateDialCode = (fullNumber, hasValidDialCode, separateDialCode, selectedCountryData) => {
3587
+ var stripSeparateDialCode = (fullNumber, hasValidDialCode, separateDialCode, selectedCountry) => {
3543
3588
  if (!separateDialCode || !hasValidDialCode) {
3544
3589
  return fullNumber;
3545
3590
  }
3546
- const dialCode = `+${selectedCountryData.dialCode}`;
3591
+ const dialCode = `+${selectedCountry.dialCode}`;
3547
3592
  const start = fullNumber[dialCode.length] === " " || fullNumber[dialCode.length] === "-" ? dialCode.length + 1 : dialCode.length;
3548
3593
  return fullNumber.substring(start);
3549
3594
  };
3550
- var formatNumberAsYouType = (fullNumber, telInputValue, utils, selectedCountryData, separateDialCode) => {
3551
- const result = utils ? utils.formatNumberAsYouType(fullNumber, selectedCountryData?.iso2) : fullNumber;
3552
- 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;
3553
3598
  if (separateDialCode && telInputValue.charAt(0) !== "+" && result.includes(`+${dialCode}`)) {
3554
3599
  const afterDialCode = result.split(`+${dialCode}`)[1] || "";
3555
3600
  return afterDialCode.trim();
@@ -3680,8 +3725,8 @@ var Iti = class _Iti {
3680
3725
  this.#ui.telInputEl.value = this.#numerals.denormalise(asciiValue);
3681
3726
  }
3682
3727
  #createInitPromise(options) {
3683
- const { initialCountry, geoIpLookup, loadUtils } = options;
3684
- const needsAutoCountryDeferred = initialCountry === INITIAL_COUNTRY.AUTO && Boolean(geoIpLookup);
3728
+ const { initialCountry, initialCountryLookup, loadUtils } = options;
3729
+ const needsAutoCountryDeferred = !initialCountry && Boolean(initialCountryLookup);
3685
3730
  const needsUtilsDeferred = Boolean(loadUtils) && !intlTelInput.utils;
3686
3731
  if (needsAutoCountryDeferred) {
3687
3732
  this.#autoCountryDeferred = createDeferred();
@@ -3703,7 +3748,7 @@ var Iti = class _Iti {
3703
3748
  this.#initListeners();
3704
3749
  this.#startAsyncLoads();
3705
3750
  if (this.#options.dropdownAlwaysOpen) {
3706
- this.#openDropdown();
3751
+ this.openCountrySelector();
3707
3752
  }
3708
3753
  }
3709
3754
  //********************
@@ -3726,8 +3771,8 @@ var Iti = class _Iti {
3726
3771
  const value = useAttribute ? attributeValue : inputValue;
3727
3772
  const dialCode = this.#getDialCode(value);
3728
3773
  const isRegionlessNanpNumber = isRegionlessNanp(value);
3729
- const { initialCountry, geoIpLookup } = this.#options;
3730
- const isAutoCountry = initialCountry === INITIAL_COUNTRY.AUTO && geoIpLookup;
3774
+ const { initialCountry, initialCountryLookup } = this.#options;
3775
+ const isAutoCountry = !initialCountry && Boolean(initialCountryLookup);
3731
3776
  const resolvedInitialCountry = isAutoCountry && intlTelInput.autoCountry ? intlTelInput.autoCountry : initialCountry;
3732
3777
  const doingAutoCountryLookup = isAutoCountry && !overrideAutoCountry && !intlTelInput.autoCountry;
3733
3778
  const isValidInitialCountry = isIso2(resolvedInitialCountry);
@@ -3756,11 +3801,11 @@ var Iti = class _Iti {
3756
3801
  //* Initialise the main event listeners: input keyup, and click selected country.
3757
3802
  #initListeners() {
3758
3803
  this.#bindAllTelInputListeners();
3759
- if (this.#options.allowDropdown) {
3760
- this.#ui.bindAllInitialDropdownListeners(
3804
+ if (this.#options.countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF) {
3805
+ this.#ui.bindAllInitialCountrySelectorListeners(
3761
3806
  this.#abortController.signal,
3762
- () => this.#openDropdown(),
3763
- () => this.#closeDropdown()
3807
+ () => this.openCountrySelector(),
3808
+ () => this.#closeCountrySelectorInternal()
3764
3809
  );
3765
3810
  }
3766
3811
  this.#ui.bindHiddenInputSubmitListener(
@@ -3769,7 +3814,7 @@ var Iti = class _Iti {
3769
3814
  () => this.#selectedCountry?.iso2 || ""
3770
3815
  );
3771
3816
  }
3772
- //* Init requests: utils script / geo ip lookup.
3817
+ //* Init requests: utils script / initial country lookup.
3773
3818
  #startAsyncLoads() {
3774
3819
  if (this.#utilsDeferred) {
3775
3820
  const { loadUtils } = this.#options;
@@ -3793,7 +3838,7 @@ var Iti = class _Iti {
3793
3838
  }
3794
3839
  }
3795
3840
  }
3796
- //* Perform the geo ip lookup.
3841
+ //* Perform the initial country lookup.
3797
3842
  async #loadAutoCountry() {
3798
3843
  if (intlTelInput.autoCountry) {
3799
3844
  this.#handleAutoCountryLoaded();
@@ -3804,14 +3849,14 @@ var Iti = class _Iti {
3804
3849
  return;
3805
3850
  }
3806
3851
  intlTelInput.startedLoadingAutoCountry = true;
3807
- if (typeof this.#options.geoIpLookup === "function") {
3852
+ if (typeof this.#options.initialCountryLookup === "function") {
3808
3853
  let timeoutId;
3809
3854
  try {
3810
3855
  const iso2 = await Promise.race([
3811
- this.#options.geoIpLookup(),
3856
+ this.#options.initialCountryLookup(),
3812
3857
  new Promise((_, reject) => {
3813
3858
  timeoutId = setTimeout(
3814
- () => reject(new Error("intl-tel-input: geoIpLookup timed out after 10s")),
3859
+ () => reject(new Error("intl-tel-input: initialCountryLookup timed out after 10s")),
3815
3860
  1e4
3816
3861
  );
3817
3862
  })
@@ -3834,8 +3879,8 @@ var Iti = class _Iti {
3834
3879
  }
3835
3880
  }
3836
3881
  }
3837
- #openDropdownWithPlus() {
3838
- this.#openDropdown();
3882
+ #openCountrySelectorWithPlus() {
3883
+ this.openCountrySelector();
3839
3884
  this.#ui.prefillSearchWithPlus();
3840
3885
  }
3841
3886
  //* Delete the character just typed (the one immediately before the caret). Used by Android workarounds where we can't preventDefault on keydown.
@@ -3855,7 +3900,7 @@ var Iti = class _Iti {
3855
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)
3856
3901
  #handleAndroidPlusKey(inputValue) {
3857
3902
  this.#removeJustTypedChar(inputValue);
3858
- this.#openDropdownWithPlus();
3903
+ this.#openCountrySelectorWithPlus();
3859
3904
  }
3860
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.
3861
3906
  #handleAndroidStrictReject(inputValue, rejectedInput) {
@@ -3925,7 +3970,7 @@ var Iti = class _Iti {
3925
3970
  strictMode,
3926
3971
  formatAsYouType,
3927
3972
  separateDialCode,
3928
- allowDropdown,
3973
+ countrySelectorMode,
3929
3974
  countrySearch
3930
3975
  } = this.#options;
3931
3976
  const detail = e?.detail;
@@ -3935,7 +3980,7 @@ var Iti = class _Iti {
3935
3980
  let inputValue = this.#getTelInputValue();
3936
3981
  const isPaste = e?.inputType === INPUT_TYPES.PASTE;
3937
3982
  const isStrictPaste = strictMode && isPaste;
3938
- if (this.#isAndroid && !isPaste && e?.data === "+" && separateDialCode && allowDropdown && countrySearch) {
3983
+ if (this.#isAndroid && !isPaste && e?.data === "+" && separateDialCode && countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF && countrySearch) {
3939
3984
  this.#handleAndroidPlusKey(inputValue);
3940
3985
  return;
3941
3986
  }
@@ -3982,13 +4027,13 @@ var Iti = class _Iti {
3982
4027
  //* On keydown event: (1) if strictMode then prevent invalid characters, (2) if separateDialCode then handle plus key
3983
4028
  //* Note that this fires BEFORE the input is updated.
3984
4029
  #handleKeydownEvent = (e) => {
3985
- const { strictMode, separateDialCode, allowDropdown, countrySearch } = this.#options;
4030
+ const { strictMode, separateDialCode, countrySelectorMode, countrySearch } = this.#options;
3986
4031
  if (!e.key || e.key.length !== 1 || e.altKey || e.ctrlKey || e.metaKey) {
3987
4032
  return;
3988
4033
  }
3989
- if (separateDialCode && allowDropdown && countrySearch && e.key === "+") {
4034
+ if (separateDialCode && countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF && countrySearch && e.key === "+") {
3990
4035
  e.preventDefault();
3991
- this.#openDropdownWithPlus();
4036
+ this.#openCountrySelectorWithPlus();
3992
4037
  return;
3993
4038
  }
3994
4039
  if (!strictMode) {
@@ -4151,27 +4196,36 @@ var Iti = class _Iti {
4151
4196
  });
4152
4197
  this.#ui.telInputEl.dispatchEvent(e);
4153
4198
  }
4154
- //* Open the dropdown. Bail if already open — otherwise the existing AbortController gets overwritten
4155
- //* and its listeners leak. Reachable via openDropdownWithPlus when dropdownAlwaysOpen is set
4156
- #openDropdown() {
4157
- 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()) {
4158
4204
  return;
4159
4205
  }
4160
- this.#ui.openDropdown(
4206
+ this.#ui.openCountrySelector(
4161
4207
  (li) => this.#selectListItem(li),
4162
- () => this.#closeDropdown()
4208
+ () => this.#closeCountrySelectorInternal()
4163
4209
  );
4164
- this.#dispatchEvent(EVENTS.OPEN_COUNTRY_DROPDOWN);
4210
+ this.#dispatchEvent(EVENTS.OPEN_COUNTRY_SELECTOR);
4165
4211
  }
4166
4212
  //* Update the input's value to the given number (format first if possible)
4167
4213
  //* NOTE: this is called from setInitialState, handleUtilsLoaded and setNumber.
4168
4214
  #updateValueFromNumber(fullNumber) {
4169
- const { formatOnDisplay, nationalMode, separateDialCode } = this.#options;
4215
+ const { numberDisplayFormat, separateDialCode } = this.#options;
4170
4216
  let number = fullNumber;
4171
- if (formatOnDisplay && intlTelInput.utils && this.#selectedCountry) {
4217
+ if (intlTelInput.utils && this.#selectedCountry) {
4172
4218
  const isRegionless = hasRegionlessDialCode(fullNumber);
4173
- const useNational = nationalMode && !isRegionless || !number.startsWith("+") && !separateDialCode;
4174
- 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
+ }
4175
4229
  number = intlTelInput.utils.formatNumber(
4176
4230
  number,
4177
4231
  this.#selectedCountry?.iso2,
@@ -4268,14 +4322,14 @@ var Iti = class _Iti {
4268
4322
  return null;
4269
4323
  }
4270
4324
  //* Update the selected country, dial code (if separateDialCode), placeholder, title, and selected list item.
4271
- //* Note: called from setInitialState, updateCountryFromNumber, selectListItem, setCountry.
4325
+ //* Note: called from setInitialState, updateCountryFromNumber, selectListItem, setSelectedCountry.
4272
4326
  #updateSelectedCountry(iso2) {
4273
4327
  const prevIso2 = this.#selectedCountry?.iso2 || "";
4274
4328
  this.#selectedCountry = iso2 ? this.#countryByIso2.get(iso2) : null;
4275
4329
  if (this.#selectedCountry) {
4276
4330
  this.#fallbackCountryIso2 = this.#selectedCountry.iso2;
4277
4331
  }
4278
- this.#ui.setCountry(this.#selectedCountry);
4332
+ this.#ui.setSelectedCountry(this.#selectedCountry);
4279
4333
  this.#updatePlaceholder();
4280
4334
  this.#updateMaxCoreNumberLength();
4281
4335
  return prevIso2 !== iso2;
@@ -4293,12 +4347,11 @@ var Iti = class _Iti {
4293
4347
  }
4294
4348
  let exampleNumber = intlTelInput.utils.getExampleNumber(
4295
4349
  iso2,
4296
- false,
4297
4350
  placeholderNumberType,
4298
- true
4351
+ NUMBER_FORMAT.E164
4299
4352
  );
4300
4353
  let validNumber = exampleNumber;
4301
- while (intlTelInput.utils.isPossibleNumber(
4354
+ while (intlTelInput.utils.isValidNumber(
4302
4355
  exampleNumber,
4303
4356
  iso2,
4304
4357
  allowedNumberTypes
@@ -4315,19 +4368,19 @@ var Iti = class _Iti {
4315
4368
  //* Update the input placeholder to an example number from the currently selected country.
4316
4369
  #updatePlaceholder() {
4317
4370
  const {
4318
- autoPlaceholder,
4371
+ placeholderNumberPolicy,
4319
4372
  placeholderNumberType,
4320
- nationalMode,
4373
+ numberDisplayFormat,
4321
4374
  customPlaceholder
4322
4375
  } = this.#options;
4323
- 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;
4324
4377
  if (!intlTelInput.utils || !shouldSetPlaceholder) {
4325
4378
  return;
4326
4379
  }
4327
4380
  let placeholder = this.#selectedCountry ? intlTelInput.utils.getExampleNumber(
4328
4381
  this.#selectedCountry.iso2,
4329
- nationalMode,
4330
- placeholderNumberType
4382
+ placeholderNumberType,
4383
+ numberDisplayFormat
4331
4384
  ) : "";
4332
4385
  placeholder = this.#prepareNumberForInput(placeholder);
4333
4386
  if (typeof customPlaceholder === "function") {
@@ -4335,36 +4388,40 @@ var Iti = class _Iti {
4335
4388
  }
4336
4389
  this.#ui.telInputEl.setAttribute("placeholder", placeholder);
4337
4390
  }
4338
- //* 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).
4339
4392
  #selectListItem(listItem) {
4340
4393
  if (!listItem) {
4341
4394
  return;
4342
4395
  }
4343
4396
  const iso2 = listItem.dataset[DATA_KEYS.ISO2];
4344
4397
  const countryChanged = this.#updateSelectedCountry(iso2);
4345
- this.#closeDropdown();
4398
+ this.#closeCountrySelectorInternal();
4346
4399
  const dialCode = listItem.dataset[DATA_KEYS.DIAL_CODE];
4347
4400
  this.#updateDialCode(dialCode);
4348
- if (this.#options.formatOnDisplay) {
4349
- const inputValue = this.#getTelInputValue();
4350
- this.#updateValueFromNumber(inputValue);
4351
- }
4401
+ const inputValue = this.#getTelInputValue();
4402
+ this.#updateValueFromNumber(inputValue);
4352
4403
  this.#ui.telInputEl.focus();
4353
4404
  if (countryChanged) {
4354
4405
  this.#dispatchCountryChangeEvent();
4355
4406
  this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
4356
4407
  }
4357
4408
  }
4358
- //* Close the dropdown and unbind any listeners.
4359
- #closeDropdown(isDestroy) {
4360
- 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) {
4361
4418
  return;
4362
4419
  }
4363
- this.#ui.closeDropdown();
4364
- this.#dispatchEvent(EVENTS.CLOSE_COUNTRY_DROPDOWN);
4420
+ this.#ui.closeCountrySelector();
4421
+ this.#dispatchEvent(EVENTS.CLOSE_COUNTRY_SELECTOR);
4365
4422
  }
4366
4423
  //* Replace any existing dial code with the new one
4367
- //* Note: called from selectListItem and setCountry
4424
+ //* Note: called from selectListItem and setSelectedCountry
4368
4425
  #updateDialCode(newDialCodeDigits) {
4369
4426
  const inputValue = this.#getTelInputValue();
4370
4427
  if (!inputValue.startsWith("+")) {
@@ -4444,7 +4501,7 @@ var Iti = class _Iti {
4444
4501
  //**************************
4445
4502
  //* INTERNAL METHODS
4446
4503
  //**************************
4447
- //* Called when the geoip call returns.
4504
+ //* Called when the initial country lookup returns.
4448
4505
  #handleAutoCountryLoaded() {
4449
4506
  if (!this.#autoCountryDeferred || !intlTelInput.autoCountry) {
4450
4507
  return;
@@ -4453,15 +4510,17 @@ var Iti = class _Iti {
4453
4510
  this.#autoCountryDeferred.resolve();
4454
4511
  return;
4455
4512
  }
4456
- if (this.#ui.isLoading()) {
4457
- 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);
4458
4517
  } else {
4459
4518
  this.#fallbackCountryIso2 = intlTelInput.autoCountry;
4460
4519
  }
4461
4520
  this.#ui.setLoading(false);
4462
4521
  this.#autoCountryDeferred.resolve();
4463
4522
  }
4464
- //* Called when the geoip call fails or times out.
4523
+ //* Called when the initial country lookup fails or times out.
4465
4524
  #handleAutoCountryFailure() {
4466
4525
  if (!this.#isActive) {
4467
4526
  this.#autoCountryDeferred?.reject();
@@ -4482,7 +4541,8 @@ var Iti = class _Iti {
4482
4541
  return;
4483
4542
  }
4484
4543
  const inputValue = this.#getTelInputValue();
4485
- if (inputValue) {
4544
+ const isFocused = document.activeElement === this.#ui.telInputEl;
4545
+ if (inputValue && !isFocused) {
4486
4546
  this.#updateValueFromNumber(inputValue);
4487
4547
  }
4488
4548
  if (this.#selectedCountry) {
@@ -4508,8 +4568,8 @@ var Iti = class _Iti {
4508
4568
  return;
4509
4569
  }
4510
4570
  this.#isActive = false;
4511
- if (this.#options.allowDropdown) {
4512
- this.#closeDropdown(true);
4571
+ if (this.#options.countrySelectorMode !== COUNTRY_SELECTOR_MODE.OFF) {
4572
+ this.#closeCountrySelectorInternal(true);
4513
4573
  }
4514
4574
  this.#abortController.abort();
4515
4575
  this.#ui.destroy();
@@ -4557,7 +4617,7 @@ var Iti = class _Iti {
4557
4617
  );
4558
4618
  }
4559
4619
  //* Get the country data for the currently selected country.
4560
- getSelectedCountryData() {
4620
+ getSelectedCountry() {
4561
4621
  return this.#selectedCountry ?? null;
4562
4622
  }
4563
4623
  //* Get the validation error e.g. "TOO_SHORT" / "TOO_LONG", or null if it can't be determined / instance is destroyed.
@@ -4612,7 +4672,7 @@ var Iti = class _Iti {
4612
4672
  if (!this.#selectedCountry && !hasRegionlessDialCode(value)) {
4613
4673
  return false;
4614
4674
  }
4615
- const check = mode === "precise" ? intlTelInput.utils.isValidNumber : intlTelInput.utils.isPossibleNumber;
4675
+ const check = mode === "precise" ? intlTelInput.utils.isValidNumberPrecise : intlTelInput.utils.isValidNumber;
4616
4676
  if (!check(value, iso2, allowedNumberTypes)) {
4617
4677
  return false;
4618
4678
  }
@@ -4625,7 +4685,7 @@ var Iti = class _Iti {
4625
4685
  return true;
4626
4686
  }
4627
4687
  //* Update the selected country, and update the input value accordingly.
4628
- setCountry(iso2) {
4688
+ setSelectedCountry(iso2) {
4629
4689
  if (!this.#isActive) {
4630
4690
  return;
4631
4691
  }
@@ -4640,10 +4700,8 @@ var Iti = class _Iti {
4640
4700
  }
4641
4701
  this.#updateSelectedCountry(iso2Lower);
4642
4702
  this.#updateDialCode(this.#selectedCountry?.dialCode || "");
4643
- if (this.#options.formatOnDisplay) {
4644
- const inputValue = this.#getTelInputValue();
4645
- this.#updateValueFromNumber(inputValue);
4646
- }
4703
+ const inputValue = this.#getTelInputValue();
4704
+ this.#updateValueFromNumber(inputValue);
4647
4705
  this.#dispatchCountryChangeEvent();
4648
4706
  this.#dispatchEvent(EVENTS.INPUT, { isCountryChange: true });
4649
4707
  }
@@ -4668,14 +4726,14 @@ var Iti = class _Iti {
4668
4726
  this.#options.placeholderNumberType = type;
4669
4727
  this.#updatePlaceholder();
4670
4728
  }
4671
- // Set the disabled state of the input and dropdown.
4729
+ // Set the disabled state of the input and country selector.
4672
4730
  setDisabled(disabled) {
4673
4731
  if (!this.#isActive) {
4674
4732
  return;
4675
4733
  }
4676
4734
  this.#ui.setDisabled(disabled);
4677
4735
  }
4678
- // Set the readonly state of the input and dropdown.
4736
+ // Set the readonly state of the input and country selector.
4679
4737
  setReadonly(readonly) {
4680
4738
  if (!this.#isActive) {
4681
4739
  return;
@@ -4685,7 +4743,7 @@ var Iti = class _Iti {
4685
4743
  //********************
4686
4744
  //* STATIC METHODS
4687
4745
  //********************
4688
- // Internal instance notification used by utils/geoip loaders.
4746
+ // Internal instance notification used by utils/initial-country loaders.
4689
4747
  // Kept public so module-level helpers (e.g. attachUtils) can call it, while still allowing
4690
4748
  // access to private instance methods.
4691
4749
  static forEachInstance(method, ...args) {
@@ -4749,8 +4807,8 @@ var intlTelInput = Object.assign(
4749
4807
  defaults,
4750
4808
  //* Using a static var like this allows us to mock it in the tests.
4751
4809
  documentReady: () => document.readyState === "complete",
4752
- //* Get the country data object.
4753
- getCountryData: () => data_default,
4810
+ //* Get the full list of all countries the library knows about.
4811
+ getAllCountries: () => data_default,
4754
4812
  //* A getter for the core library instance.
4755
4813
  getInstance: (input) => {
4756
4814
  const id = input.dataset[DATA_KEYS.INSTANCE_ID];
@@ -4761,17 +4819,21 @@ var intlTelInput = Object.assign(
4761
4819
  attachUtils,
4762
4820
  startedLoadingUtils: false,
4763
4821
  startedLoadingAutoCountry: false,
4764
- version: "28.1.0",
4822
+ version: "29.0.1",
4765
4823
  NUMBER_FORMAT,
4766
4824
  NUMBER_TYPE,
4767
- VALIDATION_ERROR
4825
+ VALIDATION_ERROR,
4826
+ PLACEHOLDER_POLICY,
4827
+ COUNTRY_SELECTOR_MODE
4768
4828
  }
4769
4829
  );
4770
4830
  var intlTelInput_default = intlTelInput;
4771
4831
  export {
4832
+ COUNTRY_SELECTOR_MODE,
4772
4833
  Iti,
4773
4834
  NUMBER_FORMAT,
4774
4835
  NUMBER_TYPE,
4836
+ PLACEHOLDER_POLICY,
4775
4837
  VALIDATION_ERROR,
4776
4838
  intlTelInput_default as default
4777
4839
  };