@ionic/core 8.7.18-dev.11770318664.15f47a5e → 8.7.18-dev.11770674094.18396f54
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/ion-refresher.js +3 -32
- package/components/modal.js +248 -13
- package/components/popover.js +152 -30
- package/dist/cjs/ion-modal.cjs.entry.js +248 -13
- package/dist/cjs/ion-popover.cjs.entry.js +152 -30
- package/dist/cjs/ion-refresher_2.cjs.entry.js +3 -32
- package/dist/collection/components/modal/modal.ios.css +0 -4
- package/dist/collection/components/modal/modal.js +127 -11
- package/dist/collection/components/modal/modal.md.css +0 -4
- package/dist/collection/components/modal/safe-area-utils.js +123 -0
- package/dist/collection/components/popover/animations/ios.enter.js +33 -8
- package/dist/collection/components/popover/animations/md.enter.js +26 -6
- package/dist/collection/components/popover/utils.js +96 -19
- package/dist/collection/components/refresher/refresher.js +4 -70
- package/dist/docs.json +4 -49
- package/dist/esm/ion-modal.entry.js +248 -13
- package/dist/esm/ion-popover.entry.js +152 -30
- package/dist/esm/ion-refresher_2.entry.js +3 -32
- package/dist/html.html-data.json +1 -1
- package/dist/ionic/ionic.esm.js +1 -1
- package/dist/ionic/p-36d78f36.entry.js +4 -0
- package/dist/ionic/p-639dd543.entry.js +4 -0
- package/dist/ionic/p-cc3446d5.entry.js +4 -0
- package/dist/types/components/modal/modal.d.ts +23 -0
- package/dist/types/components/modal/safe-area-utils.d.ts +55 -0
- package/dist/types/components/popover/utils.d.ts +23 -1
- package/dist/types/components/refresher/refresher-interface.d.ts +0 -7
- package/dist/types/components/refresher/refresher.d.ts +3 -16
- package/dist/types/components.d.ts +5 -16
- package/dist/types/interface.d.ts +1 -1
- package/hydrate/index.js +403 -75
- package/hydrate/index.mjs +403 -75
- package/package.json +1 -1
- package/dist/ionic/p-90f2d3ff.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
|
@@ -22804,9 +22804,129 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
|
|
|
22804
22804
|
};
|
|
22805
22805
|
};
|
|
22806
22806
|
|
|
22807
|
-
|
|
22807
|
+
/**
|
|
22808
|
+
* These thresholds match the SCSS media query breakpoints in modal.vars.scss
|
|
22809
|
+
* that trigger the centered dialog layout (non-fullscreen modal).
|
|
22810
|
+
*
|
|
22811
|
+
* SCSS defines two height breakpoints: $modal-inset-min-height-small (600px)
|
|
22812
|
+
* and $modal-inset-min-height-large (768px). We use the smaller one because
|
|
22813
|
+
* that's the threshold where the modal transitions from fullscreen to centered
|
|
22814
|
+
* dialog — the larger breakpoint only increases the dialog's height.
|
|
22815
|
+
*/
|
|
22816
|
+
const MODAL_INSET_MIN_WIDTH = 768;
|
|
22817
|
+
const MODAL_INSET_MIN_HEIGHT = 600;
|
|
22818
|
+
const EDGE_THRESHOLD = 5;
|
|
22819
|
+
/**
|
|
22820
|
+
* Determines if the current viewport meets the CSS media query conditions
|
|
22821
|
+
* that cause regular modals to render as centered dialogs instead of fullscreen.
|
|
22822
|
+
* Matches: @media (min-width: 768px) and (min-height: 600px)
|
|
22823
|
+
*/
|
|
22824
|
+
const isCenteredDialogViewport = () => {
|
|
22825
|
+
if (!win$1)
|
|
22826
|
+
return false;
|
|
22827
|
+
return win$1.matchMedia(`(min-width: ${MODAL_INSET_MIN_WIDTH}px) and (min-height: ${MODAL_INSET_MIN_HEIGHT}px)`)
|
|
22828
|
+
.matches;
|
|
22829
|
+
};
|
|
22830
|
+
/**
|
|
22831
|
+
* Returns the initial safe-area configuration based on modal type.
|
|
22832
|
+
* This is called before animation starts and uses configuration-based prediction.
|
|
22833
|
+
*
|
|
22834
|
+
* @param context - Modal context information
|
|
22835
|
+
* @returns SafeAreaConfig with initial safe-area values
|
|
22836
|
+
*/
|
|
22837
|
+
const getInitialSafeAreaConfig = (context) => {
|
|
22838
|
+
const { isSheetModal, isCardModal } = context;
|
|
22839
|
+
// Sheet modals use bottom safe-area, and top safe-area only when fully expanded
|
|
22840
|
+
if (isSheetModal) {
|
|
22841
|
+
return {
|
|
22842
|
+
top: context.currentBreakpoint === 1 ? 'inherit' : '0px',
|
|
22843
|
+
bottom: 'inherit',
|
|
22844
|
+
left: '0px',
|
|
22845
|
+
right: '0px',
|
|
22846
|
+
};
|
|
22847
|
+
}
|
|
22848
|
+
// Card modals need safe-area for height calculation.
|
|
22849
|
+
// Note: isCardModal is already gated on mode === 'ios' by the caller.
|
|
22850
|
+
if (isCardModal) {
|
|
22851
|
+
return {
|
|
22852
|
+
top: 'inherit',
|
|
22853
|
+
bottom: 'inherit',
|
|
22854
|
+
left: '0px',
|
|
22855
|
+
right: '0px',
|
|
22856
|
+
};
|
|
22857
|
+
}
|
|
22858
|
+
// On viewports that meet the centered dialog media query breakpoints,
|
|
22859
|
+
// regular modals render as centered dialogs (not fullscreen), so they
|
|
22860
|
+
// don't touch any screen edges and don't need safe-area insets.
|
|
22861
|
+
if (isCenteredDialogViewport()) {
|
|
22862
|
+
return {
|
|
22863
|
+
top: '0px',
|
|
22864
|
+
bottom: '0px',
|
|
22865
|
+
left: '0px',
|
|
22866
|
+
right: '0px',
|
|
22867
|
+
};
|
|
22868
|
+
}
|
|
22869
|
+
// Fullscreen modals on phone - inherit all safe areas
|
|
22870
|
+
return {
|
|
22871
|
+
top: 'inherit',
|
|
22872
|
+
bottom: 'inherit',
|
|
22873
|
+
left: 'inherit',
|
|
22874
|
+
right: 'inherit',
|
|
22875
|
+
};
|
|
22876
|
+
};
|
|
22877
|
+
/**
|
|
22878
|
+
* Returns safe-area configuration based on actual modal position.
|
|
22879
|
+
* Detects which edges the modal overlaps with and only applies safe-area to those edges.
|
|
22880
|
+
*
|
|
22881
|
+
* Note: On Android edge-to-edge (API 36+), getBoundingClientRect() may report
|
|
22882
|
+
* inconsistent values. Sheet and card modals avoid this by using configuration-based
|
|
22883
|
+
* prediction instead. Regular modals use coordinate detection which works reliably
|
|
22884
|
+
* on web and iOS; Android edge-to-edge may need a configuration-based fallback
|
|
22885
|
+
* once a reliable detection mechanism is available.
|
|
22886
|
+
*
|
|
22887
|
+
* @param wrapperEl - The modal wrapper element to measure
|
|
22888
|
+
* @returns SafeAreaConfig based on position
|
|
22889
|
+
*/
|
|
22890
|
+
const getPositionBasedSafeAreaConfig = (wrapperEl) => {
|
|
22891
|
+
var _a, _b;
|
|
22892
|
+
const rect = wrapperEl.getBoundingClientRect();
|
|
22893
|
+
const vh = (_a = win$1 === null || win$1 === void 0 ? void 0 : win$1.innerHeight) !== null && _a !== void 0 ? _a : 0;
|
|
22894
|
+
const vw = (_b = win$1 === null || win$1 === void 0 ? void 0 : win$1.innerWidth) !== null && _b !== void 0 ? _b : 0;
|
|
22895
|
+
// Only apply safe-area to sides where modal overlaps with screen edge
|
|
22896
|
+
return {
|
|
22897
|
+
top: rect.top <= EDGE_THRESHOLD ? 'inherit' : '0px',
|
|
22898
|
+
bottom: rect.bottom >= vh - EDGE_THRESHOLD ? 'inherit' : '0px',
|
|
22899
|
+
left: rect.left <= EDGE_THRESHOLD ? 'inherit' : '0px',
|
|
22900
|
+
right: rect.right >= vw - EDGE_THRESHOLD ? 'inherit' : '0px',
|
|
22901
|
+
};
|
|
22902
|
+
};
|
|
22903
|
+
/**
|
|
22904
|
+
* Applies safe-area CSS custom property overrides to the modal host element.
|
|
22905
|
+
*
|
|
22906
|
+
* @param hostEl - The modal host element (ion-modal)
|
|
22907
|
+
* @param config - Safe-area configuration to apply
|
|
22908
|
+
*/
|
|
22909
|
+
const applySafeAreaOverrides = (hostEl, config) => {
|
|
22910
|
+
hostEl.style.setProperty('--ion-safe-area-top', config.top);
|
|
22911
|
+
hostEl.style.setProperty('--ion-safe-area-bottom', config.bottom);
|
|
22912
|
+
hostEl.style.setProperty('--ion-safe-area-left', config.left);
|
|
22913
|
+
hostEl.style.setProperty('--ion-safe-area-right', config.right);
|
|
22914
|
+
};
|
|
22915
|
+
/**
|
|
22916
|
+
* Clears safe-area CSS custom property overrides from the modal host element.
|
|
22917
|
+
*
|
|
22918
|
+
* @param hostEl - The modal host element (ion-modal)
|
|
22919
|
+
*/
|
|
22920
|
+
const clearSafeAreaOverrides = (hostEl) => {
|
|
22921
|
+
hostEl.style.removeProperty('--ion-safe-area-top');
|
|
22922
|
+
hostEl.style.removeProperty('--ion-safe-area-bottom');
|
|
22923
|
+
hostEl.style.removeProperty('--ion-safe-area-left');
|
|
22924
|
+
hostEl.style.removeProperty('--ion-safe-area-right');
|
|
22925
|
+
};
|
|
22926
|
+
|
|
22927
|
+
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}";
|
|
22808
22928
|
|
|
22809
|
-
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
|
|
22929
|
+
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}";
|
|
22810
22930
|
|
|
22811
22931
|
// TODO(FW-2832): types
|
|
22812
22932
|
/**
|
|
@@ -23052,6 +23172,9 @@ class Modal {
|
|
|
23052
23172
|
this.triggerController.removeClickListener();
|
|
23053
23173
|
this.cleanupViewTransitionListener();
|
|
23054
23174
|
this.cleanupParentRemovalObserver();
|
|
23175
|
+
// Also called in dismiss() — intentional dual cleanup covers both
|
|
23176
|
+
// dismiss-then-remove and direct DOM removal without dismiss.
|
|
23177
|
+
this.cleanupSafeAreaOverrides();
|
|
23055
23178
|
}
|
|
23056
23179
|
componentWillLoad() {
|
|
23057
23180
|
var _a;
|
|
@@ -23212,6 +23335,11 @@ class Modal {
|
|
|
23212
23335
|
await waitForMount();
|
|
23213
23336
|
}
|
|
23214
23337
|
writeTask(() => this.el.classList.add('show-modal'));
|
|
23338
|
+
// Recalculate isSheetModal before safe-area setup because framework
|
|
23339
|
+
// bindings (e.g., Angular) may not have been applied when componentWillLoad ran.
|
|
23340
|
+
this.isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
|
|
23341
|
+
// Set initial safe-area overrides before animation
|
|
23342
|
+
this.setInitialSafeAreaOverrides();
|
|
23215
23343
|
const hasCardModal = presentingElement !== undefined;
|
|
23216
23344
|
/**
|
|
23217
23345
|
* We need to change the status bar at the
|
|
@@ -23229,6 +23357,10 @@ class Modal {
|
|
|
23229
23357
|
backdropBreakpoint: this.backdropBreakpoint,
|
|
23230
23358
|
expandToScroll: this.expandToScroll,
|
|
23231
23359
|
});
|
|
23360
|
+
// Update safe-area based on actual position after animation
|
|
23361
|
+
this.updateSafeAreaOverrides();
|
|
23362
|
+
// Apply fullscreen safe-area padding if needed
|
|
23363
|
+
this.applyFullscreenSafeArea();
|
|
23232
23364
|
/* tslint:disable-next-line */
|
|
23233
23365
|
if (typeof window !== 'undefined') {
|
|
23234
23366
|
/**
|
|
@@ -23260,13 +23392,7 @@ class Modal {
|
|
|
23260
23392
|
};
|
|
23261
23393
|
window.addEventListener(KEYBOARD_DID_OPEN, this.keyboardOpenCallback);
|
|
23262
23394
|
}
|
|
23263
|
-
|
|
23264
|
-
* Recalculate isSheetModal because framework bindings (e.g., Angular)
|
|
23265
|
-
* may not have been applied when componentWillLoad ran.
|
|
23266
|
-
*/
|
|
23267
|
-
const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
|
|
23268
|
-
this.isSheetModal = isSheetModal;
|
|
23269
|
-
if (isSheetModal) {
|
|
23395
|
+
if (this.isSheetModal) {
|
|
23270
23396
|
this.initSheetGesture();
|
|
23271
23397
|
}
|
|
23272
23398
|
else if (hasCardModal) {
|
|
@@ -23343,6 +23469,8 @@ class Modal {
|
|
|
23343
23469
|
if (this.currentBreakpoint !== breakpoint) {
|
|
23344
23470
|
this.currentBreakpoint = breakpoint;
|
|
23345
23471
|
this.ionBreakpointDidChange.emit({ breakpoint });
|
|
23472
|
+
// Update safe-area overrides based on new position
|
|
23473
|
+
this.updateSafeAreaOverrides();
|
|
23346
23474
|
}
|
|
23347
23475
|
});
|
|
23348
23476
|
this.gesture = gesture;
|
|
@@ -23510,6 +23638,7 @@ class Modal {
|
|
|
23510
23638
|
}
|
|
23511
23639
|
this.cleanupViewTransitionListener();
|
|
23512
23640
|
this.cleanupParentRemovalObserver();
|
|
23641
|
+
this.cleanupSafeAreaOverrides();
|
|
23513
23642
|
this.cleanupChildRoutePassthrough();
|
|
23514
23643
|
}
|
|
23515
23644
|
this.currentBreakpoint = undefined;
|
|
@@ -23630,6 +23759,9 @@ class Modal {
|
|
|
23630
23759
|
this.viewTransitionAnimation = transitionAnimation;
|
|
23631
23760
|
transitionAnimation.play().then(() => {
|
|
23632
23761
|
this.viewTransitionAnimation = undefined;
|
|
23762
|
+
// Wait for a layout pass after the transition so getBoundingClientRect()
|
|
23763
|
+
// in getPositionBasedSafeAreaConfig() reflects the new dimensions.
|
|
23764
|
+
raf(() => this.updateSafeAreaOverrides());
|
|
23633
23765
|
// After orientation transition, recreate the swipe-to-close gesture
|
|
23634
23766
|
// with updated animation that reflects the new presenting element state
|
|
23635
23767
|
this.reinitSwipeToClose();
|
|
@@ -23756,6 +23888,109 @@ class Modal {
|
|
|
23756
23888
|
(_a = this.parentRemovalObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
|
|
23757
23889
|
this.parentRemovalObserver = undefined;
|
|
23758
23890
|
}
|
|
23891
|
+
/**
|
|
23892
|
+
* Creates the context object for safe-area utilities.
|
|
23893
|
+
*/
|
|
23894
|
+
getSafeAreaContext() {
|
|
23895
|
+
return {
|
|
23896
|
+
isSheetModal: this.isSheetModal,
|
|
23897
|
+
isCardModal: this.presentingElement !== undefined && getIonMode$1(this) === 'ios',
|
|
23898
|
+
presentingElement: this.presentingElement,
|
|
23899
|
+
breakpoints: this.breakpoints,
|
|
23900
|
+
currentBreakpoint: this.currentBreakpoint,
|
|
23901
|
+
};
|
|
23902
|
+
}
|
|
23903
|
+
/**
|
|
23904
|
+
* Sets initial safe-area overrides before modal animation.
|
|
23905
|
+
* Called in present() before animation starts.
|
|
23906
|
+
*/
|
|
23907
|
+
setInitialSafeAreaOverrides() {
|
|
23908
|
+
const context = this.getSafeAreaContext();
|
|
23909
|
+
const safeAreaConfig = getInitialSafeAreaConfig(context);
|
|
23910
|
+
applySafeAreaOverrides(this.el, safeAreaConfig);
|
|
23911
|
+
}
|
|
23912
|
+
/**
|
|
23913
|
+
* Updates safe-area overrides during dynamic state changes.
|
|
23914
|
+
* Called after animations, during gestures, and on orientation changes.
|
|
23915
|
+
*/
|
|
23916
|
+
updateSafeAreaOverrides() {
|
|
23917
|
+
const { wrapperEl, el } = this;
|
|
23918
|
+
const context = this.getSafeAreaContext();
|
|
23919
|
+
// Sheet modals: the wrapper extends beyond the viewport and is translated
|
|
23920
|
+
// via breakpoint gestures, making getBoundingClientRect unreliable for
|
|
23921
|
+
// edge detection. Instead, use breakpoint value to determine top safe-area.
|
|
23922
|
+
if (context.isSheetModal) {
|
|
23923
|
+
const needsTopSafeArea = context.currentBreakpoint === 1;
|
|
23924
|
+
applySafeAreaOverrides(el, {
|
|
23925
|
+
top: needsTopSafeArea ? 'inherit' : '0px',
|
|
23926
|
+
bottom: 'inherit',
|
|
23927
|
+
left: '0px',
|
|
23928
|
+
right: '0px',
|
|
23929
|
+
});
|
|
23930
|
+
return;
|
|
23931
|
+
}
|
|
23932
|
+
// Card modals have fixed safe-area requirements set by initial prediction.
|
|
23933
|
+
if (context.isCardModal)
|
|
23934
|
+
return;
|
|
23935
|
+
// wrapperEl is required for position-based detection below
|
|
23936
|
+
if (!wrapperEl)
|
|
23937
|
+
return;
|
|
23938
|
+
// Regular modals: use position-based detection to correctly handle both
|
|
23939
|
+
// fullscreen modals and centered dialogs with custom dimensions.
|
|
23940
|
+
const safeAreaConfig = getPositionBasedSafeAreaConfig(wrapperEl);
|
|
23941
|
+
applySafeAreaOverrides(el, safeAreaConfig);
|
|
23942
|
+
}
|
|
23943
|
+
/**
|
|
23944
|
+
* Applies padding-bottom to fullscreen modal wrapper to prevent
|
|
23945
|
+
* content from overlapping system navigation bar.
|
|
23946
|
+
*/
|
|
23947
|
+
applyFullscreenSafeArea() {
|
|
23948
|
+
const { wrapperEl, el } = this;
|
|
23949
|
+
if (!wrapperEl)
|
|
23950
|
+
return;
|
|
23951
|
+
const context = this.getSafeAreaContext();
|
|
23952
|
+
if (context.isSheetModal || context.isCardModal)
|
|
23953
|
+
return;
|
|
23954
|
+
// Check for standard Ionic layout children (ion-content, ion-footer),
|
|
23955
|
+
// searching one level deep for wrapped components (e.g.,
|
|
23956
|
+
// <app-footer><ion-footer>...</ion-footer></app-footer>).
|
|
23957
|
+
let hasContent = false;
|
|
23958
|
+
let hasFooter = false;
|
|
23959
|
+
for (const child of Array.from(el.children)) {
|
|
23960
|
+
if (child.tagName === 'ION-CONTENT')
|
|
23961
|
+
hasContent = true;
|
|
23962
|
+
if (child.tagName === 'ION-FOOTER')
|
|
23963
|
+
hasFooter = true;
|
|
23964
|
+
for (const grandchild of Array.from(child.children)) {
|
|
23965
|
+
if (grandchild.tagName === 'ION-CONTENT')
|
|
23966
|
+
hasContent = true;
|
|
23967
|
+
if (grandchild.tagName === 'ION-FOOTER')
|
|
23968
|
+
hasFooter = true;
|
|
23969
|
+
}
|
|
23970
|
+
}
|
|
23971
|
+
// Only apply wrapper padding for standard Ionic layouts (has ion-content
|
|
23972
|
+
// but no ion-footer). Custom modals with raw HTML are fully
|
|
23973
|
+
// developer-controlled and should not be modified.
|
|
23974
|
+
if (!hasContent || hasFooter)
|
|
23975
|
+
return;
|
|
23976
|
+
// Reduce wrapper height by safe-area and add equivalent padding so the
|
|
23977
|
+
// total visual size stays the same but the flex content area shrinks.
|
|
23978
|
+
// Using height + padding instead of box-sizing: border-box avoids
|
|
23979
|
+
// breaking custom modals that set --border-width (border-box would
|
|
23980
|
+
// include the border inside the height, changing the layout).
|
|
23981
|
+
wrapperEl.style.setProperty('height', 'calc(var(--height) - var(--ion-safe-area-bottom, 0px))');
|
|
23982
|
+
wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
|
|
23983
|
+
}
|
|
23984
|
+
/**
|
|
23985
|
+
* Clears all safe-area overrides and padding from wrapper.
|
|
23986
|
+
*/
|
|
23987
|
+
cleanupSafeAreaOverrides() {
|
|
23988
|
+
clearSafeAreaOverrides(this.el);
|
|
23989
|
+
if (this.wrapperEl) {
|
|
23990
|
+
this.wrapperEl.style.removeProperty('height');
|
|
23991
|
+
this.wrapperEl.style.removeProperty('padding-bottom');
|
|
23992
|
+
}
|
|
23993
|
+
}
|
|
23759
23994
|
render() {
|
|
23760
23995
|
const { handle, isSheetModal, presentingElement, htmlAttributes, handleBehavior, inheritedAttributes, focusTrap, expandToScroll, } = this;
|
|
23761
23996
|
const showHandle = handle !== false && isSheetModal;
|
|
@@ -23763,20 +23998,20 @@ class Modal {
|
|
|
23763
23998
|
const isCardModal = presentingElement !== undefined && mode === 'ios';
|
|
23764
23999
|
const isHandleCycle = handleBehavior === 'cycle';
|
|
23765
24000
|
const isSheetModalWithHandle = isSheetModal && showHandle;
|
|
23766
|
-
return (hAsync(Host, Object.assign({ key: '
|
|
24001
|
+
return (hAsync(Host, Object.assign({ key: 'f6c1245b5458693f19a040b362a98140c571297f', "no-router": true,
|
|
23767
24002
|
// Allow the modal to be navigable when the handle is focusable
|
|
23768
24003
|
tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
|
|
23769
24004
|
zIndex: `${20000 + this.overlayIndex}`,
|
|
23770
|
-
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: '
|
|
24005
|
+
}, class: Object.assign({ [mode]: true, ['modal-default']: !isCardModal && !isSheetModal, [`modal-card`]: isCardModal, [`modal-sheet`]: isSheetModal, [`modal-no-expand-scroll`]: isSheetModal && !expandToScroll, 'overlay-hidden': true, [FOCUS_TRAP_DISABLE_CLASS]: focusTrap === false }, getClassMap(this.cssClass)), onIonBackdropTap: this.onBackdropTap, onIonModalDidPresent: this.onLifecycle, onIonModalWillPresent: this.onLifecycle, onIonModalWillDismiss: this.onLifecycle, onIonModalDidDismiss: this.onLifecycle, onFocus: this.onModalFocus }), hAsync("ion-backdrop", { key: '6866fb2a265d40c761ed6bfb661a217948c571ef', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: '00788de9267f1c064f16aa55f35250de16522ae6', class: "modal-shadow" }), hAsync("div", Object.assign({ key: 'a575e1755bd25a7996cdb04b014a91d538e7bc94',
|
|
23771
24006
|
/*
|
|
23772
24007
|
role and aria-modal must be used on the
|
|
23773
24008
|
same element. They must also be set inside the
|
|
23774
24009
|
shadow DOM otherwise ion-button will not be highlighted
|
|
23775
24010
|
when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
|
|
23776
24011
|
*/
|
|
23777
|
-
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '
|
|
24012
|
+
role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: 'b99c64717037e9241479ea2f5bb84e79a466d918', class: "modal-handle",
|
|
23778
24013
|
// Prevents the handle from receiving keyboard focus when it does not cycle
|
|
23779
|
-
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: '
|
|
24014
|
+
tabIndex: !isHandleCycle ? -1 : 0, "aria-label": "Activate to adjust the size of the dialog overlaying the screen", onClick: isHandleCycle ? this.onHandleClick : undefined, part: "handle", ref: (el) => (this.dragHandleEl = el) })), hAsync("slot", { key: 'ee672afd537b281aeeb73a55d61ceeb5dfae212a', onSlotchange: this.onSlotChange }))));
|
|
23780
24015
|
}
|
|
23781
24016
|
get el() { return getElement(this); }
|
|
23782
24017
|
static get watchers() { return {
|
|
@@ -26710,6 +26945,58 @@ class PickerColumnOption {
|
|
|
26710
26945
|
}; }
|
|
26711
26946
|
}
|
|
26712
26947
|
|
|
26948
|
+
/**
|
|
26949
|
+
* Shared per-frame cache for safe-area insets. Avoids creating a temporary
|
|
26950
|
+
* DOM element and forcing a synchronous reflow on every call within the same
|
|
26951
|
+
* frame (e.g., multiple popovers presenting simultaneously). Invalidated
|
|
26952
|
+
* after the next animation frame so values stay fresh across orientation
|
|
26953
|
+
* changes and viewport resizes.
|
|
26954
|
+
*/
|
|
26955
|
+
let cachedInsets = null;
|
|
26956
|
+
let cacheInvalidationScheduled = false;
|
|
26957
|
+
/**
|
|
26958
|
+
* Resolves the current --ion-safe-area-* CSS custom property values
|
|
26959
|
+
* to actual pixel numbers. A temporary element is needed because
|
|
26960
|
+
* getComputedStyle on :root returns the declared value of custom
|
|
26961
|
+
* properties (e.g. "env(safe-area-inset-top)") rather than a
|
|
26962
|
+
* resolved number. By assigning them to standard padding properties,
|
|
26963
|
+
* the browser resolves the value.
|
|
26964
|
+
*
|
|
26965
|
+
* Results are cached for the current frame to avoid repeated reflows.
|
|
26966
|
+
*/
|
|
26967
|
+
const getSafeAreaInsets = (doc) => {
|
|
26968
|
+
if (cachedInsets !== null) {
|
|
26969
|
+
return cachedInsets;
|
|
26970
|
+
}
|
|
26971
|
+
if (doc.body === null) {
|
|
26972
|
+
return { top: 0, bottom: 0, left: 0, right: 0 };
|
|
26973
|
+
}
|
|
26974
|
+
const el = doc.createElement('div');
|
|
26975
|
+
el.style.cssText =
|
|
26976
|
+
'position:fixed;visibility:hidden;pointer-events:none;top:0;left:0;' +
|
|
26977
|
+
'padding-top:var(--ion-safe-area-top,0px);' +
|
|
26978
|
+
'padding-bottom:var(--ion-safe-area-bottom,0px);' +
|
|
26979
|
+
'padding-left:var(--ion-safe-area-left,0px);' +
|
|
26980
|
+
'padding-right:var(--ion-safe-area-right,0px);';
|
|
26981
|
+
doc.body.appendChild(el);
|
|
26982
|
+
const style = getComputedStyle(el);
|
|
26983
|
+
const insets = {
|
|
26984
|
+
top: parseFloat(style.paddingTop) || 0,
|
|
26985
|
+
bottom: parseFloat(style.paddingBottom) || 0,
|
|
26986
|
+
left: parseFloat(style.paddingLeft) || 0,
|
|
26987
|
+
right: parseFloat(style.paddingRight) || 0,
|
|
26988
|
+
};
|
|
26989
|
+
el.remove();
|
|
26990
|
+
cachedInsets = insets;
|
|
26991
|
+
if (!cacheInvalidationScheduled) {
|
|
26992
|
+
cacheInvalidationScheduled = true;
|
|
26993
|
+
raf(() => {
|
|
26994
|
+
cachedInsets = null;
|
|
26995
|
+
cacheInvalidationScheduled = false;
|
|
26996
|
+
});
|
|
26997
|
+
}
|
|
26998
|
+
return insets;
|
|
26999
|
+
};
|
|
26713
27000
|
/**
|
|
26714
27001
|
* Returns the dimensions of the popover
|
|
26715
27002
|
* arrow on `ios` mode. If arrow is disabled
|
|
@@ -27345,8 +27632,10 @@ const calculatePopoverCenterAlign = (side, triggerBoundingBox, contentWidth, con
|
|
|
27345
27632
|
* Adjusts popover positioning coordinates
|
|
27346
27633
|
* such that popover does not appear offscreen
|
|
27347
27634
|
* or overlapping safe area bounds.
|
|
27635
|
+
*
|
|
27636
|
+
* @internal - This is not part of the public API.
|
|
27348
27637
|
*/
|
|
27349
|
-
const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyWidth, bodyHeight, contentWidth, contentHeight,
|
|
27638
|
+
const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeArea, contentOriginX, contentOriginY, triggerCoordinates, coordArrowTop = 0, coordArrowLeft = 0, arrowHeight = 0) => {
|
|
27350
27639
|
let arrowTop = coordArrowTop;
|
|
27351
27640
|
const arrowLeft = coordArrowLeft;
|
|
27352
27641
|
let left = coordLeft;
|
|
@@ -27356,16 +27645,19 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27356
27645
|
let originY = contentOriginY;
|
|
27357
27646
|
let checkSafeAreaLeft = false;
|
|
27358
27647
|
let checkSafeAreaRight = false;
|
|
27648
|
+
let checkSafeAreaTop = false;
|
|
27649
|
+
let checkSafeAreaBottom = false;
|
|
27359
27650
|
const triggerTop = triggerCoordinates
|
|
27360
27651
|
? triggerCoordinates.top + triggerCoordinates.height
|
|
27361
27652
|
: bodyHeight / 2 - contentHeight / 2;
|
|
27362
27653
|
const triggerHeight = triggerCoordinates ? triggerCoordinates.height : 0;
|
|
27363
27654
|
let addPopoverBottomClass = false;
|
|
27655
|
+
const hideArrow = false;
|
|
27364
27656
|
/**
|
|
27365
27657
|
* Adjust popover so it does not
|
|
27366
27658
|
* go off the left of the screen.
|
|
27367
27659
|
*/
|
|
27368
|
-
if (left < bodyPadding +
|
|
27660
|
+
if (left < bodyPadding + safeArea.left) {
|
|
27369
27661
|
left = bodyPadding;
|
|
27370
27662
|
checkSafeAreaLeft = true;
|
|
27371
27663
|
originX = 'left';
|
|
@@ -27374,41 +27666,58 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27374
27666
|
* go off the right of the screen.
|
|
27375
27667
|
*/
|
|
27376
27668
|
}
|
|
27377
|
-
else if (contentWidth + bodyPadding + left +
|
|
27669
|
+
else if (contentWidth + bodyPadding + left + safeArea.right > bodyWidth) {
|
|
27378
27670
|
checkSafeAreaRight = true;
|
|
27379
27671
|
left = bodyWidth - contentWidth - bodyPadding;
|
|
27380
27672
|
originX = 'right';
|
|
27381
27673
|
}
|
|
27382
27674
|
/**
|
|
27383
27675
|
* Adjust popover so it does not
|
|
27384
|
-
* go off the
|
|
27676
|
+
* go off the bottom of the screen
|
|
27677
|
+
* or overlap the bottom safe area.
|
|
27385
27678
|
* If popover is on the left or the right of
|
|
27386
27679
|
* the trigger, then we should not adjust top
|
|
27387
27680
|
* margins.
|
|
27388
27681
|
*/
|
|
27389
|
-
if (triggerTop + triggerHeight + contentHeight > bodyHeight
|
|
27390
|
-
|
|
27682
|
+
if (triggerTop + triggerHeight + contentHeight > bodyHeight - safeArea.bottom &&
|
|
27683
|
+
(side === 'top' || side === 'bottom')) {
|
|
27684
|
+
/**
|
|
27685
|
+
* Calculate where the popover top would be if flipped
|
|
27686
|
+
* above the trigger. Check whether that position clears
|
|
27687
|
+
* the top safe area with room for bodyPadding.
|
|
27688
|
+
*/
|
|
27689
|
+
const idealFlipTop = triggerTop - contentHeight - triggerHeight - (arrowHeight - 1);
|
|
27690
|
+
if (idealFlipTop >= safeArea.top + bodyPadding) {
|
|
27391
27691
|
/**
|
|
27392
|
-
*
|
|
27393
|
-
*
|
|
27394
|
-
*
|
|
27395
|
-
* off the bottom of the screen. However, we do not want to move
|
|
27396
|
-
* the popover up so much that it goes off the top of the screen.
|
|
27397
|
-
*
|
|
27398
|
-
* We chose 12 here so that the popover position looks a bit nicer as
|
|
27399
|
-
* it is not right up against the edge of the screen.
|
|
27692
|
+
* Popover fits above the trigger without overlapping
|
|
27693
|
+
* the top safe area. Use the ideal position directly —
|
|
27694
|
+
* no safe-area CSS vars needed since it clears both edges.
|
|
27400
27695
|
*/
|
|
27401
|
-
top =
|
|
27696
|
+
top = idealFlipTop;
|
|
27402
27697
|
arrowTop = top + contentHeight;
|
|
27403
27698
|
originY = 'bottom';
|
|
27404
27699
|
addPopoverBottomClass = true;
|
|
27405
|
-
/**
|
|
27406
|
-
* If not enough room for popover to appear
|
|
27407
|
-
* above trigger, then cut it off.
|
|
27408
|
-
*/
|
|
27409
27700
|
}
|
|
27410
27701
|
else {
|
|
27702
|
+
/**
|
|
27703
|
+
* Can't flip above the trigger. Constrain the bottom
|
|
27704
|
+
* edge while keeping the top near the trigger. This
|
|
27705
|
+
* creates a scrollable area anchored to the trigger.
|
|
27706
|
+
*/
|
|
27411
27707
|
bottom = bodyPadding;
|
|
27708
|
+
checkSafeAreaBottom = true;
|
|
27709
|
+
/**
|
|
27710
|
+
* When the trigger is near the bottom of the screen,
|
|
27711
|
+
* the calculated top may be at or past the bottom
|
|
27712
|
+
* constraint, leaving zero visible height. In that
|
|
27713
|
+
* case, pull top up to the top safe area so the
|
|
27714
|
+
* popover fills the available space between safe areas.
|
|
27715
|
+
*/
|
|
27716
|
+
const bottomEdge = bodyHeight - safeArea.bottom - bodyPadding;
|
|
27717
|
+
if (top >= bottomEdge) {
|
|
27718
|
+
top = safeArea.top + bodyPadding;
|
|
27719
|
+
checkSafeAreaTop = true;
|
|
27720
|
+
}
|
|
27412
27721
|
}
|
|
27413
27722
|
}
|
|
27414
27723
|
return {
|
|
@@ -27419,9 +27728,12 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
|
|
|
27419
27728
|
originY,
|
|
27420
27729
|
checkSafeAreaLeft,
|
|
27421
27730
|
checkSafeAreaRight,
|
|
27731
|
+
checkSafeAreaTop,
|
|
27732
|
+
checkSafeAreaBottom,
|
|
27422
27733
|
arrowTop,
|
|
27423
27734
|
arrowLeft,
|
|
27424
27735
|
addPopoverBottomClass,
|
|
27736
|
+
hideArrow,
|
|
27425
27737
|
};
|
|
27426
27738
|
};
|
|
27427
27739
|
const shouldShowArrow = (side, didAdjustBounds = false, ev, trigger) => {
|
|
@@ -27451,6 +27763,15 @@ const shouldShowArrow = (side, didAdjustBounds = false, ev, trigger) => {
|
|
|
27451
27763
|
};
|
|
27452
27764
|
|
|
27453
27765
|
const POPOVER_IOS_BODY_PADDING = 5;
|
|
27766
|
+
/**
|
|
27767
|
+
* Minimum edge margin for iOS popovers ensures visual spacing from screen
|
|
27768
|
+
* edges on devices without safe areas (e.g., older iPhones without notches).
|
|
27769
|
+
* Previously this was a hardcoded `safeAreaMargin = 25` that served dual
|
|
27770
|
+
* purpose: safe-area avoidance AND visual spacing. Now that actual safe-area
|
|
27771
|
+
* insets are read dynamically, this floor preserves the visual spacing when
|
|
27772
|
+
* safe-area values are 0.
|
|
27773
|
+
*/
|
|
27774
|
+
const POPOVER_IOS_MIN_EDGE_MARGIN = 25;
|
|
27454
27775
|
/**
|
|
27455
27776
|
* iOS Popover Enter Animation
|
|
27456
27777
|
*/
|
|
@@ -27476,8 +27797,16 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27476
27797
|
};
|
|
27477
27798
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
|
|
27478
27799
|
const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
|
|
27479
|
-
const
|
|
27480
|
-
const
|
|
27800
|
+
const rawSafeArea = getSafeAreaInsets(doc);
|
|
27801
|
+
const safeArea = size === 'cover'
|
|
27802
|
+
? { top: 0, bottom: 0, left: 0, right: 0 }
|
|
27803
|
+
: {
|
|
27804
|
+
top: Math.max(rawSafeArea.top, POPOVER_IOS_MIN_EDGE_MARGIN),
|
|
27805
|
+
bottom: Math.max(rawSafeArea.bottom, POPOVER_IOS_MIN_EDGE_MARGIN),
|
|
27806
|
+
left: Math.max(rawSafeArea.left, POPOVER_IOS_MIN_EDGE_MARGIN),
|
|
27807
|
+
right: Math.max(rawSafeArea.right, POPOVER_IOS_MIN_EDGE_MARGIN),
|
|
27808
|
+
};
|
|
27809
|
+
const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, checkSafeAreaTop, checkSafeAreaBottom, arrowTop, arrowLeft, addPopoverBottomClass} = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeArea, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
|
|
27481
27810
|
const baseAnimation = createAnimation();
|
|
27482
27811
|
const backdropAnimation = createAnimation();
|
|
27483
27812
|
const contentAnimation = createAnimation();
|
|
@@ -27508,10 +27837,14 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27508
27837
|
baseEl.classList.add('popover-bottom');
|
|
27509
27838
|
}
|
|
27510
27839
|
if (bottom !== undefined) {
|
|
27511
|
-
|
|
27840
|
+
let bottomValue = `${bottom}px`;
|
|
27841
|
+
if (checkSafeAreaBottom) {
|
|
27842
|
+
bottomValue = `${bottom}px + var(--ion-safe-area-bottom, 0px)`;
|
|
27843
|
+
}
|
|
27844
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
27512
27845
|
}
|
|
27513
|
-
const safeAreaLeft = ' + var(--ion-safe-area-left,
|
|
27514
|
-
const safeAreaRight = ' - var(--ion-safe-area-right,
|
|
27846
|
+
const safeAreaLeft = ' + var(--ion-safe-area-left, 0px)';
|
|
27847
|
+
const safeAreaRight = ' - var(--ion-safe-area-right, 0px)';
|
|
27515
27848
|
let leftValue = `${left}px`;
|
|
27516
27849
|
if (checkSafeAreaLeft) {
|
|
27517
27850
|
leftValue = `${left}px${safeAreaLeft}`;
|
|
@@ -27519,7 +27852,11 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27519
27852
|
if (checkSafeAreaRight) {
|
|
27520
27853
|
leftValue = `${left}px${safeAreaRight}`;
|
|
27521
27854
|
}
|
|
27522
|
-
|
|
27855
|
+
let topValue = `${top}px`;
|
|
27856
|
+
if (checkSafeAreaTop) {
|
|
27857
|
+
topValue = `${top}px + var(--ion-safe-area-top, 0px)`;
|
|
27858
|
+
}
|
|
27859
|
+
contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
|
|
27523
27860
|
contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
|
|
27524
27861
|
contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
|
|
27525
27862
|
if (arrowEl !== null) {
|
|
@@ -27595,7 +27932,23 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27595
27932
|
};
|
|
27596
27933
|
const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
|
|
27597
27934
|
const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
|
|
27598
|
-
|
|
27935
|
+
// MD mode now applies safe-area insets (previously passed 0, ignoring all safe areas).
|
|
27936
|
+
// This is needed for Android edge-to-edge (API 36+) where system bars overlap content.
|
|
27937
|
+
const safeArea = size === 'cover' ? { top: 0, bottom: 0, left: 0, right: 0 } : getSafeAreaInsets(doc);
|
|
27938
|
+
const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, checkSafeAreaTop, checkSafeAreaBottom, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, safeArea, results.originX, results.originY, results.referenceCoordinates);
|
|
27939
|
+
const safeAreaLeftCalc = ' + var(--ion-safe-area-left, 0px)';
|
|
27940
|
+
const safeAreaRightCalc = ' - var(--ion-safe-area-right, 0px)';
|
|
27941
|
+
let leftValue = `${left}px`;
|
|
27942
|
+
if (checkSafeAreaLeft) {
|
|
27943
|
+
leftValue = `${left}px${safeAreaLeftCalc}`;
|
|
27944
|
+
}
|
|
27945
|
+
if (checkSafeAreaRight) {
|
|
27946
|
+
leftValue = `${left}px${safeAreaRightCalc}`;
|
|
27947
|
+
}
|
|
27948
|
+
let topValue = `${top}px`;
|
|
27949
|
+
if (checkSafeAreaTop) {
|
|
27950
|
+
topValue = `${top}px + var(--ion-safe-area-top, 0px)`;
|
|
27951
|
+
}
|
|
27599
27952
|
const baseAnimation = createAnimation();
|
|
27600
27953
|
const backdropAnimation = createAnimation();
|
|
27601
27954
|
const wrapperAnimation = createAnimation();
|
|
@@ -27612,13 +27965,17 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27612
27965
|
contentAnimation
|
|
27613
27966
|
.addElement(contentEl)
|
|
27614
27967
|
.beforeStyles({
|
|
27615
|
-
top: `calc(${
|
|
27616
|
-
left: `calc(${
|
|
27968
|
+
top: `calc(${topValue} + var(--offset-y, 0px))`,
|
|
27969
|
+
left: `calc(${leftValue} + var(--offset-x, 0px))`,
|
|
27617
27970
|
'transform-origin': `${originY} ${originX}`,
|
|
27618
27971
|
})
|
|
27619
27972
|
.beforeAddWrite(() => {
|
|
27620
27973
|
if (bottom !== undefined) {
|
|
27621
|
-
|
|
27974
|
+
let bottomValue = `${bottom}px`;
|
|
27975
|
+
if (checkSafeAreaBottom) {
|
|
27976
|
+
bottomValue = `${bottom}px + var(--ion-safe-area-bottom, 0px)`;
|
|
27977
|
+
}
|
|
27978
|
+
contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
|
|
27622
27979
|
}
|
|
27623
27980
|
})
|
|
27624
27981
|
.fromTo('transform', 'scale(0.8)', 'scale(1)');
|
|
@@ -27630,7 +27987,7 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
|
|
|
27630
27987
|
if (size === 'cover') {
|
|
27631
27988
|
baseEl.style.setProperty('--width', `${contentWidth}px`);
|
|
27632
27989
|
}
|
|
27633
|
-
if (
|
|
27990
|
+
if (addPopoverBottomClass) {
|
|
27634
27991
|
baseEl.classList.add('popover-bottom');
|
|
27635
27992
|
}
|
|
27636
27993
|
})
|
|
@@ -29735,8 +30092,6 @@ class Refresher {
|
|
|
29735
30092
|
this.ionRefresh = createEvent(this, "ionRefresh", 7);
|
|
29736
30093
|
this.ionPull = createEvent(this, "ionPull", 7);
|
|
29737
30094
|
this.ionStart = createEvent(this, "ionStart", 7);
|
|
29738
|
-
this.ionPullStart = createEvent(this, "ionPullStart", 7);
|
|
29739
|
-
this.ionPullEnd = createEvent(this, "ionPullEnd", 7);
|
|
29740
30095
|
this.appliedStyles = false;
|
|
29741
30096
|
this.didStart = false;
|
|
29742
30097
|
this.progress = 0;
|
|
@@ -29791,8 +30146,8 @@ class Refresher {
|
|
|
29791
30146
|
* than `1`. The default value is `1` which is equal to the speed of the cursor.
|
|
29792
30147
|
* If a negative value is passed in, the factor will be `1` instead.
|
|
29793
30148
|
*
|
|
29794
|
-
* For example
|
|
29795
|
-
* `10` pixels, instead of `10` pixels
|
|
30149
|
+
* For example: If the value passed is `1.2` and the content is dragged by
|
|
30150
|
+
* `10` pixels, instead of `10` pixels the content will be pulled by `12` pixels
|
|
29796
30151
|
* (an increase of 20 percent). If the value passed is `0.8`, the dragged amount
|
|
29797
30152
|
* will be `8` pixels, less than the amount the cursor has moved.
|
|
29798
30153
|
*
|
|
@@ -29842,9 +30197,6 @@ class Refresher {
|
|
|
29842
30197
|
this.animations = [];
|
|
29843
30198
|
this.progress = 0;
|
|
29844
30199
|
this.state = 1 /* RefresherState.Inactive */;
|
|
29845
|
-
this.ionPullEnd.emit({
|
|
29846
|
-
reason: state === 32 /* RefresherState.Completing */ ? 'complete' : 'cancel',
|
|
29847
|
-
});
|
|
29848
30200
|
}
|
|
29849
30201
|
async setupiOSNativeRefresher(pullingSpinner, refreshingSpinner) {
|
|
29850
30202
|
this.elementToTransform = this.scrollEl;
|
|
@@ -29877,7 +30229,6 @@ class Refresher {
|
|
|
29877
30229
|
if (!this.didStart) {
|
|
29878
30230
|
this.didStart = true;
|
|
29879
30231
|
this.ionStart.emit();
|
|
29880
|
-
this.ionPullStart.emit();
|
|
29881
30232
|
}
|
|
29882
30233
|
// emit "pulling" on every move
|
|
29883
30234
|
if (this.pointerDown) {
|
|
@@ -29953,7 +30304,6 @@ class Refresher {
|
|
|
29953
30304
|
this.lastVelocityY = ev.velocityY;
|
|
29954
30305
|
},
|
|
29955
30306
|
onEnd: () => {
|
|
29956
|
-
const hadStarted = this.didStart;
|
|
29957
30307
|
this.pointerDown = false;
|
|
29958
30308
|
this.didStart = false;
|
|
29959
30309
|
if (this.needsCompletion) {
|
|
@@ -29963,13 +30313,6 @@ class Refresher {
|
|
|
29963
30313
|
else if (this.didRefresh) {
|
|
29964
30314
|
readTask(() => translateElement(this.elementToTransform, `${this.el.clientHeight}px`));
|
|
29965
30315
|
}
|
|
29966
|
-
else if (hadStarted) {
|
|
29967
|
-
/**
|
|
29968
|
-
* User started pulling but released before reaching the refresh threshold.
|
|
29969
|
-
* Emit ionPullEnd to complete the event pair.
|
|
29970
|
-
*/
|
|
29971
|
-
this.ionPullEnd.emit({ reason: 'cancel' });
|
|
29972
|
-
}
|
|
29973
30316
|
},
|
|
29974
30317
|
});
|
|
29975
30318
|
this.disabledChanged();
|
|
@@ -30016,7 +30359,6 @@ class Refresher {
|
|
|
30016
30359
|
ev.data.animation = animation;
|
|
30017
30360
|
animation.progressStart(false, 0);
|
|
30018
30361
|
this.ionStart.emit();
|
|
30019
|
-
this.ionPullStart.emit();
|
|
30020
30362
|
this.animations.push(animation);
|
|
30021
30363
|
return;
|
|
30022
30364
|
}
|
|
@@ -30039,7 +30381,6 @@ class Refresher {
|
|
|
30039
30381
|
this.animations = [];
|
|
30040
30382
|
this.gesture.enable(true);
|
|
30041
30383
|
this.state = 1 /* RefresherState.Inactive */;
|
|
30042
|
-
this.ionPullEnd.emit({ reason: 'cancel' });
|
|
30043
30384
|
});
|
|
30044
30385
|
return;
|
|
30045
30386
|
}
|
|
@@ -30284,7 +30625,6 @@ class Refresher {
|
|
|
30284
30625
|
if (!this.didStart) {
|
|
30285
30626
|
this.didStart = true;
|
|
30286
30627
|
this.ionStart.emit();
|
|
30287
|
-
this.ionPullStart.emit();
|
|
30288
30628
|
}
|
|
30289
30629
|
// emit "pulling" on every move
|
|
30290
30630
|
this.ionPull.emit();
|
|
@@ -30328,15 +30668,6 @@ class Refresher {
|
|
|
30328
30668
|
* available right away.
|
|
30329
30669
|
*/
|
|
30330
30670
|
this.restoreOverflowStyle();
|
|
30331
|
-
/**
|
|
30332
|
-
* If ionPullStart was emitted, we need to emit ionPullEnd
|
|
30333
|
-
* even though the gesture was aborted before reaching the
|
|
30334
|
-
* pulling threshold.
|
|
30335
|
-
*/
|
|
30336
|
-
if (this.didStart) {
|
|
30337
|
-
this.didStart = false;
|
|
30338
|
-
this.ionPullEnd.emit({ reason: 'cancel' });
|
|
30339
|
-
}
|
|
30340
30671
|
}
|
|
30341
30672
|
}
|
|
30342
30673
|
beginRefresh() {
|
|
@@ -30383,9 +30714,6 @@ class Refresher {
|
|
|
30383
30714
|
if (this.contentFullscreen && this.backgroundContentEl) {
|
|
30384
30715
|
(_a = this.backgroundContentEl) === null || _a === void 0 ? void 0 : _a.style.removeProperty('--offset-top');
|
|
30385
30716
|
}
|
|
30386
|
-
this.ionPullEnd.emit({
|
|
30387
|
-
reason: state === 32 /* RefresherState.Completing */ ? 'complete' : 'cancel',
|
|
30388
|
-
});
|
|
30389
30717
|
}, 600);
|
|
30390
30718
|
// reset the styles on the scroll element
|
|
30391
30719
|
// set that the refresh is actively cancelling/completing
|
|
@@ -30439,7 +30767,7 @@ class Refresher {
|
|
|
30439
30767
|
}
|
|
30440
30768
|
render() {
|
|
30441
30769
|
const mode = getIonMode$1(this);
|
|
30442
|
-
return (hAsync(Host, { key: '
|
|
30770
|
+
return (hAsync(Host, { key: '2d1bd880877b698604542ab2d602d38b9504d975', slot: "fixed", class: {
|
|
30443
30771
|
[mode]: true,
|
|
30444
30772
|
// Used internally for styling
|
|
30445
30773
|
[`refresher-${mode}`]: true,
|