@react-navigation/bottom-tabs 8.0.0-alpha.55 → 8.0.0-alpha.57
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/views/BottomTabBar.js +106 -61
- 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/BottomTabViewCustom.js +62 -37
- package/lib/module/views/BottomTabViewCustom.js.map +1 -1
- package/lib/module/views/BottomTabViewNativeImpl.js +348 -245
- package/lib/module/views/BottomTabViewNativeImpl.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 +130 -5
- package/lib/typescript/src/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/BottomTabViewCustom.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabViewNativeImpl.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 +7 -7
- package/src/types.tsx +141 -5
- package/src/views/BottomTabBar.tsx +158 -87
- package/src/views/BottomTabItem.tsx +6 -0
- package/src/views/BottomTabViewCustom.tsx +84 -47
- package/src/views/BottomTabViewNativeImpl.tsx +439 -284
- package/src/views/TabBarIcon.tsx +9 -2
package/src/types.tsx
CHANGED
|
@@ -194,7 +194,7 @@ type BottomTabCustomOptions = {
|
|
|
194
194
|
*
|
|
195
195
|
* Only supported with `custom` implementation.
|
|
196
196
|
*/
|
|
197
|
-
tabBarIconStyle?: StyleProp<
|
|
197
|
+
tabBarIconStyle?: StyleProp<ViewStyle>;
|
|
198
198
|
|
|
199
199
|
/**
|
|
200
200
|
* Whether the tab bar gets hidden when the keyboard is shown.
|
|
@@ -274,19 +274,106 @@ type BottomTabCustomOptions = {
|
|
|
274
274
|
| undefined;
|
|
275
275
|
};
|
|
276
276
|
|
|
277
|
+
type TabBarLabelOffset = {
|
|
278
|
+
horizontal?: number | undefined;
|
|
279
|
+
vertical?: number | undefined;
|
|
280
|
+
};
|
|
281
|
+
|
|
277
282
|
type BottomTabNativeOptions = {
|
|
278
283
|
/**
|
|
279
284
|
* Uses iOS built-in tab bar items with standard iOS styling and localized titles.
|
|
280
|
-
* If set to `search`, it's positioned next to the tab bar on iOS 26 and above.
|
|
281
285
|
*
|
|
282
|
-
* The `tabBarIcon` and `tabBarLabel` options
|
|
286
|
+
* The `tabBarIcon` and `tabBarLabel` options override the icon and label from the system item.
|
|
283
287
|
* If you want to keep the system behavior on iOS, but need to provide icon and label for other platforms,
|
|
284
288
|
* Use `Platform.OS` or `Platform.select` to conditionally set `undefined` for `tabBarIcon` and `tabBarLabel` on iOS.
|
|
285
289
|
*
|
|
290
|
+
* If set to `search`, it may receive the prominent visual treatment on iOS 26.
|
|
291
|
+
* See `tabBarRole` for more details on iOS 27 and above.
|
|
292
|
+
*
|
|
286
293
|
* @platform ios
|
|
287
294
|
*/
|
|
288
295
|
tabBarSystemItem?: TabsScreenSystemItem | undefined;
|
|
289
296
|
|
|
297
|
+
/**
|
|
298
|
+
* The role of the tab within the system tab bar.
|
|
299
|
+
*
|
|
300
|
+
* Supported values:
|
|
301
|
+
* - `prominent` - the tab gets the prominent visual treatment when supported
|
|
302
|
+
* - `search` - the tab is used as the system search tab
|
|
303
|
+
*
|
|
304
|
+
* If set to `prominent`, the tab may receive special styling to make it stand out,
|
|
305
|
+
* e.g., being positioned as a separate button next to the tab bar on iOS 27 and above.
|
|
306
|
+
* Only one tab can have the `prominent` role at a time.
|
|
307
|
+
* Supported on iOS 27 and above.
|
|
308
|
+
*
|
|
309
|
+
* If set to `search`, the tab gets the prominent visual treatment on iOS 26.
|
|
310
|
+
* On iOS 27 and above, it gets the prominent styling only if `tabBarSearchAutoFocus` is enabled,
|
|
311
|
+
* and no other tab has the `prominent` role.
|
|
312
|
+
* The tab uses system search icon and label by default.
|
|
313
|
+
* They can be overridden by `tabBarSystemItem`, `tabBarIcon`, and `tabBarLabel` options.
|
|
314
|
+
* Adding or removing the `search` role after the tab is rendered is not supported.
|
|
315
|
+
* Supported on iOS 26.1 and above.
|
|
316
|
+
*
|
|
317
|
+
* Only supported with `native` implementation.
|
|
318
|
+
*
|
|
319
|
+
* @platform ios
|
|
320
|
+
*/
|
|
321
|
+
tabBarRole?: 'prominent' | 'search' | undefined;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Whether the search field is automatically focused when the tab is selected.
|
|
325
|
+
* Cancelling the search selects the previously selected tab.
|
|
326
|
+
*
|
|
327
|
+
* Only applies to tabs with `tabBarRole: 'search'`,
|
|
328
|
+
* and requires `headerSearchBarOptions` to be set in a native stack nested in the tab.
|
|
329
|
+
*
|
|
330
|
+
* Defaults to `false`.
|
|
331
|
+
*
|
|
332
|
+
* Only supported with `native` implementation.
|
|
333
|
+
*
|
|
334
|
+
* Supported on iOS 26.1 and above.
|
|
335
|
+
*
|
|
336
|
+
* @platform ios
|
|
337
|
+
*/
|
|
338
|
+
tabBarSearchAutoFocus?: boolean | undefined;
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Whether the sidebar or the tab bar is preferred when only one of them can be displayed.
|
|
342
|
+
*
|
|
343
|
+
* Supported values:
|
|
344
|
+
* - `auto` - uses the system default (tab bar)
|
|
345
|
+
* - `sidebar` - the sidebar is displayed when it's available
|
|
346
|
+
* - `tabBar` - the tab bar is displayed
|
|
347
|
+
*
|
|
348
|
+
* Requires `tabBarControllerMode` to be set to `tabSidebar`.
|
|
349
|
+
*
|
|
350
|
+
* The sidebar is only available when the horizontal size class is regular,
|
|
351
|
+
* e.g. on iPad, or on larger iPhones in landscape.
|
|
352
|
+
*
|
|
353
|
+
* Defaults to `auto`.
|
|
354
|
+
*
|
|
355
|
+
* Only supported with `native` implementation.
|
|
356
|
+
*
|
|
357
|
+
* Supported on iOS 27 and above.
|
|
358
|
+
* Not supported on tvOS.
|
|
359
|
+
*
|
|
360
|
+
* @platform ios
|
|
361
|
+
*/
|
|
362
|
+
tabBarSidebarPreferredPlacement?: 'auto' | 'sidebar' | 'tabBar' | undefined;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Whether showing and hiding the tab bar is animated.
|
|
366
|
+
*
|
|
367
|
+
* Defaults to `true`.
|
|
368
|
+
*
|
|
369
|
+
* Only supported with `native` implementation.
|
|
370
|
+
*
|
|
371
|
+
* Supported on iOS 18 and above.
|
|
372
|
+
*
|
|
373
|
+
* @platform ios
|
|
374
|
+
*/
|
|
375
|
+
tabBarVisibilityAnimationEnabled?: boolean | undefined;
|
|
376
|
+
|
|
290
377
|
/**
|
|
291
378
|
* Blur effect applied to the tab bar when tab screen is selected.
|
|
292
379
|
*
|
|
@@ -337,6 +424,28 @@ type BottomTabNativeOptions = {
|
|
|
337
424
|
| 'onScrollUp'
|
|
338
425
|
| undefined;
|
|
339
426
|
|
|
427
|
+
/**
|
|
428
|
+
* Offset applied to the tab label relative to its default position.
|
|
429
|
+
* Positive `vertical` moves the label down, positive `horizontal` moves it right.
|
|
430
|
+
*
|
|
431
|
+
* The offset is specified separately for each tab bar item layout:
|
|
432
|
+
*
|
|
433
|
+
* - `stacked` - icon above the label, e.g. on iPhone in portrait
|
|
434
|
+
* - `inline` - icon beside the label in regular width, e.g. on iPad
|
|
435
|
+
* - `compactInline` - icon beside the label in compact width, e.g. on iPhone in landscape
|
|
436
|
+
*
|
|
437
|
+
* Only supported with `native` implementation.
|
|
438
|
+
*
|
|
439
|
+
* @platform ios
|
|
440
|
+
*/
|
|
441
|
+
tabBarLabelPositionAdjustment?:
|
|
442
|
+
| {
|
|
443
|
+
stacked?: TabBarLabelOffset | undefined;
|
|
444
|
+
inline?: TabBarLabelOffset | undefined;
|
|
445
|
+
compactInline?: TabBarLabelOffset | undefined;
|
|
446
|
+
}
|
|
447
|
+
| undefined;
|
|
448
|
+
|
|
340
449
|
/**
|
|
341
450
|
* Background color of the active indicator.
|
|
342
451
|
*
|
|
@@ -355,6 +464,24 @@ type BottomTabNativeOptions = {
|
|
|
355
464
|
*/
|
|
356
465
|
tabBarActiveIndicatorEnabled?: boolean | undefined;
|
|
357
466
|
|
|
467
|
+
/**
|
|
468
|
+
* Width of the tab's active indicator.
|
|
469
|
+
*
|
|
470
|
+
* Only supported with `native` implementation.
|
|
471
|
+
*
|
|
472
|
+
* @platform android
|
|
473
|
+
*/
|
|
474
|
+
tabBarActiveIndicatorWidth?: number | undefined;
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Height of the tab's active indicator.
|
|
478
|
+
*
|
|
479
|
+
* Only supported with `native` implementation.
|
|
480
|
+
*
|
|
481
|
+
* @platform android
|
|
482
|
+
*/
|
|
483
|
+
tabBarActiveIndicatorHeight?: number | undefined;
|
|
484
|
+
|
|
358
485
|
/**
|
|
359
486
|
* Function which returns a React element to display as an accessory view.
|
|
360
487
|
*
|
|
@@ -431,6 +558,17 @@ export type BottomTabNavigationOptions = {
|
|
|
431
558
|
*/
|
|
432
559
|
tabBarLabelVisibilityMode?: TabBarItemLabelVisibilityMode | undefined;
|
|
433
560
|
|
|
561
|
+
/**
|
|
562
|
+
* Size of the tab's icon.
|
|
563
|
+
*
|
|
564
|
+
* With `native` implementation, the largest size is used for the icon area in all tabs.
|
|
565
|
+
* Icons smaller than the largest size are centered in this area.
|
|
566
|
+
*
|
|
567
|
+
* Supported on all platforms with `custom` implementation.
|
|
568
|
+
* Only supported on Android with `native` implementation.
|
|
569
|
+
*/
|
|
570
|
+
tabBarIconSize?: number | undefined;
|
|
571
|
+
|
|
434
572
|
/**
|
|
435
573
|
* Style object for the tab label.
|
|
436
574
|
*/
|
|
@@ -450,8 +588,6 @@ export type BottomTabNavigationOptions = {
|
|
|
450
588
|
* - on all platforms with `custom` implementation
|
|
451
589
|
* - on iOS with `native` implementation
|
|
452
590
|
*
|
|
453
|
-
* With `native` implementation, `tinted` for `image` can only be disabled on iOS.
|
|
454
|
-
*
|
|
455
591
|
* A React element is only supported with `custom` implementation.
|
|
456
592
|
*/
|
|
457
593
|
tabBarIcon?:
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
import * as React from 'react';
|
|
16
16
|
import {
|
|
17
17
|
Animated,
|
|
18
|
+
type ColorValue,
|
|
18
19
|
type LayoutChangeEvent,
|
|
19
20
|
Platform,
|
|
20
21
|
type StyleProp,
|
|
@@ -26,8 +27,14 @@ import {
|
|
|
26
27
|
type EdgeInsets,
|
|
27
28
|
useSafeAreaInsets,
|
|
28
29
|
} from 'react-native-safe-area-context';
|
|
29
|
-
|
|
30
|
-
|
|
30
|
+
import type { TabBarItemLabelVisibilityMode } from 'react-native-screens';
|
|
31
|
+
|
|
32
|
+
import type {
|
|
33
|
+
BottomTabBarProps,
|
|
34
|
+
BottomTabDescriptor,
|
|
35
|
+
BottomTabDescriptorMap,
|
|
36
|
+
BottomTabNavigationHelpers,
|
|
37
|
+
} from '../types';
|
|
31
38
|
import { BottomTabBarHeightCallbackContext } from '../utils/BottomTabBarHeightCallbackContext';
|
|
32
39
|
import { useIsKeyboardShown } from '../utils/useIsKeyboardShown';
|
|
33
40
|
import { useTabBarPosition } from '../utils/useTabBarPosition';
|
|
@@ -437,47 +444,6 @@ export function BottomTabBar({ state, navigation, descriptors, style }: Props) {
|
|
|
437
444
|
|
|
438
445
|
const { options } = descriptor;
|
|
439
446
|
|
|
440
|
-
const onPress = () => {
|
|
441
|
-
const isRepeatedPress =
|
|
442
|
-
focused && options.tabBarSelectionEnabled !== false;
|
|
443
|
-
|
|
444
|
-
const event = navigation.emit({
|
|
445
|
-
type: 'tabPress',
|
|
446
|
-
target: route.key,
|
|
447
|
-
canPreventDefault: true,
|
|
448
|
-
data: {
|
|
449
|
-
behavior: {
|
|
450
|
-
scrollToTop:
|
|
451
|
-
isRepeatedPress &&
|
|
452
|
-
options.tabBarRepeatedPressBehavior?.scrollToTop !== false,
|
|
453
|
-
popToTop:
|
|
454
|
-
isRepeatedPress &&
|
|
455
|
-
options.tabBarRepeatedPressBehavior?.popToTop !== false,
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
if (
|
|
461
|
-
!focused &&
|
|
462
|
-
!event.defaultPrevented &&
|
|
463
|
-
options.tabBarSelectionEnabled !== false
|
|
464
|
-
) {
|
|
465
|
-
React.startTransition(() => {
|
|
466
|
-
navigation.dispatch({
|
|
467
|
-
...CommonActions.navigate(route.name, route.params),
|
|
468
|
-
target: state.key,
|
|
469
|
-
});
|
|
470
|
-
});
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
|
|
474
|
-
const onLongPress = () => {
|
|
475
|
-
navigation.emit({
|
|
476
|
-
type: 'tabLongPress',
|
|
477
|
-
target: route.key,
|
|
478
|
-
});
|
|
479
|
-
};
|
|
480
|
-
|
|
481
447
|
const label =
|
|
482
448
|
typeof options.tabBarLabel === 'function'
|
|
483
449
|
? options.tabBarLabel
|
|
@@ -496,52 +462,27 @@ export function BottomTabBar({ state, navigation, descriptors, style }: Props) {
|
|
|
496
462
|
: undefined;
|
|
497
463
|
|
|
498
464
|
return (
|
|
499
|
-
<
|
|
465
|
+
<BottomTabBarItem
|
|
500
466
|
key={route.key}
|
|
501
|
-
navigation={descriptor.navigation}
|
|
502
467
|
route={route}
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
inactiveBackgroundColor={tabBarInactiveBackgroundColor}
|
|
522
|
-
rippleColor={options.tabBarRippleColor}
|
|
523
|
-
button={options.tabBarButton}
|
|
524
|
-
icon={options.tabBarIcon}
|
|
525
|
-
badge={options.tabBarBadge}
|
|
526
|
-
badgeStyle={options.tabBarBadgeStyle}
|
|
527
|
-
label={label}
|
|
528
|
-
labelVisibilityMode={tabBarLabelVisibilityMode}
|
|
529
|
-
labelStyle={options.tabBarLabelStyle}
|
|
530
|
-
iconStyle={options.tabBarIconStyle}
|
|
531
|
-
style={[
|
|
532
|
-
sidebar
|
|
533
|
-
? {
|
|
534
|
-
marginVertical: hasHorizontalLabels
|
|
535
|
-
? tabBarVariant === 'material'
|
|
536
|
-
? 0
|
|
537
|
-
: 1
|
|
538
|
-
: spacing / 2,
|
|
539
|
-
}
|
|
540
|
-
: styles.bottomItem,
|
|
541
|
-
options.tabBarItemStyle,
|
|
542
|
-
]}
|
|
543
|
-
/>
|
|
544
|
-
</NavigationProvider>
|
|
468
|
+
descriptor={descriptor}
|
|
469
|
+
navigation={navigation}
|
|
470
|
+
navigatorKey={state.key}
|
|
471
|
+
href={buildHref(route.name, route.params)}
|
|
472
|
+
focused={focused}
|
|
473
|
+
label={label}
|
|
474
|
+
accessibilityLabel={accessibilityLabel}
|
|
475
|
+
hasHorizontalLabels={hasHorizontalLabels}
|
|
476
|
+
compact={compact}
|
|
477
|
+
sidebar={sidebar}
|
|
478
|
+
tabBarVariant={tabBarVariant}
|
|
479
|
+
spacing={spacing}
|
|
480
|
+
tabBarActiveTintColor={tabBarActiveTintColor}
|
|
481
|
+
tabBarInactiveTintColor={tabBarInactiveTintColor}
|
|
482
|
+
tabBarActiveBackgroundColor={tabBarActiveBackgroundColor}
|
|
483
|
+
tabBarInactiveBackgroundColor={tabBarInactiveBackgroundColor}
|
|
484
|
+
tabBarLabelVisibilityMode={tabBarLabelVisibilityMode}
|
|
485
|
+
/>
|
|
545
486
|
);
|
|
546
487
|
})}
|
|
547
488
|
</View>
|
|
@@ -549,6 +490,136 @@ export function BottomTabBar({ state, navigation, descriptors, style }: Props) {
|
|
|
549
490
|
);
|
|
550
491
|
}
|
|
551
492
|
|
|
493
|
+
type BottomTabBarItemProps = {
|
|
494
|
+
route: TabNavigationState<ParamListBase>['routes'][number];
|
|
495
|
+
descriptor: BottomTabDescriptor;
|
|
496
|
+
navigation: BottomTabNavigationHelpers;
|
|
497
|
+
navigatorKey: string;
|
|
498
|
+
href: string | undefined;
|
|
499
|
+
focused: boolean;
|
|
500
|
+
label: React.ComponentProps<typeof BottomTabItem>['label'];
|
|
501
|
+
accessibilityLabel: string | undefined;
|
|
502
|
+
hasHorizontalLabels: boolean;
|
|
503
|
+
compact: boolean;
|
|
504
|
+
sidebar: boolean;
|
|
505
|
+
tabBarVariant: 'uikit' | 'material';
|
|
506
|
+
spacing: number;
|
|
507
|
+
tabBarActiveTintColor: ColorValue | undefined;
|
|
508
|
+
tabBarInactiveTintColor: ColorValue | undefined;
|
|
509
|
+
tabBarActiveBackgroundColor: ColorValue | undefined;
|
|
510
|
+
tabBarInactiveBackgroundColor: ColorValue | undefined;
|
|
511
|
+
tabBarLabelVisibilityMode: TabBarItemLabelVisibilityMode | undefined;
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const BottomTabBarItem = React.memo(function BottomTabBarItem({
|
|
515
|
+
route,
|
|
516
|
+
descriptor,
|
|
517
|
+
navigation,
|
|
518
|
+
navigatorKey,
|
|
519
|
+
href,
|
|
520
|
+
focused,
|
|
521
|
+
label,
|
|
522
|
+
accessibilityLabel,
|
|
523
|
+
hasHorizontalLabels,
|
|
524
|
+
compact,
|
|
525
|
+
sidebar,
|
|
526
|
+
tabBarVariant,
|
|
527
|
+
spacing,
|
|
528
|
+
tabBarActiveTintColor,
|
|
529
|
+
tabBarInactiveTintColor,
|
|
530
|
+
tabBarActiveBackgroundColor,
|
|
531
|
+
tabBarInactiveBackgroundColor,
|
|
532
|
+
tabBarLabelVisibilityMode,
|
|
533
|
+
}: BottomTabBarItemProps) {
|
|
534
|
+
const { options } = descriptor;
|
|
535
|
+
|
|
536
|
+
const onPress = () => {
|
|
537
|
+
const isRepeatedPress = focused && options.tabBarSelectionEnabled !== false;
|
|
538
|
+
|
|
539
|
+
const event = navigation.emit({
|
|
540
|
+
type: 'tabPress',
|
|
541
|
+
target: route.key,
|
|
542
|
+
canPreventDefault: true,
|
|
543
|
+
data: {
|
|
544
|
+
behavior: {
|
|
545
|
+
scrollToTop:
|
|
546
|
+
isRepeatedPress &&
|
|
547
|
+
options.tabBarRepeatedPressBehavior?.scrollToTop !== false,
|
|
548
|
+
popToTop:
|
|
549
|
+
isRepeatedPress &&
|
|
550
|
+
options.tabBarRepeatedPressBehavior?.popToTop !== false,
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
if (
|
|
556
|
+
!focused &&
|
|
557
|
+
!event.defaultPrevented &&
|
|
558
|
+
options.tabBarSelectionEnabled !== false
|
|
559
|
+
) {
|
|
560
|
+
React.startTransition(() => {
|
|
561
|
+
navigation.dispatch({
|
|
562
|
+
...CommonActions.navigate(route.name, route.params),
|
|
563
|
+
target: navigatorKey,
|
|
564
|
+
});
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
|
|
569
|
+
const onLongPress = () => {
|
|
570
|
+
navigation.emit({
|
|
571
|
+
type: 'tabLongPress',
|
|
572
|
+
target: route.key,
|
|
573
|
+
});
|
|
574
|
+
};
|
|
575
|
+
|
|
576
|
+
return (
|
|
577
|
+
<NavigationProvider navigation={descriptor.navigation} route={route}>
|
|
578
|
+
<BottomTabItem
|
|
579
|
+
href={href}
|
|
580
|
+
route={route}
|
|
581
|
+
descriptor={descriptor}
|
|
582
|
+
focused={focused}
|
|
583
|
+
horizontal={hasHorizontalLabels}
|
|
584
|
+
compact={compact}
|
|
585
|
+
sidebar={sidebar}
|
|
586
|
+
variant={tabBarVariant}
|
|
587
|
+
onPress={onPress}
|
|
588
|
+
onLongPress={onLongPress}
|
|
589
|
+
accessibilityLabel={accessibilityLabel}
|
|
590
|
+
testID={options.tabBarButtonTestID}
|
|
591
|
+
allowFontScaling={options.tabBarAllowFontScaling}
|
|
592
|
+
activeTintColor={tabBarActiveTintColor}
|
|
593
|
+
inactiveTintColor={tabBarInactiveTintColor}
|
|
594
|
+
activeBackgroundColor={tabBarActiveBackgroundColor}
|
|
595
|
+
inactiveBackgroundColor={tabBarInactiveBackgroundColor}
|
|
596
|
+
rippleColor={options.tabBarRippleColor}
|
|
597
|
+
button={options.tabBarButton}
|
|
598
|
+
icon={options.tabBarIcon}
|
|
599
|
+
badge={options.tabBarBadge}
|
|
600
|
+
badgeStyle={options.tabBarBadgeStyle}
|
|
601
|
+
label={label}
|
|
602
|
+
labelVisibilityMode={tabBarLabelVisibilityMode}
|
|
603
|
+
labelStyle={options.tabBarLabelStyle}
|
|
604
|
+
iconSize={options.tabBarIconSize}
|
|
605
|
+
iconStyle={options.tabBarIconStyle}
|
|
606
|
+
style={[
|
|
607
|
+
sidebar
|
|
608
|
+
? {
|
|
609
|
+
marginVertical: hasHorizontalLabels
|
|
610
|
+
? tabBarVariant === 'material'
|
|
611
|
+
? 0
|
|
612
|
+
: 1
|
|
613
|
+
: spacing / 2,
|
|
614
|
+
}
|
|
615
|
+
: styles.bottomItem,
|
|
616
|
+
options.tabBarItemStyle,
|
|
617
|
+
]}
|
|
618
|
+
/>
|
|
619
|
+
</NavigationProvider>
|
|
620
|
+
);
|
|
621
|
+
});
|
|
622
|
+
|
|
552
623
|
const styles = StyleSheet.create({
|
|
553
624
|
start: {
|
|
554
625
|
top: 0,
|
|
@@ -135,6 +135,10 @@ type Props = {
|
|
|
135
135
|
* Style object for the label element.
|
|
136
136
|
*/
|
|
137
137
|
labelStyle?: StyleProp<TextStyle> | undefined;
|
|
138
|
+
/**
|
|
139
|
+
* Size of the icon.
|
|
140
|
+
*/
|
|
141
|
+
iconSize?: number | undefined;
|
|
138
142
|
/**
|
|
139
143
|
* Style object for the icon element.
|
|
140
144
|
*/
|
|
@@ -181,6 +185,7 @@ export function BottomTabItem({
|
|
|
181
185
|
// https://developer.apple.com/documentation/uikit/uiview/3183939-largecontenttitle
|
|
182
186
|
allowFontScaling = SUPPORTS_LARGE_CONTENT_VIEWER ? false : undefined,
|
|
183
187
|
labelStyle,
|
|
188
|
+
iconSize,
|
|
184
189
|
iconStyle,
|
|
185
190
|
style,
|
|
186
191
|
}: Props) {
|
|
@@ -290,6 +295,7 @@ export function BottomTabItem({
|
|
|
290
295
|
route={route}
|
|
291
296
|
variant={variant}
|
|
292
297
|
size={compact ? 'compact' : 'regular'}
|
|
298
|
+
iconSize={iconSize}
|
|
293
299
|
badge={badge}
|
|
294
300
|
badgeStyle={badgeStyle}
|
|
295
301
|
activeOpacity={activeOpacity}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
SafeAreaProviderCompat,
|
|
5
5
|
} from '@react-navigation/elements/internal';
|
|
6
6
|
import {
|
|
7
|
+
type NavigationRoute,
|
|
7
8
|
type ParamListBase,
|
|
8
9
|
StackActions,
|
|
9
10
|
type TabNavigationState,
|
|
@@ -23,6 +24,7 @@ import {
|
|
|
23
24
|
} from '../TransitionConfigs/TransitionPresets';
|
|
24
25
|
import type {
|
|
25
26
|
BottomTabBarProps,
|
|
27
|
+
BottomTabDescriptor,
|
|
26
28
|
BottomTabDescriptorMap,
|
|
27
29
|
BottomTabNavigationConfig,
|
|
28
30
|
BottomTabNavigationHelpers,
|
|
@@ -275,21 +277,12 @@ export function BottomTabViewCustom({
|
|
|
275
277
|
);
|
|
276
278
|
}
|
|
277
279
|
|
|
278
|
-
const {
|
|
279
|
-
|
|
280
|
-
const {
|
|
281
|
-
lazy = true,
|
|
282
|
-
inactiveBehavior = 'pause',
|
|
283
|
-
animation = 'none',
|
|
284
|
-
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
285
|
-
.sceneStyleInterpolator,
|
|
286
|
-
sceneStyle: customSceneStyle,
|
|
287
|
-
} = options;
|
|
280
|
+
const { lazy = true, inactiveBehavior = 'pause' } =
|
|
281
|
+
descriptor.options;
|
|
288
282
|
|
|
289
283
|
const isFocused = state.index === index;
|
|
290
284
|
const isPreloaded = state.preloadedRouteKeys.includes(route.key);
|
|
291
285
|
|
|
292
|
-
const animationEnabled = hasAnimation(descriptor.options);
|
|
293
286
|
const tabAnimation = tabAnims[route.key];
|
|
294
287
|
|
|
295
288
|
if (tabAnimation == null) {
|
|
@@ -298,29 +291,6 @@ export function BottomTabViewCustom({
|
|
|
298
291
|
);
|
|
299
292
|
}
|
|
300
293
|
|
|
301
|
-
const content = (
|
|
302
|
-
<AnimatedScreenContent
|
|
303
|
-
key={route.key}
|
|
304
|
-
progress={tabAnimation}
|
|
305
|
-
animationEnabled={animationEnabled}
|
|
306
|
-
sceneStyleInterpolator={sceneStyleInterpolator}
|
|
307
|
-
style={[StyleSheet.absoluteFill, customSceneStyle]}
|
|
308
|
-
>
|
|
309
|
-
<ScreenContent
|
|
310
|
-
isFocused={isFocused}
|
|
311
|
-
route={route}
|
|
312
|
-
navigation={navigation}
|
|
313
|
-
options={options}
|
|
314
|
-
>
|
|
315
|
-
<BottomTabBarHeightContext.Provider
|
|
316
|
-
value={tabBarPosition === 'bottom' ? tabBarHeight : 0}
|
|
317
|
-
>
|
|
318
|
-
{render()}
|
|
319
|
-
</BottomTabBarHeightContext.Provider>
|
|
320
|
-
</ScreenContent>
|
|
321
|
-
</AnimatedScreenContent>
|
|
322
|
-
);
|
|
323
|
-
|
|
324
294
|
const isAnimatingRoute =
|
|
325
295
|
lastUpdate.animating &&
|
|
326
296
|
(lastUpdate.previous === route.key ||
|
|
@@ -335,22 +305,18 @@ export function BottomTabViewCustom({
|
|
|
335
305
|
(lazy === false && !loaded.includes(route.key));
|
|
336
306
|
|
|
337
307
|
return (
|
|
338
|
-
<
|
|
308
|
+
<TabScene
|
|
339
309
|
key={route.key}
|
|
310
|
+
route={route}
|
|
311
|
+
descriptor={descriptor}
|
|
340
312
|
lazy={lazy}
|
|
341
313
|
visible={isFocused || isPreloaded || loaded.includes(route.key)}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
zIndex: isFocused ? 0 : -1,
|
|
349
|
-
}}
|
|
350
|
-
>
|
|
351
|
-
{content}
|
|
352
|
-
</ActivityView>
|
|
353
|
-
</Deferred>
|
|
314
|
+
isFocused={isFocused}
|
|
315
|
+
isActive={isActive}
|
|
316
|
+
isAnimatingRoute={isAnimatingRoute}
|
|
317
|
+
tabAnimation={tabAnimation}
|
|
318
|
+
tabBarHeight={tabBarPosition === 'bottom' ? tabBarHeight : 0}
|
|
319
|
+
/>
|
|
354
320
|
);
|
|
355
321
|
})}
|
|
356
322
|
</Container>
|
|
@@ -361,6 +327,77 @@ export function BottomTabViewCustom({
|
|
|
361
327
|
);
|
|
362
328
|
}
|
|
363
329
|
|
|
330
|
+
type TabSceneProps = {
|
|
331
|
+
route: NavigationRoute<ParamListBase, string>;
|
|
332
|
+
descriptor: BottomTabDescriptor;
|
|
333
|
+
lazy: boolean;
|
|
334
|
+
visible: boolean;
|
|
335
|
+
isFocused: boolean;
|
|
336
|
+
isActive: boolean;
|
|
337
|
+
isAnimatingRoute: boolean;
|
|
338
|
+
tabAnimation: Animated.Value;
|
|
339
|
+
tabBarHeight: number;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const TabScene = React.memo(function TabScene({
|
|
343
|
+
route,
|
|
344
|
+
descriptor,
|
|
345
|
+
lazy,
|
|
346
|
+
visible,
|
|
347
|
+
isFocused,
|
|
348
|
+
isActive,
|
|
349
|
+
isAnimatingRoute,
|
|
350
|
+
tabAnimation,
|
|
351
|
+
tabBarHeight,
|
|
352
|
+
}: TabSceneProps) {
|
|
353
|
+
const { navigation, options, render } = descriptor;
|
|
354
|
+
|
|
355
|
+
const {
|
|
356
|
+
animation = 'none',
|
|
357
|
+
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
358
|
+
.sceneStyleInterpolator,
|
|
359
|
+
sceneStyle: customSceneStyle,
|
|
360
|
+
} = options;
|
|
361
|
+
|
|
362
|
+
const animationEnabled = hasAnimation(descriptor.options);
|
|
363
|
+
|
|
364
|
+
const content = (
|
|
365
|
+
<AnimatedScreenContent
|
|
366
|
+
key={route.key}
|
|
367
|
+
progress={tabAnimation}
|
|
368
|
+
animationEnabled={animationEnabled}
|
|
369
|
+
sceneStyleInterpolator={sceneStyleInterpolator}
|
|
370
|
+
style={[StyleSheet.absoluteFill, customSceneStyle]}
|
|
371
|
+
>
|
|
372
|
+
<ScreenContent
|
|
373
|
+
isFocused={isFocused}
|
|
374
|
+
route={route}
|
|
375
|
+
navigation={navigation}
|
|
376
|
+
options={options}
|
|
377
|
+
>
|
|
378
|
+
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
|
|
379
|
+
{render()}
|
|
380
|
+
</BottomTabBarHeightContext.Provider>
|
|
381
|
+
</ScreenContent>
|
|
382
|
+
</AnimatedScreenContent>
|
|
383
|
+
);
|
|
384
|
+
|
|
385
|
+
return (
|
|
386
|
+
<Deferred lazy={lazy} visible={visible}>
|
|
387
|
+
<ActivityView
|
|
388
|
+
mode={isFocused ? 'normal' : isActive ? 'inert' : 'paused'}
|
|
389
|
+
visible={isFocused || isAnimatingRoute}
|
|
390
|
+
style={{
|
|
391
|
+
...StyleSheet.absoluteFill,
|
|
392
|
+
zIndex: isFocused ? 0 : -1,
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
{content}
|
|
396
|
+
</ActivityView>
|
|
397
|
+
</Deferred>
|
|
398
|
+
);
|
|
399
|
+
});
|
|
400
|
+
|
|
364
401
|
function AnimatedScreenContent({
|
|
365
402
|
progress,
|
|
366
403
|
animationEnabled,
|