@react-navigation/native-stack 6.2.4 → 6.4.1
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 +8 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/navigators/createNativeStackNavigator.js +12 -10
- package/lib/commonjs/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/commonjs/views/HeaderConfig.js +62 -39
- package/lib/commonjs/views/HeaderConfig.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.js +62 -40
- package/lib/commonjs/views/NativeStackView.js.map +1 -1
- package/lib/commonjs/views/NativeStackView.native.js +51 -40
- 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 +12 -10
- package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/module/views/HeaderConfig.js +62 -39
- package/lib/module/views/HeaderConfig.js.map +1 -1
- package/lib/module/views/NativeStackView.js +61 -40
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +53 -42
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -1
- package/lib/typescript/src/types.d.ts +19 -10
- package/lib/typescript/src/views/HeaderConfig.d.ts +2 -1
- package/package.json +6 -6
- package/src/index.tsx +6 -0
- package/src/navigators/createNativeStackNavigator.tsx +21 -18
- package/src/types.tsx +20 -8
- package/src/views/HeaderConfig.tsx +153 -103
- package/src/views/NativeStackView.native.tsx +61 -47
- package/src/views/NativeStackView.tsx +25 -3
|
@@ -25,18 +25,19 @@ function NativeStackNavigator({
|
|
|
25
25
|
screenOptions,
|
|
26
26
|
...rest
|
|
27
27
|
}: NativeStackNavigatorProps) {
|
|
28
|
-
const { state, descriptors, navigation } =
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
const { state, descriptors, navigation, NavigationContent } =
|
|
29
|
+
useNavigationBuilder<
|
|
30
|
+
StackNavigationState<ParamListBase>,
|
|
31
|
+
StackRouterOptions,
|
|
32
|
+
StackActionHelpers<ParamListBase>,
|
|
33
|
+
NativeStackNavigationOptions,
|
|
34
|
+
NativeStackNavigationEventMap
|
|
35
|
+
>(StackRouter, {
|
|
36
|
+
initialRouteName,
|
|
37
|
+
children,
|
|
38
|
+
screenListeners,
|
|
39
|
+
screenOptions,
|
|
40
|
+
});
|
|
40
41
|
|
|
41
42
|
React.useEffect(
|
|
42
43
|
() =>
|
|
@@ -64,12 +65,14 @@ function NativeStackNavigator({
|
|
|
64
65
|
);
|
|
65
66
|
|
|
66
67
|
return (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
68
|
+
<NavigationContent>
|
|
69
|
+
<NativeStackView
|
|
70
|
+
{...rest}
|
|
71
|
+
state={state}
|
|
72
|
+
navigation={navigation}
|
|
73
|
+
descriptors={descriptors}
|
|
74
|
+
/>
|
|
75
|
+
</NavigationContent>
|
|
73
76
|
);
|
|
74
77
|
}
|
|
75
78
|
|
package/src/types.tsx
CHANGED
|
@@ -85,20 +85,23 @@ export type NativeStackHeaderProps = {
|
|
|
85
85
|
navigation: NativeStackNavigationProp<ParamListBase>;
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
export type
|
|
88
|
+
export type HeaderButtonProps = {
|
|
89
89
|
/**
|
|
90
90
|
* Tint color for the header.
|
|
91
91
|
*/
|
|
92
92
|
tintColor?: string;
|
|
93
|
+
/**
|
|
94
|
+
* Whether it's possible to navigate back in stack.
|
|
95
|
+
*/
|
|
96
|
+
canGoBack: boolean;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export type HeaderBackButtonProps = HeaderButtonProps & {
|
|
93
100
|
/**
|
|
94
101
|
* Label text for the button. Usually the title of the previous screen.
|
|
95
102
|
* By default, this is only shown on iOS.
|
|
96
103
|
*/
|
|
97
104
|
label?: string;
|
|
98
|
-
/**
|
|
99
|
-
* Whether it's possible to navigate back in stack.
|
|
100
|
-
*/
|
|
101
|
-
canGoBack: boolean;
|
|
102
105
|
};
|
|
103
106
|
|
|
104
107
|
export type NativeStackNavigationOptions = {
|
|
@@ -179,6 +182,7 @@ export type NativeStackNavigationOptions = {
|
|
|
179
182
|
*
|
|
180
183
|
* For large title to collapse on scroll, the content of the screen should be wrapped in a scrollable view such as `ScrollView` or `FlatList`.
|
|
181
184
|
* If the scrollable area doesn't fill the screen, the large title won't collapse on scroll.
|
|
185
|
+
* You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc.
|
|
182
186
|
*
|
|
183
187
|
* Only supported on iOS.
|
|
184
188
|
*
|
|
@@ -245,6 +249,12 @@ export type NativeStackNavigationOptions = {
|
|
|
245
249
|
* Tint color for the header. Changes the color of back button and title.
|
|
246
250
|
*/
|
|
247
251
|
headerTintColor?: string;
|
|
252
|
+
/**
|
|
253
|
+
* Function which returns a React Element to render as the background of the header.
|
|
254
|
+
* This is useful for using backgrounds such as an image, a gradient, blur effect etc.
|
|
255
|
+
* You can use this with `headerTransparent` to render content underneath a translucent header.
|
|
256
|
+
*/
|
|
257
|
+
headerBackground?: () => React.ReactNode;
|
|
248
258
|
/**
|
|
249
259
|
* Function which returns a React Element to display on the left side of the header.
|
|
250
260
|
* This replaces the back button. See `headerBackVisible` to show the back button along side left element.
|
|
@@ -253,7 +263,7 @@ export type NativeStackNavigationOptions = {
|
|
|
253
263
|
/**
|
|
254
264
|
* Function which returns a React Element to display on the right side of the header.
|
|
255
265
|
*/
|
|
256
|
-
headerRight?: (props:
|
|
266
|
+
headerRight?: (props: HeaderButtonProps) => React.ReactNode;
|
|
257
267
|
/**
|
|
258
268
|
* String or a function that returns a React Element to be used by the header.
|
|
259
269
|
* Defaults to screen `title` or route name.
|
|
@@ -295,9 +305,11 @@ export type NativeStackNavigationOptions = {
|
|
|
295
305
|
}
|
|
296
306
|
>;
|
|
297
307
|
/**
|
|
298
|
-
* Options to render a native search bar
|
|
308
|
+
* Options to render a native search bar.
|
|
309
|
+
* You also need to specify `contentInsetAdjustmentBehavior="automatic"` in your `ScrollView`, `FlatList` etc.
|
|
310
|
+
* If you don't have a `ScrollView`, specify `headerTransparent: false`.
|
|
299
311
|
*
|
|
300
|
-
*
|
|
312
|
+
* Only supported on iOS and Android.
|
|
301
313
|
*/
|
|
302
314
|
headerSearchBarOptions?: SearchBarProps;
|
|
303
315
|
/**
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
} from 'react-native';
|
|
11
11
|
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
|
12
12
|
import {
|
|
13
|
+
isSearchBarAvailableForCurrentPlatform,
|
|
13
14
|
ScreenStackHeaderBackButtonImage,
|
|
14
15
|
ScreenStackHeaderCenterView,
|
|
15
16
|
ScreenStackHeaderConfig,
|
|
@@ -23,11 +24,13 @@ import type { NativeStackNavigationOptions } from '../types';
|
|
|
23
24
|
import { processFonts } from './FontProcessor';
|
|
24
25
|
|
|
25
26
|
type Props = NativeStackNavigationOptions & {
|
|
27
|
+
headerHeight: number;
|
|
26
28
|
route: Route<string>;
|
|
27
29
|
canGoBack: boolean;
|
|
28
30
|
};
|
|
29
31
|
|
|
30
32
|
export default function HeaderConfig({
|
|
33
|
+
headerHeight,
|
|
31
34
|
headerBackImageSource,
|
|
32
35
|
headerBackButtonMenuEnabled,
|
|
33
36
|
headerBackTitle,
|
|
@@ -39,6 +42,7 @@ export default function HeaderConfig({
|
|
|
39
42
|
headerLargeTitle,
|
|
40
43
|
headerLargeTitleShadowVisible,
|
|
41
44
|
headerLargeTitleStyle,
|
|
45
|
+
headerBackground,
|
|
42
46
|
headerLeft,
|
|
43
47
|
headerRight,
|
|
44
48
|
headerShown,
|
|
@@ -55,6 +59,7 @@ export default function HeaderConfig({
|
|
|
55
59
|
canGoBack,
|
|
56
60
|
}: Props): JSX.Element {
|
|
57
61
|
const insets = useSafeAreaInsets();
|
|
62
|
+
|
|
58
63
|
const { colors } = useTheme();
|
|
59
64
|
const tintColor =
|
|
60
65
|
headerTintColor ?? (Platform.OS === 'ios' ? colors.primary : colors.text);
|
|
@@ -96,20 +101,28 @@ export default function HeaderConfig({
|
|
|
96
101
|
|
|
97
102
|
const headerLeftElement = headerLeft?.({
|
|
98
103
|
tintColor,
|
|
104
|
+
canGoBack,
|
|
99
105
|
label: headerBackTitle,
|
|
106
|
+
});
|
|
107
|
+
const headerRightElement = headerRight?.({
|
|
108
|
+
tintColor,
|
|
100
109
|
canGoBack,
|
|
101
110
|
});
|
|
102
|
-
const headerRightElement = headerRight?.({ tintColor });
|
|
103
111
|
const headerTitleElement =
|
|
104
112
|
typeof headerTitle === 'function'
|
|
105
113
|
? headerTitle({ tintColor, children: titleText })
|
|
106
114
|
: null;
|
|
107
115
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
116
|
+
const supportsHeaderSearchBar =
|
|
117
|
+
typeof isSearchBarAvailableForCurrentPlatform === 'boolean'
|
|
118
|
+
? isSearchBarAvailableForCurrentPlatform
|
|
119
|
+
: // Fallback for older versions of react-native-screens
|
|
120
|
+
Platform.OS === 'ios' && SearchBar != null;
|
|
121
|
+
|
|
122
|
+
const hasHeaderSearchBar =
|
|
123
|
+
supportsHeaderSearchBar && headerSearchBarOptions != null;
|
|
124
|
+
|
|
125
|
+
if (headerSearchBarOptions != null && !supportsHeaderSearchBar) {
|
|
113
126
|
throw new Error(
|
|
114
127
|
`The current version of 'react-native-screens' doesn't support SearchBar in the header. Please update to the latest version to use this option.`
|
|
115
128
|
);
|
|
@@ -124,105 +137,132 @@ export default function HeaderConfig({
|
|
|
124
137
|
? headerLeftElement != null
|
|
125
138
|
: Platform.OS === 'android' && headerTitleElement != null;
|
|
126
139
|
|
|
140
|
+
const translucent =
|
|
141
|
+
headerBackground != null ||
|
|
142
|
+
headerTransparent ||
|
|
143
|
+
// When using a SearchBar or large title, the header needs to be translucent for it to work on iOS
|
|
144
|
+
((hasHeaderSearchBar || headerLargeTitle) &&
|
|
145
|
+
Platform.OS === 'ios' &&
|
|
146
|
+
headerTransparent !== false);
|
|
147
|
+
|
|
127
148
|
return (
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
direction={I18nManager.isRTL ? 'rtl' : 'ltr'}
|
|
140
|
-
disableBackButtonMenu={headerBackButtonMenuEnabled === false}
|
|
141
|
-
hidden={headerShown === false}
|
|
142
|
-
hideBackButton={headerBackVisible === false}
|
|
143
|
-
hideShadow={headerShadowVisible === false}
|
|
144
|
-
largeTitle={headerLargeTitle}
|
|
145
|
-
largeTitleBackgroundColor={headerLargeStyleFlattened.backgroundColor}
|
|
146
|
-
largeTitleColor={headerLargeTitleStyleFlattened.color}
|
|
147
|
-
largeTitleFontFamily={largeTitleFontFamily}
|
|
148
|
-
largeTitleFontSize={headerLargeTitleStyleFlattened.fontSize}
|
|
149
|
-
largeTitleFontWeight={headerLargeTitleStyleFlattened.fontWeight}
|
|
150
|
-
largeTitleHideShadow={headerLargeTitleShadowVisible === false}
|
|
151
|
-
title={typeof headerTitle === 'string' ? headerTitle : titleText}
|
|
152
|
-
titleColor={titleColor}
|
|
153
|
-
titleFontFamily={titleFontFamily}
|
|
154
|
-
titleFontSize={titleFontSize}
|
|
155
|
-
titleFontWeight={titleFontWeight}
|
|
156
|
-
topInsetEnabled={insets.top !== 0}
|
|
157
|
-
translucent={
|
|
158
|
-
// This defaults to `true`, so we can't pass `undefined`
|
|
159
|
-
headerTransparent === true
|
|
160
|
-
}
|
|
161
|
-
>
|
|
162
|
-
{Platform.OS === 'ios' ? (
|
|
163
|
-
<>
|
|
164
|
-
{headerLeftElement != null ? (
|
|
165
|
-
<ScreenStackHeaderLeftView>
|
|
166
|
-
{headerLeftElement}
|
|
167
|
-
</ScreenStackHeaderLeftView>
|
|
168
|
-
) : null}
|
|
169
|
-
{headerTitleElement != null ? (
|
|
170
|
-
<ScreenStackHeaderCenterView>
|
|
171
|
-
{headerTitleElement}
|
|
172
|
-
</ScreenStackHeaderCenterView>
|
|
173
|
-
) : null}
|
|
174
|
-
</>
|
|
175
|
-
) : (
|
|
176
|
-
<>
|
|
177
|
-
{headerLeftElement != null || typeof headerTitle === 'function' ? (
|
|
178
|
-
<ScreenStackHeaderLeftView>
|
|
179
|
-
<View style={styles.row}>
|
|
180
|
-
{headerLeftElement}
|
|
181
|
-
{headerTitleAlign !== 'center' ? (
|
|
182
|
-
typeof headerTitle === 'function' ? (
|
|
183
|
-
headerTitleElement
|
|
184
|
-
) : (
|
|
185
|
-
<HeaderTitle
|
|
186
|
-
tintColor={tintColor}
|
|
187
|
-
style={headerTitleStyleSupported}
|
|
188
|
-
>
|
|
189
|
-
{titleText}
|
|
190
|
-
</HeaderTitle>
|
|
191
|
-
)
|
|
192
|
-
) : null}
|
|
193
|
-
</View>
|
|
194
|
-
</ScreenStackHeaderLeftView>
|
|
195
|
-
) : null}
|
|
196
|
-
{headerTitleAlign === 'center' ? (
|
|
197
|
-
<ScreenStackHeaderCenterView>
|
|
198
|
-
{typeof headerTitle === 'function' ? (
|
|
199
|
-
headerTitleElement
|
|
200
|
-
) : (
|
|
201
|
-
<HeaderTitle
|
|
202
|
-
tintColor={tintColor}
|
|
203
|
-
style={headerTitleStyleSupported}
|
|
204
|
-
>
|
|
205
|
-
{titleText}
|
|
206
|
-
</HeaderTitle>
|
|
207
|
-
)}
|
|
208
|
-
</ScreenStackHeaderCenterView>
|
|
209
|
-
) : null}
|
|
210
|
-
</>
|
|
211
|
-
)}
|
|
212
|
-
{headerBackImageSource !== undefined ? (
|
|
213
|
-
<ScreenStackHeaderBackButtonImage source={headerBackImageSource} />
|
|
214
|
-
) : null}
|
|
215
|
-
{headerRightElement != null ? (
|
|
216
|
-
<ScreenStackHeaderRightView>
|
|
217
|
-
{headerRightElement}
|
|
218
|
-
</ScreenStackHeaderRightView>
|
|
219
|
-
) : null}
|
|
220
|
-
{Platform.OS === 'ios' && headerSearchBarOptions != null ? (
|
|
221
|
-
<ScreenStackHeaderSearchBarView>
|
|
222
|
-
<SearchBar {...headerSearchBarOptions} />
|
|
223
|
-
</ScreenStackHeaderSearchBarView>
|
|
149
|
+
<>
|
|
150
|
+
{headerBackground != null ? (
|
|
151
|
+
<View
|
|
152
|
+
style={[
|
|
153
|
+
styles.background,
|
|
154
|
+
headerTransparent ? styles.translucent : null,
|
|
155
|
+
{ height: headerHeight },
|
|
156
|
+
]}
|
|
157
|
+
>
|
|
158
|
+
{headerBackground()}
|
|
159
|
+
</View>
|
|
224
160
|
) : null}
|
|
225
|
-
|
|
161
|
+
<ScreenStackHeaderConfig
|
|
162
|
+
backButtonInCustomView={backButtonInCustomView}
|
|
163
|
+
backgroundColor={
|
|
164
|
+
headerStyleFlattened.backgroundColor ??
|
|
165
|
+
(headerBackground != null || headerTransparent
|
|
166
|
+
? 'transparent'
|
|
167
|
+
: colors.card)
|
|
168
|
+
}
|
|
169
|
+
backTitle={headerBackTitleVisible ? headerBackTitle : ' '}
|
|
170
|
+
backTitleFontFamily={backTitleFontFamily}
|
|
171
|
+
backTitleFontSize={headerBackTitleStyleFlattened.fontSize}
|
|
172
|
+
blurEffect={headerBlurEffect}
|
|
173
|
+
color={tintColor}
|
|
174
|
+
direction={I18nManager.isRTL ? 'rtl' : 'ltr'}
|
|
175
|
+
disableBackButtonMenu={headerBackButtonMenuEnabled === false}
|
|
176
|
+
hidden={headerShown === false}
|
|
177
|
+
hideBackButton={headerBackVisible === false}
|
|
178
|
+
hideShadow={
|
|
179
|
+
headerShadowVisible === false ||
|
|
180
|
+
headerBackground != null ||
|
|
181
|
+
headerTransparent
|
|
182
|
+
}
|
|
183
|
+
largeTitle={headerLargeTitle}
|
|
184
|
+
largeTitleBackgroundColor={headerLargeStyleFlattened.backgroundColor}
|
|
185
|
+
largeTitleColor={headerLargeTitleStyleFlattened.color}
|
|
186
|
+
largeTitleFontFamily={largeTitleFontFamily}
|
|
187
|
+
largeTitleFontSize={headerLargeTitleStyleFlattened.fontSize}
|
|
188
|
+
largeTitleFontWeight={headerLargeTitleStyleFlattened.fontWeight}
|
|
189
|
+
largeTitleHideShadow={headerLargeTitleShadowVisible === false}
|
|
190
|
+
title={typeof headerTitle === 'string' ? headerTitle : titleText}
|
|
191
|
+
titleColor={titleColor}
|
|
192
|
+
titleFontFamily={titleFontFamily}
|
|
193
|
+
titleFontSize={titleFontSize}
|
|
194
|
+
titleFontWeight={titleFontWeight}
|
|
195
|
+
topInsetEnabled={insets.top !== 0}
|
|
196
|
+
translucent={
|
|
197
|
+
// This defaults to `true`, so we can't pass `undefined`
|
|
198
|
+
translucent === true
|
|
199
|
+
}
|
|
200
|
+
>
|
|
201
|
+
{Platform.OS === 'ios' ? (
|
|
202
|
+
<>
|
|
203
|
+
{headerLeftElement != null ? (
|
|
204
|
+
<ScreenStackHeaderLeftView>
|
|
205
|
+
{headerLeftElement}
|
|
206
|
+
</ScreenStackHeaderLeftView>
|
|
207
|
+
) : null}
|
|
208
|
+
{headerTitleElement != null ? (
|
|
209
|
+
<ScreenStackHeaderCenterView>
|
|
210
|
+
{headerTitleElement}
|
|
211
|
+
</ScreenStackHeaderCenterView>
|
|
212
|
+
) : null}
|
|
213
|
+
</>
|
|
214
|
+
) : (
|
|
215
|
+
<>
|
|
216
|
+
{headerLeftElement != null || typeof headerTitle === 'function' ? (
|
|
217
|
+
<ScreenStackHeaderLeftView>
|
|
218
|
+
<View style={styles.row}>
|
|
219
|
+
{headerLeftElement}
|
|
220
|
+
{headerTitleAlign !== 'center' ? (
|
|
221
|
+
typeof headerTitle === 'function' ? (
|
|
222
|
+
headerTitleElement
|
|
223
|
+
) : (
|
|
224
|
+
<HeaderTitle
|
|
225
|
+
tintColor={tintColor}
|
|
226
|
+
style={headerTitleStyleSupported}
|
|
227
|
+
>
|
|
228
|
+
{titleText}
|
|
229
|
+
</HeaderTitle>
|
|
230
|
+
)
|
|
231
|
+
) : null}
|
|
232
|
+
</View>
|
|
233
|
+
</ScreenStackHeaderLeftView>
|
|
234
|
+
) : null}
|
|
235
|
+
{headerTitleAlign === 'center' ? (
|
|
236
|
+
<ScreenStackHeaderCenterView>
|
|
237
|
+
{typeof headerTitle === 'function' ? (
|
|
238
|
+
headerTitleElement
|
|
239
|
+
) : (
|
|
240
|
+
<HeaderTitle
|
|
241
|
+
tintColor={tintColor}
|
|
242
|
+
style={headerTitleStyleSupported}
|
|
243
|
+
>
|
|
244
|
+
{titleText}
|
|
245
|
+
</HeaderTitle>
|
|
246
|
+
)}
|
|
247
|
+
</ScreenStackHeaderCenterView>
|
|
248
|
+
) : null}
|
|
249
|
+
</>
|
|
250
|
+
)}
|
|
251
|
+
{headerBackImageSource !== undefined ? (
|
|
252
|
+
<ScreenStackHeaderBackButtonImage source={headerBackImageSource} />
|
|
253
|
+
) : null}
|
|
254
|
+
{headerRightElement != null ? (
|
|
255
|
+
<ScreenStackHeaderRightView>
|
|
256
|
+
{headerRightElement}
|
|
257
|
+
</ScreenStackHeaderRightView>
|
|
258
|
+
) : null}
|
|
259
|
+
{hasHeaderSearchBar ? (
|
|
260
|
+
<ScreenStackHeaderSearchBarView>
|
|
261
|
+
<SearchBar {...headerSearchBarOptions} />
|
|
262
|
+
</ScreenStackHeaderSearchBarView>
|
|
263
|
+
) : null}
|
|
264
|
+
</ScreenStackHeaderConfig>
|
|
265
|
+
</>
|
|
226
266
|
);
|
|
227
267
|
}
|
|
228
268
|
|
|
@@ -231,4 +271,14 @@ const styles = StyleSheet.create({
|
|
|
231
271
|
flexDirection: 'row',
|
|
232
272
|
alignItems: 'center',
|
|
233
273
|
},
|
|
274
|
+
translucent: {
|
|
275
|
+
position: 'absolute',
|
|
276
|
+
top: 0,
|
|
277
|
+
left: 0,
|
|
278
|
+
right: 0,
|
|
279
|
+
zIndex: 1,
|
|
280
|
+
},
|
|
281
|
+
background: {
|
|
282
|
+
overflow: 'hidden',
|
|
283
|
+
},
|
|
234
284
|
});
|
|
@@ -6,6 +6,8 @@ import {
|
|
|
6
6
|
SafeAreaProviderCompat,
|
|
7
7
|
} from '@react-navigation/elements';
|
|
8
8
|
import {
|
|
9
|
+
NavigationContext,
|
|
10
|
+
NavigationRouteContext,
|
|
9
11
|
ParamListBase,
|
|
10
12
|
Route,
|
|
11
13
|
StackActions,
|
|
@@ -13,7 +15,7 @@ import {
|
|
|
13
15
|
useTheme,
|
|
14
16
|
} from '@react-navigation/native';
|
|
15
17
|
import * as React from 'react';
|
|
16
|
-
import { Platform,
|
|
18
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
17
19
|
import {
|
|
18
20
|
useSafeAreaFrame,
|
|
19
21
|
useSafeAreaInsets,
|
|
@@ -40,11 +42,13 @@ const MaybeNestedStack = ({
|
|
|
40
42
|
options,
|
|
41
43
|
route,
|
|
42
44
|
presentation,
|
|
45
|
+
headerHeight,
|
|
43
46
|
children,
|
|
44
47
|
}: {
|
|
45
48
|
options: NativeStackNavigationOptions;
|
|
46
49
|
route: Route<string>;
|
|
47
50
|
presentation: Exclude<StackPresentationTypes, 'push'> | 'card';
|
|
51
|
+
headerHeight: number;
|
|
48
52
|
children: React.ReactNode;
|
|
49
53
|
}) => {
|
|
50
54
|
const { colors } = useTheme();
|
|
@@ -83,33 +87,17 @@ const MaybeNestedStack = ({
|
|
|
83
87
|
</DebugContainer>
|
|
84
88
|
);
|
|
85
89
|
|
|
86
|
-
const insets = useSafeAreaInsets();
|
|
87
|
-
const dimensions = useSafeAreaFrame();
|
|
88
|
-
// landscape is meaningful only for iPhone
|
|
89
|
-
const isLandscape =
|
|
90
|
-
dimensions.width > dimensions.height &&
|
|
91
|
-
!(Platform as PlatformIOSStatic).isPad &&
|
|
92
|
-
!(Platform as PlatformIOSStatic).isTVOS;
|
|
93
|
-
// `modal` and `formSheet` presentations do not take whole screen, so should not take the inset.
|
|
94
|
-
const isFullScreenModal =
|
|
95
|
-
presentation !== 'modal' && presentation !== 'formSheet';
|
|
96
|
-
const topInset = isFullScreenModal && !isLandscape ? insets.top : 0;
|
|
97
|
-
const headerHeight = getDefaultHeaderHeight(
|
|
98
|
-
dimensions,
|
|
99
|
-
!isFullScreenModal,
|
|
100
|
-
topInset
|
|
101
|
-
);
|
|
102
|
-
|
|
103
90
|
if (isHeaderInModal) {
|
|
104
91
|
return (
|
|
105
92
|
<ScreenStack style={styles.container}>
|
|
106
93
|
<Screen enabled style={StyleSheet.absoluteFill}>
|
|
107
|
-
<
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
94
|
+
<HeaderConfig
|
|
95
|
+
{...options}
|
|
96
|
+
route={route}
|
|
97
|
+
headerHeight={headerHeight}
|
|
98
|
+
canGoBack
|
|
99
|
+
/>
|
|
100
|
+
{content}
|
|
113
101
|
</Screen>
|
|
114
102
|
</ScreenStack>
|
|
115
103
|
);
|
|
@@ -142,7 +130,7 @@ const SceneView = ({
|
|
|
142
130
|
gestureEnabled,
|
|
143
131
|
header,
|
|
144
132
|
headerShown,
|
|
145
|
-
animationTypeForReplace = '
|
|
133
|
+
animationTypeForReplace = 'push',
|
|
146
134
|
animation,
|
|
147
135
|
orientation,
|
|
148
136
|
statusBarAnimation,
|
|
@@ -162,14 +150,28 @@ const SceneView = ({
|
|
|
162
150
|
? headerShown
|
|
163
151
|
: presentation === 'card' && headerShown !== false;
|
|
164
152
|
|
|
165
|
-
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
|
166
153
|
const insets = useSafeAreaInsets();
|
|
154
|
+
const frame = useSafeAreaFrame();
|
|
155
|
+
|
|
156
|
+
// `modal` and `formSheet` presentations do not take whole screen, so should not take the inset.
|
|
157
|
+
const isModal = presentation === 'modal' || presentation === 'formSheet';
|
|
158
|
+
|
|
159
|
+
// Modals are fullscreen in landscape only on iPhone
|
|
160
|
+
const isIPhone =
|
|
161
|
+
Platform.OS === 'ios' && !(Platform.isPad && Platform.isTVOS);
|
|
162
|
+
const isLandscape = frame.width > frame.height;
|
|
163
|
+
|
|
164
|
+
const topInset = isModal || (isIPhone && isLandscape) ? 0 : insets.top;
|
|
165
|
+
|
|
166
|
+
const isParentHeaderShown = React.useContext(HeaderShownContext);
|
|
167
167
|
const parentHeaderHeight = React.useContext(HeaderHeightContext);
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
168
|
+
|
|
169
|
+
const defaultHeaderHeight = getDefaultHeaderHeight(frame, isModal, topInset);
|
|
170
|
+
|
|
171
|
+
const [customHeaderHeight, setCustomHeaderHeight] =
|
|
172
|
+
React.useState(defaultHeaderHeight);
|
|
173
|
+
|
|
174
|
+
const headerHeight = header ? customHeaderHeight : defaultHeaderHeight;
|
|
173
175
|
|
|
174
176
|
return (
|
|
175
177
|
<Screen
|
|
@@ -204,25 +206,35 @@ const SceneView = ({
|
|
|
204
206
|
}
|
|
205
207
|
>
|
|
206
208
|
{header !== undefined && headerShown !== false ? (
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
209
|
+
<NavigationContext.Provider value={navigation}>
|
|
210
|
+
<NavigationRouteContext.Provider value={route}>
|
|
211
|
+
<View
|
|
212
|
+
onLayout={(e) => {
|
|
213
|
+
setCustomHeaderHeight(e.nativeEvent.layout.height);
|
|
214
|
+
}}
|
|
215
|
+
>
|
|
216
|
+
{header({
|
|
217
|
+
back: previousDescriptor
|
|
218
|
+
? {
|
|
219
|
+
title: getHeaderTitle(
|
|
220
|
+
previousDescriptor.options,
|
|
221
|
+
previousDescriptor.route.name
|
|
222
|
+
),
|
|
223
|
+
}
|
|
224
|
+
: undefined,
|
|
225
|
+
options,
|
|
226
|
+
route,
|
|
227
|
+
navigation,
|
|
228
|
+
})}
|
|
229
|
+
</View>
|
|
230
|
+
</NavigationRouteContext.Provider>
|
|
231
|
+
</NavigationContext.Provider>
|
|
221
232
|
) : (
|
|
222
233
|
<HeaderConfig
|
|
223
234
|
{...options}
|
|
224
235
|
route={route}
|
|
225
236
|
headerShown={isHeaderInPush}
|
|
237
|
+
headerHeight={headerHeight}
|
|
226
238
|
canGoBack={index !== 0}
|
|
227
239
|
/>
|
|
228
240
|
)}
|
|
@@ -230,6 +242,7 @@ const SceneView = ({
|
|
|
230
242
|
options={options}
|
|
231
243
|
route={route}
|
|
232
244
|
presentation={presentation}
|
|
245
|
+
headerHeight={headerHeight}
|
|
233
246
|
>
|
|
234
247
|
{render()}
|
|
235
248
|
</MaybeNestedStack>
|
|
@@ -246,8 +259,9 @@ type Props = {
|
|
|
246
259
|
};
|
|
247
260
|
|
|
248
261
|
function NativeStackViewInner({ state, navigation, descriptors }: Props) {
|
|
249
|
-
const [nextDismissedKey, setNextDismissedKey] =
|
|
250
|
-
|
|
262
|
+
const [nextDismissedKey, setNextDismissedKey] = React.useState<string | null>(
|
|
263
|
+
null
|
|
264
|
+
);
|
|
251
265
|
|
|
252
266
|
const dismissedRouteName = nextDismissedKey
|
|
253
267
|
? state.routes.find((route) => route.key === nextDismissedKey)?.name
|
|
@@ -25,6 +25,11 @@ type Props = {
|
|
|
25
25
|
descriptors: NativeStackDescriptorMap;
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
+
const TRANSPARENT_PRESENTATIONS = [
|
|
29
|
+
'transparentModal',
|
|
30
|
+
'containedTransparentModal',
|
|
31
|
+
];
|
|
32
|
+
|
|
28
33
|
export default function NativeStackView({ state, descriptors }: Props) {
|
|
29
34
|
return (
|
|
30
35
|
<SafeAreaProviderCompat>
|
|
@@ -33,9 +38,11 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
33
38
|
const isFocused = state.index === i;
|
|
34
39
|
const canGoBack = i !== 0;
|
|
35
40
|
const previousKey = state.routes[i - 1]?.key;
|
|
41
|
+
const nextKey = state.routes[i + 1]?.key;
|
|
36
42
|
const previousDescriptor = previousKey
|
|
37
43
|
? descriptors[previousKey]
|
|
38
44
|
: undefined;
|
|
45
|
+
const nexDescriptor = nextKey ? descriptors[nextKey] : undefined;
|
|
39
46
|
const { options, navigation, render } = descriptors[route.key];
|
|
40
47
|
|
|
41
48
|
const {
|
|
@@ -51,10 +58,13 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
51
58
|
headerStyle,
|
|
52
59
|
headerShadowVisible,
|
|
53
60
|
headerTransparent,
|
|
54
|
-
contentStyle,
|
|
55
61
|
headerBackTitle,
|
|
62
|
+
presentation,
|
|
63
|
+
contentStyle,
|
|
56
64
|
} = options;
|
|
57
65
|
|
|
66
|
+
const nextPresentation = nexDescriptor?.options.presentation;
|
|
67
|
+
|
|
58
68
|
return (
|
|
59
69
|
<Screen
|
|
60
70
|
key={route.key}
|
|
@@ -115,7 +125,8 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
115
125
|
}
|
|
116
126
|
headerRight={
|
|
117
127
|
typeof headerRight === 'function'
|
|
118
|
-
? ({ tintColor }) =>
|
|
128
|
+
? ({ tintColor }) =>
|
|
129
|
+
headerRight({ tintColor, canGoBack })
|
|
119
130
|
: headerRight
|
|
120
131
|
}
|
|
121
132
|
headerTitle={
|
|
@@ -143,7 +154,18 @@ export default function NativeStackView({ state, descriptors }: Props) {
|
|
|
143
154
|
}
|
|
144
155
|
style={[
|
|
145
156
|
StyleSheet.absoluteFill,
|
|
146
|
-
{
|
|
157
|
+
{
|
|
158
|
+
display:
|
|
159
|
+
isFocused ||
|
|
160
|
+
(nextPresentation != null &&
|
|
161
|
+
TRANSPARENT_PRESENTATIONS.includes(nextPresentation))
|
|
162
|
+
? 'flex'
|
|
163
|
+
: 'none',
|
|
164
|
+
},
|
|
165
|
+
presentation != null &&
|
|
166
|
+
TRANSPARENT_PRESENTATIONS.includes(presentation)
|
|
167
|
+
? { backgroundColor: 'transparent' }
|
|
168
|
+
: null,
|
|
147
169
|
]}
|
|
148
170
|
>
|
|
149
171
|
<View style={[styles.contentContainer, contentStyle]}>
|