@react-navigation/native-stack 7.0.0-alpha.8 → 7.0.0-rc.0
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.map +1 -1
- package/lib/commonjs/navigators/createNativeStackNavigator.js +19 -16
- package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/utils/debounce.js +16 -0
- package/lib/commonjs/utils/debounce.js.map +1 -0
- package/lib/commonjs/utils/getModalRoutesKeys.js +17 -0
- package/lib/commonjs/utils/getModalRoutesKeys.js.map +1 -0
- package/lib/commonjs/utils/useAnimatedHeaderHeight.js +1 -1
- package/lib/commonjs/utils/useAnimatedHeaderHeight.js.map +1 -1
- package/lib/commonjs/utils/useDismissedRouteError.js +1 -1
- package/lib/commonjs/utils/useDismissedRouteError.js.map +1 -1
- package/lib/commonjs/utils/useInvalidPreventRemoveError.js +1 -1
- package/lib/commonjs/utils/useInvalidPreventRemoveError.js.map +1 -1
- package/lib/commonjs/views/DebugContainer.js +1 -1
- package/lib/commonjs/views/DebugContainer.js.map +1 -1
- package/lib/commonjs/views/DebugContainer.native.js +3 -3
- 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 +1 -1
- package/lib/commonjs/views/FontProcessor.native.js.map +1 -1
- package/lib/commonjs/views/HeaderConfig.js +41 -39
- package/lib/commonjs/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.js +47 -58
- package/lib/commonjs/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.native.js +145 -58
- package/lib/commonjs/views/NativeStackView.native.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigators/createNativeStackNavigator.js +17 -14
- package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/utils/debounce.js +10 -0
- package/lib/module/utils/debounce.js.map +1 -0
- package/lib/module/utils/getModalRoutesKeys.js +10 -0
- package/lib/module/utils/getModalRoutesKeys.js.map +1 -0
- package/lib/module/utils/useAnimatedHeaderHeight.js.map +1 -1
- package/lib/module/utils/useDismissedRouteError.js.map +1 -1
- 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 +1 -1
- 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.map +1 -1
- package/lib/module/views/HeaderConfig.js +40 -38
- package/lib/module/views/HeaderConfig.js.map +1 -1
- package/lib/module/views/NativeStackView.js +47 -58
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +144 -57
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts +13 -8
- package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +95 -9
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/debounce.d.ts +2 -0
- package/lib/typescript/src/utils/debounce.d.ts.map +1 -0
- 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 +2 -2
- package/lib/typescript/src/utils/useAnimatedHeaderHeight.d.ts.map +1 -1
- package/lib/typescript/src/views/HeaderConfig.d.ts +0 -1
- package/lib/typescript/src/views/HeaderConfig.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.d.ts.map +1 -1
- package/lib/typescript/src/views/NativeStackView.native.d.ts +1 -1
- package/lib/typescript/src/views/NativeStackView.native.d.ts.map +1 -1
- package/package.json +15 -15
- package/src/index.tsx +1 -0
- package/src/navigators/createNativeStackNavigator.tsx +30 -6
- package/src/types.tsx +100 -6
- package/src/utils/debounce.tsx +14 -0
- package/src/utils/getModalRoutesKeys.ts +21 -0
- package/src/utils/useAnimatedHeaderHeight.tsx +1 -1
- package/src/views/HeaderConfig.tsx +12 -5
- package/src/views/NativeStackView.native.tsx +207 -98
- package/src/views/NativeStackView.tsx +119 -123
|
@@ -2,7 +2,7 @@ import * as React from 'react';
|
|
|
2
2
|
import type { Animated } from 'react-native';
|
|
3
3
|
|
|
4
4
|
export const AnimatedHeaderHeightContext = React.createContext<
|
|
5
|
-
Animated.
|
|
5
|
+
Animated.AnimatedInterpolation<number> | undefined
|
|
6
6
|
>(undefined);
|
|
7
7
|
|
|
8
8
|
export function useAnimatedHeaderHeight() {
|
|
@@ -58,16 +58,16 @@ export function HeaderConfig({
|
|
|
58
58
|
headerTintColor ?? (Platform.OS === 'ios' ? colors.primary : colors.text);
|
|
59
59
|
|
|
60
60
|
const headerBackTitleStyleFlattened =
|
|
61
|
-
StyleSheet.flatten([
|
|
61
|
+
StyleSheet.flatten([fonts.regular, headerBackTitleStyle]) || {};
|
|
62
62
|
const headerLargeTitleStyleFlattened =
|
|
63
63
|
StyleSheet.flatten([
|
|
64
|
-
headerLargeTitleStyle,
|
|
65
64
|
Platform.select({ ios: fonts.heavy, default: fonts.medium }),
|
|
65
|
+
headerLargeTitleStyle,
|
|
66
66
|
]) || {};
|
|
67
67
|
const headerTitleStyleFlattened =
|
|
68
68
|
StyleSheet.flatten([
|
|
69
|
-
headerTitleStyle,
|
|
70
69
|
Platform.select({ ios: fonts.bold, default: fonts.medium }),
|
|
70
|
+
headerTitleStyle,
|
|
71
71
|
]) || {};
|
|
72
72
|
const headerStyleFlattened = StyleSheet.flatten(headerStyle) || {};
|
|
73
73
|
const headerLargeStyleFlattened = StyleSheet.flatten(headerLargeStyle) || {};
|
|
@@ -130,6 +130,8 @@ export function HeaderConfig({
|
|
|
130
130
|
tintColor,
|
|
131
131
|
canGoBack,
|
|
132
132
|
label: headerBackTitle,
|
|
133
|
+
// `href` is only applicable to web
|
|
134
|
+
href: undefined,
|
|
133
135
|
});
|
|
134
136
|
const headerRightElement = headerRight?.({
|
|
135
137
|
tintColor,
|
|
@@ -179,7 +181,12 @@ export function HeaderConfig({
|
|
|
179
181
|
<ScreenStackHeaderConfig
|
|
180
182
|
backButtonInCustomView={backButtonInCustomView}
|
|
181
183
|
backgroundColor={headerBackgroundColor}
|
|
182
|
-
backTitle={
|
|
184
|
+
backTitle={
|
|
185
|
+
headerBackTitleVisible
|
|
186
|
+
? headerBackTitle
|
|
187
|
+
: ' ' /* For backward compatibility with react-native-screens versions <3.21.0, where `backTitleVisible` is not available */
|
|
188
|
+
}
|
|
189
|
+
backTitleVisible={headerBackTitleVisible}
|
|
183
190
|
backTitleFontFamily={backTitleFontFamily}
|
|
184
191
|
backTitleFontSize={backTitleFontSize}
|
|
185
192
|
blurEffect={headerBlurEffect}
|
|
@@ -204,7 +211,7 @@ export function HeaderConfig({
|
|
|
204
211
|
titleColor={titleColor}
|
|
205
212
|
titleFontFamily={titleFontFamily}
|
|
206
213
|
titleFontSize={titleFontSize}
|
|
207
|
-
titleFontWeight={titleFontWeight}
|
|
214
|
+
titleFontWeight={String(titleFontWeight)}
|
|
208
215
|
topInsetEnabled={headerTopInsetEnabled}
|
|
209
216
|
translucent={
|
|
210
217
|
// This defaults to `true`, so we can't pass `undefined`
|
|
@@ -20,6 +20,7 @@ import * as React from 'react';
|
|
|
20
20
|
import {
|
|
21
21
|
Animated,
|
|
22
22
|
Platform,
|
|
23
|
+
StatusBar,
|
|
23
24
|
StyleSheet,
|
|
24
25
|
useAnimatedValue,
|
|
25
26
|
View,
|
|
@@ -42,6 +43,8 @@ import type {
|
|
|
42
43
|
NativeStackNavigationHelpers,
|
|
43
44
|
NativeStackNavigationOptions,
|
|
44
45
|
} from '../types';
|
|
46
|
+
import { debounce } from '../utils/debounce';
|
|
47
|
+
import { getModalRouteKeys } from '../utils/getModalRoutesKeys';
|
|
45
48
|
import { AnimatedHeaderHeightContext } from '../utils/useAnimatedHeaderHeight';
|
|
46
49
|
import { useDismissedRouteError } from '../utils/useDismissedRouteError';
|
|
47
50
|
import { useInvalidPreventRemoveError } from '../utils/useInvalidPreventRemoveError';
|
|
@@ -104,7 +107,12 @@ const MaybeNestedStack = ({
|
|
|
104
107
|
if (isHeaderInModal) {
|
|
105
108
|
return (
|
|
106
109
|
<ScreenStack style={styles.container}>
|
|
107
|
-
<Screen
|
|
110
|
+
<Screen
|
|
111
|
+
enabled
|
|
112
|
+
isNativeStack
|
|
113
|
+
hasLargeHeader={options.headerLargeTitle ?? false}
|
|
114
|
+
style={StyleSheet.absoluteFill}
|
|
115
|
+
>
|
|
108
116
|
{content}
|
|
109
117
|
<HeaderConfig
|
|
110
118
|
{...options}
|
|
@@ -127,12 +135,15 @@ type SceneViewProps = {
|
|
|
127
135
|
descriptor: NativeStackDescriptor;
|
|
128
136
|
previousDescriptor?: NativeStackDescriptor;
|
|
129
137
|
nextDescriptor?: NativeStackDescriptor;
|
|
138
|
+
isPresentationModal?: boolean;
|
|
130
139
|
onWillDisappear: () => void;
|
|
140
|
+
onWillAppear: () => void;
|
|
131
141
|
onAppear: () => void;
|
|
132
142
|
onDisappear: () => void;
|
|
133
143
|
onDismissed: ScreenProps['onDismissed'];
|
|
134
144
|
onHeaderBackButtonClicked: ScreenProps['onHeaderBackButtonClicked'];
|
|
135
145
|
onNativeDismissCancelled: ScreenProps['onDismissed'];
|
|
146
|
+
onGestureCancel: ScreenProps['onGestureCancel'];
|
|
136
147
|
};
|
|
137
148
|
|
|
138
149
|
const SceneView = ({
|
|
@@ -141,20 +152,23 @@ const SceneView = ({
|
|
|
141
152
|
descriptor,
|
|
142
153
|
previousDescriptor,
|
|
143
154
|
nextDescriptor,
|
|
155
|
+
isPresentationModal,
|
|
144
156
|
onWillDisappear,
|
|
157
|
+
onWillAppear,
|
|
145
158
|
onAppear,
|
|
146
159
|
onDisappear,
|
|
147
160
|
onDismissed,
|
|
148
161
|
onHeaderBackButtonClicked,
|
|
149
162
|
onNativeDismissCancelled,
|
|
163
|
+
onGestureCancel,
|
|
150
164
|
}: SceneViewProps) => {
|
|
151
165
|
const { route, navigation, options, render } = descriptor;
|
|
152
166
|
|
|
153
167
|
let {
|
|
154
168
|
animation,
|
|
155
169
|
animationMatchesGesture,
|
|
170
|
+
presentation = isPresentationModal ? 'modal' : 'card',
|
|
156
171
|
fullScreenGestureEnabled,
|
|
157
|
-
presentation = 'card',
|
|
158
172
|
} = options;
|
|
159
173
|
|
|
160
174
|
const {
|
|
@@ -162,15 +176,23 @@ const SceneView = ({
|
|
|
162
176
|
animationTypeForReplace = 'push',
|
|
163
177
|
gestureEnabled,
|
|
164
178
|
gestureDirection = presentation === 'card' ? 'horizontal' : 'vertical',
|
|
179
|
+
gestureResponseDistance,
|
|
165
180
|
header,
|
|
166
181
|
headerBackButtonMenuEnabled,
|
|
167
182
|
headerShown,
|
|
168
183
|
headerBackground,
|
|
169
184
|
headerTransparent,
|
|
170
185
|
autoHideHomeIndicator,
|
|
186
|
+
keyboardHandlingEnabled,
|
|
171
187
|
navigationBarColor,
|
|
188
|
+
navigationBarTranslucent,
|
|
172
189
|
navigationBarHidden,
|
|
173
190
|
orientation,
|
|
191
|
+
sheetAllowedDetents = 'large',
|
|
192
|
+
sheetLargestUndimmedDetent = 'all',
|
|
193
|
+
sheetGrabberVisible = false,
|
|
194
|
+
sheetCornerRadius = -1.0,
|
|
195
|
+
sheetExpandsWhenScrolledToEdge = true,
|
|
174
196
|
statusBarAnimation,
|
|
175
197
|
statusBarHidden,
|
|
176
198
|
statusBarStyle,
|
|
@@ -231,25 +253,59 @@ const SceneView = ({
|
|
|
231
253
|
? 0
|
|
232
254
|
: insets.top;
|
|
233
255
|
|
|
234
|
-
// On models with Dynamic Island the status bar height is smaller than the safe area top inset.
|
|
235
|
-
const hasDynamicIsland = Platform.OS === 'ios' && topInset > 50;
|
|
236
|
-
const statusBarHeight = hasDynamicIsland ? topInset - 5 : topInset;
|
|
237
|
-
|
|
238
256
|
const { preventedRoutes } = usePreventRemoveContext();
|
|
239
257
|
|
|
240
|
-
const defaultHeaderHeight =
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
258
|
+
const defaultHeaderHeight = Platform.select({
|
|
259
|
+
// FIXME: Currently screens isn't using Material 3
|
|
260
|
+
// So our `getDefaultHeaderHeight` doesn't return the correct value
|
|
261
|
+
// So we hardcode the value here for now until screens is updated
|
|
262
|
+
android: 56 + topInset,
|
|
263
|
+
default: getDefaultHeaderHeight(frame, isModal, topInset),
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
const [headerHeight, setHeaderHeight] = React.useState(defaultHeaderHeight);
|
|
267
|
+
|
|
268
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
269
|
+
const setHeaderHeightDebounced = React.useCallback(
|
|
270
|
+
// Debounce the header height updates to avoid excessive re-renders
|
|
271
|
+
debounce(setHeaderHeight, 100),
|
|
272
|
+
[]
|
|
244
273
|
);
|
|
245
274
|
|
|
246
|
-
const
|
|
247
|
-
|
|
275
|
+
const hasCustomHeader = header !== undefined;
|
|
276
|
+
|
|
277
|
+
let headerHeightCorrectionOffset = 0;
|
|
248
278
|
|
|
249
|
-
|
|
279
|
+
if (isAndroid && !hasCustomHeader) {
|
|
280
|
+
const statusBarHeight = StatusBar.currentHeight ?? 0;
|
|
250
281
|
|
|
251
|
-
|
|
252
|
-
|
|
282
|
+
// FIXME: On Android, the native header height is not correctly calculated
|
|
283
|
+
// It includes status bar height even if statusbar is not translucent
|
|
284
|
+
// And the statusbar value itself doesn't match the actual status bar height
|
|
285
|
+
// So we subtract the bogus status bar height and add the actual top inset
|
|
286
|
+
headerHeightCorrectionOffset = -statusBarHeight + topInset;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const rawAnimatedHeaderHeight = useAnimatedValue(defaultHeaderHeight);
|
|
290
|
+
const animatedHeaderHeight = React.useMemo(
|
|
291
|
+
() =>
|
|
292
|
+
Animated.add<number>(
|
|
293
|
+
rawAnimatedHeaderHeight,
|
|
294
|
+
headerHeightCorrectionOffset
|
|
295
|
+
),
|
|
296
|
+
[headerHeightCorrectionOffset, rawAnimatedHeaderHeight]
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
// During the very first render topInset is > 0 when running
|
|
300
|
+
// in non edge-to-edge mode on Android, while on every consecutive render
|
|
301
|
+
// topInset === 0, causing header content to jump, as we add padding on the first frame,
|
|
302
|
+
// just to remove it in next one. To prevent this, when statusBarTranslucent is set,
|
|
303
|
+
// we apply additional padding in header only if its true.
|
|
304
|
+
// For more details see: https://github.com/react-navigation/react-navigation/pull/12014
|
|
305
|
+
const headerTopInsetEnabled =
|
|
306
|
+
typeof statusBarTranslucent === 'boolean'
|
|
307
|
+
? statusBarTranslucent
|
|
308
|
+
: topInset !== 0;
|
|
253
309
|
|
|
254
310
|
const backTitle = previousDescriptor
|
|
255
311
|
? getHeaderTitle(previousDescriptor.options, previousDescriptor.route.name)
|
|
@@ -270,7 +326,9 @@ const SceneView = ({
|
|
|
270
326
|
<Screen
|
|
271
327
|
key={route.key}
|
|
272
328
|
enabled
|
|
329
|
+
isNativeStack
|
|
273
330
|
style={StyleSheet.absoluteFill}
|
|
331
|
+
hasLargeHeader={options.headerLargeTitle ?? false}
|
|
274
332
|
customAnimationOnSwipe={animationMatchesGesture}
|
|
275
333
|
fullScreenSwipeEnabled={fullScreenGestureEnabled}
|
|
276
334
|
gestureEnabled={
|
|
@@ -281,12 +339,20 @@ const SceneView = ({
|
|
|
281
339
|
: gestureEnabled
|
|
282
340
|
}
|
|
283
341
|
homeIndicatorHidden={autoHideHomeIndicator}
|
|
342
|
+
hideKeyboardOnSwipe={keyboardHandlingEnabled}
|
|
284
343
|
navigationBarColor={navigationBarColor}
|
|
344
|
+
// @ts-expect-error prop supported from react-native-screens 3.32.0 onwards
|
|
345
|
+
navigationBarTranslucent={navigationBarTranslucent}
|
|
285
346
|
navigationBarHidden={navigationBarHidden}
|
|
286
347
|
replaceAnimation={animationTypeForReplace}
|
|
287
348
|
stackPresentation={presentation === 'card' ? 'push' : presentation}
|
|
288
349
|
stackAnimation={animation}
|
|
289
350
|
screenOrientation={orientation}
|
|
351
|
+
sheetAllowedDetents={sheetAllowedDetents}
|
|
352
|
+
sheetLargestUndimmedDetent={sheetLargestUndimmedDetent}
|
|
353
|
+
sheetGrabberVisible={sheetGrabberVisible}
|
|
354
|
+
sheetCornerRadius={sheetCornerRadius}
|
|
355
|
+
sheetExpandsWhenScrolledToEdge={sheetExpandsWhenScrolledToEdge}
|
|
290
356
|
statusBarAnimation={statusBarAnimation}
|
|
291
357
|
statusBarHidden={statusBarHidden}
|
|
292
358
|
statusBarStyle={statusBarStyle}
|
|
@@ -294,16 +360,17 @@ const SceneView = ({
|
|
|
294
360
|
statusBarTranslucent={statusBarTranslucent}
|
|
295
361
|
swipeDirection={gestureDirectionOverride}
|
|
296
362
|
transitionDuration={animationDuration}
|
|
363
|
+
onWillAppear={onWillAppear}
|
|
297
364
|
onWillDisappear={onWillDisappear}
|
|
298
365
|
onAppear={onAppear}
|
|
299
366
|
onDisappear={onDisappear}
|
|
300
367
|
onDismissed={onDismissed}
|
|
301
|
-
|
|
368
|
+
onGestureCancel={onGestureCancel}
|
|
369
|
+
gestureResponseDistance={gestureResponseDistance}
|
|
302
370
|
nativeBackButtonDismissalEnabled={false} // on Android
|
|
303
371
|
onHeaderBackButtonClicked={onHeaderBackButtonClicked}
|
|
304
372
|
preventNativeDismiss={isRemovePrevented} // on iOS
|
|
305
373
|
onNativeDismissCancelled={onNativeDismissCancelled}
|
|
306
|
-
// @ts-expect-error this prop is available since rn-screens 3.26
|
|
307
374
|
// Unfortunately, because of the bug that exists on Fabric, where native event drivers
|
|
308
375
|
// for Animated objects are being created after the first notifications about the header height
|
|
309
376
|
// from the native side, `onHeaderHeightChange` event does not notify
|
|
@@ -312,11 +379,36 @@ const SceneView = ({
|
|
|
312
379
|
[
|
|
313
380
|
{
|
|
314
381
|
nativeEvent: {
|
|
315
|
-
headerHeight:
|
|
382
|
+
headerHeight: rawAnimatedHeaderHeight,
|
|
316
383
|
},
|
|
317
384
|
},
|
|
318
385
|
],
|
|
319
|
-
{
|
|
386
|
+
{
|
|
387
|
+
useNativeDriver: true,
|
|
388
|
+
listener: (e) => {
|
|
389
|
+
if (
|
|
390
|
+
e.nativeEvent &&
|
|
391
|
+
typeof e.nativeEvent === 'object' &&
|
|
392
|
+
'headerHeight' in e.nativeEvent &&
|
|
393
|
+
typeof e.nativeEvent.headerHeight === 'number'
|
|
394
|
+
) {
|
|
395
|
+
const headerHeight =
|
|
396
|
+
e.nativeEvent.headerHeight + headerHeightCorrectionOffset;
|
|
397
|
+
|
|
398
|
+
// Only debounce if header has large title or search bar
|
|
399
|
+
// As it's the only case where the header height can change frequently
|
|
400
|
+
const doesHeaderAnimate =
|
|
401
|
+
Platform.OS === 'ios' &&
|
|
402
|
+
(options.headerLargeTitle || options.headerSearchBarOptions);
|
|
403
|
+
|
|
404
|
+
if (doesHeaderAnimate) {
|
|
405
|
+
setHeaderHeightDebounced(headerHeight);
|
|
406
|
+
} else {
|
|
407
|
+
setHeaderHeight(headerHeight);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
}
|
|
320
412
|
)}
|
|
321
413
|
// this prop is available since rn-screens 3.16
|
|
322
414
|
freezeOnBlur={freezeOnBlur}
|
|
@@ -368,7 +460,10 @@ const SceneView = ({
|
|
|
368
460
|
{header !== undefined && headerShown !== false ? (
|
|
369
461
|
<View
|
|
370
462
|
onLayout={(e) => {
|
|
371
|
-
|
|
463
|
+
const headerHeight = e.nativeEvent.layout.height;
|
|
464
|
+
|
|
465
|
+
setHeaderHeight(headerHeight);
|
|
466
|
+
rawAnimatedHeaderHeight.setValue(headerHeight);
|
|
372
467
|
}}
|
|
373
468
|
style={headerTransparent ? styles.absolute : null}
|
|
374
469
|
>
|
|
@@ -425,86 +520,100 @@ type Props = {
|
|
|
425
520
|
descriptors: NativeStackDescriptorMap;
|
|
426
521
|
};
|
|
427
522
|
|
|
428
|
-
function
|
|
523
|
+
export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
429
524
|
const { setNextDismissedKey } = useDismissedRouteError(state);
|
|
430
525
|
|
|
526
|
+
const { colors } = useTheme();
|
|
527
|
+
|
|
431
528
|
useInvalidPreventRemoveError(descriptors);
|
|
432
529
|
|
|
433
|
-
|
|
434
|
-
<ScreenStack style={styles.container}>
|
|
435
|
-
{state.routes.map((route, index) => {
|
|
436
|
-
const descriptor = descriptors[route.key];
|
|
437
|
-
const isFocused = state.index === index;
|
|
438
|
-
const previousKey = state.routes[index - 1]?.key;
|
|
439
|
-
const nextKey = state.routes[index + 1]?.key;
|
|
440
|
-
const previousDescriptor = previousKey
|
|
441
|
-
? descriptors[previousKey]
|
|
442
|
-
: undefined;
|
|
443
|
-
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
444
|
-
|
|
445
|
-
return (
|
|
446
|
-
<SceneView
|
|
447
|
-
key={route.key}
|
|
448
|
-
index={index}
|
|
449
|
-
focused={isFocused}
|
|
450
|
-
descriptor={descriptor}
|
|
451
|
-
previousDescriptor={previousDescriptor}
|
|
452
|
-
nextDescriptor={nextDescriptor}
|
|
453
|
-
onWillDisappear={() => {
|
|
454
|
-
navigation.emit({
|
|
455
|
-
type: 'transitionStart',
|
|
456
|
-
data: { closing: true },
|
|
457
|
-
target: route.key,
|
|
458
|
-
});
|
|
459
|
-
}}
|
|
460
|
-
onAppear={() => {
|
|
461
|
-
navigation.emit({
|
|
462
|
-
type: 'transitionEnd',
|
|
463
|
-
data: { closing: false },
|
|
464
|
-
target: route.key,
|
|
465
|
-
});
|
|
466
|
-
}}
|
|
467
|
-
onDisappear={() => {
|
|
468
|
-
navigation.emit({
|
|
469
|
-
type: 'transitionEnd',
|
|
470
|
-
data: { closing: true },
|
|
471
|
-
target: route.key,
|
|
472
|
-
});
|
|
473
|
-
}}
|
|
474
|
-
onDismissed={(event) => {
|
|
475
|
-
navigation.dispatch({
|
|
476
|
-
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
477
|
-
source: route.key,
|
|
478
|
-
target: state.key,
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
setNextDismissedKey(route.key);
|
|
482
|
-
}}
|
|
483
|
-
onHeaderBackButtonClicked={() => {
|
|
484
|
-
navigation.dispatch({
|
|
485
|
-
...StackActions.pop(),
|
|
486
|
-
source: route.key,
|
|
487
|
-
target: state.key,
|
|
488
|
-
});
|
|
489
|
-
}}
|
|
490
|
-
onNativeDismissCancelled={(event) => {
|
|
491
|
-
navigation.dispatch({
|
|
492
|
-
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
493
|
-
source: route.key,
|
|
494
|
-
target: state.key,
|
|
495
|
-
});
|
|
496
|
-
}}
|
|
497
|
-
/>
|
|
498
|
-
);
|
|
499
|
-
})}
|
|
500
|
-
</ScreenStack>
|
|
501
|
-
);
|
|
502
|
-
}
|
|
530
|
+
const modalRouteKeys = getModalRouteKeys(state.routes, descriptors);
|
|
503
531
|
|
|
504
|
-
export function NativeStackView(props: Props) {
|
|
505
532
|
return (
|
|
506
|
-
<SafeAreaProviderCompat>
|
|
507
|
-
<
|
|
533
|
+
<SafeAreaProviderCompat style={{ backgroundColor: colors.background }}>
|
|
534
|
+
<ScreenStack style={styles.container}>
|
|
535
|
+
{state.routes.map((route, index) => {
|
|
536
|
+
const descriptor = descriptors[route.key];
|
|
537
|
+
const isFocused = state.index === index;
|
|
538
|
+
const previousKey = state.routes[index - 1]?.key;
|
|
539
|
+
const nextKey = state.routes[index + 1]?.key;
|
|
540
|
+
const previousDescriptor = previousKey
|
|
541
|
+
? descriptors[previousKey]
|
|
542
|
+
: undefined;
|
|
543
|
+
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
544
|
+
|
|
545
|
+
const isModal = modalRouteKeys.includes(route.key);
|
|
546
|
+
|
|
547
|
+
return (
|
|
548
|
+
<SceneView
|
|
549
|
+
key={route.key}
|
|
550
|
+
index={index}
|
|
551
|
+
focused={isFocused}
|
|
552
|
+
descriptor={descriptor}
|
|
553
|
+
previousDescriptor={previousDescriptor}
|
|
554
|
+
nextDescriptor={nextDescriptor}
|
|
555
|
+
isPresentationModal={isModal}
|
|
556
|
+
onWillDisappear={() => {
|
|
557
|
+
navigation.emit({
|
|
558
|
+
type: 'transitionStart',
|
|
559
|
+
data: { closing: true },
|
|
560
|
+
target: route.key,
|
|
561
|
+
});
|
|
562
|
+
}}
|
|
563
|
+
onWillAppear={() => {
|
|
564
|
+
navigation.emit({
|
|
565
|
+
type: 'transitionStart',
|
|
566
|
+
data: { closing: false },
|
|
567
|
+
target: route.key,
|
|
568
|
+
});
|
|
569
|
+
}}
|
|
570
|
+
onAppear={() => {
|
|
571
|
+
navigation.emit({
|
|
572
|
+
type: 'transitionEnd',
|
|
573
|
+
data: { closing: false },
|
|
574
|
+
target: route.key,
|
|
575
|
+
});
|
|
576
|
+
}}
|
|
577
|
+
onDisappear={() => {
|
|
578
|
+
navigation.emit({
|
|
579
|
+
type: 'transitionEnd',
|
|
580
|
+
data: { closing: true },
|
|
581
|
+
target: route.key,
|
|
582
|
+
});
|
|
583
|
+
}}
|
|
584
|
+
onDismissed={(event) => {
|
|
585
|
+
navigation.dispatch({
|
|
586
|
+
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
587
|
+
source: route.key,
|
|
588
|
+
target: state.key,
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
setNextDismissedKey(route.key);
|
|
592
|
+
}}
|
|
593
|
+
onHeaderBackButtonClicked={() => {
|
|
594
|
+
navigation.dispatch({
|
|
595
|
+
...StackActions.pop(),
|
|
596
|
+
source: route.key,
|
|
597
|
+
target: state.key,
|
|
598
|
+
});
|
|
599
|
+
}}
|
|
600
|
+
onNativeDismissCancelled={(event) => {
|
|
601
|
+
navigation.dispatch({
|
|
602
|
+
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
603
|
+
source: route.key,
|
|
604
|
+
target: state.key,
|
|
605
|
+
});
|
|
606
|
+
}}
|
|
607
|
+
onGestureCancel={() => {
|
|
608
|
+
navigation.emit({
|
|
609
|
+
type: 'gestureCancel',
|
|
610
|
+
target: route.key,
|
|
611
|
+
});
|
|
612
|
+
}}
|
|
613
|
+
/>
|
|
614
|
+
);
|
|
615
|
+
})}
|
|
616
|
+
</ScreenStack>
|
|
508
617
|
</SafeAreaProviderCompat>
|
|
509
618
|
);
|
|
510
619
|
}
|
|
@@ -520,14 +629,14 @@ const styles = StyleSheet.create({
|
|
|
520
629
|
absolute: {
|
|
521
630
|
position: 'absolute',
|
|
522
631
|
top: 0,
|
|
523
|
-
|
|
524
|
-
|
|
632
|
+
start: 0,
|
|
633
|
+
end: 0,
|
|
525
634
|
},
|
|
526
635
|
translucent: {
|
|
527
636
|
position: 'absolute',
|
|
528
637
|
top: 0,
|
|
529
|
-
|
|
530
|
-
|
|
638
|
+
start: 0,
|
|
639
|
+
end: 0,
|
|
531
640
|
zIndex: 1,
|
|
532
641
|
elevation: 1,
|
|
533
642
|
},
|