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