@ionic/core 7.4.2-dev.11695927273.1323de22 → 7.4.2-dev.11696878562.16da82aa

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 (48) hide show
  1. package/components/alert.js +8 -0
  2. package/components/index.js +1 -0
  3. package/components/ion-select.js +1 -1
  4. package/components/ion-toast.js +144 -113
  5. package/dist/cjs/index.cjs.js +2 -0
  6. package/dist/cjs/ion-alert.cjs.entry.js +8 -0
  7. package/dist/cjs/ion-select_3.cjs.entry.js +1 -1
  8. package/dist/cjs/ion-toast.cjs.entry.js +163 -132
  9. package/dist/collection/components/alert/alert.js +8 -0
  10. package/dist/collection/components/alert/test/a11y/alert.e2e.js +11 -0
  11. package/dist/collection/components/select/select.js +1 -1
  12. package/dist/collection/components/toast/animations/ios.enter.js +1 -39
  13. package/dist/collection/components/toast/animations/ios.leave.js +4 -9
  14. package/dist/collection/components/toast/animations/md.enter.js +1 -40
  15. package/dist/collection/components/toast/animations/utils.js +85 -0
  16. package/dist/collection/components/toast/toast.js +58 -31
  17. package/dist/collection/index.js +1 -0
  18. package/dist/docs.json +3 -7
  19. package/dist/esm/index.js +1 -0
  20. package/dist/esm/ion-alert.entry.js +8 -0
  21. package/dist/esm/ion-select_3.entry.js +1 -1
  22. package/dist/esm/ion-toast.entry.js +144 -113
  23. package/dist/esm-es5/index.js +1 -1
  24. package/dist/esm-es5/ion-alert.entry.js +1 -1
  25. package/dist/esm-es5/ion-select_3.entry.js +1 -1
  26. package/dist/esm-es5/ion-toast.entry.js +1 -1
  27. package/dist/ionic/index.esm.js +1 -1
  28. package/dist/ionic/ionic.esm.js +1 -1
  29. package/dist/ionic/p-06c94b76.entry.js +4 -0
  30. package/dist/ionic/{p-2df840d9.system.entry.js → p-1e207aa7.system.entry.js} +1 -1
  31. package/dist/ionic/p-2bea5600.entry.js +4 -0
  32. package/dist/ionic/p-4e312f80.system.entry.js +4 -0
  33. package/dist/ionic/p-5f6605c4.entry.js +4 -0
  34. package/dist/ionic/{p-5d522814.system.entry.js → p-6cb04881.system.entry.js} +1 -1
  35. package/dist/ionic/p-6edbc09b.system.js +1 -1
  36. package/dist/ionic/p-8a1faf2c.system.js +4 -0
  37. package/dist/types/components/toast/animations/ios.leave.d.ts +2 -2
  38. package/dist/types/components/toast/animations/utils.d.ts +18 -0
  39. package/dist/types/components/toast/toast-interface.d.ts +8 -3
  40. package/dist/types/components/toast/toast.d.ts +12 -0
  41. package/dist/types/index.d.ts +1 -0
  42. package/hydrate/index.js +153 -114
  43. package/package.json +1 -1
  44. package/dist/ionic/p-1f448082.system.entry.js +0 -4
  45. package/dist/ionic/p-35d1c3bb.system.js +0 -4
  46. package/dist/ionic/p-8a26eaba.entry.js +0 -4
  47. package/dist/ionic/p-990aaa2f.entry.js +0 -4
  48. package/dist/ionic/p-c44645f2.entry.js +0 -4
@@ -0,0 +1,85 @@
1
+ /*!
2
+ * (C) Ionic http://ionicframework.com - MIT License
3
+ */
4
+ import { win } from "../../../utils/browser/index";
5
+ import { printIonWarning } from "../../../utils/logging/index";
6
+ /**
7
+ * Calculate the CSS top and bottom position of the toast, to be used
8
+ * as starting points for the animation keyframes.
9
+ *
10
+ * Note that MD animates bottom-positioned toasts using style.bottom,
11
+ * which calculates from the bottom edge of the screen, while iOS uses
12
+ * translateY, which calculates from the top edge of the screen. This
13
+ * is why the bottom calculates differ slightly between modes.
14
+ *
15
+ * @param position The value of the toast's position prop.
16
+ * @param positionAnchor The element the toast should be anchored to,
17
+ * if applicable.
18
+ * @param mode The toast component's mode (md, ios, etc).
19
+ * @param toast A reference to the toast element itself.
20
+ */
21
+ export function getAnimationPosition(position, positionAnchor, mode, toast) {
22
+ /**
23
+ * Start with a predefined offset from the edge the toast will be
24
+ * positioned relative to, whether on the screen or anchor element.
25
+ */
26
+ let offset;
27
+ if (mode === 'md') {
28
+ offset = 8;
29
+ }
30
+ else {
31
+ offset = position === 'top' ? 10 : -10;
32
+ }
33
+ /**
34
+ * If positionAnchor is defined, add in the distance from the target
35
+ * screen edge to the target anchor edge. For position="top", the
36
+ * bottom anchor edge is targeted. For position="bottom", the top
37
+ * anchor edge is targeted.
38
+ */
39
+ if (positionAnchor && win) {
40
+ warnIfAnchorIsHidden(positionAnchor, toast);
41
+ const box = positionAnchor.getBoundingClientRect();
42
+ if (position === 'top') {
43
+ offset += box.bottom;
44
+ }
45
+ else if (position === 'bottom') {
46
+ /**
47
+ * Just box.top is the distance from the top edge of the screen
48
+ * to the top edge of the anchor. We want to calculate from the
49
+ * bottom edge of the screen instead.
50
+ */
51
+ if (mode === 'md') {
52
+ offset += win.innerHeight - box.top;
53
+ }
54
+ else {
55
+ offset -= win.innerHeight - box.top;
56
+ }
57
+ }
58
+ /**
59
+ * We don't include safe area here because that should already be
60
+ * accounted for when checking the position of the anchor.
61
+ */
62
+ return {
63
+ top: `${offset}px`,
64
+ bottom: `${offset}px`,
65
+ };
66
+ }
67
+ else {
68
+ return {
69
+ top: `calc(${offset}px + var(--ion-safe-area-top, 0px))`,
70
+ bottom: mode === 'md'
71
+ ? `calc(${offset}px + var(--ion-safe-area-bottom, 0px))`
72
+ : `calc(${offset}px - var(--ion-safe-area-bottom, 0px))`,
73
+ };
74
+ }
75
+ }
76
+ /**
77
+ * If the anchor element is hidden, getBoundingClientRect()
78
+ * will return all 0s for it, which can cause unexpected
79
+ * results in the position calculation when animating.
80
+ */
81
+ function warnIfAnchorIsHidden(positionAnchor, toast) {
82
+ if (positionAnchor.offsetParent === null) {
83
+ 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);
84
+ }
85
+ }
@@ -15,6 +15,7 @@ import { iosEnterAnimation } from "./animations/ios.enter";
15
15
  import { iosLeaveAnimation } from "./animations/ios.leave";
16
16
  import { mdEnterAnimation } from "./animations/md.enter";
17
17
  import { mdLeaveAnimation } from "./animations/md.leave";
18
+ import { getAnimationPosition } from "./animations/utils";
18
19
  // TODO(FW-2832): types
19
20
  /**
20
21
  * @virtualProp {"ios" | "md"} mode - The mode determines which platform styles to use.
@@ -102,32 +103,18 @@ export class Toast {
102
103
  async present() {
103
104
  const unlock = await this.lockController.lock();
104
105
  await this.delegateController.attachViewToDom();
105
- const { position, positionAnchor } = this;
106
- let anchor;
107
- if (position === 'middle' && positionAnchor !== undefined) {
108
- printIonWarning('The positionAnchor property is ignored when using position="middle".', this.el);
109
- }
110
- else if (typeof positionAnchor === 'string') {
111
- /**
112
- * If the anchor is defined as an ID, find the element.
113
- * We do this on every present so the toast doesn't need
114
- * to account for the surrounding DOM changing since the
115
- * last time it was presented.
116
- */
117
- const foundEl = document.getElementById(positionAnchor);
118
- if (foundEl === null) {
119
- printIonWarning(`An anchor element with an ID of ${positionAnchor} was not found in the DOM.`, this.el);
120
- }
121
- else {
122
- anchor = foundEl;
123
- }
124
- }
125
- else {
126
- anchor = positionAnchor;
127
- }
106
+ const { el, position } = this;
107
+ const anchor = this.getAnchorElement();
108
+ const animationPosition = getAnimationPosition(position, anchor, getIonMode(this), el);
109
+ /**
110
+ * Cache the calculated position of the toast, so we can re-use it
111
+ * in the dismiss animation.
112
+ */
113
+ this.lastPresentedPosition = animationPosition;
128
114
  await present(this, 'toastEnter', iosEnterAnimation, mdEnterAnimation, {
129
115
  position,
130
- positionAnchor: anchor,
116
+ top: animationPosition.top,
117
+ bottom: animationPosition.bottom,
131
118
  });
132
119
  /**
133
120
  * Content is revealed to screen readers after
@@ -150,15 +137,28 @@ export class Toast {
150
137
  * Some examples include: ``"cancel"`, `"destructive"`, "selected"`, and `"backdrop"`.
151
138
  */
152
139
  async dismiss(data, role) {
140
+ var _a, _b;
153
141
  const unlock = await this.lockController.lock();
154
- if (this.durationTimeout) {
155
- clearTimeout(this.durationTimeout);
142
+ const { durationTimeout, position, lastPresentedPosition } = this;
143
+ if (durationTimeout) {
144
+ clearTimeout(durationTimeout);
156
145
  }
157
- const dismissed = await dismiss(this, data, role, 'toastLeave', iosLeaveAnimation, mdLeaveAnimation, this.position);
146
+ const dismissed = await dismiss(this, data, role, 'toastLeave', iosLeaveAnimation, mdLeaveAnimation,
147
+ /**
148
+ * Fetch the cached position that was calculated back in the present
149
+ * animation. We always want to animate the dismiss from the same
150
+ * position the present stopped at, so the animation looks continuous.
151
+ */
152
+ {
153
+ position,
154
+ top: (_a = lastPresentedPosition === null || lastPresentedPosition === void 0 ? void 0 : lastPresentedPosition.top) !== null && _a !== void 0 ? _a : '',
155
+ bottom: (_b = lastPresentedPosition === null || lastPresentedPosition === void 0 ? void 0 : lastPresentedPosition.bottom) !== null && _b !== void 0 ? _b : '',
156
+ });
158
157
  if (dismissed) {
159
158
  this.delegateController.removeViewFromDom();
160
159
  this.revealContentToScreenReader = false;
161
160
  }
161
+ this.lastPresentedPosition = undefined;
162
162
  unlock();
163
163
  return dismissed;
164
164
  }
@@ -182,6 +182,37 @@ export class Toast {
182
182
  : [];
183
183
  return buttons;
184
184
  }
185
+ /**
186
+ * Returns the element specified by the positionAnchor prop,
187
+ * or undefined if prop's value is an ID string and the element
188
+ * is not found in the DOM.
189
+ */
190
+ getAnchorElement() {
191
+ const { position, positionAnchor, el } = this;
192
+ if (position === 'middle' && positionAnchor !== undefined) {
193
+ printIonWarning('The positionAnchor property is ignored when using position="middle".', this.el);
194
+ return undefined;
195
+ }
196
+ if (typeof positionAnchor === 'string') {
197
+ /**
198
+ * If the anchor is defined as an ID, find the element.
199
+ * We do this on every present so the toast doesn't need
200
+ * to account for the surrounding DOM changing since the
201
+ * last time it was presented.
202
+ */
203
+ const foundEl = document.getElementById(positionAnchor);
204
+ if (foundEl === null) {
205
+ printIonWarning(`An anchor element with an ID of "${positionAnchor}" was not found in the DOM.`, el);
206
+ return undefined;
207
+ }
208
+ return foundEl;
209
+ }
210
+ if (positionAnchor instanceof HTMLElement) {
211
+ return positionAnchor;
212
+ }
213
+ printIonWarning('Invalid positionAnchor value:', positionAnchor, el);
214
+ return undefined;
215
+ }
185
216
  async buttonClick(button) {
186
217
  const role = button.role;
187
218
  if (isCancel(role)) {
@@ -861,10 +892,6 @@ export class Toast {
861
892
  "location": "global",
862
893
  "id": "global::Promise"
863
894
  },
864
- "HTMLElement": {
865
- "location": "global",
866
- "id": "global::HTMLElement"
867
- },
868
895
  "ToastPresentOptions": {
869
896
  "location": "import",
870
897
  "path": "./toast-interface",
@@ -13,6 +13,7 @@ export { componentOnReady } from './utils/helpers';
13
13
  export { isPlatform, getPlatforms } from './utils/platform';
14
14
  export { IonicSafeString } from './utils/sanitization';
15
15
  export { getMode, setupConfig } from './utils/config';
16
+ export { openURL } from './utils/theme';
16
17
  export { LIFECYCLE_WILL_ENTER, LIFECYCLE_DID_ENTER, LIFECYCLE_WILL_LEAVE, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_UNLOAD, } from './components/nav/constants';
17
18
  export { menuController } from './utils/menu-controller';
18
19
  export { alertController, actionSheetController, modalController, loadingController, pickerController, popoverController, toastController, } from './utils/overlays';
package/dist/docs.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "timestamp": "2023-09-28T18:57:10",
2
+ "timestamp": "2023-10-09T19:11:33",
3
3
  "compiler": {
4
4
  "name": "@stencil/core",
5
5
  "version": "4.3.0",
@@ -27970,10 +27970,6 @@
27970
27970
  "location": "global",
27971
27971
  "id": "global::Promise"
27972
27972
  },
27973
- "HTMLElement": {
27974
- "location": "global",
27975
- "id": "global::HTMLElement"
27976
- },
27977
27973
  "ToastPresentOptions": {
27978
27974
  "location": "import",
27979
27975
  "path": "./toast-interface",
@@ -29354,12 +29350,12 @@
29354
29350
  "path": "src/components/toast/toast-interface.ts"
29355
29351
  },
29356
29352
  "src/components/toast/toast-interface.ts::ToastPresentOptions": {
29357
- "declaration": "export interface ToastPresentOptions {\n position: ToastPosition;\n positionAnchor?: HTMLElement;\n}",
29353
+ "declaration": "export type ToastPresentOptions = ToastPositionAlias & ToastAnimationPosition;",
29358
29354
  "docstring": "",
29359
29355
  "path": "src/components/toast/toast-interface.ts"
29360
29356
  },
29361
29357
  "src/components/toast/toast-interface.ts::ToastDismissOptions": {
29362
- "declaration": "export type ToastPosition = 'top' | 'bottom' | 'middle';",
29358
+ "declaration": "export type ToastDismissOptions = ToastPositionAlias & ToastAnimationPosition;",
29363
29359
  "docstring": "",
29364
29360
  "path": "src/components/toast/toast-interface.ts"
29365
29361
  },
package/dist/esm/index.js CHANGED
@@ -10,6 +10,7 @@ export { createGesture } from './index-ff313b19.js';
10
10
  export { g as getPlatforms, i as initialize, a as isPlatform } from './ionic-global-b3fc28dd.js';
11
11
  export { c as componentOnReady } from './helpers-3379ba19.js';
12
12
  export { I as IonicSafeString, g as getMode, s as setupConfig } from './config-96c9ace3.js';
13
+ export { o as openURL } from './theme-17531cdf.js';
13
14
  export { m as menuController } from './index-61bddc90.js';
14
15
  export { b as actionSheetController, a as alertController, l as loadingController, m as modalController, p as pickerController, c as popoverController, t as toastController } from './overlays-e064e499.js';
15
16
  import './index-7a14ecec.js';
@@ -171,6 +171,14 @@ const Alert = class {
171
171
  }
172
172
  onKeydown(ev) {
173
173
  const inputTypes = new Set(this.processedInputs.map((i) => i.type));
174
+ /**
175
+ * Based on keyboard navigation requirements, the
176
+ * checkbox should not respond to the enter keydown event.
177
+ */
178
+ if (inputTypes.has('checkbox') && ev.key === 'Enter') {
179
+ ev.preventDefault();
180
+ return;
181
+ }
174
182
  // The only inputs we want to navigate between using arrow keys are the radios
175
183
  // ignore the keydown event if it is not on a radio button
176
184
  if (!inputTypes.has('radio') ||
@@ -612,7 +612,7 @@ Developers can use the "legacy" property to continue using the legacy form marku
612
612
  }
613
613
  renderListbox() {
614
614
  const { disabled, inputId, isExpanded } = this;
615
- return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "listbox", "aria-expanded": `${isExpanded}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
615
+ return (h("button", { disabled: disabled, id: inputId, "aria-label": this.ariaLabel, "aria-haspopup": "dialog", "aria-expanded": `${isExpanded}`, onFocus: this.onFocus, onBlur: this.onBlur, ref: (focusEl) => (this.focusEl = focusEl) }));
616
616
  }
617
617
  render() {
618
618
  const { legacyFormController } = this;
@@ -20,46 +20,9 @@ import './hardware-back-button-39299f84.js';
20
20
  const iosEnterAnimation = (baseEl, opts) => {
21
21
  const baseAnimation = createAnimation();
22
22
  const wrapperAnimation = createAnimation();
23
- const { position, positionAnchor } = opts;
23
+ const { position, top, bottom } = opts;
24
24
  const root = getElementRoot(baseEl);
25
25
  const wrapperEl = root.querySelector('.toast-wrapper');
26
- /**
27
- * Start with a predefined offset from the edge the toast will be
28
- * positioned relative to. By default, this will be the top edge
29
- * of the screen.
30
- */
31
- let offset = position === 'top' ? 10 : -10;
32
- let top, bottom;
33
- /**
34
- * If positionAnchor is defined, add in the distance from the top
35
- * of the screen to the target anchor edge. For position="top", the
36
- * bottom anchor edge is targeted. For position="bottom", the top
37
- * anchor edge is targeted.
38
- */
39
- if (positionAnchor && win) {
40
- const box = positionAnchor.getBoundingClientRect();
41
- if (position === 'top') {
42
- offset += box.bottom;
43
- }
44
- else if (position === 'bottom') {
45
- /**
46
- * Just box.top is the distance from the top edge of the screen
47
- * to the top edge of the anchor. We want to calculate from the
48
- * bottom edge of the screen instead.
49
- */
50
- offset -= win.innerHeight - box.top;
51
- }
52
- /**
53
- * We don't include safe area here because that should already be
54
- * accounted for when checking the position of the anchor.
55
- */
56
- top = `${offset}px`;
57
- bottom = `${offset}px`;
58
- }
59
- else {
60
- top = `calc(${offset}px + var(--ion-safe-area-top, 0px))`;
61
- bottom = `calc(${offset}px - var(--ion-safe-area-bottom, 0px))`;
62
- }
63
26
  wrapperAnimation.addElement(wrapperEl);
64
27
  switch (position) {
65
28
  case 'top':
@@ -80,27 +43,22 @@ const iosEnterAnimation = (baseEl, opts) => {
80
43
  /**
81
44
  * iOS Toast Leave Animation
82
45
  */
83
- const iosLeaveAnimation = (baseEl, position) => {
46
+ const iosLeaveAnimation = (baseEl, opts) => {
84
47
  const baseAnimation = createAnimation();
85
48
  const wrapperAnimation = createAnimation();
49
+ const { position, top, bottom } = opts;
86
50
  const root = getElementRoot(baseEl);
87
51
  const wrapperEl = root.querySelector('.toast-wrapper');
88
52
  wrapperAnimation.addElement(wrapperEl);
89
- /**
90
- * For top/bottom positions, we use to() instead of fromTo() so that if
91
- * the positionAnchor prop was used, we don't need to recalculate the
92
- * toast and/or anchor's position and duplicate work from the enter
93
- * animation.
94
- */
95
53
  switch (position) {
96
54
  case 'top':
97
- wrapperAnimation.to('transform', 'translateY(-100%)');
55
+ wrapperAnimation.fromTo('transform', `translateY(${top})`, 'translateY(-100%)');
98
56
  break;
99
57
  case 'middle':
100
58
  wrapperAnimation.fromTo('opacity', 0.99, 0);
101
59
  break;
102
60
  default:
103
- wrapperAnimation.to('transform', 'translateY(100%)');
61
+ wrapperAnimation.fromTo('transform', `translateY(${bottom})`, 'translateY(100%)');
104
62
  break;
105
63
  }
106
64
  return baseAnimation.easing('cubic-bezier(.36,.66,.04,1)').duration(300).addAnimation(wrapperAnimation);
@@ -112,47 +70,9 @@ const iosLeaveAnimation = (baseEl, position) => {
112
70
  const mdEnterAnimation = (baseEl, opts) => {
113
71
  const baseAnimation = createAnimation();
114
72
  const wrapperAnimation = createAnimation();
115
- const { position, positionAnchor } = opts;
73
+ const { position, top, bottom } = opts;
116
74
  const root = getElementRoot(baseEl);
117
75
  const wrapperEl = root.querySelector('.toast-wrapper');
118
- /**
119
- * Start with a predefined offset from the edge the toast will be
120
- * positioned relative to. By default, this will be the top edge
121
- * of the screen for position="top", or the bottom edge for
122
- * position="bottom".
123
- */
124
- let offset = 8;
125
- let top, bottom;
126
- /**
127
- * If positionAnchor is defined, add in the distance from the target
128
- * screen edge to the target anchor edge. For position="top", the
129
- * bottom anchor edge is targeted. For position="bottom", the top
130
- * anchor edge is targeted.
131
- */
132
- if (positionAnchor && win) {
133
- const box = positionAnchor.getBoundingClientRect();
134
- if (position === 'top') {
135
- offset += box.bottom;
136
- }
137
- else if (position === 'bottom') {
138
- /**
139
- * Just box.top is the distance from the top edge of the screen
140
- * to the top edge of the anchor. We want to calculate from the
141
- * bottom edge of the screen instead.
142
- */
143
- offset += win.innerHeight - box.top;
144
- }
145
- /**
146
- * We don't include safe area here because that should already be
147
- * accounted for when checking the position of the anchor.
148
- */
149
- top = `${offset}px`;
150
- bottom = `${offset}px`;
151
- }
152
- else {
153
- top = `calc(${offset}px + var(--ion-safe-area-top, 0px))`;
154
- bottom = `calc(${offset}px + var(--ion-safe-area-bottom, 0px))`;
155
- }
156
76
  wrapperAnimation.addElement(wrapperEl);
157
77
  switch (position) {
158
78
  case 'top':
@@ -184,6 +104,87 @@ const mdLeaveAnimation = (baseEl) => {
184
104
  return baseAnimation.easing('cubic-bezier(.36,.66,.04,1)').duration(300).addAnimation(wrapperAnimation);
185
105
  };
186
106
 
107
+ /**
108
+ * Calculate the CSS top and bottom position of the toast, to be used
109
+ * as starting points for the animation keyframes.
110
+ *
111
+ * Note that MD animates bottom-positioned toasts using style.bottom,
112
+ * which calculates from the bottom edge of the screen, while iOS uses
113
+ * translateY, which calculates from the top edge of the screen. This
114
+ * is why the bottom calculates differ slightly between modes.
115
+ *
116
+ * @param position The value of the toast's position prop.
117
+ * @param positionAnchor The element the toast should be anchored to,
118
+ * if applicable.
119
+ * @param mode The toast component's mode (md, ios, etc).
120
+ * @param toast A reference to the toast element itself.
121
+ */
122
+ function getAnimationPosition(position, positionAnchor, mode, toast) {
123
+ /**
124
+ * Start with a predefined offset from the edge the toast will be
125
+ * positioned relative to, whether on the screen or anchor element.
126
+ */
127
+ let offset;
128
+ if (mode === 'md') {
129
+ offset = 8;
130
+ }
131
+ else {
132
+ offset = position === 'top' ? 10 : -10;
133
+ }
134
+ /**
135
+ * If positionAnchor is defined, add in the distance from the target
136
+ * screen edge to the target anchor edge. For position="top", the
137
+ * bottom anchor edge is targeted. For position="bottom", the top
138
+ * anchor edge is targeted.
139
+ */
140
+ if (positionAnchor && win) {
141
+ warnIfAnchorIsHidden(positionAnchor, toast);
142
+ const box = positionAnchor.getBoundingClientRect();
143
+ if (position === 'top') {
144
+ offset += box.bottom;
145
+ }
146
+ else if (position === 'bottom') {
147
+ /**
148
+ * Just box.top is the distance from the top edge of the screen
149
+ * to the top edge of the anchor. We want to calculate from the
150
+ * bottom edge of the screen instead.
151
+ */
152
+ if (mode === 'md') {
153
+ offset += win.innerHeight - box.top;
154
+ }
155
+ else {
156
+ offset -= win.innerHeight - box.top;
157
+ }
158
+ }
159
+ /**
160
+ * We don't include safe area here because that should already be
161
+ * accounted for when checking the position of the anchor.
162
+ */
163
+ return {
164
+ top: `${offset}px`,
165
+ bottom: `${offset}px`,
166
+ };
167
+ }
168
+ else {
169
+ return {
170
+ top: `calc(${offset}px + var(--ion-safe-area-top, 0px))`,
171
+ bottom: mode === 'md'
172
+ ? `calc(${offset}px + var(--ion-safe-area-bottom, 0px))`
173
+ : `calc(${offset}px - var(--ion-safe-area-bottom, 0px))`,
174
+ };
175
+ }
176
+ }
177
+ /**
178
+ * If the anchor element is hidden, getBoundingClientRect()
179
+ * will return all 0s for it, which can cause unexpected
180
+ * results in the position calculation when animating.
181
+ */
182
+ function warnIfAnchorIsHidden(positionAnchor, toast) {
183
+ if (positionAnchor.offsetParent === null) {
184
+ 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);
185
+ }
186
+ }
187
+
187
188
  const toastIosCss = ":host{--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{inset-inline-start:0}}@supports not (inset-inline-start: 0){:host{left:0}:host-context([dir=rtl]){left:unset;right:unset;right:0}@supports selector(:dir(rtl)){:host:dir(rtl){left:unset;right:unset;right:0}}}:host(.overlay-hidden){display:none}:host(.ion-color){--button-color:inherit;color:var(--ion-color-contrast)}:host(.ion-color) .toast-button-cancel{color:inherit}:host(.ion-color) .toast-wrapper{background:var(--ion-color-base)}.toast-wrapper{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{inset-inline-start:var(--start);inset-inline-end:var(--end)}}@supports not (inset-inline-start: 0){.toast-wrapper{left:var(--start);right:var(--end)}:host-context([dir=rtl]) .toast-wrapper{left:unset;right:unset;left:var(--end);right:var(--start)}[dir=rtl] .toast-wrapper{left:unset;right:unset;left:var(--end);right:var(--start)}@supports selector(:dir(rtl)){.toast-wrapper:dir(rtl){left:unset;right:unset;left:var(--end);right:var(--start)}}}.toast-container{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{-ms-flex-wrap:wrap;flex-wrap:wrap}.toast-layout-baseline .toast-content{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{-webkit-margin-start:16px;margin-inline-start:16px}.toast-message{-ms-flex:1;flex:1;white-space:var(--white-space)}.toast-button-group{display:-ms-flexbox;display:flex}.toast-layout-stacked .toast-button-group{-ms-flex-pack:end;justify-content:end;width:100%}.toast-button{border:0;outline:none;color:var(--button-color);z-index:0}.toast-icon,.toast-button-icon{font-size:1.4em}.toast-button-inner{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}@media (any-hover: hover){.toast-button:hover{cursor:pointer}}:host{--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;--start:10px;--end:10px;font-size:14px}.toast-wrapper{-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{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{background:rgba(var(--ion-color-base-rgb), 0.8)}}.toast-wrapper.toast-top{-webkit-transform:translate3d(0, -100%, 0);transform:translate3d(0, -100%, 0);top:0}.toast-wrapper.toast-middle{opacity:0.01}.toast-wrapper.toast-bottom{-webkit-transform:translate3d(0, 100%, 0);transform:translate3d(0, 100%, 0);bottom:0}.toast-content{-webkit-padding-start:15px;padding-inline-start:15px;-webkit-padding-end:15px;padding-inline-end:15px;padding-top:15px;padding-bottom:15px}.toast-header{margin-bottom:2px;font-weight:500}.toast-button{-webkit-padding-start:15px;padding-inline-start:15px;-webkit-padding-end:15px;padding-inline-end:15px;padding-top:10px;padding-bottom:10px;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:17px;font-weight:500;overflow:hidden}.toast-button.ion-activated{opacity:0.4}@media (any-hover: hover){.toast-button:hover{opacity:0.6}}";
188
189
 
189
190
  const toastMdCss = ":host{--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{inset-inline-start:0}}@supports not (inset-inline-start: 0){:host{left:0}:host-context([dir=rtl]){left:unset;right:unset;right:0}@supports selector(:dir(rtl)){:host:dir(rtl){left:unset;right:unset;right:0}}}:host(.overlay-hidden){display:none}:host(.ion-color){--button-color:inherit;color:var(--ion-color-contrast)}:host(.ion-color) .toast-button-cancel{color:inherit}:host(.ion-color) .toast-wrapper{background:var(--ion-color-base)}.toast-wrapper{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{inset-inline-start:var(--start);inset-inline-end:var(--end)}}@supports not (inset-inline-start: 0){.toast-wrapper{left:var(--start);right:var(--end)}:host-context([dir=rtl]) .toast-wrapper{left:unset;right:unset;left:var(--end);right:var(--start)}[dir=rtl] .toast-wrapper{left:unset;right:unset;left:var(--end);right:var(--start)}@supports selector(:dir(rtl)){.toast-wrapper:dir(rtl){left:unset;right:unset;left:var(--end);right:var(--start)}}}.toast-container{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{-ms-flex-wrap:wrap;flex-wrap:wrap}.toast-layout-baseline .toast-content{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{-webkit-margin-start:16px;margin-inline-start:16px}.toast-message{-ms-flex:1;flex:1;white-space:var(--white-space)}.toast-button-group{display:-ms-flexbox;display:flex}.toast-layout-stacked .toast-button-group{-ms-flex-pack:end;justify-content:end;width:100%}.toast-button{border:0;outline:none;color:var(--button-color);z-index:0}.toast-icon,.toast-button-icon{font-size:1.4em}.toast-button-inner{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}@media (any-hover: hover){.toast-button:hover{cursor:pointer}}:host{--background:var(--ion-color-step-800, #333333);--border-radius:4px;--box-shadow:0 3px 5px -1px rgba(0, 0, 0, 0.2), 0 6px 10px 0 rgba(0, 0, 0, 0.14), 0 1px 18px 0 rgba(0, 0, 0, 0.12);--button-color:var(--ion-color-primary, #3880ff);--color:var(--ion-color-step-50, #f2f2f2);--max-width:700px;--start:8px;--end:8px;font-size:14px}.toast-wrapper{-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;opacity:0.01;z-index:10}.toast-content{-webkit-padding-start:16px;padding-inline-start:16px;-webkit-padding-end:16px;padding-inline-end:16px;padding-top:14px;padding-bottom:14px}.toast-header{margin-bottom:2px;font-weight:500;line-height:20px}.toast-message{line-height:20px}.toast-layout-baseline .toast-button-group-start{-webkit-margin-start:8px;margin-inline-start:8px}.toast-layout-stacked .toast-button-group-start{-webkit-margin-end:8px;margin-inline-end:8px;margin-top:8px}.toast-layout-baseline .toast-button-group-end{-webkit-margin-end:8px;margin-inline-end:8px}.toast-layout-stacked .toast-button-group-end{-webkit-margin-end:8px;margin-inline-end:8px;margin-bottom:8px}.toast-button{-webkit-padding-start:15px;padding-inline-start:15px;-webkit-padding-end:15px;padding-inline-end:15px;padding-top:10px;padding-bottom:10px;position:relative;background-color:transparent;font-family:var(--ion-font-family);font-size:14px;font-weight:500;letter-spacing:0.84px;text-transform:uppercase;overflow:hidden}.toast-button-cancel{color:var(--ion-color-step-100, #e6e6e6)}.toast-button-icon-only{border-radius:50%;-webkit-padding-start:9px;padding-inline-start:9px;-webkit-padding-end:9px;padding-inline-end:9px;padding-top:9px;padding-bottom:9px;width:36px;height:36px}@media (any-hover: hover){.toast-button:hover{background-color:rgba(var(--ion-color-primary-rgb, 56, 128, 255), 0.08)}.toast-button-cancel:hover{background-color:rgba(var(--ion-background-color-rgb, 255, 255, 255), 0.08)}}";
@@ -273,32 +274,18 @@ const Toast = class {
273
274
  async present() {
274
275
  const unlock = await this.lockController.lock();
275
276
  await this.delegateController.attachViewToDom();
276
- const { position, positionAnchor } = this;
277
- let anchor;
278
- if (position === 'middle' && positionAnchor !== undefined) {
279
- printIonWarning('The positionAnchor property is ignored when using position="middle".', this.el);
280
- }
281
- else if (typeof positionAnchor === 'string') {
282
- /**
283
- * If the anchor is defined as an ID, find the element.
284
- * We do this on every present so the toast doesn't need
285
- * to account for the surrounding DOM changing since the
286
- * last time it was presented.
287
- */
288
- const foundEl = document.getElementById(positionAnchor);
289
- if (foundEl === null) {
290
- printIonWarning(`An anchor element with an ID of ${positionAnchor} was not found in the DOM.`, this.el);
291
- }
292
- else {
293
- anchor = foundEl;
294
- }
295
- }
296
- else {
297
- anchor = positionAnchor;
298
- }
277
+ const { el, position } = this;
278
+ const anchor = this.getAnchorElement();
279
+ const animationPosition = getAnimationPosition(position, anchor, getIonMode(this), el);
280
+ /**
281
+ * Cache the calculated position of the toast, so we can re-use it
282
+ * in the dismiss animation.
283
+ */
284
+ this.lastPresentedPosition = animationPosition;
299
285
  await present(this, 'toastEnter', iosEnterAnimation, mdEnterAnimation, {
300
286
  position,
301
- positionAnchor: anchor,
287
+ top: animationPosition.top,
288
+ bottom: animationPosition.bottom,
302
289
  });
303
290
  /**
304
291
  * Content is revealed to screen readers after
@@ -321,15 +308,28 @@ const Toast = class {
321
308
  * Some examples include: ``"cancel"`, `"destructive"`, "selected"`, and `"backdrop"`.
322
309
  */
323
310
  async dismiss(data, role) {
311
+ var _a, _b;
324
312
  const unlock = await this.lockController.lock();
325
- if (this.durationTimeout) {
326
- clearTimeout(this.durationTimeout);
313
+ const { durationTimeout, position, lastPresentedPosition } = this;
314
+ if (durationTimeout) {
315
+ clearTimeout(durationTimeout);
327
316
  }
328
- const dismissed = await dismiss(this, data, role, 'toastLeave', iosLeaveAnimation, mdLeaveAnimation, this.position);
317
+ const dismissed = await dismiss(this, data, role, 'toastLeave', iosLeaveAnimation, mdLeaveAnimation,
318
+ /**
319
+ * Fetch the cached position that was calculated back in the present
320
+ * animation. We always want to animate the dismiss from the same
321
+ * position the present stopped at, so the animation looks continuous.
322
+ */
323
+ {
324
+ position,
325
+ top: (_a = lastPresentedPosition === null || lastPresentedPosition === void 0 ? void 0 : lastPresentedPosition.top) !== null && _a !== void 0 ? _a : '',
326
+ bottom: (_b = lastPresentedPosition === null || lastPresentedPosition === void 0 ? void 0 : lastPresentedPosition.bottom) !== null && _b !== void 0 ? _b : '',
327
+ });
329
328
  if (dismissed) {
330
329
  this.delegateController.removeViewFromDom();
331
330
  this.revealContentToScreenReader = false;
332
331
  }
332
+ this.lastPresentedPosition = undefined;
333
333
  unlock();
334
334
  return dismissed;
335
335
  }
@@ -353,6 +353,37 @@ const Toast = class {
353
353
  : [];
354
354
  return buttons;
355
355
  }
356
+ /**
357
+ * Returns the element specified by the positionAnchor prop,
358
+ * or undefined if prop's value is an ID string and the element
359
+ * is not found in the DOM.
360
+ */
361
+ getAnchorElement() {
362
+ const { position, positionAnchor, el } = this;
363
+ if (position === 'middle' && positionAnchor !== undefined) {
364
+ printIonWarning('The positionAnchor property is ignored when using position="middle".', this.el);
365
+ return undefined;
366
+ }
367
+ if (typeof positionAnchor === 'string') {
368
+ /**
369
+ * If the anchor is defined as an ID, find the element.
370
+ * We do this on every present so the toast doesn't need
371
+ * to account for the surrounding DOM changing since the
372
+ * last time it was presented.
373
+ */
374
+ const foundEl = document.getElementById(positionAnchor);
375
+ if (foundEl === null) {
376
+ printIonWarning(`An anchor element with an ID of "${positionAnchor}" was not found in the DOM.`, el);
377
+ return undefined;
378
+ }
379
+ return foundEl;
380
+ }
381
+ if (positionAnchor instanceof HTMLElement) {
382
+ return positionAnchor;
383
+ }
384
+ printIonWarning('Invalid positionAnchor value:', positionAnchor, el);
385
+ return undefined;
386
+ }
356
387
  async buttonClick(button) {
357
388
  const role = button.role;
358
389
  if (isCancel(role)) {
@@ -1,4 +1,4 @@
1
1
  /*!
2
2
  * (C) Ionic http://ionicframework.com - MIT License
3
3
  */
4
- export{c as createAnimation}from"./animation-a1d9e088.js";export{a as LIFECYCLE_DID_ENTER,c as LIFECYCLE_DID_LEAVE,L as LIFECYCLE_WILL_ENTER,b as LIFECYCLE_WILL_LEAVE,d as LIFECYCLE_WILL_UNLOAD,g as getIonPageElement}from"./index-dac72b4c.js";export{iosTransitionAnimation}from"./ios.transition-991c1a56.js";export{mdTransitionAnimation}from"./md.transition-fb443521.js";export{g as getTimeGivenProgression}from"./cubic-bezier-66542bc5.js";export{createGesture}from"./index-ff313b19.js";export{g as getPlatforms,i as initialize,a as isPlatform}from"./ionic-global-b3fc28dd.js";export{c as componentOnReady}from"./helpers-3379ba19.js";export{I as IonicSafeString,g as getMode,s as setupConfig}from"./config-96c9ace3.js";export{m as menuController}from"./index-61bddc90.js";export{b as actionSheetController,a as alertController,l as loadingController,m as modalController,p as pickerController,c as popoverController,t as toastController}from"./overlays-e064e499.js";import"./index-7a14ecec.js";import"./index-2d388930.js";import"./gesture-controller-0fa396c4.js";import"./hardware-back-button-39299f84.js";import"./framework-delegate-aa433dea.js";import"./index-595d62c9.js";var IonicSlides=function(e){var o=e.swiper,t=e.extendParams;var s={effect:undefined,direction:"horizontal",initialSlide:0,loop:false,parallax:false,slidesPerView:1,spaceBetween:0,speed:300,slidesPerColumn:1,slidesPerColumnFill:"column",slidesPerGroup:1,centeredSlides:false,slidesOffsetBefore:0,slidesOffsetAfter:0,touchEventsTarget:"container",freeMode:false,freeModeMomentum:true,freeModeMomentumRatio:1,freeModeMomentumBounce:true,freeModeMomentumBounceRatio:1,freeModeMomentumVelocityRatio:1,freeModeSticky:false,freeModeMinimumVelocity:.02,autoHeight:false,setWrapperSize:false,zoom:{maxRatio:3,minRatio:1,toggle:false},touchRatio:1,touchAngle:45,simulateTouch:true,touchStartPreventDefault:false,shortSwipes:true,longSwipes:true,longSwipesRatio:.5,longSwipesMs:300,followFinger:true,threshold:0,touchMoveStopPropagation:true,touchReleaseOnEdges:false,iOSEdgeSwipeDetection:false,iOSEdgeSwipeThreshold:20,resistance:true,resistanceRatio:.85,watchSlidesProgress:false,watchSlidesVisibility:false,preventClicks:true,preventClicksPropagation:true,slideToClickedSlide:false,loopAdditionalSlides:0,noSwiping:true,runCallbacksOnInit:true,coverflowEffect:{rotate:50,stretch:0,depth:100,modifier:1,slideShadows:true},flipEffect:{slideShadows:true,limitRotation:true},cubeEffect:{slideShadows:true,shadow:true,shadowOffset:20,shadowScale:.94},fadeEffect:{crossFade:false},a11y:{prevSlideMessage:"Previous slide",nextSlideMessage:"Next slide",firstSlideMessage:"This is the first slide",lastSlideMessage:"This is the last slide"}};if(o.pagination){s.pagination={type:"bullets",clickable:false,hideOnClick:false}}if(o.scrollbar){s.scrollbar={hide:true}}t(s)};export{IonicSlides};
4
+ export{c as createAnimation}from"./animation-a1d9e088.js";export{a as LIFECYCLE_DID_ENTER,c as LIFECYCLE_DID_LEAVE,L as LIFECYCLE_WILL_ENTER,b as LIFECYCLE_WILL_LEAVE,d as LIFECYCLE_WILL_UNLOAD,g as getIonPageElement}from"./index-dac72b4c.js";export{iosTransitionAnimation}from"./ios.transition-991c1a56.js";export{mdTransitionAnimation}from"./md.transition-fb443521.js";export{g as getTimeGivenProgression}from"./cubic-bezier-66542bc5.js";export{createGesture}from"./index-ff313b19.js";export{g as getPlatforms,i as initialize,a as isPlatform}from"./ionic-global-b3fc28dd.js";export{c as componentOnReady}from"./helpers-3379ba19.js";export{I as IonicSafeString,g as getMode,s as setupConfig}from"./config-96c9ace3.js";export{o as openURL}from"./theme-17531cdf.js";export{m as menuController}from"./index-61bddc90.js";export{b as actionSheetController,a as alertController,l as loadingController,m as modalController,p as pickerController,c as popoverController,t as toastController}from"./overlays-e064e499.js";import"./index-7a14ecec.js";import"./index-2d388930.js";import"./gesture-controller-0fa396c4.js";import"./hardware-back-button-39299f84.js";import"./framework-delegate-aa433dea.js";import"./index-595d62c9.js";var IonicSlides=function(e){var o=e.swiper,t=e.extendParams;var s={effect:undefined,direction:"horizontal",initialSlide:0,loop:false,parallax:false,slidesPerView:1,spaceBetween:0,speed:300,slidesPerColumn:1,slidesPerColumnFill:"column",slidesPerGroup:1,centeredSlides:false,slidesOffsetBefore:0,slidesOffsetAfter:0,touchEventsTarget:"container",freeMode:false,freeModeMomentum:true,freeModeMomentumRatio:1,freeModeMomentumBounce:true,freeModeMomentumBounceRatio:1,freeModeMomentumVelocityRatio:1,freeModeSticky:false,freeModeMinimumVelocity:.02,autoHeight:false,setWrapperSize:false,zoom:{maxRatio:3,minRatio:1,toggle:false},touchRatio:1,touchAngle:45,simulateTouch:true,touchStartPreventDefault:false,shortSwipes:true,longSwipes:true,longSwipesRatio:.5,longSwipesMs:300,followFinger:true,threshold:0,touchMoveStopPropagation:true,touchReleaseOnEdges:false,iOSEdgeSwipeDetection:false,iOSEdgeSwipeThreshold:20,resistance:true,resistanceRatio:.85,watchSlidesProgress:false,watchSlidesVisibility:false,preventClicks:true,preventClicksPropagation:true,slideToClickedSlide:false,loopAdditionalSlides:0,noSwiping:true,runCallbacksOnInit:true,coverflowEffect:{rotate:50,stretch:0,depth:100,modifier:1,slideShadows:true},flipEffect:{slideShadows:true,limitRotation:true},cubeEffect:{slideShadows:true,shadow:true,shadowOffset:20,shadowScale:.94},fadeEffect:{crossFade:false},a11y:{prevSlideMessage:"Previous slide",nextSlideMessage:"Next slide",firstSlideMessage:"This is the first slide",lastSlideMessage:"This is the last slide"}};if(o.pagination){s.pagination={type:"bullets",clickable:false,hideOnClick:false}}if(o.scrollbar){s.scrollbar={hide:true}}t(s)};export{IonicSlides};