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