@react-navigation/native-stack 7.0.0-alpha.4 → 7.0.0-alpha.6

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 (77) hide show
  1. package/lib/commonjs/index.js +7 -0
  2. package/lib/commonjs/index.js.map +1 -1
  3. package/lib/commonjs/navigators/createNativeStackNavigator.js +21 -23
  4. package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
  5. package/lib/commonjs/types.js.map +1 -1
  6. package/lib/commonjs/utils/useAnimatedHeaderHeight.js +19 -0
  7. package/lib/commonjs/utils/useAnimatedHeaderHeight.js.map +1 -0
  8. package/lib/commonjs/utils/useDismissedRouteError.js +3 -4
  9. package/lib/commonjs/utils/useDismissedRouteError.js.map +1 -1
  10. package/lib/commonjs/utils/useInvalidPreventRemoveError.js +4 -5
  11. package/lib/commonjs/utils/useInvalidPreventRemoveError.js.map +1 -1
  12. package/lib/commonjs/views/DebugContainer.js +2 -2
  13. package/lib/commonjs/views/DebugContainer.js.map +1 -1
  14. package/lib/commonjs/views/DebugContainer.native.js +3 -3
  15. package/lib/commonjs/views/DebugContainer.native.js.map +1 -1
  16. package/lib/commonjs/views/FontProcessor.js.map +1 -1
  17. package/lib/commonjs/views/FontProcessor.native.js +2 -4
  18. package/lib/commonjs/views/FontProcessor.native.js.map +1 -1
  19. package/lib/commonjs/views/HeaderConfig.js +6 -22
  20. package/lib/commonjs/views/HeaderConfig.js.map +1 -1
  21. package/lib/commonjs/views/NativeStackView.js +15 -10
  22. package/lib/commonjs/views/NativeStackView.js.map +1 -1
  23. package/lib/commonjs/views/NativeStackView.native.js +70 -28
  24. package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
  25. package/lib/module/index.js +5 -0
  26. package/lib/module/index.js.map +1 -1
  27. package/lib/module/navigators/createNativeStackNavigator.js +18 -19
  28. package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
  29. package/lib/module/types.js.map +1 -1
  30. package/lib/module/utils/useAnimatedHeaderHeight.js +10 -0
  31. package/lib/module/utils/useAnimatedHeaderHeight.js.map +1 -0
  32. package/lib/module/utils/useDismissedRouteError.js +1 -2
  33. package/lib/module/utils/useDismissedRouteError.js.map +1 -1
  34. package/lib/module/utils/useInvalidPreventRemoveError.js +2 -3
  35. package/lib/module/utils/useInvalidPreventRemoveError.js.map +1 -1
  36. package/lib/module/views/DebugContainer.js.map +1 -1
  37. package/lib/module/views/DebugContainer.native.js +1 -1
  38. package/lib/module/views/DebugContainer.native.js.map +1 -1
  39. package/lib/module/views/FontProcessor.js.map +1 -1
  40. package/lib/module/views/FontProcessor.native.js +2 -4
  41. package/lib/module/views/FontProcessor.native.js.map +1 -1
  42. package/lib/module/views/HeaderConfig.js +4 -20
  43. package/lib/module/views/HeaderConfig.js.map +1 -1
  44. package/lib/module/views/NativeStackView.js +13 -8
  45. package/lib/module/views/NativeStackView.js.map +1 -1
  46. package/lib/module/views/NativeStackView.native.js +69 -27
  47. package/lib/module/views/NativeStackView.native.js.map +1 -1
  48. package/lib/typescript/src/index.d.ts +4 -0
  49. package/lib/typescript/src/index.d.ts.map +1 -1
  50. package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts +4 -3
  51. package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
  52. package/lib/typescript/src/types.d.ts +8 -4
  53. package/lib/typescript/src/types.d.ts.map +1 -1
  54. package/lib/typescript/src/utils/useAnimatedHeaderHeight.d.ts +5 -0
  55. package/lib/typescript/src/utils/useAnimatedHeaderHeight.d.ts.map +1 -0
  56. package/lib/typescript/src/views/DebugContainer.d.ts +2 -2
  57. package/lib/typescript/src/views/DebugContainer.d.ts.map +1 -1
  58. package/lib/typescript/src/views/DebugContainer.native.d.ts +2 -2
  59. package/lib/typescript/src/views/DebugContainer.native.d.ts.map +1 -1
  60. package/lib/typescript/src/views/FontProcessor.native.d.ts.map +1 -1
  61. package/lib/typescript/src/views/HeaderConfig.d.ts +3 -2
  62. package/lib/typescript/src/views/HeaderConfig.d.ts.map +1 -1
  63. package/lib/typescript/src/views/NativeStackView.d.ts +3 -2
  64. package/lib/typescript/src/views/NativeStackView.d.ts.map +1 -1
  65. package/lib/typescript/src/views/NativeStackView.native.d.ts +3 -2
  66. package/lib/typescript/src/views/NativeStackView.native.d.ts.map +1 -1
  67. package/package.json +14 -15
  68. package/src/index.tsx +5 -0
  69. package/src/navigators/createNativeStackNavigator.tsx +7 -5
  70. package/src/types.tsx +10 -6
  71. package/src/utils/useAnimatedHeaderHeight.tsx +18 -0
  72. package/src/views/DebugContainer.native.tsx +2 -2
  73. package/src/views/DebugContainer.tsx +1 -1
  74. package/src/views/FontProcessor.native.tsx +1 -2
  75. package/src/views/HeaderConfig.tsx +100 -125
  76. package/src/views/NativeStackView.native.tsx +159 -91
  77. package/src/views/NativeStackView.tsx +12 -5
@@ -9,24 +9,30 @@ import {
9
9
  import {
10
10
  NavigationContext,
11
11
  NavigationRouteContext,
12
- ParamListBase,
13
- Route,
12
+ type ParamListBase,
13
+ type Route,
14
14
  StackActions,
15
- StackNavigationState,
15
+ type StackNavigationState,
16
16
  usePreventRemoveContext,
17
17
  useTheme,
18
18
  } from '@react-navigation/native';
19
19
  import * as React from 'react';
20
- import { Platform, StyleSheet, View } from 'react-native';
20
+ import {
21
+ Animated,
22
+ Platform,
23
+ StyleSheet,
24
+ useAnimatedValue,
25
+ View,
26
+ } from 'react-native';
21
27
  import {
22
28
  useSafeAreaFrame,
23
29
  useSafeAreaInsets,
24
30
  } from 'react-native-safe-area-context';
25
- import type { ScreenProps } from 'react-native-screens';
26
31
  import {
27
32
  Screen,
33
+ type ScreenProps,
28
34
  ScreenStack,
29
- StackPresentationTypes,
35
+ type StackPresentationTypes,
30
36
  } from 'react-native-screens';
31
37
  import warnOnce from 'warn-once';
32
38
 
@@ -36,6 +42,7 @@ import type {
36
42
  NativeStackNavigationHelpers,
37
43
  NativeStackNavigationOptions,
38
44
  } from '../types';
45
+ import { AnimatedHeaderHeightContext } from '../utils/useAnimatedHeaderHeight';
39
46
  import { useDismissedRouteError } from '../utils/useDismissedRouteError';
40
47
  import { useInvalidPreventRemoveError } from '../utils/useInvalidPreventRemoveError';
41
48
  import { DebugContainer } from './DebugContainer';
@@ -142,13 +149,23 @@ const SceneView = ({
142
149
  onNativeDismissCancelled,
143
150
  }: SceneViewProps) => {
144
151
  const { route, navigation, options, render } = descriptor;
152
+
153
+ let {
154
+ animation,
155
+ animationMatchesGesture,
156
+ fullScreenGestureEnabled,
157
+ presentation = 'card',
158
+ } = options;
159
+
145
160
  const {
146
161
  animationDuration,
147
162
  animationTypeForReplace = 'push',
148
163
  gestureEnabled,
164
+ gestureDirection = presentation === 'card' ? 'horizontal' : 'vertical',
149
165
  header,
150
166
  headerBackButtonMenuEnabled,
151
167
  headerShown,
168
+ headerBackground,
152
169
  headerTransparent,
153
170
  autoHideHomeIndicator,
154
171
  navigationBarColor,
@@ -162,26 +179,20 @@ const SceneView = ({
162
179
  freezeOnBlur,
163
180
  } = options;
164
181
 
165
- let {
166
- animation,
167
- customAnimationOnGesture,
168
- fullScreenGestureEnabled,
169
- presentation = 'card',
170
- gestureDirection = presentation === 'card' ? 'horizontal' : 'vertical',
171
- } = options;
172
-
173
182
  if (gestureDirection === 'vertical' && Platform.OS === 'ios') {
174
183
  // for `vertical` direction to work, we need to set `fullScreenGestureEnabled` to `true`
175
184
  // so the screen can be dismissed from any point on screen.
176
- // `customAnimationOnGesture` needs to be set to `true` so the `animation` set by user can be used,
185
+ // `animationMatchesGesture` needs to be set to `true` so the `animation` set by user can be used,
177
186
  // otherwise `simple_push` will be used.
178
187
  // Also, the default animation for this direction seems to be `slide_from_bottom`.
179
188
  if (fullScreenGestureEnabled === undefined) {
180
189
  fullScreenGestureEnabled = true;
181
190
  }
182
- if (customAnimationOnGesture === undefined) {
183
- customAnimationOnGesture = true;
191
+
192
+ if (animationMatchesGesture === undefined) {
193
+ animationMatchesGesture = true;
184
194
  }
195
+
185
196
  if (animation === undefined) {
186
197
  animation = 'slide_from_bottom';
187
198
  }
@@ -220,23 +231,38 @@ const SceneView = ({
220
231
  ? 0
221
232
  : insets.top;
222
233
 
234
+ // On models with Dynamic Island the status bar height is smaller than the safe area top inset.
235
+ const hasDynamicIsland = Platform.OS === 'ios' && topInset > 50;
236
+ const statusBarHeight = hasDynamicIsland ? topInset - 5 : topInset;
237
+
223
238
  const { preventedRoutes } = usePreventRemoveContext();
224
239
 
225
- const defaultHeaderHeight = getDefaultHeaderHeight(frame, isModal, topInset);
240
+ const defaultHeaderHeight = getDefaultHeaderHeight(
241
+ frame,
242
+ isModal,
243
+ statusBarHeight
244
+ );
226
245
 
227
246
  const [customHeaderHeight, setCustomHeaderHeight] =
228
247
  React.useState(defaultHeaderHeight);
229
248
 
249
+ const animatedHeaderHeight = useAnimatedValue(defaultHeaderHeight);
250
+
230
251
  const headerTopInsetEnabled = topInset !== 0;
231
252
  const headerHeight = header ? customHeaderHeight : defaultHeaderHeight;
232
- const headerBack = previousDescriptor
233
- ? {
234
- title: getHeaderTitle(
235
- previousDescriptor.options,
236
- previousDescriptor.route.name
237
- ),
238
- }
239
- : parentHeaderBack;
253
+
254
+ const backTitle = previousDescriptor
255
+ ? getHeaderTitle(previousDescriptor.options, previousDescriptor.route.name)
256
+ : parentHeaderBack?.title;
257
+
258
+ const headerBack = React.useMemo(
259
+ () => ({
260
+ // No href needed for native
261
+ href: undefined,
262
+ title: backTitle,
263
+ }),
264
+ [backTitle]
265
+ );
240
266
 
241
267
  const isRemovePrevented = preventedRoutes[route.key]?.preventRemove;
242
268
 
@@ -245,7 +271,7 @@ const SceneView = ({
245
271
  key={route.key}
246
272
  enabled
247
273
  style={StyleSheet.absoluteFill}
248
- customAnimationOnSwipe={customAnimationOnGesture}
274
+ customAnimationOnSwipe={animationMatchesGesture}
249
275
  fullScreenSwipeEnabled={fullScreenGestureEnabled}
250
276
  gestureEnabled={
251
277
  isAndroid
@@ -275,9 +301,23 @@ const SceneView = ({
275
301
  isNativeStack
276
302
  nativeBackButtonDismissalEnabled={false} // on Android
277
303
  onHeaderBackButtonClicked={onHeaderBackButtonClicked}
278
- // @ts-ignore props not exported from rn-screens
279
304
  preventNativeDismiss={isRemovePrevented} // on iOS
280
305
  onNativeDismissCancelled={onNativeDismissCancelled}
306
+ // @ts-expect-error this prop is available since rn-screens 3.26
307
+ // Unfortunately, because of the bug that exists on Fabric, where native event drivers
308
+ // for Animated objects are being created after the first notifications about the header height
309
+ // from the native side, `onHeaderHeightChange` event does not notify
310
+ // `animatedHeaderHeight` about initial values on appearing screens at the moment.
311
+ onHeaderHeightChange={Animated.event(
312
+ [
313
+ {
314
+ nativeEvent: {
315
+ headerHeight: animatedHeaderHeight,
316
+ },
317
+ },
318
+ ],
319
+ { useNativeDriver: true }
320
+ )}
281
321
  // this prop is available since rn-screens 3.16
282
322
  freezeOnBlur={freezeOnBlur}
283
323
  >
@@ -286,75 +326,92 @@ const SceneView = ({
286
326
  <HeaderShownContext.Provider
287
327
  value={isParentHeaderShown || headerShown !== false}
288
328
  >
289
- <HeaderHeightContext.Provider
290
- value={
291
- headerShown !== false ? headerHeight : parentHeaderHeight ?? 0
292
- }
293
- >
294
- <View
295
- accessibilityElementsHidden={!focused}
296
- importantForAccessibility={
297
- focused ? 'auto' : 'no-hide-descendants'
329
+ <AnimatedHeaderHeightContext.Provider value={animatedHeaderHeight}>
330
+ <HeaderHeightContext.Provider
331
+ value={
332
+ headerShown !== false ? headerHeight : parentHeaderHeight ?? 0
298
333
  }
299
- style={styles.scene}
300
334
  >
301
- <MaybeNestedStack
302
- options={options}
303
- route={route}
304
- presentation={presentation}
305
- headerHeight={headerHeight}
306
- headerTopInsetEnabled={headerTopInsetEnabled}
307
- >
308
- <HeaderBackContext.Provider value={headerBack}>
309
- {render()}
310
- </HeaderBackContext.Provider>
311
- </MaybeNestedStack>
312
- {header !== undefined && headerShown !== false ? (
335
+ {headerBackground != null ? (
336
+ /**
337
+ * To show a custom header background, we render it at the top of the screen below the header
338
+ * The header also needs to be positioned absolutely (with `translucent` style)
339
+ */
313
340
  <View
314
- onLayout={(e) => {
315
- setCustomHeaderHeight(e.nativeEvent.layout.height);
316
- }}
317
- style={headerTransparent ? styles.absolute : null}
341
+ style={[
342
+ styles.background,
343
+ headerTransparent ? styles.translucent : null,
344
+ { height: headerHeight },
345
+ ]}
318
346
  >
319
- {header({
320
- back: headerBack,
321
- options,
322
- route,
323
- navigation,
324
- })}
347
+ {headerBackground()}
325
348
  </View>
326
349
  ) : null}
327
- </View>
328
- {/**
329
- * `HeaderConfig` needs to be the direct child of `Screen` without any intermediate `View`
330
- * We don't render it conditionally to make it possible to dynamically render a custom `header`
331
- * Otherwise dynamically rendering a custom `header` leaves the native header visible
332
- *
333
- * https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md#screenstackheaderconfig
334
- *
335
- * HeaderConfig must not be first child of a Screen.
336
- * See https://github.com/software-mansion/react-native-screens/pull/1825
337
- * for detailed explanation
338
- */}
339
- <HeaderConfig
340
- {...options}
341
- route={route}
342
- headerBackButtonMenuEnabled={
343
- isRemovePrevented !== undefined
344
- ? !isRemovePrevented
345
- : headerBackButtonMenuEnabled
346
- }
347
- headerShown={header !== undefined ? false : headerShown}
348
- headerHeight={headerHeight}
349
- headerBackTitle={
350
- options.headerBackTitle !== undefined
351
- ? options.headerBackTitle
352
- : undefined
353
- }
354
- headerTopInsetEnabled={headerTopInsetEnabled}
355
- canGoBack={headerBack !== undefined}
356
- />
357
- </HeaderHeightContext.Provider>
350
+ <View
351
+ accessibilityElementsHidden={!focused}
352
+ importantForAccessibility={
353
+ focused ? 'auto' : 'no-hide-descendants'
354
+ }
355
+ style={styles.scene}
356
+ >
357
+ <MaybeNestedStack
358
+ options={options}
359
+ route={route}
360
+ presentation={presentation}
361
+ headerHeight={headerHeight}
362
+ headerTopInsetEnabled={headerTopInsetEnabled}
363
+ >
364
+ <HeaderBackContext.Provider value={headerBack}>
365
+ {render()}
366
+ </HeaderBackContext.Provider>
367
+ </MaybeNestedStack>
368
+ {header !== undefined && headerShown !== false ? (
369
+ <View
370
+ onLayout={(e) => {
371
+ setCustomHeaderHeight(e.nativeEvent.layout.height);
372
+ }}
373
+ style={headerTransparent ? styles.absolute : null}
374
+ >
375
+ {header({
376
+ back: headerBack,
377
+ options,
378
+ route,
379
+ navigation,
380
+ })}
381
+ </View>
382
+ ) : null}
383
+ </View>
384
+ {/**
385
+ * `HeaderConfig` needs to be the direct child of `Screen` without any intermediate `View`
386
+ * We don't render it conditionally to make it possible to dynamically render a custom `header`
387
+ * Otherwise dynamically rendering a custom `header` leaves the native header visible
388
+ *
389
+ * https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md#screenstackheaderconfig
390
+ *
391
+ * HeaderConfig must not be first child of a Screen.
392
+ * See https://github.com/software-mansion/react-native-screens/pull/1825
393
+ * for detailed explanation
394
+ */}
395
+ <HeaderConfig
396
+ {...options}
397
+ route={route}
398
+ headerBackButtonMenuEnabled={
399
+ isRemovePrevented !== undefined
400
+ ? !isRemovePrevented
401
+ : headerBackButtonMenuEnabled
402
+ }
403
+ headerShown={header !== undefined ? false : headerShown}
404
+ headerHeight={headerHeight}
405
+ headerBackTitle={
406
+ options.headerBackTitle !== undefined
407
+ ? options.headerBackTitle
408
+ : undefined
409
+ }
410
+ headerTopInsetEnabled={headerTopInsetEnabled}
411
+ canGoBack={headerBack !== undefined}
412
+ />
413
+ </HeaderHeightContext.Provider>
414
+ </AnimatedHeaderHeightContext.Provider>
358
415
  </HeaderShownContext.Provider>
359
416
  </NavigationRouteContext.Provider>
360
417
  </NavigationContext.Provider>
@@ -466,4 +523,15 @@ const styles = StyleSheet.create({
466
523
  left: 0,
467
524
  right: 0,
468
525
  },
526
+ translucent: {
527
+ position: 'absolute',
528
+ top: 0,
529
+ left: 0,
530
+ right: 0,
531
+ zIndex: 1,
532
+ elevation: 1,
533
+ },
534
+ background: {
535
+ overflow: 'hidden',
536
+ },
469
537
  });
@@ -6,9 +6,10 @@ import {
6
6
  SafeAreaProviderCompat,
7
7
  Screen,
8
8
  } from '@react-navigation/elements';
9
- import type {
10
- ParamListBase,
11
- StackNavigationState,
9
+ import {
10
+ type ParamListBase,
11
+ type StackNavigationState,
12
+ useLinkTools,
12
13
  } from '@react-navigation/native';
13
14
  import * as React from 'react';
14
15
  import { Image, StyleSheet, View } from 'react-native';
@@ -33,6 +34,7 @@ const TRANSPARENT_PRESENTATIONS = [
33
34
 
34
35
  export function NativeStackView({ state, descriptors }: Props) {
35
36
  const parentHeaderBack = React.useContext(HeaderBackContext);
37
+ const { buildHref } = useLinkTools();
36
38
 
37
39
  return (
38
40
  <SafeAreaProviderCompat>
@@ -53,6 +55,10 @@ export function NativeStackView({ state, descriptors }: Props) {
53
55
  previousDescriptor.options,
54
56
  previousDescriptor.route.name
55
57
  ),
58
+ href: buildHref(
59
+ previousDescriptor.route.name,
60
+ previousDescriptor.route.params
61
+ ),
56
62
  }
57
63
  : parentHeaderBack;
58
64
 
@@ -116,6 +122,7 @@ export function NativeStackView({ state, descriptors }: Props) {
116
122
  ? () => (
117
123
  <Image
118
124
  source={headerBackImageSource}
125
+ resizeMode="contain"
119
126
  style={[
120
127
  styles.backImage,
121
128
  { tintColor },
@@ -124,8 +131,9 @@ export function NativeStackView({ state, descriptors }: Props) {
124
131
  )
125
132
  : undefined
126
133
  }
127
- onPress={navigation.goBack}
128
134
  canGoBack={canGoBack}
135
+ onPress={navigation.goBack}
136
+ href={headerBack.href}
129
137
  />
130
138
  )
131
139
  : headerLeft
@@ -191,6 +199,5 @@ const styles = StyleSheet.create({
191
199
  height: 24,
192
200
  width: 24,
193
201
  margin: 3,
194
- resizeMode: 'contain',
195
202
  },
196
203
  });