@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.
Files changed (30) hide show
  1. package/lib/commonjs/index.js +8 -0
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/navigators/createNativeStackNavigator.js +12 -10
  4. package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
  5. package/lib/commonjs/views/HeaderConfig.js +62 -39
  6. package/lib/commonjs/views/HeaderConfig.js.map +1 -1
  7. package/lib/commonjs/views/NativeStackView.js +62 -40
  8. package/lib/commonjs/views/NativeStackView.js.map +1 -1
  9. package/lib/commonjs/views/NativeStackView.native.js +57 -41
  10. package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
  11. package/lib/module/index.js +5 -0
  12. package/lib/module/index.js.map +1 -1
  13. package/lib/module/navigators/createNativeStackNavigator.js +12 -10
  14. package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
  15. package/lib/module/views/HeaderConfig.js +62 -39
  16. package/lib/module/views/HeaderConfig.js.map +1 -1
  17. package/lib/module/views/NativeStackView.js +61 -40
  18. package/lib/module/views/NativeStackView.js.map +1 -1
  19. package/lib/module/views/NativeStackView.native.js +59 -43
  20. package/lib/module/views/NativeStackView.native.js.map +1 -1
  21. package/lib/typescript/src/index.d.ts +5 -1
  22. package/lib/typescript/src/types.d.ts +40 -11
  23. package/lib/typescript/src/views/HeaderConfig.d.ts +2 -1
  24. package/package.json +6 -6
  25. package/src/index.tsx +6 -0
  26. package/src/navigators/createNativeStackNavigator.tsx +21 -18
  27. package/src/types.tsx +41 -9
  28. package/src/views/HeaderConfig.tsx +153 -103
  29. package/src/views/NativeStackView.native.tsx +65 -47
  30. 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, PlatformIOSStatic, StyleSheet } from 'react-native';
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
- <HeaderShownContext.Provider value>
108
- <HeaderHeightContext.Provider value={headerHeight}>
109
- <HeaderConfig {...options} route={route} canGoBack />
110
- {content}
111
- </HeaderHeightContext.Provider>
112
- </HeaderShownContext.Provider>
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
- const headerHeight = getDefaultHeaderHeight(
170
- useSafeAreaFrame(),
171
- false,
172
- insets.top
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
- // TODO: expose custom header height
209
- header({
210
- back: previousDescriptor
211
- ? {
212
- title: getHeaderTitle(
213
- previousDescriptor.options,
214
- previousDescriptor.route.name
215
- ),
216
- }
217
- : undefined,
218
- options,
219
- route,
220
- navigation,
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
- React.useState<string | null>(null);
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 }) => headerRight({ 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
- { display: isFocused ? 'flex' : 'none' },
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]}>