react-native-tab-view 4.0.0-alpha.0 → 4.0.0-alpha.10

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 (85) hide show
  1. package/lib/commonjs/Pager.android.js.map +1 -1
  2. package/lib/commonjs/Pager.ios.js.map +1 -1
  3. package/lib/commonjs/Pager.js.map +1 -1
  4. package/lib/commonjs/PagerViewAdapter.js +9 -13
  5. package/lib/commonjs/PagerViewAdapter.js.map +1 -1
  6. package/lib/commonjs/PanResponderAdapter.js +4 -4
  7. package/lib/commonjs/PanResponderAdapter.js.map +1 -1
  8. package/lib/commonjs/PlatformPressable.js +24 -3
  9. package/lib/commonjs/PlatformPressable.js.map +1 -1
  10. package/lib/commonjs/SceneMap.js +2 -2
  11. package/lib/commonjs/SceneMap.js.map +1 -1
  12. package/lib/commonjs/SceneView.js +3 -4
  13. package/lib/commonjs/SceneView.js.map +1 -1
  14. package/lib/commonjs/TabBar.js +112 -97
  15. package/lib/commonjs/TabBar.js.map +1 -1
  16. package/lib/commonjs/TabBarIndicator.js +21 -16
  17. package/lib/commonjs/TabBarIndicator.js.map +1 -1
  18. package/lib/commonjs/TabBarItem.js +58 -80
  19. package/lib/commonjs/TabBarItem.js.map +1 -1
  20. package/lib/commonjs/TabBarItemLabel.js +4 -4
  21. package/lib/commonjs/TabBarItemLabel.js.map +1 -1
  22. package/lib/commonjs/TabView.js +3 -3
  23. package/lib/commonjs/TabView.js.map +1 -1
  24. package/lib/commonjs/index.js.map +1 -1
  25. package/lib/commonjs/types.js.map +1 -1
  26. package/lib/commonjs/useAnimatedValue.js +2 -2
  27. package/lib/commonjs/useAnimatedValue.js.map +1 -1
  28. package/lib/module/Pager.android.js.map +1 -1
  29. package/lib/module/Pager.ios.js.map +1 -1
  30. package/lib/module/Pager.js.map +1 -1
  31. package/lib/module/PagerViewAdapter.js +7 -11
  32. package/lib/module/PagerViewAdapter.js.map +1 -1
  33. package/lib/module/PanResponderAdapter.js +2 -2
  34. package/lib/module/PanResponderAdapter.js.map +1 -1
  35. package/lib/module/PlatformPressable.js +22 -1
  36. package/lib/module/PlatformPressable.js.map +1 -1
  37. package/lib/module/SceneMap.js.map +1 -1
  38. package/lib/module/SceneView.js +1 -2
  39. package/lib/module/SceneView.js.map +1 -1
  40. package/lib/module/TabBar.js +110 -95
  41. package/lib/module/TabBar.js.map +1 -1
  42. package/lib/module/TabBarIndicator.js +19 -14
  43. package/lib/module/TabBarIndicator.js.map +1 -1
  44. package/lib/module/TabBarItem.js +56 -78
  45. package/lib/module/TabBarItem.js.map +1 -1
  46. package/lib/module/TabBarItemLabel.js +3 -2
  47. package/lib/module/TabBarItemLabel.js.map +1 -1
  48. package/lib/module/TabView.js +1 -1
  49. package/lib/module/TabView.js.map +1 -1
  50. package/lib/module/index.js.map +1 -1
  51. package/lib/module/types.js.map +1 -1
  52. package/lib/module/useAnimatedValue.js.map +1 -1
  53. package/lib/typescript/src/PlatformPressable.d.ts +4 -3
  54. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  55. package/lib/typescript/src/SceneMap.d.ts +2 -2
  56. package/lib/typescript/src/SceneMap.d.ts.map +1 -1
  57. package/lib/typescript/src/SceneView.d.ts +2 -2
  58. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  59. package/lib/typescript/src/TabBar.d.ts +7 -18
  60. package/lib/typescript/src/TabBar.d.ts.map +1 -1
  61. package/lib/typescript/src/TabBarIndicator.d.ts +3 -3
  62. package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
  63. package/lib/typescript/src/TabBarItem.d.ts +4 -19
  64. package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
  65. package/lib/typescript/src/TabBarItemLabel.d.ts +1 -1
  66. package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -1
  67. package/lib/typescript/src/TabView.d.ts +5 -5
  68. package/lib/typescript/src/TabView.d.ts.map +1 -1
  69. package/lib/typescript/src/index.d.ts +1 -1
  70. package/lib/typescript/src/index.d.ts.map +1 -1
  71. package/lib/typescript/src/types.d.ts +25 -1
  72. package/lib/typescript/src/types.d.ts.map +1 -1
  73. package/package.json +11 -11
  74. package/src/PagerViewAdapter.tsx +2 -2
  75. package/src/PanResponderAdapter.tsx +4 -4
  76. package/src/PlatformPressable.tsx +31 -2
  77. package/src/SceneMap.tsx +1 -3
  78. package/src/SceneView.tsx +4 -4
  79. package/src/TabBar.tsx +178 -179
  80. package/src/TabBarIndicator.tsx +22 -14
  81. package/src/TabBarItem.tsx +92 -118
  82. package/src/TabBarItemLabel.tsx +2 -1
  83. package/src/TabView.tsx +5 -5
  84. package/src/index.tsx +6 -1
  85. package/src/types.tsx +25 -1
package/src/TabBar.tsx CHANGED
@@ -1,23 +1,27 @@
1
1
  import * as React from 'react';
2
2
  import {
3
3
  Animated,
4
+ type DimensionValue,
4
5
  FlatList,
5
6
  I18nManager,
6
- LayoutChangeEvent,
7
- ListRenderItemInfo,
7
+ type LayoutChangeEvent,
8
+ type ListRenderItemInfo,
8
9
  Platform,
9
- PressableAndroidRippleConfig,
10
- StyleProp,
10
+ type PressableAndroidRippleConfig,
11
+ type StyleProp,
11
12
  StyleSheet,
12
- TextStyle,
13
+ type TextStyle,
13
14
  View,
14
- ViewStyle,
15
- ViewToken,
15
+ type ViewStyle,
16
+ type ViewToken,
16
17
  } from 'react-native';
17
18
  import useLatestCallback from 'use-latest-callback';
18
19
 
19
- import { Props as IndicatorProps, TabBarIndicator } from './TabBarIndicator';
20
- import { Props as TabBarItemProps, TabBarItem } from './TabBarItem';
20
+ import {
21
+ type Props as IndicatorProps,
22
+ TabBarIndicator,
23
+ } from './TabBarIndicator';
24
+ import { type Props as TabBarItemProps, TabBarItem } from './TabBarItem';
21
25
  import type {
22
26
  Event,
23
27
  Layout,
@@ -26,6 +30,7 @@ import type {
26
30
  Route,
27
31
  Scene,
28
32
  SceneRendererProps,
33
+ TabDescriptor,
29
34
  } from './types';
30
35
  import { useAnimatedValue } from './useAnimatedValue';
31
36
 
@@ -37,23 +42,8 @@ export type Props<T extends Route> = SceneRendererProps & {
37
42
  inactiveColor?: string;
38
43
  pressColor?: string;
39
44
  pressOpacity?: number;
40
- getLabelText?: (scene: Scene<T>) => string | undefined;
41
- getAccessible?: (scene: Scene<T>) => boolean | undefined;
42
- getAccessibilityLabel?: (scene: Scene<T>) => string | undefined;
43
- getTestID?: (scene: Scene<T>) => string | undefined;
44
- renderLabel?: (
45
- scene: Scene<T> & {
46
- focused: boolean;
47
- color: string;
48
- }
49
- ) => React.ReactNode;
50
- renderIcon?: (
51
- scene: Scene<T> & {
52
- focused: boolean;
53
- color: string;
54
- }
55
- ) => React.ReactNode;
56
- renderBadge?: (scene: Scene<T>) => React.ReactNode;
45
+ options?: Record<string, TabDescriptor<T>>;
46
+ commonOptions?: TabDescriptor<T>;
57
47
  renderIndicator?: (props: IndicatorProps<T>) => React.ReactNode;
58
48
  renderTabBarItem?: (
59
49
  props: TabBarItemProps<T> & { key: string }
@@ -72,9 +62,6 @@ export type Props<T extends Route> = SceneRendererProps & {
72
62
  android_ripple?: PressableAndroidRippleConfig;
73
63
  };
74
64
 
75
- type FlattenedTabWidth = string | number | undefined;
76
- type FlattenedTabPadding = string | number | undefined;
77
-
78
65
  const Separator = ({ width }: { width: number }) => {
79
66
  return <View style={{ width }} />;
80
67
  };
@@ -85,24 +72,30 @@ const getFlattenedTabWidth = (style: StyleProp<ViewStyle>) => {
85
72
  return tabStyle?.width;
86
73
  };
87
74
 
88
- const getFlattenedPaddingLeft = (style: StyleProp<ViewStyle>) => {
75
+ const getFlattenedPaddingStart = (style: StyleProp<ViewStyle>) => {
89
76
  const flattenStyle = StyleSheet.flatten(style);
90
77
 
91
78
  return flattenStyle
92
- ? flattenStyle.paddingLeft || flattenStyle.paddingHorizontal || 0
79
+ ? flattenStyle.paddingLeft ||
80
+ flattenStyle.paddingStart ||
81
+ flattenStyle.paddingHorizontal ||
82
+ 0
93
83
  : 0;
94
84
  };
95
85
 
96
- const getFlattenedPaddingRight = (style: StyleProp<ViewStyle>) => {
86
+ const getFlattenedPaddingEnd = (style: StyleProp<ViewStyle>) => {
97
87
  const flattenStyle = StyleSheet.flatten(style);
98
88
 
99
89
  return flattenStyle
100
- ? flattenStyle.paddingRight || flattenStyle.paddingHorizontal || 0
90
+ ? flattenStyle.paddingRight ||
91
+ flattenStyle.paddingEnd ||
92
+ flattenStyle.paddingHorizontal ||
93
+ 0
101
94
  : 0;
102
95
  };
103
96
 
104
97
  const convertPaddingPercentToSize = (
105
- value: FlattenedTabPadding,
98
+ value: DimensionValue | undefined,
106
99
  layout: Layout
107
100
  ): number => {
108
101
  switch (typeof value) {
@@ -125,9 +118,9 @@ const getComputedTabWidth = (
125
118
  routes: Route[],
126
119
  scrollEnabled: boolean | undefined,
127
120
  tabWidths: { [key: string]: number },
128
- flattenedWidth: FlattenedTabWidth,
129
- flattenedPaddingLeft: FlattenedTabPadding,
130
- flattenedPaddingRight: FlattenedTabPadding,
121
+ flattenedWidth: DimensionValue | undefined,
122
+ flattenedPaddingStart: DimensionValue | undefined,
123
+ flattenedPaddingEnd: DimensionValue | undefined,
131
124
  gap?: number
132
125
  ) => {
133
126
  if (flattenedWidth === 'auto') {
@@ -152,8 +145,8 @@ const getComputedTabWidth = (
152
145
 
153
146
  const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
154
147
  const paddingTotalWidth =
155
- convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
156
- convertPaddingPercentToSize(flattenedPaddingRight, layout);
148
+ convertPaddingPercentToSize(flattenedPaddingStart, layout) +
149
+ convertPaddingPercentToSize(flattenedPaddingEnd, layout);
157
150
 
158
151
  return (layout.width - gapTotalWidth - paddingTotalWidth) / routes.length;
159
152
  };
@@ -179,21 +172,21 @@ const getTabBarWidth = <T extends Route>({
179
172
  gap,
180
173
  scrollEnabled,
181
174
  flattenedTabWidth,
182
- flattenedPaddingLeft,
183
- flattenedPaddingRight,
175
+ flattenedPaddingStart,
176
+ flattenedPaddingEnd,
184
177
  tabWidths,
185
178
  }: Pick<Props<T>, 'navigationState' | 'gap' | 'layout' | 'scrollEnabled'> & {
186
179
  tabWidths: Record<string, number>;
187
- flattenedPaddingLeft: FlattenedTabPadding;
188
- flattenedPaddingRight: FlattenedTabPadding;
189
- flattenedTabWidth: FlattenedTabWidth;
180
+ flattenedPaddingStart: DimensionValue | undefined;
181
+ flattenedPaddingEnd: DimensionValue | undefined;
182
+ flattenedTabWidth: DimensionValue | undefined;
190
183
  }) => {
191
184
  const { routes } = navigationState;
192
185
 
193
186
  const paddingsWidth = Math.max(
194
187
  0,
195
- convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
196
- convertPaddingPercentToSize(flattenedPaddingRight, layout)
188
+ convertPaddingPercentToSize(flattenedPaddingStart, layout) +
189
+ convertPaddingPercentToSize(flattenedPaddingEnd, layout)
197
190
  );
198
191
 
199
192
  return routes.reduce<number>(
@@ -207,8 +200,8 @@ const getTabBarWidth = <T extends Route>({
207
200
  scrollEnabled,
208
201
  tabWidths,
209
202
  flattenedTabWidth,
210
- flattenedPaddingLeft,
211
- flattenedPaddingRight,
203
+ flattenedPaddingStart,
204
+ flattenedPaddingEnd,
212
205
  gap
213
206
  ),
214
207
  paddingsWidth
@@ -223,15 +216,15 @@ const normalizeScrollValue = <T extends Route>({
223
216
  tabWidths,
224
217
  value,
225
218
  flattenedTabWidth,
226
- flattenedPaddingLeft,
227
- flattenedPaddingRight,
219
+ flattenedPaddingStart,
220
+ flattenedPaddingEnd,
228
221
  direction,
229
222
  }: Pick<Props<T>, 'layout' | 'navigationState' | 'gap' | 'scrollEnabled'> & {
230
223
  tabWidths: Record<string, number>;
231
224
  value: number;
232
- flattenedTabWidth: FlattenedTabWidth;
233
- flattenedPaddingLeft: FlattenedTabPadding;
234
- flattenedPaddingRight: FlattenedTabPadding;
225
+ flattenedTabWidth: DimensionValue | undefined;
226
+ flattenedPaddingStart: DimensionValue | undefined;
227
+ flattenedPaddingEnd: DimensionValue | undefined;
235
228
  direction: LocaleDirection;
236
229
  }) => {
237
230
  const tabBarWidth = getTabBarWidth({
@@ -241,8 +234,8 @@ const normalizeScrollValue = <T extends Route>({
241
234
  gap,
242
235
  scrollEnabled,
243
236
  flattenedTabWidth,
244
- flattenedPaddingLeft,
245
- flattenedPaddingRight,
237
+ flattenedPaddingStart,
238
+ flattenedPaddingEnd,
246
239
  });
247
240
  const maxDistance = getMaxScrollDistance(tabBarWidth, layout.width);
248
241
  const scrollValue = Math.max(Math.min(value, maxDistance), 0);
@@ -263,20 +256,20 @@ const getScrollAmount = <T extends Route>({
263
256
  scrollEnabled,
264
257
  flattenedTabWidth,
265
258
  tabWidths,
266
- flattenedPaddingLeft,
267
- flattenedPaddingRight,
259
+ flattenedPaddingStart,
260
+ flattenedPaddingEnd,
268
261
  direction,
269
262
  }: Pick<Props<T>, 'layout' | 'navigationState' | 'scrollEnabled' | 'gap'> & {
270
263
  tabWidths: Record<string, number>;
271
- flattenedTabWidth: FlattenedTabWidth;
272
- flattenedPaddingLeft: FlattenedTabPadding;
273
- flattenedPaddingRight: FlattenedTabPadding;
264
+ flattenedTabWidth: DimensionValue | undefined;
265
+ flattenedPaddingStart: DimensionValue | undefined;
266
+ flattenedPaddingEnd: DimensionValue | undefined;
274
267
  direction: LocaleDirection;
275
268
  }) => {
276
269
  const paddingInitial =
277
270
  direction === 'rtl'
278
- ? convertPaddingPercentToSize(flattenedPaddingRight, layout)
279
- : convertPaddingPercentToSize(flattenedPaddingLeft, layout);
271
+ ? convertPaddingPercentToSize(flattenedPaddingEnd, layout)
272
+ : convertPaddingPercentToSize(flattenedPaddingStart, layout);
280
273
 
281
274
  const centerDistance = Array.from({
282
275
  length: navigationState.index + 1,
@@ -288,8 +281,8 @@ const getScrollAmount = <T extends Route>({
288
281
  scrollEnabled,
289
282
  tabWidths,
290
283
  flattenedTabWidth,
291
- flattenedPaddingLeft,
292
- flattenedPaddingRight,
284
+ flattenedPaddingStart,
285
+ flattenedPaddingEnd,
293
286
  gap
294
287
  );
295
288
 
@@ -312,12 +305,11 @@ const getScrollAmount = <T extends Route>({
312
305
  gap,
313
306
  scrollEnabled,
314
307
  flattenedTabWidth,
315
- flattenedPaddingLeft,
316
- flattenedPaddingRight,
308
+ flattenedPaddingStart,
309
+ flattenedPaddingEnd,
317
310
  direction,
318
311
  });
319
312
  };
320
-
321
313
  const getLabelTextDefault = ({ route }: Scene<Route>) => route.title;
322
314
 
323
315
  const getAccessibleDefault = ({ route }: Scene<Route>) =>
@@ -327,8 +319,8 @@ const getAccessibilityLabelDefault = ({ route }: Scene<Route>) =>
327
319
  typeof route.accessibilityLabel === 'string'
328
320
  ? route.accessibilityLabel
329
321
  : typeof route.title === 'string'
330
- ? route.title
331
- : undefined;
322
+ ? route.title
323
+ : undefined;
332
324
 
333
325
  const renderIndicatorDefault = (props: IndicatorProps<Route>) => (
334
326
  <TabBarIndicator {...props} />
@@ -341,10 +333,6 @@ const getTestIdDefault = ({ route }: Scene<Route>) => route.testID;
341
333
  const MEASURE_PER_BATCH = 10;
342
334
 
343
335
  export function TabBar<T extends Route>({
344
- getLabelText = getLabelTextDefault,
345
- getAccessible = getAccessibleDefault,
346
- getAccessibilityLabel = getAccessibilityLabelDefault,
347
- getTestID = getTestIdDefault,
348
336
  renderIndicator = renderIndicatorDefault,
349
337
  gap = 0,
350
338
  scrollEnabled,
@@ -362,9 +350,6 @@ export function TabBar<T extends Route>({
362
350
  onTabPress,
363
351
  pressColor,
364
352
  pressOpacity,
365
- renderBadge,
366
- renderIcon,
367
- renderLabel,
368
353
  direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
369
354
  renderTabBarItem,
370
355
  style,
@@ -372,6 +357,8 @@ export function TabBar<T extends Route>({
372
357
  layout: propLayout,
373
358
  testID,
374
359
  android_ripple,
360
+ options,
361
+ commonOptions,
375
362
  }: Props<T>) {
376
363
  const [layout, setLayout] = React.useState<Layout>(
377
364
  propLayout ?? { width: 0, height: 0 }
@@ -384,8 +371,8 @@ export function TabBar<T extends Route>({
384
371
  const { routes } = navigationState;
385
372
  const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
386
373
  const isWidthDynamic = flattenedTabWidth === 'auto';
387
- const flattenedPaddingRight = getFlattenedPaddingRight(contentContainerStyle);
388
- const flattenedPaddingLeft = getFlattenedPaddingLeft(contentContainerStyle);
374
+ const flattenedPaddingEnd = getFlattenedPaddingEnd(contentContainerStyle);
375
+ const flattenedPaddingStart = getFlattenedPaddingStart(contentContainerStyle);
389
376
  const scrollOffset = getScrollAmount({
390
377
  layout,
391
378
  navigationState,
@@ -393,8 +380,8 @@ export function TabBar<T extends Route>({
393
380
  gap,
394
381
  scrollEnabled,
395
382
  flattenedTabWidth,
396
- flattenedPaddingLeft,
397
- flattenedPaddingRight,
383
+ flattenedPaddingStart,
384
+ flattenedPaddingEnd,
398
385
  direction,
399
386
  });
400
387
 
@@ -439,15 +426,15 @@ export function TabBar<T extends Route>({
439
426
  gap,
440
427
  scrollEnabled,
441
428
  flattenedTabWidth,
442
- flattenedPaddingLeft,
443
- flattenedPaddingRight,
429
+ flattenedPaddingStart,
430
+ flattenedPaddingEnd,
444
431
  });
445
432
 
446
433
  const separatorsWidth = Math.max(0, routes.length - 1) * gap;
447
434
  const paddingsWidth = Math.max(
448
435
  0,
449
- convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
450
- convertPaddingPercentToSize(flattenedPaddingRight, layout)
436
+ convertPaddingPercentToSize(flattenedPaddingStart, layout) +
437
+ convertPaddingPercentToSize(flattenedPaddingEnd, layout)
451
438
  );
452
439
 
453
440
  const translateX = React.useMemo(
@@ -462,86 +449,103 @@ export function TabBar<T extends Route>({
462
449
 
463
450
  const renderItem = React.useCallback(
464
451
  ({ item: route, index }: ListRenderItemInfo<T>) => {
465
- const props: TabBarItemProps<T> & { key: string } = {
466
- key: route.key,
467
- position: position,
468
- route: route,
469
- navigationState: navigationState,
470
- getAccessibilityLabel: getAccessibilityLabel,
471
- getAccessible: getAccessible,
472
- getLabelText: getLabelText,
473
- getTestID: getTestID,
474
- renderBadge: renderBadge,
475
- renderIcon: renderIcon,
476
- renderLabel: renderLabel,
477
- activeColor: activeColor,
478
- inactiveColor: inactiveColor,
479
- pressColor: pressColor,
480
- pressOpacity: pressOpacity,
481
- onLayout: isWidthDynamic
482
- ? (e: LayoutChangeEvent) => {
483
- measuredTabWidths.current[route.key] = e.nativeEvent.layout.width;
452
+ const {
453
+ testID = getTestIdDefault({ route }),
454
+ labelText = getLabelTextDefault({ route }),
455
+ accessible = getAccessibleDefault({ route }),
456
+ accessibilityLabel = getAccessibilityLabelDefault({ route }),
457
+ ...rest
458
+ } = {
459
+ ...commonOptions,
460
+ ...options?.[route.key],
461
+ };
484
462
 
485
- // When we have measured widths for all of the tabs, we should updates the state
486
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
487
- // If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
488
- if (
489
- routes.length > MEASURE_PER_BATCH &&
490
- index === MEASURE_PER_BATCH &&
491
- routes
492
- .slice(0, MEASURE_PER_BATCH)
493
- .every(
494
- (r) => typeof measuredTabWidths.current[r.key] === 'number'
495
- )
496
- ) {
497
- setTabWidths({ ...measuredTabWidths.current });
498
- } else if (
499
- routes.every(
463
+ const onLayout = isWidthDynamic
464
+ ? (e: LayoutChangeEvent) => {
465
+ measuredTabWidths.current[route.key] = e.nativeEvent.layout.width;
466
+
467
+ // When we have measured widths for all of the tabs, we should updates the state
468
+ // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
469
+ // If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
470
+ if (
471
+ routes.length > MEASURE_PER_BATCH &&
472
+ index === MEASURE_PER_BATCH &&
473
+ routes
474
+ .slice(0, MEASURE_PER_BATCH)
475
+ .every(
500
476
  (r) => typeof measuredTabWidths.current[r.key] === 'number'
501
477
  )
502
- ) {
503
- // When we have measured widths for all of the tabs, we should updates the state
504
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
505
- setTabWidths({ ...measuredTabWidths.current });
506
- }
478
+ ) {
479
+ setTabWidths({ ...measuredTabWidths.current });
480
+ } else if (
481
+ routes.every(
482
+ (r) => typeof measuredTabWidths.current[r.key] === 'number'
483
+ )
484
+ ) {
485
+ // When we have measured widths for all of the tabs, we should updates the state
486
+ // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
487
+ setTabWidths({ ...measuredTabWidths.current });
507
488
  }
508
- : undefined,
509
- onPress: () => {
510
- const event: Scene<T> & Event = {
511
- route,
512
- defaultPrevented: false,
513
- preventDefault: () => {
514
- event.defaultPrevented = true;
515
- },
516
- };
489
+ }
490
+ : undefined;
491
+
492
+ const onPress = () => {
493
+ const event: Scene<T> & Event = {
494
+ route,
495
+ defaultPrevented: false,
496
+ preventDefault: () => {
497
+ event.defaultPrevented = true;
498
+ },
499
+ };
517
500
 
518
- onTabPress?.(event);
501
+ onTabPress?.(event);
519
502
 
520
- if (event.defaultPrevented) {
521
- return;
522
- }
503
+ if (event.defaultPrevented) {
504
+ return;
505
+ }
523
506
 
524
- jumpTo(route.key);
525
- },
526
- onLongPress: () => onTabLongPress?.({ route }),
527
- labelStyle: labelStyle,
507
+ jumpTo(route.key);
508
+ };
509
+
510
+ const onLongPress = () => onTabLongPress?.({ route });
511
+
512
+ // Calculate the default width for tab for FlatList to work
513
+ const defaultTabWidth = !isWidthDynamic
514
+ ? getComputedTabWidth(
515
+ index,
516
+ layout,
517
+ routes,
518
+ scrollEnabled,
519
+ tabWidths,
520
+ getFlattenedTabWidth(tabStyle),
521
+ getFlattenedPaddingEnd(contentContainerStyle),
522
+ getFlattenedPaddingStart(contentContainerStyle),
523
+ gap
524
+ )
525
+ : undefined;
526
+
527
+ const props = {
528
+ ...rest,
529
+ key: route.key,
530
+ position,
531
+ route,
532
+ navigationState,
533
+ testID,
534
+ labelText,
535
+ accessible,
536
+ accessibilityLabel,
537
+ activeColor,
538
+ inactiveColor,
539
+ pressColor,
540
+ pressOpacity,
541
+ onLayout,
542
+ onPress,
543
+ onLongPress,
544
+ labelStyle,
528
545
  style: tabStyle,
529
- // Calculate the deafult width for tab for FlatList to work
530
- defaultTabWidth: !isWidthDynamic
531
- ? getComputedTabWidth(
532
- index,
533
- layout,
534
- routes,
535
- scrollEnabled,
536
- tabWidths,
537
- getFlattenedTabWidth(tabStyle),
538
- getFlattenedPaddingRight(contentContainerStyle),
539
- getFlattenedPaddingLeft(contentContainerStyle),
540
- gap
541
- )
542
- : undefined,
546
+ defaultTabWidth,
543
547
  android_ripple,
544
- };
548
+ } satisfies TabBarItemProps<T> & { key: string };
545
549
 
546
550
  return (
547
551
  <>
@@ -555,33 +559,28 @@ export function TabBar<T extends Route>({
555
559
  );
556
560
  },
557
561
  [
562
+ position,
563
+ navigationState,
564
+ commonOptions,
565
+ options,
558
566
  activeColor,
559
- android_ripple,
560
- gap,
561
- getAccessibilityLabel,
562
- getAccessible,
563
- getLabelText,
564
- getTestID,
565
567
  inactiveColor,
568
+ pressColor,
569
+ pressOpacity,
566
570
  isWidthDynamic,
567
- jumpTo,
568
571
  labelStyle,
572
+ tabStyle,
569
573
  layout,
570
- navigationState,
571
- onTabLongPress,
572
- onTabPress,
573
- position,
574
- pressColor,
575
- pressOpacity,
576
- renderBadge,
577
- renderIcon,
578
- renderLabel,
579
- renderTabBarItem,
580
574
  routes,
581
575
  scrollEnabled,
582
- tabStyle,
583
- contentContainerStyle,
584
576
  tabWidths,
577
+ contentContainerStyle,
578
+ gap,
579
+ android_ripple,
580
+ renderTabBarItem,
581
+ onTabPress,
582
+ jumpTo,
583
+ onTabLongPress,
585
584
  ]
586
585
  );
587
586
 
@@ -655,7 +654,7 @@ export function TabBar<T extends Route>({
655
654
  ),
656
655
  style: [
657
656
  indicatorStyle,
658
- { left: flattenedPaddingLeft, right: flattenedPaddingRight },
657
+ { start: flattenedPaddingStart, end: flattenedPaddingEnd },
659
658
  ],
660
659
  getTabWidth: (i: number) =>
661
660
  getComputedTabWidth(
@@ -665,8 +664,8 @@ export function TabBar<T extends Route>({
665
664
  scrollEnabled,
666
665
  tabWidths,
667
666
  flattenedTabWidth,
668
- flattenedPaddingRight,
669
- flattenedPaddingLeft,
667
+ flattenedPaddingEnd,
668
+ flattenedPaddingStart,
670
669
  gap
671
670
  ),
672
671
  gap,
@@ -725,8 +724,8 @@ const styles = StyleSheet.create({
725
724
  indicatorContainer: {
726
725
  position: 'absolute',
727
726
  top: 0,
728
- left: 0,
729
- right: 0,
727
+ start: 0,
728
+ end: 0,
730
729
  bottom: 0,
731
730
  },
732
731
  });
@@ -3,9 +3,9 @@ import {
3
3
  Animated,
4
4
  Easing,
5
5
  Platform,
6
- StyleProp,
6
+ type StyleProp,
7
7
  StyleSheet,
8
- ViewStyle,
8
+ type ViewStyle,
9
9
  } from 'react-native';
10
10
 
11
11
  import type {
@@ -20,7 +20,7 @@ export type GetTabWidth = (index: number) => number;
20
20
 
21
21
  export type Props<T extends Route> = SceneRendererProps & {
22
22
  navigationState: NavigationState<T>;
23
- width: string | number;
23
+ width: 'auto' | `${number}%` | number;
24
24
  getTabWidth: GetTabWidth;
25
25
  direction: LocaleDirection;
26
26
  style?: StyleProp<ViewStyle>;
@@ -131,19 +131,27 @@ export function TabBarIndicator<T extends Route>({
131
131
  );
132
132
  }
133
133
 
134
+ const styleList: StyleProp<ViewStyle> = [];
135
+
136
+ // scaleX doesn't work properly on chrome and opera for linux and android
137
+ if (Platform.OS === 'web' && width === 'auto') {
138
+ styleList.push(
139
+ { width: transform[1].scaleX },
140
+ { left: transform[0].translateX }
141
+ );
142
+ } else {
143
+ styleList.push(
144
+ { width: width === 'auto' ? 1 : width },
145
+ { start: `${(100 / routes.length) * navigationState.index}%` },
146
+ { transform }
147
+ );
148
+ }
149
+
134
150
  return (
135
151
  <Animated.View
136
152
  style={[
137
153
  styles.indicator,
138
- { width: width === 'auto' ? 1 : width },
139
- // If layout is not available, use `left` property for positioning the indicator
140
- // This avoids rendering delay until we are able to calculate translateX
141
- // If platform is macos use `left` property as `transform` is broken at the moment.
142
- // See: https://github.com/microsoft/react-native-macos/issues/280
143
- layout.width && Platform.OS !== 'macos'
144
- ? { left: 0 }
145
- : { left: `${(100 / routes.length) * navigationState.index}%` },
146
- { transform },
154
+ styleList,
147
155
  width === 'auto' ? { opacity: opacity } : null,
148
156
  style,
149
157
  ]}
@@ -157,9 +165,9 @@ const styles = StyleSheet.create({
157
165
  indicator: {
158
166
  backgroundColor: '#ffeb3b',
159
167
  position: 'absolute',
160
- left: 0,
168
+ start: 0,
161
169
  bottom: 0,
162
- right: 0,
170
+ end: 0,
163
171
  height: 2,
164
172
  },
165
173
  });