@react-navigation/native-stack 8.0.0-alpha.5 → 8.0.0-alpha.50
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/module/index.js.map +1 -1
- package/lib/module/navigators/createNativeStackNavigator.js +4 -8
- package/lib/module/navigators/createNativeStackNavigator.js.map +1 -1
- package/lib/module/utils/useAnimatedHeaderHeight.js +1 -1
- package/lib/module/utils/useAnimatedHeaderHeight.js.map +1 -1
- package/lib/module/utils/useInvalidPreventRemoveError.js +1 -1
- package/lib/module/utils/useInvalidPreventRemoveError.js.map +1 -1
- package/lib/module/views/FooterComponent.js +1 -1
- package/lib/module/views/FooterComponent.js.map +1 -1
- package/lib/module/views/NativeStackView.js +42 -25
- package/lib/module/views/NativeStackView.js.map +1 -1
- package/lib/module/views/NativeStackView.native.js +115 -71
- package/lib/module/views/NativeStackView.native.js.map +1 -1
- package/lib/module/views/useHeaderConfigProps.js +76 -23
- package/lib/module/views/useHeaderConfigProps.js.map +1 -1
- package/lib/typescript/src/index.d.ts +3 -3
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts +10 -15
- package/lib/typescript/src/navigators/createNativeStackNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +164 -156
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts +1 -1
- package/lib/typescript/src/utils/getModalRoutesKeys.d.ts.map +1 -1
- package/lib/typescript/src/utils/useInvalidPreventRemoveError.d.ts +1 -1
- package/lib/typescript/src/utils/useInvalidPreventRemoveError.d.ts.map +1 -1
- package/lib/typescript/src/views/FooterComponent.d.ts +2 -2
- package/lib/typescript/src/views/FooterComponent.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/lib/typescript/src/views/useHeaderConfigProps.d.ts +2 -3
- package/lib/typescript/src/views/useHeaderConfigProps.d.ts.map +1 -1
- package/package.json +22 -19
- package/src/index.tsx +1 -2
- package/src/navigators/createNativeStackNavigator.tsx +11 -47
- package/src/types.tsx +225 -196
- package/src/utils/useAnimatedHeaderHeight.tsx +1 -1
- package/src/utils/useInvalidPreventRemoveError.tsx +2 -1
- package/src/views/FooterComponent.tsx +1 -1
- package/src/views/NativeStackView.native.tsx +171 -92
- package/src/views/NativeStackView.tsx +70 -41
- package/src/views/useHeaderConfigProps.tsx +109 -39
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
import { getHeaderTitle, HeaderTitle } from '@react-navigation/elements';
|
|
2
2
|
import { Color } from '@react-navigation/elements/internal';
|
|
3
3
|
import {
|
|
4
|
+
MaterialSymbol,
|
|
4
5
|
type Route,
|
|
5
6
|
type Theme,
|
|
6
7
|
useLocale,
|
|
7
8
|
useTheme,
|
|
8
9
|
} from '@react-navigation/native';
|
|
10
|
+
import { useMemo } from 'react';
|
|
9
11
|
import { Platform, StyleSheet, type TextStyle, View } from 'react-native';
|
|
10
12
|
import {
|
|
11
|
-
type HeaderBarButtonItem,
|
|
12
13
|
type HeaderBarButtonItemMenuAction,
|
|
13
14
|
type HeaderBarButtonItemSubmenu,
|
|
15
|
+
type HeaderBarButtonItemWithAction,
|
|
16
|
+
type HeaderBarButtonItemWithMenu,
|
|
14
17
|
isSearchBarAvailableForCurrentPlatform,
|
|
15
18
|
ScreenStackHeaderBackButtonImage,
|
|
16
19
|
ScreenStackHeaderCenterView,
|
|
@@ -23,18 +26,20 @@ import {
|
|
|
23
26
|
|
|
24
27
|
import type {
|
|
25
28
|
NativeStackHeaderItem,
|
|
29
|
+
NativeStackHeaderItemButton,
|
|
26
30
|
NativeStackHeaderItemMenuAction,
|
|
27
31
|
NativeStackHeaderItemMenuSubmenu,
|
|
28
32
|
NativeStackNavigationOptions,
|
|
29
33
|
} from '../types';
|
|
30
34
|
|
|
31
35
|
type Props = NativeStackNavigationOptions & {
|
|
32
|
-
headerTopInsetEnabled: boolean;
|
|
33
36
|
headerHeight: number;
|
|
34
37
|
headerBack: { title?: string | undefined; href: undefined } | undefined;
|
|
35
38
|
route: Route<string>;
|
|
36
39
|
};
|
|
37
40
|
|
|
41
|
+
const ICON_SIZE = 24;
|
|
42
|
+
|
|
38
43
|
const processBarButtonItems = (
|
|
39
44
|
items: NativeStackHeaderItem[] | undefined,
|
|
40
45
|
colors: Theme['colors'],
|
|
@@ -70,7 +75,7 @@ const processBarButtonItems = (
|
|
|
70
75
|
|
|
71
76
|
const { badge, label, labelStyle, icon, ...rest } = item;
|
|
72
77
|
|
|
73
|
-
|
|
78
|
+
const processedItemCommon = {
|
|
74
79
|
...rest,
|
|
75
80
|
index,
|
|
76
81
|
title: label,
|
|
@@ -78,40 +83,43 @@ const processBarButtonItems = (
|
|
|
78
83
|
...fonts.regular,
|
|
79
84
|
...labelStyle,
|
|
80
85
|
},
|
|
81
|
-
icon:
|
|
82
|
-
icon?.type === 'image'
|
|
83
|
-
? icon.tinted === false
|
|
84
|
-
? {
|
|
85
|
-
type: 'imageSource',
|
|
86
|
-
imageSource: icon.source,
|
|
87
|
-
}
|
|
88
|
-
: {
|
|
89
|
-
type: 'templateSource',
|
|
90
|
-
templateSource: icon.source,
|
|
91
|
-
}
|
|
92
|
-
: icon,
|
|
86
|
+
icon: transformIcon(icon),
|
|
93
87
|
};
|
|
94
88
|
|
|
95
|
-
|
|
89
|
+
let processedItem:
|
|
90
|
+
| HeaderBarButtonItemWithAction
|
|
91
|
+
| HeaderBarButtonItemWithMenu;
|
|
92
|
+
|
|
93
|
+
if (processedItemCommon.type === 'menu' && item.type === 'menu') {
|
|
96
94
|
const { multiselectable, layout } = item.menu;
|
|
97
95
|
|
|
98
96
|
processedItem = {
|
|
99
|
-
...
|
|
97
|
+
...processedItemCommon,
|
|
100
98
|
menu: {
|
|
101
|
-
...
|
|
102
|
-
singleSelection:
|
|
99
|
+
...processedItemCommon.menu,
|
|
100
|
+
singleSelection:
|
|
101
|
+
typeof multiselectable === 'boolean'
|
|
102
|
+
? !multiselectable
|
|
103
|
+
: undefined,
|
|
103
104
|
displayAsPalette: layout === 'palette',
|
|
104
105
|
items: item.menu.items.map(getMenuItem),
|
|
105
106
|
},
|
|
106
107
|
};
|
|
108
|
+
} else if (
|
|
109
|
+
processedItemCommon.type === 'button' &&
|
|
110
|
+
item.type === 'button'
|
|
111
|
+
) {
|
|
112
|
+
processedItem = processedItemCommon;
|
|
113
|
+
} else {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`Invalid item type: ${JSON.stringify(item)}. Valid types are 'button' and 'menu'.`
|
|
116
|
+
);
|
|
107
117
|
}
|
|
108
118
|
|
|
109
119
|
if (badge) {
|
|
110
120
|
const badgeBackgroundColor =
|
|
111
121
|
badge.style?.backgroundColor ?? colors.notification;
|
|
112
|
-
const badgeTextColor = Color(badgeBackgroundColor)
|
|
113
|
-
? 'black'
|
|
114
|
-
: 'white';
|
|
122
|
+
const badgeTextColor = Color.foreground(badgeBackgroundColor);
|
|
115
123
|
|
|
116
124
|
processedItem = {
|
|
117
125
|
...processedItem,
|
|
@@ -138,26 +146,44 @@ const processBarButtonItems = (
|
|
|
138
146
|
.filter((item) => item != null);
|
|
139
147
|
};
|
|
140
148
|
|
|
149
|
+
const transformIcon = (
|
|
150
|
+
icon: NativeStackHeaderItemButton['icon']
|
|
151
|
+
):
|
|
152
|
+
| HeaderBarButtonItemWithAction['icon']
|
|
153
|
+
| HeaderBarButtonItemWithMenu['icon'] => {
|
|
154
|
+
if (icon?.type === 'image') {
|
|
155
|
+
return icon.tinted === false
|
|
156
|
+
? { type: 'imageSource', imageSource: icon.source }
|
|
157
|
+
: { type: 'templateSource', templateSource: icon.source };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return icon;
|
|
161
|
+
};
|
|
162
|
+
|
|
141
163
|
const getMenuItem = (
|
|
142
164
|
item: NativeStackHeaderItemMenuAction | NativeStackHeaderItemMenuSubmenu
|
|
143
165
|
): HeaderBarButtonItemMenuAction | HeaderBarButtonItemSubmenu => {
|
|
144
166
|
if (item.type === 'submenu') {
|
|
145
|
-
const { label, inline, layout, items, multiselectable, ...rest } =
|
|
167
|
+
const { label, icon, inline, layout, items, multiselectable, ...rest } =
|
|
168
|
+
item;
|
|
146
169
|
|
|
147
170
|
return {
|
|
148
171
|
...rest,
|
|
172
|
+
icon: transformIcon(icon),
|
|
149
173
|
title: label,
|
|
150
174
|
displayAsPalette: layout === 'palette',
|
|
151
175
|
displayInline: inline,
|
|
152
|
-
singleSelection:
|
|
176
|
+
singleSelection:
|
|
177
|
+
typeof multiselectable === 'boolean' ? !multiselectable : undefined,
|
|
153
178
|
items: items.map(getMenuItem),
|
|
154
179
|
};
|
|
155
180
|
}
|
|
156
181
|
|
|
157
|
-
const { label, description, ...rest } = item;
|
|
182
|
+
const { label, icon, description, ...rest } = item;
|
|
158
183
|
|
|
159
184
|
return {
|
|
160
185
|
...rest,
|
|
186
|
+
icon: transformIcon(icon),
|
|
161
187
|
title: label,
|
|
162
188
|
subtitle: description,
|
|
163
189
|
};
|
|
@@ -189,10 +215,10 @@ export function useHeaderConfigProps({
|
|
|
189
215
|
headerTitleStyle,
|
|
190
216
|
headerTransparent,
|
|
191
217
|
headerSearchBarOptions,
|
|
192
|
-
headerTopInsetEnabled,
|
|
193
218
|
headerBack,
|
|
194
219
|
route,
|
|
195
220
|
title,
|
|
221
|
+
unstable_headerInsets: headerInsets,
|
|
196
222
|
unstable_headerLeftItems: headerLeftItems,
|
|
197
223
|
unstable_headerRightItems: headerRightItems,
|
|
198
224
|
}: Props): ScreenStackHeaderConfigProps {
|
|
@@ -216,6 +242,15 @@ export function useHeaderConfigProps({
|
|
|
216
242
|
const headerStyleFlattened = StyleSheet.flatten(headerStyle) || {};
|
|
217
243
|
const headerLargeStyleFlattened = StyleSheet.flatten(headerLargeStyle) || {};
|
|
218
244
|
|
|
245
|
+
const headerBackgroundColor =
|
|
246
|
+
headerStyleFlattened.backgroundColor ??
|
|
247
|
+
(headerBackground != null ||
|
|
248
|
+
headerTransparent ||
|
|
249
|
+
// The title becomes invisible if background color is set with large title on iOS 26
|
|
250
|
+
(Platform.OS === 'ios' && headerLargeTitleEnabled)
|
|
251
|
+
? 'transparent'
|
|
252
|
+
: colors.card);
|
|
253
|
+
|
|
219
254
|
const backTitleFontSize =
|
|
220
255
|
'fontSize' in headerBackTitleStyleFlattened
|
|
221
256
|
? headerBackTitleStyleFlattened.fontSize
|
|
@@ -226,7 +261,13 @@ export function useHeaderConfigProps({
|
|
|
226
261
|
const titleColor =
|
|
227
262
|
'color' in headerTitleStyleFlattened
|
|
228
263
|
? headerTitleStyleFlattened.color
|
|
229
|
-
:
|
|
264
|
+
: Platform.OS === 'ios' &&
|
|
265
|
+
(headerTransparent || headerBackgroundColor === 'transparent')
|
|
266
|
+
? // On iOS 26, we want header title to change color based on content underneath
|
|
267
|
+
// So we don't set an explicit color when header is transparent
|
|
268
|
+
// Unless a custom tint color is explicitly provided
|
|
269
|
+
headerTintColor
|
|
270
|
+
: (headerTintColor ?? colors.text);
|
|
230
271
|
const titleFontSize =
|
|
231
272
|
'fontSize' in headerTitleStyleFlattened
|
|
232
273
|
? headerTitleStyleFlattened.fontSize
|
|
@@ -260,15 +301,6 @@ export function useHeaderConfigProps({
|
|
|
260
301
|
headerTitleStyleSupported.fontWeight = titleFontWeight;
|
|
261
302
|
}
|
|
262
303
|
|
|
263
|
-
const headerBackgroundColor =
|
|
264
|
-
headerStyleFlattened.backgroundColor ??
|
|
265
|
-
(headerBackground != null ||
|
|
266
|
-
headerTransparent ||
|
|
267
|
-
// The title becomes invisible if background color is set with large title on iOS 26
|
|
268
|
-
(Platform.OS === 'ios' && headerLargeTitleEnabled)
|
|
269
|
-
? 'transparent'
|
|
270
|
-
: colors.card);
|
|
271
|
-
|
|
272
304
|
const canGoBack = headerBack != null;
|
|
273
305
|
|
|
274
306
|
const headerLeftElement = headerLeft?.({
|
|
@@ -342,6 +374,37 @@ export function useHeaderConfigProps({
|
|
|
342
374
|
rightItems = [...rightItems].reverse();
|
|
343
375
|
}
|
|
344
376
|
|
|
377
|
+
const backImageSource = useMemo(() => {
|
|
378
|
+
if (headerBackIcon == null && Platform.OS === 'android') {
|
|
379
|
+
try {
|
|
380
|
+
// Use Material Symbol as default back icon on Android
|
|
381
|
+
// So it's consistent with other material icons
|
|
382
|
+
// Based on the available variant and weight
|
|
383
|
+
return MaterialSymbol.getImageSource({
|
|
384
|
+
name: 'arrow_back',
|
|
385
|
+
color: tintColor,
|
|
386
|
+
size: ICON_SIZE,
|
|
387
|
+
});
|
|
388
|
+
} catch (e) {
|
|
389
|
+
// Fallback to default if symbol is not available
|
|
390
|
+
// This can happen if no font, or multiple fonts are available
|
|
391
|
+
// Or in tests where native module is not available
|
|
392
|
+
}
|
|
393
|
+
} else if (headerBackIcon?.type === 'image') {
|
|
394
|
+
return headerBackIcon.source;
|
|
395
|
+
} else if (headerBackIcon?.type === 'materialSymbol') {
|
|
396
|
+
return MaterialSymbol.getImageSource({
|
|
397
|
+
name: headerBackIcon.name,
|
|
398
|
+
variant: headerBackIcon.variant,
|
|
399
|
+
weight: headerBackIcon.weight,
|
|
400
|
+
color: tintColor,
|
|
401
|
+
size: ICON_SIZE,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return undefined;
|
|
406
|
+
}, [headerBackIcon, tintColor]);
|
|
407
|
+
|
|
345
408
|
const children = (
|
|
346
409
|
<>
|
|
347
410
|
{Platform.OS === 'ios' ? (
|
|
@@ -417,8 +480,8 @@ export function useHeaderConfigProps({
|
|
|
417
480
|
) : null}
|
|
418
481
|
</>
|
|
419
482
|
)}
|
|
420
|
-
{
|
|
421
|
-
<ScreenStackHeaderBackButtonImage source={
|
|
483
|
+
{backImageSource != null ? (
|
|
484
|
+
<ScreenStackHeaderBackButtonImage source={backImageSource} />
|
|
422
485
|
) : null}
|
|
423
486
|
{Platform.OS === 'ios' && rightItems ? (
|
|
424
487
|
rightItems.map((item, index) => {
|
|
@@ -488,7 +551,14 @@ export function useHeaderConfigProps({
|
|
|
488
551
|
titleFontFamily,
|
|
489
552
|
titleFontSize,
|
|
490
553
|
titleFontWeight: String(titleFontWeight),
|
|
491
|
-
|
|
554
|
+
disableTopInsetApplication:
|
|
555
|
+
headerInsets?.top !== undefined ? !headerInsets.top : undefined,
|
|
556
|
+
disableLeftInsetApplication:
|
|
557
|
+
headerInsets?.left !== undefined ? !headerInsets.left : undefined,
|
|
558
|
+
disableRightInsetApplication:
|
|
559
|
+
headerInsets?.right !== undefined ? !headerInsets.right : undefined,
|
|
560
|
+
disableBottomInsetApplication:
|
|
561
|
+
headerInsets?.bottom !== undefined ? !headerInsets.bottom : undefined,
|
|
492
562
|
translucent: translucent === true,
|
|
493
563
|
children,
|
|
494
564
|
headerLeftBarButtonItems: processBarButtonItems(leftItems, colors, fonts),
|