@ionic/core 8.7.18-dev.11769546281.18b0f3ba → 8.7.18-dev.11769790854.11895f8f
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/modal.js +220 -12
- package/components/popover.js +83 -11
- package/dist/cjs/ion-modal.cjs.entry.js +220 -12
- package/dist/cjs/ion-popover.cjs.entry.js +83 -11
- 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 +215 -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/docs.json +1 -1
- package/dist/esm/ion-modal.entry.js +220 -12
- package/dist/esm/ion-popover.entry.js +83 -11
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-ec9ca3fe.entry.js +4 -0
- package/dist/ionic/p-ef4256eb.entry.js +4 -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 +47 -0
- package/dist/types/components/popover/utils.d.ts +2 -0
- package/hydrate/index.js +302 -22
- package/hydrate/index.mjs +302 -22
- package/package.json +1 -1
- package/dist/ionic/p-91840a80.entry.js +0 -4
- package/dist/ionic/p-f9061316.entry.js +0 -4
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
'use strict';
|
|
5
5
|
|
|
6
6
|
var index$3 = require('./index-D6Wc6v08.js');
|
|
7
|
+
var index = require('./index-DkNv4J_i.js');
|
|
7
8
|
var index$2 = require('./index-CO6eryBo.js');
|
|
8
9
|
var frameworkDelegate = require('./framework-delegate-DMJRBuDi.js');
|
|
9
10
|
var helpers = require('./helpers-DrTqNghc.js');
|
|
@@ -17,7 +18,6 @@ var keyboard = require('./keyboard-hHzlEQpk.js');
|
|
|
17
18
|
var animation = require('./animation-Bt3H9L1C.js');
|
|
18
19
|
var cubicBezier = require('./cubic-bezier-DAjy1V-e.js');
|
|
19
20
|
var index$1 = require('./index-CAvQ7Tka.js');
|
|
20
|
-
var index = require('./index-DkNv4J_i.js');
|
|
21
21
|
require('./hardware-back-button-VCK4V3mG.js');
|
|
22
22
|
require('./gesture-controller-dtqlP_q4.js');
|
|
23
23
|
require('./keyboard-UuAS4D_9.js');
|
|
@@ -249,7 +249,7 @@ const calculateSpringStep = (t) => {
|
|
|
249
249
|
const SwipeToCloseDefaults = {
|
|
250
250
|
MIN_PRESENTING_SCALE: 0.915,
|
|
251
251
|
};
|
|
252
|
-
const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
|
|
252
|
+
const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
|
|
253
253
|
/**
|
|
254
254
|
* The step value at which a card modal
|
|
255
255
|
* is eligible for dismissing via gesture.
|
|
@@ -406,6 +406,8 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) =>
|
|
|
406
406
|
const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
|
|
407
407
|
const clampedStep = helpers.clamp(0.0001, processedStep, maxStep);
|
|
408
408
|
animation.progressStep(clampedStep);
|
|
409
|
+
// Notify modal of position change for safe-area updates
|
|
410
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
409
411
|
/**
|
|
410
412
|
* When swiping down half way, the status bar style
|
|
411
413
|
* should be reset to its default value.
|
|
@@ -949,7 +951,7 @@ const mdLeaveAnimation = (baseEl, opts) => {
|
|
|
949
951
|
return baseAnimation;
|
|
950
952
|
};
|
|
951
953
|
|
|
952
|
-
const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
|
|
954
|
+
const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
|
|
953
955
|
// Defaults for the sheet swipe animation
|
|
954
956
|
const defaultBackdrop = [
|
|
955
957
|
{ offset: 0, opacity: 'var(--backdrop-opacity)' },
|
|
@@ -1280,6 +1282,8 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
1280
1282
|
: step;
|
|
1281
1283
|
offset = helpers.clamp(0.0001, processedStep, maxStep);
|
|
1282
1284
|
animation.progressStep(offset);
|
|
1285
|
+
// Notify modal of position change for safe-area updates
|
|
1286
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
1283
1287
|
};
|
|
1284
1288
|
const onEnd = (detail) => {
|
|
1285
1289
|
/**
|
|
@@ -1474,9 +1478,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
1474
1478
|
};
|
|
1475
1479
|
};
|
|
1476
1480
|
|
|
1477
|
-
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
|
|
1481
|
+
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}";
|
|
1478
1482
|
|
|
1479
|
-
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
|
|
1483
|
+
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}";
|
|
1480
1484
|
|
|
1481
1485
|
const Modal = class {
|
|
1482
1486
|
constructor(hostRef) {
|
|
@@ -1499,6 +1503,10 @@ const Modal = class {
|
|
|
1499
1503
|
this.inline = false;
|
|
1500
1504
|
// Whether or not modal is being dismissed via gesture
|
|
1501
1505
|
this.gestureAnimationDismissing = false;
|
|
1506
|
+
// Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
|
|
1507
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
1508
|
+
// Track previous safe-area state to avoid redundant DOM writes
|
|
1509
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
1502
1510
|
this.presented = false;
|
|
1503
1511
|
/** @internal */
|
|
1504
1512
|
this.hasController = false;
|
|
@@ -1689,7 +1697,10 @@ const Modal = class {
|
|
|
1689
1697
|
}
|
|
1690
1698
|
}
|
|
1691
1699
|
onWindowResize() {
|
|
1692
|
-
//
|
|
1700
|
+
// Invalidate safe-area cache on resize (device rotation may change values)
|
|
1701
|
+
this.cachedSafeAreas = undefined;
|
|
1702
|
+
this.updateSafeAreaOverrides();
|
|
1703
|
+
// Only handle view transition for iOS card modals when no custom animations are provided
|
|
1693
1704
|
if (ionicGlobal.getIonMode(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
|
|
1694
1705
|
return;
|
|
1695
1706
|
}
|
|
@@ -1712,6 +1723,8 @@ const Modal = class {
|
|
|
1712
1723
|
this.triggerController.removeClickListener();
|
|
1713
1724
|
this.cleanupViewTransitionListener();
|
|
1714
1725
|
this.cleanupParentRemovalObserver();
|
|
1726
|
+
// Reset safe-area state to handle removal without dismiss (e.g., framework unmount)
|
|
1727
|
+
this.resetSafeAreaState();
|
|
1715
1728
|
}
|
|
1716
1729
|
componentWillLoad() {
|
|
1717
1730
|
var _a;
|
|
@@ -1871,6 +1884,8 @@ const Modal = class {
|
|
|
1871
1884
|
else if (!this.keepContentsMounted) {
|
|
1872
1885
|
await index$4.waitForMount();
|
|
1873
1886
|
}
|
|
1887
|
+
// Predict safe-area needs based on modal configuration to avoid visual snap
|
|
1888
|
+
this.setInitialSafeAreaOverrides(presentingElement);
|
|
1874
1889
|
index$3.writeTask(() => this.el.classList.add('show-modal'));
|
|
1875
1890
|
const hasCardModal = presentingElement !== undefined;
|
|
1876
1891
|
/**
|
|
@@ -1932,6 +1947,8 @@ const Modal = class {
|
|
|
1932
1947
|
else if (hasCardModal) {
|
|
1933
1948
|
this.initSwipeToClose();
|
|
1934
1949
|
}
|
|
1950
|
+
// Now that animation is complete, update safe-area based on actual position
|
|
1951
|
+
this.updateSafeAreaOverrides();
|
|
1935
1952
|
// Initialize view transition listener for iOS card modals
|
|
1936
1953
|
this.initViewTransitionListener();
|
|
1937
1954
|
// Initialize parent removal observer
|
|
@@ -1983,7 +2000,7 @@ const Modal = class {
|
|
|
1983
2000
|
await this.dismiss(undefined, overlays.GESTURE);
|
|
1984
2001
|
this.gestureAnimationDismissing = false;
|
|
1985
2002
|
});
|
|
1986
|
-
});
|
|
2003
|
+
}, () => this.updateSafeAreaOverrides());
|
|
1987
2004
|
this.gesture.enable(true);
|
|
1988
2005
|
}
|
|
1989
2006
|
initSheetGesture() {
|
|
@@ -2004,7 +2021,8 @@ const Modal = class {
|
|
|
2004
2021
|
this.currentBreakpoint = breakpoint;
|
|
2005
2022
|
this.ionBreakpointDidChange.emit({ breakpoint });
|
|
2006
2023
|
}
|
|
2007
|
-
|
|
2024
|
+
this.updateSafeAreaOverrides();
|
|
2025
|
+
}, () => this.updateSafeAreaOverrides());
|
|
2008
2026
|
this.gesture = gesture;
|
|
2009
2027
|
this.moveSheetToBreakpoint = moveSheetToBreakpoint;
|
|
2010
2028
|
this.gesture.enable(true);
|
|
@@ -2082,6 +2100,194 @@ const Modal = class {
|
|
|
2082
2100
|
// Clear the cached reference
|
|
2083
2101
|
this.cachedPageParent = undefined;
|
|
2084
2102
|
}
|
|
2103
|
+
/**
|
|
2104
|
+
* Sets initial safe-area overrides based on modal configuration before
|
|
2105
|
+
* the modal becomes visible. This predicts whether the modal will touch
|
|
2106
|
+
* screen edges to avoid a visual snap after animation completes.
|
|
2107
|
+
*/
|
|
2108
|
+
setInitialSafeAreaOverrides(presentingElement) {
|
|
2109
|
+
const style = this.el.style;
|
|
2110
|
+
const mode = ionicGlobal.getIonMode(this);
|
|
2111
|
+
const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
|
|
2112
|
+
// Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
|
|
2113
|
+
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
2114
|
+
const isTablet = window.innerWidth >= 768;
|
|
2115
|
+
// Sheet modals always touch bottom edge, never top/left/right
|
|
2116
|
+
if (isSheetModal) {
|
|
2117
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
2118
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
2119
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
2120
|
+
return;
|
|
2121
|
+
}
|
|
2122
|
+
// Card modals have rounded top corners
|
|
2123
|
+
if (isCardModal) {
|
|
2124
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
2125
|
+
if (isTablet) {
|
|
2126
|
+
// On tablets, card modals are inset from all edges
|
|
2127
|
+
this.zeroAllSafeAreas();
|
|
2128
|
+
}
|
|
2129
|
+
else {
|
|
2130
|
+
// On phones, card modals still extend to the bottom edge
|
|
2131
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
2132
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
2133
|
+
this.applyFullscreenSafeArea();
|
|
2134
|
+
}
|
|
2135
|
+
return;
|
|
2136
|
+
}
|
|
2137
|
+
// Check if modal is fullscreen via CSS custom properties
|
|
2138
|
+
// This applies to both phone and tablet sizes - custom modals may have
|
|
2139
|
+
// non-fullscreen dimensions even on phones (e.g., --height: 70%)
|
|
2140
|
+
const computedStyle = getComputedStyle(this.el);
|
|
2141
|
+
const width = computedStyle.getPropertyValue('--width').trim();
|
|
2142
|
+
const height = computedStyle.getPropertyValue('--height').trim();
|
|
2143
|
+
const isFullscreen = width === '100%' && height === '100%';
|
|
2144
|
+
if (isFullscreen) {
|
|
2145
|
+
this.applyFullscreenSafeArea();
|
|
2146
|
+
}
|
|
2147
|
+
else if (isTablet) {
|
|
2148
|
+
// Centered dialog on tablet doesn't touch edges
|
|
2149
|
+
this.zeroAllSafeAreas();
|
|
2150
|
+
}
|
|
2151
|
+
else ;
|
|
2152
|
+
}
|
|
2153
|
+
/**
|
|
2154
|
+
* Applies safe-area handling for fullscreen modals.
|
|
2155
|
+
* Adds wrapper padding when no footer is present to prevent
|
|
2156
|
+
* content from overlapping system navigation areas.
|
|
2157
|
+
*/
|
|
2158
|
+
applyFullscreenSafeArea() {
|
|
2159
|
+
this.skipSafeAreaCoordinateDetection = true;
|
|
2160
|
+
this.updateFooterPadding();
|
|
2161
|
+
// Watch for dynamic footer additions/removals (e.g., async data loading)
|
|
2162
|
+
// Use subtree:true to support wrapped footers in framework components
|
|
2163
|
+
// (e.g., <my-footer><ion-footer>...</ion-footer></my-footer>)
|
|
2164
|
+
if (!this.footerObserver && index.win !== undefined && 'MutationObserver' in index.win) {
|
|
2165
|
+
this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
|
|
2166
|
+
this.footerObserver.observe(this.el, { childList: true, subtree: true });
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
/**
|
|
2170
|
+
* Updates wrapper and shadow padding based on footer presence.
|
|
2171
|
+
* Called initially and when footer is dynamically added/removed.
|
|
2172
|
+
* Both elements must be styled identically to prevent visual mismatches.
|
|
2173
|
+
*/
|
|
2174
|
+
updateFooterPadding() {
|
|
2175
|
+
if (!this.wrapperEl)
|
|
2176
|
+
return;
|
|
2177
|
+
const hasFooter = this.el.querySelector('ion-footer') !== null;
|
|
2178
|
+
// Apply to both wrapper and shadow to keep them in sync
|
|
2179
|
+
const elements = [this.wrapperEl, this.shadowEl].filter(Boolean);
|
|
2180
|
+
if (hasFooter) {
|
|
2181
|
+
elements.forEach((el) => {
|
|
2182
|
+
el.style.removeProperty('padding-bottom');
|
|
2183
|
+
el.style.removeProperty('box-sizing');
|
|
2184
|
+
});
|
|
2185
|
+
}
|
|
2186
|
+
else {
|
|
2187
|
+
elements.forEach((el) => {
|
|
2188
|
+
el.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
2189
|
+
el.style.setProperty('box-sizing', 'border-box');
|
|
2190
|
+
});
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
/**
|
|
2194
|
+
* Sets all safe-area CSS variables to 0px for modals that
|
|
2195
|
+
* don't touch screen edges.
|
|
2196
|
+
*/
|
|
2197
|
+
zeroAllSafeAreas() {
|
|
2198
|
+
const style = this.el.style;
|
|
2199
|
+
style.setProperty('--ion-safe-area-top', '0px');
|
|
2200
|
+
style.setProperty('--ion-safe-area-bottom', '0px');
|
|
2201
|
+
style.setProperty('--ion-safe-area-left', '0px');
|
|
2202
|
+
style.setProperty('--ion-safe-area-right', '0px');
|
|
2203
|
+
}
|
|
2204
|
+
/**
|
|
2205
|
+
* Resets all safe-area related state and styles.
|
|
2206
|
+
* Called during dismiss and disconnectedCallback to ensure clean state
|
|
2207
|
+
* for re-presentation of inline modals.
|
|
2208
|
+
*/
|
|
2209
|
+
resetSafeAreaState() {
|
|
2210
|
+
var _a;
|
|
2211
|
+
this.skipSafeAreaCoordinateDetection = false;
|
|
2212
|
+
this.cachedSafeAreas = undefined;
|
|
2213
|
+
this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
|
|
2214
|
+
(_a = this.footerObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
2215
|
+
this.footerObserver = undefined;
|
|
2216
|
+
// Clear wrapper and shadow styles that may have been set for safe-area handling
|
|
2217
|
+
[this.wrapperEl, this.shadowEl].forEach((el) => {
|
|
2218
|
+
if (el) {
|
|
2219
|
+
el.style.removeProperty('padding-bottom');
|
|
2220
|
+
el.style.removeProperty('box-sizing');
|
|
2221
|
+
}
|
|
2222
|
+
});
|
|
2223
|
+
// Clear safe-area CSS variable overrides
|
|
2224
|
+
const style = this.el.style;
|
|
2225
|
+
style.removeProperty('--ion-safe-area-top');
|
|
2226
|
+
style.removeProperty('--ion-safe-area-bottom');
|
|
2227
|
+
style.removeProperty('--ion-safe-area-left');
|
|
2228
|
+
style.removeProperty('--ion-safe-area-right');
|
|
2229
|
+
}
|
|
2230
|
+
/**
|
|
2231
|
+
* Gets the root safe-area values from the document element.
|
|
2232
|
+
* Uses cached values during gestures to avoid getComputedStyle calls.
|
|
2233
|
+
*/
|
|
2234
|
+
getSafeAreaValues() {
|
|
2235
|
+
if (!this.cachedSafeAreas) {
|
|
2236
|
+
const rootStyle = getComputedStyle(document.documentElement);
|
|
2237
|
+
this.cachedSafeAreas = {
|
|
2238
|
+
top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
|
|
2239
|
+
bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
|
|
2240
|
+
left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
|
|
2241
|
+
right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
|
|
2242
|
+
};
|
|
2243
|
+
}
|
|
2244
|
+
return this.cachedSafeAreas;
|
|
2245
|
+
}
|
|
2246
|
+
/**
|
|
2247
|
+
* Updates safe-area CSS variable overrides based on whether the modal
|
|
2248
|
+
* extends into each safe-area region. Called after animation
|
|
2249
|
+
* and during gestures to handle dynamic position changes.
|
|
2250
|
+
*
|
|
2251
|
+
* Optimized to avoid redundant DOM writes by tracking previous state.
|
|
2252
|
+
*/
|
|
2253
|
+
updateSafeAreaOverrides() {
|
|
2254
|
+
if (this.skipSafeAreaCoordinateDetection) {
|
|
2255
|
+
return;
|
|
2256
|
+
}
|
|
2257
|
+
const wrapper = this.wrapperEl;
|
|
2258
|
+
if (!wrapper) {
|
|
2259
|
+
return;
|
|
2260
|
+
}
|
|
2261
|
+
const rect = wrapper.getBoundingClientRect();
|
|
2262
|
+
const safeAreas = this.getSafeAreaValues();
|
|
2263
|
+
const extendsIntoTop = rect.top < safeAreas.top;
|
|
2264
|
+
const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
|
|
2265
|
+
const extendsIntoLeft = rect.left < safeAreas.left;
|
|
2266
|
+
const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
|
|
2267
|
+
// Only update DOM when state actually changes
|
|
2268
|
+
const prev = this.prevSafeAreaState;
|
|
2269
|
+
const style = this.el.style;
|
|
2270
|
+
if (extendsIntoTop !== prev.top) {
|
|
2271
|
+
extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
|
|
2272
|
+
prev.top = extendsIntoTop;
|
|
2273
|
+
}
|
|
2274
|
+
if (extendsIntoBottom !== prev.bottom) {
|
|
2275
|
+
extendsIntoBottom
|
|
2276
|
+
? style.removeProperty('--ion-safe-area-bottom')
|
|
2277
|
+
: style.setProperty('--ion-safe-area-bottom', '0px');
|
|
2278
|
+
prev.bottom = extendsIntoBottom;
|
|
2279
|
+
}
|
|
2280
|
+
if (extendsIntoLeft !== prev.left) {
|
|
2281
|
+
extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
|
|
2282
|
+
prev.left = extendsIntoLeft;
|
|
2283
|
+
}
|
|
2284
|
+
if (extendsIntoRight !== prev.right) {
|
|
2285
|
+
extendsIntoRight
|
|
2286
|
+
? style.removeProperty('--ion-safe-area-right')
|
|
2287
|
+
: style.setProperty('--ion-safe-area-right', '0px');
|
|
2288
|
+
prev.right = extendsIntoRight;
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2085
2291
|
sheetOnDismiss() {
|
|
2086
2292
|
/**
|
|
2087
2293
|
* While the gesture animation is finishing
|
|
@@ -2174,6 +2380,8 @@ const Modal = class {
|
|
|
2174
2380
|
}
|
|
2175
2381
|
this.currentBreakpoint = undefined;
|
|
2176
2382
|
this.animation = undefined;
|
|
2383
|
+
// Reset safe-area state for potential re-presentation
|
|
2384
|
+
this.resetSafeAreaState();
|
|
2177
2385
|
unlock();
|
|
2178
2386
|
return dismissed;
|
|
2179
2387
|
}
|
|
@@ -2423,20 +2631,20 @@ const Modal = class {
|
|
|
2423
2631
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
2424
2632
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
2425
2633
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
2426
|
-
return (index$3.h(index$3.Host, Object.assign({ key: '
|
|
2634
|
+
return (index$3.h(index$3.Host, Object.assign({ key: '8f42c71ec9c9270b4218ca7eec57ca998871ac07', "no-router": true,
|
|
2427
2635
|
// Allow the modal to be navigable when the handle is focusable
|
|
2428
2636
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
2429
2637
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
2430
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: '
|
|
2638
|
+
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [overlays.FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, theme.getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), index$3.h("ion-backdrop", { key: '2f7b08b019c66cbfceacafa9a68b91f8da1f4084', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && index$3.h("div", { key: '6b0d1ae90e8483332f74371be5dcbd0a7b2661ba', class: "modal-shadow", ref: (el) => (this.shadowEl = el) }), index$3.h("div", Object.assign({ key: 'c96f671fb25e5de45231ea65ac52500547b2e262',
|
|
2431
2639
|
/*
|
|
2432
2640
|
role and aria-modal must be used on the
|
|
2433
2641
|
same element. They must also be set inside the
|
|
2434
2642
|
shadow DOM otherwise ion-button will not be highlighted
|
|
2435
2643
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
2436
2644
|
*/
|
|
2437
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: '
|
|
2645
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (index$3.h("button", { key: 'e07419b01a588c0ba62ed9607c843cac7bd26337', class: "modal-handle",
|
|
2438
2646
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
2439
|
-
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) })), index$3.h("slot", { key: '
|
|
2647
|
+
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) })), index$3.h("slot", { key: '65f35079df8eb8b6cd262e3a08297866715ce7ea', onSlotchange: this.onSlotChange }))));
|
|
2440
2648
|
}
|
|
2441
2649
|
get el() { return index$3.getElement(this); }
|
|
2442
2650
|
static get watchers() { return {
|
|
@@ -660,6 +660,8 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
660
660
|
let bottom;
|
|
661
661
|
let originX = contentOriginX;
|
|
662
662
|
let originY = contentOriginY;
|
|
663
|
+
let checkSafeAreaTop = false;
|
|
664
|
+
let checkSafeAreaBottom = false;
|
|
663
665
|
let checkSafeAreaLeft = false;
|
|
664
666
|
let checkSafeAreaRight = false;
|
|
665
667
|
const triggerTop = triggerCoordinates
|
|
@@ -704,10 +706,18 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
704
706
|
* We chose 12 here so that the popover position looks a bit nicer as
|
|
705
707
|
* it is not right up against the edge of the screen.
|
|
706
708
|
*/
|
|
707
|
-
top = Math.max(
|
|
709
|
+
top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
|
|
708
710
|
arrowTop = top + contentHeight;
|
|
709
711
|
originY = 'bottom';
|
|
710
712
|
addPopoverBottomClass = true;
|
|
713
|
+
/**
|
|
714
|
+
* If the popover is positioned near the top edge, account for safe area.
|
|
715
|
+
* This ensures the popover doesn't overlap with status bars or notches.
|
|
716
|
+
*/
|
|
717
|
+
if (top <= bodyPadding + safeAreaMargin) {
|
|
718
|
+
checkSafeAreaTop = true;
|
|
719
|
+
top = bodyPadding;
|
|
720
|
+
}
|
|
711
721
|
/**
|
|
712
722
|
* If not enough room for popover to appear
|
|
713
723
|
* above trigger, then cut it off.
|
|
@@ -715,14 +725,35 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
715
725
|
}
|
|
716
726
|
else {
|
|
717
727
|
bottom = bodyPadding;
|
|
728
|
+
/**
|
|
729
|
+
* When the popover is pinned to the bottom, account for safe area.
|
|
730
|
+
* This ensures the popover doesn't overlap with home indicators
|
|
731
|
+
* or navigation bars (e.g., Android API 36+ edge-to-edge).
|
|
732
|
+
*/
|
|
733
|
+
checkSafeAreaBottom = true;
|
|
718
734
|
}
|
|
719
735
|
}
|
|
736
|
+
/**
|
|
737
|
+
* Final check: If the popover extends into any safe-area region,
|
|
738
|
+
* ensure the corresponding flag is set regardless of side.
|
|
739
|
+
* This handles cases where a side-positioned popover (left/right)
|
|
740
|
+
* still needs bottom safe-area padding because it extends into that region.
|
|
741
|
+
*/
|
|
742
|
+
const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
|
|
743
|
+
if (popoverBottom + safeAreaMargin > bodyHeight) {
|
|
744
|
+
checkSafeAreaBottom = true;
|
|
745
|
+
}
|
|
746
|
+
if (top < safeAreaMargin) {
|
|
747
|
+
checkSafeAreaTop = true;
|
|
748
|
+
}
|
|
720
749
|
return {
|
|
721
750
|
top,
|
|
722
751
|
left,
|
|
723
752
|
bottom,
|
|
724
753
|
originX,
|
|
725
754
|
originY,
|
|
755
|
+
checkSafeAreaTop,
|
|
756
|
+
checkSafeAreaBottom,
|
|
726
757
|
checkSafeAreaLeft,
|
|
727
758
|
checkSafeAreaRight,
|
|
728
759
|
arrowTop,
|
|
@@ -783,7 +814,7 @@ const iosEnterAnimation = (baseEl, opts) => {
|
|
|
783
814
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
784
815
|
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
785
816
|
const margin = size === 'cover' ? 0 : 25;
|
|
786
|
-
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);
|
|
817
|
+
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);
|
|
787
818
|
const baseAnimation = animation.createAnimation();
|
|
788
819
|
const backdropAnimation = animation.createAnimation();
|
|
789
820
|
const contentAnimation = animation.createAnimation();
|
|
@@ -813,19 +844,35 @@ const iosEnterAnimation = (baseEl, opts) => {
|
|
|
813
844
|
if (addPopoverBottomClass) {
|
|
814
845
|
baseEl.classList.add('popover-bottom');
|
|
815
846
|
}
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
847
|
+
/**
|
|
848
|
+
* Safe area CSS variable adjustments.
|
|
849
|
+
* When the popover is positioned near an edge, we add the corresponding
|
|
850
|
+
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
851
|
+
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
852
|
+
*/
|
|
853
|
+
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
854
|
+
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
819
855
|
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
820
856
|
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
857
|
+
let topValue = `${top}px`;
|
|
858
|
+
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
821
859
|
let leftValue = `${left}px`;
|
|
860
|
+
if (checkSafeAreaTop) {
|
|
861
|
+
topValue = `${top}px${safeAreaTop}`;
|
|
862
|
+
}
|
|
863
|
+
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
864
|
+
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
865
|
+
}
|
|
822
866
|
if (checkSafeAreaLeft) {
|
|
823
867
|
leftValue = `${left}px${safeAreaLeft}`;
|
|
824
868
|
}
|
|
825
869
|
if (checkSafeAreaRight) {
|
|
826
870
|
leftValue = `${left}px${safeAreaRight}`;
|
|
827
871
|
}
|
|
828
|
-
|
|
872
|
+
if (bottomValue !== undefined) {
|
|
873
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
874
|
+
}
|
|
875
|
+
contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
|
|
829
876
|
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
830
877
|
contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
|
|
831
878
|
if (arrowEl !== null) {
|
|
@@ -901,7 +948,32 @@ const mdEnterAnimation = (baseEl, opts) => {
|
|
|
901
948
|
};
|
|
902
949
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
|
|
903
950
|
const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
|
|
904
|
-
const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
|
|
951
|
+
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);
|
|
952
|
+
/**
|
|
953
|
+
* Safe area CSS variable adjustments.
|
|
954
|
+
* When the popover is positioned near an edge, we add the corresponding
|
|
955
|
+
* safe-area inset to ensure the popover doesn't overlap with system UI
|
|
956
|
+
* (status bars, home indicators, navigation bars on Android API 36+, etc.)
|
|
957
|
+
*/
|
|
958
|
+
const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
|
|
959
|
+
const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
|
|
960
|
+
const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
|
|
961
|
+
const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
|
|
962
|
+
let topValue = `${top}px`;
|
|
963
|
+
let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
|
|
964
|
+
let leftValue = `${left}px`;
|
|
965
|
+
if (checkSafeAreaTop) {
|
|
966
|
+
topValue = `${top}px${safeAreaTop}`;
|
|
967
|
+
}
|
|
968
|
+
if (checkSafeAreaBottom && bottomValue !== undefined) {
|
|
969
|
+
bottomValue = `${bottom}px${safeAreaBottom}`;
|
|
970
|
+
}
|
|
971
|
+
if (checkSafeAreaLeft) {
|
|
972
|
+
leftValue = `${left}px${safeAreaLeft}`;
|
|
973
|
+
}
|
|
974
|
+
if (checkSafeAreaRight) {
|
|
975
|
+
leftValue = `${left}px${safeAreaRight}`;
|
|
976
|
+
}
|
|
905
977
|
const baseAnimation = animation.createAnimation();
|
|
906
978
|
const backdropAnimation = animation.createAnimation();
|
|
907
979
|
const wrapperAnimation = animation.createAnimation();
|
|
@@ -918,13 +990,13 @@ const mdEnterAnimation = (baseEl, opts) => {
|
|
|
918
990
|
contentAnimation
|
|
919
991
|
.addElement(contentEl)
|
|
920
992
|
.beforeStyles({
|
|
921
|
-
top: `calc(${
|
|
922
|
-
left: `calc(${
|
|
993
|
+
top: `calc(${topValue} + var(--offset-y, 0px))`,
|
|
994
|
+
left: `calc(${leftValue} + var(--offset-x, 0px))`,
|
|
923
995
|
'transform-origin': `${originY} ${originX}`,
|
|
924
996
|
})
|
|
925
997
|
.beforeAddWrite(() => {
|
|
926
|
-
if (
|
|
927
|
-
contentEl.style.setProperty('bottom',
|
|
998
|
+
if (bottomValue !== undefined) {
|
|
999
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
928
1000
|
}
|
|
929
1001
|
})
|
|
930
1002
|
.fromTo('transform', 'scale(0.8)', 'scale(1)');
|
|
@@ -7,7 +7,7 @@ import { clamp, getElementRoot, raf } from "../../../utils/helpers";
|
|
|
7
7
|
import { FOCUS_TRAP_DISABLE_CLASS } from "../../../utils/overlays";
|
|
8
8
|
import { getBackdropValueForSheet } from "../utils";
|
|
9
9
|
import { calculateSpringStep, handleCanDismiss } from "./utils";
|
|
10
|
-
export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
|
|
10
|
+
export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
|
|
11
11
|
// Defaults for the sheet swipe animation
|
|
12
12
|
const defaultBackdrop = [
|
|
13
13
|
{ offset: 0, opacity: 'var(--backdrop-opacity)' },
|
|
@@ -338,6 +338,8 @@ export const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpo
|
|
|
338
338
|
: step;
|
|
339
339
|
offset = clamp(0.0001, processedStep, maxStep);
|
|
340
340
|
animation.progressStep(offset);
|
|
341
|
+
// Notify modal of position change for safe-area updates
|
|
342
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
341
343
|
};
|
|
342
344
|
const onEnd = (detail) => {
|
|
343
345
|
/**
|
|
@@ -12,7 +12,7 @@ import { calculateSpringStep, handleCanDismiss } from "./utils";
|
|
|
12
12
|
export const SwipeToCloseDefaults = {
|
|
13
13
|
MIN_PRESENTING_SCALE: 0.915,
|
|
14
14
|
};
|
|
15
|
-
export const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
|
|
15
|
+
export const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
|
|
16
16
|
/**
|
|
17
17
|
* The step value at which a card modal
|
|
18
18
|
* is eligible for dismissing via gesture.
|
|
@@ -169,6 +169,8 @@ export const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismi
|
|
|
169
169
|
const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
|
|
170
170
|
const clampedStep = clamp(0.0001, processedStep, maxStep);
|
|
171
171
|
animation.progressStep(clampedStep);
|
|
172
|
+
// Notify modal of position change for safe-area updates
|
|
173
|
+
onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
|
|
172
174
|
/**
|
|
173
175
|
* When swiping down half way, the status bar style
|
|
174
176
|
* should be reset to its default value.
|
|
@@ -135,10 +135,6 @@ ion-backdrop {
|
|
|
135
135
|
:host {
|
|
136
136
|
--width: 600px;
|
|
137
137
|
--height: 500px;
|
|
138
|
-
--ion-safe-area-top: 0px;
|
|
139
|
-
--ion-safe-area-bottom: 0px;
|
|
140
|
-
--ion-safe-area-right: 0px;
|
|
141
|
-
--ion-safe-area-left: 0px;
|
|
142
138
|
}
|
|
143
139
|
}
|
|
144
140
|
@media only screen and (min-width: 768px) and (min-height: 768px) {
|