@react-navigation/native-stack 6.2.5 → 6.5.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/lib/commonjs/index.js +8 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/navigators/createNativeStackNavigator.js +12 -10
- package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/commonjs/views/HeaderConfig.js +62 -39
- package/lib/commonjs/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.js +62 -40
- package/lib/commonjs/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.native.js +57 -41
- package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigators/createNativeStackNavigator.js +12 -10
- package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/module/views/HeaderConfig.js +62 -39
- package/lib/module/views/HeaderConfig.js.map +1 -1
- package/lib/module/views/NativeStackView.js +61 -40
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +59 -43
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -1
- package/lib/typescript/src/types.d.ts +40 -11
- package/lib/typescript/src/views/HeaderConfig.d.ts +2 -1
- package/package.json +6 -6
- package/src/index.tsx +6 -0
- package/src/navigators/createNativeStackNavigator.tsx +21 -18
- package/src/types.tsx +41 -9
- package/src/views/HeaderConfig.tsx +153 -103
- package/src/views/NativeStackView.native.tsx +65 -47
- package/src/views/NativeStackView.tsx +25 -3
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
SafeAreaProviderCompat,
|
|
7
7
|
} from '@react-navigation/elements';
|
|
8
8
|
import {
|
|
9
|
+
NavigationContext,
|
|
10
|
+
NavigationRouteContext,
|
|
9
11
|
ParamListBase,
|
|
10
12
|
Route,
|
|
11
13
|
StackActions,
|
|
@@ -13,7 +15,7 @@ import {
|
|
|
13
15
|
useTheme,
|
|
14
16
|
} from '@react-navigation/native';
|
|
15
17
|
import * as React from 'react';
|
|
16
|
-
import { Platform,
|
|
18
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
17
19
|
import {
|
|
18
20
|
useSafeAreaFrame,
|
|
19
21
|
useSafeAreaInsets,
|
|
@@ -40,11 +42,13 @@ const MaybeNestedStack = ({
|
|
|
40
42
|
options,
|
|
41
43
|
route,
|
|
42
44
|
presentation,
|
|
45
|
+
headerHeight,
|
|
43
46
|
children,
|
|
44
47
|
}: {
|
|
45
48
|
options: NativeStackNavigationOptions;
|
|
46
49
|
route: Route<string>;
|
|
47
50
|
presentation: Exclude<StackPresentationTypes, 'push'> | 'card';
|
|
51
|
+
headerHeight: number;
|
|
48
52
|
children: React.ReactNode;
|
|
49
53
|
}) => {
|
|
50
54
|
const { colors } = useTheme();
|
|
@@ -83,33 +87,17 @@ const MaybeNestedStack = ({
|
|
|
83
87
|
</DebugContainer>
|
|
84
88
|
);
|
|
85
89
|
|
|
86
|
-
const insets = useSafeAreaInsets();
|
|
87
|
-
const dimensions = useSafeAreaFrame();
|
|
88
|
-
// landscape is meaningful only for iPhone
|
|
89
|
-
const isLandscape =
|
|
90
|
-
dimensions.width > dimensions.height &&
|
|
91
|
-
!(Platform as PlatformIOSStatic).isPad &&
|
|
92
|
-
!(Platform as PlatformIOSStatic).isTVOS;
|
|
93
|
-
// `modal` and `formSheet` presentations do not take whole screen, so should not take the inset.
|
|
94
|
-
const isFullScreenModal =
|
|
95
|
-
presentation !== 'modal' && presentation !== 'formSheet';
|
|
96
|
-
const topInset = isFullScreenModal && !isLandscape ? insets.top : 0;
|
|
97
|
-
const headerHeight = getDefaultHeaderHeight(
|
|
98
|
-
dimensions,
|
|
99
|
-
!isFullScreenModal,
|
|
100
|
-
topInset
|
|
101
|
-
);
|
|
102
|
-
|
|
103
90
|
if (isHeaderInModal) {
|
|
104
91
|
return (
|
|
105
92
|
<ScreenStack style={styles.container}>
|
|
106
93
|
<Screen enabled style={StyleSheet.absoluteFill}>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
94
|
+
<HeaderConfig
|
|
95
|
+
{...options}
|
|
96
|
+
route={route}
|
|
97
|
+
headerHeight={headerHeight}
|
|
98
|
+
canGoBack
|
|
99
|
+
/>
|
|
100
|
+
{content}
|
|
113
101
|
</Screen>
|
|
114
102
|
</ScreenStack>
|
|
115
103
|
);
|
|
@@ -139,11 +127,13 @@ const SceneView = ({
|
|
|
139
127
|
}: SceneViewProps) => {
|
|
140
128
|
const { route, navigation, options, render } = descriptor;
|
|
141
129
|
const {
|
|
130
|
+
animation,
|
|
131
|
+
animationTypeForReplace = 'push',
|
|
132
|
+
customAnimationOnGesture,
|
|
133
|
+
fullScreenGestureEnabled,
|
|
142
134
|
gestureEnabled,
|
|
143
135
|
header,
|
|
144
136
|
headerShown,
|
|
145
|
-
animationTypeForReplace = 'push',
|
|
146
|
-
animation,
|
|
147
137
|
orientation,
|
|
148
138
|
statusBarAnimation,
|
|
149
139
|
statusBarHidden,
|
|
@@ -163,20 +153,35 @@ const SceneView = ({
|
|
|
163
153
|
: presentation === 'card' && headerShown !== false;
|
|
164
154
|
|
|
165
155
|
const insets = useSafeAreaInsets();
|
|
156
|
+
const frame = useSafeAreaFrame();
|
|
157
|
+
|
|
158
|
+
// `modal` and `formSheet` presentations do not take whole screen, so should not take the inset.
|
|
159
|
+
const isModal = presentation === 'modal' || presentation === 'formSheet';
|
|
160
|
+
|
|
161
|
+
// Modals are fullscreen in landscape only on iPhone
|
|
162
|
+
const isIPhone =
|
|
163
|
+
Platform.OS === 'ios' && !(Platform.isPad && Platform.isTVOS);
|
|
164
|
+
const isLandscape = frame.width > frame.height;
|
|
165
|
+
|
|
166
|
+
const topInset = isModal || (isIPhone && isLandscape) ? 0 : insets.top;
|
|
166
167
|
|
|
167
168
|
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
|
168
169
|
const parentHeaderHeight = React.useContext(HeaderHeightContext);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
170
|
+
|
|
171
|
+
const defaultHeaderHeight = getDefaultHeaderHeight(frame, isModal, topInset);
|
|
172
|
+
|
|
173
|
+
const [customHeaderHeight, setCustomHeaderHeight] =
|
|
174
|
+
React.useState(defaultHeaderHeight);
|
|
175
|
+
|
|
176
|
+
const headerHeight = header ? customHeaderHeight : defaultHeaderHeight;
|
|
174
177
|
|
|
175
178
|
return (
|
|
176
179
|
<Screen
|
|
177
180
|
key={route.key}
|
|
178
181
|
enabled
|
|
179
182
|
style={StyleSheet.absoluteFill}
|
|
183
|
+
customAnimationOnSwipe={customAnimationOnGesture}
|
|
184
|
+
fullScreenSwipeEnabled={fullScreenGestureEnabled}
|
|
180
185
|
gestureEnabled={
|
|
181
186
|
isAndroid
|
|
182
187
|
? // This prop enables handling of system back gestures on Android
|
|
@@ -195,6 +200,7 @@ const SceneView = ({
|
|
|
195
200
|
onAppear={onAppear}
|
|
196
201
|
onDisappear={onDisappear}
|
|
197
202
|
onDismissed={onDismissed}
|
|
203
|
+
isNativeStack
|
|
198
204
|
>
|
|
199
205
|
<HeaderShownContext.Provider
|
|
200
206
|
value={isParentHeaderShown || isHeaderInPush !== false}
|
|
@@ -205,25 +211,35 @@ const SceneView = ({
|
|
|
205
211
|
}
|
|
206
212
|
>
|
|
207
213
|
{header !== undefined && headerShown !== false ? (
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
214
|
+
<NavigationContext.Provider value={navigation}>
|
|
215
|
+
<NavigationRouteContext.Provider value={route}>
|
|
216
|
+
<View
|
|
217
|
+
onLayout={(e) => {
|
|
218
|
+
setCustomHeaderHeight(e.nativeEvent.layout.height);
|
|
219
|
+
}}
|
|
220
|
+
>
|
|
221
|
+
{header({
|
|
222
|
+
back: previousDescriptor
|
|
223
|
+
? {
|
|
224
|
+
title: getHeaderTitle(
|
|
225
|
+
previousDescriptor.options,
|
|
226
|
+
previousDescriptor.route.name
|
|
227
|
+
),
|
|
228
|
+
}
|
|
229
|
+
: undefined,
|
|
230
|
+
options,
|
|
231
|
+
route,
|
|
232
|
+
navigation,
|
|
233
|
+
})}
|
|
234
|
+
</View>
|
|
235
|
+
</NavigationRouteContext.Provider>
|
|
236
|
+
</NavigationContext.Provider>
|
|
222
237
|
) : (
|
|
223
238
|
<HeaderConfig
|
|
224
239
|
{...options}
|
|
225
240
|
route={route}
|
|
226
241
|
headerShown={isHeaderInPush}
|
|
242
|
+
headerHeight={headerHeight}
|
|
227
243
|
canGoBack={index !== 0}
|
|
228
244
|
/>
|
|
229
245
|
)}
|
|
@@ -231,6 +247,7 @@ const SceneView = ({
|
|
|
231
247
|
options={options}
|
|
232
248
|
route={route}
|
|
233
249
|
presentation={presentation}
|
|
250
|
+
headerHeight={headerHeight}
|
|
234
251
|
>
|
|
235
252
|
{render()}
|
|
236
253
|
</MaybeNestedStack>
|
|
@@ -247,8 +264,9 @@ type Props = {
|
|
|
247
264
|
};
|
|
248
265
|
|
|
249
266
|
function NativeStackViewInner({ state, navigation, descriptors }: Props) {
|
|
250
|
-
const [nextDismissedKey, setNextDismissedKey] =
|
|
251
|
-
|
|
267
|
+
const [nextDismissedKey, setNextDismissedKey] = React.useState<string | null>(
|
|
268
|
+
null
|
|
269
|
+
);
|
|
252
270
|
|
|
253
271
|
const dismissedRouteName = nextDismissedKey
|
|
254
272
|
? state.routes.find((route) => route.key === nextDismissedKey)?.name
|
|
@@ -25,6 +25,11 @@ type Props = {
|
|
|
25
25
|
descriptors: NativeStackDescriptorMap;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
const TRANSPARENT_PRESENTATIONS = [
|
|
29
|
+
'transparentModal',
|
|
30
|
+
'containedTransparentModal',
|
|
31
|
+
];
|
|
32
|
+
|
|
28
33
|
export default function NativeStackView({ state, descriptors }: Props) {
|
|
29
34
|
return (
|
|
30
35
|
<SafeAreaProviderCompat>
|
|
@@ -33,9 +38,11 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
33
38
|
const isFocused = state.index === i;
|
|
34
39
|
const canGoBack = i !== 0;
|
|
35
40
|
const previousKey = state.routes[i - 1]?.key;
|
|
41
|
+
const nextKey = state.routes[i + 1]?.key;
|
|
36
42
|
const previousDescriptor = previousKey
|
|
37
43
|
? descriptors[previousKey]
|
|
38
44
|
: undefined;
|
|
45
|
+
const nexDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
39
46
|
const { options, navigation, render } = descriptors[route.key];
|
|
40
47
|
|
|
41
48
|
const {
|
|
@@ -51,10 +58,13 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
51
58
|
headerStyle,
|
|
52
59
|
headerShadowVisible,
|
|
53
60
|
headerTransparent,
|
|
54
|
-
contentStyle,
|
|
55
61
|
headerBackTitle,
|
|
62
|
+
presentation,
|
|
63
|
+
contentStyle,
|
|
56
64
|
} = options;
|
|
57
65
|
|
|
66
|
+
const nextPresentation = nexDescriptor?.options.presentation;
|
|
67
|
+
|
|
58
68
|
return (
|
|
59
69
|
<Screen
|
|
60
70
|
key={route.key}
|
|
@@ -115,7 +125,8 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
115
125
|
}
|
|
116
126
|
headerRight={
|
|
117
127
|
typeof headerRight === 'function'
|
|
118
|
-
? ({ tintColor }) =>
|
|
128
|
+
? ({ tintColor }) =>
|
|
129
|
+
headerRight({ tintColor, canGoBack })
|
|
119
130
|
: headerRight
|
|
120
131
|
}
|
|
121
132
|
headerTitle={
|
|
@@ -143,7 +154,18 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
143
154
|
}
|
|
144
155
|
style={[
|
|
145
156
|
StyleSheet.absoluteFill,
|
|
146
|
-
{
|
|
157
|
+
{
|
|
158
|
+
display:
|
|
159
|
+
isFocused ||
|
|
160
|
+
(nextPresentation != null &&
|
|
161
|
+
TRANSPARENT_PRESENTATIONS.includes(nextPresentation))
|
|
162
|
+
? 'flex'
|
|
163
|
+
: 'none',
|
|
164
|
+
},
|
|
165
|
+
presentation != null &&
|
|
166
|
+
TRANSPARENT_PRESENTATIONS.includes(presentation)
|
|
167
|
+
? { backgroundColor: 'transparent' }
|
|
168
|
+
: null,
|
|
147
169
|
]}
|
|
148
170
|
>
|
|
149
171
|
<View style={[styles.contentContainer, contentStyle]}>
|