@rdlabo/ionic-theme-ios27 0.1.0-0

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 (158) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +241 -0
  3. package/dist/css/components/ion-action-sheet.css +1 -0
  4. package/dist/css/components/ion-alert.css +1 -0
  5. package/dist/css/components/ion-button.css +1 -0
  6. package/dist/css/components/ion-card.css +1 -0
  7. package/dist/css/components/ion-content.css +1 -0
  8. package/dist/css/components/ion-datetime.css +1 -0
  9. package/dist/css/components/ion-fab.css +1 -0
  10. package/dist/css/components/ion-inputs.css +1 -0
  11. package/dist/css/components/ion-list.css +1 -0
  12. package/dist/css/components/ion-loading.css +1 -0
  13. package/dist/css/components/ion-menu.css +1 -0
  14. package/dist/css/components/ion-modal.css +1 -0
  15. package/dist/css/components/ion-picker.css +1 -0
  16. package/dist/css/components/ion-popover.css +1 -0
  17. package/dist/css/components/ion-range.css +1 -0
  18. package/dist/css/components/ion-searchbar.css +1 -0
  19. package/dist/css/components/ion-segment.css +1 -0
  20. package/dist/css/components/ion-tabs.css +1 -0
  21. package/dist/css/components/ion-toast.css +1 -0
  22. package/dist/css/components/ion-toggle.css +1 -0
  23. package/dist/css/components/ion-toolbar.css +1 -0
  24. package/dist/css/default-variables.css +1 -0
  25. package/dist/css/ionic-theme-ios26-dark-always.css +1 -0
  26. package/dist/css/ionic-theme-ios26-dark-class.css +1 -0
  27. package/dist/css/ionic-theme-ios26-dark-system.css +1 -0
  28. package/dist/css/ionic-theme-ios26.css +1 -0
  29. package/dist/css/md-ion-list-inset.css +1 -0
  30. package/dist/css/md-remove-ios-class-effect.css +1 -0
  31. package/dist/css/utils/api.css +1 -0
  32. package/dist/css/utils/dark/ion-button.css +1 -0
  33. package/dist/css/utils/dark/ion-fab.css +1 -0
  34. package/dist/css/utils/dark/ion-segment.css +1 -0
  35. package/dist/css/utils/dark/ion-tabs.css +1 -0
  36. package/dist/css/utils/searchable.css +1 -0
  37. package/dist/css/utils/structured-list.css +1 -0
  38. package/dist/css/utils/theme-dark.css +1 -0
  39. package/dist/css/utils/translucent.css +1 -0
  40. package/dist/docs.json +234 -0
  41. package/dist/focus-controller/index.d.ts +6 -0
  42. package/dist/focus-controller/index.d.ts.map +1 -0
  43. package/dist/focus-controller/index.js +62 -0
  44. package/dist/focus-controller /index.d.ts +6 -0
  45. package/dist/focus-controller /index.d.ts.map +1 -0
  46. package/dist/focus-controller /index.js +62 -0
  47. package/dist/index.d.ts +9 -0
  48. package/dist/index.d.ts.map +1 -0
  49. package/dist/index.js +30 -0
  50. package/dist/popover/animations/ios.enter.d.ts +4 -0
  51. package/dist/popover/animations/ios.enter.d.ts.map +1 -0
  52. package/dist/popover/animations/ios.enter.js +97 -0
  53. package/dist/popover/animations/ios.leave.d.ts +3 -0
  54. package/dist/popover/animations/ios.leave.d.ts.map +1 -0
  55. package/dist/popover/animations/ios.leave.js +60 -0
  56. package/dist/popover/popover-interface.d.ts +38 -0
  57. package/dist/popover/popover-interface.d.ts.map +1 -0
  58. package/dist/popover/popover-interface.js +1 -0
  59. package/dist/popover/utils.d.ts +37 -0
  60. package/dist/popover/utils.d.ts.map +1 -0
  61. package/dist/popover/utils.js +377 -0
  62. package/dist/sheets-of-glass/animations.d.ts +8 -0
  63. package/dist/sheets-of-glass/animations.d.ts.map +1 -0
  64. package/dist/sheets-of-glass/animations.js +97 -0
  65. package/dist/sheets-of-glass/index.d.ts +3 -0
  66. package/dist/sheets-of-glass/index.d.ts.map +1 -0
  67. package/dist/sheets-of-glass/index.js +174 -0
  68. package/dist/sheets-of-glass/interfaces.d.ts +16 -0
  69. package/dist/sheets-of-glass/interfaces.d.ts.map +1 -0
  70. package/dist/sheets-of-glass/interfaces.js +1 -0
  71. package/dist/tab-bar-searchable/animations/enter.d.ts +8 -0
  72. package/dist/tab-bar-searchable/animations/enter.d.ts.map +1 -0
  73. package/dist/tab-bar-searchable/animations/enter.js +73 -0
  74. package/dist/tab-bar-searchable/animations/leave.d.ts +8 -0
  75. package/dist/tab-bar-searchable/animations/leave.d.ts.map +1 -0
  76. package/dist/tab-bar-searchable/animations/leave.js +66 -0
  77. package/dist/tab-bar-searchable/index.d.ts +4 -0
  78. package/dist/tab-bar-searchable/index.d.ts.map +1 -0
  79. package/dist/tab-bar-searchable/index.js +75 -0
  80. package/dist/tab-bar-searchable/interfaces.d.ts +41 -0
  81. package/dist/tab-bar-searchable/interfaces.d.ts.map +1 -0
  82. package/dist/tab-bar-searchable/interfaces.js +5 -0
  83. package/dist/tab-bar-searchable/utils.d.ts +12 -0
  84. package/dist/tab-bar-searchable/utils.d.ts.map +1 -0
  85. package/dist/tab-bar-searchable/utils.js +60 -0
  86. package/dist/transition/index.d.ts +19 -0
  87. package/dist/transition/index.d.ts.map +1 -0
  88. package/dist/transition/index.js +219 -0
  89. package/dist/transition/ios.transition.d.ts +5 -0
  90. package/dist/transition/ios.transition.d.ts.map +1 -0
  91. package/dist/transition/ios.transition.js +609 -0
  92. package/dist/utils.d.ts +17 -0
  93. package/dist/utils.d.ts.map +1 -0
  94. package/dist/utils.js +69 -0
  95. package/docs/e2e-testing.md +51 -0
  96. package/docs/eslint.md +73 -0
  97. package/docs/experimental-animation.md +85 -0
  98. package/docs/features.md +88 -0
  99. package/docs/feedback.md +65 -0
  100. package/docs/ios-18.md +16 -0
  101. package/docs/migration.md +94 -0
  102. package/docs/special-markup.md +145 -0
  103. package/docs/using-ion-item-group.md +47 -0
  104. package/package.json +63 -0
  105. package/src/focus-controller/index.ts +123 -0
  106. package/src/index.ts +33 -0
  107. package/src/popover/animations/ios.enter.ts +144 -0
  108. package/src/popover/animations/ios.leave.ts +76 -0
  109. package/src/popover/popover-interface.ts +44 -0
  110. package/src/popover/utils.ts +696 -0
  111. package/src/sheets-of-glass/animations.ts +120 -0
  112. package/src/sheets-of-glass/index.ts +214 -0
  113. package/src/sheets-of-glass/interfaces.ts +17 -0
  114. package/src/styles/components/ion-action-sheet.scss +85 -0
  115. package/src/styles/components/ion-alert.scss +66 -0
  116. package/src/styles/components/ion-button.scss +443 -0
  117. package/src/styles/components/ion-card.scss +7 -0
  118. package/src/styles/components/ion-content.scss +22 -0
  119. package/src/styles/components/ion-datetime.scss +9 -0
  120. package/src/styles/components/ion-fab.scss +104 -0
  121. package/src/styles/components/ion-inputs.scss +82 -0
  122. package/src/styles/components/ion-list.scss +44 -0
  123. package/src/styles/components/ion-loading.scss +11 -0
  124. package/src/styles/components/ion-menu.scss +110 -0
  125. package/src/styles/components/ion-modal.scss +42 -0
  126. package/src/styles/components/ion-picker.scss +4 -0
  127. package/src/styles/components/ion-popover.scss +33 -0
  128. package/src/styles/components/ion-range.scss +90 -0
  129. package/src/styles/components/ion-searchbar.scss +68 -0
  130. package/src/styles/components/ion-segment.scss +150 -0
  131. package/src/styles/components/ion-tabs.scss +118 -0
  132. package/src/styles/components/ion-toast.scss +35 -0
  133. package/src/styles/components/ion-toggle.scss +65 -0
  134. package/src/styles/components/ion-toolbar.scss +76 -0
  135. package/src/styles/default-variables.scss +38 -0
  136. package/src/styles/ionic-theme-ios26-dark-always.scss +6 -0
  137. package/src/styles/ionic-theme-ios26-dark-class.scss +6 -0
  138. package/src/styles/ionic-theme-ios26-dark-system.scss +8 -0
  139. package/src/styles/ionic-theme-ios26.scss +23 -0
  140. package/src/styles/md-ion-list-inset.scss +22 -0
  141. package/src/styles/md-remove-ios-class-effect.scss +14 -0
  142. package/src/styles/utils/api.scss +146 -0
  143. package/src/styles/utils/dark/ion-button.scss +58 -0
  144. package/src/styles/utils/dark/ion-fab.scss +18 -0
  145. package/src/styles/utils/dark/ion-segment.scss +12 -0
  146. package/src/styles/utils/dark/ion-tabs.scss +21 -0
  147. package/src/styles/utils/searchable.scss +0 -0
  148. package/src/styles/utils/structured-list.scss +170 -0
  149. package/src/styles/utils/theme-dark.scss +25 -0
  150. package/src/styles/utils/translucent.scss +95 -0
  151. package/src/tab-bar-searchable/animations/enter.ts +92 -0
  152. package/src/tab-bar-searchable/animations/leave.ts +89 -0
  153. package/src/tab-bar-searchable/index.ts +141 -0
  154. package/src/tab-bar-searchable/interfaces.ts +28 -0
  155. package/src/tab-bar-searchable/utils.ts +75 -0
  156. package/src/transition/index.ts +369 -0
  157. package/src/transition/ios.transition.ts +966 -0
  158. package/src/utils.ts +91 -0
@@ -0,0 +1,75 @@
1
+ import { ElementReferences, ElementSizes } from './interfaces';
2
+
3
+ export const ANIMATION_DURATION = 400;
4
+ export const ANIMATION_DELAY_BASE = 140;
5
+ export const ANIMATION_DELAY_CLOSE_BUTTONS = 240;
6
+ export const ANIMATION_EASING = 'cubic-bezier(0, 1, 0.22, 1)';
7
+ export const OPACITY_TRANSITION = 'opacity 140ms ease';
8
+
9
+ export const throwErrorByFailedClickElement = (selector: string): Error => {
10
+ return new Error('Expected click element to be inside `' + selector + '`');
11
+ };
12
+
13
+ export const throwErrorByFailedExistElement = (selector: string): Error => {
14
+ return new Error('Expected element `' + selector + '` to exist');
15
+ };
16
+
17
+ export const getElement = (docs: HTMLElement, selector: string): HTMLElement => {
18
+ const el = docs.querySelector<HTMLElement>(selector);
19
+ if (!el) {
20
+ throw throwErrorByFailedClickElement(selector);
21
+ }
22
+ return el;
23
+ };
24
+
25
+ export const getElementReferences = (ionTabBar: HTMLElement, ionFooter: HTMLElement): ElementReferences => {
26
+ const searchContainer = getElement(ionFooter, 'ion-searchbar .searchbar-input-container');
27
+ const closeButtons = getElement(ionFooter, 'ion-buttons[slot=start]');
28
+ const selectedTabButton = ionTabBar.querySelector<HTMLElement>('ion-tab-button.tab-selected');
29
+ const selectedTabButtonIcon = selectedTabButton?.querySelector('ion-icon');
30
+ const closeButtonIcon = closeButtons.querySelector('ion-icon');
31
+
32
+ if (!selectedTabButton) {
33
+ throw throwErrorByFailedExistElement('ion-tab-button.tab-selected');
34
+ }
35
+
36
+ if (!selectedTabButtonIcon) {
37
+ throw throwErrorByFailedExistElement('ion-tab-button.tab-selected ion-icon');
38
+ }
39
+
40
+ if (!closeButtonIcon) {
41
+ throw throwErrorByFailedExistElement('ion-buttons[slot=start] ion-button ion-icon');
42
+ }
43
+
44
+ return {
45
+ searchContainer,
46
+ closeButtons,
47
+ selectedTabButton,
48
+ selectedTabButtonIcon,
49
+ closeButtonIcon,
50
+ };
51
+ };
52
+
53
+ /**
54
+ * 各要素のサイズ情報を取得
55
+ */
56
+ export const getElementSizes = (ionTabBar: HTMLElement, ionFabButton: HTMLElement, references: ElementReferences): ElementSizes => {
57
+ const tabBarRect = ionTabBar.getBoundingClientRect();
58
+ const fabButtonRect = ionFabButton.getBoundingClientRect();
59
+ const closeButtonRect = references.closeButtons.getBoundingClientRect();
60
+ const searchContainerRect = references.searchContainer.getBoundingClientRect();
61
+ const selectedTabButtonIconRect = references.selectedTabButtonIcon!.getBoundingClientRect();
62
+
63
+ return {
64
+ tabBar: { width: tabBarRect.width, height: tabBarRect.height },
65
+ closeButton: { width: closeButtonRect.width, height: closeButtonRect.height },
66
+ fabButton: { width: fabButtonRect.width, height: fabButtonRect.height },
67
+ searchContainer: { width: searchContainerRect.width, height: searchContainerRect.height },
68
+ selectedTabButtonIcon: {
69
+ width: selectedTabButtonIconRect.width,
70
+ height: selectedTabButtonIconRect.height,
71
+ top: selectedTabButtonIconRect.top,
72
+ left: selectedTabButtonIconRect.left,
73
+ },
74
+ };
75
+ };
@@ -0,0 +1,369 @@
1
+ import { config } from '../utils';
2
+ import { Build, writeTask } from '@stencil/core';
3
+
4
+ import { LIFECYCLE_DID_ENTER, LIFECYCLE_DID_LEAVE, LIFECYCLE_WILL_ENTER, LIFECYCLE_WILL_LEAVE } from '@ionic/core';
5
+ import type { NavOptions, NavDirection } from '@ionic/core';
6
+ import type { Animation, AnimationBuilder } from '@ionic/core';
7
+ import { createFocusController } from '../focus-controller';
8
+ import { raf } from '../utils';
9
+
10
+ const iosTransitionAnimation = () => import('./ios.transition');
11
+ // const mdTransitionAnimation = () => import('./md.transition');
12
+ const focusController = createFocusController();
13
+
14
+ // TODO(FW-2832): types
15
+
16
+ /**
17
+ * Executes the main page transition.
18
+ * It also manages the lifecycle of header visibility (if any)
19
+ * to prevent visual flickering in iOS. The flickering only
20
+ * occurs for a condensed header that is placed above the content.
21
+ *
22
+ * @param opts Options for the transition.
23
+ * @returns A promise that resolves when the transition is complete.
24
+ */
25
+ export const transition = (opts: TransitionOptions): Promise<TransitionResult> => {
26
+ return new Promise((resolve, reject) => {
27
+ writeTask(() => {
28
+ const transitioningInactiveHeader = getIosIonHeader(opts);
29
+ beforeTransition(opts, transitioningInactiveHeader);
30
+ runTransition(opts)
31
+ .then(
32
+ (result) => {
33
+ if (result.animation) {
34
+ result.animation.destroy();
35
+ }
36
+ afterTransition(opts);
37
+ resolve(result);
38
+ },
39
+ (error) => {
40
+ afterTransition(opts);
41
+ reject(error);
42
+ },
43
+ )
44
+ .finally(() => {
45
+ // Ensure that the header is restored to its original state.
46
+ setHeaderTransitionClass(transitioningInactiveHeader, false);
47
+ });
48
+ });
49
+ });
50
+ };
51
+
52
+ const beforeTransition = (opts: TransitionOptions, transitioningInactiveHeader: HTMLElement | null) => {
53
+ const enteringEl = opts.enteringEl;
54
+ const leavingEl = opts.leavingEl;
55
+
56
+ focusController.saveViewFocus(leavingEl);
57
+
58
+ setZIndex(enteringEl, leavingEl, opts.direction);
59
+ // Prevent flickering of the header by adding a class.
60
+ setHeaderTransitionClass(transitioningInactiveHeader, true);
61
+
62
+ if (opts.showGoBack) {
63
+ enteringEl.classList.add('can-go-back');
64
+ } else {
65
+ enteringEl.classList.remove('can-go-back');
66
+ }
67
+ setPageHidden(enteringEl, false);
68
+
69
+ /**
70
+ * When transitioning, the page should not
71
+ * respond to click events. This resolves small
72
+ * issues like users double tapping the ion-back-button.
73
+ * These pointer events are removed in `afterTransition`.
74
+ */
75
+ enteringEl.style.setProperty('pointer-events', 'none');
76
+
77
+ if (leavingEl) {
78
+ setPageHidden(leavingEl, false);
79
+ leavingEl.style.setProperty('pointer-events', 'none');
80
+ }
81
+ };
82
+
83
+ const runTransition = async (opts: TransitionOptions): Promise<TransitionResult> => {
84
+ const animationBuilder = await getAnimationBuilder(opts);
85
+
86
+ const ani = animationBuilder && Build.isBrowser ? animation(animationBuilder, opts) : noAnimation(opts); // fast path for no animation
87
+
88
+ return ani;
89
+ };
90
+
91
+ const afterTransition = (opts: TransitionOptions) => {
92
+ const enteringEl = opts.enteringEl;
93
+ const leavingEl = opts.leavingEl;
94
+ enteringEl.classList.remove('ion-page-invisible');
95
+ enteringEl.style.removeProperty('pointer-events');
96
+ if (leavingEl !== undefined) {
97
+ leavingEl.classList.remove('ion-page-invisible');
98
+ leavingEl.style.removeProperty('pointer-events');
99
+ }
100
+
101
+ focusController.setViewFocus(enteringEl);
102
+ };
103
+
104
+ const getAnimationBuilder = async (opts: TransitionOptions): Promise<AnimationBuilder | undefined> => {
105
+ if (!opts.leavingEl || !opts.animated || opts.duration === 0) {
106
+ return undefined;
107
+ }
108
+
109
+ if (opts.animationBuilder) {
110
+ return opts.animationBuilder;
111
+ }
112
+
113
+ const getAnimation =
114
+ // opts.mode === 'ios'
115
+ (await iosTransitionAnimation()).iosTransitionAnimation;
116
+ // : (await mdTransitionAnimation()).mdTransitionAnimation;
117
+
118
+ return getAnimation;
119
+ };
120
+
121
+ const animation = async (animationBuilder: AnimationBuilder, opts: TransitionOptions): Promise<TransitionResult> => {
122
+ await waitForReady(opts, true);
123
+
124
+ const trans = animationBuilder(opts.baseEl, opts);
125
+
126
+ fireWillEvents(opts.enteringEl, opts.leavingEl);
127
+
128
+ const didComplete = await playTransition(trans, opts);
129
+
130
+ if (opts.progressCallback) {
131
+ opts.progressCallback(undefined);
132
+ }
133
+
134
+ if (didComplete) {
135
+ fireDidEvents(opts.enteringEl, opts.leavingEl);
136
+ }
137
+
138
+ return {
139
+ hasCompleted: didComplete,
140
+ animation: trans,
141
+ };
142
+ };
143
+
144
+ const noAnimation = async (opts: TransitionOptions): Promise<TransitionResult> => {
145
+ const enteringEl = opts.enteringEl;
146
+ const leavingEl = opts.leavingEl;
147
+ const focusManagerEnabled = config.get('focusManagerPriority', false);
148
+
149
+ /**
150
+ * If the focus manager is enabled then we need to wait for Ionic components to be
151
+ * rendered otherwise the component to focus may not be focused because it is hidden.
152
+ */
153
+ await waitForReady(opts, focusManagerEnabled);
154
+
155
+ fireWillEvents(enteringEl, leavingEl);
156
+ fireDidEvents(enteringEl, leavingEl);
157
+
158
+ return {
159
+ hasCompleted: true,
160
+ };
161
+ };
162
+
163
+ const waitForReady = async (opts: TransitionOptions, defaultDeep: boolean) => {
164
+ const deep = opts.deepWait !== undefined ? opts.deepWait : defaultDeep;
165
+
166
+ if (deep) {
167
+ await Promise.all([deepReady(opts.enteringEl), deepReady(opts.leavingEl)]);
168
+ }
169
+
170
+ await notifyViewReady(opts.viewIsReady, opts.enteringEl);
171
+ };
172
+
173
+ const notifyViewReady = async (viewIsReady: undefined | ((enteringEl: HTMLElement) => Promise<any>), enteringEl: HTMLElement) => {
174
+ if (viewIsReady) {
175
+ await viewIsReady(enteringEl);
176
+ }
177
+ };
178
+
179
+ const playTransition = (trans: Animation, opts: TransitionOptions): Promise<boolean> => {
180
+ const progressCallback = opts.progressCallback;
181
+
182
+ const promise = new Promise<boolean>((resolve) => {
183
+ trans.onFinish((currentStep: any) => resolve(currentStep === 1));
184
+ });
185
+
186
+ // cool, let's do this, start the transition
187
+ if (progressCallback) {
188
+ // this is a swipe to go back, just get the transition progress ready
189
+ // kick off the swipe animation start
190
+ trans.progressStart(true);
191
+ progressCallback(trans);
192
+ } else {
193
+ // only the top level transition should actually start "play"
194
+ // kick it off and let it play through
195
+ // ******** DOM WRITE ****************
196
+ trans.play();
197
+ }
198
+ // create a callback for when the animation is done
199
+ return promise;
200
+ };
201
+
202
+ const fireWillEvents = (enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined) => {
203
+ lifecycle(leavingEl, LIFECYCLE_WILL_LEAVE);
204
+ lifecycle(enteringEl, LIFECYCLE_WILL_ENTER);
205
+ };
206
+
207
+ const fireDidEvents = (enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined) => {
208
+ lifecycle(enteringEl, LIFECYCLE_DID_ENTER);
209
+ lifecycle(leavingEl, LIFECYCLE_DID_LEAVE);
210
+ };
211
+
212
+ export const lifecycle = (el: HTMLElement | undefined, eventName: string) => {
213
+ if (el) {
214
+ const ev = new CustomEvent(eventName, {
215
+ bubbles: false,
216
+ cancelable: false,
217
+ });
218
+ el.dispatchEvent(ev);
219
+ }
220
+ };
221
+
222
+ /**
223
+ * Wait two request animation frame loops.
224
+ * This allows the framework implementations enough time to mount
225
+ * the user-defined contents. This is often needed when using inline
226
+ * modals and popovers that accept user components. For popover,
227
+ * the contents must be mounted for the popover to be sized correctly.
228
+ * For modals, the contents must be mounted for iOS to run the
229
+ * transition correctly.
230
+ *
231
+ * On Angular and React, a single raf is enough time, but for Vue
232
+ * we need to wait two rafs. As a result we are using two rafs for
233
+ * all frameworks to ensure contents are mounted.
234
+ */
235
+ export const waitForMount = (): Promise<void> => {
236
+ return new Promise((resolve) => raf(() => raf(() => resolve())));
237
+ };
238
+
239
+ export const deepReady = async (el: any | undefined): Promise<void> => {
240
+ const element = el as any;
241
+ if (element) {
242
+ if (element.componentOnReady != null) {
243
+ // eslint-disable-next-line custom-rules/no-component-on-ready-method
244
+ const stencilEl = await element.componentOnReady();
245
+ if (stencilEl != null) {
246
+ return;
247
+ }
248
+
249
+ /**
250
+ * Custom elements in Stencil will have __registerHost.
251
+ */
252
+ } else if (element.__registerHost != null) {
253
+ /**
254
+ * Non-lazy loaded custom elements need to wait
255
+ * one frame for component to be loaded.
256
+ */
257
+ const waitForCustomElement = new Promise((resolve) => raf(resolve));
258
+ await waitForCustomElement;
259
+
260
+ return;
261
+ }
262
+ await Promise.all(Array.from(element.children).map(deepReady));
263
+ }
264
+ };
265
+
266
+ export const setPageHidden = (el: HTMLElement, hidden: boolean) => {
267
+ if (hidden) {
268
+ el.setAttribute('aria-hidden', 'true');
269
+ el.classList.add('ion-page-hidden');
270
+ } else {
271
+ el.hidden = false;
272
+ el.removeAttribute('aria-hidden');
273
+ el.classList.remove('ion-page-hidden');
274
+ }
275
+ };
276
+
277
+ const setZIndex = (enteringEl: HTMLElement | undefined, leavingEl: HTMLElement | undefined, direction: NavDirection | undefined) => {
278
+ if (enteringEl !== undefined) {
279
+ enteringEl.style.zIndex = direction === 'back' ? '99' : '101';
280
+ }
281
+ if (leavingEl !== undefined) {
282
+ leavingEl.style.zIndex = '100';
283
+ }
284
+ };
285
+
286
+ /**
287
+ * Add a class to ensure that the header (if any)
288
+ * does not flicker during the transition. By adding the
289
+ * transitioning class, we ensure that the header has
290
+ * the necessary styles to prevent the following flickers:
291
+ * 1. When entering a page with a condensed header, the
292
+ * header should never be visible. However,
293
+ * it briefly renders the background color while
294
+ * the transition is occurring.
295
+ * 2. When leaving a page with a condensed header, the
296
+ * header has an opacity of 0 and the pages
297
+ * have a z-index which causes the entering page to
298
+ * briefly show it's content underneath the leaving page.
299
+ * 3. When entering a page or leaving a page with a fade
300
+ * header, the header should not have a background color.
301
+ * However, it briefly shows the background color while
302
+ * the transition is occurring.
303
+ *
304
+ * @param header The header element to modify.
305
+ * @param isTransitioning Whether the transition is occurring.
306
+ */
307
+ const setHeaderTransitionClass = (header: HTMLElement | null, isTransitioning: boolean) => {
308
+ if (!header) {
309
+ return;
310
+ }
311
+
312
+ const transitionClass = 'header-transitioning';
313
+ if (isTransitioning) {
314
+ header.classList.add(transitionClass);
315
+ } else {
316
+ header.classList.remove(transitionClass);
317
+ }
318
+ };
319
+
320
+ export const getIonPageElement = (element: HTMLElement) => {
321
+ if (element.classList.contains('ion-page')) {
322
+ return element;
323
+ }
324
+
325
+ const ionPage = element.querySelector(':scope > .ion-page, :scope > ion-nav, :scope > ion-tabs');
326
+ if (ionPage) {
327
+ return ionPage;
328
+ }
329
+ // idk, return the original element so at least something animates and we don't have a null pointer
330
+ return element;
331
+ };
332
+
333
+ /**
334
+ * Retrieves the ion-header element from a page based on the
335
+ * direction of the transition.
336
+ *
337
+ * @param opts Options for the transition.
338
+ * @returns The ion-header element or null if not found or not in 'ios' mode.
339
+ */
340
+ const getIosIonHeader = (opts: TransitionOptions): HTMLElement | null => {
341
+ const enteringEl = opts.enteringEl;
342
+ const leavingEl = opts.leavingEl;
343
+ const direction = opts.direction;
344
+ const mode = opts.mode;
345
+
346
+ if (mode !== 'ios') {
347
+ return null;
348
+ }
349
+
350
+ const element = direction === 'back' ? leavingEl : enteringEl;
351
+
352
+ if (!element) {
353
+ return null;
354
+ }
355
+
356
+ return element.querySelector('ion-header');
357
+ };
358
+
359
+ export interface TransitionOptions extends NavOptions {
360
+ progressCallback?: (ani: Animation | undefined) => void;
361
+ baseEl: any;
362
+ enteringEl: HTMLElement;
363
+ leavingEl: HTMLElement | undefined;
364
+ }
365
+
366
+ export interface TransitionResult {
367
+ hasCompleted: boolean;
368
+ animation?: Animation;
369
+ }