@ionic/core 8.7.17-dev.11768239180.18ee1069 → 8.7.17-dev.11769628168.11eca7cd

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.
Files changed (39) hide show
  1. package/components/content.js +4 -3
  2. package/components/ion-range.js +12 -4
  3. package/components/modal.js +12 -213
  4. package/components/popover.js +11 -83
  5. package/dist/cjs/ion-app_8.cjs.entry.js +4 -3
  6. package/dist/cjs/ion-modal.cjs.entry.js +12 -213
  7. package/dist/cjs/ion-popover.cjs.entry.js +11 -83
  8. package/dist/cjs/ion-range.cjs.entry.js +12 -4
  9. package/dist/collection/components/content/content.js +4 -3
  10. package/dist/collection/components/modal/gestures/sheet.js +1 -3
  11. package/dist/collection/components/modal/gestures/swipe-to-close.js +1 -3
  12. package/dist/collection/components/modal/modal.ios.css +4 -0
  13. package/dist/collection/components/modal/modal.js +7 -205
  14. package/dist/collection/components/modal/modal.md.css +4 -0
  15. package/dist/collection/components/popover/animations/ios.enter.js +5 -21
  16. package/dist/collection/components/popover/animations/md.enter.js +5 -30
  17. package/dist/collection/components/popover/utils.js +1 -32
  18. package/dist/collection/components/range/range.js +12 -4
  19. package/dist/docs.json +1 -1
  20. package/dist/esm/ion-app_8.entry.js +4 -3
  21. package/dist/esm/ion-modal.entry.js +12 -213
  22. package/dist/esm/ion-popover.entry.js +11 -83
  23. package/dist/esm/ion-range.entry.js +12 -4
  24. package/dist/ionic/ionic.esm.js +1 -1
  25. package/dist/ionic/p-012212e4.entry.js +4 -0
  26. package/dist/ionic/p-91840a80.entry.js +4 -0
  27. package/dist/ionic/p-c73627c8.entry.js +4 -0
  28. package/dist/ionic/p-f9061316.entry.js +4 -0
  29. package/dist/types/components/modal/gestures/sheet.d.ts +1 -1
  30. package/dist/types/components/modal/gestures/swipe-to-close.d.ts +1 -1
  31. package/dist/types/components/modal/modal.d.ts +0 -45
  32. package/dist/types/components/popover/utils.d.ts +0 -2
  33. package/hydrate/index.js +38 -302
  34. package/hydrate/index.mjs +38 -302
  35. package/package.json +1 -1
  36. package/dist/ionic/p-1647c46c.entry.js +0 -4
  37. package/dist/ionic/p-732b2fd6.entry.js +0 -4
  38. package/dist/ionic/p-968a55d1.entry.js +0 -4
  39. package/dist/ionic/p-ec9ca3fe.entry.js +0 -4
@@ -2,7 +2,6 @@
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
4
  import { r as registerInstance, c as createEvent, f as printIonWarning, w as writeTask, e as config, h, d as Host, g as getElement } from './index-C8IsBmNU.js';
5
- import { w as win } from './index-ZjP4CjeZ.js';
6
5
  import { f as findClosestIonContent, i as isIonContent, d as disableContentScrollY, r as resetContentScrollY, a as findIonContent, p as printIonContentErrorMsg } from './index-Bs3kT4bc.js';
7
6
  import { C as CoreDelegate, a as attachComponent, d as detachComponent } from './framework-delegate-BYawdMXj.js';
8
7
  import { e as clamp, g as getElementRoot, r as raf, b as inheritAttributes, h as hasLazyBuild } from './helpers-DEn3pfjm.js';
@@ -16,6 +15,7 @@ import { KEYBOARD_DID_OPEN } from './keyboard-ywgs5efA.js';
16
15
  import { c as createAnimation } from './animation-Dt8bGnA-.js';
17
16
  import { g as getTimeGivenProgression } from './cubic-bezier-hHmYLOfE.js';
18
17
  import { createGesture } from './index-CfgBF1SE.js';
18
+ import { w as win } from './index-ZjP4CjeZ.js';
19
19
  import './hardware-back-button-CPLxO-Ev.js';
20
20
  import './gesture-controller-BTEOs1at.js';
21
21
  import './keyboard-CUw4ekVy.js';
@@ -247,7 +247,7 @@ const calculateSpringStep = (t) => {
247
247
  const SwipeToCloseDefaults = {
248
248
  MIN_PRESENTING_SCALE: 0.915,
249
249
  };
250
- const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
250
+ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
251
251
  /**
252
252
  * The step value at which a card modal
253
253
  * is eligible for dismissing via gesture.
@@ -404,8 +404,6 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onG
404
404
  const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
405
405
  const clampedStep = clamp(0.0001, processedStep, maxStep);
406
406
  animation.progressStep(clampedStep);
407
- // Notify modal of position change for safe-area updates
408
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
409
407
  /**
410
408
  * When swiping down half way, the status bar style
411
409
  * should be reset to its default value.
@@ -949,7 +947,7 @@ const mdLeaveAnimation = (baseEl, opts) => {
949
947
  return baseAnimation;
950
948
  };
951
949
 
952
- const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
950
+ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
953
951
  // Defaults for the sheet swipe animation
954
952
  const defaultBackdrop = [
955
953
  { offset: 0, opacity: 'var(--backdrop-opacity)' },
@@ -1280,8 +1278,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1280
1278
  : step;
1281
1279
  offset = clamp(0.0001, processedStep, maxStep);
1282
1280
  animation.progressStep(offset);
1283
- // Notify modal of position change for safe-area updates
1284
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
1285
1281
  };
1286
1282
  const onEnd = (detail) => {
1287
1283
  /**
@@ -1476,9 +1472,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
1476
1472
  };
1477
1473
  };
1478
1474
 
1479
- 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}";
1475
+ 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;--ion-safe-area-top:0px;--ion-safe-area-bottom:0px;--ion-safe-area-right:0px;--ion-safe-area-left:0px}}@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}";
1480
1476
 
1481
- 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}";
1477
+ 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;--ion-safe-area-top:0px;--ion-safe-area-bottom:0px;--ion-safe-area-right:0px;--ion-safe-area-left:0px}}@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}";
1482
1478
 
1483
1479
  const Modal = class {
1484
1480
  constructor(hostRef) {
@@ -1501,10 +1497,6 @@ const Modal = class {
1501
1497
  this.inline = false;
1502
1498
  // Whether or not modal is being dismissed via gesture
1503
1499
  this.gestureAnimationDismissing = false;
1504
- // Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
1505
- this.skipSafeAreaCoordinateDetection = false;
1506
- // Track previous safe-area state to avoid redundant DOM writes
1507
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
1508
1500
  this.presented = false;
1509
1501
  /** @internal */
1510
1502
  this.hasController = false;
@@ -1695,10 +1687,7 @@ const Modal = class {
1695
1687
  }
1696
1688
  }
1697
1689
  onWindowResize() {
1698
- // Invalidate safe-area cache on resize (device rotation may change values)
1699
- this.cachedSafeAreas = undefined;
1700
- this.updateSafeAreaOverrides();
1701
- // Only handle view transition for iOS card modals when no custom animations are provided
1690
+ // Only handle resize for iOS card modals when no custom animations are provided
1702
1691
  if (getIonMode(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
1703
1692
  return;
1704
1693
  }
@@ -1721,8 +1710,6 @@ const Modal = class {
1721
1710
  this.triggerController.removeClickListener();
1722
1711
  this.cleanupViewTransitionListener();
1723
1712
  this.cleanupParentRemovalObserver();
1724
- // Reset safe-area state to handle removal without dismiss (e.g., framework unmount)
1725
- this.resetSafeAreaState();
1726
1713
  }
1727
1714
  componentWillLoad() {
1728
1715
  var _a;
@@ -1882,8 +1869,6 @@ const Modal = class {
1882
1869
  else if (!this.keepContentsMounted) {
1883
1870
  await waitForMount();
1884
1871
  }
1885
- // Predict safe-area needs based on modal configuration to avoid visual snap
1886
- this.setInitialSafeAreaOverrides(presentingElement);
1887
1872
  writeTask(() => this.el.classList.add('show-modal'));
1888
1873
  const hasCardModal = presentingElement !== undefined;
1889
1874
  /**
@@ -1945,8 +1930,6 @@ const Modal = class {
1945
1930
  else if (hasCardModal) {
1946
1931
  this.initSwipeToClose();
1947
1932
  }
1948
- // Now that animation is complete, update safe-area based on actual position
1949
- this.updateSafeAreaOverrides();
1950
1933
  // Initialize view transition listener for iOS card modals
1951
1934
  this.initViewTransitionListener();
1952
1935
  // Initialize parent removal observer
@@ -1998,7 +1981,7 @@ const Modal = class {
1998
1981
  await this.dismiss(undefined, GESTURE);
1999
1982
  this.gestureAnimationDismissing = false;
2000
1983
  });
2001
- }, () => this.updateSafeAreaOverrides());
1984
+ });
2002
1985
  this.gesture.enable(true);
2003
1986
  }
2004
1987
  initSheetGesture() {
@@ -2019,8 +2002,7 @@ const Modal = class {
2019
2002
  this.currentBreakpoint = breakpoint;
2020
2003
  this.ionBreakpointDidChange.emit({ breakpoint });
2021
2004
  }
2022
- this.updateSafeAreaOverrides();
2023
- }, () => this.updateSafeAreaOverrides());
2005
+ });
2024
2006
  this.gesture = gesture;
2025
2007
  this.moveSheetToBreakpoint = moveSheetToBreakpoint;
2026
2008
  this.gesture.enable(true);
@@ -2098,187 +2080,6 @@ const Modal = class {
2098
2080
  // Clear the cached reference
2099
2081
  this.cachedPageParent = undefined;
2100
2082
  }
2101
- /**
2102
- * Sets initial safe-area overrides based on modal configuration before
2103
- * the modal becomes visible. This predicts whether the modal will touch
2104
- * screen edges to avoid a visual snap after animation completes.
2105
- */
2106
- setInitialSafeAreaOverrides(presentingElement) {
2107
- const style = this.el.style;
2108
- const mode = getIonMode(this);
2109
- const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
2110
- // Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
2111
- const isCardModal = presentingElement !== undefined && mode === 'ios';
2112
- const isTablet = window.innerWidth >= 768;
2113
- // Sheet modals always touch bottom edge, never top/left/right
2114
- if (isSheetModal) {
2115
- style.setProperty('--ion-safe-area-top', '0px');
2116
- style.setProperty('--ion-safe-area-left', '0px');
2117
- style.setProperty('--ion-safe-area-right', '0px');
2118
- return;
2119
- }
2120
- // Card modals have rounded top corners
2121
- if (isCardModal) {
2122
- style.setProperty('--ion-safe-area-top', '0px');
2123
- if (isTablet) {
2124
- // On tablets, card modals are inset from all edges
2125
- this.zeroAllSafeAreas();
2126
- }
2127
- else {
2128
- // On phones, card modals still extend to the bottom edge
2129
- style.setProperty('--ion-safe-area-left', '0px');
2130
- style.setProperty('--ion-safe-area-right', '0px');
2131
- this.applyFullscreenSafeArea();
2132
- }
2133
- return;
2134
- }
2135
- // Phone-sized fullscreen modals inherit safe areas and use wrapper padding
2136
- if (!isTablet) {
2137
- this.applyFullscreenSafeArea();
2138
- return;
2139
- }
2140
- // Check if tablet modal is fullscreen via CSS custom properties
2141
- const computedStyle = getComputedStyle(this.el);
2142
- const width = computedStyle.getPropertyValue('--width').trim();
2143
- const height = computedStyle.getPropertyValue('--height').trim();
2144
- const isFullscreen = width === '100%' && height === '100%';
2145
- if (isFullscreen) {
2146
- this.applyFullscreenSafeArea();
2147
- }
2148
- else {
2149
- // Centered dialog doesn't touch edges
2150
- this.zeroAllSafeAreas();
2151
- }
2152
- }
2153
- /**
2154
- * Applies safe-area handling for fullscreen modals.
2155
- * Adds wrapper padding when no footer is present to prevent
2156
- * content from overlapping system navigation areas.
2157
- */
2158
- applyFullscreenSafeArea() {
2159
- this.skipSafeAreaCoordinateDetection = true;
2160
- this.updateFooterPadding();
2161
- // Watch for dynamic footer additions/removals (e.g., async data loading)
2162
- // Use subtree:true to support wrapped footers in framework components
2163
- // (e.g., <my-footer><ion-footer>...</ion-footer></my-footer>)
2164
- if (!this.footerObserver && win !== undefined && 'MutationObserver' in win) {
2165
- this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
2166
- this.footerObserver.observe(this.el, { childList: true, subtree: true });
2167
- }
2168
- }
2169
- /**
2170
- * Updates wrapper padding based on footer presence.
2171
- * Called initially and when footer is dynamically added/removed.
2172
- */
2173
- updateFooterPadding() {
2174
- if (!this.wrapperEl)
2175
- return;
2176
- const hasFooter = this.el.querySelector('ion-footer') !== null;
2177
- if (hasFooter) {
2178
- this.wrapperEl.style.removeProperty('padding-bottom');
2179
- this.wrapperEl.style.removeProperty('box-sizing');
2180
- }
2181
- else {
2182
- this.wrapperEl.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
2183
- this.wrapperEl.style.setProperty('box-sizing', 'border-box');
2184
- }
2185
- }
2186
- /**
2187
- * Sets all safe-area CSS variables to 0px for modals that
2188
- * don't touch screen edges.
2189
- */
2190
- zeroAllSafeAreas() {
2191
- const style = this.el.style;
2192
- style.setProperty('--ion-safe-area-top', '0px');
2193
- style.setProperty('--ion-safe-area-bottom', '0px');
2194
- style.setProperty('--ion-safe-area-left', '0px');
2195
- style.setProperty('--ion-safe-area-right', '0px');
2196
- }
2197
- /**
2198
- * Resets all safe-area related state and styles.
2199
- * Called during dismiss and disconnectedCallback to ensure clean state
2200
- * for re-presentation of inline modals.
2201
- */
2202
- resetSafeAreaState() {
2203
- var _a;
2204
- this.skipSafeAreaCoordinateDetection = false;
2205
- this.cachedSafeAreas = undefined;
2206
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
2207
- (_a = this.footerObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
2208
- this.footerObserver = undefined;
2209
- // Clear wrapper styles that may have been set for safe-area handling
2210
- if (this.wrapperEl) {
2211
- this.wrapperEl.style.removeProperty('padding-bottom');
2212
- this.wrapperEl.style.removeProperty('box-sizing');
2213
- }
2214
- // Clear safe-area CSS variable overrides
2215
- const style = this.el.style;
2216
- style.removeProperty('--ion-safe-area-top');
2217
- style.removeProperty('--ion-safe-area-bottom');
2218
- style.removeProperty('--ion-safe-area-left');
2219
- style.removeProperty('--ion-safe-area-right');
2220
- }
2221
- /**
2222
- * Gets the root safe-area values from the document element.
2223
- * Uses cached values during gestures to avoid getComputedStyle calls.
2224
- */
2225
- getSafeAreaValues() {
2226
- if (!this.cachedSafeAreas) {
2227
- const rootStyle = getComputedStyle(document.documentElement);
2228
- this.cachedSafeAreas = {
2229
- top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
2230
- bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
2231
- left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
2232
- right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
2233
- };
2234
- }
2235
- return this.cachedSafeAreas;
2236
- }
2237
- /**
2238
- * Updates safe-area CSS variable overrides based on whether the modal
2239
- * extends into each safe-area region. Called after animation
2240
- * and during gestures to handle dynamic position changes.
2241
- *
2242
- * Optimized to avoid redundant DOM writes by tracking previous state.
2243
- */
2244
- updateSafeAreaOverrides() {
2245
- if (this.skipSafeAreaCoordinateDetection) {
2246
- return;
2247
- }
2248
- const wrapper = this.wrapperEl;
2249
- if (!wrapper) {
2250
- return;
2251
- }
2252
- const rect = wrapper.getBoundingClientRect();
2253
- const safeAreas = this.getSafeAreaValues();
2254
- const extendsIntoTop = rect.top < safeAreas.top;
2255
- const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
2256
- const extendsIntoLeft = rect.left < safeAreas.left;
2257
- const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
2258
- // Only update DOM when state actually changes
2259
- const prev = this.prevSafeAreaState;
2260
- const style = this.el.style;
2261
- if (extendsIntoTop !== prev.top) {
2262
- extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
2263
- prev.top = extendsIntoTop;
2264
- }
2265
- if (extendsIntoBottom !== prev.bottom) {
2266
- extendsIntoBottom
2267
- ? style.removeProperty('--ion-safe-area-bottom')
2268
- : style.setProperty('--ion-safe-area-bottom', '0px');
2269
- prev.bottom = extendsIntoBottom;
2270
- }
2271
- if (extendsIntoLeft !== prev.left) {
2272
- extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
2273
- prev.left = extendsIntoLeft;
2274
- }
2275
- if (extendsIntoRight !== prev.right) {
2276
- extendsIntoRight
2277
- ? style.removeProperty('--ion-safe-area-right')
2278
- : style.setProperty('--ion-safe-area-right', '0px');
2279
- prev.right = extendsIntoRight;
2280
- }
2281
- }
2282
2083
  sheetOnDismiss() {
2283
2084
  /**
2284
2085
  * While the gesture animation is finishing
@@ -2371,8 +2172,6 @@ const Modal = class {
2371
2172
  }
2372
2173
  this.currentBreakpoint = undefined;
2373
2174
  this.animation = undefined;
2374
- // Reset safe-area state for potential re-presentation
2375
- this.resetSafeAreaState();
2376
2175
  unlock();
2377
2176
  return dismissed;
2378
2177
  }
@@ -2622,20 +2421,20 @@ const Modal = class {
2622
2421
  const isCardModal = presentingElement !== undefined && mode === 'ios';
2623
2422
  const isHandleCycle = handleBehavior === 'cycle';
2624
2423
  const isSheetModalWithHandle = isSheetModal && showHandle;
2625
- return (h(Host, Object.assign({ key: '44022099fcaf047b97d1c2cb45b9b51c930e707c', "no-router": true,
2424
+ return (h(Host, Object.assign({ key: '87328006ea6c75ebc518ace300438492a567223e', "no-router": true,
2626
2425
  // Allow the modal to be navigable when the handle is focusable
2627
2426
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
2628
2427
  zIndex: `${20000 + this.overlayIndex}`,
2629
- }, 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 }), h("ion-backdrop", { key: 'ddd7e4f6eef51ac1f62ac70e0af10fb01e707f07', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: '58620980e3e4ec273c6787bde026e1c010b904b7', class: "modal-shadow" }), h("div", Object.assign({ key: '3fb7f6218644ba898fc504467775593eb89426a0',
2428
+ }, 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 }), h("ion-backdrop", { key: 'ee94ff8e09b691dd4ad4e4db1720f06bc3c5a469', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && h("div", { key: 'bffd69b4635c22d9f249725bd952c1e93d5615c7', class: "modal-shadow" }), h("div", Object.assign({ key: '1d394d3c68916e464ff1fbf5242419f4a3d3cca1',
2630
2429
  /*
2631
2430
  role and aria-modal must be used on the
2632
2431
  same element. They must also be set inside the
2633
2432
  shadow DOM otherwise ion-button will not be highlighted
2634
2433
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
2635
2434
  */
2636
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '9745cd590fdaa9d023a14b487ec2c87ddbafd7f7', class: "modal-handle",
2435
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (h("button", { key: '2dcf58792018e557e0c323baad2d672bc99c0bb1', class: "modal-handle",
2637
2436
  // Prevents the handle from receiving keyboard focus when it does not cycle
2638
- 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) })), h("slot", { key: 'b9a8b5d2d3d3c9b06f99179f496c9f08907d0bad', onSlotchange: this.onSlotChange }))));
2437
+ 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) })), h("slot", { key: '44164b1e8710c3895400ad9f44ecd99873874ad5', onSlotchange: this.onSlotChange }))));
2639
2438
  }
2640
2439
  get el() { return getElement(this); }
2641
2440
  static get watchers() { return {
@@ -658,8 +658,6 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
658
658
  let bottom;
659
659
  let originX = contentOriginX;
660
660
  let originY = contentOriginY;
661
- let checkSafeAreaTop = false;
662
- let checkSafeAreaBottom = false;
663
661
  let checkSafeAreaLeft = false;
664
662
  let checkSafeAreaRight = false;
665
663
  const triggerTop = triggerCoordinates
@@ -704,18 +702,10 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
704
702
  * We chose 12 here so that the popover position looks a bit nicer as
705
703
  * it is not right up against the edge of the screen.
706
704
  */
707
- top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
705
+ top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
708
706
  arrowTop = top + contentHeight;
709
707
  originY = 'bottom';
710
708
  addPopoverBottomClass = true;
711
- /**
712
- * If the popover is positioned near the top edge, account for safe area.
713
- * This ensures the popover doesn't overlap with status bars or notches.
714
- */
715
- if (top <= bodyPadding + safeAreaMargin) {
716
- checkSafeAreaTop = true;
717
- top = bodyPadding;
718
- }
719
709
  /**
720
710
  * If not enough room for popover to appear
721
711
  * above trigger, then cut it off.
@@ -723,35 +713,14 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
723
713
  }
724
714
  else {
725
715
  bottom = bodyPadding;
726
- /**
727
- * When the popover is pinned to the bottom, account for safe area.
728
- * This ensures the popover doesn't overlap with home indicators
729
- * or navigation bars (e.g., Android API 36+ edge-to-edge).
730
- */
731
- checkSafeAreaBottom = true;
732
716
  }
733
717
  }
734
- /**
735
- * Final check: If the popover extends into any safe-area region,
736
- * ensure the corresponding flag is set regardless of side.
737
- * This handles cases where a side-positioned popover (left/right)
738
- * still needs bottom safe-area padding because it extends into that region.
739
- */
740
- const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
741
- if (popoverBottom + safeAreaMargin > bodyHeight) {
742
- checkSafeAreaBottom = true;
743
- }
744
- if (top < safeAreaMargin) {
745
- checkSafeAreaTop = true;
746
- }
747
718
  return {
748
719
  top,
749
720
  left,
750
721
  bottom,
751
722
  originX,
752
723
  originY,
753
- checkSafeAreaTop,
754
- checkSafeAreaBottom,
755
724
  checkSafeAreaLeft,
756
725
  checkSafeAreaRight,
757
726
  arrowTop,
@@ -812,7 +781,7 @@ const iosEnterAnimation = (baseEl, opts) => {
812
781
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
813
782
  const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
814
783
  const margin = size === 'cover' ? 0 : 25;
815
- const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
784
+ const { originX, originY, top, left, bottom, checkSafeAreaLeft, checkSafeAreaRight, arrowTop, arrowLeft, addPopoverBottomClass, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, margin, results.originX, results.originY, results.referenceCoordinates, results.arrowTop, results.arrowLeft, arrowHeight);
816
785
  const baseAnimation = createAnimation();
817
786
  const backdropAnimation = createAnimation();
818
787
  const contentAnimation = createAnimation();
@@ -842,35 +811,19 @@ const iosEnterAnimation = (baseEl, opts) => {
842
811
  if (addPopoverBottomClass) {
843
812
  baseEl.classList.add('popover-bottom');
844
813
  }
845
- /**
846
- * Safe area CSS variable adjustments.
847
- * When the popover is positioned near an edge, we add the corresponding
848
- * safe-area inset to ensure the popover doesn't overlap with system UI
849
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
850
- */
851
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
852
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
814
+ if (bottom !== undefined) {
815
+ contentEl.style.setProperty('bottom', `${bottom}px`);
816
+ }
853
817
  const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
854
818
  const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
855
- let topValue = `${top}px`;
856
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
857
819
  let leftValue = `${left}px`;
858
- if (checkSafeAreaTop) {
859
- topValue = `${top}px${safeAreaTop}`;
860
- }
861
- if (checkSafeAreaBottom && bottomValue !== undefined) {
862
- bottomValue = `${bottom}px${safeAreaBottom}`;
863
- }
864
820
  if (checkSafeAreaLeft) {
865
821
  leftValue = `${left}px${safeAreaLeft}`;
866
822
  }
867
823
  if (checkSafeAreaRight) {
868
824
  leftValue = `${left}px${safeAreaRight}`;
869
825
  }
870
- if (bottomValue !== undefined) {
871
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
872
- }
873
- contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
826
+ contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
874
827
  contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
875
828
  contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
876
829
  if (arrowEl !== null) {
@@ -946,32 +899,7 @@ const mdEnterAnimation = (baseEl, opts) => {
946
899
  };
947
900
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
948
901
  const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
949
- const { originX, originY, top, left, bottom, checkSafeAreaTop, checkSafeAreaBottom, checkSafeAreaLeft, checkSafeAreaRight, } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
950
- /**
951
- * Safe area CSS variable adjustments.
952
- * When the popover is positioned near an edge, we add the corresponding
953
- * safe-area inset to ensure the popover doesn't overlap with system UI
954
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
955
- */
956
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
957
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
958
- const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
959
- const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
960
- let topValue = `${top}px`;
961
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
962
- let leftValue = `${left}px`;
963
- if (checkSafeAreaTop) {
964
- topValue = `${top}px${safeAreaTop}`;
965
- }
966
- if (checkSafeAreaBottom && bottomValue !== undefined) {
967
- bottomValue = `${bottom}px${safeAreaBottom}`;
968
- }
969
- if (checkSafeAreaLeft) {
970
- leftValue = `${left}px${safeAreaLeft}`;
971
- }
972
- if (checkSafeAreaRight) {
973
- leftValue = `${left}px${safeAreaRight}`;
974
- }
902
+ const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
975
903
  const baseAnimation = createAnimation();
976
904
  const backdropAnimation = createAnimation();
977
905
  const wrapperAnimation = createAnimation();
@@ -988,13 +916,13 @@ const mdEnterAnimation = (baseEl, opts) => {
988
916
  contentAnimation
989
917
  .addElement(contentEl)
990
918
  .beforeStyles({
991
- top: `calc(${topValue} + var(--offset-y, 0px))`,
992
- left: `calc(${leftValue} + var(--offset-x, 0px))`,
919
+ top: `calc(${top}px + var(--offset-y, 0px))`,
920
+ left: `calc(${left}px + var(--offset-x, 0px))`,
993
921
  'transform-origin': `${originY} ${originX}`,
994
922
  })
995
923
  .beforeAddWrite(() => {
996
- if (bottomValue !== undefined) {
997
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
924
+ if (bottom !== undefined) {
925
+ contentEl.style.setProperty('bottom', `${bottom}px`);
998
926
  }
999
927
  })
1000
928
  .fromTo('transform', 'scale(0.8)', 'scale(1)');
@@ -705,7 +705,7 @@ const Range = class {
705
705
  })));
706
706
  }
707
707
  render() {
708
- const { disabled, el, hasLabel, rangeId, pin, pressedKnob, labelPlacement, label } = this;
708
+ const { disabled, el, hasLabel, rangeId, pin, pressedKnob, labelPlacement, label, dualKnobs, min, max } = this;
709
709
  const inItem = hostContext('ion-item', el);
710
710
  /**
711
711
  * If there is no start content then the knob at
@@ -720,8 +720,14 @@ const Range = class {
720
720
  const hasEndContent = (hasLabel && labelPlacement === 'end') || this.hasEndSlotContent;
721
721
  const needsEndAdjustment = inItem && !hasEndContent;
722
722
  const mode = getIonMode(this);
723
+ /**
724
+ * Determine if any knob is at the min or max value to
725
+ * apply Host classes for styling.
726
+ */
727
+ const valueAtMin = dualKnobs ? this.valA === min || this.valB === min : this.valA === min;
728
+ const valueAtMax = dualKnobs ? this.valA === max || this.valB === max : this.valA === max;
723
729
  renderHiddenInput(true, el, this.name, JSON.stringify(this.getValue()), disabled);
724
- return (h(Host, { key: 'ef7b01f80515bcaeb2983934ad7f10a6bd5d13ec', onFocusin: this.onFocus, onFocusout: this.onBlur, id: rangeId, class: createColorClasses(this.color, {
730
+ return (h(Host, { key: 'ed646a42d51b8fe22012198c354cbcf5a389c108', onFocusin: this.onFocus, onFocusout: this.onBlur, id: rangeId, class: createColorClasses(this.color, {
725
731
  [mode]: true,
726
732
  'in-item': inItem,
727
733
  'range-disabled': disabled,
@@ -730,10 +736,12 @@ const Range = class {
730
736
  [`range-label-placement-${labelPlacement}`]: true,
731
737
  'range-item-start-adjustment': needsStartAdjustment,
732
738
  'range-item-end-adjustment': needsEndAdjustment,
733
- }) }, h("label", { key: 'fd8aa90a9d52be9da024b907e68858dae424449d', class: "range-wrapper", id: "range-label" }, h("div", { key: '2172b4f329c22017dd23475c80aac25ba6e753eb', class: {
739
+ 'range-value-min': valueAtMin,
740
+ 'range-value-max': valueAtMax,
741
+ }) }, h("label", { key: '3083e4f2a624e3b268396acb4415f7c6ac44d851', class: "range-wrapper", id: "range-label" }, h("div", { key: '47b92f94d2a0381dd7c5cd3dda54ed2942096715', class: {
734
742
  'label-text-wrapper': true,
735
743
  'label-text-wrapper-hidden': !hasLabel,
736
- }, part: "label" }, label !== undefined ? h("div", { class: "label-text" }, label) : h("slot", { name: "label" })), h("div", { key: '3c318bf2ea0576646d4c010bf44573fd0f483186', class: "native-wrapper" }, h("slot", { key: '6586fd6fc96271e73f8a86c202d1913ad1a26f96', name: "start" }), this.renderRangeSlider(), h("slot", { key: '74ac0bc2d2cb66ef708bb729f88b6ecbc1b2155d', name: "end" })))));
744
+ }, part: "label" }, label !== undefined ? h("div", { class: "label-text" }, label) : h("slot", { name: "label" })), h("div", { key: '5341da8d19eb29091df680978a0e20cc8f2eec65', class: "native-wrapper" }, h("slot", { key: '09f1437078032676695442d8c827a16faa7dffe2', name: "start" }), this.renderRangeSlider(), h("slot", { key: '02b7781970ea4d44f10b5f4627a2ca36eca45f85', name: "end" })))));
737
745
  }
738
746
  get el() { return getElement(this); }
739
747
  static get watchers() { return {