@ionic/core 8.7.17-dev.11767895575.16ea7cef → 8.7.17-dev.11767980394.1d9266a9
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 +111 -8
- package/components/ion-tab-bar.js +3 -23
- package/components/modal.js +213 -12
- package/components/popover.js +83 -11
- package/dist/cjs/ion-app_8.cjs.entry.js +122 -20
- package/dist/cjs/ion-modal.cjs.entry.js +213 -12
- package/dist/cjs/ion-popover.cjs.entry.js +83 -11
- package/dist/cjs/ion-tab-bar_2.cjs.entry.js +3 -23
- package/dist/collection/components/content/content.css +10 -0
- package/dist/collection/components/content/content.js +109 -6
- package/dist/collection/components/modal/gestures/sheet.js +3 -1
- package/dist/collection/components/modal/gestures/swipe-to-close.js +3 -1
- package/dist/collection/components/modal/modal.ios.css +0 -4
- package/dist/collection/components/modal/modal.js +205 -7
- package/dist/collection/components/modal/modal.md.css +0 -4
- package/dist/collection/components/popover/animations/ios.enter.js +21 -5
- package/dist/collection/components/popover/animations/md.enter.js +30 -5
- package/dist/collection/components/popover/utils.js +32 -1
- package/dist/collection/components/tab-bar/tab-bar.js +3 -23
- package/dist/docs.json +1 -1
- package/dist/esm/ion-app_8.entry.js +111 -9
- package/dist/esm/ion-modal.entry.js +213 -12
- package/dist/esm/ion-popover.entry.js +83 -11
- package/dist/esm/ion-tab-bar_2.entry.js +3 -23
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-7268efa5.entry.js +4 -0
- package/dist/ionic/p-968a55d1.entry.js +4 -0
- package/dist/ionic/p-b55c44c8.entry.js +4 -0
- package/dist/ionic/p-ec9ca3fe.entry.js +4 -0
- package/dist/types/components/content/content.d.ts +26 -0
- package/dist/types/components/modal/gestures/sheet.d.ts +1 -1
- package/dist/types/components/modal/gestures/swipe-to-close.d.ts +1 -1
- package/dist/types/components/modal/modal.d.ts +45 -0
- package/dist/types/components/popover/utils.d.ts +2 -0
- package/dist/types/components/tab-bar/tab-bar.d.ts +0 -1
- package/hydrate/index.js +395 -52
- package/hydrate/index.mjs +395 -52
- package/package.json +1 -1
- package/dist/ionic/p-172a579f.entry.js +0 -4
- package/dist/ionic/p-732b2fd6.entry.js +0 -4
- package/dist/ionic/p-91840a80.entry.js +0 -4
- package/dist/ionic/p-f9061316.entry.js +0 -4
package/hydrate/index.js
CHANGED
|
@@ -10499,7 +10499,7 @@ const isRTL$1 = (hostEl) => {
|
|
|
10499
10499
|
return (document === null || document === void 0 ? void 0 : document.dir.toLowerCase()) === 'rtl';
|
|
10500
10500
|
};
|
|
10501
10501
|
|
|
10502
|
-
const contentCss = ":host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:\"\"}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}::slotted([slot=fixed]){position:absolute;-webkit-transform:translateZ(0);transform:translateZ(0)}";
|
|
10502
|
+
const contentCss = ":host{--background:var(--ion-background-color, #fff);--color:var(--ion-text-color, #000);--padding-top:0px;--padding-bottom:0px;--padding-start:0px;--padding-end:0px;--keyboard-offset:0px;--offset-top:0px;--offset-bottom:0px;--overflow:auto;display:block;position:relative;-ms-flex:1;flex:1;width:100%;height:100%;margin:0 !important;padding:0 !important;font-family:var(--ion-font-family, inherit);contain:size style}:host(.ion-color) .inner-scroll{background:var(--ion-color-base);color:var(--ion-color-contrast)}#background-content{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);position:absolute;background:var(--background)}.inner-scroll{left:0px;right:0px;top:calc(var(--offset-top) * -1);bottom:calc(var(--offset-bottom) * -1);-webkit-padding-start:var(--padding-start);padding-inline-start:var(--padding-start);-webkit-padding-end:var(--padding-end);padding-inline-end:var(--padding-end);padding-top:calc(var(--padding-top) + var(--offset-top));padding-bottom:calc(var(--padding-bottom) + var(--keyboard-offset) + var(--offset-bottom));position:absolute;color:var(--color);-webkit-box-sizing:border-box;box-sizing:border-box;overflow:hidden;-ms-touch-action:pan-x pan-y pinch-zoom;touch-action:pan-x pan-y pinch-zoom}.scroll-y,.scroll-x{-webkit-overflow-scrolling:touch;z-index:0;will-change:scroll-position}.scroll-y{overflow-y:var(--overflow);overscroll-behavior-y:contain}.scroll-x{overflow-x:var(--overflow);overscroll-behavior-x:contain}.overscroll::before,.overscroll::after{position:absolute;width:1px;height:1px;content:\"\"}.overscroll::before{bottom:-1px}.overscroll::after{top:-1px}:host(.content-sizing){display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-height:0;contain:none}:host(.content-sizing) .inner-scroll{position:relative;top:0;bottom:0;margin-top:calc(var(--offset-top) * -1);margin-bottom:calc(var(--offset-bottom) * -1)}.transition-effect{display:none;position:absolute;width:100%;height:100vh;opacity:0;pointer-events:none}:host(.content-ltr) .transition-effect{left:-100%;}:host(.content-rtl) .transition-effect{right:-100%;}.transition-cover{position:absolute;right:0;width:100%;height:100%;background:black;opacity:0.1}.transition-shadow{display:block;position:absolute;width:100%;height:100%;-webkit-box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03);box-shadow:inset -9px 0 9px 0 rgba(0, 0, 100, 0.03)}:host(.content-ltr) .transition-shadow{right:0;}:host(.content-rtl) .transition-shadow{left:0;-webkit-transform:scaleX(-1);transform:scaleX(-1)}: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)}";
|
|
10503
10503
|
|
|
10504
10504
|
/**
|
|
10505
10505
|
* @slot - Content is placed in the scrollable area if provided without a slot.
|
|
@@ -10523,6 +10523,12 @@ class Content {
|
|
|
10523
10523
|
this.isMainContent = true;
|
|
10524
10524
|
this.resizeTimeout = null;
|
|
10525
10525
|
this.inheritedAttributes = {};
|
|
10526
|
+
/**
|
|
10527
|
+
* Track whether this content has sibling header/footer elements.
|
|
10528
|
+
* When absent, we need to apply safe-area padding directly.
|
|
10529
|
+
*/
|
|
10530
|
+
this.hasHeader = false;
|
|
10531
|
+
this.hasFooter = false;
|
|
10526
10532
|
this.tabsElement = null;
|
|
10527
10533
|
// Detail is used in a hot loop in the scroll event, by allocating it here
|
|
10528
10534
|
// V8 will be able to inline any read/write to it since it's a monomorphic class.
|
|
@@ -10577,7 +10583,13 @@ class Content {
|
|
|
10577
10583
|
this.inheritedAttributes = inheritAriaAttributes(this.el);
|
|
10578
10584
|
}
|
|
10579
10585
|
connectedCallback() {
|
|
10580
|
-
|
|
10586
|
+
var _a;
|
|
10587
|
+
// Content is "main" if not inside menu/popover/modal and not nested in another ion-content
|
|
10588
|
+
this.isMainContent =
|
|
10589
|
+
this.el.closest('ion-menu, ion-popover, ion-modal') === null &&
|
|
10590
|
+
((_a = this.el.parentElement) === null || _a === void 0 ? void 0 : _a.closest('ion-content')) === null;
|
|
10591
|
+
// Detect sibling header/footer for safe-area handling
|
|
10592
|
+
this.detectSiblingElements();
|
|
10581
10593
|
/**
|
|
10582
10594
|
* The fullscreen content offsets need to be
|
|
10583
10595
|
* computed after the tab bar has loaded. Since
|
|
@@ -10608,13 +10620,89 @@ class Content {
|
|
|
10608
10620
|
* bubbles, we can catch any instances of child tab bars loading by listening
|
|
10609
10621
|
* on IonTabs.
|
|
10610
10622
|
*/
|
|
10611
|
-
this.tabsLoadCallback = () =>
|
|
10623
|
+
this.tabsLoadCallback = () => {
|
|
10624
|
+
this.resize();
|
|
10625
|
+
// Re-detect footer when tab bar loads (it may not exist during initial detection)
|
|
10626
|
+
this.updateSiblingDetection();
|
|
10627
|
+
};
|
|
10612
10628
|
closestTabs.addEventListener('ionTabBarLoaded', this.tabsLoadCallback);
|
|
10613
10629
|
}
|
|
10614
10630
|
}
|
|
10615
10631
|
}
|
|
10632
|
+
/**
|
|
10633
|
+
* Detects sibling ion-header and ion-footer elements and sets up
|
|
10634
|
+
* a mutation observer to handle dynamic changes (e.g., conditional rendering).
|
|
10635
|
+
*/
|
|
10636
|
+
detectSiblingElements() {
|
|
10637
|
+
this.updateSiblingDetection();
|
|
10638
|
+
// Watch for dynamic header/footer changes (common in React conditional rendering)
|
|
10639
|
+
const parent = this.el.parentElement;
|
|
10640
|
+
if (parent && !this.parentMutationObserver && win$1 !== undefined && 'MutationObserver' in win$1) {
|
|
10641
|
+
this.parentMutationObserver = new MutationObserver(() => {
|
|
10642
|
+
this.updateSiblingDetection();
|
|
10643
|
+
});
|
|
10644
|
+
this.parentMutationObserver.observe(parent, { childList: true });
|
|
10645
|
+
}
|
|
10646
|
+
// Watch for dynamic tab bar changes in ion-tabs (common in Angular conditional rendering)
|
|
10647
|
+
const tabs = this.el.closest('ion-tabs');
|
|
10648
|
+
if (tabs && !this.tabsMutationObserver && win$1 !== undefined && 'MutationObserver' in win$1) {
|
|
10649
|
+
this.tabsMutationObserver = new MutationObserver(() => {
|
|
10650
|
+
this.updateSiblingDetection();
|
|
10651
|
+
});
|
|
10652
|
+
this.tabsMutationObserver.observe(tabs, { childList: true });
|
|
10653
|
+
}
|
|
10654
|
+
}
|
|
10655
|
+
/**
|
|
10656
|
+
* Updates hasHeader/hasFooter based on current DOM state.
|
|
10657
|
+
* Checks both direct siblings and elements wrapped in custom components
|
|
10658
|
+
* (e.g., <my-header><ion-header>...</ion-header></my-header>).
|
|
10659
|
+
*/
|
|
10660
|
+
updateSiblingDetection() {
|
|
10661
|
+
const parent = this.el.parentElement;
|
|
10662
|
+
if (parent) {
|
|
10663
|
+
// First check for direct ion-header/ion-footer siblings
|
|
10664
|
+
this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
|
|
10665
|
+
this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
|
|
10666
|
+
// If not found, check if any sibling contains them (wrapped components)
|
|
10667
|
+
if (!this.hasHeader) {
|
|
10668
|
+
this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
|
|
10669
|
+
}
|
|
10670
|
+
if (!this.hasFooter) {
|
|
10671
|
+
this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
// If no footer found, check if we're inside ion-tabs which has ion-tab-bar
|
|
10675
|
+
if (!this.hasFooter) {
|
|
10676
|
+
const tabs = this.el.closest('ion-tabs');
|
|
10677
|
+
if (tabs) {
|
|
10678
|
+
this.hasFooter = tabs.querySelector(':scope > ion-tab-bar') !== null;
|
|
10679
|
+
}
|
|
10680
|
+
}
|
|
10681
|
+
}
|
|
10682
|
+
/**
|
|
10683
|
+
* Checks if any sibling element of ion-content contains the specified element.
|
|
10684
|
+
* Only searches one level deep to avoid finding elements in nested pages.
|
|
10685
|
+
*/
|
|
10686
|
+
siblingContainsElement(parent, tagName) {
|
|
10687
|
+
for (const sibling of parent.children) {
|
|
10688
|
+
// Skip ion-content itself
|
|
10689
|
+
if (sibling === this.el)
|
|
10690
|
+
continue;
|
|
10691
|
+
// Check if this sibling contains the target element as an immediate child
|
|
10692
|
+
if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
|
|
10693
|
+
return true;
|
|
10694
|
+
}
|
|
10695
|
+
}
|
|
10696
|
+
return false;
|
|
10697
|
+
}
|
|
10616
10698
|
disconnectedCallback() {
|
|
10699
|
+
var _a, _b;
|
|
10617
10700
|
this.onScrollEnd();
|
|
10701
|
+
// Clean up mutation observers to prevent memory leaks
|
|
10702
|
+
(_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
10703
|
+
this.parentMutationObserver = undefined;
|
|
10704
|
+
(_b = this.tabsMutationObserver) === null || _b === void 0 ? void 0 : _b.disconnect();
|
|
10705
|
+
this.tabsMutationObserver = undefined;
|
|
10618
10706
|
if (hasLazyBuild(this.el)) {
|
|
10619
10707
|
/**
|
|
10620
10708
|
* The event listener and tabs caches need to
|
|
@@ -10843,26 +10931,28 @@ class Content {
|
|
|
10843
10931
|
}
|
|
10844
10932
|
}
|
|
10845
10933
|
render() {
|
|
10846
|
-
const { fixedSlotPlacement, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
|
|
10934
|
+
const { fixedSlotPlacement, hasFooter, hasHeader, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
|
|
10847
10935
|
const rtl = isRTL$1(el) ? 'rtl' : 'ltr';
|
|
10848
10936
|
const mode = getIonMode$1(this);
|
|
10849
10937
|
const forceOverscroll = this.shouldForceOverscroll();
|
|
10850
10938
|
const transitionShadow = mode === 'ios';
|
|
10851
10939
|
this.resize();
|
|
10852
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
10940
|
+
return (hAsync(Host, Object.assign({ key: '8b40855d7b3257fac16b0c945dc54b75a8a36b1f', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
|
|
10853
10941
|
[mode]: true,
|
|
10854
10942
|
'content-sizing': hostContext('ion-popover', this.el),
|
|
10855
10943
|
overscroll: forceOverscroll,
|
|
10856
10944
|
[`content-${rtl}`]: true,
|
|
10945
|
+
'safe-area-top': isMainContent && !hasHeader,
|
|
10946
|
+
'safe-area-bottom': isMainContent && !hasFooter,
|
|
10857
10947
|
}), style: {
|
|
10858
10948
|
'--offset-top': `${this.cTop}px`,
|
|
10859
10949
|
'--offset-bottom': `${this.cBottom}px`,
|
|
10860
|
-
} }, inheritedAttributes), hAsync("div", { key: '
|
|
10950
|
+
} }, inheritedAttributes), hAsync("div", { key: '1ec3de99f33ed7a252a637c14887043c96046536', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? hAsync("slot", { name: "fixed" }) : null, hAsync("div", { key: 'dae33d14c489a55fcb6eceabbec3cb3b04726f34', class: {
|
|
10861
10951
|
'inner-scroll': true,
|
|
10862
10952
|
'scroll-x': scrollX,
|
|
10863
10953
|
'scroll-y': scrollY,
|
|
10864
10954
|
overscroll: (scrollX || scrollY) && forceOverscroll,
|
|
10865
|
-
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '
|
|
10955
|
+
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: 'e99873d5fcc573e5d6ccad0f7c79192e99f4b749' })), transitionShadow ? (hAsync("div", { class: "transition-effect" }, hAsync("div", { class: "transition-cover" }), hAsync("div", { class: "transition-shadow" }))) : null, fixedSlotPlacement === 'after' ? hAsync("slot", { name: "fixed" }) : null));
|
|
10866
10956
|
}
|
|
10867
10957
|
get el() { return getElement(this); }
|
|
10868
10958
|
static get style() { return contentCss; }
|
|
@@ -21533,7 +21623,7 @@ const calculateSpringStep = (t) => {
|
|
|
21533
21623
|
const SwipeToCloseDefaults = {
|
|
21534
21624
|
MIN_PRESENTING_SCALE: 0.915,
|
|
21535
21625
|
};
|
|
21536
|
-
const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
|
|
21626
|
+
const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
|
|
21537
21627
|
/**
|
|
21538
21628
|
* The step value at which a card modal
|
|
21539
21629
|
* is eligible for dismissing via gesture.
|
|
@@ -21690,6 +21780,8 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) =>
|
|
|
21690
21780
|
const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
|
|
21691
21781
|
const clampedStep = clamp(0.0001, processedStep, maxStep);
|
|
21692
21782
|
animation.progressStep(clampedStep);
|
|
21783
|
+
// Notify modal of position change for safe-area updates
|
|
21784
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
21693
21785
|
/**
|
|
21694
21786
|
* When swiping down half way, the status bar style
|
|
21695
21787
|
* should be reset to its default value.
|
|
@@ -22233,7 +22325,7 @@ const mdLeaveAnimation$2 = (baseEl, opts) => {
|
|
|
22233
22325
|
return baseAnimation;
|
|
22234
22326
|
};
|
|
22235
22327
|
|
|
22236
|
-
const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
|
|
22328
|
+
const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
|
|
22237
22329
|
// Defaults for the sheet swipe animation
|
|
22238
22330
|
const defaultBackdrop = [
|
|
22239
22331
|
{ offset: 0, opacity: 'var(--backdrop-opacity)' },
|
|
@@ -22564,6 +22656,8 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
22564
22656
|
: step;
|
|
22565
22657
|
offset = clamp(0.0001, processedStep, maxStep);
|
|
22566
22658
|
animation.progressStep(offset);
|
|
22659
|
+
// Notify modal of position change for safe-area updates
|
|
22660
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
22567
22661
|
};
|
|
22568
22662
|
const onEnd = (detail) => {
|
|
22569
22663
|
/**
|
|
@@ -22758,9 +22852,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
22758
22852
|
};
|
|
22759
22853
|
};
|
|
22760
22854
|
|
|
22761
|
-
const modalIosCss = ":host{--width:100%;--min-width:auto;--max-width:auto;--height:100%;--min-height:auto;--max-height:auto;--overflow:hidden;--border-radius:0;--border-width:0;--border-style:none;--border-color:transparent;--background:var(--ion-background-color, #fff);--box-shadow:none;--backdrop-opacity:0;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);contain:strict}.modal-wrapper,ion-backdrop{pointer-events:auto}:host(.overlay-hidden){display:none}.modal-wrapper,.modal-shadow{border-radius:var(--border-radius);width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:var(--overflow);z-index:10}.modal-shadow{position:absolute;background:transparent}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--width:600px;--height:500px
|
|
22855
|
+
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}";
|
|
22762
22856
|
|
|
22763
|
-
const modalMdCss = ":host{--width:100%;--min-width:auto;--max-width:auto;--height:100%;--min-height:auto;--max-height:auto;--overflow:hidden;--border-radius:0;--border-width:0;--border-style:none;--border-color:transparent;--background:var(--ion-background-color, #fff);--box-shadow:none;--backdrop-opacity:0;left:0;right:0;top:0;bottom:0;display:-ms-flexbox;display:flex;position:absolute;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;outline:none;color:var(--ion-text-color, #000);contain:strict}.modal-wrapper,ion-backdrop{pointer-events:auto}:host(.overlay-hidden){display:none}.modal-wrapper,.modal-shadow{border-radius:var(--border-radius);width:var(--width);min-width:var(--min-width);max-width:var(--max-width);height:var(--height);min-height:var(--min-height);max-height:var(--max-height);border-width:var(--border-width);border-style:var(--border-style);border-color:var(--border-color);background:var(--background);-webkit-box-shadow:var(--box-shadow);box-shadow:var(--box-shadow);overflow:var(--overflow);z-index:10}.modal-shadow{position:absolute;background:transparent}@media only screen and (min-width: 768px) and (min-height: 600px){:host{--width:600px;--height:500px
|
|
22857
|
+
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}";
|
|
22764
22858
|
|
|
22765
22859
|
// TODO(FW-2832): types
|
|
22766
22860
|
/**
|
|
@@ -22793,6 +22887,10 @@ class Modal {
|
|
|
22793
22887
|
this.inline = false;
|
|
22794
22888
|
// Whether or not modal is being dismissed via gesture
|
|
22795
22889
|
this.gestureAnimationDismissing = false;
|
|
22890
|
+
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
22891
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
22892
|
+
// Track previous safe-area state to avoid redundant DOM writes
|
|
22893
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
22796
22894
|
this.presented = false;
|
|
22797
22895
|
/** @internal */
|
|
22798
22896
|
this.hasController = false;
|
|
@@ -22983,7 +23081,10 @@ class Modal {
|
|
|
22983
23081
|
}
|
|
22984
23082
|
}
|
|
22985
23083
|
onWindowResize() {
|
|
22986
|
-
//
|
|
23084
|
+
// Invalidate safe-area cache on resize (device rotation may change values)
|
|
23085
|
+
this.cachedSafeAreas = undefined;
|
|
23086
|
+
this.updateSafeAreaOverrides();
|
|
23087
|
+
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
22987
23088
|
if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
22988
23089
|
return;
|
|
22989
23090
|
}
|
|
@@ -23006,6 +23107,8 @@ class Modal {
|
|
|
23006
23107
|
this.triggerController.removeClickListener();
|
|
23007
23108
|
this.cleanupViewTransitionListener();
|
|
23008
23109
|
this.cleanupParentRemovalObserver();
|
|
23110
|
+
// Reset safe-area state to handle removal without dismiss (e.g., framework unmount)
|
|
23111
|
+
this.resetSafeAreaState();
|
|
23009
23112
|
}
|
|
23010
23113
|
componentWillLoad() {
|
|
23011
23114
|
var _a;
|
|
@@ -23165,6 +23268,8 @@ class Modal {
|
|
|
23165
23268
|
else if (!this.keepContentsMounted) {
|
|
23166
23269
|
await waitForMount();
|
|
23167
23270
|
}
|
|
23271
|
+
// Predict safe-area needs based on modal configuration to avoid visual snap
|
|
23272
|
+
this.setInitialSafeAreaOverrides(presentingElement);
|
|
23168
23273
|
writeTask(() => this.el.classList.add('show-modal'));
|
|
23169
23274
|
const hasCardModal = presentingElement !== undefined;
|
|
23170
23275
|
/**
|
|
@@ -23226,6 +23331,8 @@ class Modal {
|
|
|
23226
23331
|
else if (hasCardModal) {
|
|
23227
23332
|
this.initSwipeToClose();
|
|
23228
23333
|
}
|
|
23334
|
+
// Now that animation is complete, update safe-area based on actual position
|
|
23335
|
+
this.updateSafeAreaOverrides();
|
|
23229
23336
|
// Initialize view transition listener for iOS card modals
|
|
23230
23337
|
this.initViewTransitionListener();
|
|
23231
23338
|
// Initialize parent removal observer
|
|
@@ -23277,7 +23384,7 @@ class Modal {
|
|
|
23277
23384
|
await this.dismiss(undefined, GESTURE);
|
|
23278
23385
|
this.gestureAnimationDismissing = false;
|
|
23279
23386
|
});
|
|
23280
|
-
});
|
|
23387
|
+
}, () => this.updateSafeAreaOverrides());
|
|
23281
23388
|
this.gesture.enable(true);
|
|
23282
23389
|
}
|
|
23283
23390
|
initSheetGesture() {
|
|
@@ -23298,7 +23405,8 @@ class Modal {
|
|
|
23298
23405
|
this.currentBreakpoint = breakpoint;
|
|
23299
23406
|
this.ionBreakpointDidChange.emit({ breakpoint });
|
|
23300
23407
|
}
|
|
23301
|
-
|
|
23408
|
+
this.updateSafeAreaOverrides();
|
|
23409
|
+
}, () => this.updateSafeAreaOverrides());
|
|
23302
23410
|
this.gesture = gesture;
|
|
23303
23411
|
this.moveSheetToBreakpoint = moveSheetToBreakpoint;
|
|
23304
23412
|
this.gesture.enable(true);
|
|
@@ -23376,6 +23484,187 @@ class Modal {
|
|
|
23376
23484
|
// Clear the cached reference
|
|
23377
23485
|
this.cachedPageParent = undefined;
|
|
23378
23486
|
}
|
|
23487
|
+
/**
|
|
23488
|
+
* Sets initial safe-area overrides based on modal configuration before
|
|
23489
|
+
* the modal becomes visible. This predicts whether the modal will touch
|
|
23490
|
+
* screen edges to avoid a visual snap after animation completes.
|
|
23491
|
+
*/
|
|
23492
|
+
setInitialSafeAreaOverrides(presentingElement) {
|
|
23493
|
+
const style = this.el.style;
|
|
23494
|
+
const mode = getIonMode$1(this);
|
|
23495
|
+
const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
|
|
23496
|
+
// Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
|
|
23497
|
+
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23498
|
+
const isTablet = window.innerWidth >= 768;
|
|
23499
|
+
// Sheet modals always touch bottom edge, never top/left/right
|
|
23500
|
+
if (isSheetModal) {
|
|
23501
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
23502
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
23503
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
23504
|
+
return;
|
|
23505
|
+
}
|
|
23506
|
+
// Card modals have rounded top corners
|
|
23507
|
+
if (isCardModal) {
|
|
23508
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
23509
|
+
if (isTablet) {
|
|
23510
|
+
// On tablets, card modals are inset from all edges
|
|
23511
|
+
this.zeroAllSafeAreas();
|
|
23512
|
+
}
|
|
23513
|
+
else {
|
|
23514
|
+
// On phones, card modals still extend to the bottom edge
|
|
23515
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
23516
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
23517
|
+
this.applyFullscreenSafeArea();
|
|
23518
|
+
}
|
|
23519
|
+
return;
|
|
23520
|
+
}
|
|
23521
|
+
// Phone-sized fullscreen modals inherit safe areas and use wrapper padding
|
|
23522
|
+
if (!isTablet) {
|
|
23523
|
+
this.applyFullscreenSafeArea();
|
|
23524
|
+
return;
|
|
23525
|
+
}
|
|
23526
|
+
// Check if tablet modal is fullscreen via CSS custom properties
|
|
23527
|
+
const computedStyle = getComputedStyle(this.el);
|
|
23528
|
+
const width = computedStyle.getPropertyValue('--width').trim();
|
|
23529
|
+
const height = computedStyle.getPropertyValue('--height').trim();
|
|
23530
|
+
const isFullscreen = width === '100%' && height === '100%';
|
|
23531
|
+
if (isFullscreen) {
|
|
23532
|
+
this.applyFullscreenSafeArea();
|
|
23533
|
+
}
|
|
23534
|
+
else {
|
|
23535
|
+
// Centered dialog doesn't touch edges
|
|
23536
|
+
this.zeroAllSafeAreas();
|
|
23537
|
+
}
|
|
23538
|
+
}
|
|
23539
|
+
/**
|
|
23540
|
+
* Applies safe-area handling for fullscreen modals.
|
|
23541
|
+
* Adds wrapper padding when no footer is present to prevent
|
|
23542
|
+
* content from overlapping system navigation areas.
|
|
23543
|
+
*/
|
|
23544
|
+
applyFullscreenSafeArea() {
|
|
23545
|
+
this.skipSafeAreaCoordinateDetection = true;
|
|
23546
|
+
this.updateFooterPadding();
|
|
23547
|
+
// Watch for dynamic footer additions/removals (e.g., async data loading)
|
|
23548
|
+
// Use subtree:true to support wrapped footers in framework components
|
|
23549
|
+
// (e.g., <my-footer><ion-footer>...</ion-footer></my-footer>)
|
|
23550
|
+
if (!this.footerObserver && win$1 !== undefined && 'MutationObserver' in win$1) {
|
|
23551
|
+
this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
|
|
23552
|
+
this.footerObserver.observe(this.el, { childList: true, subtree: true });
|
|
23553
|
+
}
|
|
23554
|
+
}
|
|
23555
|
+
/**
|
|
23556
|
+
* Updates wrapper padding based on footer presence.
|
|
23557
|
+
* Called initially and when footer is dynamically added/removed.
|
|
23558
|
+
*/
|
|
23559
|
+
updateFooterPadding() {
|
|
23560
|
+
if (!this.wrapperEl)
|
|
23561
|
+
return;
|
|
23562
|
+
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
23563
|
+
if (hasFooter) {
|
|
23564
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23565
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
23566
|
+
}
|
|
23567
|
+
else {
|
|
23568
|
+
this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
23569
|
+
this.wrapperEl.style.setProperty('box-sizing', 'border-box');
|
|
23570
|
+
}
|
|
23571
|
+
}
|
|
23572
|
+
/**
|
|
23573
|
+
* Sets all safe-area CSS variables to 0px for modals that
|
|
23574
|
+
* don't touch screen edges.
|
|
23575
|
+
*/
|
|
23576
|
+
zeroAllSafeAreas() {
|
|
23577
|
+
const style = this.el.style;
|
|
23578
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
23579
|
+
style.setProperty('--ion-safe-area-bottom', '0px');
|
|
23580
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
23581
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
23582
|
+
}
|
|
23583
|
+
/**
|
|
23584
|
+
* Resets all safe-area related state and styles.
|
|
23585
|
+
* Called during dismiss and disconnectedCallback to ensure clean state
|
|
23586
|
+
* for re-presentation of inline modals.
|
|
23587
|
+
*/
|
|
23588
|
+
resetSafeAreaState() {
|
|
23589
|
+
var _a;
|
|
23590
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
23591
|
+
this.cachedSafeAreas = undefined;
|
|
23592
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
23593
|
+
(_a = this.footerObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
23594
|
+
this.footerObserver = undefined;
|
|
23595
|
+
// Clear wrapper styles that may have been set for safe-area handling
|
|
23596
|
+
if (this.wrapperEl) {
|
|
23597
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23598
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
23599
|
+
}
|
|
23600
|
+
// Clear safe-area CSS variable overrides
|
|
23601
|
+
const style = this.el.style;
|
|
23602
|
+
style.removeProperty('--ion-safe-area-top');
|
|
23603
|
+
style.removeProperty('--ion-safe-area-bottom');
|
|
23604
|
+
style.removeProperty('--ion-safe-area-left');
|
|
23605
|
+
style.removeProperty('--ion-safe-area-right');
|
|
23606
|
+
}
|
|
23607
|
+
/**
|
|
23608
|
+
* Gets the root safe-area values from the document element.
|
|
23609
|
+
* Uses cached values during gestures to avoid getComputedStyle calls.
|
|
23610
|
+
*/
|
|
23611
|
+
getSafeAreaValues() {
|
|
23612
|
+
if (!this.cachedSafeAreas) {
|
|
23613
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
23614
|
+
this.cachedSafeAreas = {
|
|
23615
|
+
top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
|
|
23616
|
+
bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
|
|
23617
|
+
left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
|
|
23618
|
+
right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
|
|
23619
|
+
};
|
|
23620
|
+
}
|
|
23621
|
+
return this.cachedSafeAreas;
|
|
23622
|
+
}
|
|
23623
|
+
/**
|
|
23624
|
+
* Updates safe-area CSS variable overrides based on whether the modal
|
|
23625
|
+
* extends into each safe-area region. Called after animation
|
|
23626
|
+
* and during gestures to handle dynamic position changes.
|
|
23627
|
+
*
|
|
23628
|
+
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
23629
|
+
*/
|
|
23630
|
+
updateSafeAreaOverrides() {
|
|
23631
|
+
if (this.skipSafeAreaCoordinateDetection) {
|
|
23632
|
+
return;
|
|
23633
|
+
}
|
|
23634
|
+
const wrapper = this.wrapperEl;
|
|
23635
|
+
if (!wrapper) {
|
|
23636
|
+
return;
|
|
23637
|
+
}
|
|
23638
|
+
const rect = wrapper.getBoundingClientRect();
|
|
23639
|
+
const safeAreas = this.getSafeAreaValues();
|
|
23640
|
+
const extendsIntoTop = rect.top < safeAreas.top;
|
|
23641
|
+
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
23642
|
+
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
23643
|
+
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
23644
|
+
// Only update DOM when state actually changes
|
|
23645
|
+
const prev = this.prevSafeAreaState;
|
|
23646
|
+
const style = this.el.style;
|
|
23647
|
+
if (extendsIntoTop !== prev.top) {
|
|
23648
|
+
extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
|
|
23649
|
+
prev.top = extendsIntoTop;
|
|
23650
|
+
}
|
|
23651
|
+
if (extendsIntoBottom !== prev.bottom) {
|
|
23652
|
+
extendsIntoBottom
|
|
23653
|
+
? style.removeProperty('--ion-safe-area-bottom')
|
|
23654
|
+
: style.setProperty('--ion-safe-area-bottom', '0px');
|
|
23655
|
+
prev.bottom = extendsIntoBottom;
|
|
23656
|
+
}
|
|
23657
|
+
if (extendsIntoLeft !== prev.left) {
|
|
23658
|
+
extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
|
|
23659
|
+
prev.left = extendsIntoLeft;
|
|
23660
|
+
}
|
|
23661
|
+
if (extendsIntoRight !== prev.right) {
|
|
23662
|
+
extendsIntoRight
|
|
23663
|
+
? style.removeProperty('--ion-safe-area-right')
|
|
23664
|
+
: style.setProperty('--ion-safe-area-right', '0px');
|
|
23665
|
+
prev.right = extendsIntoRight;
|
|
23666
|
+
}
|
|
23667
|
+
}
|
|
23379
23668
|
sheetOnDismiss() {
|
|
23380
23669
|
/**
|
|
23381
23670
|
* While the gesture animation is finishing
|
|
@@ -23468,6 +23757,8 @@ class Modal {
|
|
|
23468
23757
|
}
|
|
23469
23758
|
this.currentBreakpoint = undefined;
|
|
23470
23759
|
this.animation = undefined;
|
|
23760
|
+
// Reset safe-area state for potential re-presentation
|
|
23761
|
+
this.resetSafeAreaState();
|
|
23471
23762
|
unlock();
|
|
23472
23763
|
return dismissed;
|
|
23473
23764
|
}
|
|
@@ -23717,20 +24008,20 @@ class Modal {
|
|
|
23717
24008
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23718
24009
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
23719
24010
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
23720
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
24011
|
+
return (hAsync(Host, Object.assign({ key: '44022099fcaf047b97d1c2cb45b9b51c930e707c', "no-router": true,
|
|
23721
24012
|
// Allow the modal to be navigable when the handle is focusable
|
|
23722
24013
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
23723
24014
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
23724
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: '
|
|
24015
|
+
}, 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: 'ddd7e4f6eef51ac1f62ac70e0af10fb01e707f07', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: '58620980e3e4ec273c6787bde026e1c010b904b7', class: "modal-shadow" }), hAsync("div", Object.assign({ key: '3fb7f6218644ba898fc504467775593eb89426a0',
|
|
23725
24016
|
/*
|
|
23726
24017
|
role and aria-modal must be used on the
|
|
23727
24018
|
same element. They must also be set inside the
|
|
23728
24019
|
shadow DOM otherwise ion-button will not be highlighted
|
|
23729
24020
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
23730
24021
|
*/
|
|
23731
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '
|
|
24022
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '9745cd590fdaa9d023a14b487ec2c87ddbafd7f7', class: "modal-handle",
|
|
23732
24023
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
23733
|
-
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: '
|
|
24024
|
+
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: 'b9a8b5d2d3d3c9b06f99179f496c9f08907d0bad', onSlotchange: this.onSlotChange }))));
|
|
23734
24025
|
}
|
|
23735
24026
|
get el() { return getElement(this); }
|
|
23736
24027
|
static get watchers() { return {
|
|
@@ -27308,6 +27599,8 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27308
27599
|
let bottom;
|
|
27309
27600
|
let originX = contentOriginX;
|
|
27310
27601
|
let originY = contentOriginY;
|
|
27602
|
+
let checkSafeAreaTop = false;
|
|
27603
|
+
let checkSafeAreaBottom = false;
|
|
27311
27604
|
let checkSafeAreaLeft = false;
|
|
27312
27605
|
let checkSafeAreaRight = false;
|
|
27313
27606
|
const triggerTop = triggerCoordinates
|
|
@@ -27352,10 +27645,18 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27352
27645
|
* We chose 12 here so that the popover position looks a bit nicer as
|
|
27353
27646
|
* it is not right up against the edge of the screen.
|
|
27354
27647
|
*/
|
|
27355
|
-
top = Math.max(
|
|
27648
|
+
top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
|
|
27356
27649
|
arrowTop = top + contentHeight;
|
|
27357
27650
|
originY = 'bottom';
|
|
27358
27651
|
addPopoverBottomClass = true;
|
|
27652
|
+
/**
|
|
27653
|
+
* If the popover is positioned near the top edge, account for safe area.
|
|
27654
|
+
* This ensures the popover doesn't overlap with status bars or notches.
|
|
27655
|
+
*/
|
|
27656
|
+
if (top <= bodyPadding + safeAreaMargin) {
|
|
27657
|
+
checkSafeAreaTop = true;
|
|
27658
|
+
top = bodyPadding;
|
|
27659
|
+
}
|
|
27359
27660
|
/**
|
|
27360
27661
|
* If not enough room for popover to appear
|
|
27361
27662
|
* above trigger, then cut it off.
|
|
@@ -27363,14 +27664,35 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27363
27664
|
}
|
|
27364
27665
|
else {
|
|
27365
27666
|
bottom = bodyPadding;
|
|
27667
|
+
/**
|
|
27668
|
+
* When the popover is pinned to the bottom, account for safe area.
|
|
27669
|
+
* This ensures the popover doesn't overlap with home indicators
|
|
27670
|
+
* or navigation bars (e.g., Android API 36+ edge-to-edge).
|
|
27671
|
+
*/
|
|
27672
|
+
checkSafeAreaBottom = true;
|
|
27366
27673
|
}
|
|
27367
27674
|
}
|
|
27675
|
+
/**
|
|
27676
|
+
* Final check: If the popover extends into any safe-area region,
|
|
27677
|
+
* ensure the corresponding flag is set regardless of side.
|
|
27678
|
+
* This handles cases where a side-positioned popover (left/right)
|
|
27679
|
+
* still needs bottom safe-area padding because it extends into that region.
|
|
27680
|
+
*/
|
|
27681
|
+
const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
|
|
27682
|
+
if (popoverBottom + safeAreaMargin > bodyHeight) {
|
|
27683
|
+
checkSafeAreaBottom = true;
|
|
27684
|
+
}
|
|
27685
|
+
if (top < safeAreaMargin) {
|
|
27686
|
+
checkSafeAreaTop = true;
|
|
27687
|
+
}
|
|
27368
27688
|
return {
|
|
27369
27689
|
top,
|
|
27370
27690
|
left,
|
|
27371
27691
|
bottom,
|
|
27372
27692
|
originX,
|
|
27373
27693
|
originY,
|
|
27694
|
+
checkSafeAreaTop,
|
|
27695
|
+
checkSafeAreaBottom,
|
|
27374
27696
|
checkSafeAreaLeft,
|
|
27375
27697
|
checkSafeAreaRight,
|
|
27376
27698
|
arrowTop,
|
|
@@ -27431,7 +27753,7 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27431
27753
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
27432
27754
|
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
27433
27755
|
const margin = size === 'cover' ? 0 : 25;
|
|
27434
|
-
const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
|
|
27756
|
+
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);
|
|
27435
27757
|
const baseAnimation = createAnimation();
|
|
27436
27758
|
const backdropAnimation = createAnimation();
|
|
27437
27759
|
const contentAnimation = createAnimation();
|
|
@@ -27461,19 +27783,35 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27461
27783
|
if (addPopoverBottomClass) {
|
|
27462
27784
|
baseEl.classList.add('popover-bottom');
|
|
27463
27785
|
}
|
|
27464
|
-
|
|
27465
|
-
|
|
27466
|
-
|
|
27786
|
+
/**
|
|
27787
|
+
* Safe area CSS variable adjustments.
|
|
27788
|
+
* When the popover is positioned near an edge, we add the corresponding
|
|
27789
|
+
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
27790
|
+
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
27791
|
+
*/
|
|
27792
|
+
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
27793
|
+
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
27467
27794
|
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
27468
27795
|
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
27796
|
+
let topValue = `${top}px`;
|
|
27797
|
+
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
27469
27798
|
let leftValue = `${left}px`;
|
|
27799
|
+
if (checkSafeAreaTop) {
|
|
27800
|
+
topValue = `${top}px${safeAreaTop}`;
|
|
27801
|
+
}
|
|
27802
|
+
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
27803
|
+
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
27804
|
+
}
|
|
27470
27805
|
if (checkSafeAreaLeft) {
|
|
27471
27806
|
leftValue = `${left}px${safeAreaLeft}`;
|
|
27472
27807
|
}
|
|
27473
27808
|
if (checkSafeAreaRight) {
|
|
27474
27809
|
leftValue = `${left}px${safeAreaRight}`;
|
|
27475
27810
|
}
|
|
27476
|
-
|
|
27811
|
+
if (bottomValue !== undefined) {
|
|
27812
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
27813
|
+
}
|
|
27814
|
+
contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
|
|
27477
27815
|
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
27478
27816
|
contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
|
|
27479
27817
|
if (arrowEl !== null) {
|
|
@@ -27549,7 +27887,32 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27549
27887
|
};
|
|
27550
27888
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
|
|
27551
27889
|
const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
|
|
27552
|
-
const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
|
|
27890
|
+
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);
|
|
27891
|
+
/**
|
|
27892
|
+
* Safe area CSS variable adjustments.
|
|
27893
|
+
* When the popover is positioned near an edge, we add the corresponding
|
|
27894
|
+
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
27895
|
+
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
27896
|
+
*/
|
|
27897
|
+
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
27898
|
+
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
27899
|
+
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
27900
|
+
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
27901
|
+
let topValue = `${top}px`;
|
|
27902
|
+
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
27903
|
+
let leftValue = `${left}px`;
|
|
27904
|
+
if (checkSafeAreaTop) {
|
|
27905
|
+
topValue = `${top}px${safeAreaTop}`;
|
|
27906
|
+
}
|
|
27907
|
+
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
27908
|
+
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
27909
|
+
}
|
|
27910
|
+
if (checkSafeAreaLeft) {
|
|
27911
|
+
leftValue = `${left}px${safeAreaLeft}`;
|
|
27912
|
+
}
|
|
27913
|
+
if (checkSafeAreaRight) {
|
|
27914
|
+
leftValue = `${left}px${safeAreaRight}`;
|
|
27915
|
+
}
|
|
27553
27916
|
const baseAnimation = createAnimation();
|
|
27554
27917
|
const backdropAnimation = createAnimation();
|
|
27555
27918
|
const wrapperAnimation = createAnimation();
|
|
@@ -27566,13 +27929,13 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27566
27929
|
contentAnimation
|
|
27567
27930
|
.addElement(contentEl)
|
|
27568
27931
|
.beforeStyles({
|
|
27569
|
-
top: `calc(${
|
|
27570
|
-
left: `calc(${
|
|
27932
|
+
top: `calc(${topValue} + var(--offset-y, 0px))`,
|
|
27933
|
+
left: `calc(${leftValue} + var(--offset-x, 0px))`,
|
|
27571
27934
|
'transform-origin': `${originY} ${originX}`,
|
|
27572
27935
|
})
|
|
27573
27936
|
.beforeAddWrite(() => {
|
|
27574
|
-
if (
|
|
27575
|
-
contentEl.style.setProperty('bottom',
|
|
27937
|
+
if (bottomValue !== undefined) {
|
|
27938
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
27576
27939
|
}
|
|
27577
27940
|
})
|
|
27578
27941
|
.fromTo('transform', 'scale(0.8)', 'scale(1)');
|
|
@@ -34990,7 +35353,6 @@ class TabBar {
|
|
|
34990
35353
|
this.ionTabBarChanged = createEvent(this, "ionTabBarChanged", 7);
|
|
34991
35354
|
this.ionTabBarLoaded = createEvent(this, "ionTabBarLoaded", 7);
|
|
34992
35355
|
this.keyboardCtrl = null;
|
|
34993
|
-
this.keyboardCtrlPromise = null;
|
|
34994
35356
|
this.didLoad = false;
|
|
34995
35357
|
this.keyboardVisible = false;
|
|
34996
35358
|
/**
|
|
@@ -35026,7 +35388,7 @@ class TabBar {
|
|
|
35026
35388
|
}
|
|
35027
35389
|
}
|
|
35028
35390
|
async connectedCallback() {
|
|
35029
|
-
|
|
35391
|
+
this.keyboardCtrl = await createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
35030
35392
|
/**
|
|
35031
35393
|
* If the keyboard is hiding, then we need to wait
|
|
35032
35394
|
* for the webview to resize. Otherwise, the tab bar
|
|
@@ -35037,40 +35399,21 @@ class TabBar {
|
|
|
35037
35399
|
}
|
|
35038
35400
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
35039
35401
|
});
|
|
35040
|
-
this.keyboardCtrlPromise = promise;
|
|
35041
|
-
const keyboardCtrl = await promise;
|
|
35042
|
-
/**
|
|
35043
|
-
* Only assign if this is still the current promise.
|
|
35044
|
-
* Otherwise, a new connectedCallback has started or
|
|
35045
|
-
* disconnectedCallback was called, so destroy this instance.
|
|
35046
|
-
*/
|
|
35047
|
-
if (this.keyboardCtrlPromise === promise) {
|
|
35048
|
-
this.keyboardCtrl = keyboardCtrl;
|
|
35049
|
-
this.keyboardCtrlPromise = null;
|
|
35050
|
-
}
|
|
35051
|
-
else {
|
|
35052
|
-
keyboardCtrl.destroy();
|
|
35053
|
-
}
|
|
35054
35402
|
}
|
|
35055
35403
|
disconnectedCallback() {
|
|
35056
|
-
if (this.keyboardCtrlPromise) {
|
|
35057
|
-
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
35058
|
-
this.keyboardCtrlPromise = null;
|
|
35059
|
-
}
|
|
35060
35404
|
if (this.keyboardCtrl) {
|
|
35061
35405
|
this.keyboardCtrl.destroy();
|
|
35062
|
-
this.keyboardCtrl = null;
|
|
35063
35406
|
}
|
|
35064
35407
|
}
|
|
35065
35408
|
render() {
|
|
35066
35409
|
const { color, translucent, keyboardVisible } = this;
|
|
35067
35410
|
const mode = getIonMode$1(this);
|
|
35068
35411
|
const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
|
|
35069
|
-
return (hAsync(Host, { key: '
|
|
35412
|
+
return (hAsync(Host, { key: '388ec37ce308035bab78d6c9a016bb616e9517a9', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses$1(color, {
|
|
35070
35413
|
[mode]: true,
|
|
35071
35414
|
'tab-bar-translucent': translucent,
|
|
35072
35415
|
'tab-bar-hidden': shouldHide,
|
|
35073
|
-
}) }, hAsync("slot", { key: '
|
|
35416
|
+
}) }, hAsync("slot", { key: 'ce10ade2b86725e24f3254516483eeedd8ecb16a' })));
|
|
35074
35417
|
}
|
|
35075
35418
|
get el() { return getElement(this); }
|
|
35076
35419
|
static get watchers() { return {
|