@react-navigation/bottom-tabs 8.0.0-alpha.56 → 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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { getLabel } from '@react-navigation/elements';
4
4
  import { ActivityView, Color, SafeAreaProviderCompat } from '@react-navigation/elements/internal';
5
- import { CommonActions, MaterialSymbol, StackActions, useTheme } from '@react-navigation/native';
5
+ import { CommonActions, MaterialSymbol, StackActions, useLocale, useTheme } from '@react-navigation/native';
6
6
  import * as React from 'react';
7
7
  import { Animated, Platform, PlatformColor, StyleSheet } from 'react-native';
8
8
  import { Tabs } from 'react-native-screens';
@@ -16,6 +16,7 @@ const ICON_SIZE = Platform.select({
16
16
  ios: 25,
17
17
  default: 24
18
18
  });
19
+ const SUPPORTS_PROMINENT_TAB = Platform.OS === 'ios' && parseInt(Platform.Version, 10) >= 27;
19
20
  function reducer(state, action) {
20
21
  switch (action.type) {
21
22
  case 'TRACK_TRANSITION':
@@ -221,59 +222,54 @@ export function BottomTabViewNative({
221
222
  } = currentOptions.tabBarLabelStyle || {};
222
223
  const backgroundColor = currentOptions.tabBarStyle?.backgroundColor ?? colors.background;
223
224
  const shouldHideTabBar = currentOptions.tabBarStyle?.display === 'none';
224
- let activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor;
225
- let activeTintColor = currentOptions.tabBarActiveTintColor;
226
- let inactiveTintColor = currentOptions.tabBarInactiveTintColor;
225
+ const {
226
+ activeIndicatorColor,
227
+ activeTintColor,
228
+ inactiveTintColor
229
+ } = React.useMemo(() => {
230
+ let activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor;
231
+ let activeTintColor = currentOptions.tabBarActiveTintColor;
232
+ let inactiveTintColor = currentOptions.tabBarInactiveTintColor;
227
233
 
228
- // Derive colors based on Material Design guidelines
229
- // https://m3.material.io/components/navigation-bar/specs
230
- if (Platform.OS === 'android') {
231
- switch (getAndroidColorName(backgroundColor)) {
232
- case 'system_surface_container_light':
233
- case 'system_surface_container_high_light':
234
- case 'system_surface_container_highest_light':
235
- case 'system_surface_container_low_light':
236
- case 'system_surface_container_lowest_light':
237
- inactiveTintColor = inactiveTintColor ?? PlatformColor('@android:color/system_on_surface_variant_light');
238
- activeTintColor = activeTintColor ?? PlatformColor('@android:color/system_on_secondary_container_light');
239
- activeIndicatorColor = activeIndicatorColor ?? PlatformColor('@android:color/system_secondary_container_light');
240
- break;
241
- case 'system_surface_container_dark':
242
- case 'system_surface_container_high_dark':
243
- case 'system_surface_container_highest_dark':
244
- case 'system_surface_container_low_dark':
245
- case 'system_surface_container_lowest_dark':
246
- inactiveTintColor = inactiveTintColor ?? PlatformColor('@android:color/system_on_surface_variant_dark');
247
- activeTintColor = activeTintColor ?? PlatformColor('@android:color/system_on_secondary_container_dark');
248
- activeIndicatorColor = activeIndicatorColor ?? PlatformColor('@android:color/system_secondary_container_dark');
249
- break;
234
+ // Derive colors based on Material Design guidelines
235
+ // https://m3.material.io/components/navigation-bar/specs
236
+ if (Platform.OS === 'android') {
237
+ switch (getAndroidColorName(backgroundColor)) {
238
+ case 'system_surface_container_light':
239
+ case 'system_surface_container_high_light':
240
+ case 'system_surface_container_highest_light':
241
+ case 'system_surface_container_low_light':
242
+ case 'system_surface_container_lowest_light':
243
+ inactiveTintColor = inactiveTintColor ?? PlatformColor('@android:color/system_on_surface_variant_light');
244
+ activeTintColor = activeTintColor ?? PlatformColor('@android:color/system_on_secondary_container_light');
245
+ activeIndicatorColor = activeIndicatorColor ?? PlatformColor('@android:color/system_secondary_container_light');
246
+ break;
247
+ case 'system_surface_container_dark':
248
+ case 'system_surface_container_high_dark':
249
+ case 'system_surface_container_highest_dark':
250
+ case 'system_surface_container_low_dark':
251
+ case 'system_surface_container_lowest_dark':
252
+ inactiveTintColor = inactiveTintColor ?? PlatformColor('@android:color/system_on_surface_variant_dark');
253
+ activeTintColor = activeTintColor ?? PlatformColor('@android:color/system_on_secondary_container_dark');
254
+ activeIndicatorColor = activeIndicatorColor ?? PlatformColor('@android:color/system_secondary_container_dark');
255
+ break;
256
+ }
250
257
  }
251
- }
252
- inactiveTintColor = inactiveTintColor ?? Platform.select({
253
- ios: PlatformColor('label'),
254
- default: colors.text
255
- });
256
- activeTintColor = activeTintColor ?? colors.primary;
257
- activeIndicatorColor = activeIndicatorColor ?? Platform.select({
258
- android: Color(activeTintColor)?.alpha(0.075).string(),
259
- default: undefined
260
- });
261
- const onTransitionStart = ({
262
- route
263
- }) => {
264
- navigation.emit({
265
- type: 'transitionStart',
266
- target: route.key
258
+ inactiveTintColor = inactiveTintColor ?? Platform.select({
259
+ ios: PlatformColor('label'),
260
+ default: colors.text
267
261
  });
268
- };
269
- const onTransitionEnd = ({
270
- route
271
- }) => {
272
- navigation.emit({
273
- type: 'transitionEnd',
274
- target: route.key
262
+ activeTintColor = activeTintColor ?? colors.primary;
263
+ activeIndicatorColor = activeIndicatorColor ?? Platform.select({
264
+ android: Color(activeTintColor)?.alpha(0.075).string(),
265
+ default: undefined
275
266
  });
276
- };
267
+ return {
268
+ activeIndicatorColor,
269
+ activeTintColor,
270
+ inactiveTintColor
271
+ };
272
+ }, [currentOptions?.tabBarActiveIndicatorColor, currentOptions.tabBarActiveTintColor, currentOptions.tabBarInactiveTintColor, backgroundColor, colors.primary, colors.text]);
277
273
  const tabBarPosition = useTabBarPosition(currentOptions);
278
274
  const hasCustomTabBar = tabBar != null;
279
275
  const tabBarElement = tabBar ? tabBar({
@@ -283,6 +279,8 @@ export function BottomTabViewNative({
283
279
  }) : null;
284
280
  const tabBarControllerMode = currentOptions.tabBarControllerMode === 'auto' ? 'automatic' : currentOptions.tabBarControllerMode;
285
281
  const tabBarMinimizeBehavior = currentOptions.tabBarMinimizeBehavior === 'auto' ? 'automatic' : currentOptions.tabBarMinimizeBehavior === 'none' ? 'never' : currentOptions.tabBarMinimizeBehavior;
282
+ const tabBarSidebarPreferredPlacement = currentOptions.tabBarSidebarPreferredPlacement === 'auto' ? 'automatic' : currentOptions.tabBarSidebarPreferredPlacement;
283
+ const prominentScreenKey = SUPPORTS_PROMINENT_TAB ? state.routes.find(route => descriptors[route.key]?.options.tabBarRole === 'prominent')?.key : undefined;
286
284
  const bottomAccessory = currentOptions.bottomAccessory;
287
285
  return /*#__PURE__*/_jsxs(SafeAreaProviderCompat, {
288
286
  style: {
@@ -303,10 +301,13 @@ export function BottomTabViewNative({
303
301
  backgroundColor: colors.background
304
302
  },
305
303
  ios: {
304
+ prominentScreenKey,
306
305
  bottomAccessory: bottomAccessory ? environment => bottomAccessory({
307
306
  placement: environment
308
307
  }) : undefined,
308
+ tabBarHiddenAnimationEnabled: currentOptions.tabBarVisibilityAnimationEnabled,
309
309
  tabBarControllerMode,
310
+ tabBarSidebarPreferredPlacement,
310
311
  tabBarMinimizeBehavior,
311
312
  tabBarTintColor: activeTintColor
312
313
  },
@@ -315,197 +316,286 @@ export function BottomTabViewNative({
315
316
  if (descriptor == null) {
316
317
  throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
317
318
  }
318
- const {
319
- options,
320
- render,
321
- navigation
322
- } = descriptor;
323
319
  const isFocused = state.index === index;
324
320
  const isPreloaded = state.preloadedRouteKeys.includes(route.key);
325
321
  const {
326
- title,
327
322
  lazy = false,
328
- inactiveBehavior = 'pause',
329
- tabBarLabel,
330
- tabBarSelectionEnabled,
331
- tabBarRepeatedPressBehavior,
332
- tabBarBadgeStyle,
333
- tabBarIcon,
334
- tabBarBadge,
335
- tabBarSystemItem,
336
- tabBarBlurEffect = dark ? 'systemMaterialDark' : 'systemMaterial',
337
- tabBarStyle,
338
- tabBarAccessibilityLabel,
339
- tabBarButtonTestID,
340
- sceneStyle,
341
- overrideScrollViewContentInsetAdjustmentBehavior
342
- } = options;
343
- const {
344
- backgroundColor: tabBarBackgroundColor,
345
- shadowColor: tabBarShadowColor
346
- } = tabBarStyle || {};
347
- const tabTitle =
348
- // On iOS, `systemItem` already provides a localized label
349
- // So we should only use `tabBarLabel` if explicitly provided
350
- Platform.OS === 'ios' && tabBarSystemItem != null ? tabBarLabel : getLabel({
351
- label: tabBarLabel,
352
- title
353
- }, route.name);
354
- const badgeBackgroundColor = tabBarBadgeStyle?.backgroundColor ?? colors.notification;
355
- const badgeTextColor = tabBarBadgeStyle?.color ?? Color.foreground(badgeBackgroundColor);
356
- const tabItemAppearance = {
357
- tabBarItemTitleFontFamily: fontFamily,
358
- tabBarItemTitleFontSize: fontSize,
359
- tabBarItemTitleFontWeight: fontWeight,
360
- tabBarItemTitleFontStyle: fontStyle,
361
- tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
362
- tabBarItemIconColor: inactiveTintColor,
363
- tabBarItemBadgeBackgroundColor: badgeBackgroundColor
364
- };
365
- const normalTabItemAppearance = {
366
- tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
367
- tabBarItemIconColor: inactiveTintColor
368
- };
369
- const selectedTabItemAppearance = {
370
- tabBarItemTitleFontColor: activeTintColor,
371
- tabBarItemIconColor: activeTintColor
372
- };
373
- const getIcon = selected => {
374
- if (typeof tabBarIcon === 'function') {
375
- const result = tabBarIcon({
376
- focused: selected,
377
- size: ICON_SIZE,
378
- color: selected ? activeTintColor : inactiveTintColor
379
- });
380
- if (/*#__PURE__*/React.isValidElement(result)) {
381
- throw new Error(`Returning a React element from 'tabBarIcon' is not supported with native tab bar.`);
382
- } else if (result && typeof result === 'object' && 'type' in result) {
383
- return getPlatformIcon(result);
384
- } else {
385
- throw new Error(`The 'tabBarIcon' function must return an icon object (got ${typeof result}).`);
386
- }
387
- } else if (tabBarIcon != null) {
388
- return getPlatformIcon(tabBarIcon);
389
- }
390
- return undefined;
391
- };
392
-
393
- // An error is thrown for React Elements in `getIcon`
394
- // So we only call it when we actually render a native tab bar
395
- const icon = hasCustomTabBar ? undefined : getIcon(false);
396
- const selectedIcon = !hasCustomTabBar && typeof tabBarIcon === 'function' ? getIcon(true) : icon;
323
+ inactiveBehavior = 'pause'
324
+ } = descriptor.options;
397
325
 
398
326
  // For preloaded screens and if lazy is false,
399
327
  // Keep them active so that the effects can run
400
328
  const isActive = inactiveBehavior === 'none' || isPreloaded || nativeState.lastTransition?.from === route.key || nativeState.lastTransition?.to === route.key || lazy === false && !loaded.includes(route.key);
401
- return /*#__PURE__*/_jsx(Tabs.Screen, {
402
- onWillAppear: () => onTransitionStart({
403
- route
404
- }),
405
- onDidAppear: () => onTransitionEnd({
406
- route
407
- }),
408
- screenKey: route.key,
409
- tabBarItemAccessibilityLabel: tabBarAccessibilityLabel,
410
- tabBarItemTestID: tabBarButtonTestID,
411
- preventNativeSelection: tabBarSelectionEnabled === false,
412
- badgeValue: tabBarBadge?.toString(),
413
- title: tabTitle,
414
- nativeContainerStyle: {
415
- backgroundColor: colors.background
416
- },
417
- specialEffects: {
418
- repeatedTabSelection: {
419
- popToRoot: tabBarRepeatedPressBehavior?.popToTop !== false,
420
- scrollToTop: tabBarRepeatedPressBehavior?.scrollToTop !== false
421
- }
422
- },
423
- android: {
424
- icon: icon?.android ?? icon?.shared,
425
- selectedIcon: selectedIcon?.android ?? selectedIcon?.shared,
426
- standardAppearance: {
427
- tabBarBackgroundColor: tabBarBackgroundColor ?? backgroundColor,
428
- tabBarItemRippleColor: currentOptions?.tabBarRippleColor,
429
- tabBarItemLabelVisibilityMode: currentOptions?.tabBarLabelVisibilityMode,
430
- normal: normalTabItemAppearance,
431
- selected: selectedTabItemAppearance,
432
- tabBarItemActiveIndicatorColor: activeIndicatorColor,
433
- tabBarItemActiveIndicatorEnabled: currentOptions?.tabBarActiveIndicatorEnabled,
434
- tabBarItemTitleFontFamily: fontFamily,
435
- tabBarItemTitleFontWeight: fontWeight,
436
- tabBarItemTitleSmallLabelFontSize: fontSize,
437
- tabBarItemTitleLargeLabelFontSize: fontSize,
438
- tabBarItemTitleFontStyle: fontStyle,
439
- tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
440
- tabBarItemBadgeTextColor: badgeTextColor
441
- }
442
- },
443
- ios: {
444
- icon: icon?.ios ?? icon?.shared,
445
- selectedIcon: selectedIcon?.ios ?? selectedIcon?.shared,
446
- systemItem: tabBarSystemItem,
447
- scrollEdgeAppearance: {
448
- tabBarBackgroundColor,
449
- tabBarShadowColor,
450
- tabBarBlurEffect,
451
- stacked: {
452
- normal: tabItemAppearance
453
- },
454
- inline: {
455
- normal: tabItemAppearance
456
- },
457
- compactInline: {
458
- normal: tabItemAppearance
459
- }
460
- },
461
- standardAppearance: {
462
- tabBarBackgroundColor,
463
- tabBarShadowColor,
464
- tabBarBlurEffect,
465
- stacked: {
466
- normal: tabItemAppearance
467
- },
468
- inline: {
469
- normal: tabItemAppearance
470
- },
471
- compactInline: {
472
- normal: tabItemAppearance
473
- }
474
- },
475
- overrideScrollViewContentInsetAdjustmentBehavior
476
- },
477
- children: /*#__PURE__*/_jsx(Deferred, {
478
- lazy: lazy,
479
- visible: isFocused || isPreloaded || loaded.includes(route.key),
480
- children: /*#__PURE__*/_jsx(ActivityView, {
481
- mode: isFocused ? 'normal' : isActive ? 'inert' : 'paused',
482
- visible:
483
- // We don't need to hide the content since it's handled natively
484
- // Hiding may also cause flash due to lag after native tab switch
485
- // So we leave it always visible
486
- true,
487
- style: StyleSheet.absoluteFill,
488
- children: /*#__PURE__*/_jsx(ScreenContent, {
489
- isFocused: isFocused,
490
- route: route,
491
- navigation: navigation,
492
- options: options,
493
- style: sceneStyle,
494
- children: /*#__PURE__*/_jsx(AnimatedScreenContent, {
495
- isFocused: isFocused,
496
- children: /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
497
- value: 0,
498
- children: render()
499
- })
500
- })
501
- })
502
- }, route.key)
503
- })
329
+ return /*#__PURE__*/_jsx(TabScene, {
330
+ route: route,
331
+ descriptor: descriptor,
332
+ navigation: navigation,
333
+ isFocused: isFocused,
334
+ isActive: isActive,
335
+ lazy: lazy,
336
+ visible: isFocused || isPreloaded || loaded.includes(route.key),
337
+ hasCustomTabBar: hasCustomTabBar,
338
+ fontFamily: fontFamily,
339
+ fontWeight: fontWeight,
340
+ fontSize: fontSize,
341
+ fontStyle: fontStyle,
342
+ fontColor: fontColor,
343
+ backgroundColor: backgroundColor,
344
+ activeTintColor: activeTintColor,
345
+ inactiveTintColor: inactiveTintColor,
346
+ activeIndicatorColor: activeIndicatorColor,
347
+ rippleColor: currentOptions.tabBarRippleColor,
348
+ labelVisibilityMode: currentOptions.tabBarLabelVisibilityMode,
349
+ activeIndicatorEnabled: currentOptions.tabBarActiveIndicatorEnabled
504
350
  }, route.key);
505
351
  })
506
352
  }), tabBarPosition === 'bottom' || tabBarPosition === 'right' ? tabBarElement : null]
507
353
  });
508
354
  }
355
+ const TabScene = /*#__PURE__*/React.memo(function TabScene({
356
+ route,
357
+ descriptor,
358
+ navigation,
359
+ isFocused,
360
+ isActive,
361
+ lazy,
362
+ visible,
363
+ hasCustomTabBar,
364
+ fontFamily,
365
+ fontWeight,
366
+ fontSize,
367
+ fontStyle,
368
+ fontColor,
369
+ backgroundColor,
370
+ activeTintColor,
371
+ inactiveTintColor,
372
+ activeIndicatorColor,
373
+ rippleColor,
374
+ labelVisibilityMode,
375
+ activeIndicatorEnabled
376
+ }) {
377
+ const {
378
+ dark,
379
+ colors
380
+ } = useTheme();
381
+ const {
382
+ direction
383
+ } = useLocale();
384
+ const onTransitionStart = ({
385
+ route
386
+ }) => {
387
+ navigation.emit({
388
+ type: 'transitionStart',
389
+ target: route.key
390
+ });
391
+ };
392
+ const onTransitionEnd = ({
393
+ route
394
+ }) => {
395
+ navigation.emit({
396
+ type: 'transitionEnd',
397
+ target: route.key
398
+ });
399
+ };
400
+ const {
401
+ options,
402
+ render
403
+ } = descriptor;
404
+ const {
405
+ title,
406
+ tabBarLabel,
407
+ tabBarLabelPositionAdjustment,
408
+ tabBarSelectionEnabled,
409
+ tabBarRepeatedPressBehavior,
410
+ tabBarBadgeStyle,
411
+ tabBarIcon,
412
+ tabBarIconSize,
413
+ tabBarActiveIndicatorWidth,
414
+ tabBarActiveIndicatorHeight,
415
+ tabBarBadge,
416
+ tabBarSystemItem,
417
+ tabBarRole,
418
+ tabBarSearchAutoFocus,
419
+ tabBarBlurEffect = dark ? 'systemMaterialDark' : 'systemMaterial',
420
+ tabBarStyle,
421
+ tabBarAccessibilityLabel,
422
+ tabBarButtonTestID,
423
+ sceneStyle,
424
+ overrideScrollViewContentInsetAdjustmentBehavior
425
+ } = options;
426
+ const systemItem = tabBarSystemItem ?? (
427
+ // Setting the `role` to 'search' doesn't add title and icon automatically
428
+ // So we also set `tabBarSystemItem` to 'search' to match SwiftUI's behavior
429
+ tabBarRole === 'search' ? 'search' : undefined);
430
+ const {
431
+ backgroundColor: tabBarBackgroundColor,
432
+ shadowColor: tabBarShadowColor
433
+ } = tabBarStyle || {};
434
+ const tabTitle =
435
+ // On iOS, `systemItem` already provides a localized label
436
+ // So we should only use `tabBarLabel` if explicitly provided
437
+ Platform.OS === 'ios' && systemItem != null ? tabBarLabel : getLabel({
438
+ label: tabBarLabel,
439
+ title
440
+ }, route.name);
441
+ const badgeBackgroundColor = tabBarBadgeStyle?.backgroundColor ?? colors.notification;
442
+ const badgeTextColor = tabBarBadgeStyle?.color ?? Color.foreground(badgeBackgroundColor);
443
+ const tabItemAppearance = {
444
+ tabBarItemTitleFontFamily: fontFamily,
445
+ tabBarItemTitleFontSize: fontSize,
446
+ tabBarItemTitleFontWeight: fontWeight,
447
+ tabBarItemTitleFontStyle: fontStyle,
448
+ tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
449
+ tabBarItemIconColor: inactiveTintColor,
450
+ tabBarItemBadgeBackgroundColor: badgeBackgroundColor
451
+ };
452
+ const tabItemLayoutAppearance = {
453
+ stacked: {
454
+ normal: {
455
+ ...tabItemAppearance,
456
+ tabBarItemTitlePositionAdjustment: tabBarLabelPositionAdjustment?.stacked
457
+ }
458
+ },
459
+ inline: {
460
+ normal: {
461
+ ...tabItemAppearance,
462
+ tabBarItemTitlePositionAdjustment: tabBarLabelPositionAdjustment?.inline
463
+ }
464
+ },
465
+ compactInline: {
466
+ normal: {
467
+ ...tabItemAppearance,
468
+ tabBarItemTitlePositionAdjustment: tabBarLabelPositionAdjustment?.compactInline
469
+ }
470
+ }
471
+ };
472
+ const normalTabItemAppearance = {
473
+ tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
474
+ tabBarItemIconColor: inactiveTintColor
475
+ };
476
+ const selectedTabItemAppearance = {
477
+ tabBarItemTitleFontColor: activeTintColor,
478
+ tabBarItemIconColor: activeTintColor
479
+ };
480
+ const iconSize = Platform.OS === 'android' ? tabBarIconSize ?? ICON_SIZE : ICON_SIZE;
481
+ const getIcon = selected => {
482
+ if (typeof tabBarIcon === 'function') {
483
+ const result = tabBarIcon({
484
+ focused: selected,
485
+ size: iconSize,
486
+ color: selected ? activeTintColor : inactiveTintColor
487
+ });
488
+ if (/*#__PURE__*/React.isValidElement(result)) {
489
+ throw new Error(`Returning a React element from 'tabBarIcon' is not supported with native tab bar.`);
490
+ } else if (result && typeof result === 'object' && 'type' in result) {
491
+ return getPlatformIcon(result, direction, iconSize);
492
+ } else {
493
+ throw new Error(`The 'tabBarIcon' function must return an icon object (got ${typeof result}).`);
494
+ }
495
+ } else if (tabBarIcon != null) {
496
+ return getPlatformIcon(tabBarIcon, direction, iconSize);
497
+ }
498
+ return undefined;
499
+ };
500
+
501
+ // An error is thrown for React Elements in `getIcon`
502
+ // So we only call it when we actually render a native tab bar
503
+ const icon = hasCustomTabBar ? undefined : getIcon(false);
504
+ const selectedIcon = !hasCustomTabBar && typeof tabBarIcon === 'function' ? getIcon(true) : icon;
505
+ return /*#__PURE__*/_jsx(Tabs.Screen, {
506
+ onWillAppear: () => onTransitionStart({
507
+ route
508
+ }),
509
+ onDidAppear: () => onTransitionEnd({
510
+ route
511
+ }),
512
+ screenKey: route.key,
513
+ tabBarItemAccessibilityLabel: tabBarAccessibilityLabel,
514
+ tabBarItemTestID: tabBarButtonTestID,
515
+ preventNativeSelection: tabBarSelectionEnabled === false,
516
+ badgeValue: tabBarBadge?.toString(),
517
+ title: tabTitle,
518
+ style: {
519
+ backgroundColor: colors.background
520
+ },
521
+ specialEffects: {
522
+ repeatedTabSelection: {
523
+ popToRoot: tabBarRepeatedPressBehavior?.popToTop !== false,
524
+ scrollToTop: tabBarRepeatedPressBehavior?.scrollToTop !== false
525
+ }
526
+ },
527
+ android: {
528
+ icon: icon?.android ?? icon?.shared,
529
+ selectedIcon: selectedIcon?.android ?? selectedIcon?.shared,
530
+ standardAppearance: {
531
+ tabBarBackgroundColor: tabBarBackgroundColor ?? backgroundColor,
532
+ tabBarItemRippleColor: rippleColor,
533
+ tabBarItemLabelVisibilityMode: labelVisibilityMode,
534
+ normal: normalTabItemAppearance,
535
+ selected: selectedTabItemAppearance,
536
+ tabBarItemIconSize: tabBarIconSize,
537
+ tabBarItemActiveIndicatorColor: activeIndicatorColor,
538
+ tabBarItemActiveIndicatorEnabled: activeIndicatorEnabled,
539
+ tabBarItemActiveIndicatorWidth: tabBarActiveIndicatorWidth,
540
+ tabBarItemActiveIndicatorHeight: tabBarActiveIndicatorHeight,
541
+ tabBarItemTitleFontFamily: fontFamily,
542
+ tabBarItemTitleFontWeight: fontWeight,
543
+ tabBarItemTitleSmallLabelFontSize: fontSize,
544
+ tabBarItemTitleLargeLabelFontSize: fontSize,
545
+ tabBarItemTitleFontStyle: fontStyle,
546
+ tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
547
+ tabBarItemBadgeTextColor: badgeTextColor
548
+ }
549
+ },
550
+ ios: {
551
+ icon: icon?.ios ?? icon?.shared,
552
+ selectedIcon: selectedIcon?.ios ?? selectedIcon?.shared,
553
+ systemItem,
554
+ role: tabBarRole === 'search' ? tabBarRole : undefined,
555
+ automaticallyActivatesSearch: tabBarSearchAutoFocus,
556
+ scrollEdgeAppearance: {
557
+ tabBarBackgroundColor,
558
+ tabBarShadowColor,
559
+ tabBarBlurEffect,
560
+ ...tabItemLayoutAppearance
561
+ },
562
+ standardAppearance: {
563
+ tabBarBackgroundColor,
564
+ tabBarShadowColor,
565
+ tabBarBlurEffect,
566
+ ...tabItemLayoutAppearance
567
+ },
568
+ overrideScrollViewContentInsetAdjustmentBehavior
569
+ },
570
+ children: /*#__PURE__*/_jsx(Deferred, {
571
+ lazy: lazy,
572
+ visible: visible,
573
+ children: /*#__PURE__*/_jsx(ActivityView, {
574
+ mode: isFocused ? 'normal' : isActive ? 'inert' : 'paused',
575
+ visible:
576
+ // We don't need to hide the content since it's handled natively
577
+ // Hiding may also cause flash due to lag after native tab switch
578
+ // So we leave it always visible
579
+ true,
580
+ style: StyleSheet.absoluteFill,
581
+ children: /*#__PURE__*/_jsx(ScreenContent, {
582
+ isFocused: isFocused,
583
+ route: route,
584
+ navigation: descriptor.navigation,
585
+ options: options,
586
+ style: sceneStyle,
587
+ children: /*#__PURE__*/_jsx(AnimatedScreenContent, {
588
+ isFocused: isFocused,
589
+ children: /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
590
+ value: 0,
591
+ children: render()
592
+ })
593
+ })
594
+ })
595
+ }, route.key)
596
+ })
597
+ });
598
+ });
509
599
  function AnimatedScreenContent({
510
600
  isFocused,
511
601
  children
@@ -531,11 +621,15 @@ function AnimatedScreenContent({
531
621
  children: children
532
622
  });
533
623
  }
534
- function getPlatformIcon(icon) {
624
+ function getPlatformIcon(icon, direction, size) {
535
625
  switch (icon.type) {
536
626
  case 'sfSymbol':
537
627
  return {
538
- ios: icon,
628
+ ios: {
629
+ type: 'sfSymbol',
630
+ name: icon.name,
631
+ renderingMode: icon.renderingMode === 'multicolor' ? 'original' : icon.renderingMode
632
+ },
539
633
  android: undefined,
540
634
  shared: undefined
541
635
  };
@@ -548,26 +642,35 @@ function getPlatformIcon(icon) {
548
642
  name: icon.name,
549
643
  variant: icon.variant,
550
644
  weight: icon.weight,
551
- size: ICON_SIZE
645
+ fill: icon.fill,
646
+ size,
647
+ direction
552
648
  })
553
649
  },
554
650
  shared: undefined
555
651
  };
556
652
  case 'image':
557
- return {
558
- ios: icon.tinted === false ? {
559
- type: 'imageSource',
560
- imageSource: icon.source
561
- } : {
562
- type: 'templateSource',
563
- templateSource: icon.source
564
- },
565
- android: undefined,
566
- shared: {
567
- type: 'imageSource',
568
- imageSource: icon.source
569
- }
570
- };
653
+ {
654
+ // A `{ uri: 'name' }` source maps to an Android drawable by name.
655
+ const drawableName = typeof icon.source === 'object' && 'uri' in icon.source && icon.source.uri?.includes(':') === false ? icon.source.uri : undefined;
656
+ return {
657
+ ios: {
658
+ type: 'imageSource',
659
+ imageSource: icon.source,
660
+ renderingMode: icon.tinted === false ? 'original' : 'template'
661
+ },
662
+ android: drawableName != null ? {
663
+ type: 'drawableResource',
664
+ name: drawableName,
665
+ tinting: icon.tinted === false ? 'original' : 'tinted'
666
+ } : {
667
+ type: 'imageSource',
668
+ imageSource: icon.source,
669
+ tinting: icon.tinted === false ? 'original' : 'tinted'
670
+ },
671
+ shared: undefined
672
+ };
673
+ }
571
674
  default:
572
675
  {
573
676
  const _exhaustiveCheck = icon;