@react-navigation/native-stack 7.0.0-alpha.1 → 7.0.0-alpha.10

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 +24 -24
  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 +19 -7
  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 +10 -23
  20. package/lib/commonjs/views/HeaderConfig.js.map +1 -1
  21. package/lib/commonjs/views/NativeStackView.js +23 -11
  22. package/lib/commonjs/views/NativeStackView.js.map +1 -1
  23. package/lib/commonjs/views/NativeStackView.native.js +120 -42
  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 +21 -20
  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 +18 -4
  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 +10 -23
  43. package/lib/module/views/HeaderConfig.js.map +1 -1
  44. package/lib/module/views/NativeStackView.js +21 -9
  45. package/lib/module/views/NativeStackView.js.map +1 -1
  46. package/lib/module/views/NativeStackView.native.js +119 -41
  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 -4
  51. package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
  52. package/lib/typescript/src/types.d.ts +88 -10
  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 +7 -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 +2 -3
  62. package/lib/typescript/src/views/HeaderConfig.d.ts.map +1 -1
  63. package/lib/typescript/src/views/NativeStackView.d.ts +3 -3
  64. package/lib/typescript/src/views/NativeStackView.d.ts.map +1 -1
  65. package/lib/typescript/src/views/NativeStackView.native.d.ts +3 -3
  66. package/lib/typescript/src/views/NativeStackView.native.d.ts.map +1 -1
  67. package/package.json +16 -17
  68. package/src/index.tsx +5 -0
  69. package/src/navigators/createNativeStackNavigator.tsx +9 -5
  70. package/src/types.tsx +88 -11
  71. package/src/utils/useAnimatedHeaderHeight.tsx +18 -0
  72. package/src/views/DebugContainer.native.tsx +12 -6
  73. package/src/views/DebugContainer.tsx +1 -1
  74. package/src/views/FontProcessor.native.tsx +1 -2
  75. package/src/views/HeaderConfig.tsx +101 -131
  76. package/src/views/NativeStackView.native.tsx +198 -92
  77. package/src/views/NativeStackView.tsx +40 -26
@@ -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';
@@ -97,7 +104,13 @@ const MaybeNestedStack = ({
97
104
  if (isHeaderInModal) {
98
105
  return (
99
106
  <ScreenStack style={styles.container}>
100
- <Screen enabled style={StyleSheet.absoluteFill}>
107
+ <Screen
108
+ enabled
109
+ isNativeStack
110
+ hasLargeHeader={options.headerLargeTitle ?? false}
111
+ style={StyleSheet.absoluteFill}
112
+ >
113
+ {content}
101
114
  <HeaderConfig
102
115
  {...options}
103
116
  route={route}
@@ -105,7 +118,6 @@ const MaybeNestedStack = ({
105
118
  headerTopInsetEnabled={headerTopInsetEnabled}
106
119
  canGoBack
107
120
  />
108
- {content}
109
121
  </Screen>
110
122
  </ScreenStack>
111
123
  );
@@ -121,11 +133,13 @@ type SceneViewProps = {
121
133
  previousDescriptor?: NativeStackDescriptor;
122
134
  nextDescriptor?: NativeStackDescriptor;
123
135
  onWillDisappear: () => void;
136
+ onWillAppear: () => void;
124
137
  onAppear: () => void;
125
138
  onDisappear: () => void;
126
139
  onDismissed: ScreenProps['onDismissed'];
127
140
  onHeaderBackButtonClicked: ScreenProps['onHeaderBackButtonClicked'];
128
141
  onNativeDismissCancelled: ScreenProps['onDismissed'];
142
+ onGestureCancel: ScreenProps['onGestureCancel'];
129
143
  };
130
144
 
131
145
  const SceneView = ({
@@ -135,53 +149,66 @@ const SceneView = ({
135
149
  previousDescriptor,
136
150
  nextDescriptor,
137
151
  onWillDisappear,
152
+ onWillAppear,
138
153
  onAppear,
139
154
  onDisappear,
140
155
  onDismissed,
141
156
  onHeaderBackButtonClicked,
142
157
  onNativeDismissCancelled,
158
+ onGestureCancel,
143
159
  }: SceneViewProps) => {
144
160
  const { route, navigation, options, render } = descriptor;
161
+
162
+ let {
163
+ animation,
164
+ animationMatchesGesture,
165
+ fullScreenGestureEnabled,
166
+ presentation = 'card',
167
+ } = options;
168
+
145
169
  const {
146
170
  animationDuration,
147
171
  animationTypeForReplace = 'push',
148
172
  gestureEnabled,
173
+ gestureDirection = presentation === 'card' ? 'horizontal' : 'vertical',
174
+ gestureResponseDistance,
149
175
  header,
150
176
  headerBackButtonMenuEnabled,
151
177
  headerShown,
178
+ headerBackground,
152
179
  headerTransparent,
153
180
  autoHideHomeIndicator,
181
+ keyboardHandlingEnabled,
154
182
  navigationBarColor,
155
183
  navigationBarHidden,
156
184
  orientation,
185
+ sheetAllowedDetents = 'large',
186
+ sheetLargestUndimmedDetent = 'all',
187
+ sheetGrabberVisible = false,
188
+ sheetCornerRadius = -1.0,
189
+ sheetExpandsWhenScrolledToEdge = true,
157
190
  statusBarAnimation,
158
191
  statusBarHidden,
159
192
  statusBarStyle,
160
193
  statusBarTranslucent,
161
- statusBarColor,
194
+ statusBarBackgroundColor,
162
195
  freezeOnBlur,
163
196
  } = options;
164
197
 
165
- let {
166
- animation,
167
- customAnimationOnGesture,
168
- fullScreenGestureEnabled,
169
- presentation = 'card',
170
- gestureDirection = presentation === 'card' ? 'horizontal' : 'vertical',
171
- } = options;
172
-
173
198
  if (gestureDirection === 'vertical' && Platform.OS === 'ios') {
174
199
  // for `vertical` direction to work, we need to set `fullScreenGestureEnabled` to `true`
175
200
  // 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,
201
+ // `animationMatchesGesture` needs to be set to `true` so the `animation` set by user can be used,
177
202
  // otherwise `simple_push` will be used.
178
203
  // Also, the default animation for this direction seems to be `slide_from_bottom`.
179
204
  if (fullScreenGestureEnabled === undefined) {
180
205
  fullScreenGestureEnabled = true;
181
206
  }
182
- if (customAnimationOnGesture === undefined) {
183
- customAnimationOnGesture = true;
207
+
208
+ if (animationMatchesGesture === undefined) {
209
+ animationMatchesGesture = true;
184
210
  }
211
+
185
212
  if (animation === undefined) {
186
213
  animation = 'slide_from_bottom';
187
214
  }
@@ -227,16 +254,23 @@ const SceneView = ({
227
254
  const [customHeaderHeight, setCustomHeaderHeight] =
228
255
  React.useState(defaultHeaderHeight);
229
256
 
257
+ const animatedHeaderHeight = useAnimatedValue(defaultHeaderHeight);
258
+
230
259
  const headerTopInsetEnabled = topInset !== 0;
231
260
  const headerHeight = header ? customHeaderHeight : defaultHeaderHeight;
232
- const headerBack = previousDescriptor
233
- ? {
234
- title: getHeaderTitle(
235
- previousDescriptor.options,
236
- previousDescriptor.route.name
237
- ),
238
- }
239
- : parentHeaderBack;
261
+
262
+ const backTitle = previousDescriptor
263
+ ? getHeaderTitle(previousDescriptor.options, previousDescriptor.route.name)
264
+ : parentHeaderBack?.title;
265
+
266
+ const headerBack = React.useMemo(
267
+ () => ({
268
+ // No href needed for native
269
+ href: undefined,
270
+ title: backTitle,
271
+ }),
272
+ [backTitle]
273
+ );
240
274
 
241
275
  const isRemovePrevented = preventedRoutes[route.key]?.preventRemove;
242
276
 
@@ -244,8 +278,10 @@ const SceneView = ({
244
278
  <Screen
245
279
  key={route.key}
246
280
  enabled
281
+ isNativeStack
247
282
  style={StyleSheet.absoluteFill}
248
- customAnimationOnSwipe={customAnimationOnGesture}
283
+ hasLargeHeader={options.headerLargeTitle ?? false}
284
+ customAnimationOnSwipe={animationMatchesGesture}
249
285
  fullScreenSwipeEnabled={fullScreenGestureEnabled}
250
286
  gestureEnabled={
251
287
  isAndroid
@@ -255,29 +291,50 @@ const SceneView = ({
255
291
  : gestureEnabled
256
292
  }
257
293
  homeIndicatorHidden={autoHideHomeIndicator}
294
+ hideKeyboardOnSwipe={keyboardHandlingEnabled}
258
295
  navigationBarColor={navigationBarColor}
259
296
  navigationBarHidden={navigationBarHidden}
260
297
  replaceAnimation={animationTypeForReplace}
261
298
  stackPresentation={presentation === 'card' ? 'push' : presentation}
262
299
  stackAnimation={animation}
263
300
  screenOrientation={orientation}
301
+ sheetAllowedDetents={sheetAllowedDetents}
302
+ sheetLargestUndimmedDetent={sheetLargestUndimmedDetent}
303
+ sheetGrabberVisible={sheetGrabberVisible}
304
+ sheetCornerRadius={sheetCornerRadius}
305
+ sheetExpandsWhenScrolledToEdge={sheetExpandsWhenScrolledToEdge}
264
306
  statusBarAnimation={statusBarAnimation}
265
307
  statusBarHidden={statusBarHidden}
266
308
  statusBarStyle={statusBarStyle}
267
- statusBarColor={statusBarColor}
309
+ statusBarColor={statusBarBackgroundColor}
268
310
  statusBarTranslucent={statusBarTranslucent}
269
311
  swipeDirection={gestureDirectionOverride}
270
312
  transitionDuration={animationDuration}
313
+ onWillAppear={onWillAppear}
271
314
  onWillDisappear={onWillDisappear}
272
315
  onAppear={onAppear}
273
316
  onDisappear={onDisappear}
274
317
  onDismissed={onDismissed}
275
- isNativeStack
318
+ onGestureCancel={onGestureCancel}
319
+ gestureResponseDistance={gestureResponseDistance}
276
320
  nativeBackButtonDismissalEnabled={false} // on Android
277
321
  onHeaderBackButtonClicked={onHeaderBackButtonClicked}
278
- // @ts-ignore props not exported from rn-screens
279
322
  preventNativeDismiss={isRemovePrevented} // on iOS
280
323
  onNativeDismissCancelled={onNativeDismissCancelled}
324
+ // Unfortunately, because of the bug that exists on Fabric, where native event drivers
325
+ // for Animated objects are being created after the first notifications about the header height
326
+ // from the native side, `onHeaderHeightChange` event does not notify
327
+ // `animatedHeaderHeight` about initial values on appearing screens at the moment.
328
+ onHeaderHeightChange={Animated.event(
329
+ [
330
+ {
331
+ nativeEvent: {
332
+ headerHeight: animatedHeaderHeight,
333
+ },
334
+ },
335
+ ],
336
+ { useNativeDriver: true }
337
+ )}
281
338
  // this prop is available since rn-screens 3.16
282
339
  freezeOnBlur={freezeOnBlur}
283
340
  >
@@ -286,71 +343,92 @@ const SceneView = ({
286
343
  <HeaderShownContext.Provider
287
344
  value={isParentHeaderShown || headerShown !== false}
288
345
  >
289
- <HeaderHeightContext.Provider
290
- value={
291
- headerShown !== false ? headerHeight : parentHeaderHeight ?? 0
292
- }
293
- >
294
- {/**
295
- * `HeaderConfig` needs to be the direct child of `Screen` without any intermediate `View`
296
- * We don't render it conditionally to make it possible to dynamically render a custom `header`
297
- * Otherwise dynamically rendering a custom `header` leaves the native header visible
298
- *
299
- * https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md#screenstackheaderconfig
300
- */}
301
- <HeaderConfig
302
- {...options}
303
- route={route}
304
- headerBackButtonMenuEnabled={
305
- isRemovePrevented !== undefined
306
- ? !isRemovePrevented
307
- : headerBackButtonMenuEnabled
308
- }
309
- headerShown={header !== undefined ? false : headerShown}
310
- headerHeight={headerHeight}
311
- headerBackTitle={
312
- options.headerBackTitle !== undefined
313
- ? options.headerBackTitle
314
- : undefined
315
- }
316
- headerTopInsetEnabled={headerTopInsetEnabled}
317
- canGoBack={headerBack !== undefined}
318
- />
319
- <View
320
- accessibilityElementsHidden={!focused}
321
- importantForAccessibility={
322
- focused ? 'auto' : 'no-hide-descendants'
346
+ <AnimatedHeaderHeightContext.Provider value={animatedHeaderHeight}>
347
+ <HeaderHeightContext.Provider
348
+ value={
349
+ headerShown !== false ? headerHeight : parentHeaderHeight ?? 0
323
350
  }
324
- style={styles.scene}
325
351
  >
326
- <MaybeNestedStack
327
- options={options}
328
- route={route}
329
- presentation={presentation}
330
- headerHeight={headerHeight}
331
- headerTopInsetEnabled={headerTopInsetEnabled}
332
- >
333
- <HeaderBackContext.Provider value={headerBack}>
334
- {render()}
335
- </HeaderBackContext.Provider>
336
- </MaybeNestedStack>
337
- {header !== undefined && headerShown !== false ? (
352
+ {headerBackground != null ? (
353
+ /**
354
+ * To show a custom header background, we render it at the top of the screen below the header
355
+ * The header also needs to be positioned absolutely (with `translucent` style)
356
+ */
338
357
  <View
339
- onLayout={(e) => {
340
- setCustomHeaderHeight(e.nativeEvent.layout.height);
341
- }}
342
- style={headerTransparent ? styles.absolute : null}
358
+ style={[
359
+ styles.background,
360
+ headerTransparent ? styles.translucent : null,
361
+ { height: headerHeight },
362
+ ]}
343
363
  >
344
- {header({
345
- back: headerBack,
346
- options,
347
- route,
348
- navigation,
349
- })}
364
+ {headerBackground()}
350
365
  </View>
351
366
  ) : null}
352
- </View>
353
- </HeaderHeightContext.Provider>
367
+ <View
368
+ accessibilityElementsHidden={!focused}
369
+ importantForAccessibility={
370
+ focused ? 'auto' : 'no-hide-descendants'
371
+ }
372
+ style={styles.scene}
373
+ >
374
+ <MaybeNestedStack
375
+ options={options}
376
+ route={route}
377
+ presentation={presentation}
378
+ headerHeight={headerHeight}
379
+ headerTopInsetEnabled={headerTopInsetEnabled}
380
+ >
381
+ <HeaderBackContext.Provider value={headerBack}>
382
+ {render()}
383
+ </HeaderBackContext.Provider>
384
+ </MaybeNestedStack>
385
+ {header !== undefined && headerShown !== false ? (
386
+ <View
387
+ onLayout={(e) => {
388
+ setCustomHeaderHeight(e.nativeEvent.layout.height);
389
+ }}
390
+ style={headerTransparent ? styles.absolute : null}
391
+ >
392
+ {header({
393
+ back: headerBack,
394
+ options,
395
+ route,
396
+ navigation,
397
+ })}
398
+ </View>
399
+ ) : null}
400
+ </View>
401
+ {/**
402
+ * `HeaderConfig` needs to be the direct child of `Screen` without any intermediate `View`
403
+ * We don't render it conditionally to make it possible to dynamically render a custom `header`
404
+ * Otherwise dynamically rendering a custom `header` leaves the native header visible
405
+ *
406
+ * https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md#screenstackheaderconfig
407
+ *
408
+ * HeaderConfig must not be first child of a Screen.
409
+ * See https://github.com/software-mansion/react-native-screens/pull/1825
410
+ * for detailed explanation
411
+ */}
412
+ <HeaderConfig
413
+ {...options}
414
+ route={route}
415
+ headerBackButtonMenuEnabled={
416
+ isRemovePrevented !== undefined
417
+ ? !isRemovePrevented
418
+ : headerBackButtonMenuEnabled
419
+ }
420
+ headerShown={header !== undefined ? false : headerShown}
421
+ headerHeight={headerHeight}
422
+ headerBackTitle={
423
+ options.headerBackTitle !== undefined
424
+ ? options.headerBackTitle
425
+ : undefined
426
+ }
427
+ headerTopInsetEnabled={headerTopInsetEnabled}
428
+ canGoBack={headerBack !== undefined}
429
+ />
430
+ </HeaderHeightContext.Provider>
431
+ </AnimatedHeaderHeightContext.Provider>
354
432
  </HeaderShownContext.Provider>
355
433
  </NavigationRouteContext.Provider>
356
434
  </NavigationContext.Provider>
@@ -367,10 +445,14 @@ type Props = {
367
445
  function NativeStackViewInner({ state, navigation, descriptors }: Props) {
368
446
  const { setNextDismissedKey } = useDismissedRouteError(state);
369
447
 
448
+ const { colors } = useTheme();
449
+
370
450
  useInvalidPreventRemoveError(descriptors);
371
451
 
372
452
  return (
373
- <ScreenStack style={styles.container}>
453
+ <ScreenStack
454
+ style={[styles.container, { backgroundColor: colors.background }]}
455
+ >
374
456
  {state.routes.map((route, index) => {
375
457
  const descriptor = descriptors[route.key];
376
458
  const isFocused = state.index === index;
@@ -396,6 +478,13 @@ function NativeStackViewInner({ state, navigation, descriptors }: Props) {
396
478
  target: route.key,
397
479
  });
398
480
  }}
481
+ onWillAppear={() => {
482
+ navigation.emit({
483
+ type: 'transitionStart',
484
+ data: { closing: false },
485
+ target: route.key,
486
+ });
487
+ }}
399
488
  onAppear={() => {
400
489
  navigation.emit({
401
490
  type: 'transitionEnd',
@@ -433,6 +522,12 @@ function NativeStackViewInner({ state, navigation, descriptors }: Props) {
433
522
  target: state.key,
434
523
  });
435
524
  }}
525
+ onGestureCancel={() => {
526
+ navigation.emit({
527
+ type: 'gestureCancel',
528
+ target: route.key,
529
+ });
530
+ }}
436
531
  />
437
532
  );
438
533
  })}
@@ -462,4 +557,15 @@ const styles = StyleSheet.create({
462
557
  left: 0,
463
558
  right: 0,
464
559
  },
560
+ translucent: {
561
+ position: 'absolute',
562
+ top: 0,
563
+ left: 0,
564
+ right: 0,
565
+ zIndex: 1,
566
+ elevation: 1,
567
+ },
568
+ background: {
569
+ overflow: 'hidden',
570
+ },
465
571
  });
@@ -6,9 +6,11 @@ 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
+ useLinkBuilder,
13
+ useTheme,
12
14
  } from '@react-navigation/native';
13
15
  import * as React from 'react';
14
16
  import { Image, StyleSheet, View } from 'react-native';
@@ -33,10 +35,18 @@ const TRANSPARENT_PRESENTATIONS = [
33
35
 
34
36
  export function NativeStackView({ state, descriptors }: Props) {
35
37
  const parentHeaderBack = React.useContext(HeaderBackContext);
38
+ const { buildHref } = useLinkBuilder();
39
+ const { colors } = useTheme();
40
+
41
+ if (state.preloadedRoutes.length !== 0) {
42
+ throw new Error(
43
+ 'Preloading routes is not supported in the NativeStackNavigator navigator.'
44
+ );
45
+ }
36
46
 
37
47
  return (
38
48
  <SafeAreaProviderCompat>
39
- <View style={styles.container}>
49
+ <View style={[styles.container, { backgroundColor: colors.background }]}>
40
50
  {state.routes.map((route, i) => {
41
51
  const isFocused = state.index === i;
42
52
  const previousKey = state.routes[i - 1]?.key;
@@ -53,6 +63,10 @@ export function NativeStackView({ state, descriptors }: Props) {
53
63
  previousDescriptor.options,
54
64
  previousDescriptor.route.name
55
65
  ),
66
+ href: buildHref(
67
+ previousDescriptor.route.name,
68
+ previousDescriptor.route.params
69
+ ),
56
70
  }
57
71
  : parentHeaderBack;
58
72
 
@@ -108,27 +122,28 @@ export function NativeStackView({ state, descriptors }: Props) {
108
122
  label: headerBackTitle,
109
123
  })
110
124
  : headerLeft === undefined && canGoBack
111
- ? ({ tintColor }) => (
112
- <HeaderBackButton
113
- tintColor={tintColor}
114
- backImage={
115
- headerBackImageSource !== undefined
116
- ? () => (
117
- <Image
118
- source={headerBackImageSource}
119
- style={[
120
- styles.backImage,
121
- { tintColor },
122
- ]}
123
- />
124
- )
125
- : undefined
126
- }
127
- onPress={navigation.goBack}
128
- canGoBack={canGoBack}
129
- />
130
- )
131
- : headerLeft
125
+ ? ({ tintColor }) => (
126
+ <HeaderBackButton
127
+ tintColor={tintColor}
128
+ backImage={
129
+ headerBackImageSource !== undefined
130
+ ? () => (
131
+ <Image
132
+ source={headerBackImageSource}
133
+ resizeMode="contain"
134
+ style={[
135
+ styles.backImage,
136
+ { tintColor },
137
+ ]}
138
+ />
139
+ )
140
+ : undefined
141
+ }
142
+ onPress={navigation.goBack}
143
+ href={headerBack.href}
144
+ />
145
+ )
146
+ : headerLeft
132
147
  }
133
148
  headerRight={
134
149
  typeof headerRight === 'function'
@@ -191,6 +206,5 @@ const styles = StyleSheet.create({
191
206
  height: 24,
192
207
  width: 24,
193
208
  margin: 3,
194
- resizeMode: 'contain',
195
209
  },
196
210
  });