@ionic/core 8.7.18-dev.11769790854.11895f8f → 8.7.18-dev.11770316329.126ef4af

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 (38) hide show
  1. package/components/ion-refresher.js +32 -3
  2. package/components/modal.js +12 -220
  3. package/components/popover.js +11 -83
  4. package/dist/cjs/ion-modal.cjs.entry.js +12 -220
  5. package/dist/cjs/ion-popover.cjs.entry.js +11 -83
  6. package/dist/cjs/ion-refresher_2.cjs.entry.js +32 -3
  7. package/dist/collection/components/modal/gestures/sheet.js +1 -3
  8. package/dist/collection/components/modal/gestures/swipe-to-close.js +1 -3
  9. package/dist/collection/components/modal/modal.ios.css +4 -0
  10. package/dist/collection/components/modal/modal.js +7 -215
  11. package/dist/collection/components/modal/modal.md.css +4 -0
  12. package/dist/collection/components/popover/animations/ios.enter.js +5 -21
  13. package/dist/collection/components/popover/animations/md.enter.js +5 -30
  14. package/dist/collection/components/popover/utils.js +1 -32
  15. package/dist/collection/components/refresher/refresher.js +70 -4
  16. package/dist/docs.json +49 -4
  17. package/dist/esm/ion-modal.entry.js +12 -220
  18. package/dist/esm/ion-popover.entry.js +11 -83
  19. package/dist/esm/ion-refresher_2.entry.js +32 -3
  20. package/dist/html.html-data.json +1 -1
  21. package/dist/ionic/ionic.esm.js +1 -1
  22. package/dist/ionic/p-90f2d3ff.entry.js +4 -0
  23. package/dist/ionic/p-91840a80.entry.js +4 -0
  24. package/dist/ionic/p-f9061316.entry.js +4 -0
  25. package/dist/types/components/modal/gestures/sheet.d.ts +1 -1
  26. package/dist/types/components/modal/gestures/swipe-to-close.d.ts +1 -1
  27. package/dist/types/components/modal/modal.d.ts +0 -47
  28. package/dist/types/components/popover/utils.d.ts +0 -2
  29. package/dist/types/components/refresher/refresher-interface.d.ts +7 -0
  30. package/dist/types/components/refresher/refresher.d.ts +16 -3
  31. package/dist/types/components.d.ts +16 -5
  32. package/dist/types/interface.d.ts +1 -1
  33. package/hydrate/index.js +54 -305
  34. package/hydrate/index.mjs +54 -305
  35. package/package.json +2 -4
  36. package/dist/ionic/p-639dd543.entry.js +0 -4
  37. package/dist/ionic/p-ec9ca3fe.entry.js +0 -4
  38. package/dist/ionic/p-ef4256eb.entry.js +0 -4
package/hydrate/index.js CHANGED
@@ -21581,7 +21581,7 @@ const calculateSpringStep = (t) => {
21581
21581
  const SwipeToCloseDefaults = {
21582
21582
  MIN_PRESENTING_SCALE: 0.915,
21583
21583
  };
21584
- const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onGestureMove) => {
21584
+ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) => {
21585
21585
  /**
21586
21586
  * The step value at which a card modal
21587
21587
  * is eligible for dismissing via gesture.
@@ -21738,8 +21738,6 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss, onG
21738
21738
  const processedStep = isAttemptingDismissWithCanDismiss ? calculateSpringStep(step / maxStep) : step;
21739
21739
  const clampedStep = clamp(0.0001, processedStep, maxStep);
21740
21740
  animation.progressStep(clampedStep);
21741
- // Notify modal of position change for safe-area updates
21742
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
21743
21741
  /**
21744
21742
  * When swiping down half way, the status bar style
21745
21743
  * should be reset to its default value.
@@ -22283,7 +22281,7 @@ const mdLeaveAnimation$2 = (baseEl, opts) => {
22283
22281
  return baseAnimation;
22284
22282
  };
22285
22283
 
22286
- const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange, onGestureMove) => {
22284
+ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, backdropBreakpoint, animation, breakpoints = [], expandToScroll, getCurrentBreakpoint, onDismiss, onBreakpointChange) => {
22287
22285
  // Defaults for the sheet swipe animation
22288
22286
  const defaultBackdrop = [
22289
22287
  { offset: 0, opacity: 'var(--backdrop-opacity)' },
@@ -22614,8 +22612,6 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
22614
22612
  : step;
22615
22613
  offset = clamp(0.0001, processedStep, maxStep);
22616
22614
  animation.progressStep(offset);
22617
- // Notify modal of position change for safe-area updates
22618
- onGestureMove === null || onGestureMove === void 0 ? void 0 : onGestureMove();
22619
22615
  };
22620
22616
  const onEnd = (detail) => {
22621
22617
  /**
@@ -22810,9 +22806,9 @@ const createSheetGesture = (baseEl, backdropEl, wrapperEl, initialBreakpoint, ba
22810
22806
  };
22811
22807
  };
22812
22808
 
22813
- 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}";
22809
+ 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}";
22814
22810
 
22815
- 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}";
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;--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}";
22816
22812
 
22817
22813
  // TODO(FW-2832): types
22818
22814
  /**
@@ -22845,10 +22841,6 @@ class Modal {
22845
22841
  this.inline = false;
22846
22842
  // Whether or not modal is being dismissed via gesture
22847
22843
  this.gestureAnimationDismissing = false;
22848
- // Whether to skip coordinate-based safe-area detection (for fullscreen phone modals)
22849
- this.skipSafeAreaCoordinateDetection = false;
22850
- // Track previous safe-area state to avoid redundant DOM writes
22851
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
22852
22844
  this.presented = false;
22853
22845
  /** @internal */
22854
22846
  this.hasController = false;
@@ -23039,10 +23031,7 @@ class Modal {
23039
23031
  }
23040
23032
  }
23041
23033
  onWindowResize() {
23042
- // Invalidate safe-area cache on resize (device rotation may change values)
23043
- this.cachedSafeAreas = undefined;
23044
- this.updateSafeAreaOverrides();
23045
- // Only handle view transition for iOS card modals when no custom animations are provided
23034
+ // Only handle resize for iOS card modals when no custom animations are provided
23046
23035
  if (getIonMode$1(this) !== 'ios' || !this.presentingElement || this.enterAnimation || this.leaveAnimation) {
23047
23036
  return;
23048
23037
  }
@@ -23065,8 +23054,6 @@ class Modal {
23065
23054
  this.triggerController.removeClickListener();
23066
23055
  this.cleanupViewTransitionListener();
23067
23056
  this.cleanupParentRemovalObserver();
23068
- // Reset safe-area state to handle removal without dismiss (e.g., framework unmount)
23069
- this.resetSafeAreaState();
23070
23057
  }
23071
23058
  componentWillLoad() {
23072
23059
  var _a;
@@ -23226,8 +23213,6 @@ class Modal {
23226
23213
  else if (!this.keepContentsMounted) {
23227
23214
  await waitForMount();
23228
23215
  }
23229
- // Predict safe-area needs based on modal configuration to avoid visual snap
23230
- this.setInitialSafeAreaOverrides(presentingElement);
23231
23216
  writeTask(() => this.el.classList.add('show-modal'));
23232
23217
  const hasCardModal = presentingElement !== undefined;
23233
23218
  /**
@@ -23289,8 +23274,6 @@ class Modal {
23289
23274
  else if (hasCardModal) {
23290
23275
  this.initSwipeToClose();
23291
23276
  }
23292
- // Now that animation is complete, update safe-area based on actual position
23293
- this.updateSafeAreaOverrides();
23294
23277
  // Initialize view transition listener for iOS card modals
23295
23278
  this.initViewTransitionListener();
23296
23279
  // Initialize parent removal observer
@@ -23342,7 +23325,7 @@ class Modal {
23342
23325
  await this.dismiss(undefined, GESTURE);
23343
23326
  this.gestureAnimationDismissing = false;
23344
23327
  });
23345
- }, () => this.updateSafeAreaOverrides());
23328
+ });
23346
23329
  this.gesture.enable(true);
23347
23330
  }
23348
23331
  initSheetGesture() {
@@ -23363,8 +23346,7 @@ class Modal {
23363
23346
  this.currentBreakpoint = breakpoint;
23364
23347
  this.ionBreakpointDidChange.emit({ breakpoint });
23365
23348
  }
23366
- this.updateSafeAreaOverrides();
23367
- }, () => this.updateSafeAreaOverrides());
23349
+ });
23368
23350
  this.gesture = gesture;
23369
23351
  this.moveSheetToBreakpoint = moveSheetToBreakpoint;
23370
23352
  this.gesture.enable(true);
@@ -23442,194 +23424,6 @@ class Modal {
23442
23424
  // Clear the cached reference
23443
23425
  this.cachedPageParent = undefined;
23444
23426
  }
23445
- /**
23446
- * Sets initial safe-area overrides based on modal configuration before
23447
- * the modal becomes visible. This predicts whether the modal will touch
23448
- * screen edges to avoid a visual snap after animation completes.
23449
- */
23450
- setInitialSafeAreaOverrides(presentingElement) {
23451
- const style = this.el.style;
23452
- const mode = getIonMode$1(this);
23453
- const isSheetModal = this.breakpoints !== undefined && this.initialBreakpoint !== undefined;
23454
- // Card modals only exist in iOS mode - in MD mode, presentingElement is ignored
23455
- const isCardModal = presentingElement !== undefined && mode === 'ios';
23456
- const isTablet = window.innerWidth >= 768;
23457
- // Sheet modals always touch bottom edge, never top/left/right
23458
- if (isSheetModal) {
23459
- style.setProperty('--ion-safe-area-top', '0px');
23460
- style.setProperty('--ion-safe-area-left', '0px');
23461
- style.setProperty('--ion-safe-area-right', '0px');
23462
- return;
23463
- }
23464
- // Card modals have rounded top corners
23465
- if (isCardModal) {
23466
- style.setProperty('--ion-safe-area-top', '0px');
23467
- if (isTablet) {
23468
- // On tablets, card modals are inset from all edges
23469
- this.zeroAllSafeAreas();
23470
- }
23471
- else {
23472
- // On phones, card modals still extend to the bottom edge
23473
- style.setProperty('--ion-safe-area-left', '0px');
23474
- style.setProperty('--ion-safe-area-right', '0px');
23475
- this.applyFullscreenSafeArea();
23476
- }
23477
- return;
23478
- }
23479
- // Check if modal is fullscreen via CSS custom properties
23480
- // This applies to both phone and tablet sizes - custom modals may have
23481
- // non-fullscreen dimensions even on phones (e.g., --height: 70%)
23482
- const computedStyle = getComputedStyle(this.el);
23483
- const width = computedStyle.getPropertyValue('--width').trim();
23484
- const height = computedStyle.getPropertyValue('--height').trim();
23485
- const isFullscreen = width === '100%' && height === '100%';
23486
- if (isFullscreen) {
23487
- this.applyFullscreenSafeArea();
23488
- }
23489
- else if (isTablet) {
23490
- // Centered dialog on tablet doesn't touch edges
23491
- this.zeroAllSafeAreas();
23492
- }
23493
- else ;
23494
- }
23495
- /**
23496
- * Applies safe-area handling for fullscreen modals.
23497
- * Adds wrapper padding when no footer is present to prevent
23498
- * content from overlapping system navigation areas.
23499
- */
23500
- applyFullscreenSafeArea() {
23501
- this.skipSafeAreaCoordinateDetection = true;
23502
- this.updateFooterPadding();
23503
- // Watch for dynamic footer additions/removals (e.g., async data loading)
23504
- // Use subtree:true to support wrapped footers in framework components
23505
- // (e.g., <my-footer><ion-footer>...</ion-footer></my-footer>)
23506
- if (!this.footerObserver && win$1 !== undefined && 'MutationObserver' in win$1) {
23507
- this.footerObserver = new MutationObserver(() => this.updateFooterPadding());
23508
- this.footerObserver.observe(this.el, { childList: true, subtree: true });
23509
- }
23510
- }
23511
- /**
23512
- * Updates wrapper and shadow padding based on footer presence.
23513
- * Called initially and when footer is dynamically added/removed.
23514
- * Both elements must be styled identically to prevent visual mismatches.
23515
- */
23516
- updateFooterPadding() {
23517
- if (!this.wrapperEl)
23518
- return;
23519
- const hasFooter = this.el.querySelector('ion-footer') !== null;
23520
- // Apply to both wrapper and shadow to keep them in sync
23521
- const elements = [this.wrapperEl, this.shadowEl].filter(Boolean);
23522
- if (hasFooter) {
23523
- elements.forEach((el) => {
23524
- el.style.removeProperty('padding-bottom');
23525
- el.style.removeProperty('box-sizing');
23526
- });
23527
- }
23528
- else {
23529
- elements.forEach((el) => {
23530
- el.style.setProperty('padding-bottom', 'var(--ion-safe-area-bottom, 0px)');
23531
- el.style.setProperty('box-sizing', 'border-box');
23532
- });
23533
- }
23534
- }
23535
- /**
23536
- * Sets all safe-area CSS variables to 0px for modals that
23537
- * don't touch screen edges.
23538
- */
23539
- zeroAllSafeAreas() {
23540
- const style = this.el.style;
23541
- style.setProperty('--ion-safe-area-top', '0px');
23542
- style.setProperty('--ion-safe-area-bottom', '0px');
23543
- style.setProperty('--ion-safe-area-left', '0px');
23544
- style.setProperty('--ion-safe-area-right', '0px');
23545
- }
23546
- /**
23547
- * Resets all safe-area related state and styles.
23548
- * Called during dismiss and disconnectedCallback to ensure clean state
23549
- * for re-presentation of inline modals.
23550
- */
23551
- resetSafeAreaState() {
23552
- var _a;
23553
- this.skipSafeAreaCoordinateDetection = false;
23554
- this.cachedSafeAreas = undefined;
23555
- this.prevSafeAreaState = { top: false, bottom: false, left: false, right: false };
23556
- (_a = this.footerObserver) === null || _a === void 0 ? void 0 : _a.disconnect();
23557
- this.footerObserver = undefined;
23558
- // Clear wrapper and shadow styles that may have been set for safe-area handling
23559
- [this.wrapperEl, this.shadowEl].forEach((el) => {
23560
- if (el) {
23561
- el.style.removeProperty('padding-bottom');
23562
- el.style.removeProperty('box-sizing');
23563
- }
23564
- });
23565
- // Clear safe-area CSS variable overrides
23566
- const style = this.el.style;
23567
- style.removeProperty('--ion-safe-area-top');
23568
- style.removeProperty('--ion-safe-area-bottom');
23569
- style.removeProperty('--ion-safe-area-left');
23570
- style.removeProperty('--ion-safe-area-right');
23571
- }
23572
- /**
23573
- * Gets the root safe-area values from the document element.
23574
- * Uses cached values during gestures to avoid getComputedStyle calls.
23575
- */
23576
- getSafeAreaValues() {
23577
- if (!this.cachedSafeAreas) {
23578
- const rootStyle = getComputedStyle(document.documentElement);
23579
- this.cachedSafeAreas = {
23580
- top: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-top')) || 0,
23581
- bottom: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-bottom')) || 0,
23582
- left: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-left')) || 0,
23583
- right: parseFloat(rootStyle.getPropertyValue('--ion-safe-area-right')) || 0,
23584
- };
23585
- }
23586
- return this.cachedSafeAreas;
23587
- }
23588
- /**
23589
- * Updates safe-area CSS variable overrides based on whether the modal
23590
- * extends into each safe-area region. Called after animation
23591
- * and during gestures to handle dynamic position changes.
23592
- *
23593
- * Optimized to avoid redundant DOM writes by tracking previous state.
23594
- */
23595
- updateSafeAreaOverrides() {
23596
- if (this.skipSafeAreaCoordinateDetection) {
23597
- return;
23598
- }
23599
- const wrapper = this.wrapperEl;
23600
- if (!wrapper) {
23601
- return;
23602
- }
23603
- const rect = wrapper.getBoundingClientRect();
23604
- const safeAreas = this.getSafeAreaValues();
23605
- const extendsIntoTop = rect.top < safeAreas.top;
23606
- const extendsIntoBottom = rect.bottom > window.innerHeight - safeAreas.bottom;
23607
- const extendsIntoLeft = rect.left < safeAreas.left;
23608
- const extendsIntoRight = rect.right > window.innerWidth - safeAreas.right;
23609
- // Only update DOM when state actually changes
23610
- const prev = this.prevSafeAreaState;
23611
- const style = this.el.style;
23612
- if (extendsIntoTop !== prev.top) {
23613
- extendsIntoTop ? style.removeProperty('--ion-safe-area-top') : style.setProperty('--ion-safe-area-top', '0px');
23614
- prev.top = extendsIntoTop;
23615
- }
23616
- if (extendsIntoBottom !== prev.bottom) {
23617
- extendsIntoBottom
23618
- ? style.removeProperty('--ion-safe-area-bottom')
23619
- : style.setProperty('--ion-safe-area-bottom', '0px');
23620
- prev.bottom = extendsIntoBottom;
23621
- }
23622
- if (extendsIntoLeft !== prev.left) {
23623
- extendsIntoLeft ? style.removeProperty('--ion-safe-area-left') : style.setProperty('--ion-safe-area-left', '0px');
23624
- prev.left = extendsIntoLeft;
23625
- }
23626
- if (extendsIntoRight !== prev.right) {
23627
- extendsIntoRight
23628
- ? style.removeProperty('--ion-safe-area-right')
23629
- : style.setProperty('--ion-safe-area-right', '0px');
23630
- prev.right = extendsIntoRight;
23631
- }
23632
- }
23633
23427
  sheetOnDismiss() {
23634
23428
  /**
23635
23429
  * While the gesture animation is finishing
@@ -23722,8 +23516,6 @@ class Modal {
23722
23516
  }
23723
23517
  this.currentBreakpoint = undefined;
23724
23518
  this.animation = undefined;
23725
- // Reset safe-area state for potential re-presentation
23726
- this.resetSafeAreaState();
23727
23519
  unlock();
23728
23520
  return dismissed;
23729
23521
  }
@@ -23973,20 +23765,20 @@ class Modal {
23973
23765
  const isCardModal = presentingElement !== undefined && mode === 'ios';
23974
23766
  const isHandleCycle = handleBehavior === 'cycle';
23975
23767
  const isSheetModalWithHandle = isSheetModal && showHandle;
23976
- return (hAsync(Host, Object.assign({ key: '8f42c71ec9c9270b4218ca7eec57ca998871ac07', "no-router": true,
23768
+ return (hAsync(Host, Object.assign({ key: '87328006ea6c75ebc518ace300438492a567223e', "no-router": true,
23977
23769
  // Allow the modal to be navigable when the handle is focusable
23978
23770
  tabIndex: isHandleCycle && isSheetModalWithHandle ? 0 : -1 }, htmlAttributes, { style: {
23979
23771
  zIndex: `${20000 + this.overlayIndex}`,
23980
- }, 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: '2f7b08b019c66cbfceacafa9a68b91f8da1f4084', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: '6b0d1ae90e8483332f74371be5dcbd0a7b2661ba', class: "modal-shadow", ref: (el) => (this.shadowEl = el) }), hAsync("div", Object.assign({ key: 'c96f671fb25e5de45231ea65ac52500547b2e262',
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: 'ee94ff8e09b691dd4ad4e4db1720f06bc3c5a469', ref: (el) => (this.backdropEl = el), visible: this.showBackdrop, tappable: this.backdropDismiss, part: "backdrop" }), mode === 'ios' && hAsync("div", { key: 'bffd69b4635c22d9f249725bd952c1e93d5615c7', class: "modal-shadow" }), hAsync("div", Object.assign({ key: '1d394d3c68916e464ff1fbf5242419f4a3d3cca1',
23981
23773
  /*
23982
23774
  role and aria-modal must be used on the
23983
23775
  same element. They must also be set inside the
23984
23776
  shadow DOM otherwise ion-button will not be highlighted
23985
23777
  when using VoiceOver: https://bugs.webkit.org/show_bug.cgi?id=247134
23986
23778
  */
23987
- role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: 'e07419b01a588c0ba62ed9607c843cac7bd26337', class: "modal-handle",
23779
+ role: "dialog" }, inheritedAttributes, { "aria-modal": "true", class: "modal-wrapper ion-overlay-wrapper", part: "content", ref: (el) => (this.wrapperEl = el) }), showHandle && (hAsync("button", { key: '2dcf58792018e557e0c323baad2d672bc99c0bb1', class: "modal-handle",
23988
23780
  // Prevents the handle from receiving keyboard focus when it does not cycle
23989
- 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: '65f35079df8eb8b6cd262e3a08297866715ce7ea', onSlotchange: this.onSlotChange }))));
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: '44164b1e8710c3895400ad9f44ecd99873874ad5', onSlotchange: this.onSlotChange }))));
23990
23782
  }
23991
23783
  get el() { return getElement(this); }
23992
23784
  static get watchers() { return {
@@ -27564,8 +27356,6 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27564
27356
  let bottom;
27565
27357
  let originX = contentOriginX;
27566
27358
  let originY = contentOriginY;
27567
- let checkSafeAreaTop = false;
27568
- let checkSafeAreaBottom = false;
27569
27359
  let checkSafeAreaLeft = false;
27570
27360
  let checkSafeAreaRight = false;
27571
27361
  const triggerTop = triggerCoordinates
@@ -27610,18 +27400,10 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27610
27400
  * We chose 12 here so that the popover position looks a bit nicer as
27611
27401
  * it is not right up against the edge of the screen.
27612
27402
  */
27613
- top = Math.max(bodyPadding, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
27403
+ top = Math.max(12, triggerTop - contentHeight - triggerHeight - (arrowHeight - 1));
27614
27404
  arrowTop = top + contentHeight;
27615
27405
  originY = 'bottom';
27616
27406
  addPopoverBottomClass = true;
27617
- /**
27618
- * If the popover is positioned near the top edge, account for safe area.
27619
- * This ensures the popover doesn't overlap with status bars or notches.
27620
- */
27621
- if (top <= bodyPadding + safeAreaMargin) {
27622
- checkSafeAreaTop = true;
27623
- top = bodyPadding;
27624
- }
27625
27407
  /**
27626
27408
  * If not enough room for popover to appear
27627
27409
  * above trigger, then cut it off.
@@ -27629,35 +27411,14 @@ const calculateWindowAdjustment = (side, coordTop, coordLeft, bodyPadding, bodyW
27629
27411
  }
27630
27412
  else {
27631
27413
  bottom = bodyPadding;
27632
- /**
27633
- * When the popover is pinned to the bottom, account for safe area.
27634
- * This ensures the popover doesn't overlap with home indicators
27635
- * or navigation bars (e.g., Android API 36+ edge-to-edge).
27636
- */
27637
- checkSafeAreaBottom = true;
27638
27414
  }
27639
27415
  }
27640
- /**
27641
- * Final check: If the popover extends into any safe-area region,
27642
- * ensure the corresponding flag is set regardless of side.
27643
- * This handles cases where a side-positioned popover (left/right)
27644
- * still needs bottom safe-area padding because it extends into that region.
27645
- */
27646
- const popoverBottom = bottom !== undefined ? bodyHeight - bottom : top + contentHeight;
27647
- if (popoverBottom + safeAreaMargin > bodyHeight) {
27648
- checkSafeAreaBottom = true;
27649
- }
27650
- if (top < safeAreaMargin) {
27651
- checkSafeAreaTop = true;
27652
- }
27653
27416
  return {
27654
27417
  top,
27655
27418
  left,
27656
27419
  bottom,
27657
27420
  originX,
27658
27421
  originY,
27659
- checkSafeAreaTop,
27660
- checkSafeAreaBottom,
27661
27422
  checkSafeAreaLeft,
27662
27423
  checkSafeAreaRight,
27663
27424
  arrowTop,
@@ -27718,7 +27479,7 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
27718
27479
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, arrowWidth, arrowHeight, reference, side, align, defaultPosition, trigger, ev);
27719
27480
  const padding = size === 'cover' ? 0 : POPOVER_IOS_BODY_PADDING;
27720
27481
  const margin = size === 'cover' ? 0 : 25;
27721
- 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);
27482
+ 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);
27722
27483
  const baseAnimation = createAnimation();
27723
27484
  const backdropAnimation = createAnimation();
27724
27485
  const contentAnimation = createAnimation();
@@ -27748,35 +27509,19 @@ const iosEnterAnimation$1 = (baseEl, opts) => {
27748
27509
  if (addPopoverBottomClass) {
27749
27510
  baseEl.classList.add('popover-bottom');
27750
27511
  }
27751
- /**
27752
- * Safe area CSS variable adjustments.
27753
- * When the popover is positioned near an edge, we add the corresponding
27754
- * safe-area inset to ensure the popover doesn't overlap with system UI
27755
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
27756
- */
27757
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
27758
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
27512
+ if (bottom !== undefined) {
27513
+ contentEl.style.setProperty('bottom', `${bottom}px`);
27514
+ }
27759
27515
  const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
27760
27516
  const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
27761
- let topValue = `${top}px`;
27762
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
27763
27517
  let leftValue = `${left}px`;
27764
- if (checkSafeAreaTop) {
27765
- topValue = `${top}px${safeAreaTop}`;
27766
- }
27767
- if (checkSafeAreaBottom && bottomValue !== undefined) {
27768
- bottomValue = `${bottom}px${safeAreaBottom}`;
27769
- }
27770
27518
  if (checkSafeAreaLeft) {
27771
27519
  leftValue = `${left}px${safeAreaLeft}`;
27772
27520
  }
27773
27521
  if (checkSafeAreaRight) {
27774
27522
  leftValue = `${left}px${safeAreaRight}`;
27775
27523
  }
27776
- if (bottomValue !== undefined) {
27777
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
27778
- }
27779
- contentEl.style.setProperty('top', `calc(${topValue} + var(--offset-y, 0))`);
27524
+ contentEl.style.setProperty('top', `calc(${top}px + var(--offset-y, 0))`);
27780
27525
  contentEl.style.setProperty('left', `calc(${leftValue} + var(--offset-x, 0))`);
27781
27526
  contentEl.style.setProperty('transform-origin', `${originY} ${originX}`);
27782
27527
  if (arrowEl !== null) {
@@ -27852,32 +27597,7 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
27852
27597
  };
27853
27598
  const results = getPopoverPosition(isRTL, contentWidth, contentHeight, 0, 0, reference, side, align, defaultPosition, trigger, ev);
27854
27599
  const padding = size === 'cover' ? 0 : POPOVER_MD_BODY_PADDING;
27855
- 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);
27856
- /**
27857
- * Safe area CSS variable adjustments.
27858
- * When the popover is positioned near an edge, we add the corresponding
27859
- * safe-area inset to ensure the popover doesn't overlap with system UI
27860
- * (status bars, home indicators, navigation bars on Android API 36+, etc.)
27861
- */
27862
- const safeAreaTop = ' + var(--ion-safe-area-top, 0)';
27863
- const safeAreaBottom = ' + var(--ion-safe-area-bottom, 0)';
27864
- const safeAreaLeft = ' + var(--ion-safe-area-left, 0)';
27865
- const safeAreaRight = ' - var(--ion-safe-area-right, 0)';
27866
- let topValue = `${top}px`;
27867
- let bottomValue = bottom !== undefined ? `${bottom}px` : undefined;
27868
- let leftValue = `${left}px`;
27869
- if (checkSafeAreaTop) {
27870
- topValue = `${top}px${safeAreaTop}`;
27871
- }
27872
- if (checkSafeAreaBottom && bottomValue !== undefined) {
27873
- bottomValue = `${bottom}px${safeAreaBottom}`;
27874
- }
27875
- if (checkSafeAreaLeft) {
27876
- leftValue = `${left}px${safeAreaLeft}`;
27877
- }
27878
- if (checkSafeAreaRight) {
27879
- leftValue = `${left}px${safeAreaRight}`;
27880
- }
27600
+ const { originX, originY, top, left, bottom } = calculateWindowAdjustment(side, results.top, results.left, padding, bodyWidth, bodyHeight, contentWidth, contentHeight, 0, results.originX, results.originY, results.referenceCoordinates);
27881
27601
  const baseAnimation = createAnimation();
27882
27602
  const backdropAnimation = createAnimation();
27883
27603
  const wrapperAnimation = createAnimation();
@@ -27894,13 +27614,13 @@ const mdEnterAnimation$1 = (baseEl, opts) => {
27894
27614
  contentAnimation
27895
27615
  .addElement(contentEl)
27896
27616
  .beforeStyles({
27897
- top: `calc(${topValue} + var(--offset-y, 0px))`,
27898
- left: `calc(${leftValue} + var(--offset-x, 0px))`,
27617
+ top: `calc(${top}px + var(--offset-y, 0px))`,
27618
+ left: `calc(${left}px + var(--offset-x, 0px))`,
27899
27619
  'transform-origin': `${originY} ${originX}`,
27900
27620
  })
27901
27621
  .beforeAddWrite(() => {
27902
- if (bottomValue !== undefined) {
27903
- contentEl.style.setProperty('bottom', `calc(${bottomValue})`);
27622
+ if (bottom !== undefined) {
27623
+ contentEl.style.setProperty('bottom', `${bottom}px`);
27904
27624
  }
27905
27625
  })
27906
27626
  .fromTo('transform', 'scale(0.8)', 'scale(1)');
@@ -30017,6 +29737,8 @@ class Refresher {
30017
29737
  this.ionRefresh = createEvent(this, "ionRefresh", 7);
30018
29738
  this.ionPull = createEvent(this, "ionPull", 7);
30019
29739
  this.ionStart = createEvent(this, "ionStart", 7);
29740
+ this.ionPullStart = createEvent(this, "ionPullStart", 7);
29741
+ this.ionPullEnd = createEvent(this, "ionPullEnd", 7);
30020
29742
  this.appliedStyles = false;
30021
29743
  this.didStart = false;
30022
29744
  this.progress = 0;
@@ -30071,8 +29793,8 @@ class Refresher {
30071
29793
  * than `1`. The default value is `1` which is equal to the speed of the cursor.
30072
29794
  * If a negative value is passed in, the factor will be `1` instead.
30073
29795
  *
30074
- * For example: If the value passed is `1.2` and the content is dragged by
30075
- * `10` pixels, instead of `10` pixels the content will be pulled by `12` pixels
29796
+ * For example, If the value passed is `1.2` and the content is dragged by
29797
+ * `10` pixels, instead of `10` pixels, the content will be pulled by `12` pixels
30076
29798
  * (an increase of 20 percent). If the value passed is `0.8`, the dragged amount
30077
29799
  * will be `8` pixels, less than the amount the cursor has moved.
30078
29800
  *
@@ -30122,6 +29844,9 @@ class Refresher {
30122
29844
  this.animations = [];
30123
29845
  this.progress = 0;
30124
29846
  this.state = 1 /* RefresherState.Inactive */;
29847
+ this.ionPullEnd.emit({
29848
+ reason: state === 32 /* RefresherState.Completing */ ? 'complete' : 'cancel',
29849
+ });
30125
29850
  }
30126
29851
  async setupiOSNativeRefresher(pullingSpinner, refreshingSpinner) {
30127
29852
  this.elementToTransform = this.scrollEl;
@@ -30154,6 +29879,7 @@ class Refresher {
30154
29879
  if (!this.didStart) {
30155
29880
  this.didStart = true;
30156
29881
  this.ionStart.emit();
29882
+ this.ionPullStart.emit();
30157
29883
  }
30158
29884
  // emit "pulling" on every move
30159
29885
  if (this.pointerDown) {
@@ -30229,6 +29955,7 @@ class Refresher {
30229
29955
  this.lastVelocityY = ev.velocityY;
30230
29956
  },
30231
29957
  onEnd: () => {
29958
+ const hadStarted = this.didStart;
30232
29959
  this.pointerDown = false;
30233
29960
  this.didStart = false;
30234
29961
  if (this.needsCompletion) {
@@ -30238,6 +29965,13 @@ class Refresher {
30238
29965
  else if (this.didRefresh) {
30239
29966
  readTask(() => translateElement(this.elementToTransform, `${this.el.clientHeight}px`));
30240
29967
  }
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
+ }
30241
29975
  },
30242
29976
  });
30243
29977
  this.disabledChanged();
@@ -30284,6 +30018,7 @@ class Refresher {
30284
30018
  ev.data.animation = animation;
30285
30019
  animation.progressStart(false, 0);
30286
30020
  this.ionStart.emit();
30021
+ this.ionPullStart.emit();
30287
30022
  this.animations.push(animation);
30288
30023
  return;
30289
30024
  }
@@ -30306,6 +30041,7 @@ class Refresher {
30306
30041
  this.animations = [];
30307
30042
  this.gesture.enable(true);
30308
30043
  this.state = 1 /* RefresherState.Inactive */;
30044
+ this.ionPullEnd.emit({ reason: 'cancel' });
30309
30045
  });
30310
30046
  return;
30311
30047
  }
@@ -30550,6 +30286,7 @@ class Refresher {
30550
30286
  if (!this.didStart) {
30551
30287
  this.didStart = true;
30552
30288
  this.ionStart.emit();
30289
+ this.ionPullStart.emit();
30553
30290
  }
30554
30291
  // emit "pulling" on every move
30555
30292
  this.ionPull.emit();
@@ -30593,6 +30330,15 @@ class Refresher {
30593
30330
  * available right away.
30594
30331
  */
30595
30332
  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
+ }
30596
30342
  }
30597
30343
  }
30598
30344
  beginRefresh() {
@@ -30639,6 +30385,9 @@ class Refresher {
30639
30385
  if (this.contentFullscreen && this.backgroundContentEl) {
30640
30386
  (_a = this.backgroundContentEl) === null || _a === void 0 ? void 0 : _a.style.removeProperty('--offset-top');
30641
30387
  }
30388
+ this.ionPullEnd.emit({
30389
+ reason: state === 32 /* RefresherState.Completing */ ? 'complete' : 'cancel',
30390
+ });
30642
30391
  }, 600);
30643
30392
  // reset the styles on the scroll element
30644
30393
  // set that the refresh is actively cancelling/completing
@@ -30692,7 +30441,7 @@ class Refresher {
30692
30441
  }
30693
30442
  render() {
30694
30443
  const mode = getIonMode$1(this);
30695
- return (hAsync(Host, { key: '2d1bd880877b698604542ab2d602d38b9504d975', slot: "fixed", class: {
30444
+ return (hAsync(Host, { key: 'bed0c2f44c32ecac24b7d8326ac7c7ef1d12c44f', slot: "fixed", class: {
30696
30445
  [mode]: true,
30697
30446
  // Used internally for styling
30698
30447
  [`refresher-${mode}`]: true,