@ionic/core 7.5.4-dev.11699632730.1e6d9b70 → 7.5.4-dev.11700602203.1e7155a1

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 (105) hide show
  1. package/components/ion-infinite-scroll.js +11 -3
  2. package/components/ion-modal.js +2 -2
  3. package/components/ion-toast.js +417 -67
  4. package/components/overlays.js +2 -1
  5. package/css/ionic.bundle.css +1 -1
  6. package/css/ionic.bundle.css.map +1 -1
  7. package/css/utils.bundle.css +0 -133
  8. package/css/utils.bundle.css.map +1 -1
  9. package/dist/cjs/index.cjs.js +1 -1
  10. package/dist/cjs/ion-action-sheet.cjs.entry.js +1 -1
  11. package/dist/cjs/ion-alert.cjs.entry.js +1 -1
  12. package/dist/cjs/ion-datetime_3.cjs.entry.js +1 -1
  13. package/dist/cjs/ion-infinite-scroll_2.cjs.entry.js +11 -3
  14. package/dist/cjs/ion-loading.cjs.entry.js +1 -1
  15. package/dist/cjs/ion-menu_3.cjs.entry.js +1 -1
  16. package/dist/cjs/ion-modal.cjs.entry.js +2 -2
  17. package/dist/cjs/ion-popover.cjs.entry.js +1 -1
  18. package/dist/cjs/ion-select_3.cjs.entry.js +1 -1
  19. package/dist/cjs/ion-toast.cjs.entry.js +430 -83
  20. package/dist/cjs/ionic.cjs.js +1 -1
  21. package/dist/cjs/loader.cjs.js +1 -1
  22. package/dist/cjs/{overlays-d80a2cf2.js → overlays-ba1a1e21.js} +2 -0
  23. package/dist/collection/components/infinite-scroll/infinite-scroll.js +11 -3
  24. package/dist/collection/components/infinite-scroll/test/small-dom-update/infinite-scroll.e2e.js +31 -0
  25. package/dist/collection/components/modal/gestures/swipe-to-close.js +2 -1
  26. package/dist/collection/components/toast/animations/ios.enter.js +2 -1
  27. package/dist/collection/components/toast/animations/md.enter.js +2 -1
  28. package/dist/collection/components/toast/animations/utils.js +11 -0
  29. package/dist/collection/components/toast/gestures/swipe-to-dismiss.js +267 -0
  30. package/dist/collection/components/toast/test/swipe-gesture/toast.e2e.js +84 -0
  31. package/dist/collection/components/toast/toast.js +104 -1
  32. package/dist/collection/utils/overlays.js +1 -0
  33. package/dist/docs.json +37 -1
  34. package/dist/esm/index.js +1 -1
  35. package/dist/esm/ion-action-sheet.entry.js +1 -1
  36. package/dist/esm/ion-alert.entry.js +1 -1
  37. package/dist/esm/ion-datetime_3.entry.js +1 -1
  38. package/dist/esm/ion-infinite-scroll_2.entry.js +11 -3
  39. package/dist/esm/ion-loading.entry.js +1 -1
  40. package/dist/esm/ion-menu_3.entry.js +1 -1
  41. package/dist/esm/ion-modal.entry.js +2 -2
  42. package/dist/esm/ion-popover.entry.js +1 -1
  43. package/dist/esm/ion-select_3.entry.js +1 -1
  44. package/dist/esm/ion-toast.entry.js +413 -66
  45. package/dist/esm/ionic.js +1 -1
  46. package/dist/esm/loader.js +1 -1
  47. package/dist/esm/{overlays-a31c8878.js → overlays-d79b34b4.js} +2 -1
  48. package/dist/esm-es5/index.js +1 -1
  49. package/dist/esm-es5/ion-action-sheet.entry.js +1 -1
  50. package/dist/esm-es5/ion-alert.entry.js +1 -1
  51. package/dist/esm-es5/ion-datetime_3.entry.js +1 -1
  52. package/dist/esm-es5/ion-infinite-scroll_2.entry.js +1 -1
  53. package/dist/esm-es5/ion-loading.entry.js +1 -1
  54. package/dist/esm-es5/ion-menu_3.entry.js +1 -1
  55. package/dist/esm-es5/ion-modal.entry.js +1 -1
  56. package/dist/esm-es5/ion-popover.entry.js +1 -1
  57. package/dist/esm-es5/ion-select_3.entry.js +1 -1
  58. package/dist/esm-es5/ion-toast.entry.js +1 -1
  59. package/dist/esm-es5/ionic.js +1 -1
  60. package/dist/esm-es5/loader.js +1 -1
  61. package/dist/esm-es5/{overlays-a31c8878.js → overlays-d79b34b4.js} +1 -1
  62. package/dist/html.html-data.json +9 -0
  63. package/dist/ionic/index.esm.js +1 -1
  64. package/dist/ionic/ionic.esm.js +1 -1
  65. package/dist/ionic/{p-c32a6c04.entry.js → p-020dd47f.entry.js} +1 -1
  66. package/dist/ionic/p-02663394.system.js +1 -1
  67. package/dist/ionic/{p-b86c6c05.entry.js → p-138a4a93.entry.js} +1 -1
  68. package/dist/ionic/{p-4c75cb48.system.js → p-1511dc35.system.js} +1 -1
  69. package/dist/ionic/{p-3a769110.system.entry.js → p-1fc6b5a5.system.entry.js} +1 -1
  70. package/dist/ionic/{p-90020ef0.system.entry.js → p-2151a4a4.system.entry.js} +1 -1
  71. package/dist/ionic/{p-539c09e8.system.entry.js → p-2ae9a434.system.entry.js} +1 -1
  72. package/dist/ionic/{p-1e59dd04.system.entry.js → p-3dedd258.system.entry.js} +1 -1
  73. package/dist/ionic/{p-98c70e3c.system.js → p-53f75103.system.js} +1 -1
  74. package/dist/ionic/{p-75338011.entry.js → p-664cc492.entry.js} +1 -1
  75. package/dist/ionic/p-6c89025e.system.entry.js +4 -0
  76. package/dist/ionic/{p-8140a084.entry.js → p-78a8e1e8.entry.js} +1 -1
  77. package/dist/ionic/p-791ac0a8.system.entry.js +4 -0
  78. package/dist/ionic/{p-d54a8393.system.entry.js → p-81bdfa1c.system.entry.js} +1 -1
  79. package/dist/ionic/p-8414ed31.entry.js +4 -0
  80. package/dist/ionic/{p-307963db.js → p-938c4956.js} +1 -1
  81. package/dist/ionic/{p-c09fa14d.entry.js → p-a9424586.entry.js} +1 -1
  82. package/dist/ionic/{p-59107f99.system.entry.js → p-ab1908ba.system.entry.js} +1 -1
  83. package/dist/ionic/{p-e91c7eb4.system.entry.js → p-cb6c380e.system.entry.js} +1 -1
  84. package/dist/ionic/{p-a4e4dba1.entry.js → p-e119ac8b.entry.js} +1 -1
  85. package/dist/ionic/p-e3b7a2d0.system.entry.js +4 -0
  86. package/dist/ionic/{p-53b1930e.entry.js → p-e525dbea.entry.js} +1 -1
  87. package/dist/ionic/p-f2d4e3c8.entry.js +4 -0
  88. package/dist/ionic/p-fb415d64.entry.js +4 -0
  89. package/dist/types/components/infinite-scroll/infinite-scroll.d.ts +7 -0
  90. package/dist/types/components/toast/animations/utils.d.ts +9 -0
  91. package/dist/types/components/toast/gestures/swipe-to-dismiss.d.ts +9 -0
  92. package/dist/types/components/toast/toast-interface.d.ts +1 -0
  93. package/dist/types/components/toast/toast.d.ts +28 -1
  94. package/dist/types/components.d.ts +10 -2
  95. package/dist/types/utils/overlays.d.ts +1 -0
  96. package/hydrate/index.js +424 -69
  97. package/package.json +1 -1
  98. package/css/themes/dark.css +0 -135
  99. package/css/themes/dark.css.map +0 -1
  100. package/dist/ionic/p-139bd79d.system.entry.js +0 -4
  101. package/dist/ionic/p-7d68e4ea.entry.js +0 -4
  102. package/dist/ionic/p-999a7271.entry.js +0 -4
  103. package/dist/ionic/p-c4381a06.entry.js +0 -4
  104. package/dist/ionic/p-e66f7d10.system.entry.js +0 -4
  105. package/dist/ionic/p-f8febce9.system.entry.js +0 -4
@@ -39,7 +39,7 @@ import { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
39
39
  import { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
40
40
  import { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
41
41
  import { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
42
- import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
42
+ import { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
43
43
  import { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
44
44
  export { AccordionGroupChangeEventDetail } from "./components/accordion-group/accordion-group-interface";
45
45
  export { AnimationBuilder, AutocompleteTypes, Color, ComponentProps, ComponentRef, FrameworkDelegate, StyleEventDetail, TextFieldTypes } from "./interface";
@@ -75,7 +75,7 @@ export { SelectChangeEventDetail, SelectCompareFn, SelectInterface } from "./com
75
75
  export { SelectPopoverOption } from "./components/select-popover/select-popover-interface";
76
76
  export { TabBarChangedEventDetail, TabButtonClickEventDetail, TabButtonLayout } from "./components/tab-bar/tab-bar-interface";
77
77
  export { TextareaChangeEventDetail, TextareaInputEventDetail } from "./components/textarea/textarea-interface";
78
- export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions } from "./components/toast/toast-interface";
78
+ export { ToastButton, ToastDismissOptions, ToastLayout, ToastPosition, ToastPresentOptions, ToastSwipeGestureDirection } from "./components/toast/toast-interface";
79
79
  export { ToggleChangeEventDetail } from "./components/toggle/toggle-interface";
80
80
  export namespace Components {
81
81
  interface IonAccordion {
@@ -3172,6 +3172,10 @@ export namespace Components {
3172
3172
  * Present the toast overlay after it has been created.
3173
3173
  */
3174
3174
  "present": () => Promise<void>;
3175
+ /**
3176
+ * If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
3177
+ */
3178
+ "swipeGesture"?: ToastSwipeGestureDirection;
3175
3179
  /**
3176
3180
  * If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
3177
3181
  */
@@ -7810,6 +7814,10 @@ declare namespace LocalJSX {
7810
7814
  * The element to anchor the toast's position to. Can be set as a direct reference or the ID of the element. With `position="bottom"`, the toast will sit above the chosen element. With `position="top"`, the toast will sit below the chosen element. With `position="middle"`, the value of `positionAnchor` is ignored.
7811
7815
  */
7812
7816
  "positionAnchor"?: HTMLElement | string;
7817
+ /**
7818
+ * If set to 'vertical', the Toast can be dismissed with a swipe gesture. The swipe direction is determined by the value of the `position` property: `top`: The Toast can be swiped up to dismiss. `bottom`: The Toast can be swiped down to dismiss. `middle`: The Toast can be swiped up or down to dismiss.
7819
+ */
7820
+ "swipeGesture"?: ToastSwipeGestureDirection;
7813
7821
  /**
7814
7822
  * If `true`, the toast will be translucent. Only applies when the mode is `"ios"` and the device supports [`backdrop-filter`](https://developer.mozilla.org/en-US/docs/Web/CSS/backdrop-filter#Browser_compatibility).
7815
7823
  */
@@ -99,6 +99,7 @@ export declare const isCancel: (role: string | undefined) => boolean;
99
99
  export declare const safeCall: (handler: any, arg?: any) => any;
100
100
  export declare const BACKDROP = "backdrop";
101
101
  export declare const GESTURE = "gesture";
102
+ export declare const OVERLAY_GESTURE_PRIORITY = 39;
102
103
  /**
103
104
  * Creates a delegate controller.
104
105
  *
package/hydrate/index.js CHANGED
@@ -8728,6 +8728,7 @@ const safeCall = (handler, arg) => {
8728
8728
  };
8729
8729
  const BACKDROP = 'backdrop';
8730
8730
  const GESTURE = 'gesture';
8731
+ const OVERLAY_GESTURE_PRIORITY = 39;
8731
8732
  /**
8732
8733
  * Creates a delegate controller.
8733
8734
  *
@@ -18178,6 +18179,13 @@ class InfiniteScroll {
18178
18179
  this.ionInfinite = createEvent(this, "ionInfinite", 7);
18179
18180
  this.thrPx = 0;
18180
18181
  this.thrPc = 0;
18182
+ /**
18183
+ * didFire exists so that ionInfinite
18184
+ * does not fire multiple times if
18185
+ * users continue to scroll after
18186
+ * scrolling into the infinite
18187
+ * scroll threshold.
18188
+ */
18181
18189
  this.didFire = false;
18182
18190
  this.isBusy = false;
18183
18191
  this.onScroll = () => {
@@ -18205,9 +18213,6 @@ class InfiniteScroll {
18205
18213
  return 3;
18206
18214
  }
18207
18215
  }
18208
- else {
18209
- this.didFire = false;
18210
- }
18211
18216
  return 4;
18212
18217
  };
18213
18218
  this.isLoading = false;
@@ -18307,11 +18312,15 @@ class InfiniteScroll {
18307
18312
  writeTask(() => {
18308
18313
  scrollEl.scrollTop = newScrollTop;
18309
18314
  this.isBusy = false;
18315
+ this.didFire = false;
18310
18316
  });
18311
18317
  });
18312
18318
  });
18313
18319
  });
18314
18320
  }
18321
+ else {
18322
+ this.didFire = false;
18323
+ }
18315
18324
  }
18316
18325
  canStart() {
18317
18326
  return !this.disabled && !this.isBusy && !!this.scrollEl && !this.isLoading;
@@ -22299,7 +22308,7 @@ const createSwipeToCloseGesture = (el, animation, statusBarStyle, onDismiss) =>
22299
22308
  const gesture = createGesture({
22300
22309
  el,
22301
22310
  gestureName: 'modalSwipeToClose',
22302
- gesturePriority: 39,
22311
+ gesturePriority: OVERLAY_GESTURE_PRIORITY,
22303
22312
  direction: 'y',
22304
22313
  threshold: 10,
22305
22314
  canStart,
@@ -33963,6 +33972,93 @@ class Thumbnail {
33963
33972
  }; }
33964
33973
  }
33965
33974
 
33975
+ /**
33976
+ * Calculate the CSS top and bottom position of the toast, to be used
33977
+ * as starting points for the animation keyframes.
33978
+ *
33979
+ * The default animations for both MD and iOS
33980
+ * use translateY, which calculates from the
33981
+ * top edge of the screen. This behavior impacts
33982
+ * how we compute the offset when a toast has
33983
+ * position='bottom' since we need to calculate from
33984
+ * the bottom edge of the screen instead.
33985
+ *
33986
+ * @param position The value of the toast's position prop.
33987
+ * @param positionAnchor The element the toast should be anchored to,
33988
+ * if applicable.
33989
+ * @param mode The toast component's mode (md, ios, etc).
33990
+ * @param toast A reference to the toast element itself.
33991
+ */
33992
+ function getAnimationPosition(position, positionAnchor, mode, toast) {
33993
+ /**
33994
+ * Start with a predefined offset from the edge the toast will be
33995
+ * positioned relative to, whether on the screen or anchor element.
33996
+ */
33997
+ let offset;
33998
+ if (mode === 'md') {
33999
+ offset = position === 'top' ? 8 : -8;
34000
+ }
34001
+ else {
34002
+ offset = position === 'top' ? 10 : -10;
34003
+ }
34004
+ /**
34005
+ * If positionAnchor is defined, add in the distance from the target
34006
+ * screen edge to the target anchor edge. For position="top", the
34007
+ * bottom anchor edge is targeted. For position="bottom", the top
34008
+ * anchor edge is targeted.
34009
+ */
34010
+ if (positionAnchor && win$1) {
34011
+ warnIfAnchorIsHidden(positionAnchor, toast);
34012
+ const box = positionAnchor.getBoundingClientRect();
34013
+ if (position === 'top') {
34014
+ offset += box.bottom;
34015
+ }
34016
+ else if (position === 'bottom') {
34017
+ /**
34018
+ * Just box.top is the distance from the top edge of the screen
34019
+ * to the top edge of the anchor. We want to calculate from the
34020
+ * bottom edge of the screen instead.
34021
+ */
34022
+ offset -= win$1.innerHeight - box.top;
34023
+ }
34024
+ /**
34025
+ * We don't include safe area here because that should already be
34026
+ * accounted for when checking the position of the anchor.
34027
+ */
34028
+ return {
34029
+ top: `${offset}px`,
34030
+ bottom: `${offset}px`,
34031
+ };
34032
+ }
34033
+ else {
34034
+ return {
34035
+ top: `calc(${offset}px + var(--ion-safe-area-top, 0px))`,
34036
+ bottom: `calc(${offset}px - var(--ion-safe-area-bottom, 0px))`,
34037
+ };
34038
+ }
34039
+ }
34040
+ /**
34041
+ * If the anchor element is hidden, getBoundingClientRect()
34042
+ * will return all 0s for it, which can cause unexpected
34043
+ * results in the position calculation when animating.
34044
+ */
34045
+ function warnIfAnchorIsHidden(positionAnchor, toast) {
34046
+ if (positionAnchor.offsetParent === null) {
34047
+ printIonWarning('The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.', toast);
34048
+ }
34049
+ }
34050
+ /**
34051
+ * Returns the top offset required to place
34052
+ * the toast in the middle of the screen.
34053
+ * Only needed when position="toast".
34054
+ * @param toastHeight - The height of the ion-toast element
34055
+ * @param wrapperHeight - The height of the .toast-wrapper element
34056
+ * inside the toast's shadow root.
34057
+ */
34058
+ const getOffsetForMiddlePosition = (toastHeight, wrapperHeight) => {
34059
+ return Math.floor(toastHeight / 2 - wrapperHeight / 2);
34060
+ };
34061
+
33966
34062
  /**
33967
34063
  * iOS Toast Enter Animation
33968
34064
  */
@@ -33978,7 +34074,7 @@ const iosEnterAnimation = (baseEl, opts) => {
33978
34074
  wrapperAnimation.fromTo('transform', 'translateY(-100%)', `translateY(${top})`);
33979
34075
  break;
33980
34076
  case 'middle':
33981
- const topPosition = Math.floor(baseEl.clientHeight / 2 - wrapperEl.clientHeight / 2);
34077
+ const topPosition = getOffsetForMiddlePosition(baseEl.clientHeight, wrapperEl.clientHeight);
33982
34078
  wrapperEl.style.top = `${topPosition}px`;
33983
34079
  wrapperAnimation.fromTo('opacity', 0.01, 1);
33984
34080
  break;
@@ -34029,7 +34125,7 @@ const mdEnterAnimation = (baseEl, opts) => {
34029
34125
  wrapperAnimation.fromTo('opacity', 0.01, 1);
34030
34126
  break;
34031
34127
  case 'middle':
34032
- const topPosition = Math.floor(baseEl.clientHeight / 2 - wrapperEl.clientHeight / 2);
34128
+ const topPosition = getOffsetForMiddlePosition(baseEl.clientHeight, wrapperEl.clientHeight);
34033
34129
  wrapperEl.style.top = `${topPosition}px`;
34034
34130
  wrapperAnimation.fromTo('opacity', 0.01, 1);
34035
34131
  break;
@@ -34054,80 +34150,261 @@ const mdLeaveAnimation = (baseEl) => {
34054
34150
  };
34055
34151
 
34056
34152
  /**
34057
- * Calculate the CSS top and bottom position of the toast, to be used
34058
- * as starting points for the animation keyframes.
34059
- *
34060
- * The default animations for both MD and iOS
34061
- * use translateY, which calculates from the
34062
- * top edge of the screen. This behavior impacts
34063
- * how we compute the offset when a toast has
34064
- * position='bottom' since we need to calculate from
34065
- * the bottom edge of the screen instead.
34066
- *
34067
- * @param position The value of the toast's position prop.
34068
- * @param positionAnchor The element the toast should be anchored to,
34069
- * if applicable.
34070
- * @param mode The toast component's mode (md, ios, etc).
34071
- * @param toast A reference to the toast element itself.
34153
+ * Create a gesture that allows the Toast
34154
+ * to be swiped to dismiss.
34155
+ * @param el - The Toast element
34156
+ * @param toastPosition - The last computed position of the Toast. This is computed in the "present" method.
34157
+ * @param onDismiss - A callback to fire when the Toast was swiped to dismiss.
34072
34158
  */
34073
- function getAnimationPosition(position, positionAnchor, mode, toast) {
34159
+ const createSwipeToDismissGesture = (el, toastPosition, onDismiss) => {
34074
34160
  /**
34075
- * Start with a predefined offset from the edge the toast will be
34076
- * positioned relative to, whether on the screen or anchor element.
34161
+ * Users should swipe on the visible toast wrapper
34162
+ * rather than on ion-toast which covers the entire screen.
34163
+ * When testing the class instance the inner wrapper will not
34164
+ * be defined. As a result, we use a placeholder element in those environments.
34077
34165
  */
34078
- let offset;
34079
- if (mode === 'md') {
34080
- offset = position === 'top' ? 8 : -8;
34081
- }
34082
- else {
34083
- offset = position === 'top' ? 10 : -10;
34084
- }
34166
+ const wrapperEl = getElementRoot(el).querySelector('.toast-wrapper');
34167
+ const hostElHeight = el.clientHeight;
34168
+ const wrapperElBox = wrapperEl.getBoundingClientRect();
34085
34169
  /**
34086
- * If positionAnchor is defined, add in the distance from the target
34087
- * screen edge to the target anchor edge. For position="top", the
34088
- * bottom anchor edge is targeted. For position="bottom", the top
34089
- * anchor edge is targeted.
34170
+ * The maximum amount that
34171
+ * the toast can be swiped. This should
34172
+ * account for the wrapper element's height
34173
+ * too so the toast can be swiped offscreen
34174
+ * completely.
34090
34175
  */
34091
- if (positionAnchor && win$1) {
34092
- warnIfAnchorIsHidden(positionAnchor, toast);
34093
- const box = positionAnchor.getBoundingClientRect();
34094
- if (position === 'top') {
34095
- offset += box.bottom;
34176
+ let MAX_SWIPE_DISTANCE = 0;
34177
+ /**
34178
+ * The step value at which a toast
34179
+ * is eligible for dismissing via gesture.
34180
+ */
34181
+ const DISMISS_THRESHOLD = 0.5;
34182
+ /**
34183
+ * The middle position Toast starts 50% of the way
34184
+ * through the animation, so we need to use this
34185
+ * as the starting point for our step values.
34186
+ */
34187
+ const STEP_OFFSET = el.position === 'middle' ? 0.5 : 0;
34188
+ /**
34189
+ * When the Toast is at the top users will be
34190
+ * swiping up. As a result, the delta values will be
34191
+ * negative numbers which will result in negative steps
34192
+ * and thresholds. As a result, we need to make those numbers
34193
+ * positive.
34194
+ */
34195
+ const INVERSION_FACTOR = el.position === 'top' ? -1 : 1;
34196
+ /**
34197
+ * The top offset that places the
34198
+ * toast in the middle of the screen.
34199
+ * Only needed when position="middle".
34200
+ */
34201
+ const topPosition = getOffsetForMiddlePosition(hostElHeight, wrapperElBox.height);
34202
+ const SWIPE_UP_DOWN_KEYFRAMES = [
34203
+ { offset: 0, transform: `translateY(-${topPosition + wrapperElBox.height}px)` },
34204
+ { offset: 0.5, transform: `translateY(0px)` },
34205
+ { offset: 1, transform: `translateY(${topPosition + wrapperElBox.height}px)` },
34206
+ ];
34207
+ const swipeAnimation = createAnimation('toast-swipe-to-dismiss-animation')
34208
+ .addElement(wrapperEl)
34209
+ /**
34210
+ * The specific value here does not actually
34211
+ * matter. We just need this to be a positive
34212
+ * value so the animation does not jump
34213
+ * to the end when the user beings to drag.
34214
+ */
34215
+ .duration(100);
34216
+ switch (el.position) {
34217
+ case 'middle':
34218
+ MAX_SWIPE_DISTANCE = hostElHeight + wrapperElBox.height;
34219
+ swipeAnimation.keyframes(SWIPE_UP_DOWN_KEYFRAMES);
34220
+ /**
34221
+ * Toast can be swiped up or down but
34222
+ * should start in the middle of the screen.
34223
+ */
34224
+ swipeAnimation.progressStart(true, 0.5);
34225
+ break;
34226
+ case 'top':
34227
+ /**
34228
+ * The bottom edge of the wrapper
34229
+ * includes the distance between the top
34230
+ * of the screen and the top of the wrapper
34231
+ * as well as the wrapper height so the wrapper
34232
+ * can be dragged fully offscreen.
34233
+ */
34234
+ MAX_SWIPE_DISTANCE = wrapperElBox.bottom;
34235
+ swipeAnimation.keyframes([
34236
+ { offset: 0, transform: `translateY(${toastPosition.top})` },
34237
+ { offset: 1, transform: 'translateY(-100%)' },
34238
+ ]);
34239
+ swipeAnimation.progressStart(true, 0);
34240
+ break;
34241
+ case 'bottom':
34242
+ default:
34243
+ /**
34244
+ * This computes the distance between the
34245
+ * top of the wrapper and the bottom of the
34246
+ * screen including the height of the wrapper
34247
+ * element so it can be dragged fully offscreen.
34248
+ */
34249
+ MAX_SWIPE_DISTANCE = hostElHeight - wrapperElBox.top;
34250
+ swipeAnimation.keyframes([
34251
+ { offset: 0, transform: `translateY(${toastPosition.bottom})` },
34252
+ { offset: 1, transform: 'translateY(100%)' },
34253
+ ]);
34254
+ swipeAnimation.progressStart(true, 0);
34255
+ break;
34256
+ }
34257
+ const computeStep = (delta) => {
34258
+ return (delta * INVERSION_FACTOR) / MAX_SWIPE_DISTANCE;
34259
+ };
34260
+ const onMove = (detail) => {
34261
+ const step = STEP_OFFSET + computeStep(detail.deltaY);
34262
+ swipeAnimation.progressStep(step);
34263
+ };
34264
+ const onEnd = (detail) => {
34265
+ const velocity = detail.velocityY;
34266
+ const threshold = ((detail.deltaY + velocity * 1000) / MAX_SWIPE_DISTANCE) * INVERSION_FACTOR;
34267
+ /**
34268
+ * Disable the gesture for the remainder of the animation.
34269
+ * It will be re-enabled if the toast animates back to
34270
+ * its initial presented position.
34271
+ */
34272
+ gesture.enable(false);
34273
+ let shouldDismiss = true;
34274
+ let playTo = 1;
34275
+ let step = 0;
34276
+ let remainingDistance = 0;
34277
+ if (el.position === 'middle') {
34278
+ /**
34279
+ * A middle positioned Toast appears
34280
+ * in the middle of the screen (at animation offset 0.5).
34281
+ * As a result, the threshold will be calculated relative
34282
+ * to this starting position. In other words at animation offset 0.5
34283
+ * the threshold will be 0. We want the middle Toast to be eligible
34284
+ * for dismiss when the user has swiped either half way up or down the
34285
+ * screen. As a result, we divide DISMISS_THRESHOLD in half. We also
34286
+ * consider when the threshold is a negative in the event the
34287
+ * user drags up (since the deltaY will also be negative).
34288
+ */
34289
+ shouldDismiss = threshold >= DISMISS_THRESHOLD / 2 || threshold <= -DISMISS_THRESHOLD / 2;
34290
+ /**
34291
+ * Since we are replacing the keyframes
34292
+ * below the animation always starts from
34293
+ * the beginning of the new keyframes.
34294
+ * Similarly, we are always playing to
34295
+ * the end of the new keyframes.
34296
+ */
34297
+ playTo = 1;
34298
+ step = 0;
34299
+ /**
34300
+ * The Toast should animate from wherever its
34301
+ * current position is to the desired end state.
34302
+ *
34303
+ * To begin, we get the current position of the
34304
+ * Toast for its starting state.
34305
+ */
34306
+ const wrapperElBox = wrapperEl.getBoundingClientRect();
34307
+ const startOffset = wrapperElBox.top - topPosition;
34308
+ const startPosition = `${startOffset}px`;
34309
+ /**
34310
+ * If the deltaY is negative then the user is swiping
34311
+ * up, so the Toast should animate to the top of the screen.
34312
+ * If the deltaY is positive then the user is swiping
34313
+ * down, so the Toast should animate to the bottom of the screen.
34314
+ * We also account for when the deltaY is 0, but realistically
34315
+ * that should never happen because it means the user did not drag
34316
+ * the toast.
34317
+ */
34318
+ const offsetFactor = detail.deltaY <= 0 ? -1 : 1;
34319
+ const endOffset = (topPosition + wrapperElBox.height) * offsetFactor;
34320
+ /**
34321
+ * If the Toast should dismiss
34322
+ * then we need to figure out which edge of
34323
+ * the screen it should animate towards.
34324
+ * By default, the Toast will come
34325
+ * back to its default state in the
34326
+ * middle of the screen.
34327
+ */
34328
+ const endPosition = shouldDismiss ? `${endOffset}px` : '0px';
34329
+ const KEYFRAMES = [
34330
+ { offset: 0, transform: `translateY(${startPosition})` },
34331
+ { offset: 1, transform: `translateY(${endPosition})` },
34332
+ ];
34333
+ swipeAnimation.keyframes(KEYFRAMES);
34334
+ /**
34335
+ * Compute the remaining amount of pixels the
34336
+ * toast needs to move to be fully dismissed.
34337
+ */
34338
+ remainingDistance = endOffset - startOffset;
34096
34339
  }
34097
- else if (position === 'bottom') {
34340
+ else {
34341
+ shouldDismiss = threshold >= DISMISS_THRESHOLD;
34342
+ playTo = shouldDismiss ? 1 : 0;
34343
+ step = computeStep(detail.deltaY);
34098
34344
  /**
34099
- * Just box.top is the distance from the top edge of the screen
34100
- * to the top edge of the anchor. We want to calculate from the
34101
- * bottom edge of the screen instead.
34345
+ * Compute the remaining amount of pixels the
34346
+ * toast needs to move to be fully dismissed.
34102
34347
  */
34103
- offset -= win$1.innerHeight - box.top;
34348
+ const remainingStepAmount = shouldDismiss ? 1 - step : step;
34349
+ remainingDistance = remainingStepAmount * MAX_SWIPE_DISTANCE;
34104
34350
  }
34105
34351
  /**
34106
- * We don't include safe area here because that should already be
34107
- * accounted for when checking the position of the anchor.
34352
+ * The animation speed should depend on how quickly
34353
+ * the user flicks the toast across the screen. However,
34354
+ * it should be no slower than 200ms.
34355
+ * We use Math.abs on the remainingDistance because that value
34356
+ * can be negative when swiping up on a middle position toast.
34108
34357
  */
34109
- return {
34110
- top: `${offset}px`,
34111
- bottom: `${offset}px`,
34112
- };
34113
- }
34114
- else {
34115
- return {
34116
- top: `calc(${offset}px + var(--ion-safe-area-top, 0px))`,
34117
- bottom: `calc(${offset}px - var(--ion-safe-area-bottom, 0px))`,
34118
- };
34119
- }
34120
- }
34121
- /**
34122
- * If the anchor element is hidden, getBoundingClientRect()
34123
- * will return all 0s for it, which can cause unexpected
34124
- * results in the position calculation when animating.
34125
- */
34126
- function warnIfAnchorIsHidden(positionAnchor, toast) {
34127
- if (positionAnchor.offsetParent === null) {
34128
- printIonWarning('The positionAnchor element for ion-toast was found in the DOM, but appears to be hidden. This may lead to unexpected positioning of the toast.', toast);
34129
- }
34130
- }
34358
+ const duration = Math.min(Math.abs(remainingDistance) / Math.abs(velocity), 200);
34359
+ swipeAnimation
34360
+ .onFinish(() => {
34361
+ if (shouldDismiss) {
34362
+ onDismiss();
34363
+ swipeAnimation.destroy();
34364
+ }
34365
+ else {
34366
+ if (el.position === 'middle') {
34367
+ /**
34368
+ * If the toast snapped back to
34369
+ * the middle of the screen we need
34370
+ * to reset the keyframes
34371
+ * so the toast can be swiped
34372
+ * up or down again.
34373
+ */
34374
+ swipeAnimation.keyframes(SWIPE_UP_DOWN_KEYFRAMES).progressStart(true, 0.5);
34375
+ }
34376
+ else {
34377
+ swipeAnimation.progressStart(true, 0);
34378
+ }
34379
+ /**
34380
+ * If the toast did not dismiss then
34381
+ * the user should be able to swipe again.
34382
+ */
34383
+ gesture.enable(true);
34384
+ }
34385
+ /**
34386
+ * This must be a one time callback
34387
+ * otherwise a new callback will
34388
+ * be added every time onEnd runs.
34389
+ */
34390
+ }, { oneTimeCallback: true })
34391
+ .progressEnd(playTo, step, duration);
34392
+ };
34393
+ const gesture = createGesture({
34394
+ el: wrapperEl,
34395
+ gestureName: 'toast-swipe-to-dismiss',
34396
+ gesturePriority: OVERLAY_GESTURE_PRIORITY,
34397
+ /**
34398
+ * Toast only supports vertical swipes.
34399
+ * This needs to be updated if we later
34400
+ * support horizontal swipes.
34401
+ */
34402
+ direction: 'y',
34403
+ onMove,
34404
+ onEnd,
34405
+ });
34406
+ return gesture;
34407
+ };
34131
34408
 
34132
34409
  const toastIosCss = "/*!@:host*/.sc-ion-toast-ios-h{--border-width:0;--border-style:none;--border-color:initial;--box-shadow:none;--min-width:auto;--width:auto;--min-height:auto;--height:auto;--max-height:auto;--white-space:normal;top:0;display:block;position:absolute;width:100%;height:100%;outline:none;color:var(--color);font-family:var(--ion-font-family, inherit);contain:strict;z-index:1001;pointer-events:none}@supports (inset-inline-start: 0){/*!@:host*/.sc-ion-toast-ios-h{inset-inline-start:0}}@supports not (inset-inline-start: 0){/*!@:host*/.sc-ion-toast-ios-h{left:0}/*!@:host-context([dir=rtl])*/[dir=rtl].sc-ion-toast-ios-h,[dir=rtl] .sc-ion-toast-ios-h{left:unset;right:unset;right:0}/*!@@supports selector(:dir(rtl))*/@supports .sc-ion-toast-ios selector(.sc-ion-toast-ios:dir(rtl)){.sc-ion-toast-ios-h:dir(rtl){left:unset;right:unset;right:0}}}/*!@:host(.overlay-hidden)*/.overlay-hidden.sc-ion-toast-ios-h{display:none}/*!@:host(.ion-color)*/.ion-color.sc-ion-toast-ios-h{--button-color:inherit;color:var(--ion-color-contrast)}/*!@:host(.ion-color) .toast-button-cancel*/.ion-color.sc-ion-toast-ios-h .toast-button-cancel.sc-ion-toast-ios{color:inherit}/*!@:host(.ion-color) .toast-wrapper*/.ion-color.sc-ion-toast-ios-h .toast-wrapper.sc-ion-toast-ios{background:var(--ion-color-base)}/*!@.toast-wrapper*/.toast-wrapper.sc-ion-toast-ios{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)}@supports (inset-inline-start: 0){/*!@.toast-wrapper*/.toast-wrapper.sc-ion-toast-ios{inset-inline-start:var(--start);inset-inline-end:var(--end)}}@supports not (inset-inline-start: 0){/*!@.toast-wrapper*/.toast-wrapper.sc-ion-toast-ios{left:var(--start);right:var(--end)}/*!@:host-context([dir=rtl]) .toast-wrapper*/[dir=rtl].sc-ion-toast-ios-h .toast-wrapper.sc-ion-toast-ios,[dir=rtl] .sc-ion-toast-ios-h .toast-wrapper.sc-ion-toast-ios{left:unset;right:unset;left:var(--end);right:var(--start)}/*!@[dir=rtl] .toast-wrapper*/[dir=rtl].sc-ion-toast-ios .toast-wrapper.sc-ion-toast-ios{left:unset;right:unset;left:var(--end);right:var(--start)}/*!@@supports selector(:dir(rtl))*/@supports .sc-ion-toast-ios selector(.sc-ion-toast-ios:dir(rtl)){.toast-wrapper:dir(rtl){left:unset;right:unset;left:var(--end);right:var(--start)}}}/*!@.toast-wrapper.toast-top*/.toast-wrapper.toast-top.sc-ion-toast-ios{-webkit-transform:translate3d(0, -100%, 0);transform:translate3d(0, -100%, 0);top:0}/*!@.toast-wrapper.toast-bottom*/.toast-wrapper.toast-bottom.sc-ion-toast-ios{-webkit-transform:translate3d(0, 100%, 0);transform:translate3d(0, 100%, 0);bottom:0}/*!@.toast-container*/.toast-container.sc-ion-toast-ios{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;pointer-events:auto;height:inherit;min-height:inherit;max-height:inherit;contain:content}/*!@.toast-layout-stacked .toast-container*/.toast-layout-stacked.sc-ion-toast-ios .toast-container.sc-ion-toast-ios{-ms-flex-wrap:wrap;flex-wrap:wrap}/*!@.toast-layout-baseline .toast-content*/.toast-layout-baseline.sc-ion-toast-ios .toast-content.sc-ion-toast-ios{display:-ms-flexbox;display:flex;-ms-flex:1;flex:1;-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center}/*!@.toast-icon*/.toast-icon.sc-ion-toast-ios{-webkit-margin-start:16px;margin-inline-start:16px}/*!@.toast-content*/.toast-content.sc-ion-toast-ios{min-width:0}/*!@.toast-message*/.toast-message.sc-ion-toast-ios{-ms-flex:1;flex:1;white-space:var(--white-space)}/*!@.toast-button-group*/.toast-button-group.sc-ion-toast-ios{display:-ms-flexbox;display:flex}/*!@.toast-layout-stacked .toast-button-group*/.toast-layout-stacked.sc-ion-toast-ios .toast-button-group.sc-ion-toast-ios{-ms-flex-pack:end;justify-content:end;width:100%}/*!@.toast-button*/.toast-button.sc-ion-toast-ios{border:0;outline:none;color:var(--button-color);z-index:0}/*!@.toast-icon,\n.toast-button-icon*/.toast-icon.sc-ion-toast-ios,.toast-button-icon.sc-ion-toast-ios{font-size:1.4em}/*!@.toast-button-inner*/.toast-button-inner.sc-ion-toast-ios{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}@media (any-hover: hover){/*!@.toast-button:hover*/.toast-button.sc-ion-toast-ios:hover{cursor:pointer}}/*!@:host*/.sc-ion-toast-ios-h{--background:var(--ion-color-step-50, #f2f2f2);--border-radius:14px;--button-color:var(--ion-color-primary, #3880ff);--color:var(--ion-color-step-850, #262626);--max-width:700px;--max-height:478px;--start:10px;--end:10px;font-size:clamp(14px, 0.875rem, 43.4px)}/*!@.toast-wrapper*/.toast-wrapper.sc-ion-toast-ios{-webkit-margin-start:auto;margin-inline-start:auto;-webkit-margin-end:auto;margin-inline-end:auto;margin-top:auto;margin-bottom:auto;display:block;position:absolute;z-index:10}@supports ((-webkit-backdrop-filter: blur(0)) or (backdrop-filter: blur(0))){/*!@:host(.toast-translucent) .toast-wrapper*/.toast-translucent.sc-ion-toast-ios-h .toast-wrapper.sc-ion-toast-ios{background:rgba(var(--ion-background-color-rgb, 255, 255, 255), 0.8);-webkit-backdrop-filter:saturate(180%) blur(20px);backdrop-filter:saturate(180%) blur(20px)}/*!@:host(.ion-color.toast-translucent) .toast-wrapper*/.ion-color.toast-translucent.sc-ion-toast-ios-h .toast-wrapper.sc-ion-toast-ios{background:rgba(var(--ion-color-base-rgb), 0.8)}}/*!@.toast-wrapper.toast-middle*/.toast-wrapper.toast-middle.sc-ion-toast-ios{opacity:0.01}/*!@.toast-content*/.toast-content.sc-ion-toast-ios{-webkit-padding-start:15px;padding-inline-start:15px;-webkit-padding-end:15px;padding-inline-end:15px;padding-top:15px;padding-bottom:15px}/*!@.toast-header*/.toast-header.sc-ion-toast-ios{margin-bottom:2px;font-weight:500}/*!@.toast-button*/.toast-button.sc-ion-toast-ios{-webkit-padding-start:15px;padding-inline-start:15px;-webkit-padding-end:15px;padding-inline-end:15px;padding-top:10px;padding-bottom:10px;min-height:44px;-webkit-transition:background-color, opacity 100ms linear;transition:background-color, opacity 100ms linear;border:0;background-color:transparent;font-family:var(--ion-font-family);font-size:clamp(17px, 1.0625rem, 21.998px);font-weight:500;overflow:hidden}/*!@.toast-button.ion-activated*/.toast-button.ion-activated.sc-ion-toast-ios{opacity:0.4}@media (any-hover: hover){/*!@.toast-button:hover*/.toast-button.sc-ion-toast-ios:hover{opacity:0.6}}";
34133
34410
 
@@ -34167,6 +34444,41 @@ class Toast {
34167
34444
  this.callButtonHandler(cancelButton);
34168
34445
  }
34169
34446
  };
34447
+ /**
34448
+ * Create a new swipe gesture so Toast
34449
+ * can be swiped to dismiss.
34450
+ */
34451
+ this.createSwipeGesture = (toastPosition) => {
34452
+ const gesture = (this.gesture = createSwipeToDismissGesture(this.el, toastPosition, () => {
34453
+ /**
34454
+ * If the gesture completed then
34455
+ * we should dismiss the toast.
34456
+ */
34457
+ this.dismiss(undefined, GESTURE);
34458
+ }));
34459
+ gesture.enable(true);
34460
+ };
34461
+ /**
34462
+ * Destroy an existing swipe gesture
34463
+ * so Toast can no longer be swiped to dismiss.
34464
+ */
34465
+ this.destroySwipeGesture = () => {
34466
+ const { gesture } = this;
34467
+ if (gesture === undefined) {
34468
+ return;
34469
+ }
34470
+ gesture.destroy();
34471
+ this.gesture = undefined;
34472
+ };
34473
+ /**
34474
+ * Returns `true` if swipeGesture
34475
+ * is configured to a value that enables the swipe behavior.
34476
+ * Returns `false` otherwise.
34477
+ */
34478
+ this.prefersSwipeGesture = () => {
34479
+ const { swipeGesture } = this;
34480
+ return swipeGesture === 'vertical';
34481
+ };
34170
34482
  this.revealContentToScreenReader = false;
34171
34483
  this.overlayIndex = undefined;
34172
34484
  this.delegate = undefined;
@@ -34187,9 +34499,37 @@ class Toast {
34187
34499
  this.animated = true;
34188
34500
  this.icon = undefined;
34189
34501
  this.htmlAttributes = undefined;
34502
+ this.swipeGesture = undefined;
34190
34503
  this.isOpen = false;
34191
34504
  this.trigger = undefined;
34192
34505
  }
34506
+ swipeGestureChanged() {
34507
+ /**
34508
+ * If the Toast is presented, then we need to destroy
34509
+ * any actives gestures before a new gesture is potentially
34510
+ * created below.
34511
+ *
34512
+ * If the Toast is dismissed, then no gesture should be available
34513
+ * since the Toast is not visible. This case should never
34514
+ * happen since the "dismiss" method handles destroying
34515
+ * any active swipe gestures, but we keep this code
34516
+ * around to handle the first case.
34517
+ */
34518
+ this.destroySwipeGesture();
34519
+ /**
34520
+ * A new swipe gesture should only be created
34521
+ * if the Toast is presented. If the Toast is not
34522
+ * yet presented then the "present" method will
34523
+ * handle calling the swipe gesture setup function.
34524
+ */
34525
+ if (this.presented && this.prefersSwipeGesture()) {
34526
+ /**
34527
+ * If the Toast is presented then
34528
+ * lastPresentedPosition is defined.
34529
+ */
34530
+ this.createSwipeGesture(this.lastPresentedPosition);
34531
+ }
34532
+ }
34193
34533
  onIsOpenChange(newValue, oldValue) {
34194
34534
  if (newValue === true && oldValue === false) {
34195
34535
  this.present();
@@ -34251,6 +34591,14 @@ class Toast {
34251
34591
  if (this.duration > 0) {
34252
34592
  this.durationTimeout = setTimeout(() => this.dismiss(undefined, 'timeout'), this.duration);
34253
34593
  }
34594
+ /**
34595
+ * If the Toast has a swipe gesture then we can
34596
+ * create the gesture so users can swipe the
34597
+ * presented Toast.
34598
+ */
34599
+ if (this.prefersSwipeGesture()) {
34600
+ this.createSwipeGesture(animationPosition);
34601
+ }
34254
34602
  unlock();
34255
34603
  }
34256
34604
  /**
@@ -34285,6 +34633,11 @@ class Toast {
34285
34633
  this.revealContentToScreenReader = false;
34286
34634
  }
34287
34635
  this.lastPresentedPosition = undefined;
34636
+ /**
34637
+ * If the Toast has a swipe gesture then we can
34638
+ * safely destroy it now that it is dismissed.
34639
+ */
34640
+ this.destroySwipeGesture();
34288
34641
  unlock();
34289
34642
  return dismissed;
34290
34643
  }
@@ -34429,6 +34782,7 @@ class Toast {
34429
34782
  }
34430
34783
  get el() { return getElement(this); }
34431
34784
  static get watchers() { return {
34785
+ "swipeGesture": ["swipeGestureChanged"],
34432
34786
  "isOpen": ["onIsOpenChange"],
34433
34787
  "trigger": ["triggerChanged"]
34434
34788
  }; }
@@ -34459,6 +34813,7 @@ class Toast {
34459
34813
  "animated": [4],
34460
34814
  "icon": [1],
34461
34815
  "htmlAttributes": [16],
34816
+ "swipeGesture": [1, "swipe-gesture"],
34462
34817
  "isOpen": [4, "is-open"],
34463
34818
  "trigger": [1],
34464
34819
  "revealContentToScreenReader": [32],