@ionic/core 8.7.17-dev.11767717752.14fe98a4 → 8.7.17-dev.11767796972.148e4bc4
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.
- package/components/content.js +51 -7
- package/components/modal.js +81 -26
- package/dist/cjs/ion-app_8.cjs.entry.js +50 -6
- package/dist/cjs/ion-modal.cjs.entry.js +81 -26
- package/dist/collection/components/content/content.js +50 -6
- package/dist/collection/components/modal/modal.js +81 -26
- package/dist/docs.json +1 -1
- package/dist/esm/ion-app_8.entry.js +51 -7
- package/dist/esm/ion-modal.entry.js +81 -26
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-3830217a.entry.js +4 -0
- package/dist/ionic/p-cecd141b.entry.js +4 -0
- package/dist/types/components/content/content.d.ts +15 -2
- package/dist/types/components/modal/modal.d.ts +12 -2
- package/hydrate/index.js +131 -33
- package/hydrate/index.mjs +131 -33
- package/package.json +1 -1
- package/dist/ionic/p-34cdcd15.entry.js +0 -4
- package/dist/ionic/p-fedca459.entry.js +0 -4
package/hydrate/index.js
CHANGED
|
@@ -10622,15 +10622,38 @@ class Content {
|
|
|
10622
10622
|
}
|
|
10623
10623
|
}
|
|
10624
10624
|
/**
|
|
10625
|
-
* Detects sibling ion-header and ion-footer elements
|
|
10626
|
-
*
|
|
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
10627
|
*/
|
|
10628
10628
|
detectSiblingElements() {
|
|
10629
|
-
|
|
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() {
|
|
10630
10645
|
const parent = this.el.parentElement;
|
|
10631
10646
|
if (parent) {
|
|
10647
|
+
// First check for direct ion-header/ion-footer siblings
|
|
10632
10648
|
this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
|
|
10633
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
|
+
}
|
|
10634
10657
|
}
|
|
10635
10658
|
// If no footer found, check if we're inside ion-tabs which has ion-tab-bar
|
|
10636
10659
|
if (!this.hasFooter) {
|
|
@@ -10640,8 +10663,28 @@ class Content {
|
|
|
10640
10663
|
}
|
|
10641
10664
|
}
|
|
10642
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
|
+
}
|
|
10643
10682
|
disconnectedCallback() {
|
|
10683
|
+
var _a;
|
|
10644
10684
|
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;
|
|
10645
10688
|
if (hasLazyBuild(this.el)) {
|
|
10646
10689
|
/**
|
|
10647
10690
|
* The event listener and tabs caches need to
|
|
@@ -10876,7 +10919,7 @@ class Content {
|
|
|
10876
10919
|
const forceOverscroll = this.shouldForceOverscroll();
|
|
10877
10920
|
const transitionShadow = mode === 'ios';
|
|
10878
10921
|
this.resize();
|
|
10879
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
10922
|
+
return (hAsync(Host, Object.assign({ key: 'c8e3a93e0b1ba6f7aa81a6a6065145ece9a6e2ef', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
|
|
10880
10923
|
[mode]: true,
|
|
10881
10924
|
'content-sizing': hostContext('ion-popover', this.el),
|
|
10882
10925
|
overscroll: forceOverscroll,
|
|
@@ -10886,12 +10929,12 @@ class Content {
|
|
|
10886
10929
|
}), style: {
|
|
10887
10930
|
'--offset-top': `${this.cTop}px`,
|
|
10888
10931
|
'--offset-bottom': `${this.cBottom}px`,
|
|
10889
|
-
} }, inheritedAttributes), hAsync("div", { key: '
|
|
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: {
|
|
10890
10933
|
'inner-scroll': true,
|
|
10891
10934
|
'scroll-x': scrollX,
|
|
10892
10935
|
'scroll-y': scrollY,
|
|
10893
10936
|
overscroll: (scrollX || scrollY) && forceOverscroll,
|
|
10894
|
-
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '
|
|
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));
|
|
10895
10938
|
}
|
|
10896
10939
|
get el() { return getElement(this); }
|
|
10897
10940
|
static get style() { return contentCss; }
|
|
@@ -22828,6 +22871,8 @@ class Modal {
|
|
|
22828
22871
|
this.gestureAnimationDismissing = false;
|
|
22829
22872
|
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
22830
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 };
|
|
22831
22876
|
this.presented = false;
|
|
22832
22877
|
/** @internal */
|
|
22833
22878
|
this.hasController = false;
|
|
@@ -23018,7 +23063,8 @@ class Modal {
|
|
|
23018
23063
|
}
|
|
23019
23064
|
}
|
|
23020
23065
|
onWindowResize() {
|
|
23021
|
-
//
|
|
23066
|
+
// Invalidate safe-area cache on resize (device rotation may change values)
|
|
23067
|
+
this.cachedSafeAreas = undefined;
|
|
23022
23068
|
this.updateSafeAreaOverrides();
|
|
23023
23069
|
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
23024
23070
|
if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
@@ -23477,8 +23523,26 @@ class Modal {
|
|
|
23477
23523
|
*/
|
|
23478
23524
|
applyFullscreenSafeArea() {
|
|
23479
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;
|
|
23480
23540
|
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
23481
|
-
if (
|
|
23541
|
+
if (hasFooter) {
|
|
23542
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23543
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
23544
|
+
}
|
|
23545
|
+
else {
|
|
23482
23546
|
this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
23483
23547
|
this.wrapperEl.style.setProperty('box-sizing', 'border-box');
|
|
23484
23548
|
}
|
|
@@ -23496,21 +23560,26 @@ class Modal {
|
|
|
23496
23560
|
}
|
|
23497
23561
|
/**
|
|
23498
23562
|
* Gets the root safe-area values from the document element.
|
|
23499
|
-
*
|
|
23500
|
-
*/
|
|
23501
|
-
|
|
23502
|
-
|
|
23503
|
-
|
|
23504
|
-
|
|
23505
|
-
|
|
23506
|
-
|
|
23507
|
-
|
|
23508
|
-
|
|
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;
|
|
23509
23576
|
}
|
|
23510
23577
|
/**
|
|
23511
23578
|
* Updates safe-area CSS variable overrides based on whether the modal
|
|
23512
23579
|
* extends into each safe-area region. Called after animation
|
|
23513
23580
|
* and during gestures to handle dynamic position changes.
|
|
23581
|
+
*
|
|
23582
|
+
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
23514
23583
|
*/
|
|
23515
23584
|
updateSafeAreaOverrides() {
|
|
23516
23585
|
if (this.skipSafeAreaCoordinateDetection) {
|
|
@@ -23521,20 +23590,34 @@ class Modal {
|
|
|
23521
23590
|
return;
|
|
23522
23591
|
}
|
|
23523
23592
|
const rect = wrapper.getBoundingClientRect();
|
|
23524
|
-
const safeAreas = this.
|
|
23593
|
+
const safeAreas = this.getSafeAreaValues();
|
|
23525
23594
|
const extendsIntoTop = rect.top < safeAreas.top;
|
|
23526
23595
|
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
23527
23596
|
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
23528
23597
|
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
23598
|
+
// Only update DOM when state actually changes
|
|
23599
|
+
const prev = this.prevSafeAreaState;
|
|
23529
23600
|
const style = this.el.style;
|
|
23530
|
-
extendsIntoTop
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23535
|
-
|
|
23536
|
-
|
|
23537
|
-
|
|
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
|
+
}
|
|
23538
23621
|
}
|
|
23539
23622
|
sheetOnDismiss() {
|
|
23540
23623
|
/**
|
|
@@ -23566,7 +23649,7 @@ class Modal {
|
|
|
23566
23649
|
* For example, `cancel` or `backdrop`.
|
|
23567
23650
|
*/
|
|
23568
23651
|
async dismiss(data, role) {
|
|
23569
|
-
var _a;
|
|
23652
|
+
var _a, _b;
|
|
23570
23653
|
if (this.gestureAnimationDismissing && role !== GESTURE) {
|
|
23571
23654
|
return false;
|
|
23572
23655
|
}
|
|
@@ -23628,8 +23711,23 @@ class Modal {
|
|
|
23628
23711
|
}
|
|
23629
23712
|
this.currentBreakpoint = undefined;
|
|
23630
23713
|
this.animation = undefined;
|
|
23631
|
-
// Reset safe-area
|
|
23714
|
+
// Reset safe-area state for potential re-presentation
|
|
23632
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');
|
|
23633
23731
|
unlock();
|
|
23634
23732
|
return dismissed;
|
|
23635
23733
|
}
|
|
@@ -23879,20 +23977,20 @@ class Modal {
|
|
|
23879
23977
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23880
23978
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
23881
23979
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
23882
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
23980
|
+
return (hAsync(Host, Object.assign({ key: '11cd16cc481093a38a327abdd94467be3f71718d', "no-router": true,
|
|
23883
23981
|
// Allow the modal to be navigable when the handle is focusable
|
|
23884
23982
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
23885
23983
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
23886
|
-
}, 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: '
|
|
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',
|
|
23887
23985
|
/*
|
|
23888
23986
|
role and aria-modal must be used on the
|
|
23889
23987
|
same element. They must also be set inside the
|
|
23890
23988
|
shadow DOM otherwise ion-button will not be highlighted
|
|
23891
23989
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
23892
23990
|
*/
|
|
23893
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '
|
|
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",
|
|
23894
23992
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
23895
|
-
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: '
|
|
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 }))));
|
|
23896
23994
|
}
|
|
23897
23995
|
get el() { return getElement(this); }
|
|
23898
23996
|
static get watchers() { return {
|
package/hydrate/index.mjs
CHANGED
|
@@ -10620,15 +10620,38 @@ class Content {
|
|
|
10620
10620
|
}
|
|
10621
10621
|
}
|
|
10622
10622
|
/**
|
|
10623
|
-
* Detects sibling ion-header and ion-footer elements
|
|
10624
|
-
*
|
|
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
10625
|
*/
|
|
10626
10626
|
detectSiblingElements() {
|
|
10627
|
-
|
|
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() {
|
|
10628
10643
|
const parent = this.el.parentElement;
|
|
10629
10644
|
if (parent) {
|
|
10645
|
+
// First check for direct ion-header/ion-footer siblings
|
|
10630
10646
|
this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
|
|
10631
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
|
+
}
|
|
10632
10655
|
}
|
|
10633
10656
|
// If no footer found, check if we're inside ion-tabs which has ion-tab-bar
|
|
10634
10657
|
if (!this.hasFooter) {
|
|
@@ -10638,8 +10661,28 @@ class Content {
|
|
|
10638
10661
|
}
|
|
10639
10662
|
}
|
|
10640
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
|
+
}
|
|
10641
10680
|
disconnectedCallback() {
|
|
10681
|
+
var _a;
|
|
10642
10682
|
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;
|
|
10643
10686
|
if (hasLazyBuild(this.el)) {
|
|
10644
10687
|
/**
|
|
10645
10688
|
* The event listener and tabs caches need to
|
|
@@ -10874,7 +10917,7 @@ class Content {
|
|
|
10874
10917
|
const forceOverscroll = this.shouldForceOverscroll();
|
|
10875
10918
|
const transitionShadow = mode === 'ios';
|
|
10876
10919
|
this.resize();
|
|
10877
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
10920
|
+
return (hAsync(Host, Object.assign({ key: 'c8e3a93e0b1ba6f7aa81a6a6065145ece9a6e2ef', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
|
|
10878
10921
|
[mode]: true,
|
|
10879
10922
|
'content-sizing': hostContext('ion-popover', this.el),
|
|
10880
10923
|
overscroll: forceOverscroll,
|
|
@@ -10884,12 +10927,12 @@ class Content {
|
|
|
10884
10927
|
}), style: {
|
|
10885
10928
|
'--offset-top': `${this.cTop}px`,
|
|
10886
10929
|
'--offset-bottom': `${this.cBottom}px`,
|
|
10887
|
-
} }, inheritedAttributes), hAsync("div", { key: '
|
|
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: {
|
|
10888
10931
|
'inner-scroll': true,
|
|
10889
10932
|
'scroll-x': scrollX,
|
|
10890
10933
|
'scroll-y': scrollY,
|
|
10891
10934
|
overscroll: (scrollX || scrollY) && forceOverscroll,
|
|
10892
|
-
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '
|
|
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));
|
|
10893
10936
|
}
|
|
10894
10937
|
get el() { return getElement(this); }
|
|
10895
10938
|
static get style() { return contentCss; }
|
|
@@ -22826,6 +22869,8 @@ class Modal {
|
|
|
22826
22869
|
this.gestureAnimationDismissing = false;
|
|
22827
22870
|
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
22828
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 };
|
|
22829
22874
|
this.presented = false;
|
|
22830
22875
|
/** @internal */
|
|
22831
22876
|
this.hasController = false;
|
|
@@ -23016,7 +23061,8 @@ class Modal {
|
|
|
23016
23061
|
}
|
|
23017
23062
|
}
|
|
23018
23063
|
onWindowResize() {
|
|
23019
|
-
//
|
|
23064
|
+
// Invalidate safe-area cache on resize (device rotation may change values)
|
|
23065
|
+
this.cachedSafeAreas = undefined;
|
|
23020
23066
|
this.updateSafeAreaOverrides();
|
|
23021
23067
|
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
23022
23068
|
if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
@@ -23475,8 +23521,26 @@ class Modal {
|
|
|
23475
23521
|
*/
|
|
23476
23522
|
applyFullscreenSafeArea() {
|
|
23477
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;
|
|
23478
23538
|
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
23479
|
-
if (
|
|
23539
|
+
if (hasFooter) {
|
|
23540
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23541
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
23542
|
+
}
|
|
23543
|
+
else {
|
|
23480
23544
|
this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
23481
23545
|
this.wrapperEl.style.setProperty('box-sizing', 'border-box');
|
|
23482
23546
|
}
|
|
@@ -23494,21 +23558,26 @@ class Modal {
|
|
|
23494
23558
|
}
|
|
23495
23559
|
/**
|
|
23496
23560
|
* Gets the root safe-area values from the document element.
|
|
23497
|
-
*
|
|
23498
|
-
*/
|
|
23499
|
-
|
|
23500
|
-
|
|
23501
|
-
|
|
23502
|
-
|
|
23503
|
-
|
|
23504
|
-
|
|
23505
|
-
|
|
23506
|
-
|
|
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;
|
|
23507
23574
|
}
|
|
23508
23575
|
/**
|
|
23509
23576
|
* Updates safe-area CSS variable overrides based on whether the modal
|
|
23510
23577
|
* extends into each safe-area region. Called after animation
|
|
23511
23578
|
* and during gestures to handle dynamic position changes.
|
|
23579
|
+
*
|
|
23580
|
+
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
23512
23581
|
*/
|
|
23513
23582
|
updateSafeAreaOverrides() {
|
|
23514
23583
|
if (this.skipSafeAreaCoordinateDetection) {
|
|
@@ -23519,20 +23588,34 @@ class Modal {
|
|
|
23519
23588
|
return;
|
|
23520
23589
|
}
|
|
23521
23590
|
const rect = wrapper.getBoundingClientRect();
|
|
23522
|
-
const safeAreas = this.
|
|
23591
|
+
const safeAreas = this.getSafeAreaValues();
|
|
23523
23592
|
const extendsIntoTop = rect.top < safeAreas.top;
|
|
23524
23593
|
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
23525
23594
|
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
23526
23595
|
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
23596
|
+
// Only update DOM when state actually changes
|
|
23597
|
+
const prev = this.prevSafeAreaState;
|
|
23527
23598
|
const style = this.el.style;
|
|
23528
|
-
extendsIntoTop
|
|
23529
|
-
|
|
23530
|
-
|
|
23531
|
-
|
|
23532
|
-
|
|
23533
|
-
|
|
23534
|
-
|
|
23535
|
-
|
|
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
|
+
}
|
|
23536
23619
|
}
|
|
23537
23620
|
sheetOnDismiss() {
|
|
23538
23621
|
/**
|
|
@@ -23564,7 +23647,7 @@ class Modal {
|
|
|
23564
23647
|
* For example, `cancel` or `backdrop`.
|
|
23565
23648
|
*/
|
|
23566
23649
|
async dismiss(data, role) {
|
|
23567
|
-
var _a;
|
|
23650
|
+
var _a, _b;
|
|
23568
23651
|
if (this.gestureAnimationDismissing && role !== GESTURE) {
|
|
23569
23652
|
return false;
|
|
23570
23653
|
}
|
|
@@ -23626,8 +23709,23 @@ class Modal {
|
|
|
23626
23709
|
}
|
|
23627
23710
|
this.currentBreakpoint = undefined;
|
|
23628
23711
|
this.animation = undefined;
|
|
23629
|
-
// Reset safe-area
|
|
23712
|
+
// Reset safe-area state for potential re-presentation
|
|
23630
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');
|
|
23631
23729
|
unlock();
|
|
23632
23730
|
return dismissed;
|
|
23633
23731
|
}
|
|
@@ -23877,20 +23975,20 @@ class Modal {
|
|
|
23877
23975
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23878
23976
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
23879
23977
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
23880
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
23978
|
+
return (hAsync(Host, Object.assign({ key: '11cd16cc481093a38a327abdd94467be3f71718d', "no-router": true,
|
|
23881
23979
|
// Allow the modal to be navigable when the handle is focusable
|
|
23882
23980
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
23883
23981
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
23884
|
-
}, 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: '
|
|
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',
|
|
23885
23983
|
/*
|
|
23886
23984
|
role and aria-modal must be used on the
|
|
23887
23985
|
same element. They must also be set inside the
|
|
23888
23986
|
shadow DOM otherwise ion-button will not be highlighted
|
|
23889
23987
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
23890
23988
|
*/
|
|
23891
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '
|
|
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",
|
|
23892
23990
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
23893
|
-
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: '
|
|
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 }))));
|
|
23894
23992
|
}
|
|
23895
23993
|
get el() { return getElement(this); }
|
|
23896
23994
|
static get watchers() { return {
|