intl-tel-input 29.1.0 → 29.1.2

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.
@@ -2455,7 +2455,7 @@ var Numerals = class _Numerals {
2455
2455
 
2456
2456
  // packages/core/src/js/core/ui.ts
2457
2457
  var supportsCssAnchor = typeof CSS !== "undefined" && typeof CSS.supports === "function" && CSS.supports("anchor-name: --x");
2458
- var UI = class _UI {
2458
+ var UI = class {
2459
2459
  // private
2460
2460
  #options;
2461
2461
  #id;
@@ -2465,6 +2465,7 @@ var UI = class _UI {
2465
2465
  #searchTokens;
2466
2466
  #searchDebounceTimer = null;
2467
2467
  #inlineDropdownHeight;
2468
+ #cssAnchorPositioningDone = false;
2468
2469
  #countryContainerEl;
2469
2470
  #selectedCountryEl;
2470
2471
  #selectedFlagEl;
@@ -2657,16 +2658,6 @@ var UI = class _UI {
2657
2658
  if (countrySearch) {
2658
2659
  this.#updateSearchResultsA11yText();
2659
2660
  }
2660
- if (!isFullscreen) {
2661
- const { height, width } = this.#getHiddenInlineDropdownSize();
2662
- this.#inlineDropdownHeight = height;
2663
- if (countrySearch) {
2664
- this.#countrySelectorEl.style.height = `${height}px`;
2665
- if (!matchDropdownWidth && width > 0) {
2666
- this.#countrySelectorEl.style.width = `${width}px`;
2667
- }
2668
- }
2669
- }
2670
2661
  if (detachedParent) {
2671
2662
  const wrapperClasses = buildClassNames({
2672
2663
  iti: true,
@@ -2677,9 +2668,6 @@ var UI = class _UI {
2677
2668
  });
2678
2669
  this.#detachedCountrySelectorEl = createEl("div", { class: wrapperClasses });
2679
2670
  this.#detachedCountrySelectorEl.appendChild(this.#countrySelectorEl);
2680
- if (!isFullscreen) {
2681
- this.#setupCssAnchorPositioning();
2682
- }
2683
2671
  } else {
2684
2672
  this.#countryContainerEl.appendChild(this.#countrySelectorEl);
2685
2673
  }
@@ -2852,24 +2840,16 @@ var UI = class _UI {
2852
2840
  });
2853
2841
  this.#resizeObserver.observe(this.#selectedCountryEl);
2854
2842
  }
2855
- static #getBody() {
2856
- let body;
2857
- try {
2858
- body = window.top.document.body;
2859
- } catch (e) {
2860
- body = document.body;
2861
- }
2862
- return body;
2863
- }
2864
2843
  //* When input is in a hidden container during init, we cannot calculate the selected country width.
2865
2844
  //* Fix: clone the markup, make it invisible, add it to the end of the DOM, and then measure it's width.
2866
2845
  //* To get the right styling to apply, all we need is a shallow clone of the container,
2867
2846
  //* and then to inject a deep clone of the selectedCountryEl element.
2847
+ //* Measures in the LOCAL document.body: appending to the local body escapes any hidden ancestor container, and the input's own frame is where intl-tel-input's styles live (so the clone lays out correctly). We deliberately do NOT escape to window.top: that only measures correctly in the rare case where the top frame also loads the library's styles, and measures wrong when it doesn't (e.g. a same-origin iframe whose outer frame lacks the styles — cf. #2178). If the local frame itself isn't laid out yet (e.g. an iframe hidden during init), this returns 0 and the caller falls back to a sane constant; the ResizeObserver in #observeSelectedCountryResize then corrects the padding once the input becomes visible.
2868
2848
  #getHiddenSelectedCountryWidth() {
2869
2849
  if (!this.telInputEl.parentNode) {
2870
2850
  return 0;
2871
2851
  }
2872
- const body = _UI.#getBody();
2852
+ const body = document.body;
2873
2853
  const containerClone = this.telInputEl.parentNode.cloneNode(
2874
2854
  false
2875
2855
  );
@@ -2885,21 +2865,43 @@ var UI = class _UI {
2885
2865
  body.removeChild(containerClone);
2886
2866
  return width;
2887
2867
  }
2888
- // Get the dropdown size (before it is added to the DOM)
2868
+ //* Measure the inline dropdown size once, lazily, on first open see #getHiddenInlineDropdownSize for why measuring forces a reflow. Memoised via #inlineDropdownHeight so subsequent opens are free.
2869
+ //* Captured for two uses: (1) on open, decide whether to position the dropdown above or below the input; (2) when countrySearch is enabled, pin the dropdown height (and, when matchDropdownWidth is disabled, width) so it doesn't jump around as the country list is filtered.
2870
+ #ensureInlineDropdownSizeMeasured() {
2871
+ if (this.#inlineDropdownHeight !== void 0) {
2872
+ return;
2873
+ }
2874
+ const { countrySearch, matchDropdownWidth } = this.#options;
2875
+ const { height, width } = this.#getHiddenInlineDropdownSize();
2876
+ this.#inlineDropdownHeight = height;
2877
+ if (countrySearch) {
2878
+ this.#countrySelectorEl.style.height = `${height}px`;
2879
+ if (!matchDropdownWidth && width > 0) {
2880
+ this.#countrySelectorEl.style.width = `${width}px`;
2881
+ }
2882
+ }
2883
+ }
2884
+ // Measure the dropdown by moving it into a temporary hidden container on the body (it needs the right ancestor classes to lay out correctly). Restores it to its original position afterwards — a no-op during init (when it is still detached) but required when called lazily on first open (when it is already inserted).
2885
+ //* Deliberately measures in the LOCAL document.body (not window.top): this runs on first open, when the input's own frame is visibly rendered and styled. Escaping to the top frame breaks when the input is inside a same-origin iframe whose outer frame lacks intl-tel-input's styles (e.g. Storybook), as the dropdown would then be measured unstyled and come out far too tall (issue #2178).
2889
2886
  #getHiddenInlineDropdownSize() {
2890
- const body = _UI.#getBody();
2891
- this.#countrySelectorEl.classList.remove(CLASSES.HIDE);
2887
+ const body = document.body;
2888
+ const selectorEl = this.#countrySelectorEl;
2889
+ const originalParent = selectorEl.parentNode;
2890
+ const originalNextSibling = selectorEl.nextSibling;
2891
+ selectorEl.classList.remove(CLASSES.HIDE);
2892
2892
  const tempContainer = createEl("div", {
2893
2893
  class: "iti iti--inline-country-selector"
2894
2894
  });
2895
- tempContainer.appendChild(this.#countrySelectorEl);
2895
+ tempContainer.appendChild(selectorEl);
2896
2896
  tempContainer.style.visibility = "hidden";
2897
2897
  body.appendChild(tempContainer);
2898
- const height = this.#countrySelectorEl.offsetHeight;
2899
- const width = this.#countrySelectorEl.offsetWidth;
2898
+ const height = selectorEl.offsetHeight;
2899
+ const width = selectorEl.offsetWidth;
2900
2900
  body.removeChild(tempContainer);
2901
- tempContainer.style.visibility = "";
2902
- this.#countrySelectorEl.classList.add(CLASSES.HIDE);
2901
+ selectorEl.classList.add(CLASSES.HIDE);
2902
+ if (originalParent) {
2903
+ originalParent.insertBefore(selectorEl, originalNextSibling);
2904
+ }
2903
2905
  return {
2904
2906
  height: height > 0 ? height : LAYOUT.FALLBACK_DROPDOWN_HEIGHT,
2905
2907
  width
@@ -3063,6 +3065,9 @@ var UI = class _UI {
3063
3065
  openCountrySelector(onSelect, onClose) {
3064
3066
  const { countrySearch, dropdownAlwaysOpen } = this.#options;
3065
3067
  this.#countrySelectorAbortController = new AbortController();
3068
+ if (this.#options.countrySelectorMode !== COUNTRY_SELECTOR_MODE.FULLSCREEN) {
3069
+ this.#ensureInlineDropdownSizeMeasured();
3070
+ }
3066
3071
  this.ensureDropdownWidthSet();
3067
3072
  if (this.#detachedCountrySelectorEl) {
3068
3073
  this.#injectAndPositionDetachedCountrySelector();
@@ -3335,7 +3340,10 @@ var UI = class _UI {
3335
3340
  this.#detachedCountrySelectorEl.style.paddingLeft = `${inputPos.left}px`;
3336
3341
  this.#detachedCountrySelectorEl.style.paddingRight = `${window.innerWidth - inputPos.right}px`;
3337
3342
  }
3338
- } else if (!supportsCssAnchor) {
3343
+ } else {
3344
+ this.#setupCssAnchorPositioning();
3345
+ }
3346
+ if (!isFullscreen && !supportsCssAnchor) {
3339
3347
  const inputPos = this.telInputEl.getBoundingClientRect();
3340
3348
  this.#detachedCountrySelectorEl.style.left = `${inputPos.left}px`;
3341
3349
  if (this.#shouldPositionDropdownBelowInput()) {
@@ -3348,13 +3356,17 @@ var UI = class _UI {
3348
3356
  detachedParent.appendChild(this.#detachedCountrySelectorEl);
3349
3357
  }
3350
3358
  //* Wire up CSS Anchor Positioning between the input and the detached country selector using a
3351
- //* unique anchor name per instance. Called once at build time — the matching styles in
3359
+ //* unique anchor name per instance. Called lazily on first open (memoised) — the matching styles in
3352
3360
  //* intlTelInput.css only take effect in browsers that support anchor(); elsewhere these
3353
3361
  //* properties are inert. We append our name to any existing anchor-name (read via
3354
3362
  //* getComputedStyle so we pick up CSS-defined values), so consumer-set anchors on the input
3355
3363
  //* are preserved. Caveat: this snapshots the consumer's value once — if they later change
3356
3364
  //* anchor-name via CSS (e.g. a class swap), our inline write will shadow the change.
3357
3365
  #setupCssAnchorPositioning() {
3366
+ if (this.#cssAnchorPositioningDone) {
3367
+ return;
3368
+ }
3369
+ this.#cssAnchorPositioningDone = true;
3358
3370
  const anchorName = `--iti-anchor-${this.#id}`;
3359
3371
  const existing = getComputedStyle(this.telInputEl).anchorName;
3360
3372
  this.telInputEl.style.anchorName = existing && existing !== "none" ? `${existing}, ${anchorName}` : anchorName;
@@ -4829,7 +4841,7 @@ var intlTelInput = Object.assign(
4829
4841
  attachUtils,
4830
4842
  startedLoadingUtils: false,
4831
4843
  startedLoadingAutoCountry: false,
4832
- version: "29.1.0",
4844
+ version: "29.1.2",
4833
4845
  NUMBER_FORMAT,
4834
4846
  NUMBER_TYPE,
4835
4847
  VALIDATION_ERROR,