intl-tel-input 29.3.0 → 29.4.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.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * International Telephone Input v29.3.0
2
+ * International Telephone Input v29.4.0
3
3
  * git+https://github.com/jackocnr/intl-tel-input.git
4
4
  * Licensed under the MIT license
5
5
  */
@@ -2008,7 +2008,7 @@ var _factory = (() => {
2008
2008
  customPlaceholder: null,
2009
2009
  //* Always show the dropdown
2010
2010
  dropdownAlwaysOpen: false,
2011
- //* 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.
2011
+ //* 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 (see fullscreenParent).
2012
2012
  dropdownParent: null,
2013
2013
  //* Don't display these countries.
2014
2014
  excludeCountries: null,
@@ -2016,6 +2016,8 @@ var _factory = (() => {
2016
2016
  matchDropdownWidth: true,
2017
2017
  //* Format the number as the user types
2018
2018
  formatAsYouType: true,
2019
+ //* Optional DOM element to append the fullscreen popup to, instead of document.body (e.g. a modal <dialog>, where a body-mounted popup would be unreachable). Only consulted in fullscreen rendering (see dropdownParent for the dropdown). Null means document.body, which is resolved on open rather than here, as document may not exist yet (e.g. SSR).
2020
+ fullscreenParent: null,
2019
2021
  //* Inject hidden inputs with the names returned from this function, and on submit, populate them with the full number and selected country iso2.
2020
2022
  hiddenInputs: null,
2021
2023
  //* Translations for the core library UI strings e.g. search input placeholder, country names.
@@ -2199,8 +2201,9 @@ var _factory = (() => {
2199
2201
  validatedOptions[key] = value;
2200
2202
  break;
2201
2203
  case "dropdownParent":
2204
+ case "fullscreenParent":
2202
2205
  if (value !== null && !isElLike(value)) {
2203
- warnOption("dropdownParent", "an HTMLElement or null", value);
2206
+ warnOption(key, "an HTMLElement or null", value);
2204
2207
  break;
2205
2208
  }
2206
2209
  validatedOptions[key] = value;
@@ -2764,6 +2767,9 @@ var _factory = (() => {
2764
2767
  },
2765
2768
  this.#countrySelectorEl
2766
2769
  );
2770
+ if (!countrySearch) {
2771
+ this.#countryListEl.setAttribute("tabindex", "0");
2772
+ }
2767
2773
  this.#appendListItems();
2768
2774
  if (countrySearch) {
2769
2775
  this.#updateSearchResultsA11yText();
@@ -2784,11 +2790,12 @@ var _factory = (() => {
2784
2790
  this.#countryContainerEl.appendChild(this.#countrySelectorEl);
2785
2791
  }
2786
2792
  }
2787
- //* 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).
2793
+ //* Resolve the DOM element to attach the country selector to. Fullscreen is always detached: it uses the consumer-supplied fullscreenParent, falling back to document.body; dropdown uses the consumer-supplied dropdownParent (if any); otherwise the country selector renders inline within the input wrapper (no detached element).
2794
+ //* NOTE: these are deliberately two separate options. For the dropdown, setting a parent switches it from inline to detached rendering (pos:fixed, positioned against the input), whereas the fullscreen popup is detached regardless, so its parent only relocates it. Sharing one option would force a detached dropdown on anyone who only needs to move the fullscreen popup e.g. into a modal <dialog> (issue #2199).
2788
2795
  #getDetachedParent() {
2789
- const { countrySelectorMode, dropdownParent } = this.#options;
2796
+ const { countrySelectorMode, dropdownParent, fullscreenParent } = this.#options;
2790
2797
  if (countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN) {
2791
- return document.body;
2798
+ return fullscreenParent ?? document.body;
2792
2799
  }
2793
2800
  if (countrySelectorMode === COUNTRY_SELECTOR_MODE.DROPDOWN) {
2794
2801
  return dropdownParent;
@@ -2914,7 +2921,6 @@ var _factory = (() => {
2914
2921
  const listItem = createEl("li", {
2915
2922
  id: `iti-${this.#id}__item-${c.iso2}`,
2916
2923
  class: liClass,
2917
- tabindex: "-1",
2918
2924
  role: "option",
2919
2925
  [ARIA.SELECTED]: "false"
2920
2926
  });
@@ -3106,18 +3112,20 @@ var _factory = (() => {
3106
3112
  container.scrollTop = offsetTop - containerRect.height + elementRect.height;
3107
3113
  }
3108
3114
  }
3115
+ //* The element that holds focus while the country selector is open: the search input, or (when countrySearch is disabled) the country list itself.
3116
+ #getOpenFocusEl() {
3117
+ return this.#options.countrySearch ? this.#searchInputEl : this.#countryListEl;
3118
+ }
3109
3119
  //* Remove highlighting from the previous list item and highlight the new one.
3110
3120
  #highlightListItem(listItem, doScroll = true) {
3111
3121
  this.#highlightedListItemEl?.classList.remove(CLASSES.HIGHLIGHT);
3112
3122
  if (listItem) {
3113
3123
  listItem.classList.add(CLASSES.HIGHLIGHT);
3114
- if (this.#options.countrySearch) {
3115
- const activeDescendant = listItem.getAttribute("id") || "";
3116
- this.#searchInputEl.setAttribute(
3117
- ARIA.ACTIVE_DESCENDANT,
3118
- activeDescendant
3119
- );
3120
- }
3124
+ const activeDescendant = listItem.getAttribute("id") || "";
3125
+ this.#getOpenFocusEl().setAttribute(
3126
+ ARIA.ACTIVE_DESCENDANT,
3127
+ activeDescendant
3128
+ );
3121
3129
  if (doScroll) {
3122
3130
  this.#scrollCountryListToItem(listItem);
3123
3131
  }
@@ -3196,7 +3204,7 @@ var _factory = (() => {
3196
3204
  //* Open the country selector: create a fresh AbortController, do the DOM work, and wire up all
3197
3205
  //* open-state listeners (which invoke the caller's onSelect / onClose callbacks).
3198
3206
  openCountrySelector(onSelect, onClose) {
3199
- const { countrySearch, dropdownAlwaysOpen } = this.#options;
3207
+ const { dropdownAlwaysOpen } = this.#options;
3200
3208
  this.#countrySelectorAbortController = new AbortController();
3201
3209
  if (this.#options.countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN) {
3202
3210
  this.#ensureInlineDropdownSizeMeasured();
@@ -3219,10 +3227,11 @@ var _factory = (() => {
3219
3227
  if (itemToHighlight) {
3220
3228
  this.#highlightListItem(itemToHighlight);
3221
3229
  }
3222
- if (countrySearch && !dropdownAlwaysOpen) {
3223
- this.#searchInputEl.focus();
3230
+ if (!dropdownAlwaysOpen) {
3231
+ this.#getOpenFocusEl().focus();
3224
3232
  }
3225
3233
  if (this.#options.countrySelectorMode === COUNTRY_SELECTOR_MODE.FULLSCREEN && this.#detachedCountrySelectorEl && window.visualViewport) {
3234
+ this.#adjustFullscreenPopupToViewport();
3226
3235
  window.visualViewport.addEventListener(
3227
3236
  "resize",
3228
3237
  () => {
@@ -3339,6 +3348,20 @@ var _factory = (() => {
3339
3348
  };
3340
3349
  this.#selectedCountryEl?.addEventListener("keydown", handleKeydown, { signal });
3341
3350
  this.#countrySelectorEl?.addEventListener("keydown", handleKeydown, { signal });
3351
+ if (this.#detachedCountrySelectorEl) {
3352
+ this.#countrySelectorEl?.addEventListener(
3353
+ "keydown",
3354
+ (e) => {
3355
+ if (e.key === KEYS.TAB) {
3356
+ e.preventDefault();
3357
+ onEscape();
3358
+ const focusEl = e.shiftKey ? this.#selectedCountryEl : this.telInputEl;
3359
+ focusEl.focus();
3360
+ }
3361
+ },
3362
+ { signal }
3363
+ );
3364
+ }
3342
3365
  }
3343
3366
  //* Wire up country search input listener: typing filters the list, the clear button resets it.
3344
3367
  #bindSearchInputListener(signal) {
@@ -3436,8 +3459,8 @@ var _factory = (() => {
3436
3459
  this.#countrySelectorAbortController = null;
3437
3460
  this.#countrySelectorEl.classList.add(CLASSES.HIDE);
3438
3461
  this.#selectedCountryEl.setAttribute(ARIA.EXPANDED, "false");
3462
+ this.#getOpenFocusEl().removeAttribute(ARIA.ACTIVE_DESCENDANT);
3439
3463
  if (countrySearch) {
3440
- this.#searchInputEl.removeAttribute(ARIA.ACTIVE_DESCENDANT);
3441
3464
  this.#searchInputEl.value = "";
3442
3465
  this.#applySearchFilter();
3443
3466
  if (this.#highlightedListItemEl) {
@@ -3450,6 +3473,7 @@ var _factory = (() => {
3450
3473
  this.#detachedCountrySelectorEl.remove();
3451
3474
  this.#detachedCountrySelectorEl.style.top = "";
3452
3475
  this.#detachedCountrySelectorEl.style.bottom = "";
3476
+ this.#detachedCountrySelectorEl.style.height = "";
3453
3477
  this.#detachedCountrySelectorEl.style.paddingLeft = "";
3454
3478
  this.#detachedCountrySelectorEl.style.paddingRight = "";
3455
3479
  } else {
@@ -3515,8 +3539,7 @@ var _factory = (() => {
3515
3539
  if (!vv || !this.#detachedCountrySelectorEl) {
3516
3540
  return;
3517
3541
  }
3518
- const virtualKeyboardHeight = window.innerHeight - vv.height;
3519
- this.#detachedCountrySelectorEl.style.bottom = `${virtualKeyboardHeight}px`;
3542
+ this.#detachedCountrySelectorEl.style.height = `${vv.height}px`;
3520
3543
  }
3521
3544
  // UI: Whether the country selector is currently open (visible).
3522
3545
  isCountrySelectorOpen() {
@@ -4986,7 +5009,7 @@ var _factory = (() => {
4986
5009
  attachUtils,
4987
5010
  startedLoadingUtils: false,
4988
5011
  startedLoadingAutoCountry: false,
4989
- version: "29.3.0",
5012
+ version: "29.4.0",
4990
5013
  NUMBER_FORMAT,
4991
5014
  NUMBER_TYPE,
4992
5015
  VALIDATION_ERROR,