@ionic/core 8.7.17-dev.11767891829.1a63afa3 → 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 +8 -96
  2. package/components/ion-tab-bar.js +23 -3
  3. package/components/modal.js +12 -213
  4. package/components/popover.js +11 -83
  5. package/dist/cjs/ion-app_8.cjs.entry.js +20 -107
  6. package/dist/cjs/ion-modal.cjs.entry.js +12 -213
  7. package/dist/cjs/ion-popover.cjs.entry.js +11 -83
  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 +6 -94
  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 +7 -205
  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 +5 -30
  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 +9 -96
  22. package/dist/esm/ion-modal.entry.js +12 -213
  23. package/dist/esm/ion-popover.entry.js +11 -83
  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 -45
  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 +52 -385
  37. package/hydrate/index.mjs +52 -385
  38. package/package.json +1 -1
  39. package/dist/ionic/p-7268efa5.entry.js +0 -4
  40. package/dist/ionic/p-968a55d1.entry.js +0 -4
  41. package/dist/ionic/p-d9fd799f.entry.js +0 -4
  42. package/dist/ionic/p-ec9ca3fe.entry.js +0 -4
@@ -657,8 +657,6 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
657
657
  let bottom;
658
658
  let originX = contentOriginX;
659
659
  let originY = contentOriginY;
660
- let checkSafeAreaTop = false;
661
- let checkSafeAreaBottom = false;
662
660
  let checkSafeAreaLeft = false;
663
661
  let checkSafeAreaRight = false;
664
662
  const triggerTop = triggerCoordinates
@@ -703,18 +701,10 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
703
701
  * We chose 12 here so that the popover position looks a bit nicer as
704
702
  * it is not right up against the edge of the screen.
705
703
  */
706
- top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
704
+ top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
707
705
  arrowTop = top + contentHeight;
708
706
  originY = 'bottom';
709
707
  addPopoverBottomClass = true;
710
- /**
711
- * If the popover is positioned near the top edge, account for safe area.
712
- * This ensures the popover doesn't overlap with status bars or notches.
713
- */
714
- if (top <= bodyPadding + safeAreaMargin) {
715
- checkSafeAreaTop = true;
716
- top = bodyPadding;
717
- }
718
708
  /**
719
709
  * If not enough room for popover to appear
720
710
  * above trigger, then cut it off.
@@ -722,35 +712,14 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
722
712
  }
723
713
  else {
724
714
  bottom = bodyPadding;
725
- /**
726
- * When the popover is pinned to the bottom, account for safe area.
727
- * This ensures the popover doesn't overlap with home indicators
728
- * or navigation bars (e.g., Android API 36+ edge-to-edge).
729
- */
730
- checkSafeAreaBottom = true;
731
715
  }
732
716
  }
733
- /**
734
- * Final check: If the popover extends into any safe-area region,
735
- * ensure the corresponding flag is set regardless of side.
736
- * This handles cases where a side-positioned popover (left/right)
737
- * still needs bottom safe-area padding because it extends into that region.
738
- */
739
- const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
740
- if (popoverBottom + safeAreaMargin > bodyHeight) {
741
- checkSafeAreaBottom = true;
742
- }
743
- if (top < safeAreaMargin) {
744
- checkSafeAreaTop = true;
745
- }
746
717
  return {
747
718
  top,
748
719
  left,
749
720
  bottom,
750
721
  originX,
751
722
  originY,
752
- checkSafeAreaTop,
753
- checkSafeAreaBottom,
754
723
  checkSafeAreaLeft,
755
724
  checkSafeAreaRight,
756
725
  arrowTop,
@@ -811,7 +780,7 @@ const iosEnterAnimation = (baseEl, opts) => {
811
780
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
812
781
  const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
813
782
  const margin = size === 'cover' ? 0 : 25;
814
- 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);
783
+ 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);
815
784
  const baseAnimation = createAnimation();
816
785
  const backdropAnimation = createAnimation();
817
786
  const contentAnimation = createAnimation();
@@ -841,35 +810,19 @@ const iosEnterAnimation = (baseEl, opts) => {
841
810
  if (addPopoverBottomClass) {
842
811
  baseEl.classList.add('popover-bottom');
843
812
  }
844
- /**
845
- * Safe area CSS variable adjustments.
846
- * When the popover is positioned near an edge, we add the corresponding
847
- * safe-area inset to ensure the popover doesn't overlap with system UI
848
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
849
- */
850
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
851
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
813
+ if (bottom !== undefined) {
814
+ contentEl.style.setProperty('bottom', `${bottom}px`);
815
+ }
852
816
  const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
853
817
  const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
854
- let topValue = `${top}px`;
855
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
856
818
  let leftValue = `${left}px`;
857
- if (checkSafeAreaTop) {
858
- topValue = `${top}px${safeAreaTop}`;
859
- }
860
- if (checkSafeAreaBottom && bottomValue !== undefined) {
861
- bottomValue = `${bottom}px${safeAreaBottom}`;
862
- }
863
819
  if (checkSafeAreaLeft) {
864
820
  leftValue = `${left}px${safeAreaLeft}`;
865
821
  }
866
822
  if (checkSafeAreaRight) {
867
823
  leftValue = `${left}px${safeAreaRight}`;
868
824
  }
869
- if (bottomValue !== undefined) {
870
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
871
- }
872
- contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
825
+ contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
873
826
  contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
874
827
  contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
875
828
  if (arrowEl !== null) {
@@ -945,32 +898,7 @@ const mdEnterAnimation = (baseEl, opts) => {
945
898
  };
946
899
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
947
900
  const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
948
- const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom, checkSafeAreaLeft, checkSafeAreaRight, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
949
- /**
950
- * Safe area CSS variable adjustments.
951
- * When the popover is positioned near an edge, we add the corresponding
952
- * safe-area inset to ensure the popover doesn't overlap with system UI
953
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
954
- */
955
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
956
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
957
- const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
958
- const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
959
- let topValue = `${top}px`;
960
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
961
- let leftValue = `${left}px`;
962
- if (checkSafeAreaTop) {
963
- topValue = `${top}px${safeAreaTop}`;
964
- }
965
- if (checkSafeAreaBottom && bottomValue !== undefined) {
966
- bottomValue = `${bottom}px${safeAreaBottom}`;
967
- }
968
- if (checkSafeAreaLeft) {
969
- leftValue = `${left}px${safeAreaLeft}`;
970
- }
971
- if (checkSafeAreaRight) {
972
- leftValue = `${left}px${safeAreaRight}`;
973
- }
901
+ const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
974
902
  const baseAnimation = createAnimation();
975
903
  const backdropAnimation = createAnimation();
976
904
  const wrapperAnimation = createAnimation();
@@ -987,13 +915,13 @@ const mdEnterAnimation = (baseEl, opts) => {
987
915
  contentAnimation
988
916
  .addElement(contentEl)
989
917
  .beforeStyles({
990
- top: `calc(${topValue} + var(--offset-y, 0px))`,
991
- left: `calc(${leftValue} + var(--offset-x, 0px))`,
918
+ top: `calc(${top}px + var(--offset-y, 0px))`,
919
+ left: `calc(${left}px + var(--offset-x, 0px))`,
992
920
  'transform-origin': `${originY} ${originX}`,
993
921
  })
994
922
  .beforeAddWrite(() => {
995
- if (bottomValue !== undefined) {
996
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
923
+ if (bottom !== undefined) {
924
+ contentEl.style.setProperty('bottom', `${bottom}px`);
997
925
  }
998
926
  })
999
927
  .fromTo('transform', 'scale(0.8)', 'scale(1)');
@@ -6,16 +6,16 @@
6
6
  var index = require('./index-D6Wc6v08.js');
7
7
  var hardwareBackButton = require('./hardware-back-button-VCK4V3mG.js');
8
8
  var ionicGlobal = require('./ionic-global-HMVqOFGO.js');
9
- var index$1 = require('./index-DkNv4J_i.js');
10
9
  var helpers = require('./helpers-DrTqNghc.js');
11
10
  var dir = require('./dir-Cn0z1rJH.js');
12
11
  var theme = require('./theme-CeDs6Hcv.js');
13
- var index$2 = require('./index-CO6eryBo.js');
12
+ var index$1 = require('./index-CO6eryBo.js');
14
13
  var keyboardController = require('./keyboard-controller-GXBiBRKS.js');
15
14
  var cubicBezier = require('./cubic-bezier-DAjy1V-e.js');
16
15
  var frameworkDelegate = require('./framework-delegate-DMJRBuDi.js');
17
16
  var lockController = require('./lock-controller-aDB9wrEf.js');
18
- var index$3 = require('./index-094mMFB-.js');
17
+ var index$2 = require('./index-094mMFB-.js');
18
+ require('./index-DkNv4J_i.js');
19
19
  require('./keyboard-UuAS4D_9.js');
20
20
  require('./capacitor-DmA66EwP.js');
21
21
 
@@ -154,7 +154,7 @@ Buttons.style = {
154
154
  md: buttonsMdCss
155
155
  };
156
156
 
157
- 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)}";
157
+ 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)}";
158
158
 
159
159
  const Content = class {
160
160
  constructor(hostRef) {
@@ -171,12 +171,6 @@ const Content = class {
171
171
  this.isMainContent = true;
172
172
  this.resizeTimeout = null;
173
173
  this.inheritedAttributes = {};
174
- /**
175
- * Track whether this content has sibling header/footer elements.
176
- * When absent, we need to apply safe-area padding directly.
177
- */
178
- this.hasHeader = false;
179
- this.hasFooter = false;
180
174
  this.tabsElement = null;
181
175
  // Detail is used in a hot loop in the scroll event, by allocating it here
182
176
  // V8 will be able to inline any read/write to it since it's a monomorphic class.
@@ -231,13 +225,7 @@ const Content = class {
231
225
  this.inheritedAttributes = helpers.inheritAriaAttributes(this.el);
232
226
  }
233
227
  connectedCallback() {
234
- var _a;
235
- // Content is "main" if not inside menu/popover/modal and not nested in another ion-content
236
- this.isMainContent =
237
- this.el.closest('ion-menu, ion-popover, ion-modal') === null &&
238
- ((_a = this.el.parentElement) === null || _a === void 0 ? void 0 : _a.closest('ion-content')) === null;
239
- // Detect sibling header/footer for safe-area handling
240
- this.detectSiblingElements();
228
+ this.isMainContent = this.el.closest('ion-menu, ion-popover, ion-modal') === null;
241
229
  /**
242
230
  * The fullscreen content offsets need to be
243
231
  * computed after the tab bar has loaded. Since
@@ -268,86 +256,13 @@ const Content = class {
268
256
  * bubbles, we can catch any instances of child tab bars loading by listening
269
257
  * on IonTabs.
270
258
  */
271
- this.tabsLoadCallback = () => {
272
- this.resize();
273
- // Re-detect footer when tab bar loads (it may not exist during initial detection)
274
- this.updateSiblingDetection();
275
- index.forceUpdate(this);
276
- };
259
+ this.tabsLoadCallback = () => this.resize();
277
260
  closestTabs.addEventListener('ionTabBarLoaded', this.tabsLoadCallback);
278
261
  }
279
262
  }
280
263
  }
281
- /**
282
- * Detects sibling ion-header and ion-footer elements and sets up
283
- * a mutation observer to handle dynamic changes (e.g., conditional rendering).
284
- */
285
- detectSiblingElements() {
286
- this.updateSiblingDetection();
287
- // Watch for dynamic header/footer changes (common in React conditional rendering)
288
- const parent = this.el.parentElement;
289
- if (parent && !this.parentMutationObserver && index$1.win !== undefined && 'MutationObserver' in index$1.win) {
290
- this.parentMutationObserver = new MutationObserver(() => {
291
- const prevHasHeader = this.hasHeader;
292
- const prevHasFooter = this.hasFooter;
293
- this.updateSiblingDetection();
294
- // Only trigger re-render if header/footer detection actually changed
295
- if (prevHasHeader !== this.hasHeader || prevHasFooter !== this.hasFooter) {
296
- index.forceUpdate(this);
297
- }
298
- });
299
- this.parentMutationObserver.observe(parent, { childList: true });
300
- }
301
- }
302
- /**
303
- * Updates hasHeader/hasFooter based on current DOM state.
304
- * Checks both direct siblings and elements wrapped in custom components
305
- * (e.g., <my-header><ion-header>...</ion-header></my-header>).
306
- */
307
- updateSiblingDetection() {
308
- const parent = this.el.parentElement;
309
- if (parent) {
310
- // First check for direct ion-header/ion-footer siblings
311
- this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
312
- this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
313
- // If not found, check if any sibling contains them (wrapped components)
314
- if (!this.hasHeader) {
315
- this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
316
- }
317
- if (!this.hasFooter) {
318
- this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
319
- }
320
- }
321
- // If no footer found, check if we're inside ion-tabs which has ion-tab-bar
322
- if (!this.hasFooter) {
323
- const tabs = this.el.closest('ion-tabs');
324
- if (tabs) {
325
- this.hasFooter = tabs.querySelector(':scope > ion-tab-bar') !== null;
326
- }
327
- }
328
- }
329
- /**
330
- * Checks if any sibling element of ion-content contains the specified element.
331
- * Only searches one level deep to avoid finding elements in nested pages.
332
- */
333
- siblingContainsElement(parent, tagName) {
334
- for (const sibling of parent.children) {
335
- // Skip ion-content itself
336
- if (sibling === this.el)
337
- continue;
338
- // Check if this sibling contains the target element as an immediate child
339
- if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
340
- return true;
341
- }
342
- }
343
- return false;
344
- }
345
264
  disconnectedCallback() {
346
- var _a;
347
265
  this.onScrollEnd();
348
- // Clean up mutation observer to prevent memory leaks
349
- (_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
350
- this.parentMutationObserver = undefined;
351
266
  if (helpers.hasLazyBuild(this.el)) {
352
267
  /**
353
268
  * The event listener and tabs caches need to
@@ -594,28 +509,26 @@ const Content = class {
594
509
  }
595
510
  }
596
511
  render() {
597
- const { fixedSlotPlacement, hasFooter, hasHeader, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
512
+ const { fixedSlotPlacement, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
598
513
  const rtl = dir.isRTL(el) ? 'rtl' : 'ltr';
599
514
  const mode = ionicGlobal.getIonMode(this);
600
515
  const forceOverscroll = this.shouldForceOverscroll();
601
516
  const transitionShadow = mode === 'ios';
602
517
  this.resize();
603
- return (index.h(index.Host, Object.assign({ key: 'f7218f733e4022a30875441bd949747537d28aa1', role: isMainContent ? 'main' : undefined, class: theme.createColorClasses(this.color, {
518
+ return (index.h(index.Host, Object.assign({ key: 'cd8781f848d8dc926fe66f43d43c49564425a507', role: isMainContent ? 'main' : undefined, class: theme.createColorClasses(this.color, {
604
519
  [mode]: true,
605
520
  'content-sizing': theme.hostContext('ion-popover', this.el),
606
521
  overscroll: forceOverscroll,
607
522
  [`content-${rtl}`]: true,
608
- 'safe-area-top': isMainContent && !hasHeader,
609
- 'safe-area-bottom': isMainContent && !hasFooter,
610
523
  }), style: {
611
524
  '--offset-top': `${this.cTop}px`,
612
525
  '--offset-bottom': `${this.cBottom}px`,
613
- } }, inheritedAttributes), index.h("div", { key: 'b735ec68c18c0b99c3595bb194029830e6542cde', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? index.h("slot", { name: "fixed" }) : null, index.h("div", { key: 'e76c00d030342d44ade6648c3f9e32ca990787ba', class: {
526
+ } }, inheritedAttributes), index.h("div", { key: '95b112d7cae30f22ef778ceffb88edb4d941c170', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? index.h("slot", { name: "fixed" }) : null, index.h("div", { key: '2fdfcbc39fb66f11b6191911f2941c660f4c12e5', class: {
614
527
  'inner-scroll': true,
615
528
  'scroll-x': scrollX,
616
529
  'scroll-y': scrollY,
617
530
  overscroll: (scrollX || scrollY) && forceOverscroll,
618
- }, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, index.h("slot", { key: '9049be4cea9b5da5ec1e1012248b05286fddeb7a' })), transitionShadow ? (index.h("div", { class: "transition-effect" }, index.h("div", { class: "transition-cover" }), index.h("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? index.h("slot", { name: "fixed" }) : null));
531
+ }, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, index.h("slot", { key: '6bc77e0054ec8e21635a7f2abfe0ca46e0962e03' })), transitionShadow ? (index.h("div", { class: "transition-effect" }, index.h("div", { class: "transition-cover" }), index.h("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? index.h("slot", { name: "fixed" }) : null));
619
532
  }
620
533
  get el() { return index.getElement(this); }
621
534
  };
@@ -733,16 +646,16 @@ const Footer = class {
733
646
  this.destroyCollapsibleFooter();
734
647
  if (hasFade) {
735
648
  const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
736
- const contentEl = pageEl ? index$2.findIonContent(pageEl) : null;
649
+ const contentEl = pageEl ? index$1.findIonContent(pageEl) : null;
737
650
  if (!contentEl) {
738
- index$2.printIonContentErrorMsg(this.el);
651
+ index$1.printIonContentErrorMsg(this.el);
739
652
  return;
740
653
  }
741
654
  this.setupFadeFooter(contentEl);
742
655
  }
743
656
  };
744
657
  this.setupFadeFooter = async (contentEl) => {
745
- const scrollEl = (this.scrollEl = await index$2.getScrollElement(contentEl));
658
+ const scrollEl = (this.scrollEl = await index$1.getScrollElement(contentEl));
746
659
  /**
747
660
  * Handle fading of toolbars on scroll
748
661
  */
@@ -1055,7 +968,7 @@ const Header = class {
1055
968
  */
1056
969
  this.translucent = false;
1057
970
  this.setupFadeHeader = async (contentEl, condenseHeader) => {
1058
- const scrollEl = (this.scrollEl = await index$2.getScrollElement(contentEl));
971
+ const scrollEl = (this.scrollEl = await index$1.getScrollElement(contentEl));
1059
972
  /**
1060
973
  * Handle fading of toolbars on scroll
1061
974
  */
@@ -1089,7 +1002,7 @@ const Header = class {
1089
1002
  this.destroyCollapsibleHeader();
1090
1003
  if (hasCondense) {
1091
1004
  const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
1092
- const contentEl = pageEl ? index$2.findIonContent(pageEl) : null;
1005
+ const contentEl = pageEl ? index$1.findIonContent(pageEl) : null;
1093
1006
  // Cloned elements are always needed in iOS transition
1094
1007
  index.writeTask(() => {
1095
1008
  const title = cloneElement('ion-title');
@@ -1100,9 +1013,9 @@ const Header = class {
1100
1013
  }
1101
1014
  else if (hasFade) {
1102
1015
  const pageEl = this.el.closest('ion-app,ion-page,.ion-page,page-inner');
1103
- const contentEl = pageEl ? index$2.findIonContent(pageEl) : null;
1016
+ const contentEl = pageEl ? index$1.findIonContent(pageEl) : null;
1104
1017
  if (!contentEl) {
1105
- index$2.printIonContentErrorMsg(this.el);
1018
+ index$1.printIonContentErrorMsg(this.el);
1106
1019
  return;
1107
1020
  }
1108
1021
  const condenseHeader = contentEl.querySelector('ion-header[collapse="condense"]');
@@ -1125,13 +1038,13 @@ const Header = class {
1125
1038
  }
1126
1039
  async setupCondenseHeader(contentEl, pageEl) {
1127
1040
  if (!contentEl || !pageEl) {
1128
- index$2.printIonContentErrorMsg(this.el);
1041
+ index$1.printIonContentErrorMsg(this.el);
1129
1042
  return;
1130
1043
  }
1131
1044
  if (typeof IntersectionObserver === 'undefined') {
1132
1045
  return;
1133
1046
  }
1134
- this.scrollEl = await index$2.getScrollElement(contentEl);
1047
+ this.scrollEl = await index$1.getScrollElement(contentEl);
1135
1048
  const headers = pageEl.querySelectorAll('ion-header');
1136
1049
  this.collapsibleMainHeader = Array.from(headers).find((header) => header.collapse !== 'condense');
1137
1050
  if (!this.collapsibleMainHeader) {
@@ -1329,7 +1242,7 @@ const RouterOutlet = class {
1329
1242
  const { el, mode } = this;
1330
1243
  const animated = this.animated && index.config.getBoolean('animated', true);
1331
1244
  const animationBuilder = opts.animationBuilder || this.animation || index.config.get('navAnimation');
1332
- await index$3.transition(Object.assign(Object.assign({ mode,
1245
+ await index$2.transition(Object.assign(Object.assign({ mode,
1333
1246
  animated,
1334
1247
  enteringEl,
1335
1248
  leavingEl, baseEl: el,