react-native-screens 3.7.2 → 3.10.1

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 (83) hide show
  1. package/README.md +69 -3
  2. package/android/build.gradle +8 -7
  3. package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +71 -0
  4. package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +7 -0
  5. package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +29 -0
  6. package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -1
  7. package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +7 -41
  8. package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +55 -40
  9. package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +19 -1
  10. package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +30 -101
  11. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +76 -14
  12. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +13 -4
  13. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +8 -0
  14. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +7 -1
  15. package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +1 -0
  16. package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +90 -0
  17. package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +150 -0
  18. package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +40 -0
  19. package/ios/RNSScreen.h +1 -0
  20. package/ios/RNSScreen.m +35 -0
  21. package/ios/RNSScreenContainer.h +2 -0
  22. package/ios/RNSScreenStack.m +24 -6
  23. package/ios/RNSScreenStackHeaderConfig.m +45 -2
  24. package/ios/RNSScreenWindowTraits.h +5 -0
  25. package/ios/RNSScreenWindowTraits.m +29 -0
  26. package/lib/commonjs/index.js +24 -1
  27. package/lib/commonjs/index.js.map +1 -1
  28. package/lib/commonjs/index.native.js +103 -17
  29. package/lib/commonjs/index.native.js.map +1 -1
  30. package/lib/commonjs/native-stack/utils/useBackPressSubscription.js +67 -0
  31. package/lib/commonjs/native-stack/utils/useBackPressSubscription.js.map +1 -0
  32. package/lib/commonjs/native-stack/views/HeaderConfig.js +46 -4
  33. package/lib/commonjs/native-stack/views/HeaderConfig.js.map +1 -1
  34. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +60 -0
  35. package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
  36. package/lib/commonjs/reanimated/ReanimatedScreen.js +7 -79
  37. package/lib/commonjs/reanimated/ReanimatedScreen.js.map +1 -1
  38. package/lib/commonjs/reanimated/ReanimatedScreenProvider.js +61 -0
  39. package/lib/commonjs/reanimated/ReanimatedScreenProvider.js.map +1 -0
  40. package/lib/commonjs/reanimated/index.js +2 -2
  41. package/lib/commonjs/reanimated/index.js.map +1 -1
  42. package/lib/commonjs/utils.js +20 -0
  43. package/lib/commonjs/utils.js.map +1 -0
  44. package/lib/module/index.js +5 -0
  45. package/lib/module/index.js.map +1 -1
  46. package/lib/module/index.native.js +99 -19
  47. package/lib/module/index.native.js.map +1 -1
  48. package/lib/module/native-stack/utils/useBackPressSubscription.js +50 -0
  49. package/lib/module/native-stack/utils/useBackPressSubscription.js.map +1 -0
  50. package/lib/module/native-stack/views/HeaderConfig.js +46 -5
  51. package/lib/module/native-stack/views/HeaderConfig.js.map +1 -1
  52. package/lib/module/reanimated/ReanimatedNativeStackScreen.js +40 -0
  53. package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
  54. package/lib/module/reanimated/ReanimatedScreen.js +6 -73
  55. package/lib/module/reanimated/ReanimatedScreen.js.map +1 -1
  56. package/lib/module/reanimated/ReanimatedScreenProvider.js +49 -0
  57. package/lib/module/reanimated/ReanimatedScreenProvider.js.map +1 -0
  58. package/lib/module/reanimated/index.js +1 -1
  59. package/lib/module/reanimated/index.js.map +1 -1
  60. package/lib/module/utils.js +8 -0
  61. package/lib/module/utils.js.map +1 -0
  62. package/lib/typescript/index.d.ts +2 -0
  63. package/lib/typescript/native-stack/types.d.ts +0 -2
  64. package/lib/typescript/native-stack/utils/useBackPressSubscription.d.ts +16 -0
  65. package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +5 -0
  66. package/lib/typescript/reanimated/ReanimatedScreen.d.ts +5 -2
  67. package/lib/typescript/reanimated/ReanimatedScreenProvider.d.ts +2 -0
  68. package/lib/typescript/reanimated/index.d.ts +1 -1
  69. package/lib/typescript/types.d.ts +46 -1
  70. package/lib/typescript/utils.d.ts +2 -0
  71. package/native-stack/README.md +35 -7
  72. package/package.json +5 -2
  73. package/src/index.native.tsx +138 -43
  74. package/src/index.tsx +10 -0
  75. package/src/native-stack/types.tsx +0 -2
  76. package/src/native-stack/utils/useBackPressSubscription.tsx +66 -0
  77. package/src/native-stack/views/HeaderConfig.tsx +46 -3
  78. package/src/reanimated/ReanimatedNativeStackScreen.tsx +61 -0
  79. package/src/reanimated/ReanimatedScreen.tsx +6 -84
  80. package/src/reanimated/ReanimatedScreenProvider.tsx +42 -0
  81. package/src/reanimated/index.tsx +1 -1
  82. package/src/types.tsx +46 -1
  83. package/src/utils.ts +12 -0
@@ -10,9 +10,11 @@ import {
10
10
  View,
11
11
  ViewProps,
12
12
  } from 'react-native';
13
+ import { Freeze } from 'react-freeze';
13
14
  // @ts-ignore Getting private component
14
15
  // eslint-disable-next-line import/default
15
16
  import processColor from 'react-native/Libraries/StyleSheet/processColor';
17
+ import { version } from 'react-native/package.json';
16
18
 
17
19
  import TransitionProgressContext from './TransitionProgressContext';
18
20
  import useTransitionProgress from './useTransitionProgress';
@@ -29,6 +31,10 @@ import {
29
31
  ScreenStackHeaderConfigProps,
30
32
  SearchBarProps,
31
33
  } from './types';
34
+ import {
35
+ isSearchBarAvailableForCurrentPlatform,
36
+ executeNativeBackPress,
37
+ } from './utils';
32
38
 
33
39
  // web implementation is taken from `index.tsx`
34
40
  const isPlatformSupported =
@@ -47,6 +53,21 @@ function enableScreens(shouldEnableScreens = true): void {
47
53
  }
48
54
  }
49
55
 
56
+ let ENABLE_FREEZE = false;
57
+
58
+ function enableFreeze(shouldEnableReactFreeze = true): void {
59
+ const minor = parseInt(version.split('.')[1]); // eg. takes 66 from '0.66.0'
60
+
61
+ // react-freeze requires react-native >=0.64, react-native from main is 0.0.0
62
+ if (!(minor === 0 || minor >= 64) && shouldEnableReactFreeze) {
63
+ console.warn(
64
+ 'react-freeze library requires at least react-native 0.64. Please upgrade your react-native version in order to use this feature.'
65
+ );
66
+ }
67
+
68
+ ENABLE_FREEZE = shouldEnableReactFreeze;
69
+ }
70
+
50
71
  // const that tells if the library should use new implementation, will be undefined for older versions
51
72
  const shouldUseActivityState = true;
52
73
 
@@ -85,7 +106,9 @@ const ScreensNativeModules = {
85
106
  get NativeScreenNavigationContainer() {
86
107
  NativeScreenNavigationContainerValue =
87
108
  NativeScreenNavigationContainerValue ||
88
- requireNativeComponent('RNSScreenNavigationContainer');
109
+ (Platform.OS === 'ios'
110
+ ? requireNativeComponent('RNSScreenNavigationContainer')
111
+ : this.NativeScreenContainer);
89
112
  return NativeScreenNavigationContainerValue;
90
113
  },
91
114
 
@@ -121,6 +144,65 @@ const ScreensNativeModules = {
121
144
  },
122
145
  };
123
146
 
147
+ interface FreezeWrapperProps {
148
+ freeze: boolean;
149
+ children: React.ReactNode;
150
+ }
151
+
152
+ // This component allows one more render before freezing the screen.
153
+ // Allows activityState to reach the native side and useIsFocused to work correctly.
154
+ function DelayedFreeze({ freeze, children }: FreezeWrapperProps) {
155
+ // flag used for determining whether freeze should be enabled
156
+ const [freezeState, setFreezeState] = React.useState(false);
157
+
158
+ if (freeze !== freezeState) {
159
+ // setImmediate is executed at the end of the JS execution block.
160
+ // Used here for changing the state right after the render.
161
+ setImmediate(() => {
162
+ setFreezeState(freeze);
163
+ });
164
+ }
165
+
166
+ return <Freeze freeze={freeze ? freezeState : false}>{children}</Freeze>;
167
+ }
168
+
169
+ function MaybeFreeze({ freeze, children }: FreezeWrapperProps) {
170
+ if (ENABLE_FREEZE) {
171
+ return <DelayedFreeze freeze={freeze}>{children}</DelayedFreeze>;
172
+ } else {
173
+ return <>{children}</>;
174
+ }
175
+ }
176
+
177
+ function ScreenStack(props: ScreenStackProps) {
178
+ if (ENABLE_FREEZE) {
179
+ const { children, ...rest } = props;
180
+ const size = React.Children.count(children);
181
+ // freezes all screens except the top one
182
+ const childrenWithFreeze = React.Children.map(children, (child, index) => (
183
+ <DelayedFreeze freeze={size - index > 1}>{child}</DelayedFreeze>
184
+ ));
185
+ return (
186
+ <ScreensNativeModules.NativeScreenStack {...rest}>
187
+ {childrenWithFreeze}
188
+ </ScreensNativeModules.NativeScreenStack>
189
+ );
190
+ }
191
+ return <ScreensNativeModules.NativeScreenStack {...props} />;
192
+ }
193
+
194
+ // Incomplete type, all accessible properties available at:
195
+ // react-native/Libraries/Components/View/ReactNativeViewViewConfig.js
196
+ interface ViewConfig extends View {
197
+ viewConfig: {
198
+ validAttributes: {
199
+ style: {
200
+ display: boolean;
201
+ };
202
+ };
203
+ };
204
+ }
205
+
124
206
  class Screen extends React.Component<ScreenProps> {
125
207
  private ref: React.ElementRef<typeof View> | null = null;
126
208
  private closing = new Animated.Value(0);
@@ -166,40 +248,52 @@ class Screen extends React.Component<ScreenProps> {
166
248
  const processedColor = processColor(statusBarColor);
167
249
 
168
250
  return (
169
- <AnimatedNativeScreen
170
- {...props}
171
- statusBarColor={processedColor}
172
- activityState={activityState}
173
- ref={this.setRef}
174
- onTransitionProgress={
175
- !isNativeStack
176
- ? undefined
177
- : Animated.event(
178
- [
179
- {
180
- nativeEvent: {
181
- progress: this.progress,
182
- closing: this.closing,
183
- goingForward: this.goingForward,
251
+ <MaybeFreeze freeze={activityState === 0}>
252
+ <AnimatedNativeScreen
253
+ {...props}
254
+ statusBarColor={processedColor}
255
+ activityState={activityState}
256
+ // This prevents showing blank screen when navigating between multiple screens with freezing
257
+ // https://github.com/software-mansion/react-native-screens/pull/1208
258
+ ref={(ref: ViewConfig) => {
259
+ if (ref?.viewConfig?.validAttributes?.style) {
260
+ ref.viewConfig.validAttributes.style = {
261
+ ...ref.viewConfig.validAttributes.style,
262
+ display: false,
263
+ };
264
+ }
265
+ this.setRef(ref);
266
+ }}
267
+ onTransitionProgress={
268
+ !isNativeStack
269
+ ? undefined
270
+ : Animated.event(
271
+ [
272
+ {
273
+ nativeEvent: {
274
+ progress: this.progress,
275
+ closing: this.closing,
276
+ goingForward: this.goingForward,
277
+ },
184
278
  },
185
- },
186
- ],
187
- { useNativeDriver: true }
188
- )
189
- }>
190
- {!isNativeStack ? ( // see comment of this prop in types.tsx for information why it is needed
191
- children
192
- ) : (
193
- <TransitionProgressContext.Provider
194
- value={{
195
- progress: this.progress,
196
- closing: this.closing,
197
- goingForward: this.goingForward,
198
- }}>
199
- {children}
200
- </TransitionProgressContext.Provider>
201
- )}
202
- </AnimatedNativeScreen>
279
+ ],
280
+ { useNativeDriver: true }
281
+ )
282
+ }>
283
+ {!isNativeStack ? ( // see comment of this prop in types.tsx for information why it is needed
284
+ children
285
+ ) : (
286
+ <TransitionProgressContext.Provider
287
+ value={{
288
+ progress: this.progress,
289
+ closing: this.closing,
290
+ goingForward: this.goingForward,
291
+ }}>
292
+ {children}
293
+ </TransitionProgressContext.Provider>
294
+ )}
295
+ </AnimatedNativeScreen>
296
+ </MaybeFreeze>
203
297
  );
204
298
  } else {
205
299
  // same reason as above
@@ -321,6 +415,7 @@ module.exports = {
321
415
  Screen,
322
416
  ScreenContainer,
323
417
  ScreenContext,
418
+ ScreenStack,
324
419
 
325
420
  get NativeScreen() {
326
421
  return ScreensNativeModules.NativeScreen;
@@ -331,15 +426,9 @@ module.exports = {
331
426
  },
332
427
 
333
428
  get NativeScreenNavigationContainer() {
334
- if (Platform.OS === 'ios') {
335
- return ScreensNativeModules.NativeScreenNavigationContainer;
336
- }
337
- return ScreensNativeModules.NativeScreenContainer;
429
+ return ScreensNativeModules.NativeScreenNavigationContainer;
338
430
  },
339
431
 
340
- get ScreenStack() {
341
- return ScreensNativeModules.NativeScreenStack;
342
- },
343
432
  get ScreenStackHeaderConfig() {
344
433
  return ScreensNativeModules.NativeScreenStackHeaderConfig;
345
434
  },
@@ -347,8 +436,10 @@ module.exports = {
347
436
  return ScreensNativeModules.NativeScreenStackHeaderSubview;
348
437
  },
349
438
  get SearchBar() {
350
- if (Platform.OS !== 'ios') {
351
- console.warn('Importing SearchBar is only valid on iOS devices.');
439
+ if (!isSearchBarAvailableForCurrentPlatform) {
440
+ console.warn(
441
+ 'Importing SearchBar is only valid on iOS and Android devices.'
442
+ );
352
443
  return View;
353
444
  }
354
445
 
@@ -371,7 +462,11 @@ module.exports = {
371
462
  ScreenStackHeaderSearchBarView,
372
463
 
373
464
  enableScreens,
465
+ enableFreeze,
374
466
  screensEnabled,
375
467
  shouldUseActivityState,
376
468
  useTransitionProgress,
469
+
470
+ isSearchBarAvailableForCurrentPlatform,
471
+ executeNativeBackPress,
377
472
  };
package/src/index.tsx CHANGED
@@ -11,6 +11,10 @@ import {
11
11
 
12
12
  export * from './types';
13
13
  export { default as useTransitionProgress } from './useTransitionProgress';
14
+ export {
15
+ isSearchBarAvailableForCurrentPlatform,
16
+ executeNativeBackPress,
17
+ } from './utils';
14
18
 
15
19
  let ENABLE_SCREENS = true;
16
20
 
@@ -22,6 +26,12 @@ export function screensEnabled(): boolean {
22
26
  return ENABLE_SCREENS;
23
27
  }
24
28
 
29
+ // @ts-ignore function stub, freezing logic is located in index.native.tsx
30
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
31
+ export function enableFreeze(shouldEnableReactFreeze = true): void {
32
+ // noop
33
+ }
34
+
25
35
  export class NativeScreen extends React.Component<ScreenProps> {
26
36
  render(): JSX.Element {
27
37
  let {
@@ -273,8 +273,6 @@ export type NativeStackNavigationOptions = {
273
273
  screenOrientation?: ScreenProps['screenOrientation'];
274
274
  /**
275
275
  * Object in which you should pass props in order to render native iOS searchBar.
276
- *
277
- * @platform ios
278
276
  */
279
277
  searchBar?: SearchBarProps;
280
278
  /**
@@ -0,0 +1,66 @@
1
+ import React from 'react';
2
+ import { BackHandler, NativeEventSubscription } from 'react-native';
3
+
4
+ interface Args {
5
+ onBackPress: () => boolean;
6
+ isDisabled: boolean;
7
+ }
8
+
9
+ interface UseBackPressSubscription {
10
+ handleAttached: () => void;
11
+ handleDetached: () => void;
12
+ createSubscription: () => void;
13
+ clearSubscription: () => void;
14
+ }
15
+
16
+ /**
17
+ * This hook is an abstraction for keeping back press subscription
18
+ * logic in one place.
19
+ */
20
+ export function useBackPressSubscription({
21
+ onBackPress,
22
+ isDisabled,
23
+ }: Args): UseBackPressSubscription {
24
+ const [isActive, setIsActive] = React.useState(false);
25
+ const subscription = React.useRef<NativeEventSubscription | undefined>();
26
+
27
+ const clearSubscription = React.useCallback((shouldSetActive = true) => {
28
+ subscription.current?.remove();
29
+ subscription.current = undefined;
30
+ if (shouldSetActive) setIsActive(false);
31
+ }, []);
32
+
33
+ const createSubscription = React.useCallback(() => {
34
+ if (!isDisabled) {
35
+ subscription.current?.remove();
36
+ subscription.current = BackHandler.addEventListener(
37
+ 'hardwareBackPress',
38
+ onBackPress
39
+ );
40
+ setIsActive(true);
41
+ }
42
+ }, [isDisabled, onBackPress]);
43
+
44
+ const handleAttached = React.useCallback(() => {
45
+ if (isActive) {
46
+ createSubscription();
47
+ }
48
+ }, [createSubscription, isActive]);
49
+
50
+ const handleDetached = React.useCallback(() => {
51
+ clearSubscription(false);
52
+ }, [clearSubscription]);
53
+
54
+ React.useEffect(() => {
55
+ if (isDisabled) {
56
+ clearSubscription();
57
+ }
58
+ }, [isDisabled, clearSubscription]);
59
+
60
+ return {
61
+ handleAttached,
62
+ handleDetached,
63
+ createSubscription,
64
+ clearSubscription,
65
+ };
66
+ }
@@ -9,8 +9,12 @@ import {
9
9
  ScreenStackHeaderRightView,
10
10
  ScreenStackHeaderSearchBarView,
11
11
  SearchBar,
12
+ SearchBarProps,
13
+ isSearchBarAvailableForCurrentPlatform,
14
+ executeNativeBackPress,
12
15
  } from 'react-native-screens';
13
16
  import { NativeStackNavigationOptions } from '../types';
17
+ import { useBackPressSubscription } from '../utils/useBackPressSubscription';
14
18
  import { processFonts } from './FontProcessor';
15
19
 
16
20
  type Props = NativeStackNavigationOptions & {
@@ -48,6 +52,19 @@ export default function HeaderConfig({
48
52
  const { colors } = useTheme();
49
53
  const tintColor = headerTintColor ?? colors.primary;
50
54
 
55
+ // We need to use back press subscription here to override back button behavior on JS side.
56
+ // Because screens are usually used with react-navigation and this library overrides back button
57
+ // we need to handle it first in case when search bar is open
58
+ const {
59
+ handleAttached,
60
+ handleDetached,
61
+ clearSubscription,
62
+ createSubscription,
63
+ } = useBackPressSubscription({
64
+ onBackPress: executeNativeBackPress,
65
+ isDisabled: !searchBar || !!searchBar.disableBackButtonOverride,
66
+ });
67
+
51
68
  const [
52
69
  backTitleFontFamily,
53
70
  largeTitleFontFamily,
@@ -58,6 +75,29 @@ export default function HeaderConfig({
58
75
  headerTitleStyle.fontFamily,
59
76
  ]);
60
77
 
78
+ // We want to clear clearSubscription only when components unmounts or search bar changes
79
+ React.useEffect(() => clearSubscription, [searchBar]);
80
+
81
+ const processedSearchBarOptions = React.useMemo(() => {
82
+ if (
83
+ Platform.OS === 'android' &&
84
+ searchBar &&
85
+ !searchBar.disableBackButtonOverride
86
+ ) {
87
+ const onFocus: SearchBarProps['onFocus'] = (...args) => {
88
+ createSubscription();
89
+ searchBar.onFocus?.(...args);
90
+ };
91
+ const onClose: SearchBarProps['onClose'] = (...args) => {
92
+ clearSubscription();
93
+ searchBar.onClose?.(...args);
94
+ };
95
+
96
+ return { ...searchBar, onFocus, onClose };
97
+ }
98
+ return searchBar;
99
+ }, [searchBar, createSubscription, clearSubscription]);
100
+
61
101
  return (
62
102
  <ScreenStackHeaderConfig
63
103
  backButtonInCustomView={backButtonInCustomView}
@@ -99,7 +139,9 @@ export default function HeaderConfig({
99
139
  titleFontSize={headerTitleStyle.fontSize}
100
140
  titleFontWeight={headerTitleStyle.fontWeight}
101
141
  topInsetEnabled={headerTopInsetEnabled}
102
- translucent={headerTranslucent === true}>
142
+ translucent={headerTranslucent === true}
143
+ onAttached={handleAttached}
144
+ onDetached={handleDetached}>
103
145
  {headerRight !== undefined ? (
104
146
  <ScreenStackHeaderRightView>
105
147
  {headerRight({ tintColor })}
@@ -121,9 +163,10 @@ export default function HeaderConfig({
121
163
  {headerCenter({ tintColor })}
122
164
  </ScreenStackHeaderCenterView>
123
165
  ) : null}
124
- {Platform.OS === 'ios' && searchBar !== undefined ? (
166
+ {isSearchBarAvailableForCurrentPlatform &&
167
+ processedSearchBarOptions !== undefined ? (
125
168
  <ScreenStackHeaderSearchBarView>
126
- <SearchBar {...searchBar} />
169
+ <SearchBar {...processedSearchBarOptions} />
127
170
  </ScreenStackHeaderSearchBarView>
128
171
  ) : null}
129
172
  </ScreenStackHeaderConfig>
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import { Platform } from 'react-native';
3
+ import {
4
+ Screen,
5
+ ScreenProps,
6
+ TransitionProgressEventType,
7
+ } from 'react-native-screens';
8
+
9
+ // @ts-ignore file to be used only if `react-native-reanimated` available in the project
10
+ import Animated, { useEvent, useSharedValue } from 'react-native-reanimated';
11
+ import ReanimatedTransitionProgressContext from './ReanimatedTransitionProgressContext';
12
+
13
+ const AnimatedScreen = Animated.createAnimatedComponent(
14
+ (Screen as unknown) as React.ComponentClass
15
+ );
16
+
17
+ const ReanimatedNativeStackScreen = React.forwardRef<
18
+ typeof AnimatedScreen,
19
+ ScreenProps
20
+ >((props, ref) => {
21
+ const { children, ...rest } = props;
22
+
23
+ const progress = useSharedValue(0);
24
+ const closing = useSharedValue(0);
25
+ const goingForward = useSharedValue(0);
26
+
27
+ return (
28
+ <AnimatedScreen
29
+ // @ts-ignore some problems with ref and onTransitionProgressReanimated being "fake" prop for parsing of `useEvent` return value
30
+ ref={ref}
31
+ onTransitionProgressReanimated={useEvent(
32
+ (event: TransitionProgressEventType) => {
33
+ 'worklet';
34
+ progress.value = event.progress;
35
+ closing.value = event.closing;
36
+ goingForward.value = event.goingForward;
37
+ },
38
+ [
39
+ // This should not be necessary, but is not properly managed by `react-native-reanimated`
40
+ // @ts-ignore wrong type
41
+ Platform.OS === 'android'
42
+ ? 'onTransitionProgress'
43
+ : 'topTransitionProgress',
44
+ ]
45
+ )}
46
+ {...rest}>
47
+ <ReanimatedTransitionProgressContext.Provider
48
+ value={{
49
+ progress: progress,
50
+ closing: closing,
51
+ goingForward: goingForward,
52
+ }}>
53
+ {children}
54
+ </ReanimatedTransitionProgressContext.Provider>
55
+ </AnimatedScreen>
56
+ );
57
+ });
58
+
59
+ ReanimatedNativeStackScreen.displayName = 'ReanimatedNativeStackScreen';
60
+
61
+ export default ReanimatedNativeStackScreen;
@@ -1,103 +1,25 @@
1
- import React, { PropsWithChildren } from 'react';
2
- import { Platform, View } from 'react-native';
3
- import {
4
- Screen,
5
- ScreenProps,
6
- ScreenContext,
7
- TransitionProgressEventType,
8
- } from 'react-native-screens';
1
+ import React from 'react';
2
+ import { Screen, ScreenProps } from 'react-native-screens';
9
3
 
10
4
  // @ts-ignore file to be used only if `react-native-reanimated` available in the project
11
- import Animated, { useEvent, useSharedValue } from 'react-native-reanimated';
12
- import ReanimatedTransitionProgressContext from './ReanimatedTransitionProgressContext';
5
+ import Animated from 'react-native-reanimated';
13
6
 
14
7
  const AnimatedScreen = Animated.createAnimatedComponent(
15
8
  (Screen as unknown) as React.ComponentClass
16
9
  );
17
10
 
18
- class ReanimatedScreenWrapper extends React.Component<ScreenProps> {
19
- private ref: React.ElementRef<typeof View> | null = null;
20
-
21
- setNativeProps(props: ScreenProps): void {
22
- this.ref?.setNativeProps(props);
23
- }
24
-
25
- setRef = (ref: React.ElementRef<typeof View> | null): void => {
26
- this.ref = ref;
27
- this.props.onComponentRef?.(ref);
28
- };
29
-
30
- render() {
31
- return (
32
- <ReanimatedScreen
33
- {...this.props}
34
- // @ts-ignore some problems with ref
35
- ref={this.setRef}
36
- />
37
- );
38
- }
39
- }
40
-
41
11
  const ReanimatedScreen = React.forwardRef<typeof AnimatedScreen, ScreenProps>(
42
12
  (props, ref) => {
43
- const { children, ...rest } = props;
44
-
45
- const progress = useSharedValue(0);
46
- const closing = useSharedValue(0);
47
- const goingForward = useSharedValue(0);
48
-
49
13
  return (
50
14
  <AnimatedScreen
51
15
  // @ts-ignore some problems with ref and onTransitionProgressReanimated being "fake" prop for parsing of `useEvent` return value
52
16
  ref={ref}
53
- // ReanimatedScreen.tsx should only be used by Screens of native-stack, but it always better to check
54
- onTransitionProgressReanimated={
55
- !props.isNativeStack
56
- ? undefined
57
- : useEvent(
58
- (event: TransitionProgressEventType) => {
59
- 'worklet';
60
- progress.value = event.progress;
61
- closing.value = event.closing;
62
- goingForward.value = event.goingForward;
63
- },
64
- [
65
- // This should not be necessary, but is not properly managed by `react-native-reanimated`
66
- // @ts-ignore wrong type
67
- Platform.OS === 'android'
68
- ? 'onTransitionProgress'
69
- : 'topTransitionProgress',
70
- ]
71
- )
72
- }
73
- {...rest}>
74
- {!props.isNativeStack ? ( // see comment of this prop in types.tsx for information why it is needed
75
- children
76
- ) : (
77
- <ReanimatedTransitionProgressContext.Provider
78
- value={{
79
- progress: progress,
80
- closing: closing,
81
- goingForward: goingForward,
82
- }}>
83
- {children}
84
- </ReanimatedTransitionProgressContext.Provider>
85
- )}
86
- </AnimatedScreen>
17
+ {...props}
18
+ />
87
19
  );
88
20
  }
89
21
  );
90
22
 
91
- // used to silence error "Component definition is missing display name"
92
23
  ReanimatedScreen.displayName = 'ReanimatedScreen';
93
24
 
94
- export default function ReanimatedScreenProvider(
95
- props: PropsWithChildren<unknown>
96
- ) {
97
- return (
98
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
99
- <ScreenContext.Provider value={ReanimatedScreenWrapper as any}>
100
- {props.children}
101
- </ScreenContext.Provider>
102
- );
103
- }
25
+ export default ReanimatedScreen;
@@ -0,0 +1,42 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import { View } from 'react-native';
3
+ import { ScreenProps, ScreenContext } from 'react-native-screens';
4
+ import ReanimatedNativeStackScreen from './ReanimatedNativeStackScreen';
5
+ import AnimatedScreen from './ReanimatedScreen';
6
+
7
+ class ReanimatedScreenWrapper extends React.Component<ScreenProps> {
8
+ private ref: React.ElementRef<typeof View> | null = null;
9
+
10
+ setNativeProps(props: ScreenProps): void {
11
+ this.ref?.setNativeProps(props);
12
+ }
13
+
14
+ setRef = (ref: React.ElementRef<typeof View> | null): void => {
15
+ this.ref = ref;
16
+ this.props.onComponentRef?.(ref);
17
+ };
18
+
19
+ render() {
20
+ const ReanimatedScreen = this.props.isNativeStack
21
+ ? ReanimatedNativeStackScreen
22
+ : AnimatedScreen;
23
+ return (
24
+ <ReanimatedScreen
25
+ {...this.props}
26
+ // @ts-ignore some problems with ref
27
+ ref={this.setRef}
28
+ />
29
+ );
30
+ }
31
+ }
32
+
33
+ export default function ReanimatedScreenProvider(
34
+ props: PropsWithChildren<unknown>
35
+ ) {
36
+ return (
37
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
38
+ <ScreenContext.Provider value={ReanimatedScreenWrapper as any}>
39
+ {props.children}
40
+ </ScreenContext.Provider>
41
+ );
42
+ }
@@ -1,2 +1,2 @@
1
- export { default as ReanimatedScreenProvider } from './ReanimatedScreen';
1
+ export { default as ReanimatedScreenProvider } from './ReanimatedScreenProvider';
2
2
  export { default as useReanimatedTransitionProgress } from './useReanimatedTransitionProgress';