@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.mjs CHANGED
@@ -10497,7 +10497,7 @@ const isRTL$1 = (hostEl) => {
10497
10497
  return (document === null || document === void 0 ? void 0 : document.dir.toLowerCase()) === 'rtl';
10498
10498
  };
10499
10499
 
10500
- 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)}";
10500
+ 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)}";
10501
10501
 
10502
10502
  /**
10503
10503
  * @slot - Content is placed in the scrollable area if provided without a slot.
@@ -10521,12 +10521,6 @@ class Content {
10521
10521
  this.isMainContent = true;
10522
10522
  this.resizeTimeout = null;
10523
10523
  this.inheritedAttributes = {};
10524
- /**
10525
- * Track whether this content has sibling header/footer elements.
10526
- * When absent, we need to apply safe-area padding directly.
10527
- */
10528
- this.hasHeader = false;
10529
- this.hasFooter = false;
10530
10524
  this.tabsElement = null;
10531
10525
  // Detail is used in a hot loop in the scroll event, by allocating it here
10532
10526
  // V8 will be able to inline any read/write to it since it's a monomorphic class.
@@ -10582,8 +10576,6 @@ class Content {
10582
10576
  }
10583
10577
  connectedCallback() {
10584
10578
  this.isMainContent = this.el.closest('ion-menu, ion-popover, ion-modal') === null;
10585
- // Detect sibling header/footer for safe-area handling
10586
- this.detectSiblingElements();
10587
10579
  /**
10588
10580
  * The fullscreen content offsets need to be
10589
10581
  * computed after the tab bar has loaded. Since
@@ -10619,70 +10611,8 @@ class Content {
10619
10611
  }
10620
10612
  }
10621
10613
  }
10622
- /**
10623
- * Detects sibling ion-header and ion-footer elements and sets up
10624
- * a mutation observer to handle dynamic changes (e.g., conditional rendering).
10625
- */
10626
- detectSiblingElements() {
10627
- this.updateSiblingDetection();
10628
- // Watch for dynamic header/footer changes (common in React conditional rendering)
10629
- const parent = this.el.parentElement;
10630
- if (parent && !this.parentMutationObserver) {
10631
- this.parentMutationObserver = new MutationObserver(() => {
10632
- this.updateSiblingDetection();
10633
- });
10634
- this.parentMutationObserver.observe(parent, { childList: true });
10635
- }
10636
- }
10637
- /**
10638
- * Updates hasHeader/hasFooter based on current DOM state.
10639
- * Checks both direct siblings and elements wrapped in custom components
10640
- * (e.g., <my-header><ion-header>...</ion-header></my-header>).
10641
- */
10642
- updateSiblingDetection() {
10643
- const parent = this.el.parentElement;
10644
- if (parent) {
10645
- // First check for direct ion-header/ion-footer siblings
10646
- this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
10647
- this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
10648
- // If not found, check if any sibling contains them (wrapped components)
10649
- if (!this.hasHeader) {
10650
- this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
10651
- }
10652
- if (!this.hasFooter) {
10653
- this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
10654
- }
10655
- }
10656
- // If no footer found, check if we're inside ion-tabs which has ion-tab-bar
10657
- if (!this.hasFooter) {
10658
- const tabs = this.el.closest('ion-tabs');
10659
- if (tabs) {
10660
- this.hasFooter = tabs.querySelector(':scope > ion-tab-bar') !== null;
10661
- }
10662
- }
10663
- }
10664
- /**
10665
- * Checks if any sibling element of ion-content contains the specified element.
10666
- * Only searches one level deep to avoid finding elements in nested pages.
10667
- */
10668
- siblingContainsElement(parent, tagName) {
10669
- for (const sibling of parent.children) {
10670
- // Skip ion-content itself
10671
- if (sibling === this.el)
10672
- continue;
10673
- // Check if this sibling contains the target element as an immediate child
10674
- if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
10675
- return true;
10676
- }
10677
- }
10678
- return false;
10679
- }
10680
10614
  disconnectedCallback() {
10681
- var _a;
10682
10615
  this.onScrollEnd();
10683
- // Clean up mutation observer to prevent memory leaks
10684
- (_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
10685
- this.parentMutationObserver = undefined;
10686
10616
  if (hasLazyBuild(this.el)) {
10687
10617
  /**
10688
10618
  * The event listener and tabs caches need to
@@ -10911,28 +10841,26 @@ class Content {
10911
10841
  }
10912
10842
  }
10913
10843
  render() {
10914
- const { fixedSlotPlacement, hasFooter, hasHeader, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
10844
+ const { fixedSlotPlacement, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
10915
10845
  const rtl = isRTL$1(el) ? 'rtl' : 'ltr';
10916
10846
  const mode = getIonMode$1(this);
10917
10847
  const forceOverscroll = this.shouldForceOverscroll();
10918
10848
  const transitionShadow = mode === 'ios';
10919
10849
  this.resize();
10920
- return (hAsync(Host, Object.assign({ key: 'c8e3a93e0b1ba6f7aa81a6a6065145ece9a6e2ef', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
10850
+ return (hAsync(Host, Object.assign({ key: 'cd8781f848d8dc926fe66f43d43c49564425a507', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
10921
10851
  [mode]: true,
10922
10852
  'content-sizing': hostContext('ion-popover', this.el),
10923
10853
  overscroll: forceOverscroll,
10924
10854
  [`content-${rtl}`]: true,
10925
- 'safe-area-top': isMainContent && !hasHeader,
10926
- 'safe-area-bottom': isMainContent && !hasFooter,
10927
10855
  }), style: {
10928
10856
  '--offset-top': `${this.cTop}px`,
10929
10857
  '--offset-bottom': `${this.cBottom}px`,
10930
- } }, 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: {
10858
+ } }, 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: {
10931
10859
  'inner-scroll': true,
10932
10860
  'scroll-x': scrollX,
10933
10861
  'scroll-y': scrollY,
10934
10862
  overscroll: (scrollX || scrollY) && forceOverscroll,
10935
- }, 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));
10863
+ }, 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));
10936
10864
  }
10937
10865
  get el() { return getElement(this); }
10938
10866
  static get style() { return contentCss; }
@@ -21603,7 +21531,7 @@ const calculateSpringStep = (t) => {
21603
21531
  const SwipeToCloseDefaults = {
21604
21532
  MIN_PRESENTING_SCALE: 0.915,
21605
21533
  };
21606
- const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
21534
+ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
21607
21535
  /**
21608
21536
  * The step value at which a card modal
21609
21537
  * is eligible for dismissing via gesture.
@@ -21760,8 +21688,6 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onG
21760
21688
  const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
21761
21689
  const clampedStep = clamp(0.0001, processedStep, maxStep);
21762
21690
  animation.progressStep(clampedStep);
21763
- // Notify modal of position change for safe-area updates
21764
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
21765
21691
  /**
21766
21692
  * When swiping down half way, the status bar style
21767
21693
  * should be reset to its default value.
@@ -22305,7 +22231,7 @@ const mdLeaveAnimation$2 = (baseEl, opts) => {
22305
22231
  return baseAnimation;
22306
22232
  };
22307
22233
 
22308
- const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
22234
+ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
22309
22235
  // Defaults for the sheet swipe animation
22310
22236
  const defaultBackdrop = [
22311
22237
  { offset: 0, opacity: 'var(--backdrop-opacity)' },
@@ -22636,8 +22562,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
22636
22562
  : step;
22637
22563
  offset = clamp(0.0001, processedStep, maxStep);
22638
22564
  animation.progressStep(offset);
22639
- // Notify modal of position change for safe-area updates
22640
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
22641
22565
  };
22642
22566
  const onEnd = (detail) => {
22643
22567
  /**
@@ -22832,9 +22756,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
22832
22756
  };
22833
22757
  };
22834
22758
 
22835
- 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}";
22759
+ 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}";
22836
22760
 
22837
- 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}";
22761
+ 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}";
22838
22762
 
22839
22763
  // TODO(FW-2832): types
22840
22764
  /**
@@ -22867,10 +22791,6 @@ class Modal {
22867
22791
  this.inline = false;
22868
22792
  // Whether or not modal is being dismissed via gesture
22869
22793
  this.gestureAnimationDismissing = false;
22870
- // Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
22871
- this.skipSafeAreaCoordinateDetection = false;
22872
- // Track previous safe-area state to avoid redundant DOM writes
22873
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
22874
22794
  this.presented = false;
22875
22795
  /** @internal */
22876
22796
  this.hasController = false;
@@ -23061,10 +22981,7 @@ class Modal {
23061
22981
  }
23062
22982
  }
23063
22983
  onWindowResize() {
23064
- // Invalidate safe-area cache on resize (device rotation may change values)
23065
- this.cachedSafeAreas = undefined;
23066
- this.updateSafeAreaOverrides();
23067
- // Only handle view transition for iOS card modals when no custom animations are provided
22984
+ // Only handle resize for iOS card modals when no custom animations are provided
23068
22985
  if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
23069
22986
  return;
23070
22987
  }
@@ -23246,8 +23163,6 @@ class Modal {
23246
23163
  else if (!this.keepContentsMounted) {
23247
23164
  await waitForMount();
23248
23165
  }
23249
- // Predict safe-area needs based on modal configuration to avoid visual snap
23250
- this.setInitialSafeAreaOverrides(presentingElement);
23251
23166
  writeTask(() => this.el.classList.add('show-modal'));
23252
23167
  const hasCardModal = presentingElement !== undefined;
23253
23168
  /**
@@ -23309,8 +23224,6 @@ class Modal {
23309
23224
  else if (hasCardModal) {
23310
23225
  this.initSwipeToClose();
23311
23226
  }
23312
- // Now that animation is complete, update safe-area based on actual position
23313
- this.updateSafeAreaOverrides();
23314
23227
  // Initialize view transition listener for iOS card modals
23315
23228
  this.initViewTransitionListener();
23316
23229
  // Initialize parent removal observer
@@ -23362,7 +23275,7 @@ class Modal {
23362
23275
  await this.dismiss(undefined, GESTURE);
23363
23276
  this.gestureAnimationDismissing = false;
23364
23277
  });
23365
- }, () => this.updateSafeAreaOverrides());
23278
+ });
23366
23279
  this.gesture.enable(true);
23367
23280
  }
23368
23281
  initSheetGesture() {
@@ -23383,8 +23296,7 @@ class Modal {
23383
23296
  this.currentBreakpoint = breakpoint;
23384
23297
  this.ionBreakpointDidChange.emit({ breakpoint });
23385
23298
  }
23386
- this.updateSafeAreaOverrides();
23387
- }, () => this.updateSafeAreaOverrides());
23299
+ });
23388
23300
  this.gesture = gesture;
23389
23301
  this.moveSheetToBreakpoint = moveSheetToBreakpoint;
23390
23302
  this.gesture.enable(true);
@@ -23462,161 +23374,6 @@ class Modal {
23462
23374
  // Clear the cached reference
23463
23375
  this.cachedPageParent = undefined;
23464
23376
  }
23465
- /**
23466
- * Sets initial safe-area overrides based on modal configuration before
23467
- * the modal becomes visible. This predicts whether the modal will touch
23468
- * screen edges to avoid a visual snap after animation completes.
23469
- */
23470
- setInitialSafeAreaOverrides(presentingElement) {
23471
- const style = this.el.style;
23472
- const mode = getIonMode$1(this);
23473
- const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
23474
- // Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
23475
- const isCardModal = presentingElement !== undefined && mode === 'ios';
23476
- const isTablet = window.innerWidth >= 768;
23477
- // Sheet modals always touch bottom edge, never top/left/right
23478
- if (isSheetModal) {
23479
- style.setProperty('--ion-safe-area-top', '0px');
23480
- style.setProperty('--ion-safe-area-left', '0px');
23481
- style.setProperty('--ion-safe-area-right', '0px');
23482
- return;
23483
- }
23484
- // Card modals have rounded top corners
23485
- if (isCardModal) {
23486
- style.setProperty('--ion-safe-area-top', '0px');
23487
- if (isTablet) {
23488
- // On tablets, card modals are inset from all edges
23489
- this.zeroAllSafeAreas();
23490
- }
23491
- else {
23492
- // On phones, card modals still extend to the bottom edge
23493
- style.setProperty('--ion-safe-area-left', '0px');
23494
- style.setProperty('--ion-safe-area-right', '0px');
23495
- this.applyFullscreenSafeArea();
23496
- }
23497
- return;
23498
- }
23499
- // Phone-sized fullscreen modals inherit safe areas and use wrapper padding
23500
- if (!isTablet) {
23501
- this.applyFullscreenSafeArea();
23502
- return;
23503
- }
23504
- // Check if tablet modal is fullscreen via CSS custom properties
23505
- const computedStyle = getComputedStyle(this.el);
23506
- const width = computedStyle.getPropertyValue('--width').trim();
23507
- const height = computedStyle.getPropertyValue('--height').trim();
23508
- const isFullscreen = width === '100%' && height === '100%';
23509
- if (isFullscreen) {
23510
- this.applyFullscreenSafeArea();
23511
- }
23512
- else {
23513
- // Centered dialog doesn't touch edges
23514
- this.zeroAllSafeAreas();
23515
- }
23516
- }
23517
- /**
23518
- * Applies safe-area handling for fullscreen modals.
23519
- * Adds wrapper padding when no footer is present to prevent
23520
- * content from overlapping system navigation areas.
23521
- */
23522
- applyFullscreenSafeArea() {
23523
- this.skipSafeAreaCoordinateDetection = true;
23524
- this.updateFooterPadding();
23525
- // Watch for dynamic footer additions/removals (e.g., async data loading)
23526
- if (!this.footerObserver) {
23527
- this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
23528
- this.footerObserver.observe(this.el, { childList: true, subtree: true });
23529
- }
23530
- }
23531
- /**
23532
- * Updates wrapper padding based on footer presence.
23533
- * Called initially and when footer is dynamically added/removed.
23534
- */
23535
- updateFooterPadding() {
23536
- if (!this.wrapperEl)
23537
- return;
23538
- const hasFooter = this.el.querySelector('ion-footer') !== null;
23539
- if (hasFooter) {
23540
- this.wrapperEl.style.removeProperty('padding-bottom');
23541
- this.wrapperEl.style.removeProperty('box-sizing');
23542
- }
23543
- else {
23544
- this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
23545
- this.wrapperEl.style.setProperty('box-sizing', 'border-box');
23546
- }
23547
- }
23548
- /**
23549
- * Sets all safe-area CSS variables to 0px for modals that
23550
- * don't touch screen edges.
23551
- */
23552
- zeroAllSafeAreas() {
23553
- const style = this.el.style;
23554
- style.setProperty('--ion-safe-area-top', '0px');
23555
- style.setProperty('--ion-safe-area-bottom', '0px');
23556
- style.setProperty('--ion-safe-area-left', '0px');
23557
- style.setProperty('--ion-safe-area-right', '0px');
23558
- }
23559
- /**
23560
- * Gets the root safe-area values from the document element.
23561
- * Uses cached values during gestures to avoid getComputedStyle calls.
23562
- */
23563
- getSafeAreaValues() {
23564
- if (!this.cachedSafeAreas) {
23565
- const rootStyle = getComputedStyle(document.documentElement);
23566
- this.cachedSafeAreas = {
23567
- top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
23568
- bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
23569
- left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
23570
- right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
23571
- };
23572
- }
23573
- return this.cachedSafeAreas;
23574
- }
23575
- /**
23576
- * Updates safe-area CSS variable overrides based on whether the modal
23577
- * extends into each safe-area region. Called after animation
23578
- * and during gestures to handle dynamic position changes.
23579
- *
23580
- * Optimized to avoid redundant DOM writes by tracking previous state.
23581
- */
23582
- updateSafeAreaOverrides() {
23583
- if (this.skipSafeAreaCoordinateDetection) {
23584
- return;
23585
- }
23586
- const wrapper = this.wrapperEl;
23587
- if (!wrapper) {
23588
- return;
23589
- }
23590
- const rect = wrapper.getBoundingClientRect();
23591
- const safeAreas = this.getSafeAreaValues();
23592
- const extendsIntoTop = rect.top < safeAreas.top;
23593
- const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
23594
- const extendsIntoLeft = rect.left < safeAreas.left;
23595
- const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
23596
- // Only update DOM when state actually changes
23597
- const prev = this.prevSafeAreaState;
23598
- const style = this.el.style;
23599
- if (extendsIntoTop !== prev.top) {
23600
- extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
23601
- prev.top = extendsIntoTop;
23602
- }
23603
- if (extendsIntoBottom !== prev.bottom) {
23604
- extendsIntoBottom
23605
- ? style.removeProperty('--ion-safe-area-bottom')
23606
- : style.setProperty('--ion-safe-area-bottom', '0px');
23607
- prev.bottom = extendsIntoBottom;
23608
- }
23609
- if (extendsIntoLeft !== prev.left) {
23610
- extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
23611
- prev.left = extendsIntoLeft;
23612
- }
23613
- if (extendsIntoRight !== prev.right) {
23614
- extendsIntoRight
23615
- ? style.removeProperty('--ion-safe-area-right')
23616
- : style.setProperty('--ion-safe-area-right', '0px');
23617
- prev.right = extendsIntoRight;
23618
- }
23619
- }
23620
23377
  sheetOnDismiss() {
23621
23378
  /**
23622
23379
  * While the gesture animation is finishing
@@ -23647,7 +23404,7 @@ class Modal {
23647
23404
  * For example, `cancel` or `backdrop`.
23648
23405
  */
23649
23406
  async dismiss(data, role) {
23650
- var _a, _b;
23407
+ var _a;
23651
23408
  if (this.gestureAnimationDismissing && role !== GESTURE) {
23652
23409
  return false;
23653
23410
  }
@@ -23709,23 +23466,6 @@ class Modal {
23709
23466
  }
23710
23467
  this.currentBreakpoint = undefined;
23711
23468
  this.animation = undefined;
23712
- // Reset safe-area state for potential re-presentation
23713
- this.skipSafeAreaCoordinateDetection = false;
23714
- this.cachedSafeAreas = undefined;
23715
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
23716
- (_b = this.footerObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
23717
- this.footerObserver = undefined;
23718
- // Clear styles that may have been set for safe-area handling
23719
- if (this.wrapperEl) {
23720
- this.wrapperEl.style.removeProperty('padding-bottom');
23721
- this.wrapperEl.style.removeProperty('box-sizing');
23722
- }
23723
- // Clear safe-area CSS variable overrides
23724
- const style = this.el.style;
23725
- style.removeProperty('--ion-safe-area-top');
23726
- style.removeProperty('--ion-safe-area-bottom');
23727
- style.removeProperty('--ion-safe-area-left');
23728
- style.removeProperty('--ion-safe-area-right');
23729
23469
  unlock();
23730
23470
  return dismissed;
23731
23471
  }
@@ -23975,20 +23715,20 @@ class Modal {
23975
23715
  const isCardModal = presentingElement !== undefined && mode === 'ios';
23976
23716
  const isHandleCycle = handleBehavior === 'cycle';
23977
23717
  const isSheetModalWithHandle = isSheetModal && showHandle;
23978
- return (hAsync(Host, Object.assign({ key: '11cd16cc481093a38a327abdd94467be3f71718d', "no-router": true,
23718
+ return (hAsync(Host, Object.assign({ key: '87328006ea6c75ebc518ace300438492a567223e', "no-router": true,
23979
23719
  // Allow the modal to be navigable when the handle is focusable
23980
23720
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
23981
23721
  zIndex: `${20000 + this.overlayIndex}`,
23982
- }, 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',
23722
+ }, 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',
23983
23723
  /*
23984
23724
  role and aria-modal must be used on the
23985
23725
  same element. They must also be set inside the
23986
23726
  shadow DOM otherwise ion-button will not be highlighted
23987
23727
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
23988
23728
  */
23989
- 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",
23729
+ 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",
23990
23730
  // Prevents the handle from receiving keyboard focus when it does not cycle
23991
- 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 }))));
23731
+ 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 }))));
23992
23732
  }
23993
23733
  get el() { return getElement(this); }
23994
23734
  static get watchers() { return {
@@ -27566,8 +27306,6 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27566
27306
  let bottom;
27567
27307
  let originX = contentOriginX;
27568
27308
  let originY = contentOriginY;
27569
- let checkSafeAreaTop = false;
27570
- let checkSafeAreaBottom = false;
27571
27309
  let checkSafeAreaLeft = false;
27572
27310
  let checkSafeAreaRight = false;
27573
27311
  const triggerTop = triggerCoordinates
@@ -27612,18 +27350,10 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27612
27350
  * We chose 12 here so that the popover position looks a bit nicer as
27613
27351
  * it is not right up against the edge of the screen.
27614
27352
  */
27615
- top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
27353
+ top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
27616
27354
  arrowTop = top + contentHeight;
27617
27355
  originY = 'bottom';
27618
27356
  addPopoverBottomClass = true;
27619
- /**
27620
- * If the popover is positioned near the top edge, account for safe area.
27621
- * This ensures the popover doesn't overlap with status bars or notches.
27622
- */
27623
- if (top <= bodyPadding + safeAreaMargin) {
27624
- checkSafeAreaTop = true;
27625
- top = bodyPadding;
27626
- }
27627
27357
  /**
27628
27358
  * If not enough room for popover to appear
27629
27359
  * above trigger, then cut it off.
@@ -27631,35 +27361,14 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27631
27361
  }
27632
27362
  else {
27633
27363
  bottom = bodyPadding;
27634
- /**
27635
- * When the popover is pinned to the bottom, account for safe area.
27636
- * This ensures the popover doesn't overlap with home indicators
27637
- * or navigation bars (e.g., Android API 36+ edge-to-edge).
27638
- */
27639
- checkSafeAreaBottom = true;
27640
27364
  }
27641
27365
  }
27642
- /**
27643
- * Final check: If the popover extends into any safe-area region,
27644
- * ensure the corresponding flag is set regardless of side.
27645
- * This handles cases where a side-positioned popover (left/right)
27646
- * still needs bottom safe-area padding because it extends into that region.
27647
- */
27648
- const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
27649
- if (popoverBottom + safeAreaMargin > bodyHeight) {
27650
- checkSafeAreaBottom = true;
27651
- }
27652
- if (top < safeAreaMargin) {
27653
- checkSafeAreaTop = true;
27654
- }
27655
27366
  return {
27656
27367
  top,
27657
27368
  left,
27658
27369
  bottom,
27659
27370
  originX,
27660
27371
  originY,
27661
- checkSafeAreaTop,
27662
- checkSafeAreaBottom,
27663
27372
  checkSafeAreaLeft,
27664
27373
  checkSafeAreaRight,
27665
27374
  arrowTop,
@@ -27720,7 +27429,7 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
27720
27429
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
27721
27430
  const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
27722
27431
  const margin = size === 'cover' ? 0 : 25;
27723
- 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);
27432
+ 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);
27724
27433
  const baseAnimation = createAnimation();
27725
27434
  const backdropAnimation = createAnimation();
27726
27435
  const contentAnimation = createAnimation();
@@ -27750,35 +27459,19 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
27750
27459
  if (addPopoverBottomClass) {
27751
27460
  baseEl.classList.add('popover-bottom');
27752
27461
  }
27753
- /**
27754
- * Safe area CSS variable adjustments.
27755
- * When the popover is positioned near an edge, we add the corresponding
27756
- * safe-area inset to ensure the popover doesn't overlap with system UI
27757
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
27758
- */
27759
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
27760
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
27462
+ if (bottom !== undefined) {
27463
+ contentEl.style.setProperty('bottom', `${bottom}px`);
27464
+ }
27761
27465
  const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
27762
27466
  const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
27763
- let topValue = `${top}px`;
27764
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
27765
27467
  let leftValue = `${left}px`;
27766
- if (checkSafeAreaTop) {
27767
- topValue = `${top}px${safeAreaTop}`;
27768
- }
27769
- if (checkSafeAreaBottom && bottomValue !== undefined) {
27770
- bottomValue = `${bottom}px${safeAreaBottom}`;
27771
- }
27772
27468
  if (checkSafeAreaLeft) {
27773
27469
  leftValue = `${left}px${safeAreaLeft}`;
27774
27470
  }
27775
27471
  if (checkSafeAreaRight) {
27776
27472
  leftValue = `${left}px${safeAreaRight}`;
27777
27473
  }
27778
- if (bottomValue !== undefined) {
27779
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
27780
- }
27781
- contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
27474
+ contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
27782
27475
  contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
27783
27476
  contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
27784
27477
  if (arrowEl !== null) {
@@ -27854,23 +27547,7 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
27854
27547
  };
27855
27548
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
27856
27549
  const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
27857
- 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);
27858
- /**
27859
- * Safe area CSS variable adjustments.
27860
- * When the popover is positioned near an edge, we add the corresponding
27861
- * safe-area inset to ensure the popover doesn't overlap with system UI
27862
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
27863
- */
27864
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
27865
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
27866
- let topValue = `${top}px`;
27867
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
27868
- if (checkSafeAreaTop) {
27869
- topValue = `${top}px${safeAreaTop}`;
27870
- }
27871
- if (checkSafeAreaBottom && bottomValue !== undefined) {
27872
- bottomValue = `${bottom}px${safeAreaBottom}`;
27873
- }
27550
+ const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
27874
27551
  const baseAnimation = createAnimation();
27875
27552
  const backdropAnimation = createAnimation();
27876
27553
  const wrapperAnimation = createAnimation();
@@ -27887,13 +27564,13 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
27887
27564
  contentAnimation
27888
27565
  .addElement(contentEl)
27889
27566
  .beforeStyles({
27890
- top: `calc(${topValue} + var(--offset-y, 0px))`,
27567
+ top: `calc(${top}px + var(--offset-y, 0px))`,
27891
27568
  left: `calc(${left}px + var(--offset-x, 0px))`,
27892
27569
  'transform-origin': `${originY} ${originX}`,
27893
27570
  })
27894
27571
  .beforeAddWrite(() => {
27895
- if (bottomValue !== undefined) {
27896
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
27572
+ if (bottom !== undefined) {
27573
+ contentEl.style.setProperty('bottom', `${bottom}px`);
27897
27574
  }
27898
27575
  })
27899
27576
  .fromTo('transform', 'scale(0.8)', 'scale(1)');
@@ -35311,6 +34988,7 @@ class TabBar {
35311
34988
  this.ionTabBarChanged = createEvent(this, "ionTabBarChanged", 7);
35312
34989
  this.ionTabBarLoaded = createEvent(this, "ionTabBarLoaded", 7);
35313
34990
  this.keyboardCtrl = null;
34991
+ this.keyboardCtrlPromise = null;
35314
34992
  this.didLoad = false;
35315
34993
  this.keyboardVisible = false;
35316
34994
  /**
@@ -35346,7 +35024,7 @@ class TabBar {
35346
35024
  }
35347
35025
  }
35348
35026
  async connectedCallback() {
35349
- this.keyboardCtrl = await createKeyboardController(async (keyboardOpen, waitForResize) => {
35027
+ const promise = createKeyboardController(async (keyboardOpen, waitForResize) => {
35350
35028
  /**
35351
35029
  * If the keyboard is hiding, then we need to wait
35352
35030
  * for the webview to resize. Otherwise, the tab bar
@@ -35357,21 +35035,40 @@ class TabBar {
35357
35035
  }
35358
35036
  this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
35359
35037
  });
35038
+ this.keyboardCtrlPromise = promise;
35039
+ const keyboardCtrl = await promise;
35040
+ /**
35041
+ * Only assign if this is still the current promise.
35042
+ * Otherwise, a new connectedCallback has started or
35043
+ * disconnectedCallback was called, so destroy this instance.
35044
+ */
35045
+ if (this.keyboardCtrlPromise === promise) {
35046
+ this.keyboardCtrl = keyboardCtrl;
35047
+ this.keyboardCtrlPromise = null;
35048
+ }
35049
+ else {
35050
+ keyboardCtrl.destroy();
35051
+ }
35360
35052
  }
35361
35053
  disconnectedCallback() {
35054
+ if (this.keyboardCtrlPromise) {
35055
+ this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
35056
+ this.keyboardCtrlPromise = null;
35057
+ }
35362
35058
  if (this.keyboardCtrl) {
35363
35059
  this.keyboardCtrl.destroy();
35060
+ this.keyboardCtrl = null;
35364
35061
  }
35365
35062
  }
35366
35063
  render() {
35367
35064
  const { color, translucent, keyboardVisible } = this;
35368
35065
  const mode = getIonMode$1(this);
35369
35066
  const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
35370
- return (hAsync(Host, { key: '388ec37ce308035bab78d6c9a016bb616e9517a9', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses$1(color, {
35067
+ return (hAsync(Host, { key: '9daf4e2acaff6e3ce3878cf9dd5109fb1afbbebe', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses$1(color, {
35371
35068
  [mode]: true,
35372
35069
  'tab-bar-translucent': translucent,
35373
35070
  'tab-bar-hidden': shouldHide,
35374
- }) }, hAsync("slot", { key: 'ce10ade2b86725e24f3254516483eeedd8ecb16a' })));
35071
+ }) }, hAsync("slot", { key: '1d15aa2da8501e8e7eff11ad4a491478be845c43' })));
35375
35072
  }
35376
35073
  get el() { return getElement(this); }
35377
35074
  static get watchers() { return {