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