react-native-screens 3.9.0 → 3.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +47 -7
- package/android/build.gradle +1 -2
- package/android/src/main/java/com/swmansion/rnscreens/CustomSearchView.kt +71 -0
- package/android/src/main/java/com/swmansion/rnscreens/CustomToolbar.kt +7 -0
- package/android/src/main/java/com/swmansion/rnscreens/FragmentBackPressOverrider.kt +29 -0
- package/android/src/main/java/com/swmansion/rnscreens/RNScreensPackage.kt +2 -1
- package/android/src/main/java/com/swmansion/rnscreens/Screen.kt +35 -52
- package/android/src/main/java/com/swmansion/rnscreens/ScreenContainer.kt +1 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenFragment.kt +83 -34
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStack.kt +38 -33
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackFragment.kt +77 -42
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfig.kt +25 -9
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderConfigViewManager.kt +8 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubview.kt +7 -1
- package/android/src/main/java/com/swmansion/rnscreens/ScreenStackHeaderSubviewManager.kt +1 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenViewManager.kt +10 -0
- package/android/src/main/java/com/swmansion/rnscreens/ScreenWindowTraits.kt +72 -11
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarManager.kt +107 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchBarView.kt +155 -0
- package/android/src/main/java/com/swmansion/rnscreens/SearchViewFormatter.kt +67 -0
- package/android/src/main/res/anim/rns_default_enter_in.xml +18 -0
- package/android/src/main/res/anim/rns_default_enter_out.xml +19 -0
- package/android/src/main/res/anim/rns_default_exit_in.xml +17 -0
- package/android/src/main/res/anim/rns_default_exit_out.xml +18 -0
- package/android/src/main/res/anim/rns_fade_in.xml +7 -0
- package/android/src/main/res/anim/rns_fade_out.xml +7 -0
- package/android/src/main/res/anim/rns_no_animation_20.xml +6 -0
- package/createNativeStackNavigator/README.md +12 -0
- package/ios/RNSScreen.h +10 -0
- package/ios/RNSScreen.m +38 -0
- package/ios/RNSScreenContainer.m +5 -0
- package/ios/RNSScreenStack.m +29 -13
- package/ios/RNSScreenStackAnimator.m +45 -14
- package/ios/RNSScreenStackHeaderConfig.m +4 -1
- package/ios/RNSScreenWindowTraits.h +1 -0
- package/ios/RNSScreenWindowTraits.m +20 -0
- package/ios/UIViewController+RNScreens.m +10 -0
- package/lib/commonjs/index.js +17 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/index.native.js +66 -18
- package/lib/commonjs/index.native.js.map +1 -1
- package/lib/commonjs/native-stack/utils/useBackPressSubscription.js +67 -0
- package/lib/commonjs/native-stack/utils/useBackPressSubscription.js.map +1 -0
- package/lib/commonjs/native-stack/views/HeaderConfig.js +46 -4
- package/lib/commonjs/native-stack/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/native-stack/views/NativeStackView.js +33 -4
- package/lib/commonjs/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js +60 -0
- package/lib/commonjs/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
- package/lib/commonjs/reanimated/ReanimatedScreen.js +7 -79
- package/lib/commonjs/reanimated/ReanimatedScreen.js.map +1 -1
- package/lib/commonjs/reanimated/ReanimatedScreenProvider.js +61 -0
- package/lib/commonjs/reanimated/ReanimatedScreenProvider.js.map +1 -0
- package/lib/commonjs/reanimated/index.js +2 -2
- package/lib/commonjs/reanimated/index.js.map +1 -1
- package/lib/commonjs/utils.js +20 -0
- package/lib/commonjs/utils.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/index.native.js +65 -19
- package/lib/module/index.native.js.map +1 -1
- package/lib/module/native-stack/utils/useBackPressSubscription.js +50 -0
- package/lib/module/native-stack/utils/useBackPressSubscription.js.map +1 -0
- package/lib/module/native-stack/views/HeaderConfig.js +46 -5
- package/lib/module/native-stack/views/HeaderConfig.js.map +1 -1
- package/lib/module/native-stack/views/NativeStackView.js +33 -4
- package/lib/module/native-stack/views/NativeStackView.js.map +1 -1
- package/lib/module/reanimated/ReanimatedNativeStackScreen.js +40 -0
- package/lib/module/reanimated/ReanimatedNativeStackScreen.js.map +1 -0
- package/lib/module/reanimated/ReanimatedScreen.js +6 -73
- package/lib/module/reanimated/ReanimatedScreen.js.map +1 -1
- package/lib/module/reanimated/ReanimatedScreenProvider.js +49 -0
- package/lib/module/reanimated/ReanimatedScreenProvider.js.map +1 -0
- package/lib/module/reanimated/index.js +1 -1
- package/lib/module/reanimated/index.js.map +1 -1
- package/lib/module/utils.js +8 -0
- package/lib/module/utils.js.map +1 -0
- package/lib/typescript/index.d.ts +1 -0
- package/lib/typescript/native-stack/types.d.ts +34 -2
- package/lib/typescript/native-stack/utils/useBackPressSubscription.d.ts +16 -0
- package/lib/typescript/reanimated/ReanimatedNativeStackScreen.d.ts +5 -0
- package/lib/typescript/reanimated/ReanimatedScreen.d.ts +5 -2
- package/lib/typescript/reanimated/ReanimatedScreenProvider.d.ts +2 -0
- package/lib/typescript/reanimated/index.d.ts +1 -1
- package/lib/typescript/types.d.ts +101 -1
- package/lib/typescript/utils.d.ts +2 -0
- package/native-stack/README.md +70 -8
- package/package.json +2 -1
- package/reanimated/package.json +6 -0
- package/src/index.native.tsx +94 -36
- package/src/index.tsx +4 -0
- package/src/native-stack/types.tsx +34 -2
- package/src/native-stack/utils/useBackPressSubscription.tsx +66 -0
- package/src/native-stack/views/HeaderConfig.tsx +46 -3
- package/src/native-stack/views/NativeStackView.tsx +33 -4
- package/src/reanimated/ReanimatedNativeStackScreen.tsx +61 -0
- package/src/reanimated/ReanimatedScreen.tsx +6 -84
- package/src/reanimated/ReanimatedScreenProvider.tsx +42 -0
- package/src/reanimated/index.tsx +1 -1
- package/src/types.tsx +101 -1
- package/src/utils.ts +12 -0
|
@@ -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
|
-
{
|
|
166
|
+
{isSearchBarAvailableForCurrentPlatform &&
|
|
167
|
+
processedSearchBarOptions !== undefined ? (
|
|
125
168
|
<ScreenStackHeaderSearchBarView>
|
|
126
|
-
<SearchBar {...
|
|
169
|
+
<SearchBar {...processedSearchBarOptions} />
|
|
127
170
|
</ScreenStackHeaderSearchBarView>
|
|
128
171
|
) : null}
|
|
129
172
|
</ScreenStackHeaderConfig>
|
|
@@ -149,22 +149,46 @@ const RouteView = ({
|
|
|
149
149
|
}) => {
|
|
150
150
|
const { options, render: renderScene } = descriptors[route.key];
|
|
151
151
|
const {
|
|
152
|
-
customAnimationOnSwipe,
|
|
153
|
-
fullScreenSwipeEnabled,
|
|
154
152
|
gestureEnabled,
|
|
155
153
|
headerShown,
|
|
154
|
+
homeIndicatorHidden,
|
|
156
155
|
nativeBackButtonDismissalEnabled = false,
|
|
156
|
+
navigationBarColor,
|
|
157
|
+
navigationBarHidden,
|
|
157
158
|
replaceAnimation = 'pop',
|
|
158
159
|
screenOrientation,
|
|
159
|
-
stackAnimation,
|
|
160
160
|
statusBarAnimation,
|
|
161
161
|
statusBarColor,
|
|
162
162
|
statusBarHidden,
|
|
163
163
|
statusBarStyle,
|
|
164
164
|
statusBarTranslucent,
|
|
165
|
+
swipeDirection = 'horizontal',
|
|
166
|
+
transitionDuration,
|
|
167
|
+
} = options;
|
|
168
|
+
|
|
169
|
+
let {
|
|
170
|
+
customAnimationOnSwipe,
|
|
171
|
+
fullScreenSwipeEnabled,
|
|
172
|
+
stackAnimation,
|
|
173
|
+
stackPresentation = 'push',
|
|
165
174
|
} = options;
|
|
166
175
|
|
|
167
|
-
|
|
176
|
+
if (swipeDirection === 'vertical') {
|
|
177
|
+
// for `vertical` direction to work, we need to set `fullScreenSwipeEnabled` to `true`
|
|
178
|
+
// so the screen can be dismissed from any point on screen.
|
|
179
|
+
// `customAnimationOnSwipe` needs to be set to `true` so the `stackAnimation` set by user can be used,
|
|
180
|
+
// otherwise `simple_push` will be used.
|
|
181
|
+
// Also, the default animation for this direction seems to be `slide_from_bottom`.
|
|
182
|
+
if (fullScreenSwipeEnabled === undefined) {
|
|
183
|
+
fullScreenSwipeEnabled = true;
|
|
184
|
+
}
|
|
185
|
+
if (customAnimationOnSwipe === undefined) {
|
|
186
|
+
customAnimationOnSwipe = true;
|
|
187
|
+
}
|
|
188
|
+
if (stackAnimation === undefined) {
|
|
189
|
+
stackAnimation = 'slide_from_bottom';
|
|
190
|
+
}
|
|
191
|
+
}
|
|
168
192
|
|
|
169
193
|
if (index === 0) {
|
|
170
194
|
// first screen should always be treated as `push`, it resolves problems with no header animation
|
|
@@ -194,8 +218,11 @@ const RouteView = ({
|
|
|
194
218
|
style={StyleSheet.absoluteFill}
|
|
195
219
|
customAnimationOnSwipe={customAnimationOnSwipe}
|
|
196
220
|
fullScreenSwipeEnabled={fullScreenSwipeEnabled}
|
|
221
|
+
homeIndicatorHidden={homeIndicatorHidden}
|
|
197
222
|
gestureEnabled={isAndroid ? false : gestureEnabled}
|
|
198
223
|
nativeBackButtonDismissalEnabled={nativeBackButtonDismissalEnabled}
|
|
224
|
+
navigationBarColor={navigationBarColor}
|
|
225
|
+
navigationBarHidden={navigationBarHidden}
|
|
199
226
|
replaceAnimation={replaceAnimation}
|
|
200
227
|
screenOrientation={screenOrientation}
|
|
201
228
|
stackAnimation={stackAnimation}
|
|
@@ -205,6 +232,8 @@ const RouteView = ({
|
|
|
205
232
|
statusBarHidden={statusBarHidden}
|
|
206
233
|
statusBarStyle={statusBarStyle}
|
|
207
234
|
statusBarTranslucent={statusBarTranslucent}
|
|
235
|
+
swipeDirection={swipeDirection}
|
|
236
|
+
transitionDuration={transitionDuration}
|
|
208
237
|
onHeaderBackButtonClicked={() => {
|
|
209
238
|
navigation.dispatch({
|
|
210
239
|
...StackActions.pop(),
|
|
@@ -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
|
|
2
|
-
import {
|
|
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
|
|
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
|
-
|
|
54
|
-
|
|
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
|
|
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
|
+
}
|
package/src/reanimated/index.tsx
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { default as ReanimatedScreenProvider } from './
|
|
1
|
+
export { default as ReanimatedScreenProvider } from './ReanimatedScreenProvider';
|
|
2
2
|
export { default as useReanimatedTransitionProgress } from './useReanimatedTransitionProgress';
|
package/src/types.tsx
CHANGED
|
@@ -47,6 +47,7 @@ export type BlurEffectTypes =
|
|
|
47
47
|
| 'systemThickMaterialDark'
|
|
48
48
|
| 'systemChromeMaterialDark';
|
|
49
49
|
export type ScreenReplaceTypes = 'push' | 'pop';
|
|
50
|
+
export type SwipeDirectionTypes = 'vertical' | 'horizontal';
|
|
50
51
|
export type ScreenOrientationTypes =
|
|
51
52
|
| 'default'
|
|
52
53
|
| 'all'
|
|
@@ -101,6 +102,12 @@ export interface ScreenProps extends ViewProps {
|
|
|
101
102
|
* @platform ios
|
|
102
103
|
*/
|
|
103
104
|
gestureEnabled?: boolean;
|
|
105
|
+
/**
|
|
106
|
+
* Whether the home indicator should be hidden on this screen. Defaults to `false`.
|
|
107
|
+
*
|
|
108
|
+
* @platform ios
|
|
109
|
+
*/
|
|
110
|
+
homeIndicatorHidden?: boolean;
|
|
104
111
|
/**
|
|
105
112
|
* Boolean indicating whether, when the Android default back button is clicked, the `pop` action should be performed on the native side or on the JS side to be able to prevent it.
|
|
106
113
|
* Unfortunately the same behavior is not available on iOS since the behavior of native back button cannot be changed there.
|
|
@@ -109,6 +116,18 @@ export interface ScreenProps extends ViewProps {
|
|
|
109
116
|
* @platform android
|
|
110
117
|
*/
|
|
111
118
|
nativeBackButtonDismissalEnabled?: boolean;
|
|
119
|
+
/**
|
|
120
|
+
* Sets the navigation bar color. Defaults to initial status bar color.
|
|
121
|
+
*
|
|
122
|
+
* @platform android
|
|
123
|
+
*/
|
|
124
|
+
navigationBarColor?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Sets the visibility of the navigation bar. Defaults to `false`.
|
|
127
|
+
*
|
|
128
|
+
* @platform android
|
|
129
|
+
*/
|
|
130
|
+
navigationBarHidden?: boolean;
|
|
112
131
|
/**
|
|
113
132
|
* A callback that gets called when the current screen appears.
|
|
114
133
|
*/
|
|
@@ -214,6 +233,23 @@ export interface ScreenProps extends ViewProps {
|
|
|
214
233
|
* @platform android
|
|
215
234
|
*/
|
|
216
235
|
statusBarTranslucent?: boolean;
|
|
236
|
+
/**
|
|
237
|
+
* Sets the direction in which you should swipe to dismiss the screen.
|
|
238
|
+
* When using `vertical` option, options `fullScreenSwipeEnabled: true`, `customAnimationOnSwipe: true` and `stackAnimation: 'slide_from_bottom'` are set by default.
|
|
239
|
+
* The following values are supported:
|
|
240
|
+
* - `vertical` – dismiss screen vertically
|
|
241
|
+
* - `horizontal` – dismiss screen horizontally (default)
|
|
242
|
+
*
|
|
243
|
+
* @platform ios
|
|
244
|
+
*/
|
|
245
|
+
swipeDirection?: SwipeDirectionTypes;
|
|
246
|
+
/**
|
|
247
|
+
* Changes the duration (in milliseconds) of `slide_from_bottom`, `fade_from_bottom`, `fade` and `simple_push` transitions on iOS. Defaults to `350`.
|
|
248
|
+
* The duration of `default` and `flip` transitions isn't customizable.
|
|
249
|
+
*
|
|
250
|
+
* @platform ios
|
|
251
|
+
*/
|
|
252
|
+
transitionDuration?: number;
|
|
217
253
|
}
|
|
218
254
|
|
|
219
255
|
export interface ScreenContainerProps extends ViewProps {
|
|
@@ -331,6 +367,14 @@ export interface ScreenStackHeaderConfigProps extends ViewProps {
|
|
|
331
367
|
* Boolean that allows for disabling drop shadow under navigation header when the edge of any scrollable content reaches the matching edge of the navigation bar.
|
|
332
368
|
*/
|
|
333
369
|
largeTitleHideShadow?: boolean;
|
|
370
|
+
/**
|
|
371
|
+
* Callback which is executed when screen header is attached
|
|
372
|
+
*/
|
|
373
|
+
onAttached?: () => void;
|
|
374
|
+
/**
|
|
375
|
+
* Callback which is executed when screen header is detached
|
|
376
|
+
*/
|
|
377
|
+
onDetached?: () => void;
|
|
334
378
|
/**
|
|
335
379
|
* String that can be displayed in the header as a fallback for `headerTitle`.
|
|
336
380
|
*/
|
|
@@ -371,24 +415,47 @@ export interface SearchBarProps {
|
|
|
371
415
|
* The auto-capitalization behavior
|
|
372
416
|
*/
|
|
373
417
|
autoCapitalize?: 'none' | 'words' | 'sentences' | 'characters';
|
|
418
|
+
/**
|
|
419
|
+
* Automatically focuses search bar on mount
|
|
420
|
+
*
|
|
421
|
+
* @platform android
|
|
422
|
+
*/
|
|
423
|
+
autoFocus?: boolean;
|
|
374
424
|
/**
|
|
375
425
|
* The search field background color
|
|
376
426
|
*/
|
|
377
427
|
barTintColor?: string;
|
|
378
428
|
/**
|
|
379
429
|
* The text to be used instead of default `Cancel` button text
|
|
430
|
+
*
|
|
431
|
+
* @platform ios
|
|
380
432
|
*/
|
|
381
433
|
cancelButtonText?: string;
|
|
382
|
-
|
|
434
|
+
/**
|
|
435
|
+
* Specifies whether the back button should close search bar's text input or not.
|
|
436
|
+
*
|
|
437
|
+
* @platform android
|
|
438
|
+
*/
|
|
439
|
+
disableBackButtonOverride?: boolean;
|
|
383
440
|
/**
|
|
384
441
|
* Indicates whether to hide the navigation bar
|
|
442
|
+
*
|
|
443
|
+
* @platform ios
|
|
385
444
|
*/
|
|
386
445
|
hideNavigationBar?: boolean;
|
|
387
446
|
/**
|
|
388
447
|
* Indicates whether to hide the search bar when scrolling
|
|
448
|
+
*
|
|
449
|
+
* @platform ios
|
|
389
450
|
*/
|
|
390
451
|
hideWhenScrolling?: boolean;
|
|
391
452
|
|
|
453
|
+
/**
|
|
454
|
+
* Sets type of the input. Defaults to `text`.
|
|
455
|
+
*
|
|
456
|
+
* @platform android
|
|
457
|
+
*/
|
|
458
|
+
inputType?: 'text' | 'phone' | 'number' | 'email';
|
|
392
459
|
/**
|
|
393
460
|
* Indicates whether to to obscure the underlying content
|
|
394
461
|
*/
|
|
@@ -399,6 +466,8 @@ export interface SearchBarProps {
|
|
|
399
466
|
onBlur?: (e: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
400
467
|
/**
|
|
401
468
|
* A callback that gets called when the cancel button is pressed
|
|
469
|
+
*
|
|
470
|
+
* @platform ios
|
|
402
471
|
*/
|
|
403
472
|
onCancelButtonPress?: (e: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
404
473
|
|
|
@@ -407,10 +476,22 @@ export interface SearchBarProps {
|
|
|
407
476
|
*/
|
|
408
477
|
onChangeText?: (e: NativeSyntheticEvent<TextInputFocusEventData>) => void;
|
|
409
478
|
|
|
479
|
+
/**
|
|
480
|
+
* A callback that gets called when search bar is closed
|
|
481
|
+
*
|
|
482
|
+
* @platform android
|
|
483
|
+
*/
|
|
484
|
+
onClose?: () => void;
|
|
410
485
|
/**
|
|
411
486
|
* A callback that gets called when search bar has received focus
|
|
412
487
|
*/
|
|
413
488
|
onFocus?: (e: NativeSyntheticEvent<TargetedEvent>) => void;
|
|
489
|
+
/**
|
|
490
|
+
* A callback that gets called when search bar is opened
|
|
491
|
+
*
|
|
492
|
+
* @platform android
|
|
493
|
+
*/
|
|
494
|
+
onOpen?: () => void;
|
|
414
495
|
/**
|
|
415
496
|
* A callback that gets called when the search button is pressed. It receives the current text value of the search bar.
|
|
416
497
|
*/
|
|
@@ -425,4 +506,23 @@ export interface SearchBarProps {
|
|
|
425
506
|
* The search field text color
|
|
426
507
|
*/
|
|
427
508
|
textColor?: string;
|
|
509
|
+
/**
|
|
510
|
+
* The search hint text color
|
|
511
|
+
*
|
|
512
|
+
* @plaform android
|
|
513
|
+
*/
|
|
514
|
+
hintTextColor?: string;
|
|
515
|
+
/**
|
|
516
|
+
* The search and close icon color shown in the header
|
|
517
|
+
*
|
|
518
|
+
* @plaform android
|
|
519
|
+
*/
|
|
520
|
+
headerIconColor?: string;
|
|
521
|
+
/**
|
|
522
|
+
* Show the search hint icon when search bar is focused
|
|
523
|
+
*
|
|
524
|
+
* @plaform android
|
|
525
|
+
* @default true
|
|
526
|
+
*/
|
|
527
|
+
shouldShowHintSearchIcon?: boolean;
|
|
428
528
|
}
|
package/src/utils.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BackHandler, Platform } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export const isSearchBarAvailableForCurrentPlatform = [
|
|
4
|
+
'ios',
|
|
5
|
+
'android',
|
|
6
|
+
].includes(Platform.OS);
|
|
7
|
+
|
|
8
|
+
export function executeNativeBackPress() {
|
|
9
|
+
// This function invokes the native back press event
|
|
10
|
+
BackHandler.exitApp();
|
|
11
|
+
return true;
|
|
12
|
+
}
|