@react-navigation/bottom-tabs 8.0.0-alpha.5 → 8.0.0-alpha.51

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 (84) hide show
  1. package/lib/module/index.js +1 -1
  2. package/lib/module/index.js.map +1 -1
  3. package/lib/module/navigators/createBottomTabNavigator.js +11 -34
  4. package/lib/module/navigators/createBottomTabNavigator.js.map +1 -1
  5. package/lib/module/utils/useBottomTabAnimation.js +1 -1
  6. package/lib/module/utils/useBottomTabAnimation.js.map +1 -1
  7. package/lib/module/utils/useBottomTabBarHeight.js +1 -1
  8. package/lib/module/utils/useBottomTabBarHeight.js.map +1 -1
  9. package/lib/module/utils/useIsKeyboardShown.js.map +1 -1
  10. package/lib/module/views/BottomTabBar.js +40 -29
  11. package/lib/module/views/BottomTabBar.js.map +1 -1
  12. package/lib/module/views/BottomTabItem.js +3 -5
  13. package/lib/module/views/BottomTabItem.js.map +1 -1
  14. package/lib/module/views/{BottomTabViewCommon.js → BottomTabView.js} +1 -1
  15. package/lib/module/views/BottomTabView.js.map +1 -0
  16. package/lib/module/views/BottomTabViewCustom.js +97 -81
  17. package/lib/module/views/BottomTabViewCustom.js.map +1 -1
  18. package/lib/module/views/BottomTabViewNativeImpl.js +339 -121
  19. package/lib/module/views/BottomTabViewNativeImpl.js.map +1 -1
  20. package/lib/module/views/Deferred.js +41 -0
  21. package/lib/module/views/Deferred.js.map +1 -0
  22. package/lib/module/views/ScreenContent.js.map +1 -1
  23. package/lib/module/views/TabBarIcon.js +11 -48
  24. package/lib/module/views/TabBarIcon.js.map +1 -1
  25. package/lib/typescript/src/TransitionConfigs/SceneStyleInterpolators.d.ts +1 -1
  26. package/lib/typescript/src/TransitionConfigs/SceneStyleInterpolators.d.ts.map +1 -1
  27. package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts +1 -1
  28. package/lib/typescript/src/TransitionConfigs/TransitionPresets.d.ts.map +1 -1
  29. package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts +1 -1
  30. package/lib/typescript/src/TransitionConfigs/TransitionSpecs.d.ts.map +1 -1
  31. package/lib/typescript/src/index.d.ts +11 -11
  32. package/lib/typescript/src/index.d.ts.map +1 -1
  33. package/lib/typescript/src/navigators/createBottomTabNavigator.d.ts +9 -15
  34. package/lib/typescript/src/navigators/createBottomTabNavigator.d.ts.map +1 -1
  35. package/lib/typescript/src/types.d.ts +66 -104
  36. package/lib/typescript/src/types.d.ts.map +1 -1
  37. package/lib/typescript/src/utils/BottomTabAnimationContext.d.ts +1 -1
  38. package/lib/typescript/src/utils/BottomTabAnimationContext.d.ts.map +1 -1
  39. package/lib/typescript/src/utils/useAnimatedHashMap.d.ts +1 -1
  40. package/lib/typescript/src/utils/useAnimatedHashMap.d.ts.map +1 -1
  41. package/lib/typescript/src/utils/useBottomTabAnimation.d.ts +1 -1
  42. package/lib/typescript/src/utils/useBottomTabAnimation.d.ts.map +1 -1
  43. package/lib/typescript/src/utils/useTabBarPosition.d.ts +1 -1
  44. package/lib/typescript/src/utils/useTabBarPosition.d.ts.map +1 -1
  45. package/lib/typescript/src/views/BottomTabBar.d.ts +3 -2
  46. package/lib/typescript/src/views/BottomTabBar.d.ts.map +1 -1
  47. package/lib/typescript/src/views/BottomTabItem.d.ts +20 -20
  48. package/lib/typescript/src/views/BottomTabItem.d.ts.map +1 -1
  49. package/lib/typescript/src/views/{BottomTabViewCommon.d.ts → BottomTabView.d.ts} +3 -3
  50. package/lib/typescript/src/views/BottomTabView.d.ts.map +1 -0
  51. package/lib/typescript/src/views/BottomTabViewCustom.d.ts +3 -2
  52. package/lib/typescript/src/views/BottomTabViewCustom.d.ts.map +1 -1
  53. package/lib/typescript/src/views/BottomTabViewNative.android.d.ts +1 -1
  54. package/lib/typescript/src/views/BottomTabViewNative.android.d.ts.map +1 -1
  55. package/lib/typescript/src/views/BottomTabViewNative.d.ts +1 -1
  56. package/lib/typescript/src/views/BottomTabViewNative.d.ts.map +1 -1
  57. package/lib/typescript/src/views/BottomTabViewNative.ios.d.ts +1 -1
  58. package/lib/typescript/src/views/BottomTabViewNative.ios.d.ts.map +1 -1
  59. package/lib/typescript/src/views/BottomTabViewNativeImpl.d.ts +3 -2
  60. package/lib/typescript/src/views/BottomTabViewNativeImpl.d.ts.map +1 -1
  61. package/lib/typescript/src/views/Deferred.d.ts +31 -0
  62. package/lib/typescript/src/views/Deferred.d.ts.map +1 -0
  63. package/lib/typescript/src/views/ScreenContent.d.ts +4 -4
  64. package/lib/typescript/src/views/ScreenContent.d.ts.map +1 -1
  65. package/lib/typescript/src/views/TabBarIcon.d.ts +8 -8
  66. package/lib/typescript/src/views/TabBarIcon.d.ts.map +1 -1
  67. package/package.json +22 -22
  68. package/src/index.tsx +2 -2
  69. package/src/navigators/createBottomTabNavigator.tsx +34 -102
  70. package/src/types.tsx +66 -112
  71. package/src/utils/useAnimatedHashMap.tsx +1 -1
  72. package/src/utils/useBottomTabAnimation.tsx +1 -1
  73. package/src/utils/useBottomTabBarHeight.tsx +1 -1
  74. package/src/utils/useIsKeyboardShown.tsx +2 -2
  75. package/src/views/BottomTabBar.tsx +68 -25
  76. package/src/views/BottomTabItem.tsx +19 -21
  77. package/src/views/BottomTabViewCustom.tsx +172 -143
  78. package/src/views/BottomTabViewNativeImpl.tsx +480 -161
  79. package/src/views/Deferred.tsx +57 -0
  80. package/src/views/ScreenContent.tsx +1 -2
  81. package/src/views/TabBarIcon.tsx +13 -56
  82. package/lib/module/views/BottomTabViewCommon.js.map +0 -1
  83. package/lib/typescript/src/views/BottomTabViewCommon.d.ts.map +0 -1
  84. /package/src/views/{BottomTabViewCommon.tsx → BottomTabView.tsx} +0 -0
@@ -1,14 +1,15 @@
1
1
  "use strict";
2
2
 
3
3
  import { getLabel } from '@react-navigation/elements';
4
- import { Color, Lazy, SafeAreaProviderCompat } from '@react-navigation/elements/internal';
4
+ import { ActivityView, Color, SafeAreaProviderCompat } from '@react-navigation/elements/internal';
5
5
  import { CommonActions, MaterialSymbol, NavigationMetaContext, StackActions, useTheme } from '@react-navigation/native';
6
6
  import * as React from 'react';
7
- import { Animated, Platform, PlatformColor } from 'react-native';
7
+ import { Animated, Platform, PlatformColor, StyleSheet } from 'react-native';
8
8
  import { Tabs } from 'react-native-screens';
9
9
  import { BottomTabAnimationContext } from "../utils/BottomTabAnimationContext.js";
10
10
  import { BottomTabBarHeightContext } from "../utils/BottomTabBarHeightContext.js";
11
11
  import { useTabBarPosition } from "../utils/useTabBarPosition.js";
12
+ import { Deferred } from "./Deferred.js";
12
13
  import { ScreenContent } from "./ScreenContent.js";
13
14
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
15
  const ICON_SIZE = Platform.select({
@@ -18,6 +19,26 @@ const ICON_SIZE = Platform.select({
18
19
  const meta = {
19
20
  type: 'native-tabs'
20
21
  };
22
+ function reducer(state, action) {
23
+ switch (action.type) {
24
+ case 'TRACK_TRANSITION':
25
+ return {
26
+ ...state,
27
+ lastTransition: action.lastTransition,
28
+ confirmed: action.confirmed
29
+ };
30
+ case 'CLEAR_TRANSITION':
31
+ return state.lastTransition?.to === action.to ? {
32
+ ...state,
33
+ lastTransition: null
34
+ } : state;
35
+ case 'CONFIRM_STATE':
36
+ return state.confirmed.routeKey === action.confirmed.routeKey && state.confirmed.provenance === action.confirmed.provenance ? state : {
37
+ ...state,
38
+ confirmed: action.confirmed
39
+ };
40
+ }
41
+ }
21
42
  export function BottomTabViewNative({
22
43
  state,
23
44
  navigation,
@@ -29,23 +50,147 @@ export function BottomTabViewNative({
29
50
  colors,
30
51
  fonts
31
52
  } = useTheme();
32
- const focusedRouteKey = state.routes[state.index].key;
53
+ const focusedRoute = state.routes[state.index];
54
+ if (focusedRoute == null) {
55
+ throw new Error(`Couldn't find a route at index ${state.index}.`);
56
+ }
57
+ const focusedRouteKey = focusedRoute.key;
58
+ const [loaded, setLoaded] = React.useState([focusedRouteKey]);
59
+ if (!loaded.includes(focusedRouteKey)) {
60
+ setLoaded([...loaded, focusedRouteKey]);
61
+ }
62
+ const [nativeState, dispatch] = React.useReducer(reducer, {
63
+ lastTransition: null,
64
+ confirmed: {
65
+ routeKey: focusedRouteKey,
66
+ provenance: 0
67
+ }
68
+ });
33
69
  const previousRouteKeyRef = React.useRef(focusedRouteKey);
34
70
  React.useEffect(() => {
35
71
  const previousRouteKey = previousRouteKeyRef.current;
36
72
  if (previousRouteKey !== focusedRouteKey && descriptors[previousRouteKey]?.options.popToTopOnBlur) {
37
- const prevRoute = state.routes.find(route => route.key === previousRouteKey);
38
- if (prevRoute?.state?.type === 'stack' && prevRoute.state.key) {
39
- const popToTopAction = {
73
+ const currentState = navigation.getState();
74
+ const prevRoute = currentState.routes.find(route => route.key === previousRouteKey);
75
+ if (prevRoute?.state?.type === 'stack' && prevRoute.state.key && ((prevRoute.state.index ?? prevRoute.state.routes.length - 1) > 0 || prevRoute.state.routes[0]?.history?.length)) {
76
+ navigation.dispatch({
40
77
  ...StackActions.popToTop(),
41
78
  target: prevRoute.state.key
42
- };
43
- navigation.dispatch(popToTopAction);
79
+ });
44
80
  }
45
81
  }
46
82
  previousRouteKeyRef.current = focusedRouteKey;
47
- }, [descriptors, focusedRouteKey, navigation, state.index, state.routes]);
48
- const currentOptions = descriptors[state.routes[state.index].key]?.options;
83
+
84
+ // We dispatch `popToTop` for unfocused tabs when `popToTopOnBlur` is true
85
+ // So we delay clearing `lastTransition` to keep the screen active for longer
86
+ // This gives time for the action to be handled before the screen is paused,
87
+ const timer = setTimeout(() => {
88
+ dispatch({
89
+ type: 'CLEAR_TRANSITION',
90
+ to: focusedRouteKey
91
+ });
92
+ }, 32);
93
+ return () => clearTimeout(timer);
94
+ }, [descriptors, focusedRouteKey, navigation]);
95
+ const navigate = (route, confirmed) => {
96
+ dispatch({
97
+ type: 'TRACK_TRANSITION',
98
+ confirmed,
99
+ lastTransition: {
100
+ from: previousRouteKeyRef.current,
101
+ to: route.key
102
+ }
103
+ });
104
+ navigation.dispatch({
105
+ ...CommonActions.navigate(route.name, route.params),
106
+ target: state.key
107
+ });
108
+ };
109
+
110
+ // Native tabs are the source of truth for the selected tab.
111
+ // JS sends a requested tab with the native provenance it was based on.
112
+ // Native replies with the selected tab and its new provenance.
113
+ const onTabSelected = event => {
114
+ const {
115
+ selectedScreenKey,
116
+ provenance,
117
+ actionOrigin
118
+ } = event.nativeEvent;
119
+ const confirmed = {
120
+ routeKey: selectedScreenKey,
121
+ provenance
122
+ };
123
+ const route = state.routes.find(route => route.key === selectedScreenKey);
124
+ if (!route) {
125
+ console.error(`Received 'tabSelected' for route that doesn't exist: ${selectedScreenKey}`);
126
+ return;
127
+ }
128
+ if (actionOrigin === 'user') {
129
+ const event = navigation.emit({
130
+ type: 'tabPress',
131
+ target: route.key,
132
+ canPreventDefault: true
133
+ });
134
+ if (event.defaultPrevented) {
135
+ throw new Error("Preventing default for 'tabPress' is not supported with native tab bar. Use the 'tabBarSelectionEnabled: false' option instead.");
136
+ }
137
+ }
138
+ if (actionOrigin === 'programmatic-js' || focusedRouteKey === route.key) {
139
+ dispatch({
140
+ type: 'CONFIRM_STATE',
141
+ confirmed
142
+ });
143
+ return;
144
+ }
145
+ navigate(route, confirmed);
146
+ };
147
+
148
+ // If native rejects a JS request, keep native as the source of truth
149
+ // and move JS back to the tab that native says is selected.
150
+ const onTabSelectionRejected = event => {
151
+ const {
152
+ selectedScreenKey,
153
+ provenance
154
+ } = event.nativeEvent;
155
+ const confirmed = {
156
+ routeKey: selectedScreenKey,
157
+ provenance
158
+ };
159
+ const route = state.routes.find(route => route.key === selectedScreenKey);
160
+ if (!route) {
161
+ console.error(`Received 'tabSelectionRejected' for route that doesn't exist: ${selectedScreenKey}`);
162
+ return;
163
+ }
164
+ if (focusedRouteKey === route.key) {
165
+ dispatch({
166
+ type: 'CONFIRM_STATE',
167
+ confirmed
168
+ });
169
+ return;
170
+ }
171
+ navigate(route, confirmed);
172
+ };
173
+ const onTabSelectionPrevented = event => {
174
+ const {
175
+ selectedScreenKey,
176
+ provenance,
177
+ preventedScreenKey
178
+ } = event.nativeEvent;
179
+ const confirmed = {
180
+ routeKey: selectedScreenKey,
181
+ provenance
182
+ };
183
+ dispatch({
184
+ type: 'CONFIRM_STATE',
185
+ confirmed
186
+ });
187
+ navigation.emit({
188
+ type: 'tabPress',
189
+ target: preventedScreenKey,
190
+ canPreventDefault: true
191
+ });
192
+ };
193
+ const currentOptions = descriptors[focusedRouteKey]?.options ?? {};
49
194
  const {
50
195
  fontFamily = Platform.select({
51
196
  ios: fonts.medium.fontFamily,
@@ -56,14 +201,48 @@ export function BottomTabViewNative({
56
201
  default: fonts.regular.fontWeight
57
202
  }),
58
203
  fontSize,
59
- fontStyle
204
+ fontStyle,
205
+ color: fontColor
60
206
  } = currentOptions.tabBarLabelStyle || {};
61
- const activeTintColor = currentOptions.tabBarActiveTintColor ?? colors.primary;
62
- const inactiveTintColor = currentOptions.tabBarInactiveTintColor ?? Platform.select({
207
+ const backgroundColor = currentOptions.tabBarStyle?.backgroundColor ?? colors.background;
208
+ const shouldHideTabBar = currentOptions.tabBarStyle?.display === 'none';
209
+ let activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor;
210
+ let activeTintColor = currentOptions.tabBarActiveTintColor;
211
+ let inactiveTintColor = currentOptions.tabBarInactiveTintColor;
212
+
213
+ // Derive colors based on Material Design guidelines
214
+ // https://m3.material.io/components/navigation-bar/specs
215
+ if (Platform.OS === 'android') {
216
+ switch (getAndroidColorName(backgroundColor)) {
217
+ case 'system_surface_container_light':
218
+ case 'system_surface_container_high_light':
219
+ case 'system_surface_container_highest_light':
220
+ case 'system_surface_container_low_light':
221
+ case 'system_surface_container_lowest_light':
222
+ inactiveTintColor = inactiveTintColor ?? PlatformColor('@android:color/system_on_surface_variant_light');
223
+ activeTintColor = activeTintColor ?? PlatformColor('@android:color/system_on_secondary_container_light');
224
+ activeIndicatorColor = activeIndicatorColor ?? PlatformColor('@android:color/system_secondary_container_light');
225
+ break;
226
+ case 'system_surface_container_dark':
227
+ case 'system_surface_container_high_dark':
228
+ case 'system_surface_container_highest_dark':
229
+ case 'system_surface_container_low_dark':
230
+ case 'system_surface_container_lowest_dark':
231
+ inactiveTintColor = inactiveTintColor ?? PlatformColor('@android:color/system_on_surface_variant_dark');
232
+ activeTintColor = activeTintColor ?? PlatformColor('@android:color/system_on_secondary_container_dark');
233
+ activeIndicatorColor = activeIndicatorColor ?? PlatformColor('@android:color/system_secondary_container_dark');
234
+ break;
235
+ }
236
+ }
237
+ inactiveTintColor = inactiveTintColor ?? Platform.select({
63
238
  ios: PlatformColor('label'),
64
239
  default: colors.text
65
240
  });
66
- const activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor ?? Color(activeTintColor)?.alpha(0.1).string();
241
+ activeTintColor = activeTintColor ?? colors.primary;
242
+ activeIndicatorColor = activeIndicatorColor ?? Platform.select({
243
+ android: Color(activeTintColor)?.alpha(0.075).string(),
244
+ default: undefined
245
+ });
67
246
  const onTransitionStart = ({
68
247
  route
69
248
  }) => {
@@ -95,60 +274,45 @@ export function BottomTabViewNative({
95
274
  flexDirection: tabBarPosition === 'left' || tabBarPosition === 'right' ? 'row' : 'column'
96
275
  },
97
276
  children: [tabBarPosition === 'top' || tabBarPosition === 'left' ? tabBarElement : null, /*#__PURE__*/_jsx(Tabs.Host, {
98
- tabBarHidden: hasCustomTabBar,
99
- bottomAccessory: bottomAccessory ? environment => bottomAccessory({
100
- placement: environment
101
- }) : undefined,
102
- tabBarItemLabelVisibilityMode: currentOptions?.tabBarLabelVisibilityMode,
103
- tabBarControllerMode: tabBarControllerMode,
104
- tabBarMinimizeBehavior: tabBarMinimizeBehavior,
105
- tabBarTintColor: activeTintColor,
106
- tabBarItemIconColor: inactiveTintColor,
107
- tabBarItemIconColorActive: activeTintColor,
108
- tabBarItemTitleFontColor: inactiveTintColor,
109
- tabBarItemTitleFontColorActive: activeTintColor,
110
- tabBarItemTitleFontFamily: fontFamily,
111
- tabBarItemTitleFontWeight: fontWeight,
112
- tabBarItemTitleFontSize: fontSize,
113
- tabBarItemTitleFontSizeActive: fontSize,
114
- tabBarItemTitleFontStyle: fontStyle,
115
- tabBarBackgroundColor: currentOptions.tabBarStyle?.backgroundColor ?? colors.card,
116
- tabBarItemActiveIndicatorColor: activeIndicatorColor,
117
- tabBarItemActiveIndicatorEnabled: currentOptions?.tabBarActiveIndicatorEnabled,
118
- tabBarItemRippleColor: currentOptions?.tabBarRippleColor,
119
- experimentalControlNavigationStateInJS: false,
120
- onNativeFocusChange: e => {
121
- const route = state.routes.find(route => route.key === e.nativeEvent.tabKey);
122
- if (route) {
123
- const event = navigation.emit({
124
- type: 'tabPress',
125
- target: route.key,
126
- canPreventDefault: true
127
- });
128
- if (event.defaultPrevented) {
129
- throw new Error("Preventing default for 'tabPress' is not supported with native tab bar.");
130
- }
131
- const isFocused = state.index === state.routes.findIndex(r => r.key === route.key);
132
- if (!isFocused) {
133
- navigation.dispatch({
134
- ...CommonActions.navigate(route.name, route.params),
135
- target: state.key
136
- });
137
- }
138
- }
277
+ navStateRequest: {
278
+ selectedScreenKey: focusedRouteKey,
279
+ baseProvenance: nativeState.confirmed.provenance
280
+ },
281
+ rejectStaleNavStateUpdates: true,
282
+ onTabSelected: onTabSelected,
283
+ onTabSelectionRejected: onTabSelectionRejected,
284
+ onTabSelectionPrevented: onTabSelectionPrevented,
285
+ tabBarHidden: hasCustomTabBar || shouldHideTabBar,
286
+ colorScheme: dark ? 'dark' : 'light',
287
+ nativeContainerStyle: {
288
+ backgroundColor: colors.background
289
+ },
290
+ ios: {
291
+ bottomAccessory: bottomAccessory ? environment => bottomAccessory({
292
+ placement: environment
293
+ }) : undefined,
294
+ tabBarControllerMode,
295
+ tabBarMinimizeBehavior,
296
+ tabBarTintColor: activeTintColor
139
297
  },
140
298
  children: state.routes.map((route, index) => {
299
+ const descriptor = descriptors[route.key];
300
+ if (descriptor == null) {
301
+ throw new Error(`Couldn't find a descriptor for route '${route.key}'.`);
302
+ }
141
303
  const {
142
304
  options,
143
305
  render,
144
306
  navigation
145
- } = descriptors[route.key];
307
+ } = descriptor;
146
308
  const isFocused = state.index === index;
147
309
  const isPreloaded = state.preloadedRouteKeys.includes(route.key);
148
310
  const {
149
311
  title,
150
- lazy = true,
312
+ lazy = false,
313
+ inactiveBehavior = 'pause',
151
314
  tabBarLabel,
315
+ tabBarSelectionEnabled,
152
316
  tabBarBadgeStyle,
153
317
  tabBarIcon,
154
318
  tabBarBadge,
@@ -158,7 +322,7 @@ export function BottomTabViewNative({
158
322
  tabBarAccessibilityLabel,
159
323
  tabBarButtonTestID,
160
324
  sceneStyle,
161
- scrollEdgeEffects
325
+ overrideScrollViewContentInsetAdjustmentBehavior
162
326
  } = options;
163
327
  const {
164
328
  backgroundColor: tabBarBackgroundColor,
@@ -171,14 +335,25 @@ export function BottomTabViewNative({
171
335
  label: tabBarLabel,
172
336
  title
173
337
  }, route.name);
338
+ const badgeBackgroundColor = tabBarBadgeStyle?.backgroundColor ?? colors.notification;
339
+ const badgeTextColor = tabBarBadgeStyle?.color ?? Color.foreground(badgeBackgroundColor);
174
340
  const tabItemAppearance = {
175
341
  tabBarItemTitleFontFamily: fontFamily,
176
342
  tabBarItemTitleFontSize: fontSize,
177
343
  tabBarItemTitleFontWeight: fontWeight,
178
- tabBarItemTitleFontStyle: fontStyle
344
+ tabBarItemTitleFontStyle: fontStyle,
345
+ tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
346
+ tabBarItemIconColor: inactiveTintColor,
347
+ tabBarItemBadgeBackgroundColor: badgeBackgroundColor
348
+ };
349
+ const normalTabItemAppearance = {
350
+ tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
351
+ tabBarItemIconColor: inactiveTintColor
352
+ };
353
+ const selectedTabItemAppearance = {
354
+ tabBarItemTitleFontColor: activeTintColor,
355
+ tabBarItemIconColor: activeTintColor
179
356
  };
180
- const badgeBackgroundColor = tabBarBadgeStyle?.backgroundColor ?? colors.notification;
181
- const badgeTextColor = tabBarBadgeStyle?.color ?? (Color(badgeBackgroundColor)?.isLight() ? 'black' : 'white');
182
357
  const getIcon = selected => {
183
358
  if (typeof tabBarIcon === 'function') {
184
359
  const result = tabBarIcon({
@@ -198,8 +373,15 @@ export function BottomTabViewNative({
198
373
  }
199
374
  return undefined;
200
375
  };
201
- const icon = getIcon(false);
202
- const selectedIcon = getIcon(true);
376
+
377
+ // An error is thrown for React Elements in `getIcon`
378
+ // So we only call it when we actually render a native tab bar
379
+ const icon = hasCustomTabBar ? undefined : getIcon(false);
380
+ const selectedIcon = hasCustomTabBar ? undefined : getIcon(true);
381
+
382
+ // For preloaded screens and if lazy is false,
383
+ // Keep them active so that the effects can run
384
+ const isActive = inactiveBehavior === 'none' || isPreloaded || nativeState.lastTransition?.from === route.key || nativeState.lastTransition?.to === route.key || lazy === false && !loaded.includes(route.key);
203
385
  return /*#__PURE__*/_jsx(Tabs.Screen, {
204
386
  onWillAppear: () => onTransitionStart({
205
387
  route
@@ -207,40 +389,14 @@ export function BottomTabViewNative({
207
389
  onDidAppear: () => onTransitionEnd({
208
390
  route
209
391
  }),
210
- tabKey: route.key,
211
- icon: icon,
212
- selectedIcon: selectedIcon?.ios ?? selectedIcon?.shared,
213
- tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
214
- tabBarItemBadgeTextColor: badgeTextColor,
392
+ screenKey: route.key,
215
393
  tabBarItemAccessibilityLabel: tabBarAccessibilityLabel,
216
394
  tabBarItemTestID: tabBarButtonTestID,
395
+ preventNativeSelection: tabBarSelectionEnabled === false,
217
396
  badgeValue: tabBarBadge?.toString(),
218
- systemItem: tabBarSystemItem,
219
- isFocused: isFocused,
220
397
  title: tabTitle,
221
- scrollEdgeEffects: {
222
- top: scrollEdgeEffects?.top === 'auto' ? 'automatic' : scrollEdgeEffects?.top,
223
- bottom: scrollEdgeEffects?.bottom === 'auto' ? 'automatic' : scrollEdgeEffects?.bottom,
224
- left: scrollEdgeEffects?.left === 'auto' ? 'automatic' : scrollEdgeEffects?.left,
225
- right: scrollEdgeEffects?.right === 'auto' ? 'automatic' : scrollEdgeEffects?.right
226
- }
227
- // FIXME: if this is not provided, ScrollView on lazy tabs glitches on iOS 18
228
- // For now we provide an empty object before adding proper support
229
- ,
230
- scrollEdgeAppearance: {},
231
- standardAppearance: {
232
- tabBarBackgroundColor,
233
- tabBarShadowColor,
234
- tabBarBlurEffect,
235
- stacked: {
236
- normal: tabItemAppearance
237
- },
238
- inline: {
239
- normal: tabItemAppearance
240
- },
241
- compactInline: {
242
- normal: tabItemAppearance
243
- }
398
+ nativeContainerStyle: {
399
+ backgroundColor: colors.background
244
400
  },
245
401
  specialEffects: {
246
402
  repeatedTabSelection: {
@@ -248,27 +404,89 @@ export function BottomTabViewNative({
248
404
  scrollToTop: true
249
405
  }
250
406
  },
251
- experimental_userInterfaceStyle: dark ? 'dark' : 'light',
252
- children: /*#__PURE__*/_jsx(Lazy, {
253
- enabled: lazy,
254
- visible: isFocused || isPreloaded,
255
- children: /*#__PURE__*/_jsx(ScreenContent, {
256
- isFocused: isFocused,
257
- route: route,
258
- navigation: navigation,
259
- options: options,
260
- style: sceneStyle,
261
- children: /*#__PURE__*/_jsx(AnimatedScreenContent, {
407
+ android: {
408
+ icon: icon?.android ?? icon?.shared,
409
+ selectedIcon: selectedIcon?.android ?? selectedIcon?.shared,
410
+ standardAppearance: {
411
+ tabBarBackgroundColor: tabBarBackgroundColor ?? backgroundColor,
412
+ tabBarItemRippleColor: currentOptions?.tabBarRippleColor,
413
+ tabBarItemLabelVisibilityMode: currentOptions?.tabBarLabelVisibilityMode,
414
+ normal: normalTabItemAppearance,
415
+ selected: selectedTabItemAppearance,
416
+ tabBarItemActiveIndicatorColor: activeIndicatorColor,
417
+ tabBarItemActiveIndicatorEnabled: currentOptions?.tabBarActiveIndicatorEnabled,
418
+ tabBarItemTitleFontFamily: fontFamily,
419
+ tabBarItemTitleFontWeight: fontWeight,
420
+ tabBarItemTitleSmallLabelFontSize: fontSize,
421
+ tabBarItemTitleLargeLabelFontSize: fontSize,
422
+ tabBarItemTitleFontStyle: fontStyle,
423
+ tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
424
+ tabBarItemBadgeTextColor: badgeTextColor
425
+ }
426
+ },
427
+ ios: {
428
+ icon: icon?.ios ?? icon?.shared,
429
+ selectedIcon: selectedIcon?.ios ?? selectedIcon?.shared,
430
+ systemItem: tabBarSystemItem,
431
+ scrollEdgeAppearance: {
432
+ tabBarBackgroundColor,
433
+ tabBarShadowColor,
434
+ tabBarBlurEffect,
435
+ stacked: {
436
+ normal: tabItemAppearance
437
+ },
438
+ inline: {
439
+ normal: tabItemAppearance
440
+ },
441
+ compactInline: {
442
+ normal: tabItemAppearance
443
+ }
444
+ },
445
+ standardAppearance: {
446
+ tabBarBackgroundColor,
447
+ tabBarShadowColor,
448
+ tabBarBlurEffect,
449
+ stacked: {
450
+ normal: tabItemAppearance
451
+ },
452
+ inline: {
453
+ normal: tabItemAppearance
454
+ },
455
+ compactInline: {
456
+ normal: tabItemAppearance
457
+ }
458
+ },
459
+ overrideScrollViewContentInsetAdjustmentBehavior
460
+ },
461
+ children: /*#__PURE__*/_jsx(Deferred, {
462
+ lazy: lazy,
463
+ visible: isFocused || isPreloaded || loaded.includes(route.key),
464
+ children: /*#__PURE__*/_jsx(ActivityView, {
465
+ mode: isFocused ? 'normal' : isActive ? 'inert' : 'paused',
466
+ visible:
467
+ // We don't need to hide the content since it's handled natively
468
+ // Hiding may also cause flash due to lag after native tab switch
469
+ // So we leave it always visible
470
+ true,
471
+ style: StyleSheet.absoluteFill,
472
+ children: /*#__PURE__*/_jsx(ScreenContent, {
262
473
  isFocused: isFocused,
263
- children: /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
264
- value: 0,
265
- children: /*#__PURE__*/_jsx(NavigationMetaContext.Provider, {
266
- value: meta,
267
- children: render()
474
+ route: route,
475
+ navigation: navigation,
476
+ options: options,
477
+ style: sceneStyle,
478
+ children: /*#__PURE__*/_jsx(AnimatedScreenContent, {
479
+ isFocused: isFocused,
480
+ children: /*#__PURE__*/_jsx(BottomTabBarHeightContext.Provider, {
481
+ value: 0,
482
+ children: /*#__PURE__*/_jsx(NavigationMetaContext.Provider, {
483
+ value: meta,
484
+ children: render()
485
+ })
268
486
  })
269
487
  })
270
488
  })
271
- })
489
+ }, route.key)
272
490
  })
273
491
  }, route.key);
274
492
  })
@@ -314,7 +532,10 @@ function getPlatformIcon(icon) {
314
532
  android: {
315
533
  type: 'imageSource',
316
534
  imageSource: MaterialSymbol.getImageSource({
317
- name: icon.name
535
+ name: icon.name,
536
+ variant: icon.variant,
537
+ weight: icon.weight,
538
+ size: ICON_SIZE
318
539
  })
319
540
  },
320
541
  shared: undefined
@@ -334,16 +555,6 @@ function getPlatformIcon(icon) {
334
555
  imageSource: icon.source
335
556
  }
336
557
  };
337
- case 'resource':
338
- return {
339
- // TODO: support xcasset once new version of react-native-screens is released
340
- ios: undefined,
341
- android: {
342
- type: 'drawableResource',
343
- name: icon.name
344
- },
345
- shared: undefined
346
- };
347
558
  default:
348
559
  {
349
560
  const _exhaustiveCheck = icon;
@@ -351,4 +562,11 @@ function getPlatformIcon(icon) {
351
562
  }
352
563
  }
353
564
  }
565
+ function getAndroidColorName(color) {
566
+ const value = color;
567
+ if (typeof value === 'object' && value != null && 'resource_paths' in value && Array.isArray(value.resource_paths) && typeof value.resource_paths[0] === 'string') {
568
+ return value.resource_paths[0].replace('@android:color/', '');
569
+ }
570
+ return null;
571
+ }
354
572
  //# sourceMappingURL=BottomTabViewNativeImpl.js.map