@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,50 @@
1
+ import { useTheme } from '@amazon-devices/react-navigation__native';
2
+ import * as React from 'react';
3
+ import { StatusBar, StyleSheet } from 'react-native';
4
+ import type { EdgeInsets } from '@amazon-devices/react-native-safe-area-context';
5
+
6
+ import type { Layout } from '../types';
7
+
8
+ type Props = {
9
+ dark: boolean | undefined;
10
+ layout: Layout;
11
+ insets: EdgeInsets;
12
+ style: any;
13
+ };
14
+
15
+ export default function ModalStatusBarManager({
16
+ dark,
17
+ layout,
18
+ insets,
19
+ style,
20
+ }: Props) {
21
+ const { dark: darkTheme } = useTheme();
22
+ const [overlapping, setOverlapping] = React.useState(true);
23
+
24
+ const scale = 1 - 20 / layout.width;
25
+ const offset = (insets.top - 34) * scale;
26
+
27
+ const flattenedStyle = StyleSheet.flatten(style);
28
+ const translateY = flattenedStyle?.transform?.find(
29
+ (s: any) => s.translateY !== undefined
30
+ )?.translateY;
31
+
32
+ React.useEffect(() => {
33
+ const listener = ({ value }: { value: number }) => {
34
+ setOverlapping(value < offset);
35
+ };
36
+
37
+ const sub = translateY?.addListener(listener);
38
+
39
+ return () => translateY?.removeListener(sub);
40
+ }, [offset, translateY]);
41
+
42
+ const darkContent = dark ?? !darkTheme;
43
+
44
+ return (
45
+ <StatusBar
46
+ animated
47
+ barStyle={overlapping && darkContent ? 'dark-content' : 'light-content'}
48
+ />
49
+ );
50
+ }
@@ -0,0 +1,43 @@
1
+ import * as React from 'react';
2
+ import { View, ViewProps } from 'react-native';
3
+
4
+ let Screens: typeof import('@amazon-devices/react-native-screens') | undefined;
5
+
6
+ try {
7
+ Screens = require('@amazon-devices/react-native-screens');
8
+ } catch (e) {
9
+ // Ignore
10
+ }
11
+
12
+ export const MaybeScreenContainer = ({
13
+ enabled,
14
+ ...rest
15
+ }: ViewProps & {
16
+ enabled: boolean;
17
+ children: React.ReactNode;
18
+ }) => {
19
+ if (Screens != null) {
20
+ return <Screens.ScreenContainer enabled={enabled} {...rest} />;
21
+ }
22
+
23
+ return <View {...rest} />;
24
+ };
25
+
26
+ export const MaybeScreen = ({
27
+ enabled,
28
+ active,
29
+ ...rest
30
+ }: ViewProps & {
31
+ enabled: boolean;
32
+ active: 0 | 1;
33
+ children: React.ReactNode;
34
+ freezeOnBlur?: boolean;
35
+ }) => {
36
+ if (Screens != null) {
37
+ return (
38
+ <Screens.Screen enabled={enabled} activityState={active} {...rest} />
39
+ );
40
+ }
41
+
42
+ return <View {...rest} />;
43
+ };
@@ -0,0 +1,628 @@
1
+ import Color from 'color';
2
+ import * as React from 'react';
3
+ import {
4
+ Animated,
5
+ InteractionManager,
6
+ Platform,
7
+ StyleProp,
8
+ StyleSheet,
9
+ View,
10
+ ViewProps,
11
+ ViewStyle,
12
+ } from 'react-native';
13
+ import type { EdgeInsets } from '@amazon-devices/react-native-safe-area-context';
14
+
15
+ import { forModalPresentationIOS } from '../../TransitionConfigs/CardStyleInterpolators';
16
+ import type {
17
+ GestureDirection,
18
+ Layout,
19
+ StackCardInterpolationProps,
20
+ StackCardStyleInterpolator,
21
+ TransitionSpec,
22
+ } from '../../types';
23
+ import CardAnimationContext from '../../utils/CardAnimationContext';
24
+ import getDistanceForDirection from '../../utils/getDistanceForDirection';
25
+ import getInvertedMultiplier from '../../utils/getInvertedMultiplier';
26
+ import memoize from '../../utils/memoize';
27
+ import {
28
+ GestureState,
29
+ PanGestureHandler,
30
+ PanGestureHandlerGestureEvent,
31
+ } from '../GestureHandler';
32
+ import ModalStatusBarManager from '../ModalStatusBarManager';
33
+ import CardSheet, { CardSheetRef } from './CardSheet';
34
+
35
+ type Props = ViewProps & {
36
+ interpolationIndex: number;
37
+ closing: boolean;
38
+ next?: Animated.AnimatedInterpolation<number>;
39
+ current: Animated.AnimatedInterpolation<number>;
40
+ gesture: Animated.Value;
41
+ layout: Layout;
42
+ insets: EdgeInsets;
43
+ headerDarkContent: boolean | undefined;
44
+ pageOverflowEnabled: boolean;
45
+ gestureDirection: GestureDirection;
46
+ onOpen: () => void;
47
+ onClose: () => void;
48
+ onTransition: (props: { closing: boolean; gesture: boolean }) => void;
49
+ onGestureBegin: () => void;
50
+ onGestureCanceled: () => void;
51
+ onGestureEnd: () => void;
52
+ children: React.ReactNode;
53
+ overlay: (props: {
54
+ style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
55
+ }) => React.ReactNode;
56
+ overlayEnabled: boolean;
57
+ shadowEnabled: boolean;
58
+ gestureEnabled: boolean;
59
+ gestureResponseDistance?: number;
60
+ gestureVelocityImpact: number;
61
+ transitionSpec: {
62
+ open: TransitionSpec;
63
+ close: TransitionSpec;
64
+ };
65
+ styleInterpolator: StackCardStyleInterpolator;
66
+ containerStyle?: StyleProp<ViewStyle>;
67
+ contentStyle?: StyleProp<ViewStyle>;
68
+ };
69
+
70
+ const GESTURE_VELOCITY_IMPACT = 0.3;
71
+
72
+ const TRUE = 1;
73
+ const FALSE = 0;
74
+
75
+ /**
76
+ * The distance of touch start from the edge of the screen where the gesture will be recognized
77
+ */
78
+ const GESTURE_RESPONSE_DISTANCE_HORIZONTAL = 50;
79
+ const GESTURE_RESPONSE_DISTANCE_VERTICAL = 135;
80
+
81
+ const useNativeDriver = Platform.OS !== 'web';
82
+
83
+ const hasOpacityStyle = (style: any) => {
84
+ if (style) {
85
+ const flattenedStyle = StyleSheet.flatten(style);
86
+ return flattenedStyle.opacity != null;
87
+ }
88
+
89
+ return false;
90
+ };
91
+
92
+ export default class Card extends React.Component<Props> {
93
+ static defaultProps = {
94
+ shadowEnabled: false,
95
+ gestureEnabled: true,
96
+ gestureVelocityImpact: GESTURE_VELOCITY_IMPACT,
97
+ overlay: ({
98
+ style,
99
+ }: {
100
+ style: Animated.WithAnimatedValue<StyleProp<ViewStyle>>;
101
+ }) =>
102
+ style ? (
103
+ <Animated.View pointerEvents="none" style={[styles.overlay, style]} />
104
+ ) : null,
105
+ };
106
+
107
+ componentDidMount() {
108
+ this.animate({ closing: this.props.closing });
109
+ this.isCurrentlyMounted = true;
110
+ }
111
+
112
+ componentDidUpdate(prevProps: Props) {
113
+ const { layout, gestureDirection, closing } = this.props;
114
+ const { width, height } = layout;
115
+
116
+ if (width !== prevProps.layout.width) {
117
+ this.layout.width.setValue(width);
118
+ }
119
+
120
+ if (height !== prevProps.layout.height) {
121
+ this.layout.height.setValue(height);
122
+ }
123
+
124
+ if (gestureDirection !== prevProps.gestureDirection) {
125
+ this.inverted.setValue(getInvertedMultiplier(gestureDirection));
126
+ }
127
+
128
+ const toValue = this.getAnimateToValue(this.props);
129
+
130
+ if (
131
+ this.getAnimateToValue(prevProps) !== toValue ||
132
+ this.lastToValue !== toValue
133
+ ) {
134
+ // We need to trigger the animation when route was closed
135
+ // Thr route might have been closed by a `POP` action or by a gesture
136
+ // When route was closed due to a gesture, the animation would've happened already
137
+ // It's still important to trigger the animation so that `onClose` is called
138
+ // If `onClose` is not called, cleanup step won't be performed for gestures
139
+ this.animate({ closing });
140
+ }
141
+ }
142
+
143
+ componentWillUnmount() {
144
+ this.props.gesture.stopAnimation();
145
+ this.isCurrentlyMounted = false;
146
+ this.handleEndInteraction();
147
+ }
148
+
149
+ private isCurrentlyMounted = false;
150
+
151
+ private isClosing = new Animated.Value(FALSE);
152
+
153
+ private inverted = new Animated.Value(
154
+ getInvertedMultiplier(this.props.gestureDirection)
155
+ );
156
+
157
+ private layout = {
158
+ width: new Animated.Value(this.props.layout.width),
159
+ height: new Animated.Value(this.props.layout.height),
160
+ };
161
+
162
+ private isSwiping = new Animated.Value(FALSE);
163
+
164
+ private interactionHandle: number | undefined;
165
+
166
+ private pendingGestureCallback: number | undefined;
167
+
168
+ private lastToValue: number | undefined;
169
+
170
+ private animate = ({
171
+ closing,
172
+ velocity,
173
+ }: {
174
+ closing: boolean;
175
+ velocity?: number;
176
+ }) => {
177
+ const { gesture, transitionSpec, onOpen, onClose, onTransition } =
178
+ this.props;
179
+
180
+ const toValue = this.getAnimateToValue({
181
+ ...this.props,
182
+ closing,
183
+ });
184
+
185
+ this.lastToValue = toValue;
186
+
187
+ this.isClosing.setValue(closing ? TRUE : FALSE);
188
+
189
+ const spec = closing ? transitionSpec.close : transitionSpec.open;
190
+
191
+ const animation =
192
+ spec.animation === 'spring' ? Animated.spring : Animated.timing;
193
+
194
+ this.setPointerEventsEnabled(!closing);
195
+ this.handleStartInteraction();
196
+
197
+ clearTimeout(this.pendingGestureCallback);
198
+
199
+ onTransition?.({ closing, gesture: velocity !== undefined });
200
+ animation(gesture, {
201
+ ...spec.config,
202
+ velocity,
203
+ toValue,
204
+ useNativeDriver,
205
+ isInteraction: false,
206
+ }).start(({ finished }) => {
207
+ this.handleEndInteraction();
208
+
209
+ clearTimeout(this.pendingGestureCallback);
210
+
211
+ if (finished) {
212
+ if (closing) {
213
+ onClose();
214
+ } else {
215
+ onOpen();
216
+ }
217
+
218
+ if (this.isCurrentlyMounted) {
219
+ // Make sure to re-open screen if it wasn't removed
220
+ this.forceUpdate();
221
+ }
222
+ }
223
+ });
224
+ };
225
+
226
+ private getAnimateToValue = ({
227
+ closing,
228
+ layout,
229
+ gestureDirection,
230
+ }: {
231
+ closing?: boolean;
232
+ layout: Layout;
233
+ gestureDirection: GestureDirection;
234
+ }) => {
235
+ if (!closing) {
236
+ return 0;
237
+ }
238
+
239
+ return getDistanceForDirection(layout, gestureDirection);
240
+ };
241
+
242
+ private setPointerEventsEnabled = (enabled: boolean) => {
243
+ const pointerEvents = enabled ? 'box-none' : 'none';
244
+
245
+ this.ref.current?.setPointerEvents(pointerEvents);
246
+ };
247
+
248
+ private handleStartInteraction = () => {
249
+ if (this.interactionHandle === undefined) {
250
+ this.interactionHandle = InteractionManager.createInteractionHandle();
251
+ }
252
+ };
253
+
254
+ private handleEndInteraction = () => {
255
+ if (this.interactionHandle !== undefined) {
256
+ InteractionManager.clearInteractionHandle(this.interactionHandle);
257
+ this.interactionHandle = undefined;
258
+ }
259
+ };
260
+
261
+ private handleGestureStateChange = ({
262
+ nativeEvent,
263
+ }: PanGestureHandlerGestureEvent) => {
264
+ const {
265
+ layout,
266
+ onClose,
267
+ onGestureBegin,
268
+ onGestureCanceled,
269
+ onGestureEnd,
270
+ gestureDirection,
271
+ gestureVelocityImpact,
272
+ } = this.props;
273
+
274
+ switch (nativeEvent.state) {
275
+ case GestureState.ACTIVE:
276
+ this.isSwiping.setValue(TRUE);
277
+ this.handleStartInteraction();
278
+ onGestureBegin?.();
279
+ break;
280
+ case GestureState.CANCELLED: {
281
+ this.isSwiping.setValue(FALSE);
282
+ this.handleEndInteraction();
283
+
284
+ const velocity =
285
+ gestureDirection === 'vertical' ||
286
+ gestureDirection === 'vertical-inverted'
287
+ ? nativeEvent.velocityY
288
+ : nativeEvent.velocityX;
289
+
290
+ this.animate({ closing: this.props.closing, velocity });
291
+
292
+ onGestureCanceled?.();
293
+ break;
294
+ }
295
+ case GestureState.END: {
296
+ this.isSwiping.setValue(FALSE);
297
+
298
+ let distance;
299
+ let translation;
300
+ let velocity;
301
+
302
+ if (
303
+ gestureDirection === 'vertical' ||
304
+ gestureDirection === 'vertical-inverted'
305
+ ) {
306
+ distance = layout.height;
307
+ translation = nativeEvent.translationY;
308
+ velocity = nativeEvent.velocityY;
309
+ } else {
310
+ distance = layout.width;
311
+ translation = nativeEvent.translationX;
312
+ velocity = nativeEvent.velocityX;
313
+ }
314
+
315
+ const closing =
316
+ (translation + velocity * gestureVelocityImpact) *
317
+ getInvertedMultiplier(gestureDirection) >
318
+ distance / 2
319
+ ? velocity !== 0 || translation !== 0
320
+ : this.props.closing;
321
+
322
+ this.animate({ closing, velocity });
323
+
324
+ if (closing) {
325
+ // We call onClose with a delay to make sure that the animation has already started
326
+ // This will make sure that the state update caused by this doesn't affect start of animation
327
+ this.pendingGestureCallback = setTimeout(() => {
328
+ onClose();
329
+
330
+ // Trigger an update after we dispatch the action to remove the screen
331
+ // This will make sure that we check if the screen didn't get removed so we can cancel the animation
332
+ this.forceUpdate();
333
+ }, 32) as any as number;
334
+ }
335
+
336
+ onGestureEnd?.();
337
+ break;
338
+ }
339
+ }
340
+ };
341
+
342
+ // Memoize this to avoid extra work on re-render
343
+ private getInterpolatedStyle = memoize(
344
+ (
345
+ styleInterpolator: StackCardStyleInterpolator,
346
+ animation: StackCardInterpolationProps
347
+ ) => styleInterpolator(animation)
348
+ );
349
+
350
+ // Keep track of the animation context when deps changes.
351
+ private getCardAnimation = memoize(
352
+ (
353
+ interpolationIndex: number,
354
+ current: Animated.AnimatedInterpolation<number>,
355
+ next: Animated.AnimatedInterpolation<number> | undefined,
356
+ layout: Layout,
357
+ insetTop: number,
358
+ insetRight: number,
359
+ insetBottom: number,
360
+ insetLeft: number
361
+ ) => ({
362
+ index: interpolationIndex,
363
+ current: { progress: current },
364
+ next: next && { progress: next },
365
+ closing: this.isClosing,
366
+ swiping: this.isSwiping,
367
+ inverted: this.inverted,
368
+ layouts: {
369
+ screen: layout,
370
+ },
371
+ insets: {
372
+ top: insetTop,
373
+ right: insetRight,
374
+ bottom: insetBottom,
375
+ left: insetLeft,
376
+ },
377
+ })
378
+ );
379
+
380
+ private gestureActivationCriteria() {
381
+ const { layout, gestureDirection, gestureResponseDistance } = this.props;
382
+ const enableTrackpadTwoFingerGesture = true;
383
+
384
+ const distance =
385
+ gestureResponseDistance !== undefined
386
+ ? gestureResponseDistance
387
+ : gestureDirection === 'vertical' ||
388
+ gestureDirection === 'vertical-inverted'
389
+ ? GESTURE_RESPONSE_DISTANCE_VERTICAL
390
+ : GESTURE_RESPONSE_DISTANCE_HORIZONTAL;
391
+
392
+ if (gestureDirection === 'vertical') {
393
+ return {
394
+ maxDeltaX: 15,
395
+ minOffsetY: 5,
396
+ hitSlop: { bottom: -layout.height + distance },
397
+ enableTrackpadTwoFingerGesture,
398
+ };
399
+ } else if (gestureDirection === 'vertical-inverted') {
400
+ return {
401
+ maxDeltaX: 15,
402
+ minOffsetY: -5,
403
+ hitSlop: { top: -layout.height + distance },
404
+ enableTrackpadTwoFingerGesture,
405
+ };
406
+ } else {
407
+ const hitSlop = -layout.width + distance;
408
+ const invertedMultiplier = getInvertedMultiplier(gestureDirection);
409
+
410
+ if (invertedMultiplier === 1) {
411
+ return {
412
+ minOffsetX: 5,
413
+ maxDeltaY: 20,
414
+ hitSlop: { right: hitSlop },
415
+ enableTrackpadTwoFingerGesture,
416
+ };
417
+ } else {
418
+ return {
419
+ minOffsetX: -5,
420
+ maxDeltaY: 20,
421
+ hitSlop: { left: hitSlop },
422
+ enableTrackpadTwoFingerGesture,
423
+ };
424
+ }
425
+ }
426
+ }
427
+
428
+ private ref = React.createRef<CardSheetRef>();
429
+
430
+ render() {
431
+ const {
432
+ styleInterpolator,
433
+ interpolationIndex,
434
+ current,
435
+ gesture,
436
+ next,
437
+ layout,
438
+ insets,
439
+ overlay,
440
+ overlayEnabled,
441
+ shadowEnabled,
442
+ gestureEnabled,
443
+ gestureDirection,
444
+ pageOverflowEnabled,
445
+ headerDarkContent,
446
+ children,
447
+ containerStyle: customContainerStyle,
448
+ contentStyle,
449
+ ...rest
450
+ } = this.props;
451
+
452
+ const interpolationProps = this.getCardAnimation(
453
+ interpolationIndex,
454
+ current,
455
+ next,
456
+ layout,
457
+ insets.top,
458
+ insets.right,
459
+ insets.bottom,
460
+ insets.left
461
+ );
462
+
463
+ const interpolatedStyle = this.getInterpolatedStyle(
464
+ styleInterpolator,
465
+ interpolationProps
466
+ );
467
+
468
+ const { containerStyle, cardStyle, overlayStyle, shadowStyle } =
469
+ interpolatedStyle;
470
+
471
+ const handleGestureEvent = gestureEnabled
472
+ ? Animated.event(
473
+ [
474
+ {
475
+ nativeEvent:
476
+ gestureDirection === 'vertical' ||
477
+ gestureDirection === 'vertical-inverted'
478
+ ? { translationY: gesture }
479
+ : { translationX: gesture },
480
+ },
481
+ ],
482
+ { useNativeDriver }
483
+ )
484
+ : undefined;
485
+
486
+ const { backgroundColor } = StyleSheet.flatten(contentStyle || {});
487
+ const isTransparent =
488
+ typeof backgroundColor === 'string'
489
+ ? Color(backgroundColor).alpha() === 0
490
+ : false;
491
+
492
+ return (
493
+ <CardAnimationContext.Provider value={interpolationProps}>
494
+ {
495
+ // StatusBar messes with translucent status bar on Android
496
+ // So we should only enable it on iOS
497
+ Platform.OS === 'ios' &&
498
+ overlayEnabled &&
499
+ next &&
500
+ getIsModalPresentation(styleInterpolator) ? (
501
+ <ModalStatusBarManager
502
+ dark={headerDarkContent}
503
+ layout={layout}
504
+ insets={insets}
505
+ style={cardStyle}
506
+ />
507
+ ) : null
508
+ }
509
+ <Animated.View
510
+ style={{
511
+ // This is a dummy style that doesn't actually change anything visually.
512
+ // Animated needs the animated value to be used somewhere, otherwise things don't update properly.
513
+ // If we disable animations and hide header, it could end up making the value unused.
514
+ // So we have this dummy style that will always be used regardless of what else changed.
515
+ opacity: current,
516
+ }}
517
+ // Make sure that this view isn't removed. If this view is removed, our style with animated value won't apply
518
+ collapsable={false}
519
+ />
520
+ <View
521
+ pointerEvents="box-none"
522
+ // Make sure this view is not removed on the new architecture, as it causes focus loss during navigation on Android.
523
+ // This can happen when the view flattening results in different trees - due to `overflow` style changing in a parent.
524
+ collapsable={false}
525
+ {...rest}
526
+ >
527
+ {overlayEnabled ? (
528
+ <View pointerEvents="box-none" style={StyleSheet.absoluteFill}>
529
+ {overlay({ style: overlayStyle })}
530
+ </View>
531
+ ) : null}
532
+ <Animated.View
533
+ style={[styles.container, containerStyle, customContainerStyle]}
534
+ pointerEvents="box-none"
535
+ >
536
+ <PanGestureHandler
537
+ enabled={layout.width !== 0 && gestureEnabled}
538
+ onGestureEvent={handleGestureEvent}
539
+ onHandlerStateChange={this.handleGestureStateChange}
540
+ {...this.gestureActivationCriteria()}
541
+ >
542
+ <Animated.View
543
+ needsOffscreenAlphaCompositing={hasOpacityStyle(cardStyle)}
544
+ style={[styles.container, cardStyle]}
545
+ >
546
+ {shadowEnabled && shadowStyle && !isTransparent ? (
547
+ <Animated.View
548
+ style={[
549
+ styles.shadow,
550
+ gestureDirection === 'horizontal'
551
+ ? [styles.shadowHorizontal, styles.shadowLeft]
552
+ : gestureDirection === 'horizontal-inverted'
553
+ ? [styles.shadowHorizontal, styles.shadowRight]
554
+ : gestureDirection === 'vertical'
555
+ ? [styles.shadowVertical, styles.shadowTop]
556
+ : [styles.shadowVertical, styles.shadowBottom],
557
+ { backgroundColor },
558
+ shadowStyle,
559
+ ]}
560
+ pointerEvents="none"
561
+ />
562
+ ) : null}
563
+ <CardSheet
564
+ ref={this.ref}
565
+ enabled={pageOverflowEnabled}
566
+ layout={layout}
567
+ style={contentStyle}
568
+ >
569
+ {children}
570
+ </CardSheet>
571
+ </Animated.View>
572
+ </PanGestureHandler>
573
+ </Animated.View>
574
+ </View>
575
+ </CardAnimationContext.Provider>
576
+ );
577
+ }
578
+ }
579
+
580
+ export const getIsModalPresentation = (
581
+ cardStyleInterpolator: StackCardStyleInterpolator
582
+ ) => {
583
+ return (
584
+ cardStyleInterpolator === forModalPresentationIOS ||
585
+ // Handle custom modal presentation interpolators as well
586
+ cardStyleInterpolator.name === 'forModalPresentationIOS'
587
+ );
588
+ };
589
+
590
+ const styles = StyleSheet.create({
591
+ container: {
592
+ flex: 1,
593
+ },
594
+ overlay: {
595
+ flex: 1,
596
+ backgroundColor: '#000',
597
+ },
598
+ shadow: {
599
+ position: 'absolute',
600
+ shadowRadius: 5,
601
+ shadowColor: '#000',
602
+ shadowOpacity: 0.3,
603
+ },
604
+ shadowHorizontal: {
605
+ top: 0,
606
+ bottom: 0,
607
+ width: 3,
608
+ shadowOffset: { width: -1, height: 1 },
609
+ },
610
+ shadowLeft: {
611
+ left: 0,
612
+ },
613
+ shadowRight: {
614
+ right: 0,
615
+ },
616
+ shadowVertical: {
617
+ left: 0,
618
+ right: 0,
619
+ height: 3,
620
+ shadowOffset: { width: 1, height: -1 },
621
+ },
622
+ shadowTop: {
623
+ top: 0,
624
+ },
625
+ shadowBottom: {
626
+ bottom: 0,
627
+ },
628
+ });