@amazon-devices/react-navigation__stack 2.0.1758683737

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 (242) hide show
  1. package/LICENSE +7 -0
  2. package/README.md +5 -0
  3. package/SystemBundles/ed2637c0b74cd42ce066fca701acc5cb0850978c5296fae5bd715d5e4c67b128.bundle +29321 -0
  4. package/SystemBundles/ed2637c0b74cd42ce066fca701acc5cb0850978c5296fae5bd715d5e4c67b128.map +1 -0
  5. package/SystemModules/amzn__react-navigation__stack-2.modules.txt +95 -0
  6. package/SystemModules/amzn__react-navigation__stack-2.private.modules.txt +258 -0
  7. package/SystemModules/keplerscript-system-bundle-config.json +7 -0
  8. package/THIRD_PARTY_NOTICES.txt +24 -0
  9. package/kepler-compatibility.json +8 -0
  10. package/kepler-transformed-package-info.json +3 -0
  11. package/lib/commonjs/TransitionConfigs/CardStyleInterpolators.js +353 -0
  12. package/lib/commonjs/TransitionConfigs/CardStyleInterpolators.js.map +1 -0
  13. package/lib/commonjs/TransitionConfigs/HeaderStyleInterpolators.js +259 -0
  14. package/lib/commonjs/TransitionConfigs/HeaderStyleInterpolators.js.map +1 -0
  15. package/lib/commonjs/TransitionConfigs/TransitionPresets.js +134 -0
  16. package/lib/commonjs/TransitionConfigs/TransitionPresets.js.map +1 -0
  17. package/lib/commonjs/TransitionConfigs/TransitionSpecs.js +100 -0
  18. package/lib/commonjs/TransitionConfigs/TransitionSpecs.js.map +1 -0
  19. package/lib/commonjs/index.js +69 -0
  20. package/lib/commonjs/index.js.map +1 -0
  21. package/lib/commonjs/navigators/createStackNavigator.js +82 -0
  22. package/lib/commonjs/navigators/createStackNavigator.js.map +1 -0
  23. package/lib/commonjs/types.js +6 -0
  24. package/lib/commonjs/types.js.map +1 -0
  25. package/lib/commonjs/utils/CardAnimationContext.js +11 -0
  26. package/lib/commonjs/utils/CardAnimationContext.js.map +1 -0
  27. package/lib/commonjs/utils/GestureHandlerRefContext.js +11 -0
  28. package/lib/commonjs/utils/GestureHandlerRefContext.js.map +1 -0
  29. package/lib/commonjs/utils/ModalPresentationContext.js +12 -0
  30. package/lib/commonjs/utils/ModalPresentationContext.js.map +1 -0
  31. package/lib/commonjs/utils/conditional.js +32 -0
  32. package/lib/commonjs/utils/conditional.js.map +1 -0
  33. package/lib/commonjs/utils/debounce.js +19 -0
  34. package/lib/commonjs/utils/debounce.js.map +1 -0
  35. package/lib/commonjs/utils/findLastIndex.js +15 -0
  36. package/lib/commonjs/utils/findLastIndex.js.map +1 -0
  37. package/lib/commonjs/utils/getDistanceForDirection.js +20 -0
  38. package/lib/commonjs/utils/getDistanceForDirection.js.map +1 -0
  39. package/lib/commonjs/utils/getInvertedMultiplier.js +20 -0
  40. package/lib/commonjs/utils/getInvertedMultiplier.js.map +1 -0
  41. package/lib/commonjs/utils/memoize.js +33 -0
  42. package/lib/commonjs/utils/memoize.js.map +1 -0
  43. package/lib/commonjs/utils/useCardAnimation.js +19 -0
  44. package/lib/commonjs/utils/useCardAnimation.js.map +1 -0
  45. package/lib/commonjs/utils/useGestureHandlerRef.js +19 -0
  46. package/lib/commonjs/utils/useGestureHandlerRef.js.map +1 -0
  47. package/lib/commonjs/utils/useKeyboardManager.js +96 -0
  48. package/lib/commonjs/utils/useKeyboardManager.js.map +1 -0
  49. package/lib/commonjs/views/GestureHandler.android.js +17 -0
  50. package/lib/commonjs/views/GestureHandler.android.js.map +1 -0
  51. package/lib/commonjs/views/GestureHandler.ios.js +17 -0
  52. package/lib/commonjs/views/GestureHandler.ios.js.map +1 -0
  53. package/lib/commonjs/views/GestureHandler.js +24 -0
  54. package/lib/commonjs/views/GestureHandler.js.map +1 -0
  55. package/lib/commonjs/views/GestureHandlerNative.js +34 -0
  56. package/lib/commonjs/views/GestureHandlerNative.js.map +1 -0
  57. package/lib/commonjs/views/Header/Header.js +61 -0
  58. package/lib/commonjs/views/Header/Header.js.map +1 -0
  59. package/lib/commonjs/views/Header/HeaderContainer.js +127 -0
  60. package/lib/commonjs/views/Header/HeaderContainer.js.map +1 -0
  61. package/lib/commonjs/views/Header/HeaderSegment.js +139 -0
  62. package/lib/commonjs/views/Header/HeaderSegment.js.map +1 -0
  63. package/lib/commonjs/views/ModalStatusBarManager.js +42 -0
  64. package/lib/commonjs/views/ModalStatusBarManager.js.map +1 -0
  65. package/lib/commonjs/views/Screens.js +44 -0
  66. package/lib/commonjs/views/Screens.js.map +1 -0
  67. package/lib/commonjs/views/Stack/Card.js +469 -0
  68. package/lib/commonjs/views/Stack/Card.js.map +1 -0
  69. package/lib/commonjs/views/Stack/CardContainer.js +259 -0
  70. package/lib/commonjs/views/Stack/CardContainer.js.map +1 -0
  71. package/lib/commonjs/views/Stack/CardSheet.js +54 -0
  72. package/lib/commonjs/views/Stack/CardSheet.js.map +1 -0
  73. package/lib/commonjs/views/Stack/CardStack.js +442 -0
  74. package/lib/commonjs/views/Stack/CardStack.js.map +1 -0
  75. package/lib/commonjs/views/Stack/StackView.js +333 -0
  76. package/lib/commonjs/views/Stack/StackView.js.map +1 -0
  77. package/lib/module/TransitionConfigs/CardStyleInterpolators.js +338 -0
  78. package/lib/module/TransitionConfigs/CardStyleInterpolators.js.map +1 -0
  79. package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js +248 -0
  80. package/lib/module/TransitionConfigs/HeaderStyleInterpolators.js.map +1 -0
  81. package/lib/module/TransitionConfigs/TransitionPresets.js +128 -0
  82. package/lib/module/TransitionConfigs/TransitionPresets.js.map +1 -0
  83. package/lib/module/TransitionConfigs/TransitionSpecs.js +94 -0
  84. package/lib/module/TransitionConfigs/TransitionSpecs.js.map +1 -0
  85. package/lib/module/index.js +33 -0
  86. package/lib/module/index.js.map +1 -0
  87. package/lib/module/navigators/createStackNavigator.js +73 -0
  88. package/lib/module/navigators/createStackNavigator.js.map +1 -0
  89. package/lib/module/types.js +2 -0
  90. package/lib/module/types.js.map +1 -0
  91. package/lib/module/utils/CardAnimationContext.js +3 -0
  92. package/lib/module/utils/CardAnimationContext.js.map +1 -0
  93. package/lib/module/utils/GestureHandlerRefContext.js +3 -0
  94. package/lib/module/utils/GestureHandlerRefContext.js.map +1 -0
  95. package/lib/module/utils/ModalPresentationContext.js +4 -0
  96. package/lib/module/utils/ModalPresentationContext.js.map +1 -0
  97. package/lib/module/utils/conditional.js +26 -0
  98. package/lib/module/utils/conditional.js.map +1 -0
  99. package/lib/module/utils/debounce.js +13 -0
  100. package/lib/module/utils/debounce.js.map +1 -0
  101. package/lib/module/utils/findLastIndex.js +9 -0
  102. package/lib/module/utils/findLastIndex.js.map +1 -0
  103. package/lib/module/utils/getDistanceForDirection.js +13 -0
  104. package/lib/module/utils/getDistanceForDirection.js.map +1 -0
  105. package/lib/module/utils/getInvertedMultiplier.js +14 -0
  106. package/lib/module/utils/getInvertedMultiplier.js.map +1 -0
  107. package/lib/module/utils/memoize.js +27 -0
  108. package/lib/module/utils/memoize.js.map +1 -0
  109. package/lib/module/utils/useCardAnimation.js +10 -0
  110. package/lib/module/utils/useCardAnimation.js.map +1 -0
  111. package/lib/module/utils/useGestureHandlerRef.js +10 -0
  112. package/lib/module/utils/useGestureHandlerRef.js.map +1 -0
  113. package/lib/module/utils/useKeyboardManager.js +88 -0
  114. package/lib/module/utils/useKeyboardManager.js.map +1 -0
  115. package/lib/module/views/GestureHandler.android.js +2 -0
  116. package/lib/module/views/GestureHandler.android.js.map +1 -0
  117. package/lib/module/views/GestureHandler.ios.js +2 -0
  118. package/lib/module/views/GestureHandler.ios.js.map +1 -0
  119. package/lib/module/views/GestureHandler.js +16 -0
  120. package/lib/module/views/GestureHandler.js.map +1 -0
  121. package/lib/module/views/GestureHandlerNative.js +14 -0
  122. package/lib/module/views/GestureHandlerNative.js.map +1 -0
  123. package/lib/module/views/Header/Header.js +52 -0
  124. package/lib/module/views/Header/Header.js.map +1 -0
  125. package/lib/module/views/Header/HeaderContainer.js +118 -0
  126. package/lib/module/views/Header/HeaderContainer.js.map +1 -0
  127. package/lib/module/views/Header/HeaderSegment.js +130 -0
  128. package/lib/module/views/Header/HeaderSegment.js.map +1 -0
  129. package/lib/module/views/ModalStatusBarManager.js +34 -0
  130. package/lib/module/views/ModalStatusBarManager.js.map +1 -0
  131. package/lib/module/views/Screens.js +34 -0
  132. package/lib/module/views/Screens.js.map +1 -0
  133. package/lib/module/views/Stack/Card.js +458 -0
  134. package/lib/module/views/Stack/Card.js.map +1 -0
  135. package/lib/module/views/Stack/CardContainer.js +250 -0
  136. package/lib/module/views/Stack/CardContainer.js.map +1 -0
  137. package/lib/module/views/Stack/CardSheet.js +46 -0
  138. package/lib/module/views/Stack/CardSheet.js.map +1 -0
  139. package/lib/module/views/Stack/CardStack.js +432 -0
  140. package/lib/module/views/Stack/CardStack.js.map +1 -0
  141. package/lib/module/views/Stack/StackView.js +323 -0
  142. package/lib/module/views/Stack/StackView.js.map +1 -0
  143. package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts +35 -0
  144. package/lib/typescript/src/TransitionConfigs/CardStyleInterpolators.d.ts.map +1 -0
  145. package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts +23 -0
  146. package/lib/typescript/src/TransitionConfigs/HeaderStyleInterpolators.d.ts.map +1 -0
  147. package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +42 -0
  148. package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -0
  149. package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +36 -0
  150. package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts.map +1 -0
  151. package/lib/typescript/src/index.d.ts +29 -0
  152. package/lib/typescript/src/index.d.ts.map +1 -0
  153. package/lib/typescript/src/navigators/createStackNavigator.d.ts +8 -0
  154. package/lib/typescript/src/navigators/createStackNavigator.d.ts.map +1 -0
  155. package/lib/typescript/src/types.d.ts +481 -0
  156. package/lib/typescript/src/types.d.ts.map +1 -0
  157. package/lib/typescript/src/utils/CardAnimationContext.d.ts +5 -0
  158. package/lib/typescript/src/utils/CardAnimationContext.d.ts.map +1 -0
  159. package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts +4 -0
  160. package/lib/typescript/src/utils/GestureHandlerRefContext.d.ts.map +1 -0
  161. package/lib/typescript/src/utils/ModalPresentationContext.d.ts +4 -0
  162. package/lib/typescript/src/utils/ModalPresentationContext.d.ts.map +1 -0
  163. package/lib/typescript/src/utils/conditional.d.ts +10 -0
  164. package/lib/typescript/src/utils/conditional.d.ts.map +1 -0
  165. package/lib/typescript/src/utils/debounce.d.ts +2 -0
  166. package/lib/typescript/src/utils/debounce.d.ts.map +1 -0
  167. package/lib/typescript/src/utils/findLastIndex.d.ts +2 -0
  168. package/lib/typescript/src/utils/findLastIndex.d.ts.map +1 -0
  169. package/lib/typescript/src/utils/getDistanceForDirection.d.ts +3 -0
  170. package/lib/typescript/src/utils/getDistanceForDirection.d.ts.map +1 -0
  171. package/lib/typescript/src/utils/getInvertedMultiplier.d.ts +3 -0
  172. package/lib/typescript/src/utils/getInvertedMultiplier.d.ts.map +1 -0
  173. package/lib/typescript/src/utils/memoize.d.ts +2 -0
  174. package/lib/typescript/src/utils/memoize.d.ts.map +1 -0
  175. package/lib/typescript/src/utils/useCardAnimation.d.ts +2 -0
  176. package/lib/typescript/src/utils/useCardAnimation.d.ts.map +1 -0
  177. package/lib/typescript/src/utils/useGestureHandlerRef.d.ts +3 -0
  178. package/lib/typescript/src/utils/useGestureHandlerRef.d.ts.map +1 -0
  179. package/lib/typescript/src/utils/useKeyboardManager.d.ts +6 -0
  180. package/lib/typescript/src/utils/useKeyboardManager.d.ts.map +1 -0
  181. package/lib/typescript/src/views/GestureHandler.android.d.ts +2 -0
  182. package/lib/typescript/src/views/GestureHandler.android.d.ts.map +1 -0
  183. package/lib/typescript/src/views/GestureHandler.d.ts +14 -0
  184. package/lib/typescript/src/views/GestureHandler.d.ts.map +1 -0
  185. package/lib/typescript/src/views/GestureHandler.ios.d.ts +2 -0
  186. package/lib/typescript/src/views/GestureHandler.ios.d.ts.map +1 -0
  187. package/lib/typescript/src/views/GestureHandlerNative.d.ts +6 -0
  188. package/lib/typescript/src/views/GestureHandlerNative.d.ts.map +1 -0
  189. package/lib/typescript/src/views/Header/Header.d.ts +5 -0
  190. package/lib/typescript/src/views/Header/Header.d.ts.map +1 -0
  191. package/lib/typescript/src/views/Header/HeaderContainer.d.ts +20 -0
  192. package/lib/typescript/src/views/Header/HeaderContainer.d.ts.map +1 -0
  193. package/lib/typescript/src/views/Header/HeaderSegment.d.ts +14 -0
  194. package/lib/typescript/src/views/Header/HeaderSegment.d.ts.map +1 -0
  195. package/lib/typescript/src/views/ModalStatusBarManager.d.ts +12 -0
  196. package/lib/typescript/src/views/ModalStatusBarManager.d.ts.map +1 -0
  197. package/lib/typescript/src/views/Screens.d.ts +13 -0
  198. package/lib/typescript/src/views/Screens.d.ts.map +1 -0
  199. package/lib/typescript/src/views/Stack/Card.d.ts +76 -0
  200. package/lib/typescript/src/views/Stack/Card.d.ts.map +1 -0
  201. package/lib/typescript/src/views/Stack/CardContainer.d.ts +63 -0
  202. package/lib/typescript/src/views/Stack/CardContainer.d.ts.map +1 -0
  203. package/lib/typescript/src/views/Stack/CardSheet.d.ts +15 -0
  204. package/lib/typescript/src/views/Stack/CardSheet.d.ts.map +1 -0
  205. package/lib/typescript/src/views/Stack/CardStack.d.ts +67 -0
  206. package/lib/typescript/src/views/Stack/CardStack.d.ts.map +1 -0
  207. package/lib/typescript/src/views/Stack/StackView.d.ts +122 -0
  208. package/lib/typescript/src/views/Stack/StackView.d.ts.map +1 -0
  209. package/package.json +1 -0
  210. package/src/TransitionConfigs/CardStyleInterpolators.tsx +394 -0
  211. package/src/TransitionConfigs/HeaderStyleInterpolators.tsx +268 -0
  212. package/src/TransitionConfigs/TransitionPresets.tsx +152 -0
  213. package/src/TransitionConfigs/TransitionSpecs.tsx +96 -0
  214. package/src/index.tsx +51 -0
  215. package/src/navigators/createStackNavigator.tsx +136 -0
  216. package/src/types.tsx +547 -0
  217. package/src/utils/CardAnimationContext.tsx +7 -0
  218. package/src/utils/GestureHandlerRefContext.tsx +5 -0
  219. package/src/utils/ModalPresentationContext.tsx +5 -0
  220. package/src/utils/conditional.tsx +33 -0
  221. package/src/utils/debounce.tsx +17 -0
  222. package/src/utils/findLastIndex.tsx +12 -0
  223. package/src/utils/getDistanceForDirection.tsx +18 -0
  224. package/src/utils/getInvertedMultiplier.tsx +18 -0
  225. package/src/utils/memoize.tsx +33 -0
  226. package/src/utils/useCardAnimation.tsx +15 -0
  227. package/src/utils/useGestureHandlerRef.tsx +15 -0
  228. package/src/utils/useKeyboardManager.tsx +105 -0
  229. package/src/views/GestureHandler.android.tsx +1 -0
  230. package/src/views/GestureHandler.ios.tsx +1 -0
  231. package/src/views/GestureHandler.tsx +23 -0
  232. package/src/views/GestureHandlerNative.tsx +23 -0
  233. package/src/views/Header/Header.tsx +72 -0
  234. package/src/views/Header/HeaderContainer.tsx +186 -0
  235. package/src/views/Header/HeaderSegment.tsx +205 -0
  236. package/src/views/ModalStatusBarManager.tsx +50 -0
  237. package/src/views/Screens.tsx +43 -0
  238. package/src/views/Stack/Card.tsx +628 -0
  239. package/src/views/Stack/CardContainer.tsx +320 -0
  240. package/src/views/Stack/CardSheet.tsx +61 -0
  241. package/src/views/Stack/CardStack.tsx +717 -0
  242. package/src/views/Stack/StackView.tsx +474 -0
@@ -0,0 +1,474 @@
1
+ import {
2
+ HeaderShownContext,
3
+ SafeAreaProviderCompat,
4
+ } from '@amazon-devices/react-navigation__elements';
5
+ import {
6
+ ParamListBase,
7
+ Route,
8
+ StackActions,
9
+ StackNavigationState,
10
+ } from '@amazon-devices/react-navigation__native';
11
+ import * as React from 'react';
12
+ import { StyleSheet, View } from 'react-native';
13
+ import {
14
+ EdgeInsets,
15
+ SafeAreaInsetsContext,
16
+ } from '@amazon-devices/react-native-safe-area-context';
17
+
18
+ import type {
19
+ StackDescriptorMap,
20
+ StackNavigationConfig,
21
+ StackNavigationHelpers,
22
+ } from '../../types';
23
+ import ModalPresentationContext from '../../utils/ModalPresentationContext';
24
+ import { GestureHandlerRootView } from '../GestureHandler';
25
+ import HeaderContainer, {
26
+ Props as HeaderContainerProps,
27
+ } from '../Header/HeaderContainer';
28
+ import CardStack from './CardStack';
29
+
30
+ type Props = StackNavigationConfig & {
31
+ state: StackNavigationState<ParamListBase>;
32
+ navigation: StackNavigationHelpers;
33
+ descriptors: StackDescriptorMap;
34
+ };
35
+
36
+ type State = {
37
+ // Local copy of the routes which are actually rendered
38
+ routes: Route<string>[];
39
+ // Previous routes, to compare whether routes have changed or not
40
+ previousRoutes: Route<string>[];
41
+ // Previous descriptors, to compare whether descriptors have changed or not
42
+ previousDescriptors: StackDescriptorMap;
43
+ // List of routes being opened, we need to animate pushing of these new routes
44
+ openingRouteKeys: string[];
45
+ // List of routes being closed, we need to animate popping of these routes
46
+ closingRouteKeys: string[];
47
+ // List of routes being replaced, we need to keep a copy until the new route animates in
48
+ replacingRouteKeys: string[];
49
+ // Since the local routes can vary from the routes from props, we need to keep the descriptors for old routes
50
+ // Otherwise we won't be able to access the options for routes that were removed
51
+ descriptors: StackDescriptorMap;
52
+ };
53
+
54
+ const GestureHandlerWrapper = GestureHandlerRootView ?? View;
55
+
56
+ /**
57
+ * Compare two arrays with primitive values as the content.
58
+ * We need to make sure that both values and order match.
59
+ */
60
+ const isArrayEqual = (a: any[], b: any[]) =>
61
+ a.length === b.length && a.every((it, index) => it === b[index]);
62
+
63
+ export default class StackView extends React.Component<Props, State> {
64
+ static getDerivedStateFromProps(
65
+ props: Readonly<Props>,
66
+ state: Readonly<State>
67
+ ) {
68
+ // If there was no change in routes, we don't need to compute anything
69
+ if (
70
+ (props.state.routes === state.previousRoutes ||
71
+ isArrayEqual(
72
+ props.state.routes.map((r) => r.key),
73
+ state.previousRoutes.map((r) => r.key)
74
+ )) &&
75
+ state.routes.length
76
+ ) {
77
+ let routes = state.routes;
78
+ let previousRoutes = state.previousRoutes;
79
+ let descriptors = props.descriptors;
80
+ let previousDescriptors = state.previousDescriptors;
81
+
82
+ if (props.descriptors !== state.previousDescriptors) {
83
+ descriptors = state.routes.reduce<StackDescriptorMap>((acc, route) => {
84
+ acc[route.key] =
85
+ props.descriptors[route.key] || state.descriptors[route.key];
86
+
87
+ return acc;
88
+ }, {});
89
+
90
+ previousDescriptors = props.descriptors;
91
+ }
92
+
93
+ if (props.state.routes !== state.previousRoutes) {
94
+ // if any route objects have changed, we should update them
95
+ const map = props.state.routes.reduce<Record<string, Route<string>>>(
96
+ (acc, route) => {
97
+ acc[route.key] = route;
98
+ return acc;
99
+ },
100
+ {}
101
+ );
102
+
103
+ routes = state.routes.map((route) => map[route.key] || route);
104
+ previousRoutes = props.state.routes;
105
+ }
106
+
107
+ return {
108
+ routes,
109
+ previousRoutes,
110
+ descriptors,
111
+ previousDescriptors,
112
+ };
113
+ }
114
+
115
+ // Here we determine which routes were added or removed to animate them
116
+ // We keep a copy of the route being removed in local state to be able to animate it
117
+
118
+ let routes =
119
+ props.state.index < props.state.routes.length - 1
120
+ ? // Remove any extra routes from the state
121
+ // The last visible route should be the focused route, i.e. at current index
122
+ props.state.routes.slice(0, props.state.index + 1)
123
+ : props.state.routes;
124
+
125
+ // Now we need to determine which routes were added and removed
126
+ let {
127
+ openingRouteKeys,
128
+ closingRouteKeys,
129
+ replacingRouteKeys,
130
+ previousRoutes,
131
+ } = state;
132
+
133
+ const previousFocusedRoute = previousRoutes[previousRoutes.length - 1] as
134
+ | Route<string>
135
+ | undefined;
136
+ const nextFocusedRoute = routes[routes.length - 1];
137
+
138
+ const isAnimationEnabled = (key: string) => {
139
+ const descriptor = props.descriptors[key] || state.descriptors[key];
140
+
141
+ return descriptor ? descriptor.options.animationEnabled !== false : true;
142
+ };
143
+
144
+ const getAnimationTypeForReplace = (key: string) => {
145
+ const descriptor = props.descriptors[key] || state.descriptors[key];
146
+
147
+ return descriptor.options.animationTypeForReplace ?? 'push';
148
+ };
149
+
150
+ if (
151
+ previousFocusedRoute &&
152
+ previousFocusedRoute.key !== nextFocusedRoute.key
153
+ ) {
154
+ // We only need to animate routes if the focused route changed
155
+ // Animating previous routes won't be visible coz the focused route is on top of everything
156
+
157
+ if (!previousRoutes.some((r) => r.key === nextFocusedRoute.key)) {
158
+ // A new route has come to the focus, we treat this as a push
159
+ // A replace can also trigger this, the animation should look like push
160
+
161
+ if (
162
+ isAnimationEnabled(nextFocusedRoute.key) &&
163
+ !openingRouteKeys.includes(nextFocusedRoute.key)
164
+ ) {
165
+ // In this case, we need to animate pushing the focused route
166
+ // We don't care about animating any other added routes because they won't be visible
167
+ openingRouteKeys = [...openingRouteKeys, nextFocusedRoute.key];
168
+
169
+ closingRouteKeys = closingRouteKeys.filter(
170
+ (key) => key !== nextFocusedRoute.key
171
+ );
172
+ replacingRouteKeys = replacingRouteKeys.filter(
173
+ (key) => key !== nextFocusedRoute.key
174
+ );
175
+
176
+ if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
177
+ // The previous focused route isn't present in state, we treat this as a replace
178
+
179
+ openingRouteKeys = openingRouteKeys.filter(
180
+ (key) => key !== previousFocusedRoute.key
181
+ );
182
+
183
+ if (getAnimationTypeForReplace(nextFocusedRoute.key) === 'pop') {
184
+ closingRouteKeys = [
185
+ ...closingRouteKeys,
186
+ previousFocusedRoute.key,
187
+ ];
188
+
189
+ // By default, new routes have a push animation, so we add it to `openingRouteKeys` before
190
+ // But since user configured it to animate the old screen like a pop, we need to add this without animation
191
+ // So remove it from `openingRouteKeys` which will remove the animation
192
+ openingRouteKeys = openingRouteKeys.filter(
193
+ (key) => key !== nextFocusedRoute.key
194
+ );
195
+
196
+ // Keep the route being removed at the end to animate it out
197
+ routes = [...routes, previousFocusedRoute];
198
+ } else {
199
+ replacingRouteKeys = [
200
+ ...replacingRouteKeys,
201
+ previousFocusedRoute.key,
202
+ ];
203
+
204
+ closingRouteKeys = closingRouteKeys.filter(
205
+ (key) => key !== previousFocusedRoute.key
206
+ );
207
+
208
+ // Keep the old route in the state because it's visible under the new route, and removing it will feel abrupt
209
+ // We need to insert it just before the focused one (the route being pushed)
210
+ // After the push animation is completed, routes being replaced will be removed completely
211
+ routes = routes.slice();
212
+ routes.splice(routes.length - 1, 0, previousFocusedRoute);
213
+ }
214
+ }
215
+ }
216
+ } else if (!routes.some((r) => r.key === previousFocusedRoute.key)) {
217
+ // The previously focused route was removed, we treat this as a pop
218
+
219
+ if (
220
+ isAnimationEnabled(previousFocusedRoute.key) &&
221
+ !closingRouteKeys.includes(previousFocusedRoute.key)
222
+ ) {
223
+ closingRouteKeys = [...closingRouteKeys, previousFocusedRoute.key];
224
+
225
+ // Sometimes a route can be closed before the opening animation finishes
226
+ // So we also need to remove it from the opening list
227
+ openingRouteKeys = openingRouteKeys.filter(
228
+ (key) => key !== previousFocusedRoute.key
229
+ );
230
+ replacingRouteKeys = replacingRouteKeys.filter(
231
+ (key) => key !== previousFocusedRoute.key
232
+ );
233
+
234
+ // Keep a copy of route being removed in the state to be able to animate it
235
+ routes = [...routes, previousFocusedRoute];
236
+ }
237
+ } else {
238
+ // Looks like some routes were re-arranged and no focused routes were added/removed
239
+ // i.e. the currently focused route already existed and the previously focused route still exists
240
+ // We don't know how to animate this
241
+ }
242
+ } else if (replacingRouteKeys.length || closingRouteKeys.length) {
243
+ // Keep the routes we are closing or replacing if animation is enabled for them
244
+ routes = routes.slice();
245
+ routes.splice(
246
+ routes.length - 1,
247
+ 0,
248
+ ...state.routes.filter(({ key }) =>
249
+ isAnimationEnabled(key)
250
+ ? replacingRouteKeys.includes(key) || closingRouteKeys.includes(key)
251
+ : false
252
+ )
253
+ );
254
+ }
255
+
256
+ if (!routes.length) {
257
+ throw new Error(
258
+ 'There should always be at least one route in the navigation state.'
259
+ );
260
+ }
261
+
262
+ const descriptors = routes.reduce<StackDescriptorMap>((acc, route) => {
263
+ acc[route.key] =
264
+ props.descriptors[route.key] || state.descriptors[route.key];
265
+
266
+ return acc;
267
+ }, {});
268
+
269
+ return {
270
+ routes,
271
+ previousRoutes: props.state.routes,
272
+ previousDescriptors: props.descriptors,
273
+ openingRouteKeys,
274
+ closingRouteKeys,
275
+ replacingRouteKeys,
276
+ descriptors,
277
+ };
278
+ }
279
+
280
+ state: State = {
281
+ routes: [],
282
+ previousRoutes: [],
283
+ previousDescriptors: {},
284
+ openingRouteKeys: [],
285
+ closingRouteKeys: [],
286
+ replacingRouteKeys: [],
287
+ descriptors: {},
288
+ };
289
+
290
+ private getPreviousRoute = ({ route }: { route: Route<string> }) => {
291
+ const { closingRouteKeys, replacingRouteKeys } = this.state;
292
+ const routes = this.state.routes.filter(
293
+ (r) =>
294
+ r.key === route.key ||
295
+ (!closingRouteKeys.includes(r.key) &&
296
+ !replacingRouteKeys.includes(r.key))
297
+ );
298
+
299
+ const index = routes.findIndex((r) => r.key === route.key);
300
+
301
+ return routes[index - 1];
302
+ };
303
+
304
+ private renderScene = ({ route }: { route: Route<string> }) => {
305
+ const descriptor =
306
+ this.state.descriptors[route.key] || this.props.descriptors[route.key];
307
+
308
+ if (!descriptor) {
309
+ return null;
310
+ }
311
+
312
+ return descriptor.render();
313
+ };
314
+
315
+ private renderHeader = (props: HeaderContainerProps) => {
316
+ return <HeaderContainer {...props} />;
317
+ };
318
+
319
+ private handleOpenRoute = ({ route }: { route: Route<string> }) => {
320
+ const { state, navigation } = this.props;
321
+ const { closingRouteKeys, replacingRouteKeys } = this.state;
322
+
323
+ if (
324
+ closingRouteKeys.some((key) => key === route.key) &&
325
+ replacingRouteKeys.every((key) => key !== route.key) &&
326
+ state.routeNames.includes(route.name) &&
327
+ !state.routes.some((r) => r.key === route.key)
328
+ ) {
329
+ // If route isn't present in current state, but was closing, assume that a close animation was cancelled
330
+ // So we need to add this route back to the state
331
+ navigation.navigate(route);
332
+ } else {
333
+ this.setState((state) => ({
334
+ routes: state.replacingRouteKeys.length
335
+ ? state.routes.filter(
336
+ (r) => !state.replacingRouteKeys.includes(r.key)
337
+ )
338
+ : state.routes,
339
+ openingRouteKeys: state.openingRouteKeys.filter(
340
+ (key) => key !== route.key
341
+ ),
342
+ closingRouteKeys: state.closingRouteKeys.filter(
343
+ (key) => key !== route.key
344
+ ),
345
+ replacingRouteKeys: [],
346
+ }));
347
+ }
348
+ };
349
+
350
+ private handleCloseRoute = ({ route }: { route: Route<string> }) => {
351
+ const { state, navigation } = this.props;
352
+
353
+ if (state.routes.some((r) => r.key === route.key)) {
354
+ // If a route exists in state, trigger a pop
355
+ // This will happen in when the route was closed from the card component
356
+ // e.g. When the close animation triggered from a gesture ends
357
+ navigation.dispatch({
358
+ ...StackActions.pop(),
359
+ source: route.key,
360
+ target: state.key,
361
+ });
362
+ } else {
363
+ // We need to clean up any state tracking the route and pop it immediately
364
+ this.setState((state) => ({
365
+ routes: state.routes.filter((r) => r.key !== route.key),
366
+ openingRouteKeys: state.openingRouteKeys.filter(
367
+ (key) => key !== route.key
368
+ ),
369
+ closingRouteKeys: state.closingRouteKeys.filter(
370
+ (key) => key !== route.key
371
+ ),
372
+ }));
373
+ }
374
+ };
375
+
376
+ private handleTransitionStart = (
377
+ { route }: { route: Route<string> },
378
+ closing: boolean
379
+ ) =>
380
+ this.props.navigation.emit({
381
+ type: 'transitionStart',
382
+ data: { closing },
383
+ target: route.key,
384
+ });
385
+
386
+ private handleTransitionEnd = (
387
+ { route }: { route: Route<string> },
388
+ closing: boolean
389
+ ) =>
390
+ this.props.navigation.emit({
391
+ type: 'transitionEnd',
392
+ data: { closing },
393
+ target: route.key,
394
+ });
395
+
396
+ private handleGestureStart = ({ route }: { route: Route<string> }) => {
397
+ this.props.navigation.emit({
398
+ type: 'gestureStart',
399
+ target: route.key,
400
+ });
401
+ };
402
+
403
+ private handleGestureEnd = ({ route }: { route: Route<string> }) => {
404
+ this.props.navigation.emit({
405
+ type: 'gestureEnd',
406
+ target: route.key,
407
+ });
408
+ };
409
+
410
+ private handleGestureCancel = ({ route }: { route: Route<string> }) => {
411
+ this.props.navigation.emit({
412
+ type: 'gestureCancel',
413
+ target: route.key,
414
+ });
415
+ };
416
+
417
+ render() {
418
+ const {
419
+ state,
420
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
421
+ descriptors: _,
422
+ ...rest
423
+ } = this.props;
424
+
425
+ const { routes, descriptors, openingRouteKeys, closingRouteKeys } =
426
+ this.state;
427
+
428
+ return (
429
+ <GestureHandlerWrapper style={styles.container}>
430
+ <SafeAreaProviderCompat>
431
+ <SafeAreaInsetsContext.Consumer>
432
+ {(insets) => (
433
+ <ModalPresentationContext.Consumer>
434
+ {(isParentModal) => (
435
+ <HeaderShownContext.Consumer>
436
+ {(isParentHeaderShown) => (
437
+ <CardStack
438
+ insets={insets as EdgeInsets}
439
+ isParentHeaderShown={isParentHeaderShown}
440
+ isParentModal={isParentModal}
441
+ getPreviousRoute={this.getPreviousRoute}
442
+ routes={routes}
443
+ openingRouteKeys={openingRouteKeys}
444
+ closingRouteKeys={closingRouteKeys}
445
+ onOpenRoute={this.handleOpenRoute}
446
+ onCloseRoute={this.handleCloseRoute}
447
+ onTransitionStart={this.handleTransitionStart}
448
+ onTransitionEnd={this.handleTransitionEnd}
449
+ renderHeader={this.renderHeader}
450
+ renderScene={this.renderScene}
451
+ state={state}
452
+ descriptors={descriptors}
453
+ onGestureStart={this.handleGestureStart}
454
+ onGestureEnd={this.handleGestureEnd}
455
+ onGestureCancel={this.handleGestureCancel}
456
+ {...rest}
457
+ />
458
+ )}
459
+ </HeaderShownContext.Consumer>
460
+ )}
461
+ </ModalPresentationContext.Consumer>
462
+ )}
463
+ </SafeAreaInsetsContext.Consumer>
464
+ </SafeAreaProviderCompat>
465
+ </GestureHandlerWrapper>
466
+ );
467
+ }
468
+ }
469
+
470
+ const styles = StyleSheet.create({
471
+ container: {
472
+ flex: 1,
473
+ },
474
+ });