@ionic/core 8.7.17-dev.11767796972.148e4bc4 → 8.7.17-dev.11767895575.16ea7cef

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 (42) hide show
  1. package/components/content.js +6 -79
  2. package/components/ion-tab-bar.js +23 -3
  3. package/components/modal.js +12 -200
  4. package/components/popover.js +10 -73
  5. package/dist/cjs/ion-app_8.cjs.entry.js +5 -78
  6. package/dist/cjs/ion-modal.cjs.entry.js +12 -200
  7. package/dist/cjs/ion-popover.cjs.entry.js +10 -73
  8. package/dist/cjs/ion-tab-bar_2.cjs.entry.js +23 -3
  9. package/dist/collection/components/content/content.css +0 -10
  10. package/dist/collection/components/content/content.js +4 -77
  11. package/dist/collection/components/modal/gestures/sheet.js +1 -3
  12. package/dist/collection/components/modal/gestures/swipe-to-close.js +1 -3
  13. package/dist/collection/components/modal/modal.ios.css +4 -0
  14. package/dist/collection/components/modal/modal.js +8 -192
  15. package/dist/collection/components/modal/modal.md.css +4 -0
  16. package/dist/collection/components/popover/animations/ios.enter.js +5 -21
  17. package/dist/collection/components/popover/animations/md.enter.js +4 -20
  18. package/dist/collection/components/popover/utils.js +1 -32
  19. package/dist/collection/components/tab-bar/tab-bar.js +23 -3
  20. package/dist/docs.json +1 -1
  21. package/dist/esm/ion-app_8.entry.js +6 -79
  22. package/dist/esm/ion-modal.entry.js +12 -200
  23. package/dist/esm/ion-popover.entry.js +10 -73
  24. package/dist/esm/ion-tab-bar_2.entry.js +23 -3
  25. package/dist/ionic/ionic.esm.js +1 -1
  26. package/dist/ionic/p-172a579f.entry.js +4 -0
  27. package/dist/ionic/p-732b2fd6.entry.js +4 -0
  28. package/dist/ionic/p-91840a80.entry.js +4 -0
  29. package/dist/ionic/p-f9061316.entry.js +4 -0
  30. package/dist/types/components/content/content.d.ts +0 -24
  31. package/dist/types/components/modal/gestures/sheet.d.ts +1 -1
  32. package/dist/types/components/modal/gestures/swipe-to-close.d.ts +1 -1
  33. package/dist/types/components/modal/modal.d.ts +0 -39
  34. package/dist/types/components/popover/utils.d.ts +0 -2
  35. package/dist/types/components/tab-bar/tab-bar.d.ts +1 -0
  36. package/hydrate/index.js +50 -353
  37. package/hydrate/index.mjs +50 -353
  38. package/package.json +1 -1
  39. package/dist/ionic/p-3830217a.entry.js +0 -4
  40. package/dist/ionic/p-7268efa5.entry.js +0 -4
  41. package/dist/ionic/p-a4827773.entry.js +0 -4
  42. package/dist/ionic/p-cecd141b.entry.js +0 -4
package/hydrate/index.js CHANGED
@@ -10499,7 +10499,7 @@ const isRTL$1 = (hostEl) => {
10499
10499
  return (document === null || document === void 0 ? void 0 : document.dir.toLowerCase()) === 'rtl';
10500
10500
  };
10501
10501
 
10502
- const contentCss = ":host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:\"\"}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}:host(.safe-area-top) #background-content,:host(.safe-area-top) .inner-scroll{top:var(--ion-safe-area-top, 0px)}:host(.safe-area-bottom) #background-content,:host(.safe-area-bottom) .inner-scroll{bottom:var(--ion-safe-area-bottom, 0px)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}";
10502
+ const contentCss = ":host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:\"\"}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}";
10503
10503
 
10504
10504
  /**
10505
10505
  * @slot - Content is placed in the scrollable area if provided without a slot.
@@ -10523,12 +10523,6 @@ class Content {
10523
10523
  this.isMainContent = true;
10524
10524
  this.resizeTimeout = null;
10525
10525
  this.inheritedAttributes = {};
10526
- /**
10527
- * Track whether this content has sibling header/footer elements.
10528
- * When absent, we need to apply safe-area padding directly.
10529
- */
10530
- this.hasHeader = false;
10531
- this.hasFooter = false;
10532
10526
  this.tabsElement = null;
10533
10527
  // Detail is used in a hot loop in the scroll event, by allocating it here
10534
10528
  // V8 will be able to inline any read/write to it since it's a monomorphic class.
@@ -10584,8 +10578,6 @@ class Content {
10584
10578
  }
10585
10579
  connectedCallback() {
10586
10580
  this.isMainContent = this.el.closest('ion-menu, ion-popover, ion-modal') === null;
10587
- // Detect sibling header/footer for safe-area handling
10588
- this.detectSiblingElements();
10589
10581
  /**
10590
10582
  * The fullscreen content offsets need to be
10591
10583
  * computed after the tab bar has loaded. Since
@@ -10621,70 +10613,8 @@ class Content {
10621
10613
  }
10622
10614
  }
10623
10615
  }
10624
- /**
10625
- * Detects sibling ion-header and ion-footer elements and sets up
10626
- * a mutation observer to handle dynamic changes (e.g., conditional rendering).
10627
- */
10628
- detectSiblingElements() {
10629
- this.updateSiblingDetection();
10630
- // Watch for dynamic header/footer changes (common in React conditional rendering)
10631
- const parent = this.el.parentElement;
10632
- if (parent && !this.parentMutationObserver) {
10633
- this.parentMutationObserver = new MutationObserver(() => {
10634
- this.updateSiblingDetection();
10635
- });
10636
- this.parentMutationObserver.observe(parent, { childList: true });
10637
- }
10638
- }
10639
- /**
10640
- * Updates hasHeader/hasFooter based on current DOM state.
10641
- * Checks both direct siblings and elements wrapped in custom components
10642
- * (e.g., <my-header><ion-header>...</ion-header></my-header>).
10643
- */
10644
- updateSiblingDetection() {
10645
- const parent = this.el.parentElement;
10646
- if (parent) {
10647
- // First check for direct ion-header/ion-footer siblings
10648
- this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
10649
- this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
10650
- // If not found, check if any sibling contains them (wrapped components)
10651
- if (!this.hasHeader) {
10652
- this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
10653
- }
10654
- if (!this.hasFooter) {
10655
- this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
10656
- }
10657
- }
10658
- // If no footer found, check if we're inside ion-tabs which has ion-tab-bar
10659
- if (!this.hasFooter) {
10660
- const tabs = this.el.closest('ion-tabs');
10661
- if (tabs) {
10662
- this.hasFooter = tabs.querySelector(':scope > ion-tab-bar') !== null;
10663
- }
10664
- }
10665
- }
10666
- /**
10667
- * Checks if any sibling element of ion-content contains the specified element.
10668
- * Only searches one level deep to avoid finding elements in nested pages.
10669
- */
10670
- siblingContainsElement(parent, tagName) {
10671
- for (const sibling of parent.children) {
10672
- // Skip ion-content itself
10673
- if (sibling === this.el)
10674
- continue;
10675
- // Check if this sibling contains the target element as an immediate child
10676
- if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
10677
- return true;
10678
- }
10679
- }
10680
- return false;
10681
- }
10682
10616
  disconnectedCallback() {
10683
- var _a;
10684
10617
  this.onScrollEnd();
10685
- // Clean up mutation observer to prevent memory leaks
10686
- (_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
10687
- this.parentMutationObserver = undefined;
10688
10618
  if (hasLazyBuild(this.el)) {
10689
10619
  /**
10690
10620
  * The event listener and tabs caches need to
@@ -10913,28 +10843,26 @@ class Content {
10913
10843
  }
10914
10844
  }
10915
10845
  render() {
10916
- const { fixedSlotPlacement, hasFooter, hasHeader, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
10846
+ const { fixedSlotPlacement, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
10917
10847
  const rtl = isRTL$1(el) ? 'rtl' : 'ltr';
10918
10848
  const mode = getIonMode$1(this);
10919
10849
  const forceOverscroll = this.shouldForceOverscroll();
10920
10850
  const transitionShadow = mode === 'ios';
10921
10851
  this.resize();
10922
- return (hAsync(Host, Object.assign({ key: 'c8e3a93e0b1ba6f7aa81a6a6065145ece9a6e2ef', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
10852
+ return (hAsync(Host, Object.assign({ key: 'cd8781f848d8dc926fe66f43d43c49564425a507', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
10923
10853
  [mode]: true,
10924
10854
  'content-sizing': hostContext('ion-popover', this.el),
10925
10855
  overscroll: forceOverscroll,
10926
10856
  [`content-${rtl}`]: true,
10927
- 'safe-area-top': isMainContent && !hasHeader,
10928
- 'safe-area-bottom': isMainContent && !hasFooter,
10929
10857
  }), style: {
10930
10858
  '--offset-top': `${this.cTop}px`,
10931
10859
  '--offset-bottom': `${this.cBottom}px`,
10932
- } }, inheritedAttributes), hAsync("div", { key: '4c0482cda885348eea9eb66d7f076af6b38c52e5', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? hAsync("slot", { name: "fixed" }) : null, hAsync("div", { key: '6fbb39bf7ab7120009c56aea9340de45c934eeed', class: {
10860
+ } }, inheritedAttributes), hAsync("div", { key: '95b112d7cae30f22ef778ceffb88edb4d941c170', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? hAsync("slot", { name: "fixed" }) : null, hAsync("div", { key: '2fdfcbc39fb66f11b6191911f2941c660f4c12e5', class: {
10933
10861
  'inner-scroll': true,
10934
10862
  'scroll-x': scrollX,
10935
10863
  'scroll-y': scrollY,
10936
10864
  overscroll: (scrollX || scrollY) && forceOverscroll,
10937
- }, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '6425bc84edbc0c5b1f2764a1d611df1b46628274' })), transitionShadow ? (hAsync("div", { class: "transition-effect" }, hAsync("div", { class: "transition-cover" }), hAsync("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? hAsync("slot", { name: "fixed" }) : null));
10865
+ }, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '6bc77e0054ec8e21635a7f2abfe0ca46e0962e03' })), transitionShadow ? (hAsync("div", { class: "transition-effect" }, hAsync("div", { class: "transition-cover" }), hAsync("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? hAsync("slot", { name: "fixed" }) : null));
10938
10866
  }
10939
10867
  get el() { return getElement(this); }
10940
10868
  static get style() { return contentCss; }
@@ -21605,7 +21533,7 @@ const calculateSpringStep = (t) => {
21605
21533
  const SwipeToCloseDefaults = {
21606
21534
  MIN_PRESENTING_SCALE: 0.915,
21607
21535
  };
21608
- const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
21536
+ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
21609
21537
  /**
21610
21538
  * The step value at which a card modal
21611
21539
  * is eligible for dismissing via gesture.
@@ -21762,8 +21690,6 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onG
21762
21690
  const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
21763
21691
  const clampedStep = clamp(0.0001, processedStep, maxStep);
21764
21692
  animation.progressStep(clampedStep);
21765
- // Notify modal of position change for safe-area updates
21766
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
21767
21693
  /**
21768
21694
  * When swiping down half way, the status bar style
21769
21695
  * should be reset to its default value.
@@ -22307,7 +22233,7 @@ const mdLeaveAnimation$2 = (baseEl, opts) => {
22307
22233
  return baseAnimation;
22308
22234
  };
22309
22235
 
22310
- const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
22236
+ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
22311
22237
  // Defaults for the sheet swipe animation
22312
22238
  const defaultBackdrop = [
22313
22239
  { offset: 0, opacity: 'var(--backdrop-opacity)' },
@@ -22638,8 +22564,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
22638
22564
  : step;
22639
22565
  offset = clamp(0.0001, processedStep, maxStep);
22640
22566
  animation.progressStep(offset);
22641
- // Notify modal of position change for safe-area updates
22642
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
22643
22567
  };
22644
22568
  const onEnd = (detail) => {
22645
22569
  /**
@@ -22834,9 +22758,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
22834
22758
  };
22835
22759
  };
22836
22760
 
22837
- const modalIosCss = ":host{--width:100%;--min-width:auto;--max-width:auto;--height:100%;--min-height:auto;--max-height:auto;--overflow:hidden;--border-radius:0;--border-width:0;--border-style:none;--border-color:transparent;--background:var(--ion-background-color, #fff);--box-shadow:none;--backdrop-opacity:0;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);contain:strict}.modal-wrapper,ion-backdrop{pointer-events:auto}:host(.overlay-hidden){display:none}.modal-wrapper,.modal-shadow{border-radius:var(--border-radius);width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:var(--overflow);z-index:10}.modal-shadow{position:absolute;background:transparent}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--width:600px;--height:500px}}@media only screen and (min-width: 768px) and (min-height: 768px){:host{--width:600px;--height:600px}}.modal-handle{left:0px;right:0px;top:5px;border-radius:8px;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;position:absolute;width:36px;height:5px;-webkit-transform:translateZ(0);transform:translateZ(0);border:0;background:var(--ion-color-step-350, var(--ion-background-color-step-350, #c0c0be));cursor:pointer;z-index:11}.modal-handle::before{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;position:absolute;width:36px;height:5px;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);content:\"\"}:host(.modal-sheet){--height:calc(100% - (var(--ion-safe-area-top) + 10px))}:host(.modal-sheet) .modal-wrapper,:host(.modal-sheet) .modal-shadow{position:absolute;bottom:0}:host(.modal-sheet.modal-no-expand-scroll) ion-footer{position:absolute;bottom:0;width:var(--width)}:host{--backdrop-opacity:var(--ion-backdrop-opacity, 0.4)}:host(.modal-card),:host(.modal-sheet){--border-radius:10px}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--border-radius:10px}}.modal-wrapper{-webkit-transform:translate3d(0, 100%, 0);transform:translate3d(0, 100%, 0)}@media screen and (max-width: 767px){@supports (width: max(0px, 1px)){:host(.modal-card){--height:calc(100% - max(30px, var(--ion-safe-area-top)) - 10px)}}@supports not (width: max(0px, 1px)){:host(.modal-card){--height:calc(100% - 40px)}}:host(.modal-card) .modal-wrapper{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius);border-end-end-radius:0;border-end-start-radius:0}:host(.modal-card){--backdrop-opacity:0;--width:100%;-ms-flex-align:end;align-items:flex-end}:host(.modal-card) .modal-shadow{display:none}:host(.modal-card) ion-backdrop{pointer-events:none}}@media screen and (min-width: 768px){:host(.modal-card){--width:calc(100% - 120px);--height:calc(100% - (120px + var(--ion-safe-area-top) + var(--ion-safe-area-bottom)));--max-width:720px;--max-height:1000px;--backdrop-opacity:0;--box-shadow:0px 0px 30px 10px rgba(0, 0, 0, 0.1);-webkit-transition:all 0.5s ease-in-out;transition:all 0.5s ease-in-out}:host(.modal-card) .modal-wrapper{-webkit-box-shadow:none;box-shadow:none}:host(.modal-card) .modal-shadow{-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow)}}:host(.modal-sheet) .modal-wrapper{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius);border-end-end-radius:0;border-end-start-radius:0}";
22761
+ const modalIosCss = ":host{--width:100%;--min-width:auto;--max-width:auto;--height:100%;--min-height:auto;--max-height:auto;--overflow:hidden;--border-radius:0;--border-width:0;--border-style:none;--border-color:transparent;--background:var(--ion-background-color, #fff);--box-shadow:none;--backdrop-opacity:0;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);contain:strict}.modal-wrapper,ion-backdrop{pointer-events:auto}:host(.overlay-hidden){display:none}.modal-wrapper,.modal-shadow{border-radius:var(--border-radius);width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:var(--overflow);z-index:10}.modal-shadow{position:absolute;background:transparent}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--width:600px;--height:500px;--ion-safe-area-top:0px;--ion-safe-area-bottom:0px;--ion-safe-area-right:0px;--ion-safe-area-left:0px}}@media only screen and (min-width: 768px) and (min-height: 768px){:host{--width:600px;--height:600px}}.modal-handle{left:0px;right:0px;top:5px;border-radius:8px;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;position:absolute;width:36px;height:5px;-webkit-transform:translateZ(0);transform:translateZ(0);border:0;background:var(--ion-color-step-350, var(--ion-background-color-step-350, #c0c0be));cursor:pointer;z-index:11}.modal-handle::before{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;position:absolute;width:36px;height:5px;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);content:\"\"}:host(.modal-sheet){--height:calc(100% - (var(--ion-safe-area-top) + 10px))}:host(.modal-sheet) .modal-wrapper,:host(.modal-sheet) .modal-shadow{position:absolute;bottom:0}:host(.modal-sheet.modal-no-expand-scroll) ion-footer{position:absolute;bottom:0;width:var(--width)}:host{--backdrop-opacity:var(--ion-backdrop-opacity, 0.4)}:host(.modal-card),:host(.modal-sheet){--border-radius:10px}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--border-radius:10px}}.modal-wrapper{-webkit-transform:translate3d(0, 100%, 0);transform:translate3d(0, 100%, 0)}@media screen and (max-width: 767px){@supports (width: max(0px, 1px)){:host(.modal-card){--height:calc(100% - max(30px, var(--ion-safe-area-top)) - 10px)}}@supports not (width: max(0px, 1px)){:host(.modal-card){--height:calc(100% - 40px)}}:host(.modal-card) .modal-wrapper{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius);border-end-end-radius:0;border-end-start-radius:0}:host(.modal-card){--backdrop-opacity:0;--width:100%;-ms-flex-align:end;align-items:flex-end}:host(.modal-card) .modal-shadow{display:none}:host(.modal-card) ion-backdrop{pointer-events:none}}@media screen and (min-width: 768px){:host(.modal-card){--width:calc(100% - 120px);--height:calc(100% - (120px + var(--ion-safe-area-top) + var(--ion-safe-area-bottom)));--max-width:720px;--max-height:1000px;--backdrop-opacity:0;--box-shadow:0px 0px 30px 10px rgba(0, 0, 0, 0.1);-webkit-transition:all 0.5s ease-in-out;transition:all 0.5s ease-in-out}:host(.modal-card) .modal-wrapper{-webkit-box-shadow:none;box-shadow:none}:host(.modal-card) .modal-shadow{-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow)}}:host(.modal-sheet) .modal-wrapper{border-start-start-radius:var(--border-radius);border-start-end-radius:var(--border-radius);border-end-end-radius:0;border-end-start-radius:0}";
22838
22762
 
22839
- const modalMdCss = ":host{--width:100%;--min-width:auto;--max-width:auto;--height:100%;--min-height:auto;--max-height:auto;--overflow:hidden;--border-radius:0;--border-width:0;--border-style:none;--border-color:transparent;--background:var(--ion-background-color, #fff);--box-shadow:none;--backdrop-opacity:0;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);contain:strict}.modal-wrapper,ion-backdrop{pointer-events:auto}:host(.overlay-hidden){display:none}.modal-wrapper,.modal-shadow{border-radius:var(--border-radius);width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:var(--overflow);z-index:10}.modal-shadow{position:absolute;background:transparent}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--width:600px;--height:500px}}@media only screen and (min-width: 768px) and (min-height: 768px){:host{--width:600px;--height:600px}}.modal-handle{left:0px;right:0px;top:5px;border-radius:8px;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;position:absolute;width:36px;height:5px;-webkit-transform:translateZ(0);transform:translateZ(0);border:0;background:var(--ion-color-step-350, var(--ion-background-color-step-350, #c0c0be));cursor:pointer;z-index:11}.modal-handle::before{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;position:absolute;width:36px;height:5px;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);content:\"\"}:host(.modal-sheet){--height:calc(100% - (var(--ion-safe-area-top) + 10px))}:host(.modal-sheet) .modal-wrapper,:host(.modal-sheet) .modal-shadow{position:absolute;bottom:0}:host(.modal-sheet.modal-no-expand-scroll) ion-footer{position:absolute;bottom:0;width:var(--width)}:host{--backdrop-opacity:var(--ion-backdrop-opacity, 0.32)}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--border-radius:2px;--box-shadow:0 28px 48px rgba(0, 0, 0, 0.4)}}.modal-wrapper{-webkit-transform:translate3d(0, 40px, 0);transform:translate3d(0, 40px, 0);opacity:0.01}";
22763
+ const modalMdCss = ":host{--width:100%;--min-width:auto;--max-width:auto;--height:100%;--min-height:auto;--max-height:auto;--overflow:hidden;--border-radius:0;--border-width:0;--border-style:none;--border-color:transparent;--background:var(--ion-background-color, #fff);--box-shadow:none;--backdrop-opacity:0;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);contain:strict}.modal-wrapper,ion-backdrop{pointer-events:auto}:host(.overlay-hidden){display:none}.modal-wrapper,.modal-shadow{border-radius:var(--border-radius);width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:var(--overflow);z-index:10}.modal-shadow{position:absolute;background:transparent}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--width:600px;--height:500px;--ion-safe-area-top:0px;--ion-safe-area-bottom:0px;--ion-safe-area-right:0px;--ion-safe-area-left:0px}}@media only screen and (min-width: 768px) and (min-height: 768px){:host{--width:600px;--height:600px}}.modal-handle{left:0px;right:0px;top:5px;border-radius:8px;-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;position:absolute;width:36px;height:5px;-webkit-transform:translateZ(0);transform:translateZ(0);border:0;background:var(--ion-color-step-350, var(--ion-background-color-step-350, #c0c0be));cursor:pointer;z-index:11}.modal-handle::before{-webkit-padding-start:4px;padding-inline-start:4px;-webkit-padding-end:4px;padding-inline-end:4px;padding-top:4px;padding-bottom:4px;position:absolute;width:36px;height:5px;-webkit-transform:translate(-50%, -50%);transform:translate(-50%, -50%);content:\"\"}:host(.modal-sheet){--height:calc(100% - (var(--ion-safe-area-top) + 10px))}:host(.modal-sheet) .modal-wrapper,:host(.modal-sheet) .modal-shadow{position:absolute;bottom:0}:host(.modal-sheet.modal-no-expand-scroll) ion-footer{position:absolute;bottom:0;width:var(--width)}:host{--backdrop-opacity:var(--ion-backdrop-opacity, 0.32)}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--border-radius:2px;--box-shadow:0 28px 48px rgba(0, 0, 0, 0.4)}}.modal-wrapper{-webkit-transform:translate3d(0, 40px, 0);transform:translate3d(0, 40px, 0);opacity:0.01}";
22840
22764
 
22841
22765
  // TODO(FW-2832): types
22842
22766
  /**
@@ -22869,10 +22793,6 @@ class Modal {
22869
22793
  this.inline = false;
22870
22794
  // Whether or not modal is being dismissed via gesture
22871
22795
  this.gestureAnimationDismissing = false;
22872
- // Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
22873
- this.skipSafeAreaCoordinateDetection = false;
22874
- // Track previous safe-area state to avoid redundant DOM writes
22875
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
22876
22796
  this.presented = false;
22877
22797
  /** @internal */
22878
22798
  this.hasController = false;
@@ -23063,10 +22983,7 @@ class Modal {
23063
22983
  }
23064
22984
  }
23065
22985
  onWindowResize() {
23066
- // Invalidate safe-area cache on resize (device rotation may change values)
23067
- this.cachedSafeAreas = undefined;
23068
- this.updateSafeAreaOverrides();
23069
- // Only handle view transition for iOS card modals when no custom animations are provided
22986
+ // Only handle resize for iOS card modals when no custom animations are provided
23070
22987
  if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
23071
22988
  return;
23072
22989
  }
@@ -23248,8 +23165,6 @@ class Modal {
23248
23165
  else if (!this.keepContentsMounted) {
23249
23166
  await waitForMount();
23250
23167
  }
23251
- // Predict safe-area needs based on modal configuration to avoid visual snap
23252
- this.setInitialSafeAreaOverrides(presentingElement);
23253
23168
  writeTask(() => this.el.classList.add('show-modal'));
23254
23169
  const hasCardModal = presentingElement !== undefined;
23255
23170
  /**
@@ -23311,8 +23226,6 @@ class Modal {
23311
23226
  else if (hasCardModal) {
23312
23227
  this.initSwipeToClose();
23313
23228
  }
23314
- // Now that animation is complete, update safe-area based on actual position
23315
- this.updateSafeAreaOverrides();
23316
23229
  // Initialize view transition listener for iOS card modals
23317
23230
  this.initViewTransitionListener();
23318
23231
  // Initialize parent removal observer
@@ -23364,7 +23277,7 @@ class Modal {
23364
23277
  await this.dismiss(undefined, GESTURE);
23365
23278
  this.gestureAnimationDismissing = false;
23366
23279
  });
23367
- }, () => this.updateSafeAreaOverrides());
23280
+ });
23368
23281
  this.gesture.enable(true);
23369
23282
  }
23370
23283
  initSheetGesture() {
@@ -23385,8 +23298,7 @@ class Modal {
23385
23298
  this.currentBreakpoint = breakpoint;
23386
23299
  this.ionBreakpointDidChange.emit({ breakpoint });
23387
23300
  }
23388
- this.updateSafeAreaOverrides();
23389
- }, () => this.updateSafeAreaOverrides());
23301
+ });
23390
23302
  this.gesture = gesture;
23391
23303
  this.moveSheetToBreakpoint = moveSheetToBreakpoint;
23392
23304
  this.gesture.enable(true);
@@ -23464,161 +23376,6 @@ class Modal {
23464
23376
  // Clear the cached reference
23465
23377
  this.cachedPageParent = undefined;
23466
23378
  }
23467
- /**
23468
- * Sets initial safe-area overrides based on modal configuration before
23469
- * the modal becomes visible. This predicts whether the modal will touch
23470
- * screen edges to avoid a visual snap after animation completes.
23471
- */
23472
- setInitialSafeAreaOverrides(presentingElement) {
23473
- const style = this.el.style;
23474
- const mode = getIonMode$1(this);
23475
- const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
23476
- // Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
23477
- const isCardModal = presentingElement !== undefined && mode === 'ios';
23478
- const isTablet = window.innerWidth >= 768;
23479
- // Sheet modals always touch bottom edge, never top/left/right
23480
- if (isSheetModal) {
23481
- style.setProperty('--ion-safe-area-top', '0px');
23482
- style.setProperty('--ion-safe-area-left', '0px');
23483
- style.setProperty('--ion-safe-area-right', '0px');
23484
- return;
23485
- }
23486
- // Card modals have rounded top corners
23487
- if (isCardModal) {
23488
- style.setProperty('--ion-safe-area-top', '0px');
23489
- if (isTablet) {
23490
- // On tablets, card modals are inset from all edges
23491
- this.zeroAllSafeAreas();
23492
- }
23493
- else {
23494
- // On phones, card modals still extend to the bottom edge
23495
- style.setProperty('--ion-safe-area-left', '0px');
23496
- style.setProperty('--ion-safe-area-right', '0px');
23497
- this.applyFullscreenSafeArea();
23498
- }
23499
- return;
23500
- }
23501
- // Phone-sized fullscreen modals inherit safe areas and use wrapper padding
23502
- if (!isTablet) {
23503
- this.applyFullscreenSafeArea();
23504
- return;
23505
- }
23506
- // Check if tablet modal is fullscreen via CSS custom properties
23507
- const computedStyle = getComputedStyle(this.el);
23508
- const width = computedStyle.getPropertyValue('--width').trim();
23509
- const height = computedStyle.getPropertyValue('--height').trim();
23510
- const isFullscreen = width === '100%' && height === '100%';
23511
- if (isFullscreen) {
23512
- this.applyFullscreenSafeArea();
23513
- }
23514
- else {
23515
- // Centered dialog doesn't touch edges
23516
- this.zeroAllSafeAreas();
23517
- }
23518
- }
23519
- /**
23520
- * Applies safe-area handling for fullscreen modals.
23521
- * Adds wrapper padding when no footer is present to prevent
23522
- * content from overlapping system navigation areas.
23523
- */
23524
- applyFullscreenSafeArea() {
23525
- this.skipSafeAreaCoordinateDetection = true;
23526
- this.updateFooterPadding();
23527
- // Watch for dynamic footer additions/removals (e.g., async data loading)
23528
- if (!this.footerObserver) {
23529
- this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
23530
- this.footerObserver.observe(this.el, { childList: true, subtree: true });
23531
- }
23532
- }
23533
- /**
23534
- * Updates wrapper padding based on footer presence.
23535
- * Called initially and when footer is dynamically added/removed.
23536
- */
23537
- updateFooterPadding() {
23538
- if (!this.wrapperEl)
23539
- return;
23540
- const hasFooter = this.el.querySelector('ion-footer') !== null;
23541
- if (hasFooter) {
23542
- this.wrapperEl.style.removeProperty('padding-bottom');
23543
- this.wrapperEl.style.removeProperty('box-sizing');
23544
- }
23545
- else {
23546
- this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
23547
- this.wrapperEl.style.setProperty('box-sizing', 'border-box');
23548
- }
23549
- }
23550
- /**
23551
- * Sets all safe-area CSS variables to 0px for modals that
23552
- * don't touch screen edges.
23553
- */
23554
- zeroAllSafeAreas() {
23555
- const style = this.el.style;
23556
- style.setProperty('--ion-safe-area-top', '0px');
23557
- style.setProperty('--ion-safe-area-bottom', '0px');
23558
- style.setProperty('--ion-safe-area-left', '0px');
23559
- style.setProperty('--ion-safe-area-right', '0px');
23560
- }
23561
- /**
23562
- * Gets the root safe-area values from the document element.
23563
- * Uses cached values during gestures to avoid getComputedStyle calls.
23564
- */
23565
- getSafeAreaValues() {
23566
- if (!this.cachedSafeAreas) {
23567
- const rootStyle = getComputedStyle(document.documentElement);
23568
- this.cachedSafeAreas = {
23569
- top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
23570
- bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
23571
- left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
23572
- right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
23573
- };
23574
- }
23575
- return this.cachedSafeAreas;
23576
- }
23577
- /**
23578
- * Updates safe-area CSS variable overrides based on whether the modal
23579
- * extends into each safe-area region. Called after animation
23580
- * and during gestures to handle dynamic position changes.
23581
- *
23582
- * Optimized to avoid redundant DOM writes by tracking previous state.
23583
- */
23584
- updateSafeAreaOverrides() {
23585
- if (this.skipSafeAreaCoordinateDetection) {
23586
- return;
23587
- }
23588
- const wrapper = this.wrapperEl;
23589
- if (!wrapper) {
23590
- return;
23591
- }
23592
- const rect = wrapper.getBoundingClientRect();
23593
- const safeAreas = this.getSafeAreaValues();
23594
- const extendsIntoTop = rect.top < safeAreas.top;
23595
- const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
23596
- const extendsIntoLeft = rect.left < safeAreas.left;
23597
- const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
23598
- // Only update DOM when state actually changes
23599
- const prev = this.prevSafeAreaState;
23600
- const style = this.el.style;
23601
- if (extendsIntoTop !== prev.top) {
23602
- extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
23603
- prev.top = extendsIntoTop;
23604
- }
23605
- if (extendsIntoBottom !== prev.bottom) {
23606
- extendsIntoBottom
23607
- ? style.removeProperty('--ion-safe-area-bottom')
23608
- : style.setProperty('--ion-safe-area-bottom', '0px');
23609
- prev.bottom = extendsIntoBottom;
23610
- }
23611
- if (extendsIntoLeft !== prev.left) {
23612
- extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
23613
- prev.left = extendsIntoLeft;
23614
- }
23615
- if (extendsIntoRight !== prev.right) {
23616
- extendsIntoRight
23617
- ? style.removeProperty('--ion-safe-area-right')
23618
- : style.setProperty('--ion-safe-area-right', '0px');
23619
- prev.right = extendsIntoRight;
23620
- }
23621
- }
23622
23379
  sheetOnDismiss() {
23623
23380
  /**
23624
23381
  * While the gesture animation is finishing
@@ -23649,7 +23406,7 @@ class Modal {
23649
23406
  * For example, `cancel` or `backdrop`.
23650
23407
  */
23651
23408
  async dismiss(data, role) {
23652
- var _a, _b;
23409
+ var _a;
23653
23410
  if (this.gestureAnimationDismissing && role !== GESTURE) {
23654
23411
  return false;
23655
23412
  }
@@ -23711,23 +23468,6 @@ class Modal {
23711
23468
  }
23712
23469
  this.currentBreakpoint = undefined;
23713
23470
  this.animation = undefined;
23714
- // Reset safe-area state for potential re-presentation
23715
- this.skipSafeAreaCoordinateDetection = false;
23716
- this.cachedSafeAreas = undefined;
23717
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
23718
- (_b = this.footerObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
23719
- this.footerObserver = undefined;
23720
- // Clear styles that may have been set for safe-area handling
23721
- if (this.wrapperEl) {
23722
- this.wrapperEl.style.removeProperty('padding-bottom');
23723
- this.wrapperEl.style.removeProperty('box-sizing');
23724
- }
23725
- // Clear safe-area CSS variable overrides
23726
- const style = this.el.style;
23727
- style.removeProperty('--ion-safe-area-top');
23728
- style.removeProperty('--ion-safe-area-bottom');
23729
- style.removeProperty('--ion-safe-area-left');
23730
- style.removeProperty('--ion-safe-area-right');
23731
23471
  unlock();
23732
23472
  return dismissed;
23733
23473
  }
@@ -23977,20 +23717,20 @@ class Modal {
23977
23717
  const isCardModal = presentingElement !== undefined && mode === 'ios';
23978
23718
  const isHandleCycle = handleBehavior === 'cycle';
23979
23719
  const isSheetModalWithHandle = isSheetModal && showHandle;
23980
- return (hAsync(Host, Object.assign({ key: '11cd16cc481093a38a327abdd94467be3f71718d', "no-router": true,
23720
+ return (hAsync(Host, Object.assign({ key: '87328006ea6c75ebc518ace300438492a567223e', "no-router": true,
23981
23721
  // Allow the modal to be navigable when the handle is focusable
23982
23722
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
23983
23723
  zIndex: `${20000 + this.overlayIndex}`,
23984
- }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: '125658fbb071960da3905854668078e15bce56da', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: '4a63815ef165e5806fef85ef48bf509813ff55c9', class: "modal-shadow" }), hAsync("div", Object.assign({ key: 'cfc4b20354cbf2c0f873f6aee91c9b8f553de61d',
23724
+ }, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: 'ee94ff8e09b691dd4ad4e4db1720f06bc3c5a469', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: 'bffd69b4635c22d9f249725bd952c1e93d5615c7', class: "modal-shadow" }), hAsync("div", Object.assign({ key: '1d394d3c68916e464ff1fbf5242419f4a3d3cca1',
23985
23725
  /*
23986
23726
  role and aria-modal must be used on the
23987
23727
  same element. They must also be set inside the
23988
23728
  shadow DOM otherwise ion-button will not be highlighted
23989
23729
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
23990
23730
  */
23991
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '33a4ee89bb8b6512883cb8756641c1e27fdb0ebc', class: "modal-handle",
23731
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '2dcf58792018e557e0c323baad2d672bc99c0bb1', class: "modal-handle",
23992
23732
  // Prevents the handle from receiving keyboard focus when it does not cycle
23993
- tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: '986fafb71234591c96e927f92b7330bb5c76fc2e', onSlotchange: this.onSlotChange }))));
23733
+ tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: '44164b1e8710c3895400ad9f44ecd99873874ad5', onSlotchange: this.onSlotChange }))));
23994
23734
  }
23995
23735
  get el() { return getElement(this); }
23996
23736
  static get watchers() { return {
@@ -27568,8 +27308,6 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27568
27308
  let bottom;
27569
27309
  let originX = contentOriginX;
27570
27310
  let originY = contentOriginY;
27571
- let checkSafeAreaTop = false;
27572
- let checkSafeAreaBottom = false;
27573
27311
  let checkSafeAreaLeft = false;
27574
27312
  let checkSafeAreaRight = false;
27575
27313
  const triggerTop = triggerCoordinates
@@ -27614,18 +27352,10 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27614
27352
  * We chose 12 here so that the popover position looks a bit nicer as
27615
27353
  * it is not right up against the edge of the screen.
27616
27354
  */
27617
- top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
27355
+ top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
27618
27356
  arrowTop = top + contentHeight;
27619
27357
  originY = 'bottom';
27620
27358
  addPopoverBottomClass = true;
27621
- /**
27622
- * If the popover is positioned near the top edge, account for safe area.
27623
- * This ensures the popover doesn't overlap with status bars or notches.
27624
- */
27625
- if (top <= bodyPadding + safeAreaMargin) {
27626
- checkSafeAreaTop = true;
27627
- top = bodyPadding;
27628
- }
27629
27359
  /**
27630
27360
  * If not enough room for popover to appear
27631
27361
  * above trigger, then cut it off.
@@ -27633,35 +27363,14 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27633
27363
  }
27634
27364
  else {
27635
27365
  bottom = bodyPadding;
27636
- /**
27637
- * When the popover is pinned to the bottom, account for safe area.
27638
- * This ensures the popover doesn't overlap with home indicators
27639
- * or navigation bars (e.g., Android API 36+ edge-to-edge).
27640
- */
27641
- checkSafeAreaBottom = true;
27642
27366
  }
27643
27367
  }
27644
- /**
27645
- * Final check: If the popover extends into any safe-area region,
27646
- * ensure the corresponding flag is set regardless of side.
27647
- * This handles cases where a side-positioned popover (left/right)
27648
- * still needs bottom safe-area padding because it extends into that region.
27649
- */
27650
- const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
27651
- if (popoverBottom + safeAreaMargin > bodyHeight) {
27652
- checkSafeAreaBottom = true;
27653
- }
27654
- if (top < safeAreaMargin) {
27655
- checkSafeAreaTop = true;
27656
- }
27657
27368
  return {
27658
27369
  top,
27659
27370
  left,
27660
27371
  bottom,
27661
27372
  originX,
27662
27373
  originY,
27663
- checkSafeAreaTop,
27664
- checkSafeAreaBottom,
27665
27374
  checkSafeAreaLeft,
27666
27375
  checkSafeAreaRight,
27667
27376
  arrowTop,
@@ -27722,7 +27431,7 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
27722
27431
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
27723
27432
  const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
27724
27433
  const margin = size === 'cover' ? 0 : 25;
27725
- const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
27434
+ const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
27726
27435
  const baseAnimation = createAnimation();
27727
27436
  const backdropAnimation = createAnimation();
27728
27437
  const contentAnimation = createAnimation();
@@ -27752,35 +27461,19 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
27752
27461
  if (addPopoverBottomClass) {
27753
27462
  baseEl.classList.add('popover-bottom');
27754
27463
  }
27755
- /**
27756
- * Safe area CSS variable adjustments.
27757
- * When the popover is positioned near an edge, we add the corresponding
27758
- * safe-area inset to ensure the popover doesn't overlap with system UI
27759
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
27760
- */
27761
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
27762
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
27464
+ if (bottom !== undefined) {
27465
+ contentEl.style.setProperty('bottom', `${bottom}px`);
27466
+ }
27763
27467
  const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
27764
27468
  const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
27765
- let topValue = `${top}px`;
27766
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
27767
27469
  let leftValue = `${left}px`;
27768
- if (checkSafeAreaTop) {
27769
- topValue = `${top}px${safeAreaTop}`;
27770
- }
27771
- if (checkSafeAreaBottom && bottomValue !== undefined) {
27772
- bottomValue = `${bottom}px${safeAreaBottom}`;
27773
- }
27774
27470
  if (checkSafeAreaLeft) {
27775
27471
  leftValue = `${left}px${safeAreaLeft}`;
27776
27472
  }
27777
27473
  if (checkSafeAreaRight) {
27778
27474
  leftValue = `${left}px${safeAreaRight}`;
27779
27475
  }
27780
- if (bottomValue !== undefined) {
27781
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
27782
- }
27783
- contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
27476
+ contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
27784
27477
  contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
27785
27478
  contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
27786
27479
  if (arrowEl !== null) {
@@ -27856,23 +27549,7 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
27856
27549
  };
27857
27550
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
27858
27551
  const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
27859
- const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
27860
- /**
27861
- * Safe area CSS variable adjustments.
27862
- * When the popover is positioned near an edge, we add the corresponding
27863
- * safe-area inset to ensure the popover doesn't overlap with system UI
27864
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
27865
- */
27866
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
27867
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
27868
- let topValue = `${top}px`;
27869
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
27870
- if (checkSafeAreaTop) {
27871
- topValue = `${top}px${safeAreaTop}`;
27872
- }
27873
- if (checkSafeAreaBottom && bottomValue !== undefined) {
27874
- bottomValue = `${bottom}px${safeAreaBottom}`;
27875
- }
27552
+ const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
27876
27553
  const baseAnimation = createAnimation();
27877
27554
  const backdropAnimation = createAnimation();
27878
27555
  const wrapperAnimation = createAnimation();
@@ -27889,13 +27566,13 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
27889
27566
  contentAnimation
27890
27567
  .addElement(contentEl)
27891
27568
  .beforeStyles({
27892
- top: `calc(${topValue} + var(--offset-y, 0px))`,
27569
+ top: `calc(${top}px + var(--offset-y, 0px))`,
27893
27570
  left: `calc(${left}px + var(--offset-x, 0px))`,
27894
27571
  'transform-origin': `${originY} ${originX}`,
27895
27572
  })
27896
27573
  .beforeAddWrite(() => {
27897
- if (bottomValue !== undefined) {
27898
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
27574
+ if (bottom !== undefined) {
27575
+ contentEl.style.setProperty('bottom', `${bottom}px`);
27899
27576
  }
27900
27577
  })
27901
27578
  .fromTo('transform', 'scale(0.8)', 'scale(1)');
@@ -35313,6 +34990,7 @@ class TabBar {
35313
34990
  this.ionTabBarChanged = createEvent(this, "ionTabBarChanged", 7);
35314
34991
  this.ionTabBarLoaded = createEvent(this, "ionTabBarLoaded", 7);
35315
34992
  this.keyboardCtrl = null;
34993
+ this.keyboardCtrlPromise = null;
35316
34994
  this.didLoad = false;
35317
34995
  this.keyboardVisible = false;
35318
34996
  /**
@@ -35348,7 +35026,7 @@ class TabBar {
35348
35026
  }
35349
35027
  }
35350
35028
  async connectedCallback() {
35351
- this.keyboardCtrl = await createKeyboardController(async (keyboardOpen, waitForResize) => {
35029
+ const promise = createKeyboardController(async (keyboardOpen, waitForResize) => {
35352
35030
  /**
35353
35031
  * If the keyboard is hiding, then we need to wait
35354
35032
  * for the webview to resize. Otherwise, the tab bar
@@ -35359,21 +35037,40 @@ class TabBar {
35359
35037
  }
35360
35038
  this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
35361
35039
  });
35040
+ this.keyboardCtrlPromise = promise;
35041
+ const keyboardCtrl = await promise;
35042
+ /**
35043
+ * Only assign if this is still the current promise.
35044
+ * Otherwise, a new connectedCallback has started or
35045
+ * disconnectedCallback was called, so destroy this instance.
35046
+ */
35047
+ if (this.keyboardCtrlPromise === promise) {
35048
+ this.keyboardCtrl = keyboardCtrl;
35049
+ this.keyboardCtrlPromise = null;
35050
+ }
35051
+ else {
35052
+ keyboardCtrl.destroy();
35053
+ }
35362
35054
  }
35363
35055
  disconnectedCallback() {
35056
+ if (this.keyboardCtrlPromise) {
35057
+ this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
35058
+ this.keyboardCtrlPromise = null;
35059
+ }
35364
35060
  if (this.keyboardCtrl) {
35365
35061
  this.keyboardCtrl.destroy();
35062
+ this.keyboardCtrl = null;
35366
35063
  }
35367
35064
  }
35368
35065
  render() {
35369
35066
  const { color, translucent, keyboardVisible } = this;
35370
35067
  const mode = getIonMode$1(this);
35371
35068
  const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
35372
- return (hAsync(Host, { key: '388ec37ce308035bab78d6c9a016bb616e9517a9', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses$1(color, {
35069
+ return (hAsync(Host, { key: '9daf4e2acaff6e3ce3878cf9dd5109fb1afbbebe', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses$1(color, {
35373
35070
  [mode]: true,
35374
35071
  'tab-bar-translucent': translucent,
35375
35072
  'tab-bar-hidden': shouldHide,
35376
- }) }, hAsync("slot", { key: 'ce10ade2b86725e24f3254516483eeedd8ecb16a' })));
35073
+ }) }, hAsync("slot", { key: '1d15aa2da8501e8e7eff11ad4a491478be845c43' })));
35377
35074
  }
35378
35075
  get el() { return getElement(this); }
35379
35076
  static get watchers() { return {