intl-tel-input 26.5.2 → 26.7.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 (32) hide show
  1. package/README.md +1 -1
  2. package/angular/build/IntlTelInput.js +85 -34
  3. package/angular/build/IntlTelInputWithUtils.js +85 -34
  4. package/angular/build/types/modules/constants.d.ts +1 -0
  5. package/angular/build/types/modules/core/ui.d.ts +6 -2
  6. package/angular/build/types/modules/types/public-api.d.ts +4 -3
  7. package/build/css/demo.css +1 -3
  8. package/build/css/intlTelInput-no-assets.css +22 -23
  9. package/build/css/intlTelInput-no-assets.min.css +1 -1
  10. package/build/css/intlTelInput.css +22 -23
  11. package/build/css/intlTelInput.min.css +1 -1
  12. package/build/js/data.js +1 -1
  13. package/build/js/data.min.js +1 -1
  14. package/build/js/intlTelInput.d.ts +11 -5
  15. package/build/js/intlTelInput.js +90 -35
  16. package/build/js/intlTelInput.min.js +3 -3
  17. package/build/js/intlTelInputWithUtils.js +90 -35
  18. package/build/js/intlTelInputWithUtils.min.js +4 -4
  19. package/package.json +1 -1
  20. package/react/build/IntlTelInput.cjs +89 -34
  21. package/react/build/IntlTelInput.d.ts +11 -5
  22. package/react/build/IntlTelInput.js +89 -34
  23. package/react/build/IntlTelInputWithUtils.cjs +89 -34
  24. package/react/build/IntlTelInputWithUtils.js +89 -34
  25. package/svelte/build/IntlTelInput.mjs +492 -461
  26. package/svelte/build/IntlTelInputWithUtils.mjs +893 -862
  27. package/vue/build/exports/IntlTelInput.mjs +1 -1
  28. package/vue/build/exports/IntlTelInputWithUtils.mjs +1 -1
  29. package/vue/build/{intl-tel-input-F-UinPxX.mjs → intl-tel-input-ZLbr7QRR.mjs} +136 -105
  30. package/vue/build/modules/constants.d.ts +1 -0
  31. package/vue/build/modules/core/ui.d.ts +6 -2
  32. package/vue/build/modules/types/public-api.d.ts +4 -3
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  A JavaScript plugin for entering, formatting and validating international telephone numbers. Includes TypeScript definitions, plus React, Vue, Angular and Svelte components.
4
4
 
5
- [Explore docs »](https://intl-tel-input.com/docs/getting-started)
5
+ [Explore docs »](https://intl-tel-input.com/docs/choose-integration)
6
6
 
7
7
  <picture>
8
8
  <source media="(prefers-color-scheme: dark)" srcset="https://raw.github.com/jackocnr/intl-tel-input/master/screenshots/vanilla-dark.png">
@@ -1753,8 +1753,10 @@ var LAYOUT = {
1753
1753
  // px width fallback when separateDialCode enabled
1754
1754
  SANE_SELECTED_NO_DIAL_WIDTH: 42,
1755
1755
  // px width fallback when no separate dial code
1756
- INPUT_PADDING_EXTRA_LEFT: 6
1756
+ INPUT_PADDING_EXTRA_LEFT: 6,
1757
1757
  // px gap between selected country container and input text
1758
+ DROPDOWN_MARGIN: 3
1759
+ // px margin between dropdown and tel input
1758
1760
  };
1759
1761
  var DIAL = {
1760
1762
  PLUS: "+",
@@ -1856,7 +1858,7 @@ var defaults = {
1856
1858
  allowPhonewords: false,
1857
1859
  //* Add a placeholder in the input with an example number for the selected country.
1858
1860
  autoPlaceholder: PLACEHOLDER_MODES.POLITE,
1859
- //* Modify the parentClass.
1861
+ //* Add a custom class to the (injected) container element.
1860
1862
  containerClass: "",
1861
1863
  //* Locale for localising country names via Intl.DisplayNames.
1862
1864
  countryNameLocale: "en",
@@ -1894,10 +1896,12 @@ var defaults = {
1894
1896
  onlyCountries: [],
1895
1897
  //* Number type to use for placeholders.
1896
1898
  placeholderNumberType: "MOBILE",
1897
- //* Show flags - for both the selected country, and in the country dropdown
1898
- showFlags: true,
1899
+ //* Add custom classes to the search input element.
1900
+ searchInputClass: "",
1899
1901
  //* Display the international dial code next to the selected flag.
1900
1902
  separateDialCode: false,
1903
+ //* Show flags - for both the selected country, and in the country dropdown
1904
+ showFlags: true,
1901
1905
  //* Only allow certain chars e.g. a plus followed by numeric digits, and cap at max valid length.
1902
1906
  strictMode: false,
1903
1907
  //* Use full screen popup instead of dropdown for country list.
@@ -1985,6 +1989,7 @@ var validateOptions = (customOptions) => {
1985
1989
  validatedOptions[key] = value;
1986
1990
  break;
1987
1991
  case "containerClass":
1992
+ case "searchInputClass":
1988
1993
  case "countryNameLocale":
1989
1994
  if (typeof value !== "string") {
1990
1995
  warnOption(key, "a string", value);
@@ -2190,9 +2195,11 @@ var buildGlobeIcon = () => `
2190
2195
  </svg>`;
2191
2196
 
2192
2197
  // angular/build/temp/modules/core/ui.js
2193
- var UI = class {
2198
+ var UI = class _UI {
2194
2199
  constructor(input, options, id2) {
2195
2200
  this.searchKeyupTimer = null;
2201
+ this.dropdownHeight = null;
2202
+ this.dropdownForContainer = null;
2196
2203
  this.highlightedItem = null;
2197
2204
  this.selectedItem = null;
2198
2205
  input.dataset.intlTelInputId = id2.toString();
@@ -2308,6 +2315,13 @@ var UI = class {
2308
2315
  if (countrySearch) {
2309
2316
  this.updateSearchResultsA11yText();
2310
2317
  }
2318
+ if (fixDropdownWidth) {
2319
+ this.dropdownContent.style.width = `${this.telInput.offsetWidth}px`;
2320
+ }
2321
+ this.dropdownHeight = this.getHiddenDropdownHeight();
2322
+ if (countrySearch && !useFullscreenPopup) {
2323
+ this.dropdownContent.style.height = `${this.dropdownHeight}px`;
2324
+ }
2311
2325
  if (dropdownContainer) {
2312
2326
  const dropdownClasses = buildClassNames({
2313
2327
  iti: true,
@@ -2316,14 +2330,14 @@ var UI = class {
2316
2330
  "iti--inline-dropdown": !useFullscreenPopup,
2317
2331
  [containerClass]: Boolean(containerClass)
2318
2332
  });
2319
- this.dropdown = createEl("div", { class: dropdownClasses });
2320
- this.dropdown.appendChild(this.dropdownContent);
2333
+ this.dropdownForContainer = createEl("div", { class: dropdownClasses });
2334
+ this.dropdownForContainer.appendChild(this.dropdownContent);
2321
2335
  } else {
2322
2336
  this.countryContainer.appendChild(this.dropdownContent);
2323
2337
  }
2324
2338
  }
2325
2339
  _buildSearchUI() {
2326
- const { i18n } = this.options;
2340
+ const { i18n, searchInputClass } = this.options;
2327
2341
  const searchWrapper = createEl("div", { class: "iti__search-input-wrapper" }, this.dropdownContent);
2328
2342
  this.searchIcon = createEl("span", {
2329
2343
  class: "iti__search-icon",
@@ -2334,7 +2348,7 @@ var UI = class {
2334
2348
  id: `iti-${this.id}__search-input`,
2335
2349
  // Chrome says inputs need either a name or an id
2336
2350
  type: "search",
2337
- class: "iti__search-input",
2351
+ class: `iti__search-input ${searchInputClass}`,
2338
2352
  placeholder: i18n.searchPlaceholder,
2339
2353
  // 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
2340
2354
  role: "combobox",
@@ -2419,12 +2433,12 @@ var UI = class {
2419
2433
  createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
2420
2434
  }
2421
2435
  const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
2422
- nameEl.textContent = c.name;
2436
+ nameEl.textContent = `${c.name} `;
2423
2437
  const dialEl = createEl("span", { class: "iti__dial-code" }, nameEl);
2424
2438
  if (this.isRTL) {
2425
2439
  dialEl.setAttribute("dir", "ltr");
2426
2440
  }
2427
- dialEl.textContent = `+${c.dialCode}`;
2441
+ dialEl.textContent = `(+${c.dialCode})`;
2428
2442
  frag.appendChild(listItem);
2429
2443
  }
2430
2444
  this.countryList.appendChild(frag);
@@ -2438,18 +2452,22 @@ var UI = class {
2438
2452
  this.telInput.style.paddingLeft = `${inputPadding}px`;
2439
2453
  }
2440
2454
  }
2455
+ static getBody() {
2456
+ let body;
2457
+ try {
2458
+ body = window.top.document.body;
2459
+ } catch (e) {
2460
+ body = document.body;
2461
+ }
2462
+ return body;
2463
+ }
2441
2464
  //* When input is in a hidden container during init, we cannot calculate the selected country width.
2442
2465
  //* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.
2443
2466
  //* To get the right styling to apply, all we need is a shallow clone of the container,
2444
2467
  //* and then to inject a deep clone of the selectedCountry element.
2445
2468
  _getHiddenSelectedCountryWidth() {
2446
2469
  if (this.telInput.parentNode) {
2447
- let body;
2448
- try {
2449
- body = window.top.document.body;
2450
- } catch (e) {
2451
- body = document.body;
2452
- }
2470
+ const body = _UI.getBody();
2453
2471
  const containerClone = this.telInput.parentNode.cloneNode(false);
2454
2472
  containerClone.style.visibility = "hidden";
2455
2473
  body.appendChild(containerClone);
@@ -2463,6 +2481,20 @@ var UI = class {
2463
2481
  }
2464
2482
  return 0;
2465
2483
  }
2484
+ // this is run before we add the dropdown to the DOM
2485
+ getHiddenDropdownHeight() {
2486
+ const body = _UI.getBody();
2487
+ this.dropdownContent.classList.remove(CLASSES.HIDE);
2488
+ const tempContainer = createEl("div", { class: "iti iti--inline-dropdown" });
2489
+ tempContainer.appendChild(this.dropdownContent);
2490
+ tempContainer.style.visibility = "hidden";
2491
+ body.appendChild(tempContainer);
2492
+ const height = this.dropdownContent.offsetHeight;
2493
+ body.removeChild(tempContainer);
2494
+ tempContainer.style.visibility = "";
2495
+ this.dropdownContent.classList.add(CLASSES.HIDE);
2496
+ return height;
2497
+ }
2466
2498
  //* Update search results text (for a11y).
2467
2499
  updateSearchResultsA11yText() {
2468
2500
  const { i18n } = this.options;
@@ -2616,7 +2648,7 @@ var UI = class {
2616
2648
  this.searchNoResults = null;
2617
2649
  this.searchResultsA11yText = null;
2618
2650
  this.countryList = null;
2619
- this.dropdown = null;
2651
+ this.dropdownForContainer = null;
2620
2652
  this.hiddenInput = null;
2621
2653
  this.hiddenInputCountry = null;
2622
2654
  this.highlightedItem = null;
@@ -2628,13 +2660,20 @@ var UI = class {
2628
2660
  }
2629
2661
  // UI: Open the dropdown (DOM only).
2630
2662
  openDropdown() {
2631
- const { fixDropdownWidth, countrySearch, dropdownAlwaysOpen } = this.options;
2632
- if (fixDropdownWidth) {
2633
- this.dropdownContent.style.width = `${this.telInput.offsetWidth}px`;
2663
+ const { countrySearch, dropdownAlwaysOpen, dropdownContainer } = this.options;
2664
+ if (dropdownContainer) {
2665
+ this._handleDropdownContainer();
2666
+ } else {
2667
+ const positionBelow = this._shouldPositionDropdownBelowInput();
2668
+ const distance = this.telInput.offsetHeight + LAYOUT.DROPDOWN_MARGIN;
2669
+ if (positionBelow) {
2670
+ this.dropdownContent.style.top = `${distance}px`;
2671
+ } else {
2672
+ this.dropdownContent.style.bottom = `${distance}px`;
2673
+ }
2634
2674
  }
2635
2675
  this.dropdownContent.classList.remove(CLASSES.HIDE);
2636
2676
  this.selectedCountry.setAttribute(ARIA.EXPANDED, "true");
2637
- this.setDropdownPosition();
2638
2677
  if (countrySearch) {
2639
2678
  const firstCountryItem = this.countryList.firstElementChild;
2640
2679
  if (firstCountryItem) {
@@ -2661,21 +2700,33 @@ var UI = class {
2661
2700
  }
2662
2701
  this.dropdownArrow.classList.remove(CLASSES.ARROW_UP);
2663
2702
  if (dropdownContainer) {
2664
- this.dropdown.remove();
2703
+ this.dropdownForContainer.remove();
2704
+ this.dropdownForContainer.style.top = "";
2705
+ } else {
2706
+ this.dropdownContent.style.top = "";
2707
+ this.dropdownContent.style.bottom = "";
2665
2708
  }
2666
2709
  }
2667
- // UI: Position the dropdown (DOM only). Does not bind scroll listeners.
2668
- setDropdownPosition() {
2710
+ _shouldPositionDropdownBelowInput() {
2711
+ const inputPos = this.telInput.getBoundingClientRect();
2712
+ const spaceAbove = inputPos.top;
2713
+ const spaceBelow = window.innerHeight - inputPos.bottom;
2714
+ return spaceBelow >= this.dropdownHeight || spaceBelow >= spaceAbove;
2715
+ }
2716
+ // inject dropdown into container and apply positioning styles
2717
+ _handleDropdownContainer() {
2669
2718
  const { dropdownContainer, useFullscreenPopup } = this.options;
2670
2719
  if (dropdownContainer) {
2671
- dropdownContainer.appendChild(this.dropdown);
2672
- }
2673
- if (!useFullscreenPopup) {
2674
- const inputPosRelativeToVP = this.telInput.getBoundingClientRect();
2675
- const inputHeight = this.telInput.offsetHeight;
2676
- if (dropdownContainer && this.dropdown) {
2677
- this.dropdown.style.top = `${inputPosRelativeToVP.top + inputHeight}px`;
2678
- this.dropdown.style.left = `${inputPosRelativeToVP.left}px`;
2720
+ dropdownContainer.appendChild(this.dropdownForContainer);
2721
+ if (!useFullscreenPopup) {
2722
+ const inputPos = this.telInput.getBoundingClientRect();
2723
+ this.dropdownForContainer.style.left = `${inputPos.left}px`;
2724
+ const positionBelow = this._shouldPositionDropdownBelowInput();
2725
+ if (positionBelow) {
2726
+ this.dropdownForContainer.style.top = `${inputPos.bottom + LAYOUT.DROPDOWN_MARGIN}px`;
2727
+ } else {
2728
+ this.dropdownForContainer.style.top = `${inputPos.top - this.dropdownHeight - LAYOUT.DROPDOWN_MARGIN}px`;
2729
+ }
2679
2730
  }
2680
2731
  }
2681
2732
  }
@@ -3975,7 +4026,7 @@ var intlTelInput = Object.assign((input, options) => {
3975
4026
  attachUtils,
3976
4027
  startedLoadingUtilsScript: false,
3977
4028
  startedLoadingAutoCountry: false,
3978
- version: "26.5.2"
4029
+ version: "26.7.0"
3979
4030
  });
3980
4031
  var intl_tel_input_default = intlTelInput;
3981
4032
 
@@ -1753,8 +1753,10 @@ var LAYOUT = {
1753
1753
  // px width fallback when separateDialCode enabled
1754
1754
  SANE_SELECTED_NO_DIAL_WIDTH: 42,
1755
1755
  // px width fallback when no separate dial code
1756
- INPUT_PADDING_EXTRA_LEFT: 6
1756
+ INPUT_PADDING_EXTRA_LEFT: 6,
1757
1757
  // px gap between selected country container and input text
1758
+ DROPDOWN_MARGIN: 3
1759
+ // px margin between dropdown and tel input
1758
1760
  };
1759
1761
  var DIAL = {
1760
1762
  PLUS: "+",
@@ -1856,7 +1858,7 @@ var defaults = {
1856
1858
  allowPhonewords: false,
1857
1859
  //* Add a placeholder in the input with an example number for the selected country.
1858
1860
  autoPlaceholder: PLACEHOLDER_MODES.POLITE,
1859
- //* Modify the parentClass.
1861
+ //* Add a custom class to the (injected) container element.
1860
1862
  containerClass: "",
1861
1863
  //* Locale for localising country names via Intl.DisplayNames.
1862
1864
  countryNameLocale: "en",
@@ -1894,10 +1896,12 @@ var defaults = {
1894
1896
  onlyCountries: [],
1895
1897
  //* Number type to use for placeholders.
1896
1898
  placeholderNumberType: "MOBILE",
1897
- //* Show flags - for both the selected country, and in the country dropdown
1898
- showFlags: true,
1899
+ //* Add custom classes to the search input element.
1900
+ searchInputClass: "",
1899
1901
  //* Display the international dial code next to the selected flag.
1900
1902
  separateDialCode: false,
1903
+ //* Show flags - for both the selected country, and in the country dropdown
1904
+ showFlags: true,
1901
1905
  //* Only allow certain chars e.g. a plus followed by numeric digits, and cap at max valid length.
1902
1906
  strictMode: false,
1903
1907
  //* Use full screen popup instead of dropdown for country list.
@@ -1985,6 +1989,7 @@ var validateOptions = (customOptions) => {
1985
1989
  validatedOptions[key] = value;
1986
1990
  break;
1987
1991
  case "containerClass":
1992
+ case "searchInputClass":
1988
1993
  case "countryNameLocale":
1989
1994
  if (typeof value !== "string") {
1990
1995
  warnOption(key, "a string", value);
@@ -2190,9 +2195,11 @@ var buildGlobeIcon = () => `
2190
2195
  </svg>`;
2191
2196
 
2192
2197
  // angular/build/temp/modules/core/ui.js
2193
- var UI = class {
2198
+ var UI = class _UI {
2194
2199
  constructor(input, options, id2) {
2195
2200
  this.searchKeyupTimer = null;
2201
+ this.dropdownHeight = null;
2202
+ this.dropdownForContainer = null;
2196
2203
  this.highlightedItem = null;
2197
2204
  this.selectedItem = null;
2198
2205
  input.dataset.intlTelInputId = id2.toString();
@@ -2308,6 +2315,13 @@ var UI = class {
2308
2315
  if (countrySearch) {
2309
2316
  this.updateSearchResultsA11yText();
2310
2317
  }
2318
+ if (fixDropdownWidth) {
2319
+ this.dropdownContent.style.width = `${this.telInput.offsetWidth}px`;
2320
+ }
2321
+ this.dropdownHeight = this.getHiddenDropdownHeight();
2322
+ if (countrySearch && !useFullscreenPopup) {
2323
+ this.dropdownContent.style.height = `${this.dropdownHeight}px`;
2324
+ }
2311
2325
  if (dropdownContainer) {
2312
2326
  const dropdownClasses = buildClassNames({
2313
2327
  iti: true,
@@ -2316,14 +2330,14 @@ var UI = class {
2316
2330
  "iti--inline-dropdown": !useFullscreenPopup,
2317
2331
  [containerClass]: Boolean(containerClass)
2318
2332
  });
2319
- this.dropdown = createEl("div", { class: dropdownClasses });
2320
- this.dropdown.appendChild(this.dropdownContent);
2333
+ this.dropdownForContainer = createEl("div", { class: dropdownClasses });
2334
+ this.dropdownForContainer.appendChild(this.dropdownContent);
2321
2335
  } else {
2322
2336
  this.countryContainer.appendChild(this.dropdownContent);
2323
2337
  }
2324
2338
  }
2325
2339
  _buildSearchUI() {
2326
- const { i18n } = this.options;
2340
+ const { i18n, searchInputClass } = this.options;
2327
2341
  const searchWrapper = createEl("div", { class: "iti__search-input-wrapper" }, this.dropdownContent);
2328
2342
  this.searchIcon = createEl("span", {
2329
2343
  class: "iti__search-icon",
@@ -2334,7 +2348,7 @@ var UI = class {
2334
2348
  id: `iti-${this.id}__search-input`,
2335
2349
  // Chrome says inputs need either a name or an id
2336
2350
  type: "search",
2337
- class: "iti__search-input",
2351
+ class: `iti__search-input ${searchInputClass}`,
2338
2352
  placeholder: i18n.searchPlaceholder,
2339
2353
  // 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
2340
2354
  role: "combobox",
@@ -2419,12 +2433,12 @@ var UI = class {
2419
2433
  createEl("div", { class: `${CLASSES.FLAG} iti__${c.iso2}` }, listItem);
2420
2434
  }
2421
2435
  const nameEl = createEl("span", { class: "iti__country-name" }, listItem);
2422
- nameEl.textContent = c.name;
2436
+ nameEl.textContent = `${c.name} `;
2423
2437
  const dialEl = createEl("span", { class: "iti__dial-code" }, nameEl);
2424
2438
  if (this.isRTL) {
2425
2439
  dialEl.setAttribute("dir", "ltr");
2426
2440
  }
2427
- dialEl.textContent = `+${c.dialCode}`;
2441
+ dialEl.textContent = `(+${c.dialCode})`;
2428
2442
  frag.appendChild(listItem);
2429
2443
  }
2430
2444
  this.countryList.appendChild(frag);
@@ -2438,18 +2452,22 @@ var UI = class {
2438
2452
  this.telInput.style.paddingLeft = `${inputPadding}px`;
2439
2453
  }
2440
2454
  }
2455
+ static getBody() {
2456
+ let body;
2457
+ try {
2458
+ body = window.top.document.body;
2459
+ } catch (e) {
2460
+ body = document.body;
2461
+ }
2462
+ return body;
2463
+ }
2441
2464
  //* When input is in a hidden container during init, we cannot calculate the selected country width.
2442
2465
  //* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.
2443
2466
  //* To get the right styling to apply, all we need is a shallow clone of the container,
2444
2467
  //* and then to inject a deep clone of the selectedCountry element.
2445
2468
  _getHiddenSelectedCountryWidth() {
2446
2469
  if (this.telInput.parentNode) {
2447
- let body;
2448
- try {
2449
- body = window.top.document.body;
2450
- } catch (e) {
2451
- body = document.body;
2452
- }
2470
+ const body = _UI.getBody();
2453
2471
  const containerClone = this.telInput.parentNode.cloneNode(false);
2454
2472
  containerClone.style.visibility = "hidden";
2455
2473
  body.appendChild(containerClone);
@@ -2463,6 +2481,20 @@ var UI = class {
2463
2481
  }
2464
2482
  return 0;
2465
2483
  }
2484
+ // this is run before we add the dropdown to the DOM
2485
+ getHiddenDropdownHeight() {
2486
+ const body = _UI.getBody();
2487
+ this.dropdownContent.classList.remove(CLASSES.HIDE);
2488
+ const tempContainer = createEl("div", { class: "iti iti--inline-dropdown" });
2489
+ tempContainer.appendChild(this.dropdownContent);
2490
+ tempContainer.style.visibility = "hidden";
2491
+ body.appendChild(tempContainer);
2492
+ const height = this.dropdownContent.offsetHeight;
2493
+ body.removeChild(tempContainer);
2494
+ tempContainer.style.visibility = "";
2495
+ this.dropdownContent.classList.add(CLASSES.HIDE);
2496
+ return height;
2497
+ }
2466
2498
  //* Update search results text (for a11y).
2467
2499
  updateSearchResultsA11yText() {
2468
2500
  const { i18n } = this.options;
@@ -2616,7 +2648,7 @@ var UI = class {
2616
2648
  this.searchNoResults = null;
2617
2649
  this.searchResultsA11yText = null;
2618
2650
  this.countryList = null;
2619
- this.dropdown = null;
2651
+ this.dropdownForContainer = null;
2620
2652
  this.hiddenInput = null;
2621
2653
  this.hiddenInputCountry = null;
2622
2654
  this.highlightedItem = null;
@@ -2628,13 +2660,20 @@ var UI = class {
2628
2660
  }
2629
2661
  // UI: Open the dropdown (DOM only).
2630
2662
  openDropdown() {
2631
- const { fixDropdownWidth, countrySearch, dropdownAlwaysOpen } = this.options;
2632
- if (fixDropdownWidth) {
2633
- this.dropdownContent.style.width = `${this.telInput.offsetWidth}px`;
2663
+ const { countrySearch, dropdownAlwaysOpen, dropdownContainer } = this.options;
2664
+ if (dropdownContainer) {
2665
+ this._handleDropdownContainer();
2666
+ } else {
2667
+ const positionBelow = this._shouldPositionDropdownBelowInput();
2668
+ const distance = this.telInput.offsetHeight + LAYOUT.DROPDOWN_MARGIN;
2669
+ if (positionBelow) {
2670
+ this.dropdownContent.style.top = `${distance}px`;
2671
+ } else {
2672
+ this.dropdownContent.style.bottom = `${distance}px`;
2673
+ }
2634
2674
  }
2635
2675
  this.dropdownContent.classList.remove(CLASSES.HIDE);
2636
2676
  this.selectedCountry.setAttribute(ARIA.EXPANDED, "true");
2637
- this.setDropdownPosition();
2638
2677
  if (countrySearch) {
2639
2678
  const firstCountryItem = this.countryList.firstElementChild;
2640
2679
  if (firstCountryItem) {
@@ -2661,21 +2700,33 @@ var UI = class {
2661
2700
  }
2662
2701
  this.dropdownArrow.classList.remove(CLASSES.ARROW_UP);
2663
2702
  if (dropdownContainer) {
2664
- this.dropdown.remove();
2703
+ this.dropdownForContainer.remove();
2704
+ this.dropdownForContainer.style.top = "";
2705
+ } else {
2706
+ this.dropdownContent.style.top = "";
2707
+ this.dropdownContent.style.bottom = "";
2665
2708
  }
2666
2709
  }
2667
- // UI: Position the dropdown (DOM only). Does not bind scroll listeners.
2668
- setDropdownPosition() {
2710
+ _shouldPositionDropdownBelowInput() {
2711
+ const inputPos = this.telInput.getBoundingClientRect();
2712
+ const spaceAbove = inputPos.top;
2713
+ const spaceBelow = window.innerHeight - inputPos.bottom;
2714
+ return spaceBelow >= this.dropdownHeight || spaceBelow >= spaceAbove;
2715
+ }
2716
+ // inject dropdown into container and apply positioning styles
2717
+ _handleDropdownContainer() {
2669
2718
  const { dropdownContainer, useFullscreenPopup } = this.options;
2670
2719
  if (dropdownContainer) {
2671
- dropdownContainer.appendChild(this.dropdown);
2672
- }
2673
- if (!useFullscreenPopup) {
2674
- const inputPosRelativeToVP = this.telInput.getBoundingClientRect();
2675
- const inputHeight = this.telInput.offsetHeight;
2676
- if (dropdownContainer && this.dropdown) {
2677
- this.dropdown.style.top = `${inputPosRelativeToVP.top + inputHeight}px`;
2678
- this.dropdown.style.left = `${inputPosRelativeToVP.left}px`;
2720
+ dropdownContainer.appendChild(this.dropdownForContainer);
2721
+ if (!useFullscreenPopup) {
2722
+ const inputPos = this.telInput.getBoundingClientRect();
2723
+ this.dropdownForContainer.style.left = `${inputPos.left}px`;
2724
+ const positionBelow = this._shouldPositionDropdownBelowInput();
2725
+ if (positionBelow) {
2726
+ this.dropdownForContainer.style.top = `${inputPos.bottom + LAYOUT.DROPDOWN_MARGIN}px`;
2727
+ } else {
2728
+ this.dropdownForContainer.style.top = `${inputPos.top - this.dropdownHeight - LAYOUT.DROPDOWN_MARGIN}px`;
2729
+ }
2679
2730
  }
2680
2731
  }
2681
2732
  }
@@ -3975,7 +4026,7 @@ var intlTelInput = Object.assign((input, options) => {
3975
4026
  attachUtils,
3976
4027
  startedLoadingUtilsScript: false,
3977
4028
  startedLoadingAutoCountry: false,
3978
- version: "26.5.2"
4029
+ version: "26.7.0"
3979
4030
  });
3980
4031
  var intl_tel_input_default = intlTelInput;
3981
4032
 
@@ -46,6 +46,7 @@ export declare const LAYOUT: {
46
46
  readonly SANE_SELECTED_WITH_DIAL_WIDTH: 78;
47
47
  readonly SANE_SELECTED_NO_DIAL_WIDTH: 42;
48
48
  readonly INPUT_PADDING_EXTRA_LEFT: 6;
49
+ readonly DROPDOWN_MARGIN: 3;
49
50
  };
50
51
  export declare const DIAL: {
51
52
  readonly PLUS: "+";
@@ -7,6 +7,7 @@ export default class UI {
7
7
  private readonly originalPaddingLeft;
8
8
  private countries;
9
9
  private searchKeyupTimer;
10
+ private dropdownHeight;
10
11
  telInput: HTMLInputElement;
11
12
  countryContainer: HTMLElement;
12
13
  selectedCountry: HTMLElement;
@@ -20,7 +21,7 @@ export default class UI {
20
21
  searchNoResults: HTMLElement;
21
22
  searchResultsA11yText: HTMLElement;
22
23
  countryList: HTMLElement;
23
- dropdown: HTMLElement;
24
+ dropdownForContainer: HTMLElement | null;
24
25
  hiddenInput: HTMLInputElement;
25
26
  hiddenInputCountry: HTMLInputElement;
26
27
  highlightedItem: HTMLElement | null;
@@ -37,7 +38,9 @@ export default class UI {
37
38
  private _maybeBuildHiddenInputs;
38
39
  private _appendListItems;
39
40
  updateInputPadding(): void;
41
+ private static getBody;
40
42
  private _getHiddenSelectedCountryWidth;
43
+ private getHiddenDropdownHeight;
41
44
  updateSearchResultsA11yText(): void;
42
45
  filterCountriesByQuery(query: string): void;
43
46
  private doFilter;
@@ -51,7 +54,8 @@ export default class UI {
51
54
  destroy(): void;
52
55
  openDropdown(): void;
53
56
  closeDropdown(): void;
54
- setDropdownPosition(): void;
57
+ _shouldPositionDropdownBelowInput(): boolean;
58
+ _handleDropdownContainer(): void;
55
59
  isDropdownClosed(): boolean;
56
60
  setCountry(selectedCountryData: SelectedCountryData): void;
57
61
  }
@@ -28,13 +28,14 @@ export type NumberType = SetValues<typeof NUMBER_TYPE_SET>;
28
28
  type ValueOf<T> = T[keyof T];
29
29
  export interface AllOptions {
30
30
  allowDropdown: boolean;
31
+ allowedNumberTypes: NumberType[] | null;
31
32
  allowNumberExtensions: boolean;
32
33
  allowPhonewords: boolean;
33
34
  autoPlaceholder: ValueOf<typeof PLACEHOLDER_MODES>;
34
35
  containerClass: string;
36
+ countryNameLocale: string;
35
37
  countryOrder: Iso2[] | null;
36
38
  countrySearch: boolean;
37
- countryNameLocale: string;
38
39
  customPlaceholder: ((selectedCountryPlaceholder: string, selectedCountryData: SelectedCountryData) => string) | null;
39
40
  dropdownAlwaysOpen: boolean;
40
41
  dropdownContainer: HTMLElement | null;
@@ -53,11 +54,11 @@ export interface AllOptions {
53
54
  nationalMode: boolean;
54
55
  onlyCountries: Iso2[];
55
56
  placeholderNumberType: NumberType;
56
- showFlags: boolean;
57
+ searchInputClass: string;
57
58
  separateDialCode: boolean;
59
+ showFlags: boolean;
58
60
  strictMode: boolean;
59
61
  useFullscreenPopup: boolean;
60
- allowedNumberTypes: NumberType[] | null;
61
62
  }
62
63
  export type SomeOptions = Partial<AllOptions>;
63
64
  export interface IntlTelInputInterface {
@@ -11,9 +11,8 @@ body {
11
11
 
12
12
  input,
13
13
  .button {
14
- height: 35px;
15
14
  margin: 0;
16
- padding: 6px 12px;
15
+ padding: 8px 12px;
17
16
  border-radius: 2px;
18
17
  font-family: inherit;
19
18
  font-size: 100%;
@@ -67,7 +66,6 @@ input::placeholder {
67
66
  }
68
67
  .iti {
69
68
  --iti-border-color: #5b5b5b;
70
- --iti-dialcode-color: #999999;
71
69
  --iti-dropdown-bg: #0d1117;
72
70
  --iti-hover-color: #30363d;
73
71
  --iti-icon-color: #aaa;