@react-navigation/native-stack 7.0.0-alpha.9 → 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 -57
- package/lib/commonjs/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.native.js +110 -54
- 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 -57
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +109 -53
- 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 +16 -4
- 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.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 +13 -13
- package/src/index.tsx +1 -0
- package/src/navigators/createNativeStackNavigator.tsx +30 -6
- package/src/types.tsx +23 -2
- 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 +180 -101
- package/src/views/NativeStackView.tsx +141 -144
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createNavigatorFactory,
|
|
3
3
|
type EventArg,
|
|
4
|
+
type NavigatorTypeBagBase,
|
|
4
5
|
type ParamListBase,
|
|
5
6
|
type StackActionHelpers,
|
|
6
7
|
StackActions,
|
|
7
8
|
type StackNavigationState,
|
|
8
9
|
StackRouter,
|
|
9
10
|
type StackRouterOptions,
|
|
11
|
+
type StaticConfig,
|
|
12
|
+
type TypedNavigator,
|
|
10
13
|
useNavigationBuilder,
|
|
11
14
|
} from '@react-navigation/native';
|
|
12
15
|
import * as React from 'react';
|
|
@@ -14,6 +17,7 @@ import * as React from 'react';
|
|
|
14
17
|
import type {
|
|
15
18
|
NativeStackNavigationEventMap,
|
|
16
19
|
NativeStackNavigationOptions,
|
|
20
|
+
NativeStackNavigationProp,
|
|
17
21
|
NativeStackNavigatorProps,
|
|
18
22
|
} from '../types';
|
|
19
23
|
import { NativeStackView } from '../views/NativeStackView';
|
|
@@ -26,6 +30,7 @@ function NativeStackNavigator({
|
|
|
26
30
|
screenListeners,
|
|
27
31
|
screenOptions,
|
|
28
32
|
screenLayout,
|
|
33
|
+
UNSTABLE_getStateForRouteNamesChange,
|
|
29
34
|
...rest
|
|
30
35
|
}: NativeStackNavigatorProps) {
|
|
31
36
|
const { state, descriptors, navigation, NavigationContent } =
|
|
@@ -43,6 +48,7 @@ function NativeStackNavigator({
|
|
|
43
48
|
screenListeners,
|
|
44
49
|
screenOptions,
|
|
45
50
|
screenLayout,
|
|
51
|
+
UNSTABLE_getStateForRouteNamesChange,
|
|
46
52
|
});
|
|
47
53
|
|
|
48
54
|
React.useEffect(
|
|
@@ -83,9 +89,27 @@ function NativeStackNavigator({
|
|
|
83
89
|
);
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
export
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
+
export function createNativeStackNavigator<
|
|
93
|
+
ParamList extends ParamListBase,
|
|
94
|
+
NavigatorID extends string | undefined = undefined,
|
|
95
|
+
TypeBag extends NavigatorTypeBagBase = {
|
|
96
|
+
ParamList: ParamList;
|
|
97
|
+
NavigatorID: NavigatorID;
|
|
98
|
+
State: StackNavigationState<ParamList>;
|
|
99
|
+
ScreenOptions: NativeStackNavigationOptions;
|
|
100
|
+
EventMap: NativeStackNavigationEventMap;
|
|
101
|
+
NavigationList: {
|
|
102
|
+
[RouteName in keyof ParamList]: NativeStackNavigationProp<
|
|
103
|
+
ParamList,
|
|
104
|
+
RouteName,
|
|
105
|
+
NavigatorID
|
|
106
|
+
>;
|
|
107
|
+
};
|
|
108
|
+
Navigator: typeof NativeStackNavigator;
|
|
109
|
+
},
|
|
110
|
+
Config extends StaticConfig<TypeBag> | undefined =
|
|
111
|
+
| StaticConfig<TypeBag>
|
|
112
|
+
| undefined,
|
|
113
|
+
>(config?: Config): TypedNavigator<TypeBag, Config> {
|
|
114
|
+
return createNavigatorFactory(NativeStackNavigator)(config);
|
|
115
|
+
}
|
package/src/types.tsx
CHANGED
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
StackActionHelpers,
|
|
10
10
|
StackNavigationState,
|
|
11
11
|
StackRouterOptions,
|
|
12
|
+
Theme,
|
|
12
13
|
} from '@react-navigation/native';
|
|
13
14
|
import type {
|
|
14
15
|
ImageSourcePropType,
|
|
@@ -61,6 +62,14 @@ export type NativeStackScreenProps<
|
|
|
61
62
|
route: RouteProp<ParamList, RouteName>;
|
|
62
63
|
};
|
|
63
64
|
|
|
65
|
+
export type NativeStackOptionsArgs<
|
|
66
|
+
ParamList extends ParamListBase,
|
|
67
|
+
RouteName extends keyof ParamList = keyof ParamList,
|
|
68
|
+
NavigatorID extends string | undefined = undefined,
|
|
69
|
+
> = NativeStackScreenProps<ParamList, RouteName, NavigatorID> & {
|
|
70
|
+
theme: Theme;
|
|
71
|
+
};
|
|
72
|
+
|
|
64
73
|
export type NativeStackNavigationHelpers = NavigationHelpers<
|
|
65
74
|
ParamListBase,
|
|
66
75
|
NativeStackNavigationEventMap
|
|
@@ -114,6 +123,10 @@ export type NativeStackHeaderLeftProps = NativeStackHeaderRightProps & {
|
|
|
114
123
|
* By default, this is only shown on iOS.
|
|
115
124
|
*/
|
|
116
125
|
label?: string;
|
|
126
|
+
/**
|
|
127
|
+
* The `href` to use for the anchor tag on web
|
|
128
|
+
*/
|
|
129
|
+
href?: string;
|
|
117
130
|
};
|
|
118
131
|
|
|
119
132
|
export type NativeStackNavigationOptions = {
|
|
@@ -319,7 +332,7 @@ export type NativeStackNavigationOptions = {
|
|
|
319
332
|
*
|
|
320
333
|
* Only supported on iOS and Android.
|
|
321
334
|
*/
|
|
322
|
-
headerSearchBarOptions?: SearchBarProps
|
|
335
|
+
headerSearchBarOptions?: Omit<SearchBarProps, 'ref'>;
|
|
323
336
|
/**
|
|
324
337
|
* Boolean indicating whether to show the menu on longPress of iOS >= 14 back button. Defaults to `true`.
|
|
325
338
|
* Requires `react-native-screens` version >=3.3.0.
|
|
@@ -347,6 +360,12 @@ export type NativeStackNavigationOptions = {
|
|
|
347
360
|
* @platform android
|
|
348
361
|
*/
|
|
349
362
|
navigationBarColor?: string;
|
|
363
|
+
/**
|
|
364
|
+
* Boolean indicating whether the content should be visible behind the navigation bar. Defaults to `false`.
|
|
365
|
+
*
|
|
366
|
+
* @platform android
|
|
367
|
+
*/
|
|
368
|
+
navigationBarTranslucent?: boolean;
|
|
350
369
|
/**
|
|
351
370
|
* Sets the visibility of the navigation bar. Defaults to `false`.
|
|
352
371
|
*
|
|
@@ -571,9 +590,11 @@ export type NativeStackNavigationOptions = {
|
|
|
571
590
|
|
|
572
591
|
export type NativeStackNavigatorProps = DefaultNavigatorOptions<
|
|
573
592
|
ParamListBase,
|
|
593
|
+
string | undefined,
|
|
574
594
|
StackNavigationState<ParamListBase>,
|
|
575
595
|
NativeStackNavigationOptions,
|
|
576
|
-
NativeStackNavigationEventMap
|
|
596
|
+
NativeStackNavigationEventMap,
|
|
597
|
+
NativeStackNavigationProp<ParamListBase>
|
|
577
598
|
> &
|
|
578
599
|
StackRouterOptions &
|
|
579
600
|
NativeStackNavigationConfig;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export function debounce<T extends (...args: any[]) => void>(
|
|
2
|
+
func: T,
|
|
3
|
+
duration: number
|
|
4
|
+
): T {
|
|
5
|
+
let timeout: NodeJS.Timeout;
|
|
6
|
+
|
|
7
|
+
return function (this: unknown, ...args) {
|
|
8
|
+
clearTimeout(timeout);
|
|
9
|
+
|
|
10
|
+
timeout = setTimeout(() => {
|
|
11
|
+
func.apply(this, args);
|
|
12
|
+
}, duration);
|
|
13
|
+
} as T;
|
|
14
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Route } from '@react-navigation/native';
|
|
2
|
+
|
|
3
|
+
import type { NativeStackDescriptorMap } from '../types';
|
|
4
|
+
|
|
5
|
+
export const getModalRouteKeys = (
|
|
6
|
+
routes: Route<string>[],
|
|
7
|
+
descriptors: NativeStackDescriptorMap
|
|
8
|
+
) =>
|
|
9
|
+
routes.reduce<string[]>((acc, route) => {
|
|
10
|
+
const { presentation } = descriptors[route.key]?.options ?? {};
|
|
11
|
+
|
|
12
|
+
if (
|
|
13
|
+
(acc.length && !presentation) ||
|
|
14
|
+
presentation === 'modal' ||
|
|
15
|
+
presentation === 'transparentModal'
|
|
16
|
+
) {
|
|
17
|
+
acc.push(route.key);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return acc;
|
|
21
|
+
}, []);
|
|
@@ -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';
|
|
@@ -132,6 +135,7 @@ type SceneViewProps = {
|
|
|
132
135
|
descriptor: NativeStackDescriptor;
|
|
133
136
|
previousDescriptor?: NativeStackDescriptor;
|
|
134
137
|
nextDescriptor?: NativeStackDescriptor;
|
|
138
|
+
isPresentationModal?: boolean;
|
|
135
139
|
onWillDisappear: () => void;
|
|
136
140
|
onWillAppear: () => void;
|
|
137
141
|
onAppear: () => void;
|
|
@@ -148,6 +152,7 @@ const SceneView = ({
|
|
|
148
152
|
descriptor,
|
|
149
153
|
previousDescriptor,
|
|
150
154
|
nextDescriptor,
|
|
155
|
+
isPresentationModal,
|
|
151
156
|
onWillDisappear,
|
|
152
157
|
onWillAppear,
|
|
153
158
|
onAppear,
|
|
@@ -162,8 +167,8 @@ const SceneView = ({
|
|
|
162
167
|
let {
|
|
163
168
|
animation,
|
|
164
169
|
animationMatchesGesture,
|
|
170
|
+
presentation = isPresentationModal ? 'modal' : 'card',
|
|
165
171
|
fullScreenGestureEnabled,
|
|
166
|
-
presentation = 'card',
|
|
167
172
|
} = options;
|
|
168
173
|
|
|
169
174
|
const {
|
|
@@ -180,6 +185,7 @@ const SceneView = ({
|
|
|
180
185
|
autoHideHomeIndicator,
|
|
181
186
|
keyboardHandlingEnabled,
|
|
182
187
|
navigationBarColor,
|
|
188
|
+
navigationBarTranslucent,
|
|
183
189
|
navigationBarHidden,
|
|
184
190
|
orientation,
|
|
185
191
|
sheetAllowedDetents = 'large',
|
|
@@ -249,15 +255,57 @@ const SceneView = ({
|
|
|
249
255
|
|
|
250
256
|
const { preventedRoutes } = usePreventRemoveContext();
|
|
251
257
|
|
|
252
|
-
const defaultHeaderHeight =
|
|
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
|
+
[]
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
const hasCustomHeader = header !== undefined;
|
|
276
|
+
|
|
277
|
+
let headerHeightCorrectionOffset = 0;
|
|
278
|
+
|
|
279
|
+
if (isAndroid && !hasCustomHeader) {
|
|
280
|
+
const statusBarHeight = StatusBar.currentHeight ?? 0;
|
|
253
281
|
|
|
254
|
-
|
|
255
|
-
|
|
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
|
+
}
|
|
256
288
|
|
|
257
|
-
const
|
|
289
|
+
const rawAnimatedHeaderHeight = useAnimatedValue(defaultHeaderHeight);
|
|
290
|
+
const animatedHeaderHeight = React.useMemo(
|
|
291
|
+
() =>
|
|
292
|
+
Animated.add<number>(
|
|
293
|
+
rawAnimatedHeaderHeight,
|
|
294
|
+
headerHeightCorrectionOffset
|
|
295
|
+
),
|
|
296
|
+
[headerHeightCorrectionOffset, rawAnimatedHeaderHeight]
|
|
297
|
+
);
|
|
258
298
|
|
|
259
|
-
|
|
260
|
-
|
|
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;
|
|
261
309
|
|
|
262
310
|
const backTitle = previousDescriptor
|
|
263
311
|
? getHeaderTitle(previousDescriptor.options, previousDescriptor.route.name)
|
|
@@ -293,6 +341,8 @@ const SceneView = ({
|
|
|
293
341
|
homeIndicatorHidden={autoHideHomeIndicator}
|
|
294
342
|
hideKeyboardOnSwipe={keyboardHandlingEnabled}
|
|
295
343
|
navigationBarColor={navigationBarColor}
|
|
344
|
+
// @ts-expect-error prop supported from react-native-screens 3.32.0 onwards
|
|
345
|
+
navigationBarTranslucent={navigationBarTranslucent}
|
|
296
346
|
navigationBarHidden={navigationBarHidden}
|
|
297
347
|
replaceAnimation={animationTypeForReplace}
|
|
298
348
|
stackPresentation={presentation === 'card' ? 'push' : presentation}
|
|
@@ -329,11 +379,36 @@ const SceneView = ({
|
|
|
329
379
|
[
|
|
330
380
|
{
|
|
331
381
|
nativeEvent: {
|
|
332
|
-
headerHeight:
|
|
382
|
+
headerHeight: rawAnimatedHeaderHeight,
|
|
333
383
|
},
|
|
334
384
|
},
|
|
335
385
|
],
|
|
336
|
-
{
|
|
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
|
+
}
|
|
337
412
|
)}
|
|
338
413
|
// this prop is available since rn-screens 3.16
|
|
339
414
|
freezeOnBlur={freezeOnBlur}
|
|
@@ -385,7 +460,10 @@ const SceneView = ({
|
|
|
385
460
|
{header !== undefined && headerShown !== false ? (
|
|
386
461
|
<View
|
|
387
462
|
onLayout={(e) => {
|
|
388
|
-
|
|
463
|
+
const headerHeight = e.nativeEvent.layout.height;
|
|
464
|
+
|
|
465
|
+
setHeaderHeight(headerHeight);
|
|
466
|
+
rawAnimatedHeaderHeight.setValue(headerHeight);
|
|
389
467
|
}}
|
|
390
468
|
style={headerTransparent ? styles.absolute : null}
|
|
391
469
|
>
|
|
@@ -442,99 +520,100 @@ type Props = {
|
|
|
442
520
|
descriptors: NativeStackDescriptorMap;
|
|
443
521
|
};
|
|
444
522
|
|
|
445
|
-
function
|
|
523
|
+
export function NativeStackView({ state, navigation, descriptors }: Props) {
|
|
446
524
|
const { setNextDismissedKey } = useDismissedRouteError(state);
|
|
447
525
|
|
|
526
|
+
const { colors } = useTheme();
|
|
527
|
+
|
|
448
528
|
useInvalidPreventRemoveError(descriptors);
|
|
449
529
|
|
|
450
|
-
|
|
451
|
-
<ScreenStack style={styles.container}>
|
|
452
|
-
{state.routes.map((route, index) => {
|
|
453
|
-
const descriptor = descriptors[route.key];
|
|
454
|
-
const isFocused = state.index === index;
|
|
455
|
-
const previousKey = state.routes[index - 1]?.key;
|
|
456
|
-
const nextKey = state.routes[index + 1]?.key;
|
|
457
|
-
const previousDescriptor = previousKey
|
|
458
|
-
? descriptors[previousKey]
|
|
459
|
-
: undefined;
|
|
460
|
-
const nextDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
461
|
-
|
|
462
|
-
return (
|
|
463
|
-
<SceneView
|
|
464
|
-
key={route.key}
|
|
465
|
-
index={index}
|
|
466
|
-
focused={isFocused}
|
|
467
|
-
descriptor={descriptor}
|
|
468
|
-
previousDescriptor={previousDescriptor}
|
|
469
|
-
nextDescriptor={nextDescriptor}
|
|
470
|
-
onWillDisappear={() => {
|
|
471
|
-
navigation.emit({
|
|
472
|
-
type: 'transitionStart',
|
|
473
|
-
data: { closing: true },
|
|
474
|
-
target: route.key,
|
|
475
|
-
});
|
|
476
|
-
}}
|
|
477
|
-
onWillAppear={() => {
|
|
478
|
-
navigation.emit({
|
|
479
|
-
type: 'transitionStart',
|
|
480
|
-
data: { closing: false },
|
|
481
|
-
target: route.key,
|
|
482
|
-
});
|
|
483
|
-
}}
|
|
484
|
-
onAppear={() => {
|
|
485
|
-
navigation.emit({
|
|
486
|
-
type: 'transitionEnd',
|
|
487
|
-
data: { closing: false },
|
|
488
|
-
target: route.key,
|
|
489
|
-
});
|
|
490
|
-
}}
|
|
491
|
-
onDisappear={() => {
|
|
492
|
-
navigation.emit({
|
|
493
|
-
type: 'transitionEnd',
|
|
494
|
-
data: { closing: true },
|
|
495
|
-
target: route.key,
|
|
496
|
-
});
|
|
497
|
-
}}
|
|
498
|
-
onDismissed={(event) => {
|
|
499
|
-
navigation.dispatch({
|
|
500
|
-
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
501
|
-
source: route.key,
|
|
502
|
-
target: state.key,
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
setNextDismissedKey(route.key);
|
|
506
|
-
}}
|
|
507
|
-
onHeaderBackButtonClicked={() => {
|
|
508
|
-
navigation.dispatch({
|
|
509
|
-
...StackActions.pop(),
|
|
510
|
-
source: route.key,
|
|
511
|
-
target: state.key,
|
|
512
|
-
});
|
|
513
|
-
}}
|
|
514
|
-
onNativeDismissCancelled={(event) => {
|
|
515
|
-
navigation.dispatch({
|
|
516
|
-
...StackActions.pop(event.nativeEvent.dismissCount),
|
|
517
|
-
source: route.key,
|
|
518
|
-
target: state.key,
|
|
519
|
-
});
|
|
520
|
-
}}
|
|
521
|
-
onGestureCancel={() => {
|
|
522
|
-
navigation.emit({
|
|
523
|
-
type: 'gestureCancel',
|
|
524
|
-
target: route.key,
|
|
525
|
-
});
|
|
526
|
-
}}
|
|
527
|
-
/>
|
|
528
|
-
);
|
|
529
|
-
})}
|
|
530
|
-
</ScreenStack>
|
|
531
|
-
);
|
|
532
|
-
}
|
|
530
|
+
const modalRouteKeys = getModalRouteKeys(state.routes, descriptors);
|
|
533
531
|
|
|
534
|
-
export function NativeStackView(props: Props) {
|
|
535
532
|
return (
|
|
536
|
-
<SafeAreaProviderCompat>
|
|
537
|
-
<
|
|
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>
|
|
538
617
|
</SafeAreaProviderCompat>
|
|
539
618
|
);
|
|
540
619
|
}
|
|
@@ -550,14 +629,14 @@ const styles = StyleSheet.create({
|
|
|
550
629
|
absolute: {
|
|
551
630
|
position: 'absolute',
|
|
552
631
|
top: 0,
|
|
553
|
-
|
|
554
|
-
|
|
632
|
+
start: 0,
|
|
633
|
+
end: 0,
|
|
555
634
|
},
|
|
556
635
|
translucent: {
|
|
557
636
|
position: 'absolute',
|
|
558
637
|
top: 0,
|
|
559
|
-
|
|
560
|
-
|
|
638
|
+
start: 0,
|
|
639
|
+
end: 0,
|
|
561
640
|
zIndex: 1,
|
|
562
641
|
elevation: 1,
|
|
563
642
|
},
|