@react-navigation/native-stack 7.0.0-rc.27 → 7.0.0-rc.29
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/navigators/createNativeStackNavigator.js +3 -1
- package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/commonjs/views/DebugContainer.native.js +8 -10
- package/lib/commonjs/views/DebugContainer.native.js.map +1 -1
- package/lib/commonjs/views/FooterComponent.js +19 -0
- package/lib/commonjs/views/FooterComponent.js.map +1 -0
- package/lib/commonjs/views/NativeStackView.js +11 -8
- package/lib/commonjs/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.native.js +84 -95
- package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
- package/lib/commonjs/views/ScreenStackContent.js +70 -0
- package/lib/commonjs/views/ScreenStackContent.js.map +1 -0
- package/lib/commonjs/views/{HeaderConfig.js → useHeaderConfigProps.js} +59 -48
- package/lib/commonjs/views/useHeaderConfigProps.js.map +1 -0
- package/lib/module/navigators/createNativeStackNavigator.js +3 -1
- package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/module/views/DebugContainer.native.js +9 -11
- package/lib/module/views/DebugContainer.native.js.map +1 -1
- package/lib/module/views/FooterComponent.js +14 -0
- package/lib/module/views/FooterComponent.js.map +1 -0
- package/lib/module/views/NativeStackView.js +11 -8
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +85 -95
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/module/views/ScreenStackContent.js +63 -0
- package/lib/module/views/ScreenStackContent.js.map +1 -0
- package/lib/module/views/{HeaderConfig.js → useHeaderConfigProps.js} +59 -48
- package/lib/module/views/useHeaderConfigProps.js.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +1 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/types.d.ts +80 -18
- package/lib/typescript/commonjs/src/types.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/views/DebugContainer.d.ts +1 -1
- package/lib/typescript/commonjs/src/views/DebugContainer.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/views/DebugContainer.native.d.ts +1 -1
- package/lib/typescript/commonjs/src/views/DebugContainer.native.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/views/FooterComponent.d.ts +7 -0
- package/lib/typescript/commonjs/src/views/FooterComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/views/NativeStackView.d.ts +4 -3
- package/lib/typescript/commonjs/src/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/views/NativeStackView.native.d.ts +4 -3
- package/lib/typescript/commonjs/src/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/views/ScreenStackContent.d.ts +9 -0
- package/lib/typescript/commonjs/src/views/ScreenStackContent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/views/useHeaderConfigProps.d.ts +41 -0
- package/lib/typescript/commonjs/src/views/useHeaderConfigProps.d.ts.map +1 -0
- package/lib/typescript/commonjs/tsconfig.build.tsbuildinfo +1 -1
- package/lib/typescript/module/src/index.d.ts +1 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
- package/lib/typescript/module/src/types.d.ts +80 -18
- package/lib/typescript/module/src/types.d.ts.map +1 -1
- package/lib/typescript/module/src/views/DebugContainer.d.ts +1 -1
- package/lib/typescript/module/src/views/DebugContainer.d.ts.map +1 -1
- package/lib/typescript/module/src/views/DebugContainer.native.d.ts +1 -1
- package/lib/typescript/module/src/views/DebugContainer.native.d.ts.map +1 -1
- package/lib/typescript/module/src/views/FooterComponent.d.ts +7 -0
- package/lib/typescript/module/src/views/FooterComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/views/NativeStackView.d.ts +4 -3
- package/lib/typescript/module/src/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/module/src/views/NativeStackView.native.d.ts +4 -3
- package/lib/typescript/module/src/views/NativeStackView.native.d.ts.map +1 -1
- package/lib/typescript/module/src/views/ScreenStackContent.d.ts +9 -0
- package/lib/typescript/module/src/views/ScreenStackContent.d.ts.map +1 -0
- package/lib/typescript/module/src/views/useHeaderConfigProps.d.ts +41 -0
- package/lib/typescript/module/src/views/useHeaderConfigProps.d.ts.map +1 -0
- package/lib/typescript/module/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -9
- package/src/index.tsx +2 -0
- package/src/navigators/createNativeStackNavigator.tsx +2 -1
- package/src/types.tsx +74 -18
- package/src/views/DebugContainer.native.tsx +13 -6
- package/src/views/DebugContainer.tsx +1 -1
- package/src/views/FooterComponent.tsx +10 -0
- package/src/views/NativeStackView.native.tsx +111 -151
- package/src/views/NativeStackView.tsx +24 -12
- package/src/views/ScreenStackContent.tsx +121 -0
- package/src/views/{HeaderConfig.tsx → useHeaderConfigProps.tsx} +67 -71
- package/lib/commonjs/views/HeaderConfig.js.map +0 -1
- package/lib/module/views/HeaderConfig.js.map +0 -1
- package/lib/typescript/commonjs/src/views/HeaderConfig.d.ts +0 -11
- package/lib/typescript/commonjs/src/views/HeaderConfig.d.ts.map +0 -1
- package/lib/typescript/module/src/views/HeaderConfig.d.ts +0 -11
- package/lib/typescript/module/src/views/HeaderConfig.d.ts.map +0 -1
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
NavigationContext,
|
|
11
11
|
NavigationRouteContext,
|
|
12
12
|
type ParamListBase,
|
|
13
|
-
type
|
|
13
|
+
type RouteProp,
|
|
14
14
|
StackActions,
|
|
15
15
|
type StackNavigationState,
|
|
16
16
|
usePreventRemoveContext,
|
|
@@ -29,107 +29,24 @@ import {
|
|
|
29
29
|
useSafeAreaFrame,
|
|
30
30
|
useSafeAreaInsets,
|
|
31
31
|
} from 'react-native-safe-area-context';
|
|
32
|
-
import {
|
|
33
|
-
Screen,
|
|
34
|
-
type ScreenProps,
|
|
35
|
-
ScreenStack,
|
|
36
|
-
type StackPresentationTypes,
|
|
37
|
-
} from 'react-native-screens';
|
|
38
|
-
import warnOnce from 'warn-once';
|
|
32
|
+
import { type ScreenProps, ScreenStack } from 'react-native-screens';
|
|
39
33
|
|
|
40
34
|
import type {
|
|
41
35
|
NativeStackDescriptor,
|
|
42
36
|
NativeStackDescriptorMap,
|
|
43
37
|
NativeStackNavigationHelpers,
|
|
44
|
-
NativeStackNavigationOptions,
|
|
45
38
|
} from '../types';
|
|
46
39
|
import { debounce } from '../utils/debounce';
|
|
47
40
|
import { getModalRouteKeys } from '../utils/getModalRoutesKeys';
|
|
48
41
|
import { AnimatedHeaderHeightContext } from '../utils/useAnimatedHeaderHeight';
|
|
49
42
|
import { useDismissedRouteError } from '../utils/useDismissedRouteError';
|
|
50
43
|
import { useInvalidPreventRemoveError } from '../utils/useInvalidPreventRemoveError';
|
|
51
|
-
import {
|
|
52
|
-
import {
|
|
44
|
+
import { FooterComponent } from './FooterComponent';
|
|
45
|
+
import { ScreenStackContent } from './ScreenStackContent';
|
|
46
|
+
import { useHeaderConfigProps } from './useHeaderConfigProps';
|
|
53
47
|
|
|
54
48
|
const ANDROID_DEFAULT_HEADER_HEIGHT = 56;
|
|
55
49
|
|
|
56
|
-
const MaybeNestedStack = ({
|
|
57
|
-
options,
|
|
58
|
-
route,
|
|
59
|
-
presentation,
|
|
60
|
-
headerHeight,
|
|
61
|
-
headerTopInsetEnabled,
|
|
62
|
-
children,
|
|
63
|
-
}: {
|
|
64
|
-
options: NativeStackNavigationOptions;
|
|
65
|
-
route: Route<string>;
|
|
66
|
-
presentation: Exclude<StackPresentationTypes, 'push'> | 'card';
|
|
67
|
-
headerHeight: number;
|
|
68
|
-
headerTopInsetEnabled: boolean;
|
|
69
|
-
children: React.ReactNode;
|
|
70
|
-
}) => {
|
|
71
|
-
const { colors } = useTheme();
|
|
72
|
-
const { header, headerShown = true, contentStyle } = options;
|
|
73
|
-
|
|
74
|
-
const isHeaderInModal =
|
|
75
|
-
Platform.OS === 'android'
|
|
76
|
-
? false
|
|
77
|
-
: presentation !== 'card' && headerShown === true && header === undefined;
|
|
78
|
-
|
|
79
|
-
const headerShownPreviousRef = React.useRef(headerShown);
|
|
80
|
-
|
|
81
|
-
React.useEffect(() => {
|
|
82
|
-
warnOnce(
|
|
83
|
-
Platform.OS !== 'android' &&
|
|
84
|
-
presentation !== 'card' &&
|
|
85
|
-
headerShownPreviousRef.current !== headerShown,
|
|
86
|
-
`Dynamically changing 'headerShown' in modals will result in remounting the screen and losing all local state. See options for the screen '${route.name}'.`
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
headerShownPreviousRef.current = headerShown;
|
|
90
|
-
}, [headerShown, presentation, route.name]);
|
|
91
|
-
|
|
92
|
-
const content = (
|
|
93
|
-
<DebugContainer
|
|
94
|
-
style={[
|
|
95
|
-
styles.container,
|
|
96
|
-
presentation !== 'transparentModal' &&
|
|
97
|
-
presentation !== 'containedTransparentModal' && {
|
|
98
|
-
backgroundColor: colors.background,
|
|
99
|
-
},
|
|
100
|
-
contentStyle,
|
|
101
|
-
]}
|
|
102
|
-
stackPresentation={presentation === 'card' ? 'push' : presentation}
|
|
103
|
-
>
|
|
104
|
-
{children}
|
|
105
|
-
</DebugContainer>
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
if (isHeaderInModal) {
|
|
109
|
-
return (
|
|
110
|
-
<ScreenStack style={styles.container}>
|
|
111
|
-
<Screen
|
|
112
|
-
enabled
|
|
113
|
-
isNativeStack
|
|
114
|
-
hasLargeHeader={options.headerLargeTitle ?? false}
|
|
115
|
-
style={StyleSheet.absoluteFill}
|
|
116
|
-
>
|
|
117
|
-
{content}
|
|
118
|
-
<HeaderConfig
|
|
119
|
-
{...options}
|
|
120
|
-
route={route}
|
|
121
|
-
headerHeight={headerHeight}
|
|
122
|
-
headerTopInsetEnabled={headerTopInsetEnabled}
|
|
123
|
-
canGoBack
|
|
124
|
-
/>
|
|
125
|
-
</Screen>
|
|
126
|
-
</ScreenStack>
|
|
127
|
-
);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return content;
|
|
131
|
-
};
|
|
132
|
-
|
|
133
50
|
type SceneViewProps = {
|
|
134
51
|
index: number;
|
|
135
52
|
focused: boolean;
|
|
@@ -137,6 +54,7 @@ type SceneViewProps = {
|
|
|
137
54
|
previousDescriptor?: NativeStackDescriptor;
|
|
138
55
|
nextDescriptor?: NativeStackDescriptor;
|
|
139
56
|
isPresentationModal?: boolean;
|
|
57
|
+
isPreloaded?: boolean;
|
|
140
58
|
onWillDisappear: () => void;
|
|
141
59
|
onWillAppear: () => void;
|
|
142
60
|
onAppear: () => void;
|
|
@@ -145,6 +63,7 @@ type SceneViewProps = {
|
|
|
145
63
|
onHeaderBackButtonClicked: ScreenProps['onHeaderBackButtonClicked'];
|
|
146
64
|
onNativeDismissCancelled: ScreenProps['onDismissed'];
|
|
147
65
|
onGestureCancel: ScreenProps['onGestureCancel'];
|
|
66
|
+
onSheetDetentChanged: ScreenProps['onSheetDetentChanged'];
|
|
148
67
|
};
|
|
149
68
|
|
|
150
69
|
const SceneView = ({
|
|
@@ -154,6 +73,7 @@ const SceneView = ({
|
|
|
154
73
|
previousDescriptor,
|
|
155
74
|
nextDescriptor,
|
|
156
75
|
isPresentationModal,
|
|
76
|
+
isPreloaded,
|
|
157
77
|
onWillDisappear,
|
|
158
78
|
onWillAppear,
|
|
159
79
|
onAppear,
|
|
@@ -162,6 +82,7 @@ const SceneView = ({
|
|
|
162
82
|
onHeaderBackButtonClicked,
|
|
163
83
|
onNativeDismissCancelled,
|
|
164
84
|
onGestureCancel,
|
|
85
|
+
onSheetDetentChanged,
|
|
165
86
|
}: SceneViewProps) => {
|
|
166
87
|
const { route, navigation, options, render } = descriptor;
|
|
167
88
|
|
|
@@ -170,6 +91,7 @@ const SceneView = ({
|
|
|
170
91
|
animationMatchesGesture,
|
|
171
92
|
presentation = isPresentationModal ? 'modal' : 'card',
|
|
172
93
|
fullScreenGestureEnabled,
|
|
94
|
+
unstable_screenStyle = null,
|
|
173
95
|
} = options;
|
|
174
96
|
|
|
175
97
|
const {
|
|
@@ -190,19 +112,31 @@ const SceneView = ({
|
|
|
190
112
|
navigationBarTranslucent,
|
|
191
113
|
navigationBarHidden,
|
|
192
114
|
orientation,
|
|
193
|
-
sheetAllowedDetents =
|
|
194
|
-
|
|
115
|
+
sheetAllowedDetents = [1.0],
|
|
116
|
+
sheetLargestUndimmedDetentIndex = -1,
|
|
195
117
|
sheetGrabberVisible = false,
|
|
196
118
|
sheetCornerRadius = -1.0,
|
|
119
|
+
sheetElevation = 24,
|
|
197
120
|
sheetExpandsWhenScrolledToEdge = true,
|
|
121
|
+
sheetInitialDetentIndex = 0,
|
|
198
122
|
statusBarAnimation,
|
|
199
123
|
statusBarHidden,
|
|
200
124
|
statusBarStyle,
|
|
201
125
|
statusBarTranslucent,
|
|
202
126
|
statusBarBackgroundColor,
|
|
127
|
+
unstable_sheetFooter = null,
|
|
203
128
|
freezeOnBlur,
|
|
129
|
+
contentStyle,
|
|
204
130
|
} = options;
|
|
205
131
|
|
|
132
|
+
// We want to allow only backgroundColor setting for now.
|
|
133
|
+
// This allows to workaround one issue with truncated
|
|
134
|
+
// content with formSheet presentation.
|
|
135
|
+
unstable_screenStyle =
|
|
136
|
+
unstable_screenStyle && presentation === 'formSheet'
|
|
137
|
+
? { backgroundColor: unstable_screenStyle.backgroundColor }
|
|
138
|
+
: null;
|
|
139
|
+
|
|
206
140
|
if (gestureDirection === 'vertical' && Platform.OS === 'ios') {
|
|
207
141
|
// for `vertical` direction to work, we need to set `fullScreenGestureEnabled` to `true`
|
|
208
142
|
// so the screen can be dismissed from any point on screen.
|
|
@@ -234,6 +168,7 @@ const SceneView = ({
|
|
|
234
168
|
presentation = 'card';
|
|
235
169
|
}
|
|
236
170
|
|
|
171
|
+
const { colors } = useTheme();
|
|
237
172
|
const insets = useSafeAreaInsets();
|
|
238
173
|
const frame = useSafeAreaFrame();
|
|
239
174
|
|
|
@@ -309,32 +244,52 @@ const SceneView = ({
|
|
|
309
244
|
? statusBarTranslucent
|
|
310
245
|
: topInset !== 0;
|
|
311
246
|
|
|
247
|
+
const canGoBack = previousDescriptor != null || parentHeaderBack != null;
|
|
312
248
|
const backTitle = previousDescriptor
|
|
313
249
|
? getHeaderTitle(previousDescriptor.options, previousDescriptor.route.name)
|
|
314
250
|
: parentHeaderBack?.title;
|
|
315
251
|
|
|
316
|
-
const headerBack = React.useMemo(
|
|
317
|
-
()
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
252
|
+
const headerBack = React.useMemo(() => {
|
|
253
|
+
if (canGoBack) {
|
|
254
|
+
return {
|
|
255
|
+
href: undefined, // No href needed for native
|
|
256
|
+
title: backTitle,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return undefined;
|
|
261
|
+
}, [canGoBack, backTitle]);
|
|
324
262
|
|
|
325
263
|
const isRemovePrevented = preventedRoutes[route.key]?.preventRemove;
|
|
326
264
|
|
|
265
|
+
const headerConfig = useHeaderConfigProps({
|
|
266
|
+
...options,
|
|
267
|
+
route,
|
|
268
|
+
canGoBack,
|
|
269
|
+
headerBackButtonMenuEnabled:
|
|
270
|
+
isRemovePrevented !== undefined
|
|
271
|
+
? !isRemovePrevented
|
|
272
|
+
: headerBackButtonMenuEnabled,
|
|
273
|
+
headerBackTitle:
|
|
274
|
+
options.headerBackTitle !== undefined
|
|
275
|
+
? options.headerBackTitle
|
|
276
|
+
: undefined,
|
|
277
|
+
headerHeight,
|
|
278
|
+
headerShown: header !== undefined ? false : headerShown,
|
|
279
|
+
headerTopInsetEnabled,
|
|
280
|
+
});
|
|
281
|
+
|
|
327
282
|
return (
|
|
328
|
-
<
|
|
283
|
+
<ScreenStackContent
|
|
329
284
|
key={route.key}
|
|
330
|
-
|
|
331
|
-
|
|
285
|
+
activityState={isPreloaded ? 0 : 2}
|
|
286
|
+
style={[StyleSheet.absoluteFill, unstable_screenStyle]}
|
|
332
287
|
accessibilityElementsHidden={!focused}
|
|
333
288
|
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
|
|
334
|
-
style={StyleSheet.absoluteFill}
|
|
335
|
-
hasLargeHeader={options.headerLargeTitle ?? false}
|
|
336
289
|
customAnimationOnSwipe={animationMatchesGesture}
|
|
337
290
|
fullScreenSwipeEnabled={fullScreenGestureEnabled}
|
|
291
|
+
fullScreenSwipeShadowEnabled={fullScreenGestureShadowEnabled}
|
|
292
|
+
freezeOnBlur={freezeOnBlur}
|
|
338
293
|
gestureEnabled={
|
|
339
294
|
Platform.OS === 'android'
|
|
340
295
|
? // This prop enables handling of system back gestures on Android
|
|
@@ -352,9 +307,11 @@ const SceneView = ({
|
|
|
352
307
|
stackAnimation={animation}
|
|
353
308
|
screenOrientation={orientation}
|
|
354
309
|
sheetAllowedDetents={sheetAllowedDetents}
|
|
355
|
-
|
|
310
|
+
sheetLargestUndimmedDetentIndex={sheetLargestUndimmedDetentIndex}
|
|
356
311
|
sheetGrabberVisible={sheetGrabberVisible}
|
|
312
|
+
sheetInitialDetentIndex={sheetInitialDetentIndex}
|
|
357
313
|
sheetCornerRadius={sheetCornerRadius}
|
|
314
|
+
sheetElevation={sheetElevation}
|
|
358
315
|
sheetExpandsWhenScrolledToEdge={sheetExpandsWhenScrolledToEdge}
|
|
359
316
|
statusBarAnimation={statusBarAnimation}
|
|
360
317
|
statusBarHidden={statusBarHidden}
|
|
@@ -369,6 +326,7 @@ const SceneView = ({
|
|
|
369
326
|
onDisappear={onDisappear}
|
|
370
327
|
onDismissed={onDismissed}
|
|
371
328
|
onGestureCancel={onGestureCancel}
|
|
329
|
+
onSheetDetentChanged={onSheetDetentChanged}
|
|
372
330
|
gestureResponseDistance={gestureResponseDistance}
|
|
373
331
|
nativeBackButtonDismissalEnabled={false} // on Android
|
|
374
332
|
onHeaderBackButtonClicked={onHeaderBackButtonClicked}
|
|
@@ -423,12 +381,17 @@ const SceneView = ({
|
|
|
423
381
|
},
|
|
424
382
|
}
|
|
425
383
|
)}
|
|
426
|
-
|
|
384
|
+
contentStyle={[
|
|
385
|
+
presentation !== 'transparentModal' &&
|
|
386
|
+
presentation !== 'containedTransparentModal' && {
|
|
387
|
+
backgroundColor: colors.background,
|
|
388
|
+
},
|
|
389
|
+
contentStyle,
|
|
390
|
+
]}
|
|
391
|
+
headerConfig={headerConfig}
|
|
427
392
|
// When ts-expect-error is added, it affects all the props below it
|
|
428
393
|
// So we keep any props that need it at the end
|
|
429
394
|
// Otherwise invalid props may not be caught by TypeScript
|
|
430
|
-
// @ts-expect-error Props available in newer versions of `react-native-screens`
|
|
431
|
-
fullScreenSwipeShadowEnabled={fullScreenGestureShadowEnabled} // 3.33.0 onwards
|
|
432
395
|
>
|
|
433
396
|
<NavigationContext.Provider value={navigation}>
|
|
434
397
|
<NavigationRouteContext.Provider value={route}>
|
|
@@ -477,52 +440,18 @@ const SceneView = ({
|
|
|
477
440
|
<HeaderShownContext.Provider
|
|
478
441
|
value={isParentHeaderShown || headerShown !== false}
|
|
479
442
|
>
|
|
480
|
-
<
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
presentation={presentation}
|
|
484
|
-
headerHeight={headerHeight}
|
|
485
|
-
headerTopInsetEnabled={headerTopInsetEnabled}
|
|
486
|
-
>
|
|
487
|
-
<HeaderBackContext.Provider value={headerBack}>
|
|
488
|
-
{render()}
|
|
489
|
-
</HeaderBackContext.Provider>
|
|
490
|
-
</MaybeNestedStack>
|
|
443
|
+
<HeaderBackContext.Provider value={headerBack}>
|
|
444
|
+
{render()}
|
|
445
|
+
</HeaderBackContext.Provider>
|
|
491
446
|
</HeaderShownContext.Provider>
|
|
492
|
-
{
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
* Otherwise dynamically rendering a custom `header` leaves the native header visible
|
|
496
|
-
*
|
|
497
|
-
* https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md#screenstackheaderconfig
|
|
498
|
-
*
|
|
499
|
-
* HeaderConfig must not be first child of a Screen.
|
|
500
|
-
* See https://github.com/software-mansion/react-native-screens/pull/1825
|
|
501
|
-
* for detailed explanation
|
|
502
|
-
*/}
|
|
503
|
-
<HeaderConfig
|
|
504
|
-
{...options}
|
|
505
|
-
route={route}
|
|
506
|
-
headerBackButtonMenuEnabled={
|
|
507
|
-
isRemovePrevented !== undefined
|
|
508
|
-
? !isRemovePrevented
|
|
509
|
-
: headerBackButtonMenuEnabled
|
|
510
|
-
}
|
|
511
|
-
headerShown={header !== undefined ? false : headerShown}
|
|
512
|
-
headerHeight={headerHeight}
|
|
513
|
-
headerBackTitle={
|
|
514
|
-
options.headerBackTitle !== undefined
|
|
515
|
-
? options.headerBackTitle
|
|
516
|
-
: undefined
|
|
517
|
-
}
|
|
518
|
-
headerTopInsetEnabled={headerTopInsetEnabled}
|
|
519
|
-
canGoBack={headerBack !== undefined}
|
|
520
|
-
/>
|
|
447
|
+
{presentation === 'formSheet' && unstable_sheetFooter && (
|
|
448
|
+
<FooterComponent>{unstable_sheetFooter()}</FooterComponent>
|
|
449
|
+
)}
|
|
521
450
|
</HeaderHeightContext.Provider>
|
|
522
451
|
</AnimatedHeaderHeightContext.Provider>
|
|
523
452
|
</NavigationRouteContext.Provider>
|
|
524
453
|
</NavigationContext.Provider>
|
|
525
|
-
</
|
|
454
|
+
</ScreenStackContent>
|
|
526
455
|
);
|
|
527
456
|
};
|
|
528
457
|
|
|
@@ -530,9 +459,18 @@ type Props = {
|
|
|
530
459
|
state: StackNavigationState<ParamListBase>;
|
|
531
460
|
navigation: NativeStackNavigationHelpers;
|
|
532
461
|
descriptors: NativeStackDescriptorMap;
|
|
462
|
+
describe: (
|
|
463
|
+
route: RouteProp<ParamListBase>,
|
|
464
|
+
placeholder: boolean
|
|
465
|
+
) => NativeStackDescriptor;
|
|
533
466
|
};
|
|
534
467
|
|
|
535
|
-
export function NativeStackView({
|
|
468
|
+
export function NativeStackView({
|
|
469
|
+
state,
|
|
470
|
+
navigation,
|
|
471
|
+
descriptors,
|
|
472
|
+
describe,
|
|
473
|
+
}: Props) {
|
|
536
474
|
const { setNextDismissedKey } = useDismissedRouteError(state);
|
|
537
475
|
|
|
538
476
|
const { colors } = useTheme();
|
|
@@ -541,11 +479,18 @@ export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
|
541
479
|
|
|
542
480
|
const modalRouteKeys = getModalRouteKeys(state.routes, descriptors);
|
|
543
481
|
|
|
482
|
+
const preloadedDescriptors =
|
|
483
|
+
state.preloadedRoutes.reduce<NativeStackDescriptorMap>((acc, route) => {
|
|
484
|
+
acc[route.key] = acc[route.key] || describe(route, true);
|
|
485
|
+
return acc;
|
|
486
|
+
}, {});
|
|
487
|
+
|
|
544
488
|
return (
|
|
545
489
|
<SafeAreaProviderCompat style={{ backgroundColor: colors.background }}>
|
|
546
490
|
<ScreenStack style={styles.container}>
|
|
547
|
-
{state.routes.map((route, index) => {
|
|
548
|
-
const descriptor =
|
|
491
|
+
{state.routes.concat(state.preloadedRoutes).map((route, index) => {
|
|
492
|
+
const descriptor =
|
|
493
|
+
descriptors[route.key] ?? preloadedDescriptors[route.key];
|
|
549
494
|
const isFocused = state.index === index;
|
|
550
495
|
const previousKey = state.routes[index - 1]?.key;
|
|
551
496
|
const nextKey = state.routes[index + 1]?.key;
|
|
@@ -556,6 +501,10 @@ export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
|
556
501
|
|
|
557
502
|
const isModal = modalRouteKeys.includes(route.key);
|
|
558
503
|
|
|
504
|
+
const isPreloaded =
|
|
505
|
+
preloadedDescriptors[route.key] !== undefined &&
|
|
506
|
+
descriptors[route.key] === undefined;
|
|
507
|
+
|
|
559
508
|
return (
|
|
560
509
|
<SceneView
|
|
561
510
|
key={route.key}
|
|
@@ -565,6 +514,7 @@ export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
|
565
514
|
previousDescriptor={previousDescriptor}
|
|
566
515
|
nextDescriptor={nextDescriptor}
|
|
567
516
|
isPresentationModal={isModal}
|
|
517
|
+
isPreloaded={isPreloaded}
|
|
568
518
|
onWillDisappear={() => {
|
|
569
519
|
navigation.emit({
|
|
570
520
|
type: 'transitionStart',
|
|
@@ -622,6 +572,16 @@ export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
|
622
572
|
target: route.key,
|
|
623
573
|
});
|
|
624
574
|
}}
|
|
575
|
+
onSheetDetentChanged={(event) => {
|
|
576
|
+
navigation.emit({
|
|
577
|
+
type: 'sheetDetentChange',
|
|
578
|
+
target: route.key,
|
|
579
|
+
data: {
|
|
580
|
+
index: event.nativeEvent.index,
|
|
581
|
+
stable: event.nativeEvent.isStable,
|
|
582
|
+
},
|
|
583
|
+
});
|
|
584
|
+
}}
|
|
625
585
|
/>
|
|
626
586
|
);
|
|
627
587
|
})}
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
} from '@react-navigation/elements';
|
|
10
10
|
import {
|
|
11
11
|
type ParamListBase,
|
|
12
|
+
type RouteProp,
|
|
12
13
|
type StackNavigationState,
|
|
13
14
|
useLinkBuilder,
|
|
14
15
|
useTheme,
|
|
@@ -17,6 +18,7 @@ import * as React from 'react';
|
|
|
17
18
|
import { Animated, Image, StyleSheet, View } from 'react-native';
|
|
18
19
|
|
|
19
20
|
import type {
|
|
21
|
+
NativeStackDescriptor,
|
|
20
22
|
NativeStackDescriptorMap,
|
|
21
23
|
NativeStackNavigationHelpers,
|
|
22
24
|
} from '../types';
|
|
@@ -27,6 +29,10 @@ type Props = {
|
|
|
27
29
|
// This is used for the native implementation of the stack.
|
|
28
30
|
navigation: NativeStackNavigationHelpers;
|
|
29
31
|
descriptors: NativeStackDescriptorMap;
|
|
32
|
+
describe: (
|
|
33
|
+
route: RouteProp<ParamListBase>,
|
|
34
|
+
placeholder: boolean
|
|
35
|
+
) => NativeStackDescriptor;
|
|
30
36
|
};
|
|
31
37
|
|
|
32
38
|
const TRANSPARENT_PRESENTATIONS = [
|
|
@@ -34,20 +40,20 @@ const TRANSPARENT_PRESENTATIONS = [
|
|
|
34
40
|
'containedTransparentModal',
|
|
35
41
|
];
|
|
36
42
|
|
|
37
|
-
export function NativeStackView({ state, descriptors }: Props) {
|
|
43
|
+
export function NativeStackView({ state, descriptors, describe }: Props) {
|
|
38
44
|
const parentHeaderBack = React.useContext(HeaderBackContext);
|
|
39
45
|
const { buildHref } = useLinkBuilder();
|
|
40
46
|
const { colors } = useTheme();
|
|
41
47
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
48
|
+
const preloadedDescriptors =
|
|
49
|
+
state.preloadedRoutes.reduce<NativeStackDescriptorMap>((acc, route) => {
|
|
50
|
+
acc[route.key] = acc[route.key] || describe(route, true);
|
|
51
|
+
return acc;
|
|
52
|
+
}, {});
|
|
47
53
|
|
|
48
54
|
return (
|
|
49
55
|
<SafeAreaProviderCompat style={{ backgroundColor: colors.background }}>
|
|
50
|
-
{state.routes.map((route, i) => {
|
|
56
|
+
{state.routes.concat(state.preloadedRoutes).map((route, i) => {
|
|
51
57
|
const isFocused = state.index === i;
|
|
52
58
|
const previousKey = state.routes[i - 1]?.key;
|
|
53
59
|
const nextKey = state.routes[i + 1]?.key;
|
|
@@ -55,7 +61,8 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
55
61
|
? descriptors[previousKey]
|
|
56
62
|
: undefined;
|
|
57
63
|
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
58
|
-
const { options, navigation, render } =
|
|
64
|
+
const { options, navigation, render } =
|
|
65
|
+
descriptors[route.key] ?? preloadedDescriptors[route.key];
|
|
59
66
|
|
|
60
67
|
const headerBack = previousDescriptor
|
|
61
68
|
? {
|
|
@@ -70,7 +77,7 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
70
77
|
}
|
|
71
78
|
: parentHeaderBack;
|
|
72
79
|
|
|
73
|
-
const canGoBack = headerBack
|
|
80
|
+
const canGoBack = headerBack != null;
|
|
74
81
|
|
|
75
82
|
const {
|
|
76
83
|
header,
|
|
@@ -86,6 +93,10 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
86
93
|
|
|
87
94
|
const nextPresentation = nextDescriptor?.options.presentation;
|
|
88
95
|
|
|
96
|
+
const isPreloaded =
|
|
97
|
+
preloadedDescriptors[route.key] !== undefined &&
|
|
98
|
+
descriptors[route.key] === undefined;
|
|
99
|
+
|
|
89
100
|
return (
|
|
90
101
|
<Screen
|
|
91
102
|
key={route.key}
|
|
@@ -146,9 +157,10 @@ export function NativeStackView({ state, descriptors }: Props) {
|
|
|
146
157
|
StyleSheet.absoluteFill,
|
|
147
158
|
{
|
|
148
159
|
display:
|
|
149
|
-
isFocused ||
|
|
150
|
-
|
|
151
|
-
|
|
160
|
+
(isFocused ||
|
|
161
|
+
(nextPresentation != null &&
|
|
162
|
+
TRANSPARENT_PRESENTATIONS.includes(nextPresentation))) &&
|
|
163
|
+
!isPreloaded
|
|
152
164
|
? 'flex'
|
|
153
165
|
: 'none',
|
|
154
166
|
},
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Platform,
|
|
4
|
+
type StyleProp,
|
|
5
|
+
StyleSheet,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import {
|
|
9
|
+
Screen,
|
|
10
|
+
type ScreenProps,
|
|
11
|
+
ScreenStack,
|
|
12
|
+
ScreenStackHeaderConfig,
|
|
13
|
+
type ScreenStackHeaderConfigProps,
|
|
14
|
+
} from 'react-native-screens';
|
|
15
|
+
import warnOnce from 'warn-once';
|
|
16
|
+
|
|
17
|
+
import { DebugContainer } from './DebugContainer';
|
|
18
|
+
|
|
19
|
+
type Props = Omit<
|
|
20
|
+
ScreenProps,
|
|
21
|
+
'enabled' | 'isNativeStack' | 'hasLargeHeader'
|
|
22
|
+
> & {
|
|
23
|
+
headerConfig?: ScreenStackHeaderConfigProps;
|
|
24
|
+
contentStyle?: StyleProp<ViewStyle>;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export function ScreenStackContent({
|
|
28
|
+
children,
|
|
29
|
+
headerConfig,
|
|
30
|
+
activityState,
|
|
31
|
+
stackPresentation,
|
|
32
|
+
contentStyle,
|
|
33
|
+
...rest
|
|
34
|
+
}: Props) {
|
|
35
|
+
const isHeaderInModal =
|
|
36
|
+
Platform.OS === 'android'
|
|
37
|
+
? false
|
|
38
|
+
: stackPresentation !== 'push' && headerConfig?.hidden === false;
|
|
39
|
+
|
|
40
|
+
const headerHiddenPreviousRef = React.useRef(headerConfig?.hidden);
|
|
41
|
+
|
|
42
|
+
React.useEffect(() => {
|
|
43
|
+
warnOnce(
|
|
44
|
+
Platform.OS !== 'android' &&
|
|
45
|
+
stackPresentation !== 'push' &&
|
|
46
|
+
headerHiddenPreviousRef.current !== headerConfig?.hidden,
|
|
47
|
+
`Dynamically changing header's visibility in modals will result in remounting the screen and losing all local state.`
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
headerHiddenPreviousRef.current = headerConfig?.hidden;
|
|
51
|
+
}, [headerConfig?.hidden, stackPresentation]);
|
|
52
|
+
|
|
53
|
+
const content = (
|
|
54
|
+
<>
|
|
55
|
+
<DebugContainer
|
|
56
|
+
style={[
|
|
57
|
+
stackPresentation === 'formSheet'
|
|
58
|
+
? Platform.OS === 'ios'
|
|
59
|
+
? styles.absolute
|
|
60
|
+
: null
|
|
61
|
+
: styles.container,
|
|
62
|
+
contentStyle,
|
|
63
|
+
]}
|
|
64
|
+
stackPresentation={stackPresentation ?? 'push'}
|
|
65
|
+
>
|
|
66
|
+
{children}
|
|
67
|
+
</DebugContainer>
|
|
68
|
+
{/**
|
|
69
|
+
* `HeaderConfig` needs to be the direct child of `Screen` without any intermediate `View`
|
|
70
|
+
* We don't render it conditionally based on visibility to make it possible to dynamically render a custom `header`
|
|
71
|
+
* Otherwise dynamically rendering a custom `header` leaves the native header visible
|
|
72
|
+
*
|
|
73
|
+
* https://github.com/software-mansion/react-native-screens/blob/main/guides/GUIDE_FOR_LIBRARY_AUTHORS.md#screenstackheaderconfig
|
|
74
|
+
*
|
|
75
|
+
* HeaderConfig must not be first child of a Screen.
|
|
76
|
+
* See https://github.com/software-mansion/react-native-screens/pull/1825
|
|
77
|
+
* for detailed explanation.
|
|
78
|
+
*/}
|
|
79
|
+
<ScreenStackHeaderConfig {...headerConfig} />
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<Screen
|
|
85
|
+
enabled
|
|
86
|
+
isNativeStack
|
|
87
|
+
activityState={activityState}
|
|
88
|
+
stackPresentation={stackPresentation}
|
|
89
|
+
hasLargeHeader={headerConfig?.largeTitle ?? false}
|
|
90
|
+
{...rest}
|
|
91
|
+
>
|
|
92
|
+
{isHeaderInModal ? (
|
|
93
|
+
<ScreenStack style={styles.container}>
|
|
94
|
+
<Screen
|
|
95
|
+
enabled
|
|
96
|
+
isNativeStack
|
|
97
|
+
activityState={activityState}
|
|
98
|
+
hasLargeHeader={headerConfig?.largeTitle ?? false}
|
|
99
|
+
style={StyleSheet.absoluteFill}
|
|
100
|
+
>
|
|
101
|
+
{content}
|
|
102
|
+
</Screen>
|
|
103
|
+
</ScreenStack>
|
|
104
|
+
) : (
|
|
105
|
+
content
|
|
106
|
+
)}
|
|
107
|
+
</Screen>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const styles = StyleSheet.create({
|
|
112
|
+
container: {
|
|
113
|
+
flex: 1,
|
|
114
|
+
},
|
|
115
|
+
absolute: {
|
|
116
|
+
position: 'absolute',
|
|
117
|
+
top: 0,
|
|
118
|
+
start: 0,
|
|
119
|
+
end: 0,
|
|
120
|
+
},
|
|
121
|
+
});
|