@react-navigation/native-stack 8.0.0-alpha.4 → 8.0.0-alpha.40
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/module/index.js.map +1 -1
- package/lib/module/navigators/createNativeStackNavigator.js +4 -8
- package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/module/utils/useAnimatedHeaderHeight.js +1 -1
- package/lib/module/utils/useAnimatedHeaderHeight.js.map +1 -1
- package/lib/module/utils/useInvalidPreventRemoveError.js +1 -1
- package/lib/module/utils/useInvalidPreventRemoveError.js.map +1 -1
- package/lib/module/views/NativeStackView.js +39 -25
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +99 -68
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/module/views/useHeaderConfigProps.js +71 -21
- package/lib/module/views/useHeaderConfigProps.js.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts +10 -15
- package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +145 -148
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts.map +1 -1
- package/lib/typescript/src/utils/useInvalidPreventRemoveError.d.ts +1 -1
- package/lib/typescript/src/utils/useInvalidPreventRemoveError.d.ts.map +1 -1
- package/lib/typescript/src/views/FooterComponent.d.ts +1 -1
- package/lib/typescript/src/views/FooterComponent.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.d.ts +3 -2
- package/lib/typescript/src/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.native.d.ts +3 -2
- package/lib/typescript/src/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/src/views/useHeaderConfigProps.d.ts +1 -1
- package/lib/typescript/src/views/useHeaderConfigProps.d.ts.map +1 -1
- package/package.json +18 -19
- package/src/index.tsx +1 -0
- package/src/navigators/createNativeStackNavigator.tsx +11 -47
- package/src/types.tsx +206 -186
- package/src/utils/useAnimatedHeaderHeight.tsx +1 -1
- package/src/utils/useInvalidPreventRemoveError.tsx +2 -1
- package/src/views/NativeStackView.native.tsx +148 -89
- package/src/views/NativeStackView.tsx +61 -41
- package/src/views/useHeaderConfigProps.tsx +100 -36
|
@@ -6,7 +6,7 @@ export const AnimatedHeaderHeightContext = React.createContext<
|
|
|
6
6
|
>(undefined);
|
|
7
7
|
|
|
8
8
|
export function useAnimatedHeaderHeight() {
|
|
9
|
-
const animatedValue = React.
|
|
9
|
+
const animatedValue = React.use(AnimatedHeaderHeightContext);
|
|
10
10
|
|
|
11
11
|
if (animatedValue === undefined) {
|
|
12
12
|
throw new Error(
|
|
@@ -8,7 +8,8 @@ export function useInvalidPreventRemoveError(
|
|
|
8
8
|
) {
|
|
9
9
|
const { preventedRoutes } = usePreventRemoveContext();
|
|
10
10
|
const preventedRouteKey = Object.keys(preventedRoutes)[0];
|
|
11
|
-
const preventedDescriptor =
|
|
11
|
+
const preventedDescriptor =
|
|
12
|
+
preventedRouteKey == null ? undefined : descriptors[preventedRouteKey];
|
|
12
13
|
const isHeaderBackButtonMenuEnabledOnPreventedScreen =
|
|
13
14
|
preventedDescriptor?.options?.headerBackButtonMenuEnabled;
|
|
14
15
|
const preventedRouteName = preventedDescriptor?.route?.name;
|
|
@@ -6,7 +6,10 @@ import {
|
|
|
6
6
|
HeaderShownContext,
|
|
7
7
|
useFrameSize,
|
|
8
8
|
} from '@react-navigation/elements';
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
ActivityView,
|
|
11
|
+
SafeAreaProviderCompat,
|
|
12
|
+
} from '@react-navigation/elements/internal';
|
|
10
13
|
import {
|
|
11
14
|
NavigationProvider,
|
|
12
15
|
type ParamListBase,
|
|
@@ -44,19 +47,16 @@ import { useHeaderConfigProps } from './useHeaderConfigProps';
|
|
|
44
47
|
|
|
45
48
|
const ANDROID_DEFAULT_HEADER_HEIGHT = 56;
|
|
46
49
|
|
|
47
|
-
function isFabric() {
|
|
48
|
-
return 'nativeFabricUIManager' in global;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
50
|
type SceneViewProps = {
|
|
52
51
|
index: number;
|
|
53
52
|
focused: boolean;
|
|
54
|
-
shouldFreeze: boolean;
|
|
55
53
|
descriptor: NativeStackDescriptor;
|
|
56
|
-
previousDescriptor?: NativeStackDescriptor;
|
|
57
|
-
nextDescriptor?: NativeStackDescriptor;
|
|
58
|
-
isPresentationModal
|
|
59
|
-
|
|
54
|
+
previousDescriptor?: NativeStackDescriptor | undefined;
|
|
55
|
+
nextDescriptor?: NativeStackDescriptor | undefined;
|
|
56
|
+
isPresentationModal: boolean;
|
|
57
|
+
isNextScreenTransparent: boolean;
|
|
58
|
+
isInactive: boolean;
|
|
59
|
+
isBeforeLast: boolean;
|
|
60
60
|
onWillDisappear: () => void;
|
|
61
61
|
onWillAppear: () => void;
|
|
62
62
|
onAppear: () => void;
|
|
@@ -70,14 +70,20 @@ type SceneViewProps = {
|
|
|
70
70
|
|
|
71
71
|
const useNativeDriver = Platform.OS !== 'web';
|
|
72
72
|
|
|
73
|
+
const TRANSPARENT_PRESENTATIONS = [
|
|
74
|
+
'transparentModal',
|
|
75
|
+
'containedTransparentModal',
|
|
76
|
+
];
|
|
77
|
+
|
|
73
78
|
const SceneView = ({
|
|
74
79
|
index,
|
|
75
80
|
focused,
|
|
76
|
-
shouldFreeze,
|
|
77
81
|
descriptor,
|
|
78
82
|
previousDescriptor,
|
|
79
83
|
isPresentationModal,
|
|
80
|
-
|
|
84
|
+
isNextScreenTransparent,
|
|
85
|
+
isInactive,
|
|
86
|
+
isBeforeLast,
|
|
81
87
|
onWillDisappear,
|
|
82
88
|
onWillAppear,
|
|
83
89
|
onAppear,
|
|
@@ -91,6 +97,7 @@ const SceneView = ({
|
|
|
91
97
|
const { route, navigation, options, render } = descriptor;
|
|
92
98
|
|
|
93
99
|
const {
|
|
100
|
+
inactiveBehavior = 'pause',
|
|
94
101
|
animation,
|
|
95
102
|
animationDuration,
|
|
96
103
|
animationMatchesGesture,
|
|
@@ -123,7 +130,6 @@ const SceneView = ({
|
|
|
123
130
|
statusBarStyle,
|
|
124
131
|
unstable_sheetFooter,
|
|
125
132
|
scrollEdgeEffects,
|
|
126
|
-
freezeOnBlur,
|
|
127
133
|
contentStyle,
|
|
128
134
|
} = options;
|
|
129
135
|
|
|
@@ -147,9 +153,9 @@ const SceneView = ({
|
|
|
147
153
|
// Modals are fullscreen in landscape only on iPhone
|
|
148
154
|
const isIPhone = Platform.OS === 'ios' && !(Platform.isPad || Platform.isTV);
|
|
149
155
|
|
|
150
|
-
const isParentHeaderShown = React.
|
|
151
|
-
const parentHeaderHeight = React.
|
|
152
|
-
const parentHeaderBack = React.
|
|
156
|
+
const isParentHeaderShown = React.use(HeaderShownContext);
|
|
157
|
+
const parentHeaderHeight = React.use(HeaderHeightContext);
|
|
158
|
+
const parentHeaderBack = React.use(HeaderBackContext);
|
|
153
159
|
|
|
154
160
|
const isLandscape = useFrameSize((frame) => frame.width > frame.height);
|
|
155
161
|
|
|
@@ -226,6 +232,15 @@ const SceneView = ({
|
|
|
226
232
|
headerBack,
|
|
227
233
|
});
|
|
228
234
|
|
|
235
|
+
const headerContainerRef = React.useRef<View>(null);
|
|
236
|
+
|
|
237
|
+
React.useLayoutEffect(() => {
|
|
238
|
+
headerContainerRef.current?.measure((_x, _y, _width, height) => {
|
|
239
|
+
animatedHeaderHeight.setValue(height);
|
|
240
|
+
setHeaderHeight(height);
|
|
241
|
+
});
|
|
242
|
+
}, [animatedHeaderHeight]);
|
|
243
|
+
|
|
229
244
|
const onHeaderHeightChange = hasCustomHeader
|
|
230
245
|
? // If we have a custom header, don't use native header height
|
|
231
246
|
undefined
|
|
@@ -265,7 +280,8 @@ const SceneView = ({
|
|
|
265
280
|
if (
|
|
266
281
|
Platform.OS === 'android' &&
|
|
267
282
|
headerHeight !== 0 &&
|
|
268
|
-
|
|
283
|
+
// On some devices, height maybe slightly off (e.g. 56.17 instead of 56)
|
|
284
|
+
Math.round(headerHeight) <= ANDROID_DEFAULT_HEADER_HEIGHT
|
|
269
285
|
) {
|
|
270
286
|
// FIXME: On Android, events may get delivered out-of-order
|
|
271
287
|
// https://github.com/facebook/react-native/issues/54636
|
|
@@ -285,18 +301,96 @@ const SceneView = ({
|
|
|
285
301
|
}
|
|
286
302
|
);
|
|
287
303
|
|
|
304
|
+
const activityMode =
|
|
305
|
+
// Render focused screens normally
|
|
306
|
+
// Unpause preloaded and retained screens so updates are visible
|
|
307
|
+
// This lets effects on those screens run
|
|
308
|
+
// We don't need to handle inert as it'll be handled natively
|
|
309
|
+
inactiveBehavior === 'none' ||
|
|
310
|
+
focused ||
|
|
311
|
+
isInactive ||
|
|
312
|
+
isNextScreenTransparent
|
|
313
|
+
? 'normal'
|
|
314
|
+
: inactiveBehavior === 'unmount' &&
|
|
315
|
+
!isBeforeLast &&
|
|
316
|
+
!('state' in route && route.state)
|
|
317
|
+
? 'unmounted'
|
|
318
|
+
: 'paused';
|
|
319
|
+
|
|
320
|
+
const content = (
|
|
321
|
+
<AnimatedHeaderHeightContext.Provider value={animatedHeaderHeight}>
|
|
322
|
+
<HeaderHeightContext.Provider
|
|
323
|
+
value={headerShown !== false ? headerHeight : (parentHeaderHeight ?? 0)}
|
|
324
|
+
>
|
|
325
|
+
{headerBackground != null ? (
|
|
326
|
+
/**
|
|
327
|
+
* To show a custom header background, we render it at the top of the screen below the header
|
|
328
|
+
* The header also needs to be positioned absolutely (with `translucent` style)
|
|
329
|
+
*/
|
|
330
|
+
<View
|
|
331
|
+
style={[
|
|
332
|
+
styles.background,
|
|
333
|
+
headerTransparent ? styles.translucent : null,
|
|
334
|
+
{ height: headerHeight },
|
|
335
|
+
]}
|
|
336
|
+
>
|
|
337
|
+
{headerBackground()}
|
|
338
|
+
</View>
|
|
339
|
+
) : null}
|
|
340
|
+
{header != null && headerShown !== false ? (
|
|
341
|
+
<View
|
|
342
|
+
style={[
|
|
343
|
+
styles.header,
|
|
344
|
+
headerTransparent
|
|
345
|
+
? [
|
|
346
|
+
styles.absolute,
|
|
347
|
+
// Specify an explicit min height for Android screen readers
|
|
348
|
+
{ minHeight: headerHeight },
|
|
349
|
+
]
|
|
350
|
+
: null,
|
|
351
|
+
]}
|
|
352
|
+
>
|
|
353
|
+
<View
|
|
354
|
+
ref={headerContainerRef}
|
|
355
|
+
onLayout={(e) => {
|
|
356
|
+
const headerHeight = e.nativeEvent.layout.height;
|
|
357
|
+
|
|
358
|
+
animatedHeaderHeight.setValue(headerHeight);
|
|
359
|
+
setHeaderHeight(headerHeight);
|
|
360
|
+
}}
|
|
361
|
+
style={{ pointerEvents: 'box-none' }}
|
|
362
|
+
>
|
|
363
|
+
{header({
|
|
364
|
+
back: headerBack,
|
|
365
|
+
options,
|
|
366
|
+
route,
|
|
367
|
+
navigation,
|
|
368
|
+
})}
|
|
369
|
+
</View>
|
|
370
|
+
</View>
|
|
371
|
+
) : null}
|
|
372
|
+
<HeaderShownContext.Provider
|
|
373
|
+
value={isParentHeaderShown || headerShown !== false}
|
|
374
|
+
>
|
|
375
|
+
<HeaderBackContext.Provider value={headerBack}>
|
|
376
|
+
{render()}
|
|
377
|
+
</HeaderBackContext.Provider>
|
|
378
|
+
</HeaderShownContext.Provider>
|
|
379
|
+
</HeaderHeightContext.Provider>
|
|
380
|
+
</AnimatedHeaderHeightContext.Provider>
|
|
381
|
+
);
|
|
382
|
+
|
|
288
383
|
return (
|
|
289
384
|
<NavigationProvider navigation={navigation} route={route}>
|
|
290
385
|
<ScreenStackItem
|
|
291
386
|
key={route.key}
|
|
292
387
|
screenId={route.key}
|
|
293
|
-
activityState={
|
|
388
|
+
activityState={isInactive ? 0 : 2}
|
|
294
389
|
style={StyleSheet.absoluteFill}
|
|
295
390
|
aria-hidden={!focused}
|
|
296
391
|
customAnimationOnSwipe={animationMatchesGesture}
|
|
297
392
|
fullScreenSwipeEnabled={fullScreenGestureEnabled}
|
|
298
393
|
fullScreenSwipeShadowEnabled={fullScreenGestureShadowEnabled}
|
|
299
|
-
freezeOnBlur={freezeOnBlur}
|
|
300
394
|
gestureEnabled={
|
|
301
395
|
Platform.OS === 'android'
|
|
302
396
|
? // This prop enables handling of system back gestures on Android
|
|
@@ -353,62 +447,21 @@ const SceneView = ({
|
|
|
353
447
|
]}
|
|
354
448
|
headerConfig={headerConfig}
|
|
355
449
|
unstable_sheetFooter={unstable_sheetFooter}
|
|
356
|
-
// When ts-expect-error is added, it affects all the props below it
|
|
357
|
-
// So we keep any props that need it at the end
|
|
358
|
-
// Otherwise invalid props may not be caught by TypeScript
|
|
359
|
-
shouldFreeze={shouldFreeze}
|
|
360
450
|
>
|
|
361
|
-
|
|
362
|
-
<
|
|
363
|
-
|
|
364
|
-
|
|
451
|
+
{activityMode === 'unmounted' ? null : (
|
|
452
|
+
<ActivityView
|
|
453
|
+
mode={activityMode}
|
|
454
|
+
visible={
|
|
455
|
+
// We don't need to hide the content since it's handled natively
|
|
456
|
+
// Hiding may also cause flash due to lag after native tab switch
|
|
457
|
+
// So we leave it always visible
|
|
458
|
+
true
|
|
365
459
|
}
|
|
460
|
+
style={styles.content}
|
|
366
461
|
>
|
|
367
|
-
{
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
* The header also needs to be positioned absolutely (with `translucent` style)
|
|
371
|
-
*/
|
|
372
|
-
<View
|
|
373
|
-
style={[
|
|
374
|
-
styles.background,
|
|
375
|
-
headerTransparent ? styles.translucent : null,
|
|
376
|
-
{ height: headerHeight },
|
|
377
|
-
]}
|
|
378
|
-
>
|
|
379
|
-
{headerBackground()}
|
|
380
|
-
</View>
|
|
381
|
-
) : null}
|
|
382
|
-
{header != null && headerShown !== false ? (
|
|
383
|
-
<View
|
|
384
|
-
onLayout={(e) => {
|
|
385
|
-
const headerHeight = e.nativeEvent.layout.height;
|
|
386
|
-
|
|
387
|
-
animatedHeaderHeight.setValue(headerHeight);
|
|
388
|
-
setHeaderHeight(headerHeight);
|
|
389
|
-
}}
|
|
390
|
-
style={[
|
|
391
|
-
styles.header,
|
|
392
|
-
headerTransparent ? styles.absolute : null,
|
|
393
|
-
]}
|
|
394
|
-
>
|
|
395
|
-
{header({
|
|
396
|
-
back: headerBack,
|
|
397
|
-
options,
|
|
398
|
-
route,
|
|
399
|
-
navigation,
|
|
400
|
-
})}
|
|
401
|
-
</View>
|
|
402
|
-
) : null}
|
|
403
|
-
<HeaderShownContext.Provider
|
|
404
|
-
value={isParentHeaderShown || headerShown !== false}
|
|
405
|
-
>
|
|
406
|
-
<HeaderBackContext.Provider value={headerBack}>
|
|
407
|
-
{render()}
|
|
408
|
-
</HeaderBackContext.Provider>
|
|
409
|
-
</HeaderShownContext.Provider>
|
|
410
|
-
</HeaderHeightContext.Provider>
|
|
411
|
-
</AnimatedHeaderHeightContext.Provider>
|
|
462
|
+
{content}
|
|
463
|
+
</ActivityView>
|
|
464
|
+
)}
|
|
412
465
|
</ScreenStackItem>
|
|
413
466
|
</NavigationProvider>
|
|
414
467
|
);
|
|
@@ -425,46 +478,49 @@ export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
|
425
478
|
|
|
426
479
|
useInvalidPreventRemoveError(descriptors);
|
|
427
480
|
|
|
428
|
-
const
|
|
481
|
+
const activeRoutes = state.routes.slice(0, state.index + 1);
|
|
482
|
+
const modalRouteKeys = getModalRouteKeys(activeRoutes, descriptors);
|
|
429
483
|
|
|
430
484
|
return (
|
|
431
485
|
<SafeAreaProviderCompat>
|
|
432
486
|
<ScreenStack style={styles.container}>
|
|
433
|
-
{state.routes.
|
|
487
|
+
{state.routes.map((route, index) => {
|
|
434
488
|
const descriptor = descriptors[route.key];
|
|
489
|
+
|
|
490
|
+
if (descriptor == null) {
|
|
491
|
+
throw new Error(
|
|
492
|
+
`Couldn't find a descriptor for route '${route.key}'.`
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
435
496
|
const isFocused = state.index === index;
|
|
436
|
-
const
|
|
437
|
-
const
|
|
438
|
-
const nextKey = state.routes[index + 1]?.key;
|
|
497
|
+
const previousKey = activeRoutes[index - 1]?.key;
|
|
498
|
+
const nextKey = activeRoutes[index + 1]?.key;
|
|
439
499
|
const previousDescriptor = previousKey
|
|
440
500
|
? descriptors[previousKey]
|
|
441
501
|
: undefined;
|
|
442
502
|
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
443
503
|
|
|
444
|
-
const
|
|
445
|
-
const isModalOnIos = isModal && Platform.OS === 'ios';
|
|
504
|
+
const nextPresentation = nextDescriptor?.options.presentation;
|
|
446
505
|
|
|
447
|
-
const
|
|
448
|
-
|
|
449
|
-
|
|
506
|
+
const isNextScreenTransparent =
|
|
507
|
+
nextPresentation != null &&
|
|
508
|
+
TRANSPARENT_PRESENTATIONS.includes(nextPresentation);
|
|
450
509
|
|
|
451
|
-
|
|
452
|
-
// due to component being unmounted. To avoid this, we don't freeze the previous screen there
|
|
453
|
-
const shouldFreeze = isFabric()
|
|
454
|
-
? !isPreloaded && !isFocused && !isBelowFocused && !isModalOnIos
|
|
455
|
-
: !isPreloaded && !isFocused && !isModalOnIos;
|
|
510
|
+
const isModal = modalRouteKeys.includes(route.key);
|
|
456
511
|
|
|
457
512
|
return (
|
|
458
513
|
<SceneView
|
|
459
514
|
key={route.key}
|
|
460
515
|
index={index}
|
|
461
516
|
focused={isFocused}
|
|
462
|
-
shouldFreeze={shouldFreeze}
|
|
463
517
|
descriptor={descriptor}
|
|
464
518
|
previousDescriptor={previousDescriptor}
|
|
465
519
|
nextDescriptor={nextDescriptor}
|
|
466
520
|
isPresentationModal={isModal}
|
|
467
|
-
|
|
521
|
+
isNextScreenTransparent={isNextScreenTransparent}
|
|
522
|
+
isInactive={index > state.index}
|
|
523
|
+
isBeforeLast={index === activeRoutes.length - 2}
|
|
468
524
|
onWillDisappear={() => {
|
|
469
525
|
navigation.emit({
|
|
470
526
|
type: 'transitionStart',
|
|
@@ -544,6 +600,9 @@ const styles = StyleSheet.create({
|
|
|
544
600
|
container: {
|
|
545
601
|
flex: 1,
|
|
546
602
|
},
|
|
603
|
+
content: {
|
|
604
|
+
flex: 1,
|
|
605
|
+
},
|
|
547
606
|
header: {
|
|
548
607
|
zIndex: 1,
|
|
549
608
|
},
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
useHeaderHeight,
|
|
7
7
|
} from '@react-navigation/elements';
|
|
8
8
|
import {
|
|
9
|
+
ActivityView,
|
|
9
10
|
SafeAreaProviderCompat,
|
|
10
11
|
Screen,
|
|
11
12
|
} from '@react-navigation/elements/internal';
|
|
@@ -15,7 +16,7 @@ import {
|
|
|
15
16
|
useLinkBuilder,
|
|
16
17
|
} from '@react-navigation/native';
|
|
17
18
|
import * as React from 'react';
|
|
18
|
-
import { Animated,
|
|
19
|
+
import { Animated, StyleSheet, View } from 'react-native';
|
|
19
20
|
|
|
20
21
|
import type {
|
|
21
22
|
NativeStackDescriptorMap,
|
|
@@ -35,20 +36,30 @@ const TRANSPARENT_PRESENTATIONS = [
|
|
|
35
36
|
];
|
|
36
37
|
|
|
37
38
|
export function NativeStackView({ state, descriptors }: Props) {
|
|
38
|
-
const parentHeaderBack = React.
|
|
39
|
+
const parentHeaderBack = React.use(HeaderBackContext);
|
|
39
40
|
const { buildHref } = useLinkBuilder();
|
|
40
41
|
|
|
42
|
+
const activeRoutes = state.routes.slice(0, state.index + 1);
|
|
43
|
+
|
|
41
44
|
return (
|
|
42
45
|
<SafeAreaProviderCompat>
|
|
43
|
-
{state.routes.
|
|
46
|
+
{state.routes.map((route, i) => {
|
|
44
47
|
const isFocused = state.index === i;
|
|
45
|
-
const previousKey =
|
|
46
|
-
const nextKey =
|
|
48
|
+
const previousKey = activeRoutes[i - 1]?.key;
|
|
49
|
+
const nextKey = activeRoutes[i + 1]?.key;
|
|
47
50
|
const previousDescriptor = previousKey
|
|
48
51
|
? descriptors[previousKey]
|
|
49
52
|
: undefined;
|
|
50
53
|
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
51
|
-
const
|
|
54
|
+
const descriptor = descriptors[route.key];
|
|
55
|
+
|
|
56
|
+
if (descriptor == null) {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Couldn't find a descriptor for route '${route.key}'.`
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const { options, navigation, render } = descriptor;
|
|
52
63
|
|
|
53
64
|
const headerBack = previousDescriptor
|
|
54
65
|
? {
|
|
@@ -66,6 +77,7 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
66
77
|
const canGoBack = headerBack != null;
|
|
67
78
|
|
|
68
79
|
const {
|
|
80
|
+
inactiveBehavior = 'pause',
|
|
69
81
|
header,
|
|
70
82
|
headerShown,
|
|
71
83
|
headerBackIcon,
|
|
@@ -79,11 +91,33 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
79
91
|
|
|
80
92
|
const nextPresentation = nextDescriptor?.options.presentation;
|
|
81
93
|
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
94
|
+
const isNextScreenTransparent =
|
|
95
|
+
nextPresentation != null &&
|
|
96
|
+
TRANSPARENT_PRESENTATIONS.includes(nextPresentation);
|
|
85
97
|
|
|
86
|
-
|
|
98
|
+
const isInactive = i > state.index;
|
|
99
|
+
|
|
100
|
+
const isBeforeLast = i === activeRoutes.length - 2;
|
|
101
|
+
|
|
102
|
+
const activityMode =
|
|
103
|
+
// Render focused screens normally
|
|
104
|
+
isFocused
|
|
105
|
+
? 'normal'
|
|
106
|
+
: // Unpause preloaded and retained screens so updates are visible
|
|
107
|
+
// This lets effects on those screens run
|
|
108
|
+
inactiveBehavior === 'none' ||
|
|
109
|
+
isInactive ||
|
|
110
|
+
isNextScreenTransparent
|
|
111
|
+
? 'inert'
|
|
112
|
+
: inactiveBehavior === 'unmount' && !isBeforeLast && !route.state
|
|
113
|
+
? 'unmounted'
|
|
114
|
+
: 'paused';
|
|
115
|
+
|
|
116
|
+
if (activityMode === 'unmounted') {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const content = (
|
|
87
121
|
<Screen
|
|
88
122
|
key={route.key}
|
|
89
123
|
focused={isFocused}
|
|
@@ -117,18 +151,7 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
117
151
|
{...rest}
|
|
118
152
|
label={headerBackTitle ?? label}
|
|
119
153
|
tintColor={tintColor}
|
|
120
|
-
|
|
121
|
-
headerBackIcon !== undefined
|
|
122
|
-
? () => (
|
|
123
|
-
<Image
|
|
124
|
-
source={headerBackIcon.source}
|
|
125
|
-
resizeMode="contain"
|
|
126
|
-
tintColor={tintColor}
|
|
127
|
-
style={styles.backImage}
|
|
128
|
-
/>
|
|
129
|
-
)
|
|
130
|
-
: undefined
|
|
131
|
-
}
|
|
154
|
+
icon={headerBackIcon}
|
|
132
155
|
onPress={navigation.goBack}
|
|
133
156
|
/>
|
|
134
157
|
)
|
|
@@ -138,22 +161,13 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
138
161
|
/>
|
|
139
162
|
)
|
|
140
163
|
}
|
|
141
|
-
style={
|
|
142
|
-
StyleSheet.absoluteFill,
|
|
143
|
-
|
|
144
|
-
display:
|
|
145
|
-
(isFocused ||
|
|
146
|
-
(nextPresentation != null &&
|
|
147
|
-
TRANSPARENT_PRESENTATIONS.includes(nextPresentation))) &&
|
|
148
|
-
!isPreloaded
|
|
149
|
-
? 'flex'
|
|
150
|
-
: 'none',
|
|
151
|
-
},
|
|
152
|
-
presentation != null &&
|
|
164
|
+
style={{
|
|
165
|
+
...StyleSheet.absoluteFill,
|
|
166
|
+
...(presentation != null &&
|
|
153
167
|
TRANSPARENT_PRESENTATIONS.includes(presentation)
|
|
154
168
|
? { backgroundColor: 'transparent' }
|
|
155
|
-
: null,
|
|
156
|
-
|
|
169
|
+
: null),
|
|
170
|
+
}}
|
|
157
171
|
>
|
|
158
172
|
<HeaderBackContext.Provider value={headerBack}>
|
|
159
173
|
<AnimatedHeaderHeightProvider>
|
|
@@ -164,6 +178,17 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
164
178
|
</HeaderBackContext.Provider>
|
|
165
179
|
</Screen>
|
|
166
180
|
);
|
|
181
|
+
|
|
182
|
+
return (
|
|
183
|
+
<ActivityView
|
|
184
|
+
key={route.key}
|
|
185
|
+
mode={activityMode}
|
|
186
|
+
visible={isFocused || isNextScreenTransparent}
|
|
187
|
+
style={StyleSheet.absoluteFill}
|
|
188
|
+
>
|
|
189
|
+
{content}
|
|
190
|
+
</ActivityView>
|
|
191
|
+
);
|
|
167
192
|
})}
|
|
168
193
|
</SafeAreaProviderCompat>
|
|
169
194
|
);
|
|
@@ -194,9 +219,4 @@ const styles = StyleSheet.create({
|
|
|
194
219
|
contentContainer: {
|
|
195
220
|
flex: 1,
|
|
196
221
|
},
|
|
197
|
-
backImage: {
|
|
198
|
-
height: 24,
|
|
199
|
-
width: 24,
|
|
200
|
-
margin: 3,
|
|
201
|
-
},
|
|
202
222
|
});
|