@ionic/core 8.7.17-dev.11767895575.16ea7cef → 8.7.17-dev.11767897190.1ef0f479
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 +96 -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 +107 -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 +94 -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 +96 -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-d9fd799f.entry.js +4 -0
- package/dist/ionic/p-ec9ca3fe.entry.js +4 -0
- package/dist/types/components/content/content.d.ts +24 -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 +385 -52
- package/hydrate/index.mjs +385 -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,79 @@ 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
|
+
}
|
|
10647
|
+
/**
|
|
10648
|
+
* Updates hasHeader/hasFooter based on current DOM state.
|
|
10649
|
+
* Checks both direct siblings and elements wrapped in custom components
|
|
10650
|
+
* (e.g., <my-header><ion-header>...</ion-header></my-header>).
|
|
10651
|
+
*/
|
|
10652
|
+
updateSiblingDetection() {
|
|
10653
|
+
const parent = this.el.parentElement;
|
|
10654
|
+
if (parent) {
|
|
10655
|
+
// First check for direct ion-header/ion-footer siblings
|
|
10656
|
+
this.hasHeader = parent.querySelector(':scope > ion-header') !== null;
|
|
10657
|
+
this.hasFooter = parent.querySelector(':scope > ion-footer') !== null;
|
|
10658
|
+
// If not found, check if any sibling contains them (wrapped components)
|
|
10659
|
+
if (!this.hasHeader) {
|
|
10660
|
+
this.hasHeader = this.siblingContainsElement(parent, 'ion-header');
|
|
10661
|
+
}
|
|
10662
|
+
if (!this.hasFooter) {
|
|
10663
|
+
this.hasFooter = this.siblingContainsElement(parent, 'ion-footer');
|
|
10664
|
+
}
|
|
10665
|
+
}
|
|
10666
|
+
// If no footer found, check if we're inside ion-tabs which has ion-tab-bar
|
|
10667
|
+
if (!this.hasFooter) {
|
|
10668
|
+
const tabs = this.el.closest('ion-tabs');
|
|
10669
|
+
if (tabs) {
|
|
10670
|
+
this.hasFooter = tabs.querySelector(':scope > ion-tab-bar') !== null;
|
|
10671
|
+
}
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
/**
|
|
10675
|
+
* Checks if any sibling element of ion-content contains the specified element.
|
|
10676
|
+
* Only searches one level deep to avoid finding elements in nested pages.
|
|
10677
|
+
*/
|
|
10678
|
+
siblingContainsElement(parent, tagName) {
|
|
10679
|
+
for (const sibling of parent.children) {
|
|
10680
|
+
// Skip ion-content itself
|
|
10681
|
+
if (sibling === this.el)
|
|
10682
|
+
continue;
|
|
10683
|
+
// Check if this sibling contains the target element as an immediate child
|
|
10684
|
+
if (sibling.querySelector(`:scope > ${tagName}`) !== null) {
|
|
10685
|
+
return true;
|
|
10686
|
+
}
|
|
10687
|
+
}
|
|
10688
|
+
return false;
|
|
10689
|
+
}
|
|
10616
10690
|
disconnectedCallback() {
|
|
10691
|
+
var _a;
|
|
10617
10692
|
this.onScrollEnd();
|
|
10693
|
+
// Clean up mutation observer to prevent memory leaks
|
|
10694
|
+
(_a = this.parentMutationObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
10695
|
+
this.parentMutationObserver = undefined;
|
|
10618
10696
|
if (hasLazyBuild(this.el)) {
|
|
10619
10697
|
/**
|
|
10620
10698
|
* The event listener and tabs caches need to
|
|
@@ -10843,26 +10921,28 @@ class Content {
|
|
|
10843
10921
|
}
|
|
10844
10922
|
}
|
|
10845
10923
|
render() {
|
|
10846
|
-
const { fixedSlotPlacement, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
|
|
10924
|
+
const { fixedSlotPlacement, hasFooter, hasHeader, inheritedAttributes, isMainContent, scrollX, scrollY, el } = this;
|
|
10847
10925
|
const rtl = isRTL$1(el) ? 'rtl' : 'ltr';
|
|
10848
10926
|
const mode = getIonMode$1(this);
|
|
10849
10927
|
const forceOverscroll = this.shouldForceOverscroll();
|
|
10850
10928
|
const transitionShadow = mode === 'ios';
|
|
10851
10929
|
this.resize();
|
|
10852
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
10930
|
+
return (hAsync(Host, Object.assign({ key: 'f7218f733e4022a30875441bd949747537d28aa1', role: isMainContent ? 'main' : undefined, class: createColorClasses$1(this.color, {
|
|
10853
10931
|
[mode]: true,
|
|
10854
10932
|
'content-sizing': hostContext('ion-popover', this.el),
|
|
10855
10933
|
overscroll: forceOverscroll,
|
|
10856
10934
|
[`content-${rtl}`]: true,
|
|
10935
|
+
'safe-area-top': isMainContent && !hasHeader,
|
|
10936
|
+
'safe-area-bottom': isMainContent && !hasFooter,
|
|
10857
10937
|
}), style: {
|
|
10858
10938
|
'--offset-top': `${this.cTop}px`,
|
|
10859
10939
|
'--offset-bottom': `${this.cBottom}px`,
|
|
10860
|
-
} }, inheritedAttributes), hAsync("div", { key: '
|
|
10940
|
+
} }, inheritedAttributes), hAsync("div", { key: 'b735ec68c18c0b99c3595bb194029830e6542cde', ref: (el) => (this.backgroundContentEl = el), id: "background-content", part: "background" }), fixedSlotPlacement === 'before' ? hAsync("slot", { name: "fixed" }) : null, hAsync("div", { key: 'e76c00d030342d44ade6648c3f9e32ca990787ba', class: {
|
|
10861
10941
|
'inner-scroll': true,
|
|
10862
10942
|
'scroll-x': scrollX,
|
|
10863
10943
|
'scroll-y': scrollY,
|
|
10864
10944
|
overscroll: (scrollX || scrollY) && forceOverscroll,
|
|
10865
|
-
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '
|
|
10945
|
+
}, ref: (scrollEl) => (this.scrollEl = scrollEl), onScroll: this.scrollEvents ? (ev) => this.onScroll(ev) : undefined, part: "scroll" }, hAsync("slot", { key: '9049be4cea9b5da5ec1e1012248b05286fddeb7a' })), 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
10946
|
}
|
|
10867
10947
|
get el() { return getElement(this); }
|
|
10868
10948
|
static get style() { return contentCss; }
|
|
@@ -21533,7 +21613,7 @@ const calculateSpringStep = (t) => {
|
|
|
21533
21613
|
const SwipeToCloseDefaults = {
|
|
21534
21614
|
MIN_PRESENTING_SCALE: 0.915,
|
|
21535
21615
|
};
|
|
21536
|
-
const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
|
|
21616
|
+
const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
|
|
21537
21617
|
/**
|
|
21538
21618
|
* The step value at which a card modal
|
|
21539
21619
|
* is eligible for dismissing via gesture.
|
|
@@ -21690,6 +21770,8 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) =>
|
|
|
21690
21770
|
const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
|
|
21691
21771
|
const clampedStep = clamp(0.0001, processedStep, maxStep);
|
|
21692
21772
|
animation.progressStep(clampedStep);
|
|
21773
|
+
// Notify modal of position change for safe-area updates
|
|
21774
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
21693
21775
|
/**
|
|
21694
21776
|
* When swiping down half way, the status bar style
|
|
21695
21777
|
* should be reset to its default value.
|
|
@@ -22233,7 +22315,7 @@ const mdLeaveAnimation$2 = (baseEl, opts) => {
|
|
|
22233
22315
|
return baseAnimation;
|
|
22234
22316
|
};
|
|
22235
22317
|
|
|
22236
|
-
const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
|
|
22318
|
+
const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
|
|
22237
22319
|
// Defaults for the sheet swipe animation
|
|
22238
22320
|
const defaultBackdrop = [
|
|
22239
22321
|
{ offset: 0, opacity: 'var(--backdrop-opacity)' },
|
|
@@ -22564,6 +22646,8 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
22564
22646
|
: step;
|
|
22565
22647
|
offset = clamp(0.0001, processedStep, maxStep);
|
|
22566
22648
|
animation.progressStep(offset);
|
|
22649
|
+
// Notify modal of position change for safe-area updates
|
|
22650
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
22567
22651
|
};
|
|
22568
22652
|
const onEnd = (detail) => {
|
|
22569
22653
|
/**
|
|
@@ -22758,9 +22842,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
22758
22842
|
};
|
|
22759
22843
|
};
|
|
22760
22844
|
|
|
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
|
|
22845
|
+
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
22846
|
|
|
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
|
|
22847
|
+
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
22848
|
|
|
22765
22849
|
// TODO(FW-2832): types
|
|
22766
22850
|
/**
|
|
@@ -22793,6 +22877,10 @@ class Modal {
|
|
|
22793
22877
|
this.inline = false;
|
|
22794
22878
|
// Whether or not modal is being dismissed via gesture
|
|
22795
22879
|
this.gestureAnimationDismissing = false;
|
|
22880
|
+
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
22881
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
22882
|
+
// Track previous safe-area state to avoid redundant DOM writes
|
|
22883
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
22796
22884
|
this.presented = false;
|
|
22797
22885
|
/** @internal */
|
|
22798
22886
|
this.hasController = false;
|
|
@@ -22983,7 +23071,10 @@ class Modal {
|
|
|
22983
23071
|
}
|
|
22984
23072
|
}
|
|
22985
23073
|
onWindowResize() {
|
|
22986
|
-
//
|
|
23074
|
+
// Invalidate safe-area cache on resize (device rotation may change values)
|
|
23075
|
+
this.cachedSafeAreas = undefined;
|
|
23076
|
+
this.updateSafeAreaOverrides();
|
|
23077
|
+
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
22987
23078
|
if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
22988
23079
|
return;
|
|
22989
23080
|
}
|
|
@@ -23006,6 +23097,8 @@ class Modal {
|
|
|
23006
23097
|
this.triggerController.removeClickListener();
|
|
23007
23098
|
this.cleanupViewTransitionListener();
|
|
23008
23099
|
this.cleanupParentRemovalObserver();
|
|
23100
|
+
// Reset safe-area state to handle removal without dismiss (e.g., framework unmount)
|
|
23101
|
+
this.resetSafeAreaState();
|
|
23009
23102
|
}
|
|
23010
23103
|
componentWillLoad() {
|
|
23011
23104
|
var _a;
|
|
@@ -23165,6 +23258,8 @@ class Modal {
|
|
|
23165
23258
|
else if (!this.keepContentsMounted) {
|
|
23166
23259
|
await waitForMount();
|
|
23167
23260
|
}
|
|
23261
|
+
// Predict safe-area needs based on modal configuration to avoid visual snap
|
|
23262
|
+
this.setInitialSafeAreaOverrides(presentingElement);
|
|
23168
23263
|
writeTask(() => this.el.classList.add('show-modal'));
|
|
23169
23264
|
const hasCardModal = presentingElement !== undefined;
|
|
23170
23265
|
/**
|
|
@@ -23226,6 +23321,8 @@ class Modal {
|
|
|
23226
23321
|
else if (hasCardModal) {
|
|
23227
23322
|
this.initSwipeToClose();
|
|
23228
23323
|
}
|
|
23324
|
+
// Now that animation is complete, update safe-area based on actual position
|
|
23325
|
+
this.updateSafeAreaOverrides();
|
|
23229
23326
|
// Initialize view transition listener for iOS card modals
|
|
23230
23327
|
this.initViewTransitionListener();
|
|
23231
23328
|
// Initialize parent removal observer
|
|
@@ -23277,7 +23374,7 @@ class Modal {
|
|
|
23277
23374
|
await this.dismiss(undefined, GESTURE);
|
|
23278
23375
|
this.gestureAnimationDismissing = false;
|
|
23279
23376
|
});
|
|
23280
|
-
});
|
|
23377
|
+
}, () => this.updateSafeAreaOverrides());
|
|
23281
23378
|
this.gesture.enable(true);
|
|
23282
23379
|
}
|
|
23283
23380
|
initSheetGesture() {
|
|
@@ -23298,7 +23395,8 @@ class Modal {
|
|
|
23298
23395
|
this.currentBreakpoint = breakpoint;
|
|
23299
23396
|
this.ionBreakpointDidChange.emit({ breakpoint });
|
|
23300
23397
|
}
|
|
23301
|
-
|
|
23398
|
+
this.updateSafeAreaOverrides();
|
|
23399
|
+
}, () => this.updateSafeAreaOverrides());
|
|
23302
23400
|
this.gesture = gesture;
|
|
23303
23401
|
this.moveSheetToBreakpoint = moveSheetToBreakpoint;
|
|
23304
23402
|
this.gesture.enable(true);
|
|
@@ -23376,6 +23474,187 @@ class Modal {
|
|
|
23376
23474
|
// Clear the cached reference
|
|
23377
23475
|
this.cachedPageParent = undefined;
|
|
23378
23476
|
}
|
|
23477
|
+
/**
|
|
23478
|
+
* Sets initial safe-area overrides based on modal configuration before
|
|
23479
|
+
* the modal becomes visible. This predicts whether the modal will touch
|
|
23480
|
+
* screen edges to avoid a visual snap after animation completes.
|
|
23481
|
+
*/
|
|
23482
|
+
setInitialSafeAreaOverrides(presentingElement) {
|
|
23483
|
+
const style = this.el.style;
|
|
23484
|
+
const mode = getIonMode$1(this);
|
|
23485
|
+
const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
|
|
23486
|
+
// Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
|
|
23487
|
+
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23488
|
+
const isTablet = window.innerWidth >= 768;
|
|
23489
|
+
// Sheet modals always touch bottom edge, never top/left/right
|
|
23490
|
+
if (isSheetModal) {
|
|
23491
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
23492
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
23493
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
23494
|
+
return;
|
|
23495
|
+
}
|
|
23496
|
+
// Card modals have rounded top corners
|
|
23497
|
+
if (isCardModal) {
|
|
23498
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
23499
|
+
if (isTablet) {
|
|
23500
|
+
// On tablets, card modals are inset from all edges
|
|
23501
|
+
this.zeroAllSafeAreas();
|
|
23502
|
+
}
|
|
23503
|
+
else {
|
|
23504
|
+
// On phones, card modals still extend to the bottom edge
|
|
23505
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
23506
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
23507
|
+
this.applyFullscreenSafeArea();
|
|
23508
|
+
}
|
|
23509
|
+
return;
|
|
23510
|
+
}
|
|
23511
|
+
// Phone-sized fullscreen modals inherit safe areas and use wrapper padding
|
|
23512
|
+
if (!isTablet) {
|
|
23513
|
+
this.applyFullscreenSafeArea();
|
|
23514
|
+
return;
|
|
23515
|
+
}
|
|
23516
|
+
// Check if tablet modal is fullscreen via CSS custom properties
|
|
23517
|
+
const computedStyle = getComputedStyle(this.el);
|
|
23518
|
+
const width = computedStyle.getPropertyValue('--width').trim();
|
|
23519
|
+
const height = computedStyle.getPropertyValue('--height').trim();
|
|
23520
|
+
const isFullscreen = width === '100%' && height === '100%';
|
|
23521
|
+
if (isFullscreen) {
|
|
23522
|
+
this.applyFullscreenSafeArea();
|
|
23523
|
+
}
|
|
23524
|
+
else {
|
|
23525
|
+
// Centered dialog doesn't touch edges
|
|
23526
|
+
this.zeroAllSafeAreas();
|
|
23527
|
+
}
|
|
23528
|
+
}
|
|
23529
|
+
/**
|
|
23530
|
+
* Applies safe-area handling for fullscreen modals.
|
|
23531
|
+
* Adds wrapper padding when no footer is present to prevent
|
|
23532
|
+
* content from overlapping system navigation areas.
|
|
23533
|
+
*/
|
|
23534
|
+
applyFullscreenSafeArea() {
|
|
23535
|
+
this.skipSafeAreaCoordinateDetection = true;
|
|
23536
|
+
this.updateFooterPadding();
|
|
23537
|
+
// Watch for dynamic footer additions/removals (e.g., async data loading)
|
|
23538
|
+
// Use subtree:true to support wrapped footers in framework components
|
|
23539
|
+
// (e.g., <my-footer><ion-footer>...</ion-footer></my-footer>)
|
|
23540
|
+
if (!this.footerObserver && win$1 !== undefined && 'MutationObserver' in win$1) {
|
|
23541
|
+
this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
|
|
23542
|
+
this.footerObserver.observe(this.el, { childList: true, subtree: true });
|
|
23543
|
+
}
|
|
23544
|
+
}
|
|
23545
|
+
/**
|
|
23546
|
+
* Updates wrapper padding based on footer presence.
|
|
23547
|
+
* Called initially and when footer is dynamically added/removed.
|
|
23548
|
+
*/
|
|
23549
|
+
updateFooterPadding() {
|
|
23550
|
+
if (!this.wrapperEl)
|
|
23551
|
+
return;
|
|
23552
|
+
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
23553
|
+
if (hasFooter) {
|
|
23554
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23555
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
23556
|
+
}
|
|
23557
|
+
else {
|
|
23558
|
+
this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
23559
|
+
this.wrapperEl.style.setProperty('box-sizing', 'border-box');
|
|
23560
|
+
}
|
|
23561
|
+
}
|
|
23562
|
+
/**
|
|
23563
|
+
* Sets all safe-area CSS variables to 0px for modals that
|
|
23564
|
+
* don't touch screen edges.
|
|
23565
|
+
*/
|
|
23566
|
+
zeroAllSafeAreas() {
|
|
23567
|
+
const style = this.el.style;
|
|
23568
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
23569
|
+
style.setProperty('--ion-safe-area-bottom', '0px');
|
|
23570
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
23571
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
23572
|
+
}
|
|
23573
|
+
/**
|
|
23574
|
+
* Resets all safe-area related state and styles.
|
|
23575
|
+
* Called during dismiss and disconnectedCallback to ensure clean state
|
|
23576
|
+
* for re-presentation of inline modals.
|
|
23577
|
+
*/
|
|
23578
|
+
resetSafeAreaState() {
|
|
23579
|
+
var _a;
|
|
23580
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
23581
|
+
this.cachedSafeAreas = undefined;
|
|
23582
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
23583
|
+
(_a = this.footerObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
23584
|
+
this.footerObserver = undefined;
|
|
23585
|
+
// Clear wrapper styles that may have been set for safe-area handling
|
|
23586
|
+
if (this.wrapperEl) {
|
|
23587
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23588
|
+
this.wrapperEl.style.removeProperty('box-sizing');
|
|
23589
|
+
}
|
|
23590
|
+
// Clear safe-area CSS variable overrides
|
|
23591
|
+
const style = this.el.style;
|
|
23592
|
+
style.removeProperty('--ion-safe-area-top');
|
|
23593
|
+
style.removeProperty('--ion-safe-area-bottom');
|
|
23594
|
+
style.removeProperty('--ion-safe-area-left');
|
|
23595
|
+
style.removeProperty('--ion-safe-area-right');
|
|
23596
|
+
}
|
|
23597
|
+
/**
|
|
23598
|
+
* Gets the root safe-area values from the document element.
|
|
23599
|
+
* Uses cached values during gestures to avoid getComputedStyle calls.
|
|
23600
|
+
*/
|
|
23601
|
+
getSafeAreaValues() {
|
|
23602
|
+
if (!this.cachedSafeAreas) {
|
|
23603
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
23604
|
+
this.cachedSafeAreas = {
|
|
23605
|
+
top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
|
|
23606
|
+
bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
|
|
23607
|
+
left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
|
|
23608
|
+
right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
|
|
23609
|
+
};
|
|
23610
|
+
}
|
|
23611
|
+
return this.cachedSafeAreas;
|
|
23612
|
+
}
|
|
23613
|
+
/**
|
|
23614
|
+
* Updates safe-area CSS variable overrides based on whether the modal
|
|
23615
|
+
* extends into each safe-area region. Called after animation
|
|
23616
|
+
* and during gestures to handle dynamic position changes.
|
|
23617
|
+
*
|
|
23618
|
+
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
23619
|
+
*/
|
|
23620
|
+
updateSafeAreaOverrides() {
|
|
23621
|
+
if (this.skipSafeAreaCoordinateDetection) {
|
|
23622
|
+
return;
|
|
23623
|
+
}
|
|
23624
|
+
const wrapper = this.wrapperEl;
|
|
23625
|
+
if (!wrapper) {
|
|
23626
|
+
return;
|
|
23627
|
+
}
|
|
23628
|
+
const rect = wrapper.getBoundingClientRect();
|
|
23629
|
+
const safeAreas = this.getSafeAreaValues();
|
|
23630
|
+
const extendsIntoTop = rect.top < safeAreas.top;
|
|
23631
|
+
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
23632
|
+
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
23633
|
+
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
23634
|
+
// Only update DOM when state actually changes
|
|
23635
|
+
const prev = this.prevSafeAreaState;
|
|
23636
|
+
const style = this.el.style;
|
|
23637
|
+
if (extendsIntoTop !== prev.top) {
|
|
23638
|
+
extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
|
|
23639
|
+
prev.top = extendsIntoTop;
|
|
23640
|
+
}
|
|
23641
|
+
if (extendsIntoBottom !== prev.bottom) {
|
|
23642
|
+
extendsIntoBottom
|
|
23643
|
+
? style.removeProperty('--ion-safe-area-bottom')
|
|
23644
|
+
: style.setProperty('--ion-safe-area-bottom', '0px');
|
|
23645
|
+
prev.bottom = extendsIntoBottom;
|
|
23646
|
+
}
|
|
23647
|
+
if (extendsIntoLeft !== prev.left) {
|
|
23648
|
+
extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
|
|
23649
|
+
prev.left = extendsIntoLeft;
|
|
23650
|
+
}
|
|
23651
|
+
if (extendsIntoRight !== prev.right) {
|
|
23652
|
+
extendsIntoRight
|
|
23653
|
+
? style.removeProperty('--ion-safe-area-right')
|
|
23654
|
+
: style.setProperty('--ion-safe-area-right', '0px');
|
|
23655
|
+
prev.right = extendsIntoRight;
|
|
23656
|
+
}
|
|
23657
|
+
}
|
|
23379
23658
|
sheetOnDismiss() {
|
|
23380
23659
|
/**
|
|
23381
23660
|
* While the gesture animation is finishing
|
|
@@ -23468,6 +23747,8 @@ class Modal {
|
|
|
23468
23747
|
}
|
|
23469
23748
|
this.currentBreakpoint = undefined;
|
|
23470
23749
|
this.animation = undefined;
|
|
23750
|
+
// Reset safe-area state for potential re-presentation
|
|
23751
|
+
this.resetSafeAreaState();
|
|
23471
23752
|
unlock();
|
|
23472
23753
|
return dismissed;
|
|
23473
23754
|
}
|
|
@@ -23717,20 +23998,20 @@ class Modal {
|
|
|
23717
23998
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23718
23999
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
23719
24000
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
23720
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
24001
|
+
return (hAsync(Host, Object.assign({ key: '44022099fcaf047b97d1c2cb45b9b51c930e707c', "no-router": true,
|
|
23721
24002
|
// Allow the modal to be navigable when the handle is focusable
|
|
23722
24003
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
23723
24004
|
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: '
|
|
24005
|
+
}, 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
24006
|
/*
|
|
23726
24007
|
role and aria-modal must be used on the
|
|
23727
24008
|
same element. They must also be set inside the
|
|
23728
24009
|
shadow DOM otherwise ion-button will not be highlighted
|
|
23729
24010
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
23730
24011
|
*/
|
|
23731
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '
|
|
24012
|
+
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
24013
|
// 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: '
|
|
24014
|
+
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
24015
|
}
|
|
23735
24016
|
get el() { return getElement(this); }
|
|
23736
24017
|
static get watchers() { return {
|
|
@@ -27308,6 +27589,8 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27308
27589
|
let bottom;
|
|
27309
27590
|
let originX = contentOriginX;
|
|
27310
27591
|
let originY = contentOriginY;
|
|
27592
|
+
let checkSafeAreaTop = false;
|
|
27593
|
+
let checkSafeAreaBottom = false;
|
|
27311
27594
|
let checkSafeAreaLeft = false;
|
|
27312
27595
|
let checkSafeAreaRight = false;
|
|
27313
27596
|
const triggerTop = triggerCoordinates
|
|
@@ -27352,10 +27635,18 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27352
27635
|
* We chose 12 here so that the popover position looks a bit nicer as
|
|
27353
27636
|
* it is not right up against the edge of the screen.
|
|
27354
27637
|
*/
|
|
27355
|
-
top = Math.max(
|
|
27638
|
+
top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
|
|
27356
27639
|
arrowTop = top + contentHeight;
|
|
27357
27640
|
originY = 'bottom';
|
|
27358
27641
|
addPopoverBottomClass = true;
|
|
27642
|
+
/**
|
|
27643
|
+
* If the popover is positioned near the top edge, account for safe area.
|
|
27644
|
+
* This ensures the popover doesn't overlap with status bars or notches.
|
|
27645
|
+
*/
|
|
27646
|
+
if (top <= bodyPadding + safeAreaMargin) {
|
|
27647
|
+
checkSafeAreaTop = true;
|
|
27648
|
+
top = bodyPadding;
|
|
27649
|
+
}
|
|
27359
27650
|
/**
|
|
27360
27651
|
* If not enough room for popover to appear
|
|
27361
27652
|
* above trigger, then cut it off.
|
|
@@ -27363,14 +27654,35 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27363
27654
|
}
|
|
27364
27655
|
else {
|
|
27365
27656
|
bottom = bodyPadding;
|
|
27657
|
+
/**
|
|
27658
|
+
* When the popover is pinned to the bottom, account for safe area.
|
|
27659
|
+
* This ensures the popover doesn't overlap with home indicators
|
|
27660
|
+
* or navigation bars (e.g., Android API 36+ edge-to-edge).
|
|
27661
|
+
*/
|
|
27662
|
+
checkSafeAreaBottom = true;
|
|
27366
27663
|
}
|
|
27367
27664
|
}
|
|
27665
|
+
/**
|
|
27666
|
+
* Final check: If the popover extends into any safe-area region,
|
|
27667
|
+
* ensure the corresponding flag is set regardless of side.
|
|
27668
|
+
* This handles cases where a side-positioned popover (left/right)
|
|
27669
|
+
* still needs bottom safe-area padding because it extends into that region.
|
|
27670
|
+
*/
|
|
27671
|
+
const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
|
|
27672
|
+
if (popoverBottom + safeAreaMargin > bodyHeight) {
|
|
27673
|
+
checkSafeAreaBottom = true;
|
|
27674
|
+
}
|
|
27675
|
+
if (top < safeAreaMargin) {
|
|
27676
|
+
checkSafeAreaTop = true;
|
|
27677
|
+
}
|
|
27368
27678
|
return {
|
|
27369
27679
|
top,
|
|
27370
27680
|
left,
|
|
27371
27681
|
bottom,
|
|
27372
27682
|
originX,
|
|
27373
27683
|
originY,
|
|
27684
|
+
checkSafeAreaTop,
|
|
27685
|
+
checkSafeAreaBottom,
|
|
27374
27686
|
checkSafeAreaLeft,
|
|
27375
27687
|
checkSafeAreaRight,
|
|
27376
27688
|
arrowTop,
|
|
@@ -27431,7 +27743,7 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27431
27743
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
27432
27744
|
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
27433
27745
|
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);
|
|
27746
|
+
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
27747
|
const baseAnimation = createAnimation();
|
|
27436
27748
|
const backdropAnimation = createAnimation();
|
|
27437
27749
|
const contentAnimation = createAnimation();
|
|
@@ -27461,19 +27773,35 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27461
27773
|
if (addPopoverBottomClass) {
|
|
27462
27774
|
baseEl.classList.add('popover-bottom');
|
|
27463
27775
|
}
|
|
27464
|
-
|
|
27465
|
-
|
|
27466
|
-
|
|
27776
|
+
/**
|
|
27777
|
+
* Safe area CSS variable adjustments.
|
|
27778
|
+
* When the popover is positioned near an edge, we add the corresponding
|
|
27779
|
+
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
27780
|
+
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
27781
|
+
*/
|
|
27782
|
+
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
27783
|
+
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
27467
27784
|
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
27468
27785
|
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
27786
|
+
let topValue = `${top}px`;
|
|
27787
|
+
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
27469
27788
|
let leftValue = `${left}px`;
|
|
27789
|
+
if (checkSafeAreaTop) {
|
|
27790
|
+
topValue = `${top}px${safeAreaTop}`;
|
|
27791
|
+
}
|
|
27792
|
+
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
27793
|
+
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
27794
|
+
}
|
|
27470
27795
|
if (checkSafeAreaLeft) {
|
|
27471
27796
|
leftValue = `${left}px${safeAreaLeft}`;
|
|
27472
27797
|
}
|
|
27473
27798
|
if (checkSafeAreaRight) {
|
|
27474
27799
|
leftValue = `${left}px${safeAreaRight}`;
|
|
27475
27800
|
}
|
|
27476
|
-
|
|
27801
|
+
if (bottomValue !== undefined) {
|
|
27802
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
27803
|
+
}
|
|
27804
|
+
contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
|
|
27477
27805
|
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
27478
27806
|
contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
|
|
27479
27807
|
if (arrowEl !== null) {
|
|
@@ -27549,7 +27877,32 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27549
27877
|
};
|
|
27550
27878
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
|
|
27551
27879
|
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);
|
|
27880
|
+
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);
|
|
27881
|
+
/**
|
|
27882
|
+
* Safe area CSS variable adjustments.
|
|
27883
|
+
* When the popover is positioned near an edge, we add the corresponding
|
|
27884
|
+
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
27885
|
+
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
27886
|
+
*/
|
|
27887
|
+
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
27888
|
+
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
27889
|
+
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
27890
|
+
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
27891
|
+
let topValue = `${top}px`;
|
|
27892
|
+
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
27893
|
+
let leftValue = `${left}px`;
|
|
27894
|
+
if (checkSafeAreaTop) {
|
|
27895
|
+
topValue = `${top}px${safeAreaTop}`;
|
|
27896
|
+
}
|
|
27897
|
+
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
27898
|
+
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
27899
|
+
}
|
|
27900
|
+
if (checkSafeAreaLeft) {
|
|
27901
|
+
leftValue = `${left}px${safeAreaLeft}`;
|
|
27902
|
+
}
|
|
27903
|
+
if (checkSafeAreaRight) {
|
|
27904
|
+
leftValue = `${left}px${safeAreaRight}`;
|
|
27905
|
+
}
|
|
27553
27906
|
const baseAnimation = createAnimation();
|
|
27554
27907
|
const backdropAnimation = createAnimation();
|
|
27555
27908
|
const wrapperAnimation = createAnimation();
|
|
@@ -27566,13 +27919,13 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27566
27919
|
contentAnimation
|
|
27567
27920
|
.addElement(contentEl)
|
|
27568
27921
|
.beforeStyles({
|
|
27569
|
-
top: `calc(${
|
|
27570
|
-
left: `calc(${
|
|
27922
|
+
top: `calc(${topValue} + var(--offset-y, 0px))`,
|
|
27923
|
+
left: `calc(${leftValue} + var(--offset-x, 0px))`,
|
|
27571
27924
|
'transform-origin': `${originY} ${originX}`,
|
|
27572
27925
|
})
|
|
27573
27926
|
.beforeAddWrite(() => {
|
|
27574
|
-
if (
|
|
27575
|
-
contentEl.style.setProperty('bottom',
|
|
27927
|
+
if (bottomValue !== undefined) {
|
|
27928
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
27576
27929
|
}
|
|
27577
27930
|
})
|
|
27578
27931
|
.fromTo('transform', 'scale(0.8)', 'scale(1)');
|
|
@@ -34990,7 +35343,6 @@ class TabBar {
|
|
|
34990
35343
|
this.ionTabBarChanged = createEvent(this, "ionTabBarChanged", 7);
|
|
34991
35344
|
this.ionTabBarLoaded = createEvent(this, "ionTabBarLoaded", 7);
|
|
34992
35345
|
this.keyboardCtrl = null;
|
|
34993
|
-
this.keyboardCtrlPromise = null;
|
|
34994
35346
|
this.didLoad = false;
|
|
34995
35347
|
this.keyboardVisible = false;
|
|
34996
35348
|
/**
|
|
@@ -35026,7 +35378,7 @@ class TabBar {
|
|
|
35026
35378
|
}
|
|
35027
35379
|
}
|
|
35028
35380
|
async connectedCallback() {
|
|
35029
|
-
|
|
35381
|
+
this.keyboardCtrl = await createKeyboardController(async (keyboardOpen, waitForResize) => {
|
|
35030
35382
|
/**
|
|
35031
35383
|
* If the keyboard is hiding, then we need to wait
|
|
35032
35384
|
* for the webview to resize. Otherwise, the tab bar
|
|
@@ -35037,40 +35389,21 @@ class TabBar {
|
|
|
35037
35389
|
}
|
|
35038
35390
|
this.keyboardVisible = keyboardOpen; // trigger re-render by updating state
|
|
35039
35391
|
});
|
|
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
35392
|
}
|
|
35055
35393
|
disconnectedCallback() {
|
|
35056
|
-
if (this.keyboardCtrlPromise) {
|
|
35057
|
-
this.keyboardCtrlPromise.then((ctrl) => ctrl.destroy());
|
|
35058
|
-
this.keyboardCtrlPromise = null;
|
|
35059
|
-
}
|
|
35060
35394
|
if (this.keyboardCtrl) {
|
|
35061
35395
|
this.keyboardCtrl.destroy();
|
|
35062
|
-
this.keyboardCtrl = null;
|
|
35063
35396
|
}
|
|
35064
35397
|
}
|
|
35065
35398
|
render() {
|
|
35066
35399
|
const { color, translucent, keyboardVisible } = this;
|
|
35067
35400
|
const mode = getIonMode$1(this);
|
|
35068
35401
|
const shouldHide = keyboardVisible && this.el.getAttribute('slot') !== 'top';
|
|
35069
|
-
return (hAsync(Host, { key: '
|
|
35402
|
+
return (hAsync(Host, { key: '388ec37ce308035bab78d6c9a016bb616e9517a9', role: "tablist", "aria-hidden": shouldHide ? 'true' : null, class: createColorClasses$1(color, {
|
|
35070
35403
|
[mode]: true,
|
|
35071
35404
|
'tab-bar-translucent': translucent,
|
|
35072
35405
|
'tab-bar-hidden': shouldHide,
|
|
35073
|
-
}) }, hAsync("slot", { key: '
|
|
35406
|
+
}) }, hAsync("slot", { key: 'ce10ade2b86725e24f3254516483eeedd8ecb16a' })));
|
|
35074
35407
|
}
|
|
35075
35408
|
get el() { return getElement(this); }
|
|
35076
35409
|
static get watchers() { return {
|