@react-navigation/bottom-tabs 7.20.0 → 7.22.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.
Files changed (49) hide show
  1. package/lib/module/unstable/Deferred.js +3 -10
  2. package/lib/module/unstable/Deferred.js.map +1 -1
  3. package/lib/module/unstable/NativeBottomTabView.native.js +288 -196
  4. package/lib/module/unstable/NativeBottomTabView.native.js.map +1 -1
  5. package/lib/module/unstable/NativeScreen/NativeScreen.js +1 -1
  6. package/lib/module/unstable/NativeScreen/useAnimatedHeaderHeight.js.map +1 -1
  7. package/lib/module/unstable/NativeScreen/useHeaderConfig.js +11 -11
  8. package/lib/module/unstable/NativeScreen/useHeaderConfig.js.map +1 -1
  9. package/lib/module/views/BottomTabBar.js +130 -67
  10. package/lib/module/views/BottomTabBar.js.map +1 -1
  11. package/lib/module/views/BottomTabItem.js +4 -4
  12. package/lib/module/views/BottomTabItem.js.map +1 -1
  13. package/lib/module/views/BottomTabView.js +78 -57
  14. package/lib/module/views/BottomTabView.js.map +1 -1
  15. package/lib/module/views/TabBarIcon.js +6 -2
  16. package/lib/module/views/TabBarIcon.js.map +1 -1
  17. package/lib/typescript/src/types.d.ts +17 -4
  18. package/lib/typescript/src/types.d.ts.map +1 -1
  19. package/lib/typescript/src/unstable/Deferred.d.ts.map +1 -1
  20. package/lib/typescript/src/unstable/NativeBottomTabView.d.ts +1 -1
  21. package/lib/typescript/src/unstable/NativeBottomTabView.d.ts.map +1 -1
  22. package/lib/typescript/src/unstable/NativeBottomTabView.native.d.ts.map +1 -1
  23. package/lib/typescript/src/unstable/NativeScreen/types.d.ts +1 -6
  24. package/lib/typescript/src/unstable/NativeScreen/types.d.ts.map +1 -1
  25. package/lib/typescript/src/unstable/NativeScreen/useAnimatedHeaderHeight.d.ts.map +1 -1
  26. package/lib/typescript/src/unstable/NativeScreen/useHeaderConfig.d.ts.map +1 -1
  27. package/lib/typescript/src/unstable/types.d.ts +125 -5
  28. package/lib/typescript/src/unstable/types.d.ts.map +1 -1
  29. package/lib/typescript/src/views/BottomTabBar.d.ts +1 -1
  30. package/lib/typescript/src/views/BottomTabBar.d.ts.map +1 -1
  31. package/lib/typescript/src/views/BottomTabItem.d.ts +5 -1
  32. package/lib/typescript/src/views/BottomTabItem.d.ts.map +1 -1
  33. package/lib/typescript/src/views/BottomTabView.d.ts.map +1 -1
  34. package/lib/typescript/src/views/TabBarIcon.d.ts +2 -1
  35. package/lib/typescript/src/views/TabBarIcon.d.ts.map +1 -1
  36. package/package.json +14 -14
  37. package/src/types.tsx +33 -4
  38. package/src/unstable/Deferred.tsx +3 -11
  39. package/src/unstable/NativeBottomTabView.native.tsx +356 -218
  40. package/src/unstable/NativeBottomTabView.tsx +3 -3
  41. package/src/unstable/NativeScreen/NativeScreen.tsx +1 -1
  42. package/src/unstable/NativeScreen/types.ts +3 -6
  43. package/src/unstable/NativeScreen/useAnimatedHeaderHeight.tsx +5 -3
  44. package/src/unstable/NativeScreen/useHeaderConfig.tsx +8 -14
  45. package/src/unstable/types.tsx +134 -8
  46. package/src/views/BottomTabBar.tsx +176 -86
  47. package/src/views/BottomTabItem.tsx +11 -2
  48. package/src/views/BottomTabView.tsx +100 -66
  49. package/src/views/TabBarIcon.tsx +9 -2
@@ -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
- <MaybeScreen
308
+ <TabScene
341
309
  key={route.key}
342
- style={[StyleSheet.absoluteFill, { zIndex: isFocused ? 0 : -1 }]}
343
- active={activityState}
344
- enabled={detachInactiveScreens}
345
- freezeOnBlur={freezeOnBlur}
346
- shouldFreeze={activityState === STATE_INACTIVE && !isPreloaded}
347
- pointerEvents={isFocused ? 'box-none' : 'none'}
348
- >
349
- <BottomTabBarHeightContext.Provider
350
- value={tabBarPosition === 'bottom' ? tabBarHeight : 0}
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,
@@ -13,6 +13,7 @@ type Props = {
13
13
  route: Route<string>;
14
14
  variant: 'uikit' | 'material';
15
15
  size: 'compact' | 'regular';
16
+ iconSize?: number;
16
17
  badge?: string | number;
17
18
  badgeStyle?: StyleProp<TextStyle>;
18
19
  activeOpacity: number;
@@ -44,6 +45,7 @@ export function TabBarIcon({
44
45
  route: _,
45
46
  variant,
46
47
  size,
48
+ iconSize: iconSizeOverride,
47
49
  badge,
48
50
  badgeStyle,
49
51
  activeOpacity,
@@ -55,11 +57,12 @@ export function TabBarIcon({
55
57
  style,
56
58
  }: Props) {
57
59
  const iconSize =
58
- variant === 'material'
60
+ iconSizeOverride ??
61
+ (variant === 'material'
59
62
  ? ICON_SIZE_MATERIAL
60
63
  : size === 'compact'
61
64
  ? ICON_SIZE_ROUND_COMPACT
62
- : ICON_SIZE_ROUND;
65
+ : ICON_SIZE_ROUND);
63
66
 
64
67
  // We render the icon twice at the same position on top of each other:
65
68
  // active and inactive one, so we can fade between them.
@@ -71,6 +74,10 @@ export function TabBarIcon({
71
74
  : size === 'compact'
72
75
  ? styles.wrapperUikitCompact
73
76
  : styles.wrapperUikit,
77
+ iconSizeOverride != null && {
78
+ width: iconSizeOverride,
79
+ height: iconSizeOverride,
80
+ },
74
81
  style,
75
82
  ]}
76
83
  >