@react-navigation/bottom-tabs 7.20.0 → 7.21.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/Deferred.js +3 -10
- package/lib/module/unstable/Deferred.js.map +1 -1
- package/lib/module/unstable/NativeBottomTabView.native.js +288 -196
- package/lib/module/unstable/NativeBottomTabView.native.js.map +1 -1
- package/lib/module/views/BottomTabBar.js +111 -64
- package/lib/module/views/BottomTabBar.js.map +1 -1
- package/lib/module/views/BottomTabItem.js +2 -0
- package/lib/module/views/BottomTabItem.js.map +1 -1
- package/lib/module/views/BottomTabView.js +78 -57
- package/lib/module/views/BottomTabView.js.map +1 -1
- package/lib/module/views/TabBarIcon.js +6 -2
- package/lib/module/views/TabBarIcon.js.map +1 -1
- package/lib/typescript/src/types.d.ts +4 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/unstable/Deferred.d.ts.map +1 -1
- package/lib/typescript/src/unstable/NativeBottomTabView.native.d.ts.map +1 -1
- package/lib/typescript/src/unstable/types.d.ts +124 -4
- package/lib/typescript/src/unstable/types.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabBar.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabItem.d.ts +5 -1
- package/lib/typescript/src/views/BottomTabItem.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabView.d.ts.map +1 -1
- package/lib/typescript/src/views/TabBarIcon.d.ts +2 -1
- package/lib/typescript/src/views/TabBarIcon.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/types.tsx +5 -0
- package/src/unstable/Deferred.tsx +3 -11
- package/src/unstable/NativeBottomTabView.native.tsx +356 -218
- package/src/unstable/types.tsx +133 -4
- package/src/views/BottomTabBar.tsx +153 -82
- package/src/views/BottomTabItem.tsx +6 -0
- package/src/views/BottomTabView.tsx +100 -66
- package/src/views/TabBarIcon.tsx +9 -2
package/src/unstable/types.tsx
CHANGED
|
@@ -101,8 +101,6 @@ type IconImage = {
|
|
|
101
101
|
* Disabling will keep the image's original colors.
|
|
102
102
|
*
|
|
103
103
|
* Defaults to `true`.
|
|
104
|
-
*
|
|
105
|
-
* @platform ios
|
|
106
104
|
*/
|
|
107
105
|
tinted?: boolean;
|
|
108
106
|
};
|
|
@@ -118,10 +116,25 @@ type IconIOSSfSymbol = {
|
|
|
118
116
|
* @platform ios
|
|
119
117
|
*/
|
|
120
118
|
name: SFSymbol;
|
|
119
|
+
/**
|
|
120
|
+
* How to color the symbol.
|
|
121
|
+
* - `monochrome`: Single color, using the tint color.
|
|
122
|
+
* - `multicolor`: Uses the symbol's built-in multicolor scheme.
|
|
123
|
+
*
|
|
124
|
+
* Defaults to a single color for system symbols.
|
|
125
|
+
*
|
|
126
|
+
* @platform ios
|
|
127
|
+
*/
|
|
128
|
+
renderingMode?: 'monochrome' | 'multicolor';
|
|
121
129
|
};
|
|
122
130
|
|
|
123
131
|
export type NativeBottomTabIcon = IconIOSSfSymbol | IconImage;
|
|
124
132
|
|
|
133
|
+
type TabBarLabelOffset = {
|
|
134
|
+
horizontal?: number;
|
|
135
|
+
vertical?: number;
|
|
136
|
+
};
|
|
137
|
+
|
|
125
138
|
export type NativeBottomTabNavigationOptions = NativeHeaderOptions & {
|
|
126
139
|
/**
|
|
127
140
|
* Title text for the screen.
|
|
@@ -130,16 +143,90 @@ export type NativeBottomTabNavigationOptions = NativeHeaderOptions & {
|
|
|
130
143
|
|
|
131
144
|
/**
|
|
132
145
|
* Uses iOS built-in tab bar items with standard iOS styling and localized titles.
|
|
133
|
-
* If set to `search`, it's positioned next to the tab bar on iOS 26 and above.
|
|
134
146
|
*
|
|
135
|
-
* The `tabBarIcon` and `tabBarLabel` options
|
|
147
|
+
* The `tabBarIcon` and `tabBarLabel` options override the icon and label from the system item.
|
|
136
148
|
* If you want to keep the system behavior on iOS, but need to provide icon and label for other platforms,
|
|
137
149
|
* Use `Platform.OS` or `Platform.select` to conditionally set `undefined` for `tabBarIcon` and `tabBarLabel` on iOS.
|
|
138
150
|
*
|
|
151
|
+
* If set to `search`, it may receive the prominent visual treatment on iOS 26.
|
|
152
|
+
* See `tabBarRole` for more details on iOS 27 and above.
|
|
153
|
+
*
|
|
139
154
|
* @platform ios
|
|
140
155
|
*/
|
|
141
156
|
tabBarSystemItem?: TabsScreenSystemItem;
|
|
142
157
|
|
|
158
|
+
/**
|
|
159
|
+
* The role of the tab within the system tab bar.
|
|
160
|
+
*
|
|
161
|
+
* Supported values:
|
|
162
|
+
* - `prominent` - the tab gets the prominent visual treatment when supported
|
|
163
|
+
* - `search` - the tab is used as the system search tab
|
|
164
|
+
*
|
|
165
|
+
* If set to `prominent`, the tab may receive special styling to make it stand out,
|
|
166
|
+
* e.g., being positioned as a separate button next to the tab bar on iOS 27 and above.
|
|
167
|
+
* Only one tab can have the `prominent` role at a time.
|
|
168
|
+
* Supported on iOS 27 and above.
|
|
169
|
+
*
|
|
170
|
+
* If set to `search`, the tab gets the prominent visual treatment on iOS 26.
|
|
171
|
+
* On iOS 27 and above, it gets the prominent styling only if `tabBarSearchAutoFocus` is enabled,
|
|
172
|
+
* and no other tab has the `prominent` role.
|
|
173
|
+
* The tab uses system search icon and label by default.
|
|
174
|
+
* They can be overridden by `tabBarSystemItem`, `tabBarIcon`, and `tabBarLabel` options.
|
|
175
|
+
* Adding or removing the `search` role after the tab is rendered is not supported.
|
|
176
|
+
* Supported on iOS 26.1 and above.
|
|
177
|
+
*
|
|
178
|
+
* @platform ios
|
|
179
|
+
*/
|
|
180
|
+
tabBarRole?: 'prominent' | 'search';
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Whether the search field is automatically focused when the tab is selected.
|
|
184
|
+
* Cancelling the search selects the previously selected tab.
|
|
185
|
+
*
|
|
186
|
+
* Only applies to tabs with `tabBarRole: 'search'`,
|
|
187
|
+
* and requires `headerSearchBarOptions` to be set in a native stack nested in the tab.
|
|
188
|
+
*
|
|
189
|
+
* Defaults to `false`.
|
|
190
|
+
*
|
|
191
|
+
* Supported on iOS 26.1 and above.
|
|
192
|
+
*
|
|
193
|
+
* @platform ios
|
|
194
|
+
*/
|
|
195
|
+
tabBarSearchAutoFocus?: boolean;
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Whether the sidebar or the tab bar is preferred when only one of them can be displayed.
|
|
199
|
+
*
|
|
200
|
+
* Supported values:
|
|
201
|
+
* - `auto` - uses the system default (tab bar)
|
|
202
|
+
* - `sidebar` - the sidebar is displayed when it's available
|
|
203
|
+
* - `tabBar` - the tab bar is displayed
|
|
204
|
+
*
|
|
205
|
+
* Requires `tabBarControllerMode` to be set to `tabSidebar`.
|
|
206
|
+
*
|
|
207
|
+
* The sidebar is only available when the horizontal size class is regular,
|
|
208
|
+
* e.g. on iPad, or on larger iPhones in landscape.
|
|
209
|
+
*
|
|
210
|
+
* Defaults to `auto`.
|
|
211
|
+
*
|
|
212
|
+
* Supported on iOS 27 and above.
|
|
213
|
+
* Not supported on tvOS.
|
|
214
|
+
*
|
|
215
|
+
* @platform ios
|
|
216
|
+
*/
|
|
217
|
+
tabBarSidebarPreferredPlacement?: 'auto' | 'sidebar' | 'tabBar';
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Whether showing and hiding the tab bar is animated.
|
|
221
|
+
*
|
|
222
|
+
* Defaults to `true`.
|
|
223
|
+
*
|
|
224
|
+
* Supported on iOS 18 and above.
|
|
225
|
+
*
|
|
226
|
+
* @platform ios
|
|
227
|
+
*/
|
|
228
|
+
tabBarVisibilityAnimationEnabled?: boolean;
|
|
229
|
+
|
|
143
230
|
/**
|
|
144
231
|
* Title string of the tab displayed in the tab bar
|
|
145
232
|
*
|
|
@@ -186,6 +273,16 @@ export type NativeBottomTabNavigationOptions = NativeHeaderOptions & {
|
|
|
186
273
|
| NativeBottomTabIcon
|
|
187
274
|
| ((props: { focused: boolean }) => NativeBottomTabIcon);
|
|
188
275
|
|
|
276
|
+
/**
|
|
277
|
+
* Size of the tab's icon.
|
|
278
|
+
*
|
|
279
|
+
* The largest size is used for the icon area in all tabs.
|
|
280
|
+
* Icons smaller than the largest size are centered in this area.
|
|
281
|
+
*
|
|
282
|
+
* @platform android
|
|
283
|
+
*/
|
|
284
|
+
tabBarIconSize?: number;
|
|
285
|
+
|
|
189
286
|
/**
|
|
190
287
|
* Text to show in a badge on the tab icon.
|
|
191
288
|
*/
|
|
@@ -228,6 +325,20 @@ export type NativeBottomTabNavigationOptions = NativeHeaderOptions & {
|
|
|
228
325
|
*/
|
|
229
326
|
tabBarActiveIndicatorEnabled?: boolean;
|
|
230
327
|
|
|
328
|
+
/**
|
|
329
|
+
* Width of the tab's active indicator.
|
|
330
|
+
*
|
|
331
|
+
* @platform android
|
|
332
|
+
*/
|
|
333
|
+
tabBarActiveIndicatorWidth?: number;
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Height of the tab's active indicator.
|
|
337
|
+
*
|
|
338
|
+
* @platform android
|
|
339
|
+
*/
|
|
340
|
+
tabBarActiveIndicatorHeight?: number;
|
|
341
|
+
|
|
231
342
|
/**
|
|
232
343
|
* Color of tab bar item's ripple effect.
|
|
233
344
|
*
|
|
@@ -347,6 +458,24 @@ export type NativeBottomTabNavigationOptions = NativeHeaderOptions & {
|
|
|
347
458
|
*/
|
|
348
459
|
tabBarMinimizeBehavior?: 'auto' | 'none' | 'onScrollDown' | 'onScrollUp';
|
|
349
460
|
|
|
461
|
+
/**
|
|
462
|
+
* Offset applied to the tab label relative to its default position.
|
|
463
|
+
* Positive `vertical` moves the label down, positive `horizontal` moves it right.
|
|
464
|
+
*
|
|
465
|
+
* The offset is specified separately for each tab bar item layout:
|
|
466
|
+
*
|
|
467
|
+
* - `stacked` - icon above the label, e.g. on iPhone in portrait
|
|
468
|
+
* - `inline` - icon beside the label in regular width, e.g. on iPad
|
|
469
|
+
* - `compactInline` - icon beside the label in compact width, e.g. on iPhone in landscape
|
|
470
|
+
*
|
|
471
|
+
* @platform ios
|
|
472
|
+
*/
|
|
473
|
+
tabBarLabelPositionAdjustment?: {
|
|
474
|
+
stacked?: TabBarLabelOffset;
|
|
475
|
+
inline?: TabBarLabelOffset;
|
|
476
|
+
compactInline?: TabBarLabelOffset;
|
|
477
|
+
};
|
|
478
|
+
|
|
350
479
|
/**
|
|
351
480
|
* Function which returns a React element to display as an accessory view.
|
|
352
481
|
*
|
|
@@ -25,7 +25,13 @@ import {
|
|
|
25
25
|
} from 'react-native';
|
|
26
26
|
import { type EdgeInsets } from 'react-native-safe-area-context';
|
|
27
27
|
|
|
28
|
-
import type {
|
|
28
|
+
import type {
|
|
29
|
+
BottomTabBarProps,
|
|
30
|
+
BottomTabDescriptor,
|
|
31
|
+
BottomTabDescriptorMap,
|
|
32
|
+
LabelVisibilityMode,
|
|
33
|
+
Variant,
|
|
34
|
+
} from '../types';
|
|
29
35
|
import { BottomTabBarHeightCallbackContext } from '../utils/BottomTabBarHeightCallbackContext';
|
|
30
36
|
import { useIsKeyboardShown } from '../utils/useIsKeyboardShown';
|
|
31
37
|
import { BottomTabItem } from './BottomTabItem';
|
|
@@ -396,38 +402,6 @@ export function BottomTabBar({
|
|
|
396
402
|
const focused = index === state.index;
|
|
397
403
|
const { options } = descriptors[route.key];
|
|
398
404
|
|
|
399
|
-
const onPress = () => {
|
|
400
|
-
const event = navigation.emit({
|
|
401
|
-
type: 'tabPress',
|
|
402
|
-
target: route.key,
|
|
403
|
-
canPreventDefault: true,
|
|
404
|
-
data: {
|
|
405
|
-
behavior: {
|
|
406
|
-
scrollToTop:
|
|
407
|
-
focused &&
|
|
408
|
-
options.tabBarRepeatedPressBehavior?.scrollToTop !== false,
|
|
409
|
-
popToTop:
|
|
410
|
-
focused &&
|
|
411
|
-
options.tabBarRepeatedPressBehavior?.popToTop !== false,
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
if (!focused && !event.defaultPrevented) {
|
|
417
|
-
navigation.dispatch({
|
|
418
|
-
...CommonActions.navigate(route.name, route.params),
|
|
419
|
-
target: state.key,
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
};
|
|
423
|
-
|
|
424
|
-
const onLongPress = () => {
|
|
425
|
-
navigation.emit({
|
|
426
|
-
type: 'tabLongPress',
|
|
427
|
-
target: route.key,
|
|
428
|
-
});
|
|
429
|
-
};
|
|
430
|
-
|
|
431
405
|
const label =
|
|
432
406
|
typeof options.tabBarLabel === 'function'
|
|
433
407
|
? options.tabBarLabel
|
|
@@ -444,57 +418,28 @@ export function BottomTabBar({
|
|
|
444
418
|
: undefined;
|
|
445
419
|
|
|
446
420
|
return (
|
|
447
|
-
<
|
|
421
|
+
<BottomTabBarItem
|
|
448
422
|
key={route.key}
|
|
449
423
|
route={route}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
inactiveBackgroundColor={tabBarInactiveBackgroundColor}
|
|
470
|
-
button={options.tabBarButton}
|
|
471
|
-
icon={
|
|
472
|
-
options.tabBarIcon ??
|
|
473
|
-
(({ color, size }) => (
|
|
474
|
-
<MissingIcon color={color} size={size} />
|
|
475
|
-
))
|
|
476
|
-
}
|
|
477
|
-
badge={options.tabBarBadge}
|
|
478
|
-
badgeStyle={options.tabBarBadgeStyle}
|
|
479
|
-
label={label}
|
|
480
|
-
showLabel={tabBarShowLabel}
|
|
481
|
-
labelVisibilityMode={tabBarLabelVisibilityMode}
|
|
482
|
-
labelStyle={options.tabBarLabelStyle}
|
|
483
|
-
iconStyle={options.tabBarIconStyle}
|
|
484
|
-
style={[
|
|
485
|
-
sidebar
|
|
486
|
-
? {
|
|
487
|
-
marginVertical: hasHorizontalLabels
|
|
488
|
-
? tabBarVariant === 'material'
|
|
489
|
-
? 0
|
|
490
|
-
: 1
|
|
491
|
-
: spacing / 2,
|
|
492
|
-
}
|
|
493
|
-
: styles.bottomItem,
|
|
494
|
-
options.tabBarItemStyle,
|
|
495
|
-
]}
|
|
496
|
-
/>
|
|
497
|
-
</NavigationProvider>
|
|
424
|
+
descriptor={descriptors[route.key]}
|
|
425
|
+
navigation={navigation}
|
|
426
|
+
navigatorKey={state.key}
|
|
427
|
+
href={buildHref(route.name, route.params)}
|
|
428
|
+
focused={focused}
|
|
429
|
+
label={label}
|
|
430
|
+
accessibilityLabel={accessibilityLabel}
|
|
431
|
+
hasHorizontalLabels={hasHorizontalLabels}
|
|
432
|
+
compact={compact}
|
|
433
|
+
sidebar={sidebar}
|
|
434
|
+
tabBarVariant={tabBarVariant}
|
|
435
|
+
spacing={spacing}
|
|
436
|
+
tabBarActiveTintColor={tabBarActiveTintColor}
|
|
437
|
+
tabBarInactiveTintColor={tabBarInactiveTintColor}
|
|
438
|
+
tabBarActiveBackgroundColor={tabBarActiveBackgroundColor}
|
|
439
|
+
tabBarInactiveBackgroundColor={tabBarInactiveBackgroundColor}
|
|
440
|
+
tabBarShowLabel={tabBarShowLabel}
|
|
441
|
+
tabBarLabelVisibilityMode={tabBarLabelVisibilityMode}
|
|
442
|
+
/>
|
|
498
443
|
);
|
|
499
444
|
})}
|
|
500
445
|
</View>
|
|
@@ -502,6 +447,132 @@ export function BottomTabBar({
|
|
|
502
447
|
);
|
|
503
448
|
}
|
|
504
449
|
|
|
450
|
+
type BottomTabBarItemProps = {
|
|
451
|
+
route: TabNavigationState<ParamListBase>['routes'][number];
|
|
452
|
+
descriptor: BottomTabDescriptor;
|
|
453
|
+
navigation: BottomTabBarProps['navigation'];
|
|
454
|
+
navigatorKey: string;
|
|
455
|
+
href: string | undefined;
|
|
456
|
+
focused: boolean;
|
|
457
|
+
label: React.ComponentProps<typeof BottomTabItem>['label'];
|
|
458
|
+
accessibilityLabel: string | undefined;
|
|
459
|
+
hasHorizontalLabels: boolean;
|
|
460
|
+
compact: boolean;
|
|
461
|
+
sidebar: boolean;
|
|
462
|
+
tabBarVariant: Variant;
|
|
463
|
+
spacing: number;
|
|
464
|
+
tabBarActiveTintColor: string | undefined;
|
|
465
|
+
tabBarInactiveTintColor: string | undefined;
|
|
466
|
+
tabBarActiveBackgroundColor: string | undefined;
|
|
467
|
+
tabBarInactiveBackgroundColor: string | undefined;
|
|
468
|
+
tabBarShowLabel: boolean | undefined;
|
|
469
|
+
tabBarLabelVisibilityMode: LabelVisibilityMode | undefined;
|
|
470
|
+
};
|
|
471
|
+
|
|
472
|
+
const BottomTabBarItem = React.memo(function BottomTabBarItem({
|
|
473
|
+
route,
|
|
474
|
+
descriptor,
|
|
475
|
+
navigation,
|
|
476
|
+
navigatorKey,
|
|
477
|
+
href,
|
|
478
|
+
focused,
|
|
479
|
+
label,
|
|
480
|
+
accessibilityLabel,
|
|
481
|
+
hasHorizontalLabels,
|
|
482
|
+
compact,
|
|
483
|
+
sidebar,
|
|
484
|
+
tabBarVariant,
|
|
485
|
+
spacing,
|
|
486
|
+
tabBarActiveTintColor,
|
|
487
|
+
tabBarInactiveTintColor,
|
|
488
|
+
tabBarActiveBackgroundColor,
|
|
489
|
+
tabBarInactiveBackgroundColor,
|
|
490
|
+
tabBarShowLabel,
|
|
491
|
+
tabBarLabelVisibilityMode,
|
|
492
|
+
}: BottomTabBarItemProps) {
|
|
493
|
+
const { options } = descriptor;
|
|
494
|
+
|
|
495
|
+
const onPress = () => {
|
|
496
|
+
const event = navigation.emit({
|
|
497
|
+
type: 'tabPress',
|
|
498
|
+
target: route.key,
|
|
499
|
+
canPreventDefault: true,
|
|
500
|
+
data: {
|
|
501
|
+
behavior: {
|
|
502
|
+
scrollToTop:
|
|
503
|
+
focused &&
|
|
504
|
+
options.tabBarRepeatedPressBehavior?.scrollToTop !== false,
|
|
505
|
+
popToTop:
|
|
506
|
+
focused && options.tabBarRepeatedPressBehavior?.popToTop !== false,
|
|
507
|
+
},
|
|
508
|
+
},
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
if (!focused && !event.defaultPrevented) {
|
|
512
|
+
navigation.dispatch({
|
|
513
|
+
...CommonActions.navigate(route.name, route.params),
|
|
514
|
+
target: navigatorKey,
|
|
515
|
+
});
|
|
516
|
+
}
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const onLongPress = () => {
|
|
520
|
+
navigation.emit({
|
|
521
|
+
type: 'tabLongPress',
|
|
522
|
+
target: route.key,
|
|
523
|
+
});
|
|
524
|
+
};
|
|
525
|
+
|
|
526
|
+
return (
|
|
527
|
+
<NavigationProvider route={route} navigation={descriptor.navigation}>
|
|
528
|
+
<BottomTabItem
|
|
529
|
+
href={href}
|
|
530
|
+
route={route}
|
|
531
|
+
descriptor={descriptor}
|
|
532
|
+
focused={focused}
|
|
533
|
+
horizontal={hasHorizontalLabels}
|
|
534
|
+
compact={compact}
|
|
535
|
+
sidebar={sidebar}
|
|
536
|
+
variant={tabBarVariant}
|
|
537
|
+
onPress={onPress}
|
|
538
|
+
onLongPress={onLongPress}
|
|
539
|
+
accessibilityLabel={accessibilityLabel}
|
|
540
|
+
testID={options.tabBarButtonTestID}
|
|
541
|
+
allowFontScaling={options.tabBarAllowFontScaling}
|
|
542
|
+
activeTintColor={tabBarActiveTintColor}
|
|
543
|
+
inactiveTintColor={tabBarInactiveTintColor}
|
|
544
|
+
activeBackgroundColor={tabBarActiveBackgroundColor}
|
|
545
|
+
inactiveBackgroundColor={tabBarInactiveBackgroundColor}
|
|
546
|
+
button={options.tabBarButton}
|
|
547
|
+
icon={
|
|
548
|
+
options.tabBarIcon ??
|
|
549
|
+
(({ color, size }) => <MissingIcon color={color} size={size} />)
|
|
550
|
+
}
|
|
551
|
+
badge={options.tabBarBadge}
|
|
552
|
+
badgeStyle={options.tabBarBadgeStyle}
|
|
553
|
+
label={label}
|
|
554
|
+
showLabel={tabBarShowLabel}
|
|
555
|
+
labelVisibilityMode={tabBarLabelVisibilityMode}
|
|
556
|
+
labelStyle={options.tabBarLabelStyle}
|
|
557
|
+
iconSize={options.tabBarIconSize}
|
|
558
|
+
iconStyle={options.tabBarIconStyle}
|
|
559
|
+
style={[
|
|
560
|
+
sidebar
|
|
561
|
+
? {
|
|
562
|
+
marginVertical: hasHorizontalLabels
|
|
563
|
+
? tabBarVariant === 'material'
|
|
564
|
+
? 0
|
|
565
|
+
: 1
|
|
566
|
+
: spacing / 2,
|
|
567
|
+
}
|
|
568
|
+
: styles.bottomItem,
|
|
569
|
+
options.tabBarItemStyle,
|
|
570
|
+
]}
|
|
571
|
+
/>
|
|
572
|
+
</NavigationProvider>
|
|
573
|
+
);
|
|
574
|
+
});
|
|
575
|
+
|
|
505
576
|
const styles = StyleSheet.create({
|
|
506
577
|
start: {
|
|
507
578
|
top: 0,
|
|
@@ -140,6 +140,10 @@ type Props = {
|
|
|
140
140
|
* Style object for the label element.
|
|
141
141
|
*/
|
|
142
142
|
labelStyle?: StyleProp<TextStyle>;
|
|
143
|
+
/**
|
|
144
|
+
* Size of the icon.
|
|
145
|
+
*/
|
|
146
|
+
iconSize?: number;
|
|
143
147
|
/**
|
|
144
148
|
* Style object for the icon element.
|
|
145
149
|
*/
|
|
@@ -186,6 +190,7 @@ export function BottomTabItem({
|
|
|
186
190
|
// https://developer.apple.com/documentation/uikit/uiview/3183939-largecontenttitle
|
|
187
191
|
allowFontScaling = SUPPORTS_LARGE_CONTENT_VIEWER ? false : undefined,
|
|
188
192
|
labelStyle,
|
|
193
|
+
iconSize,
|
|
189
194
|
iconStyle,
|
|
190
195
|
style,
|
|
191
196
|
}: Props) {
|
|
@@ -300,6 +305,7 @@ export function BottomTabItem({
|
|
|
300
305
|
route={route}
|
|
301
306
|
variant={variant}
|
|
302
307
|
size={compact ? 'compact' : 'regular'}
|
|
308
|
+
iconSize={iconSize}
|
|
303
309
|
badge={badge}
|
|
304
310
|
badgeStyle={badgeStyle}
|
|
305
311
|
activeOpacity={activeOpacity}
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Screen,
|
|
6
6
|
} from '@react-navigation/elements';
|
|
7
7
|
import {
|
|
8
|
+
type NavigationRoute,
|
|
8
9
|
type ParamListBase,
|
|
9
10
|
StackActions,
|
|
10
11
|
type TabNavigationState,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
} from '../TransitionConfigs/TransitionPresets';
|
|
20
21
|
import type {
|
|
21
22
|
BottomTabBarProps,
|
|
23
|
+
BottomTabDescriptor,
|
|
22
24
|
BottomTabDescriptorMap,
|
|
23
25
|
BottomTabHeaderProps,
|
|
24
26
|
BottomTabNavigationConfig,
|
|
@@ -283,12 +285,7 @@ export function BottomTabView(props: Props) {
|
|
|
283
285
|
>
|
|
284
286
|
{routes.map((route, index) => {
|
|
285
287
|
const descriptor = descriptors[route.key];
|
|
286
|
-
const {
|
|
287
|
-
lazy = true,
|
|
288
|
-
animation = 'none',
|
|
289
|
-
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
290
|
-
.sceneStyleInterpolator,
|
|
291
|
-
} = descriptor.options;
|
|
288
|
+
const { lazy = true } = descriptor.options;
|
|
292
289
|
const isFocused = state.index === index;
|
|
293
290
|
const isPreloaded = state.preloadedRouteKeys.includes(route.key);
|
|
294
291
|
|
|
@@ -302,73 +299,23 @@ export function BottomTabView(props: Props) {
|
|
|
302
299
|
return null;
|
|
303
300
|
}
|
|
304
301
|
|
|
305
|
-
const {
|
|
306
|
-
freezeOnBlur,
|
|
307
|
-
header = ({ layout, options }: BottomTabHeaderProps) => (
|
|
308
|
-
<Header
|
|
309
|
-
{...options}
|
|
310
|
-
layout={layout}
|
|
311
|
-
title={getHeaderTitle(options, route.name)}
|
|
312
|
-
/>
|
|
313
|
-
),
|
|
314
|
-
headerShown,
|
|
315
|
-
headerStatusBarHeight,
|
|
316
|
-
headerTransparent,
|
|
317
|
-
sceneStyle: customSceneStyle,
|
|
318
|
-
} = descriptor.options;
|
|
319
|
-
|
|
320
|
-
const { sceneStyle } =
|
|
321
|
-
sceneStyleInterpolator?.({
|
|
322
|
-
current: {
|
|
323
|
-
progress: tabAnims[route.key],
|
|
324
|
-
},
|
|
325
|
-
}) ?? {};
|
|
326
|
-
|
|
327
|
-
const animationEnabled = hasAnimation(descriptor.options);
|
|
328
302
|
const isAnimatingRoute =
|
|
329
303
|
lastUpdate.animating &&
|
|
330
304
|
(lastUpdate.previous === route.key ||
|
|
331
305
|
lastUpdate.current === route.key);
|
|
332
306
|
|
|
333
|
-
const activityState = isFocused
|
|
334
|
-
? STATE_ON_TOP // the screen is on top after the transition
|
|
335
|
-
: animationEnabled && isAnimatingRoute
|
|
336
|
-
? STATE_TRANSITIONING_OR_BELOW_TOP // screen visible during transition
|
|
337
|
-
: STATE_INACTIVE;
|
|
338
|
-
|
|
339
307
|
return (
|
|
340
|
-
<
|
|
308
|
+
<TabScene
|
|
341
309
|
key={route.key}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
>
|
|
352
|
-
<Screen
|
|
353
|
-
focused={isFocused}
|
|
354
|
-
route={descriptor.route}
|
|
355
|
-
navigation={descriptor.navigation}
|
|
356
|
-
headerShown={headerShown}
|
|
357
|
-
headerStatusBarHeight={headerStatusBarHeight}
|
|
358
|
-
headerTransparent={headerTransparent}
|
|
359
|
-
header={header({
|
|
360
|
-
layout: dimensions,
|
|
361
|
-
route: descriptor.route,
|
|
362
|
-
navigation:
|
|
363
|
-
descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
|
|
364
|
-
options: descriptor.options,
|
|
365
|
-
})}
|
|
366
|
-
style={[customSceneStyle, animationEnabled && sceneStyle]}
|
|
367
|
-
>
|
|
368
|
-
{descriptor.render()}
|
|
369
|
-
</Screen>
|
|
370
|
-
</BottomTabBarHeightContext.Provider>
|
|
371
|
-
</MaybeScreen>
|
|
310
|
+
route={route}
|
|
311
|
+
descriptor={descriptor}
|
|
312
|
+
isFocused={isFocused}
|
|
313
|
+
isPreloaded={isPreloaded}
|
|
314
|
+
isAnimatingRoute={isAnimatingRoute}
|
|
315
|
+
tabAnimation={tabAnims[route.key]}
|
|
316
|
+
tabBarHeight={tabBarPosition === 'bottom' ? tabBarHeight : 0}
|
|
317
|
+
detachInactiveScreens={detachInactiveScreens}
|
|
318
|
+
/>
|
|
372
319
|
);
|
|
373
320
|
})}
|
|
374
321
|
</MaybeScreenContainer>
|
|
@@ -379,6 +326,93 @@ export function BottomTabView(props: Props) {
|
|
|
379
326
|
);
|
|
380
327
|
}
|
|
381
328
|
|
|
329
|
+
type TabSceneProps = {
|
|
330
|
+
route: NavigationRoute<ParamListBase, string>;
|
|
331
|
+
descriptor: BottomTabDescriptor;
|
|
332
|
+
isFocused: boolean;
|
|
333
|
+
isPreloaded: boolean;
|
|
334
|
+
isAnimatingRoute: boolean;
|
|
335
|
+
tabAnimation: Animated.Value;
|
|
336
|
+
tabBarHeight: number;
|
|
337
|
+
detachInactiveScreens: boolean;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const TabScene = React.memo(function TabScene({
|
|
341
|
+
route,
|
|
342
|
+
descriptor,
|
|
343
|
+
isFocused,
|
|
344
|
+
isPreloaded,
|
|
345
|
+
isAnimatingRoute,
|
|
346
|
+
tabAnimation,
|
|
347
|
+
tabBarHeight,
|
|
348
|
+
detachInactiveScreens,
|
|
349
|
+
}: TabSceneProps) {
|
|
350
|
+
const {
|
|
351
|
+
animation = 'none',
|
|
352
|
+
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
353
|
+
.sceneStyleInterpolator,
|
|
354
|
+
freezeOnBlur,
|
|
355
|
+
header = ({ layout, options }: BottomTabHeaderProps) => (
|
|
356
|
+
<Header
|
|
357
|
+
{...options}
|
|
358
|
+
layout={layout}
|
|
359
|
+
title={getHeaderTitle(options, route.name)}
|
|
360
|
+
/>
|
|
361
|
+
),
|
|
362
|
+
headerShown,
|
|
363
|
+
headerStatusBarHeight,
|
|
364
|
+
headerTransparent,
|
|
365
|
+
sceneStyle: customSceneStyle,
|
|
366
|
+
} = descriptor.options;
|
|
367
|
+
|
|
368
|
+
const { sceneStyle } =
|
|
369
|
+
sceneStyleInterpolator?.({
|
|
370
|
+
current: {
|
|
371
|
+
progress: tabAnimation,
|
|
372
|
+
},
|
|
373
|
+
}) ?? {};
|
|
374
|
+
|
|
375
|
+
const animationEnabled = hasAnimation(descriptor.options);
|
|
376
|
+
|
|
377
|
+
const activityState = isFocused
|
|
378
|
+
? STATE_ON_TOP // the screen is on top after the transition
|
|
379
|
+
: animationEnabled && isAnimatingRoute
|
|
380
|
+
? STATE_TRANSITIONING_OR_BELOW_TOP // screen visible during transition
|
|
381
|
+
: STATE_INACTIVE;
|
|
382
|
+
|
|
383
|
+
return (
|
|
384
|
+
<MaybeScreen
|
|
385
|
+
style={[StyleSheet.absoluteFill, { zIndex: isFocused ? 0 : -1 }]}
|
|
386
|
+
active={activityState}
|
|
387
|
+
enabled={detachInactiveScreens}
|
|
388
|
+
freezeOnBlur={freezeOnBlur}
|
|
389
|
+
shouldFreeze={activityState === STATE_INACTIVE && !isPreloaded}
|
|
390
|
+
pointerEvents={isFocused ? 'box-none' : 'none'}
|
|
391
|
+
>
|
|
392
|
+
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
|
|
393
|
+
<Screen
|
|
394
|
+
focused={isFocused}
|
|
395
|
+
route={descriptor.route}
|
|
396
|
+
navigation={descriptor.navigation}
|
|
397
|
+
headerShown={headerShown}
|
|
398
|
+
headerStatusBarHeight={headerStatusBarHeight}
|
|
399
|
+
headerTransparent={headerTransparent}
|
|
400
|
+
header={header({
|
|
401
|
+
layout: SafeAreaProviderCompat.initialMetrics.frame,
|
|
402
|
+
route: descriptor.route,
|
|
403
|
+
navigation:
|
|
404
|
+
descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
|
|
405
|
+
options: descriptor.options,
|
|
406
|
+
})}
|
|
407
|
+
style={[customSceneStyle, animationEnabled && sceneStyle]}
|
|
408
|
+
>
|
|
409
|
+
{descriptor.render()}
|
|
410
|
+
</Screen>
|
|
411
|
+
</BottomTabBarHeightContext.Provider>
|
|
412
|
+
</MaybeScreen>
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
382
416
|
const styles = StyleSheet.create({
|
|
383
417
|
screens: {
|
|
384
418
|
flex: 1,
|