@react-navigation/bottom-tabs 7.6.0 → 7.7.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/module/unstable/NativeBottomTabView.js +6 -0
- package/lib/module/unstable/NativeBottomTabView.js.map +1 -0
- package/lib/module/unstable/NativeBottomTabView.native.js +225 -0
- package/lib/module/unstable/NativeBottomTabView.native.js.map +1 -0
- package/lib/module/unstable/NativeScreen/NativeScreen.js +166 -0
- package/lib/module/unstable/NativeScreen/NativeScreen.js.map +1 -0
- package/lib/module/unstable/NativeScreen/debounce.js +12 -0
- package/lib/module/unstable/NativeScreen/debounce.js.map +1 -0
- package/lib/module/unstable/NativeScreen/types.js +4 -0
- package/lib/module/unstable/NativeScreen/types.js.map +1 -0
- package/lib/module/unstable/NativeScreen/useAnimatedHeaderHeight.js +12 -0
- package/lib/module/unstable/NativeScreen/useAnimatedHeaderHeight.js.map +1 -0
- package/lib/module/unstable/NativeScreen/useHeaderConfig.js +283 -0
- package/lib/module/unstable/NativeScreen/useHeaderConfig.js.map +1 -0
- package/lib/module/unstable/createNativeBottomTabNavigator.js +6 -0
- package/lib/module/unstable/createNativeBottomTabNavigator.js.map +1 -0
- package/lib/module/unstable/createNativeBottomTabNavigator.native.js +65 -0
- package/lib/module/unstable/createNativeBottomTabNavigator.native.js.map +1 -0
- package/lib/module/unstable/index.js +16 -0
- package/lib/module/unstable/index.js.map +1 -0
- package/lib/module/unstable/types.js +4 -0
- package/lib/module/unstable/types.js.map +1 -0
- package/lib/typescript/src/unstable/NativeBottomTabView.d.ts +10 -0
- package/lib/typescript/src/unstable/NativeBottomTabView.d.ts.map +1 -0
- package/lib/typescript/src/unstable/NativeBottomTabView.native.d.ts +10 -0
- package/lib/typescript/src/unstable/NativeBottomTabView.native.d.ts.map +1 -0
- package/lib/typescript/src/unstable/NativeScreen/NativeScreen.d.ts +8 -0
- package/lib/typescript/src/unstable/NativeScreen/NativeScreen.d.ts.map +1 -0
- package/lib/typescript/src/unstable/NativeScreen/debounce.d.ts +2 -0
- package/lib/typescript/src/unstable/NativeScreen/debounce.d.ts.map +1 -0
- package/lib/typescript/src/unstable/NativeScreen/types.d.ts +467 -0
- package/lib/typescript/src/unstable/NativeScreen/types.d.ts.map +1 -0
- package/lib/typescript/src/unstable/NativeScreen/useAnimatedHeaderHeight.d.ts +5 -0
- package/lib/typescript/src/unstable/NativeScreen/useAnimatedHeaderHeight.d.ts.map +1 -0
- package/lib/typescript/src/unstable/NativeScreen/useHeaderConfig.d.ts +11 -0
- package/lib/typescript/src/unstable/NativeScreen/useHeaderConfig.d.ts.map +1 -0
- package/lib/typescript/src/unstable/createNativeBottomTabNavigator.d.ts +2 -0
- package/lib/typescript/src/unstable/createNativeBottomTabNavigator.d.ts.map +1 -0
- package/lib/typescript/src/unstable/createNativeBottomTabNavigator.native.d.ts +16 -0
- package/lib/typescript/src/unstable/createNativeBottomTabNavigator.native.d.ts.map +1 -0
- package/lib/typescript/src/unstable/index.d.ts +13 -0
- package/lib/typescript/src/unstable/index.d.ts.map +1 -0
- package/lib/typescript/src/unstable/types.d.ts +276 -0
- package/lib/typescript/src/unstable/types.d.ts.map +1 -0
- package/package.json +10 -4
- package/src/unstable/NativeBottomTabView.native.tsx +303 -0
- package/src/unstable/NativeBottomTabView.tsx +20 -0
- package/src/unstable/NativeScreen/NativeScreen.tsx +242 -0
- package/src/unstable/NativeScreen/debounce.tsx +14 -0
- package/src/unstable/NativeScreen/types.ts +517 -0
- package/src/unstable/NativeScreen/useAnimatedHeaderHeight.tsx +18 -0
- package/src/unstable/NativeScreen/useHeaderConfig.tsx +423 -0
- package/src/unstable/createNativeBottomTabNavigator.native.tsx +116 -0
- package/src/unstable/createNativeBottomTabNavigator.tsx +4 -0
- package/src/unstable/index.tsx +22 -0
- package/src/unstable/types.tsx +353 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
import { getHeaderTitle, HeaderTitle } from '@react-navigation/elements';
|
|
2
|
+
import {
|
|
3
|
+
type Route,
|
|
4
|
+
type Theme,
|
|
5
|
+
useLocale,
|
|
6
|
+
useTheme,
|
|
7
|
+
} from '@react-navigation/native';
|
|
8
|
+
import Color from 'color';
|
|
9
|
+
import { Platform, StyleSheet, type TextStyle, View } from 'react-native';
|
|
10
|
+
import {
|
|
11
|
+
type HeaderBarButtonItem,
|
|
12
|
+
type HeaderBarButtonItemMenuAction,
|
|
13
|
+
type HeaderBarButtonItemSubmenu,
|
|
14
|
+
isSearchBarAvailableForCurrentPlatform,
|
|
15
|
+
ScreenStackHeaderCenterView,
|
|
16
|
+
type ScreenStackHeaderConfigProps,
|
|
17
|
+
ScreenStackHeaderLeftView,
|
|
18
|
+
ScreenStackHeaderRightView,
|
|
19
|
+
ScreenStackHeaderSearchBarView,
|
|
20
|
+
SearchBar,
|
|
21
|
+
} from 'react-native-screens';
|
|
22
|
+
|
|
23
|
+
import type {
|
|
24
|
+
NativeHeaderOptions,
|
|
25
|
+
NativeScreenHeaderItem,
|
|
26
|
+
NativeScreenHeaderItemMenuAction,
|
|
27
|
+
NativeScreenHeaderItemMenuSubmenu,
|
|
28
|
+
} from './types';
|
|
29
|
+
|
|
30
|
+
type Props = NativeHeaderOptions & {
|
|
31
|
+
headerTopInsetEnabled: boolean;
|
|
32
|
+
headerHeight: number;
|
|
33
|
+
route: Route<string>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const processBarButtonItems = (
|
|
37
|
+
items: NativeScreenHeaderItem[] | undefined,
|
|
38
|
+
colors: Theme['colors'],
|
|
39
|
+
fonts: Theme['fonts']
|
|
40
|
+
) => {
|
|
41
|
+
return items
|
|
42
|
+
?.map((item, index) => {
|
|
43
|
+
if (item.type === 'custom') {
|
|
44
|
+
// Handled with `ScreenStackHeaderLeftView` or `ScreenStackHeaderRightView`
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (item.type === 'spacing') {
|
|
49
|
+
if (item.spacing == null) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Spacing item must have a 'spacing' property defined: ${JSON.stringify(
|
|
52
|
+
item
|
|
53
|
+
)}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return item;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (item.type === 'button' || item.type === 'menu') {
|
|
61
|
+
if (item.type === 'menu' && item.menu == null) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Menu item must have a 'menu' property defined: ${JSON.stringify(
|
|
64
|
+
item
|
|
65
|
+
)}`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { badge, label, labelStyle, icon, ...rest } = item;
|
|
70
|
+
|
|
71
|
+
let processedItem: HeaderBarButtonItem = {
|
|
72
|
+
...rest,
|
|
73
|
+
index,
|
|
74
|
+
title: label,
|
|
75
|
+
titleStyle: {
|
|
76
|
+
...fonts.regular,
|
|
77
|
+
...labelStyle,
|
|
78
|
+
},
|
|
79
|
+
icon:
|
|
80
|
+
icon?.type === 'image'
|
|
81
|
+
? icon.tinted === false
|
|
82
|
+
? {
|
|
83
|
+
type: 'imageSource',
|
|
84
|
+
imageSource: icon.source,
|
|
85
|
+
}
|
|
86
|
+
: {
|
|
87
|
+
type: 'templateSource',
|
|
88
|
+
templateSource: icon.source,
|
|
89
|
+
}
|
|
90
|
+
: icon,
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
if (processedItem.type === 'menu' && item.type === 'menu') {
|
|
94
|
+
processedItem = {
|
|
95
|
+
...processedItem,
|
|
96
|
+
menu: {
|
|
97
|
+
...processedItem.menu,
|
|
98
|
+
items: item.menu.items.map(getMenuItem),
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (badge) {
|
|
104
|
+
const badgeBackgroundColor =
|
|
105
|
+
badge.style?.backgroundColor ?? colors.notification;
|
|
106
|
+
const badgeTextColor =
|
|
107
|
+
typeof badgeBackgroundColor === 'string' &&
|
|
108
|
+
Color(badgeBackgroundColor)?.isLight()
|
|
109
|
+
? 'black'
|
|
110
|
+
: 'white';
|
|
111
|
+
|
|
112
|
+
processedItem = {
|
|
113
|
+
...processedItem,
|
|
114
|
+
badge: {
|
|
115
|
+
...badge,
|
|
116
|
+
value: String(badge.value),
|
|
117
|
+
style: {
|
|
118
|
+
backgroundColor: badgeBackgroundColor,
|
|
119
|
+
color: badgeTextColor,
|
|
120
|
+
...fonts.regular,
|
|
121
|
+
...badge.style,
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return processedItem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Invalid item type: ${JSON.stringify(item)}. Valid types are 'button', 'menu', 'custom' and 'spacing'.`
|
|
132
|
+
);
|
|
133
|
+
})
|
|
134
|
+
.filter((item) => item != null);
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const getMenuItem = (
|
|
138
|
+
item: NativeScreenHeaderItemMenuAction | NativeScreenHeaderItemMenuSubmenu
|
|
139
|
+
): HeaderBarButtonItemMenuAction | HeaderBarButtonItemSubmenu => {
|
|
140
|
+
const { label, ...rest } = item;
|
|
141
|
+
|
|
142
|
+
if (rest.type === 'submenu') {
|
|
143
|
+
return {
|
|
144
|
+
...rest,
|
|
145
|
+
title: label,
|
|
146
|
+
items: rest.items.map(getMenuItem),
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
...rest,
|
|
152
|
+
title: label,
|
|
153
|
+
};
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export function useHeaderConfig({
|
|
157
|
+
headerShadowVisible,
|
|
158
|
+
headerLargeStyle,
|
|
159
|
+
headerLargeTitle,
|
|
160
|
+
headerLargeTitleShadowVisible,
|
|
161
|
+
headerLargeTitleStyle,
|
|
162
|
+
headerBackground,
|
|
163
|
+
headerLeft,
|
|
164
|
+
headerRight,
|
|
165
|
+
headerShown,
|
|
166
|
+
headerStyle,
|
|
167
|
+
headerBlurEffect,
|
|
168
|
+
headerTintColor,
|
|
169
|
+
headerTitle,
|
|
170
|
+
headerTitleAlign,
|
|
171
|
+
headerTitleStyle,
|
|
172
|
+
headerTransparent,
|
|
173
|
+
headerSearchBarOptions,
|
|
174
|
+
headerTopInsetEnabled,
|
|
175
|
+
route,
|
|
176
|
+
title,
|
|
177
|
+
unstable_headerLeftItems: headerLeftItems,
|
|
178
|
+
unstable_headerRightItems: headerRightItems,
|
|
179
|
+
}: Props): ScreenStackHeaderConfigProps {
|
|
180
|
+
const { direction } = useLocale();
|
|
181
|
+
const { colors, fonts } = useTheme();
|
|
182
|
+
const tintColor =
|
|
183
|
+
headerTintColor ?? (Platform.OS === 'ios' ? colors.primary : colors.text);
|
|
184
|
+
|
|
185
|
+
const headerLargeTitleStyleFlattened =
|
|
186
|
+
StyleSheet.flatten([
|
|
187
|
+
Platform.select({ ios: fonts.heavy, default: fonts.medium }),
|
|
188
|
+
headerLargeTitleStyle,
|
|
189
|
+
]) || {};
|
|
190
|
+
|
|
191
|
+
const headerTitleStyleFlattened =
|
|
192
|
+
StyleSheet.flatten([
|
|
193
|
+
Platform.select({ ios: fonts.bold, default: fonts.medium }),
|
|
194
|
+
headerTitleStyle,
|
|
195
|
+
]) || {};
|
|
196
|
+
|
|
197
|
+
const headerStyleFlattened = StyleSheet.flatten(headerStyle) || {};
|
|
198
|
+
const headerLargeStyleFlattened = StyleSheet.flatten(headerLargeStyle) || {};
|
|
199
|
+
|
|
200
|
+
const titleText = getHeaderTitle({ title, headerTitle }, route.name);
|
|
201
|
+
|
|
202
|
+
const titleColor =
|
|
203
|
+
'color' in headerTitleStyleFlattened
|
|
204
|
+
? headerTitleStyleFlattened.color
|
|
205
|
+
: (headerTintColor ?? colors.text);
|
|
206
|
+
|
|
207
|
+
const titleFontSize =
|
|
208
|
+
'fontSize' in headerTitleStyleFlattened
|
|
209
|
+
? headerTitleStyleFlattened.fontSize
|
|
210
|
+
: undefined;
|
|
211
|
+
|
|
212
|
+
const titleFontFamily = headerTitleStyleFlattened.fontFamily;
|
|
213
|
+
const titleFontWeight = headerTitleStyleFlattened.fontWeight;
|
|
214
|
+
|
|
215
|
+
const largeTitleFontFamily = headerLargeTitleStyleFlattened.fontFamily;
|
|
216
|
+
const largeTitleBackgroundColor = headerLargeStyleFlattened.backgroundColor;
|
|
217
|
+
|
|
218
|
+
const largeTitleColor =
|
|
219
|
+
'color' in headerLargeTitleStyleFlattened
|
|
220
|
+
? headerLargeTitleStyleFlattened.color
|
|
221
|
+
: undefined;
|
|
222
|
+
|
|
223
|
+
const largeTitleFontSize =
|
|
224
|
+
'fontSize' in headerLargeTitleStyleFlattened
|
|
225
|
+
? headerLargeTitleStyleFlattened.fontSize
|
|
226
|
+
: undefined;
|
|
227
|
+
|
|
228
|
+
const largeTitleFontWeight = headerLargeTitleStyleFlattened.fontWeight;
|
|
229
|
+
|
|
230
|
+
const headerTitleStyleSupported: TextStyle = { color: titleColor };
|
|
231
|
+
|
|
232
|
+
if (headerTitleStyleFlattened.fontFamily != null) {
|
|
233
|
+
headerTitleStyleSupported.fontFamily = headerTitleStyleFlattened.fontFamily;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (titleFontSize != null) {
|
|
237
|
+
headerTitleStyleSupported.fontSize = titleFontSize;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (titleFontWeight != null) {
|
|
241
|
+
headerTitleStyleSupported.fontWeight = titleFontWeight;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const headerBackgroundColor =
|
|
245
|
+
headerStyleFlattened.backgroundColor ??
|
|
246
|
+
(headerBackground != null || headerTransparent
|
|
247
|
+
? 'transparent'
|
|
248
|
+
: colors.card);
|
|
249
|
+
|
|
250
|
+
const headerLeftElement = headerLeft?.({
|
|
251
|
+
tintColor,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
const headerRightElement = headerRight?.({
|
|
255
|
+
tintColor,
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const headerTitleElement =
|
|
259
|
+
typeof headerTitle === 'function'
|
|
260
|
+
? headerTitle({
|
|
261
|
+
tintColor,
|
|
262
|
+
children: titleText,
|
|
263
|
+
})
|
|
264
|
+
: null;
|
|
265
|
+
|
|
266
|
+
const hasHeaderSearchBar =
|
|
267
|
+
isSearchBarAvailableForCurrentPlatform && headerSearchBarOptions != null;
|
|
268
|
+
|
|
269
|
+
const translucent =
|
|
270
|
+
headerBackground != null ||
|
|
271
|
+
headerTransparent ||
|
|
272
|
+
// When using a SearchBar or large title, the header needs to be translucent for it to work on iOS
|
|
273
|
+
((hasHeaderSearchBar || headerLargeTitle) &&
|
|
274
|
+
Platform.OS === 'ios' &&
|
|
275
|
+
headerTransparent !== false);
|
|
276
|
+
|
|
277
|
+
const isCenterViewRenderedAndroid = headerTitleAlign === 'center';
|
|
278
|
+
|
|
279
|
+
const leftItems = headerLeftItems?.({
|
|
280
|
+
tintColor,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
let rightItems = headerRightItems?.({
|
|
284
|
+
tintColor,
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
if (rightItems) {
|
|
288
|
+
// iOS renders right items in reverse order
|
|
289
|
+
// So we need to reverse them here to match the order
|
|
290
|
+
rightItems = [...rightItems].reverse();
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const children = (
|
|
294
|
+
<>
|
|
295
|
+
{Platform.OS === 'ios' ? (
|
|
296
|
+
<>
|
|
297
|
+
{leftItems ? (
|
|
298
|
+
leftItems.map((item, index) => {
|
|
299
|
+
if (item.type === 'custom') {
|
|
300
|
+
return (
|
|
301
|
+
<ScreenStackHeaderLeftView
|
|
302
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
303
|
+
key={index}
|
|
304
|
+
hidesSharedBackground={item.hidesSharedBackground}
|
|
305
|
+
>
|
|
306
|
+
{item.element}
|
|
307
|
+
</ScreenStackHeaderLeftView>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return null;
|
|
312
|
+
})
|
|
313
|
+
) : headerLeftElement != null ? (
|
|
314
|
+
<ScreenStackHeaderLeftView>
|
|
315
|
+
{headerLeftElement}
|
|
316
|
+
</ScreenStackHeaderLeftView>
|
|
317
|
+
) : null}
|
|
318
|
+
{headerTitleElement != null ? (
|
|
319
|
+
<ScreenStackHeaderCenterView>
|
|
320
|
+
{headerTitleElement}
|
|
321
|
+
</ScreenStackHeaderCenterView>
|
|
322
|
+
) : null}
|
|
323
|
+
</>
|
|
324
|
+
) : (
|
|
325
|
+
<>
|
|
326
|
+
{headerLeftElement != null || typeof headerTitle === 'function' ? (
|
|
327
|
+
// The style passed to header left, together with title element being wrapped
|
|
328
|
+
// in flex view is reqruied for proper header layout, in particular,
|
|
329
|
+
// for the text truncation to work.
|
|
330
|
+
<ScreenStackHeaderLeftView
|
|
331
|
+
style={!isCenterViewRenderedAndroid ? { flex: 1 } : null}
|
|
332
|
+
>
|
|
333
|
+
{headerLeftElement}
|
|
334
|
+
{headerTitleAlign !== 'center' ? (
|
|
335
|
+
typeof headerTitle === 'function' ? (
|
|
336
|
+
<View style={{ flex: 1 }}>{headerTitleElement}</View>
|
|
337
|
+
) : (
|
|
338
|
+
<View style={{ flex: 1 }}>
|
|
339
|
+
<HeaderTitle
|
|
340
|
+
tintColor={tintColor}
|
|
341
|
+
style={headerTitleStyleSupported}
|
|
342
|
+
>
|
|
343
|
+
{titleText}
|
|
344
|
+
</HeaderTitle>
|
|
345
|
+
</View>
|
|
346
|
+
)
|
|
347
|
+
) : null}
|
|
348
|
+
</ScreenStackHeaderLeftView>
|
|
349
|
+
) : null}
|
|
350
|
+
{isCenterViewRenderedAndroid ? (
|
|
351
|
+
<ScreenStackHeaderCenterView>
|
|
352
|
+
{typeof headerTitle === 'function' ? (
|
|
353
|
+
headerTitleElement
|
|
354
|
+
) : (
|
|
355
|
+
<HeaderTitle
|
|
356
|
+
tintColor={tintColor}
|
|
357
|
+
style={headerTitleStyleSupported}
|
|
358
|
+
>
|
|
359
|
+
{titleText}
|
|
360
|
+
</HeaderTitle>
|
|
361
|
+
)}
|
|
362
|
+
</ScreenStackHeaderCenterView>
|
|
363
|
+
) : null}
|
|
364
|
+
</>
|
|
365
|
+
)}
|
|
366
|
+
{Platform.OS === 'ios' && rightItems ? (
|
|
367
|
+
rightItems.map((item, index) => {
|
|
368
|
+
if (item.type === 'custom') {
|
|
369
|
+
return (
|
|
370
|
+
<ScreenStackHeaderRightView
|
|
371
|
+
// eslint-disable-next-line @eslint-react/no-array-index-key
|
|
372
|
+
key={index}
|
|
373
|
+
hidesSharedBackground={item.hidesSharedBackground}
|
|
374
|
+
>
|
|
375
|
+
{item.element}
|
|
376
|
+
</ScreenStackHeaderRightView>
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return null;
|
|
381
|
+
})
|
|
382
|
+
) : headerRightElement != null ? (
|
|
383
|
+
<ScreenStackHeaderRightView>
|
|
384
|
+
{headerRightElement}
|
|
385
|
+
</ScreenStackHeaderRightView>
|
|
386
|
+
) : null}
|
|
387
|
+
{hasHeaderSearchBar ? (
|
|
388
|
+
<ScreenStackHeaderSearchBarView>
|
|
389
|
+
<SearchBar {...headerSearchBarOptions} />
|
|
390
|
+
</ScreenStackHeaderSearchBarView>
|
|
391
|
+
) : null}
|
|
392
|
+
</>
|
|
393
|
+
);
|
|
394
|
+
|
|
395
|
+
return {
|
|
396
|
+
backgroundColor: headerBackgroundColor,
|
|
397
|
+
blurEffect: headerBlurEffect,
|
|
398
|
+
color: tintColor,
|
|
399
|
+
direction,
|
|
400
|
+
hidden: headerShown === false,
|
|
401
|
+
hideShadow:
|
|
402
|
+
headerShadowVisible === false ||
|
|
403
|
+
headerBackground != null ||
|
|
404
|
+
(headerTransparent && headerShadowVisible !== true),
|
|
405
|
+
largeTitle: headerLargeTitle,
|
|
406
|
+
largeTitleBackgroundColor,
|
|
407
|
+
largeTitleColor,
|
|
408
|
+
largeTitleFontFamily,
|
|
409
|
+
largeTitleFontSize,
|
|
410
|
+
largeTitleFontWeight,
|
|
411
|
+
largeTitleHideShadow: headerLargeTitleShadowVisible === false,
|
|
412
|
+
title: titleText,
|
|
413
|
+
titleColor,
|
|
414
|
+
titleFontFamily,
|
|
415
|
+
titleFontSize,
|
|
416
|
+
titleFontWeight: String(titleFontWeight),
|
|
417
|
+
topInsetEnabled: headerTopInsetEnabled,
|
|
418
|
+
translucent: translucent === true,
|
|
419
|
+
children,
|
|
420
|
+
headerLeftBarButtonItems: processBarButtonItems(leftItems, colors, fonts),
|
|
421
|
+
headerRightBarButtonItems: processBarButtonItems(rightItems, colors, fonts),
|
|
422
|
+
} as const;
|
|
423
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createNavigatorFactory,
|
|
3
|
+
type NavigatorTypeBagBase,
|
|
4
|
+
type ParamListBase,
|
|
5
|
+
StackActions,
|
|
6
|
+
type StaticConfig,
|
|
7
|
+
type TabActionHelpers,
|
|
8
|
+
type TabNavigationState,
|
|
9
|
+
TabRouter,
|
|
10
|
+
type TabRouterOptions,
|
|
11
|
+
type TypedNavigator,
|
|
12
|
+
useNavigationBuilder,
|
|
13
|
+
} from '@react-navigation/native';
|
|
14
|
+
import * as React from 'react';
|
|
15
|
+
|
|
16
|
+
import { NativeBottomTabView } from './NativeBottomTabView.native';
|
|
17
|
+
import type {
|
|
18
|
+
NativeBottomTabNavigationEventMap,
|
|
19
|
+
NativeBottomTabNavigationOptions,
|
|
20
|
+
NativeBottomTabNavigationProp,
|
|
21
|
+
NativeBottomTabNavigatorProps,
|
|
22
|
+
} from './types';
|
|
23
|
+
|
|
24
|
+
function NativeBottomTabNavigator({
|
|
25
|
+
id,
|
|
26
|
+
initialRouteName,
|
|
27
|
+
backBehavior,
|
|
28
|
+
children,
|
|
29
|
+
layout,
|
|
30
|
+
screenListeners,
|
|
31
|
+
screenOptions,
|
|
32
|
+
screenLayout,
|
|
33
|
+
UNSTABLE_router,
|
|
34
|
+
UNSTABLE_routeNamesChangeBehavior,
|
|
35
|
+
...rest
|
|
36
|
+
}: NativeBottomTabNavigatorProps) {
|
|
37
|
+
const { state, navigation, descriptors, NavigationContent } =
|
|
38
|
+
useNavigationBuilder<
|
|
39
|
+
TabNavigationState<ParamListBase>,
|
|
40
|
+
TabRouterOptions,
|
|
41
|
+
TabActionHelpers<ParamListBase>,
|
|
42
|
+
NativeBottomTabNavigationOptions,
|
|
43
|
+
NativeBottomTabNavigationEventMap
|
|
44
|
+
>(TabRouter, {
|
|
45
|
+
id,
|
|
46
|
+
initialRouteName,
|
|
47
|
+
backBehavior,
|
|
48
|
+
children,
|
|
49
|
+
layout,
|
|
50
|
+
screenListeners,
|
|
51
|
+
screenOptions,
|
|
52
|
+
screenLayout,
|
|
53
|
+
UNSTABLE_router,
|
|
54
|
+
UNSTABLE_routeNamesChangeBehavior,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const focusedRouteKey = state.routes[state.index].key;
|
|
58
|
+
const previousRouteKeyRef = React.useRef(focusedRouteKey);
|
|
59
|
+
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
const previousRouteKey = previousRouteKeyRef.current;
|
|
62
|
+
|
|
63
|
+
if (
|
|
64
|
+
previousRouteKey !== focusedRouteKey &&
|
|
65
|
+
descriptors[previousRouteKey]?.options.popToTopOnBlur
|
|
66
|
+
) {
|
|
67
|
+
const prevRoute = state.routes.find(
|
|
68
|
+
(route) => route.key === previousRouteKey
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
if (prevRoute?.state?.type === 'stack' && prevRoute.state.key) {
|
|
72
|
+
const popToTopAction = {
|
|
73
|
+
...StackActions.popToTop(),
|
|
74
|
+
target: prevRoute.state.key,
|
|
75
|
+
};
|
|
76
|
+
navigation.dispatch(popToTopAction);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
previousRouteKeyRef.current = focusedRouteKey;
|
|
81
|
+
}, [descriptors, focusedRouteKey, navigation, state.index, state.routes]);
|
|
82
|
+
|
|
83
|
+
return (
|
|
84
|
+
<NavigationContent>
|
|
85
|
+
<NativeBottomTabView
|
|
86
|
+
{...rest}
|
|
87
|
+
state={state}
|
|
88
|
+
navigation={navigation}
|
|
89
|
+
descriptors={descriptors}
|
|
90
|
+
/>
|
|
91
|
+
</NavigationContent>
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function createNativeBottomTabNavigator<
|
|
96
|
+
const ParamList extends ParamListBase,
|
|
97
|
+
const NavigatorID extends string | undefined = undefined,
|
|
98
|
+
const TypeBag extends NavigatorTypeBagBase = {
|
|
99
|
+
ParamList: ParamList;
|
|
100
|
+
NavigatorID: NavigatorID;
|
|
101
|
+
State: TabNavigationState<ParamList>;
|
|
102
|
+
ScreenOptions: NativeBottomTabNavigationOptions;
|
|
103
|
+
EventMap: NativeBottomTabNavigationEventMap;
|
|
104
|
+
NavigationList: {
|
|
105
|
+
[RouteName in keyof ParamList]: NativeBottomTabNavigationProp<
|
|
106
|
+
ParamList,
|
|
107
|
+
RouteName,
|
|
108
|
+
NavigatorID
|
|
109
|
+
>;
|
|
110
|
+
};
|
|
111
|
+
Navigator: typeof NativeBottomTabNavigator;
|
|
112
|
+
},
|
|
113
|
+
const Config extends StaticConfig<TypeBag> = StaticConfig<TypeBag>,
|
|
114
|
+
>(config?: Config): TypedNavigator<TypeBag, Config> {
|
|
115
|
+
return createNavigatorFactory(NativeBottomTabNavigator)(config);
|
|
116
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigators
|
|
3
|
+
*/
|
|
4
|
+
export { createNativeBottomTabNavigator } from './createNativeBottomTabNavigator';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Views
|
|
8
|
+
*/
|
|
9
|
+
export { NativeBottomTabView } from './NativeBottomTabView';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Types
|
|
13
|
+
*/
|
|
14
|
+
export type {
|
|
15
|
+
NativeBottomTabBarProps,
|
|
16
|
+
NativeBottomTabNavigationEventMap,
|
|
17
|
+
NativeBottomTabNavigationOptions,
|
|
18
|
+
NativeBottomTabNavigationProp,
|
|
19
|
+
NativeBottomTabNavigatorProps,
|
|
20
|
+
NativeBottomTabOptionsArgs,
|
|
21
|
+
NativeBottomTabScreenProps,
|
|
22
|
+
} from './types';
|