@react-navigation/bottom-tabs 7.19.2 → 7.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) 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 +309 -198
  4. package/lib/module/unstable/NativeBottomTabView.native.js.map +1 -1
  5. package/lib/module/views/BottomTabBar.js +111 -58
  6. package/lib/module/views/BottomTabBar.js.map +1 -1
  7. package/lib/module/views/BottomTabItem.js +2 -0
  8. package/lib/module/views/BottomTabItem.js.map +1 -1
  9. package/lib/module/views/BottomTabView.js +81 -60
  10. package/lib/module/views/BottomTabView.js.map +1 -1
  11. package/lib/module/views/TabBarIcon.js +6 -2
  12. package/lib/module/views/TabBarIcon.js.map +1 -1
  13. package/lib/typescript/src/types.d.ts +26 -1
  14. package/lib/typescript/src/types.d.ts.map +1 -1
  15. package/lib/typescript/src/unstable/Deferred.d.ts.map +1 -1
  16. package/lib/typescript/src/unstable/NativeBottomTabView.native.d.ts.map +1 -1
  17. package/lib/typescript/src/unstable/types.d.ts +148 -5
  18. package/lib/typescript/src/unstable/types.d.ts.map +1 -1
  19. package/lib/typescript/src/views/BottomTabBar.d.ts.map +1 -1
  20. package/lib/typescript/src/views/BottomTabItem.d.ts +5 -1
  21. package/lib/typescript/src/views/BottomTabItem.d.ts.map +1 -1
  22. package/lib/typescript/src/views/BottomTabView.d.ts.map +1 -1
  23. package/lib/typescript/src/views/TabBarIcon.d.ts +2 -1
  24. package/lib/typescript/src/views/TabBarIcon.d.ts.map +1 -1
  25. package/package.json +6 -6
  26. package/src/types.tsx +36 -2
  27. package/src/unstable/Deferred.tsx +3 -11
  28. package/src/unstable/NativeBottomTabView.native.tsx +376 -217
  29. package/src/unstable/types.tsx +164 -6
  30. package/src/views/BottomTabBar.tsx +153 -72
  31. package/src/views/BottomTabItem.tsx +6 -0
  32. package/src/views/BottomTabView.tsx +101 -69
  33. package/src/views/TabBarIcon.tsx +9 -2
@@ -19,21 +19,14 @@ export function Deferred({
19
19
  visible,
20
20
  children
21
21
  }) {
22
- const [rendered, setRendered] = React.useState(lazy ? visible : false);
22
+ const [rendered, setRendered] = React.useState(visible);
23
23
  const shouldRenderDeferred = !(lazy || visible || rendered);
24
- React.useEffect(() => {
25
- if (shouldRenderDeferred === false) {
26
- return;
27
- }
28
- React.startTransition(() => {
29
- setRendered(true);
30
- });
31
- }, [shouldRenderDeferred]);
24
+ const canRenderDeferred = React.useDeferredValue(shouldRenderDeferred, false);
32
25
  if (visible && rendered === false) {
33
26
  setRendered(true);
34
27
  return children;
35
28
  }
36
- if (rendered) {
29
+ if (rendered || canRenderDeferred) {
37
30
  return children;
38
31
  }
39
32
  return null;
@@ -1 +1 @@
1
- {"version":3,"names":["React","Deferred","lazy","visible","children","rendered","setRendered","useState","shouldRenderDeferred","useEffect","startTransition"],"sourceRoot":"../../../src","sources":["unstable/Deferred.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAiB9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAAC;EAAEC,IAAI;EAAEC,OAAO;EAAEC;AAAgB,CAAC,EAAE;EAC3D,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAGN,KAAK,CAACO,QAAQ,CAACL,IAAI,GAAGC,OAAO,GAAG,KAAK,CAAC;EAEtE,MAAMK,oBAAoB,GAAG,EAAEN,IAAI,IAAIC,OAAO,IAAIE,QAAQ,CAAC;EAE3DL,KAAK,CAACS,SAAS,CAAC,MAAM;IACpB,IAAID,oBAAoB,KAAK,KAAK,EAAE;MAClC;IACF;IAEAR,KAAK,CAACU,eAAe,CAAC,MAAM;MAC1BJ,WAAW,CAAC,IAAI,CAAC;IACnB,CAAC,CAAC;EACJ,CAAC,EAAE,CAACE,oBAAoB,CAAC,CAAC;EAE1B,IAAIL,OAAO,IAAIE,QAAQ,KAAK,KAAK,EAAE;IACjCC,WAAW,CAAC,IAAI,CAAC;IAEjB,OAAOF,QAAQ;EACjB;EAEA,IAAIC,QAAQ,EAAE;IACZ,OAAOD,QAAQ;EACjB;EAEA,OAAO,IAAI;AACb","ignoreList":[]}
1
+ {"version":3,"names":["React","Deferred","lazy","visible","children","rendered","setRendered","useState","shouldRenderDeferred","canRenderDeferred","useDeferredValue"],"sourceRoot":"../../../src","sources":["unstable/Deferred.tsx"],"mappings":";;AAAA,OAAO,KAAKA,KAAK,MAAM,OAAO;AAiB9B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,QAAQA,CAAC;EAAEC,IAAI;EAAEC,OAAO;EAAEC;AAAgB,CAAC,EAAE;EAC3D,MAAM,CAACC,QAAQ,EAAEC,WAAW,CAAC,GAAGN,KAAK,CAACO,QAAQ,CAACJ,OAAO,CAAC;EAEvD,MAAMK,oBAAoB,GAAG,EAAEN,IAAI,IAAIC,OAAO,IAAIE,QAAQ,CAAC;EAE3D,MAAMI,iBAAiB,GAAGT,KAAK,CAACU,gBAAgB,CAACF,oBAAoB,EAAE,KAAK,CAAC;EAE7E,IAAIL,OAAO,IAAIE,QAAQ,KAAK,KAAK,EAAE;IACjCC,WAAW,CAAC,IAAI,CAAC;IAEjB,OAAOF,QAAQ;EACjB;EAEA,IAAIC,QAAQ,IAAII,iBAAiB,EAAE;IACjC,OAAOL,QAAQ;EACjB;EAEA,OAAO,IAAI;AACb","ignoreList":[]}
@@ -12,6 +12,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
12
12
  const meta = {
13
13
  type: 'native-tabs'
14
14
  };
15
+ const SUPPORTS_PROMINENT_TAB = Platform.OS === 'ios' && parseInt(Platform.Version, 10) >= 27;
15
16
  function reducer(state, action) {
16
17
  switch (action.type) {
17
18
  case 'CONFIRM_STATE':
@@ -71,7 +72,8 @@ export function NativeBottomTabView({
71
72
  const {
72
73
  selectedScreenKey,
73
74
  provenance,
74
- actionOrigin
75
+ actionOrigin,
76
+ isRepeated
75
77
  } = event.nativeEvent;
76
78
  const confirmed = {
77
79
  routeKey: selectedScreenKey,
@@ -83,9 +85,19 @@ export function NativeBottomTabView({
83
85
  return;
84
86
  }
85
87
  if (actionOrigin === 'user') {
88
+ const {
89
+ tabBarRepeatedPressBehavior
90
+ } = descriptors[route.key].options;
86
91
  navigation.emit({
87
92
  type: 'tabPress',
88
- target: route.key
93
+ target: route.key,
94
+ data: {
95
+ origin: 'native',
96
+ behavior: {
97
+ scrollToTop: isRepeated && tabBarRepeatedPressBehavior?.scrollToTop !== false,
98
+ popToTop: isRepeated && tabBarRepeatedPressBehavior?.popToTop !== false
99
+ }
100
+ }
89
101
  });
90
102
  }
91
103
  if (actionOrigin === 'programmatic-js' || focusedRouteKey === route.key) {
@@ -138,7 +150,14 @@ export function NativeBottomTabView({
138
150
  });
139
151
  navigation.emit({
140
152
  type: 'tabPress',
141
- target: preventedScreenKey
153
+ target: preventedScreenKey,
154
+ data: {
155
+ origin: 'native',
156
+ behavior: {
157
+ scrollToTop: false,
158
+ popToTop: false
159
+ }
160
+ }
142
161
  });
143
162
  };
144
163
  const currentOptions = descriptors[state.routes[state.index].key]?.options;
@@ -155,38 +174,27 @@ export function NativeBottomTabView({
155
174
  fontStyle,
156
175
  color: fontColor
157
176
  } = currentOptions.tabBarLabelStyle || {};
158
- const activeTintColor = currentOptions.tabBarActiveTintColor ?? colors.primary;
159
- const inactiveTintColor = currentOptions.tabBarInactiveTintColor ?? Platform.select({
160
- ios: PlatformColor('label'),
161
- default: colors.text
162
- });
163
- const activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor ?? typeof activeTintColor === 'string' ? Color(activeTintColor)?.alpha(0.1).string() : undefined;
164
- const onTransitionStart = ({
165
- closing,
166
- route
167
- }) => {
168
- navigation.emit({
169
- type: 'transitionStart',
170
- data: {
171
- closing
172
- },
173
- target: route.key
174
- });
175
- };
176
- const onTransitionEnd = ({
177
- closing,
178
- route
179
- }) => {
180
- navigation.emit({
181
- type: 'transitionEnd',
182
- data: {
183
- closing
184
- },
185
- target: route.key
177
+ const {
178
+ activeIndicatorColor,
179
+ activeTintColor,
180
+ inactiveTintColor
181
+ } = React.useMemo(() => {
182
+ const activeTintColor = currentOptions.tabBarActiveTintColor ?? colors.primary;
183
+ const inactiveTintColor = currentOptions.tabBarInactiveTintColor ?? Platform.select({
184
+ ios: PlatformColor('label'),
185
+ default: colors.text
186
186
  });
187
- };
187
+ const activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor ?? (typeof activeTintColor === 'string' ? Color(activeTintColor)?.alpha(0.1).string() : undefined);
188
+ return {
189
+ activeIndicatorColor,
190
+ activeTintColor,
191
+ inactiveTintColor
192
+ };
193
+ }, [currentOptions?.tabBarActiveIndicatorColor, currentOptions.tabBarActiveTintColor, currentOptions.tabBarInactiveTintColor, colors.primary, colors.text]);
188
194
  const tabBarControllerMode = currentOptions.tabBarControllerMode === 'auto' ? 'automatic' : currentOptions.tabBarControllerMode;
189
195
  const tabBarMinimizeBehavior = currentOptions.tabBarMinimizeBehavior === 'auto' ? 'automatic' : currentOptions.tabBarMinimizeBehavior === 'none' ? 'never' : currentOptions.tabBarMinimizeBehavior;
196
+ const tabBarSidebarPreferredPlacement = currentOptions.tabBarSidebarPreferredPlacement === 'auto' ? 'automatic' : currentOptions.tabBarSidebarPreferredPlacement;
197
+ const prominentScreenKey = SUPPORTS_PROMINENT_TAB ? state.routes.find(route => descriptors[route.key]?.options.tabBarRole === 'prominent')?.key : undefined;
190
198
  const shouldHideTabBar = currentOptions.tabBarStyle?.display === 'none';
191
199
  const bottomAccessory = currentOptions.bottomAccessory;
192
200
  return /*#__PURE__*/_jsx(SafeAreaProviderCompat, {
@@ -205,173 +213,265 @@ export function NativeBottomTabView({
205
213
  backgroundColor: colors.background
206
214
  },
207
215
  ios: {
216
+ prominentScreenKey,
208
217
  bottomAccessory: bottomAccessory ? environment => bottomAccessory({
209
218
  placement: environment
210
219
  }) : undefined,
220
+ tabBarHiddenAnimationEnabled: currentOptions.tabBarVisibilityAnimationEnabled,
211
221
  tabBarControllerMode,
222
+ tabBarSidebarPreferredPlacement,
212
223
  tabBarMinimizeBehavior,
213
224
  tabBarTintColor: activeTintColor
214
225
  },
215
226
  children: state.routes.map((route, index) => {
216
- const {
217
- options,
218
- render,
219
- navigation
220
- } = descriptors[route.key];
227
+ const descriptor = descriptors[route.key];
221
228
  const isFocused = state.index === index;
222
229
  const isPreloaded = state.preloadedRouteKeys.includes(route.key);
223
230
  const {
224
- title,
225
- lazy = false,
226
- tabBarLabel,
227
- tabBarSelectionEnabled,
228
- tabBarBadgeStyle,
229
- tabBarIcon,
230
- tabBarBadge,
231
- tabBarSystemItem,
232
- tabBarBlurEffect = dark ? 'systemMaterialDark' : 'systemMaterial',
233
- tabBarStyle,
234
- overrideScrollViewContentInsetAdjustmentBehavior
235
- } = options;
236
- const {
237
- backgroundColor: tabBarBackgroundColor,
238
- shadowColor: tabBarShadowColor
239
- } = tabBarStyle || {};
240
- const tabTitle =
241
- // On iOS, `systemItem` already provides a localized label
242
- // So we should only use `tabBarLabel` if explicitly provided
243
- Platform.OS === 'ios' && tabBarSystemItem != null ? tabBarLabel : getLabel({
244
- label: tabBarLabel,
245
- title
246
- }, route.name);
247
- const badgeBackgroundColor = tabBarBadgeStyle?.backgroundColor ?? colors.notification;
248
- const badgeTextColor = tabBarBadgeStyle?.color ?? (typeof badgeBackgroundColor === 'string' ? Color(badgeBackgroundColor).isLight() ? 'black' : 'white' : undefined);
249
- const tabItemAppearance = {
250
- tabBarItemTitleFontFamily: fontFamily,
251
- tabBarItemTitleFontSize: fontSize,
252
- tabBarItemTitleFontWeight: fontWeight,
253
- tabBarItemTitleFontStyle: fontStyle,
254
- tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
255
- tabBarItemIconColor: inactiveTintColor,
256
- tabBarItemBadgeBackgroundColor: badgeBackgroundColor
257
- };
258
- const normalTabItemAppearance = {
259
- tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
260
- tabBarItemIconColor: inactiveTintColor
261
- };
262
- const selectedTabItemAppearance = {
263
- tabBarItemTitleFontColor: activeTintColor,
264
- tabBarItemIconColor: activeTintColor
265
- };
266
- const icon = typeof tabBarIcon === 'function' ? getPlatformIcon(tabBarIcon({
267
- focused: false
268
- })) : tabBarIcon != null ? getPlatformIcon(tabBarIcon) : undefined;
269
- const selectedIcon = typeof tabBarIcon === 'function' ? getPlatformIcon(tabBarIcon({
270
- focused: true
271
- })) : undefined;
272
- return /*#__PURE__*/_jsx(Tabs.Screen, {
273
- onWillDisappear: () => onTransitionStart({
274
- closing: true,
275
- route
276
- }),
277
- onWillAppear: () => onTransitionStart({
278
- closing: false,
279
- route
280
- }),
281
- onDidAppear: () => onTransitionEnd({
282
- closing: false,
283
- route
284
- }),
285
- onDidDisappear: () => onTransitionEnd({
286
- closing: true,
287
- route
288
- }),
289
- screenKey: route.key,
290
- preventNativeSelection: tabBarSelectionEnabled === false,
291
- badgeValue: tabBarBadge?.toString(),
292
- title: tabTitle,
293
- nativeContainerStyle: {
294
- backgroundColor: colors.background
295
- },
296
- specialEffects: {
297
- repeatedTabSelection: {
298
- popToRoot: true,
299
- scrollToTop: true
300
- }
301
- },
302
- android: {
303
- icon: icon?.android ?? icon?.shared,
304
- selectedIcon: selectedIcon?.android ?? selectedIcon?.shared,
305
- standardAppearance: {
306
- tabBarBackgroundColor: tabBarBackgroundColor ?? colors.card,
307
- tabBarItemRippleColor: currentOptions?.tabBarRippleColor,
308
- tabBarItemLabelVisibilityMode: currentOptions?.tabBarLabelVisibilityMode,
309
- normal: normalTabItemAppearance,
310
- selected: selectedTabItemAppearance,
311
- tabBarItemActiveIndicatorColor: activeIndicatorColor,
312
- tabBarItemActiveIndicatorEnabled: currentOptions?.tabBarActiveIndicatorEnabled,
313
- tabBarItemTitleFontFamily: fontFamily,
314
- tabBarItemTitleFontWeight: fontWeight,
315
- tabBarItemTitleSmallLabelFontSize: fontSize,
316
- tabBarItemTitleLargeLabelFontSize: fontSize,
317
- tabBarItemTitleFontStyle: fontStyle,
318
- tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
319
- tabBarItemBadgeTextColor: badgeTextColor
320
- }
321
- },
322
- ios: {
323
- icon: icon?.ios ?? icon?.shared,
324
- selectedIcon: selectedIcon?.ios ?? selectedIcon?.shared,
325
- systemItem: tabBarSystemItem,
326
- scrollEdgeAppearance: {
327
- tabBarBackgroundColor,
328
- tabBarShadowColor,
329
- tabBarBlurEffect,
330
- stacked: {
331
- normal: tabItemAppearance
332
- },
333
- inline: {
334
- normal: tabItemAppearance
335
- },
336
- compactInline: {
337
- normal: tabItemAppearance
338
- }
339
- },
340
- standardAppearance: {
341
- tabBarBackgroundColor,
342
- tabBarShadowColor,
343
- tabBarBlurEffect,
344
- stacked: {
345
- normal: tabItemAppearance
346
- },
347
- inline: {
348
- normal: tabItemAppearance
349
- },
350
- compactInline: {
351
- normal: tabItemAppearance
352
- }
353
- },
354
- overrideScrollViewContentInsetAdjustmentBehavior
355
- },
356
- children: /*#__PURE__*/_jsx(Deferred, {
357
- lazy: lazy,
358
- visible: isFocused || isPreloaded,
359
- children: /*#__PURE__*/_jsx(ScreenWithHeader, {
360
- isFocused: isFocused,
361
- route: route,
362
- navigation: navigation,
363
- options: options,
364
- children: /*#__PURE__*/_jsx(NavigationMetaContext.Provider, {
365
- value: meta,
366
- children: render()
367
- })
368
- })
369
- })
231
+ lazy = false
232
+ } = descriptor.options;
233
+ return /*#__PURE__*/_jsx(TabScene, {
234
+ route: route,
235
+ descriptor: descriptor,
236
+ navigation: navigation,
237
+ isFocused: isFocused,
238
+ lazy: lazy,
239
+ visible: isFocused || isPreloaded,
240
+ fontFamily: fontFamily,
241
+ fontWeight: fontWeight,
242
+ fontSize: fontSize,
243
+ fontStyle: fontStyle,
244
+ fontColor: fontColor,
245
+ activeTintColor: activeTintColor,
246
+ inactiveTintColor: inactiveTintColor,
247
+ activeIndicatorColor: activeIndicatorColor,
248
+ rippleColor: currentOptions.tabBarRippleColor,
249
+ labelVisibilityMode: currentOptions.tabBarLabelVisibilityMode,
250
+ activeIndicatorEnabled: currentOptions.tabBarActiveIndicatorEnabled
370
251
  }, route.key);
371
252
  })
372
253
  })
373
254
  });
374
255
  }
256
+ const TabScene = /*#__PURE__*/React.memo(function TabScene({
257
+ route,
258
+ descriptor,
259
+ navigation,
260
+ isFocused,
261
+ lazy,
262
+ visible,
263
+ fontFamily,
264
+ fontWeight,
265
+ fontSize,
266
+ fontStyle,
267
+ fontColor,
268
+ activeTintColor,
269
+ inactiveTintColor,
270
+ activeIndicatorColor,
271
+ rippleColor,
272
+ labelVisibilityMode,
273
+ activeIndicatorEnabled
274
+ }) {
275
+ const {
276
+ dark,
277
+ colors
278
+ } = useTheme();
279
+ const onTransitionStart = ({
280
+ closing,
281
+ route
282
+ }) => {
283
+ navigation.emit({
284
+ type: 'transitionStart',
285
+ data: {
286
+ closing
287
+ },
288
+ target: route.key
289
+ });
290
+ };
291
+ const onTransitionEnd = ({
292
+ closing,
293
+ route
294
+ }) => {
295
+ navigation.emit({
296
+ type: 'transitionEnd',
297
+ data: {
298
+ closing
299
+ },
300
+ target: route.key
301
+ });
302
+ };
303
+ const {
304
+ options,
305
+ render
306
+ } = descriptor;
307
+ const {
308
+ title,
309
+ tabBarLabel,
310
+ tabBarLabelPositionAdjustment,
311
+ tabBarSelectionEnabled,
312
+ tabBarRepeatedPressBehavior,
313
+ tabBarBadgeStyle,
314
+ tabBarIcon,
315
+ tabBarIconSize,
316
+ tabBarActiveIndicatorWidth,
317
+ tabBarActiveIndicatorHeight,
318
+ tabBarBadge,
319
+ tabBarSystemItem,
320
+ tabBarRole,
321
+ tabBarSearchAutoFocus,
322
+ tabBarBlurEffect = dark ? 'systemMaterialDark' : 'systemMaterial',
323
+ tabBarStyle,
324
+ overrideScrollViewContentInsetAdjustmentBehavior
325
+ } = options;
326
+ const systemItem = tabBarSystemItem ?? (
327
+ // Setting the `role` to 'search' doesn't add title and icon automatically
328
+ // So we also set `tabBarSystemItem` to 'search' to match SwiftUI's behavior
329
+ tabBarRole === 'search' ? 'search' : undefined);
330
+ const {
331
+ backgroundColor: tabBarBackgroundColor,
332
+ shadowColor: tabBarShadowColor
333
+ } = tabBarStyle || {};
334
+ const tabTitle =
335
+ // On iOS, `systemItem` already provides a localized label
336
+ // So we should only use `tabBarLabel` if explicitly provided
337
+ Platform.OS === 'ios' && systemItem != null ? tabBarLabel : getLabel({
338
+ label: tabBarLabel,
339
+ title
340
+ }, route.name);
341
+ const badgeBackgroundColor = tabBarBadgeStyle?.backgroundColor ?? colors.notification;
342
+ const badgeTextColor = tabBarBadgeStyle?.color ?? (typeof badgeBackgroundColor === 'string' ? Color(badgeBackgroundColor).isLight() ? 'black' : 'white' : undefined);
343
+ const tabItemAppearance = {
344
+ tabBarItemTitleFontFamily: fontFamily,
345
+ tabBarItemTitleFontSize: fontSize,
346
+ tabBarItemTitleFontWeight: fontWeight,
347
+ tabBarItemTitleFontStyle: fontStyle,
348
+ tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
349
+ tabBarItemIconColor: inactiveTintColor,
350
+ tabBarItemBadgeBackgroundColor: badgeBackgroundColor
351
+ };
352
+ const tabItemLayoutAppearance = {
353
+ stacked: {
354
+ normal: {
355
+ ...tabItemAppearance,
356
+ tabBarItemTitlePositionAdjustment: tabBarLabelPositionAdjustment?.stacked
357
+ }
358
+ },
359
+ inline: {
360
+ normal: {
361
+ ...tabItemAppearance,
362
+ tabBarItemTitlePositionAdjustment: tabBarLabelPositionAdjustment?.inline
363
+ }
364
+ },
365
+ compactInline: {
366
+ normal: {
367
+ ...tabItemAppearance,
368
+ tabBarItemTitlePositionAdjustment: tabBarLabelPositionAdjustment?.compactInline
369
+ }
370
+ }
371
+ };
372
+ const normalTabItemAppearance = {
373
+ tabBarItemTitleFontColor: inactiveTintColor ?? fontColor,
374
+ tabBarItemIconColor: inactiveTintColor
375
+ };
376
+ const selectedTabItemAppearance = {
377
+ tabBarItemTitleFontColor: activeTintColor,
378
+ tabBarItemIconColor: activeTintColor
379
+ };
380
+ const icon = typeof tabBarIcon === 'function' ? getPlatformIcon(tabBarIcon({
381
+ focused: false
382
+ })) : tabBarIcon != null ? getPlatformIcon(tabBarIcon) : undefined;
383
+ const selectedIcon = typeof tabBarIcon === 'function' ? getPlatformIcon(tabBarIcon({
384
+ focused: true
385
+ })) : undefined;
386
+ return /*#__PURE__*/_jsx(Tabs.Screen, {
387
+ onWillDisappear: () => onTransitionStart({
388
+ closing: true,
389
+ route
390
+ }),
391
+ onWillAppear: () => onTransitionStart({
392
+ closing: false,
393
+ route
394
+ }),
395
+ onDidAppear: () => onTransitionEnd({
396
+ closing: false,
397
+ route
398
+ }),
399
+ onDidDisappear: () => onTransitionEnd({
400
+ closing: true,
401
+ route
402
+ }),
403
+ screenKey: route.key,
404
+ preventNativeSelection: tabBarSelectionEnabled === false,
405
+ badgeValue: tabBarBadge?.toString(),
406
+ title: tabTitle,
407
+ style: {
408
+ backgroundColor: colors.background
409
+ },
410
+ specialEffects: {
411
+ repeatedTabSelection: {
412
+ popToRoot: tabBarRepeatedPressBehavior?.popToTop !== false,
413
+ scrollToTop: tabBarRepeatedPressBehavior?.scrollToTop !== false
414
+ }
415
+ },
416
+ android: {
417
+ icon: icon?.android ?? icon?.shared,
418
+ selectedIcon: selectedIcon?.android ?? selectedIcon?.shared,
419
+ standardAppearance: {
420
+ tabBarBackgroundColor: tabBarBackgroundColor ?? colors.card,
421
+ tabBarItemRippleColor: rippleColor,
422
+ tabBarItemLabelVisibilityMode: labelVisibilityMode,
423
+ normal: normalTabItemAppearance,
424
+ selected: selectedTabItemAppearance,
425
+ tabBarItemIconSize: tabBarIconSize,
426
+ tabBarItemActiveIndicatorColor: activeIndicatorColor,
427
+ tabBarItemActiveIndicatorEnabled: activeIndicatorEnabled,
428
+ tabBarItemActiveIndicatorWidth: tabBarActiveIndicatorWidth,
429
+ tabBarItemActiveIndicatorHeight: tabBarActiveIndicatorHeight,
430
+ tabBarItemTitleFontFamily: fontFamily,
431
+ tabBarItemTitleFontWeight: fontWeight,
432
+ tabBarItemTitleSmallLabelFontSize: fontSize,
433
+ tabBarItemTitleLargeLabelFontSize: fontSize,
434
+ tabBarItemTitleFontStyle: fontStyle,
435
+ tabBarItemBadgeBackgroundColor: badgeBackgroundColor,
436
+ tabBarItemBadgeTextColor: badgeTextColor
437
+ }
438
+ },
439
+ ios: {
440
+ icon: icon?.ios ?? icon?.shared,
441
+ selectedIcon: selectedIcon?.ios ?? selectedIcon?.shared,
442
+ systemItem,
443
+ role: tabBarRole === 'search' ? tabBarRole : undefined,
444
+ automaticallyActivatesSearch: tabBarSearchAutoFocus,
445
+ scrollEdgeAppearance: {
446
+ tabBarBackgroundColor,
447
+ tabBarShadowColor,
448
+ tabBarBlurEffect,
449
+ ...tabItemLayoutAppearance
450
+ },
451
+ standardAppearance: {
452
+ tabBarBackgroundColor,
453
+ tabBarShadowColor,
454
+ tabBarBlurEffect,
455
+ ...tabItemLayoutAppearance
456
+ },
457
+ overrideScrollViewContentInsetAdjustmentBehavior
458
+ },
459
+ children: /*#__PURE__*/_jsx(Deferred, {
460
+ lazy: lazy,
461
+ visible: visible,
462
+ children: /*#__PURE__*/_jsx(ScreenWithHeader, {
463
+ isFocused: isFocused,
464
+ route: route,
465
+ navigation: descriptor.navigation,
466
+ options: options,
467
+ children: /*#__PURE__*/_jsx(NavigationMetaContext.Provider, {
468
+ value: meta,
469
+ children: render()
470
+ })
471
+ })
472
+ })
473
+ });
474
+ });
375
475
  function ScreenWithHeader({
376
476
  isFocused,
377
477
  route,
@@ -417,25 +517,36 @@ function getPlatformIcon(icon) {
417
517
  switch (icon.type) {
418
518
  case 'sfSymbol':
419
519
  return {
420
- ios: icon,
520
+ ios: {
521
+ type: 'sfSymbol',
522
+ name: icon.name,
523
+ renderingMode: icon.renderingMode === 'multicolor' ? 'original' : icon.renderingMode
524
+ },
421
525
  android: undefined,
422
526
  shared: undefined
423
527
  };
424
528
  case 'image':
425
- return {
426
- ios: icon.tinted === false ? {
427
- type: 'imageSource',
428
- imageSource: icon.source
429
- } : {
430
- type: 'templateSource',
431
- templateSource: icon.source
432
- },
433
- android: undefined,
434
- shared: {
435
- type: 'imageSource',
436
- imageSource: icon.source
437
- }
438
- };
529
+ {
530
+ // A `{ uri: 'name' }` source maps to an Android drawable by name.
531
+ const drawableName = typeof icon.source === 'object' && 'uri' in icon.source && icon.source.uri?.includes(':') === false ? icon.source.uri : undefined;
532
+ return {
533
+ ios: {
534
+ type: 'imageSource',
535
+ imageSource: icon.source,
536
+ renderingMode: icon.tinted === false ? 'original' : 'template'
537
+ },
538
+ android: drawableName != null ? {
539
+ type: 'drawableResource',
540
+ name: drawableName,
541
+ tinting: icon.tinted === false ? 'original' : 'tinted'
542
+ } : {
543
+ type: 'imageSource',
544
+ imageSource: icon.source,
545
+ tinting: icon.tinted === false ? 'original' : 'tinted'
546
+ },
547
+ shared: undefined
548
+ };
549
+ }
439
550
  default:
440
551
  {
441
552
  const _exhaustiveCheck = icon;