react-native-tab-view 5.0.0-alpha.0 → 5.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 (108) hide show
  1. package/lib/module/DefaultAdapter.android.js +4 -0
  2. package/lib/module/DefaultAdapter.android.js.map +1 -0
  3. package/lib/module/DefaultAdapter.ios.js +4 -0
  4. package/lib/module/DefaultAdapter.ios.js.map +1 -0
  5. package/lib/module/DefaultAdapter.js +5 -0
  6. package/lib/module/DefaultAdapter.js.map +1 -0
  7. package/lib/module/PagerViewAdapter.native.js +29 -13
  8. package/lib/module/PagerViewAdapter.native.js.map +1 -1
  9. package/lib/module/PanResponderAdapter.js +3 -0
  10. package/lib/module/PanResponderAdapter.js.map +1 -1
  11. package/lib/module/PlatformPressable.js +1 -1
  12. package/lib/module/PlatformPressable.js.map +1 -1
  13. package/lib/module/SceneMap.js +12 -6
  14. package/lib/module/SceneMap.js.map +1 -1
  15. package/lib/module/SceneView.js +25 -1
  16. package/lib/module/SceneView.js.map +1 -1
  17. package/lib/module/ScrollViewAdapter.js +49 -18
  18. package/lib/module/ScrollViewAdapter.js.map +1 -1
  19. package/lib/module/TabBar.js +279 -169
  20. package/lib/module/TabBar.js.map +1 -1
  21. package/lib/module/TabBarIndicator.js +314 -90
  22. package/lib/module/TabBarIndicator.js.map +1 -1
  23. package/lib/module/TabBarItem.js +94 -44
  24. package/lib/module/TabBarItem.js.map +1 -1
  25. package/lib/module/TabBarItemLabel.js +3 -2
  26. package/lib/module/TabBarItemLabel.js.map +1 -1
  27. package/lib/module/TabView.js +2 -2
  28. package/lib/module/TabView.js.map +1 -1
  29. package/lib/module/constants.js +10 -0
  30. package/lib/module/constants.js.map +1 -0
  31. package/lib/module/index.js +1 -0
  32. package/lib/module/index.js.map +1 -1
  33. package/lib/module/useLayoutWidths.js +46 -0
  34. package/lib/module/useLayoutWidths.js.map +1 -0
  35. package/lib/typescript/src/DefaultAdapter.android.d.ts +2 -0
  36. package/lib/typescript/src/DefaultAdapter.android.d.ts.map +1 -0
  37. package/lib/typescript/src/DefaultAdapter.d.ts +6 -0
  38. package/lib/typescript/src/DefaultAdapter.d.ts.map +1 -0
  39. package/lib/typescript/src/DefaultAdapter.ios.d.ts +2 -0
  40. package/lib/typescript/src/DefaultAdapter.ios.d.ts.map +1 -0
  41. package/lib/typescript/src/PagerViewAdapter.d.ts +1 -1
  42. package/lib/typescript/src/PagerViewAdapter.d.ts.map +1 -1
  43. package/lib/typescript/src/PagerViewAdapter.native.d.ts +2 -2
  44. package/lib/typescript/src/PagerViewAdapter.native.d.ts.map +1 -1
  45. package/lib/typescript/src/PanResponderAdapter.d.ts +1 -1
  46. package/lib/typescript/src/PanResponderAdapter.d.ts.map +1 -1
  47. package/lib/typescript/src/PlatformPressable.d.ts +5 -5
  48. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  49. package/lib/typescript/src/SceneMap.d.ts +2 -2
  50. package/lib/typescript/src/SceneMap.d.ts.map +1 -1
  51. package/lib/typescript/src/SceneView.d.ts +2 -2
  52. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  53. package/lib/typescript/src/ScrollViewAdapter.d.ts +2 -3
  54. package/lib/typescript/src/ScrollViewAdapter.d.ts.map +1 -1
  55. package/lib/typescript/src/TabBar.d.ts +26 -25
  56. package/lib/typescript/src/TabBar.d.ts.map +1 -1
  57. package/lib/typescript/src/TabBarIndicator.d.ts +5 -7
  58. package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
  59. package/lib/typescript/src/TabBarItem.d.ts +17 -10
  60. package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
  61. package/lib/typescript/src/TabBarItemLabel.d.ts +4 -3
  62. package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -1
  63. package/lib/typescript/src/TabView.d.ts +111 -17
  64. package/lib/typescript/src/TabView.d.ts.map +1 -1
  65. package/lib/typescript/src/constants.d.ts +8 -0
  66. package/lib/typescript/src/constants.d.ts.map +1 -0
  67. package/lib/typescript/src/index.d.ts +13 -12
  68. package/lib/typescript/src/index.d.ts.map +1 -1
  69. package/lib/typescript/src/types.d.ts +31 -31
  70. package/lib/typescript/src/types.d.ts.map +1 -1
  71. package/lib/typescript/src/useLayoutWidths.d.ts +2 -0
  72. package/lib/typescript/src/useLayoutWidths.d.ts.map +1 -0
  73. package/lib/typescript/src/useMeasureLayout.d.ts +1 -1
  74. package/lib/typescript/src/useMeasureLayout.d.ts.map +1 -1
  75. package/package.json +13 -12
  76. package/src/DefaultAdapter.android.tsx +1 -0
  77. package/src/DefaultAdapter.ios.tsx +1 -0
  78. package/src/DefaultAdapter.tsx +13 -0
  79. package/src/PagerViewAdapter.native.tsx +36 -18
  80. package/src/PanResponderAdapter.tsx +4 -0
  81. package/src/PlatformPressable.tsx +11 -7
  82. package/src/SceneMap.tsx +17 -9
  83. package/src/SceneView.tsx +41 -2
  84. package/src/ScrollViewAdapter.tsx +88 -22
  85. package/src/TabBar.tsx +440 -235
  86. package/src/TabBarIndicator.tsx +389 -124
  87. package/src/TabBarItem.tsx +107 -46
  88. package/src/TabBarItemLabel.tsx +5 -4
  89. package/src/TabView.tsx +118 -20
  90. package/src/constants.tsx +8 -0
  91. package/src/index.tsx +5 -1
  92. package/src/types.tsx +40 -36
  93. package/src/useLayoutWidths.tsx +51 -0
  94. package/lib/module/Pager.android.js +0 -4
  95. package/lib/module/Pager.android.js.map +0 -1
  96. package/lib/module/Pager.ios.js +0 -4
  97. package/lib/module/Pager.ios.js.map +0 -1
  98. package/lib/module/Pager.js +0 -4
  99. package/lib/module/Pager.js.map +0 -1
  100. package/lib/typescript/src/Pager.android.d.ts +0 -2
  101. package/lib/typescript/src/Pager.android.d.ts.map +0 -1
  102. package/lib/typescript/src/Pager.d.ts +0 -2
  103. package/lib/typescript/src/Pager.d.ts.map +0 -1
  104. package/lib/typescript/src/Pager.ios.d.ts +0 -2
  105. package/lib/typescript/src/Pager.ios.d.ts.map +0 -1
  106. package/src/Pager.android.tsx +0 -1
  107. package/src/Pager.ios.tsx +0 -1
  108. package/src/Pager.tsx +0 -1
@@ -2,13 +2,13 @@
2
2
 
3
3
  import * as React from 'react';
4
4
  import { Animated, I18nManager, Platform, StyleSheet, View } from 'react-native';
5
- import useLatestCallback from 'use-latest-callback';
5
+ import { PRIMARY_INDICATOR_MIN_WIDTH, TAB_BAR_BACKGROUND_COLOR, TAB_BAR_BORDER_COLOR, TAB_MIN_WIDTH } from "./constants.js";
6
6
  import { TabBarIndicator } from "./TabBarIndicator.js";
7
7
  import { TabBarItem } from "./TabBarItem.js";
8
8
  import { useAnimatedValue } from "./useAnimatedValue.js";
9
+ import { useLayoutWidths } from "./useLayoutWidths.js";
9
10
  import { useMeasureLayout } from "./useMeasureLayout.js";
10
- import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
11
- const useNativeDriver = Platform.OS !== 'web';
11
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
12
12
  const Separator = ({
13
13
  width
14
14
  }) => {
@@ -55,8 +55,12 @@ const getComputedTabWidth = ({
55
55
  flattenedPaddingEnd,
56
56
  gap
57
57
  }) => {
58
+ const route = routes[index];
59
+ if (route == null) {
60
+ throw new Error(`Couldn't find a route at index ${index}.`);
61
+ }
58
62
  if (flattenedTabWidth === 'auto') {
59
- return tabWidths[routes[index].key] || 0;
63
+ return tabWidths[route.key] || 0;
60
64
  }
61
65
  switch (typeof flattenedTabWidth) {
62
66
  case 'number':
@@ -70,14 +74,31 @@ const getComputedTabWidth = ({
70
74
  }
71
75
  }
72
76
  if (scrollEnabled) {
73
- return layoutWidth / 5 * 2;
77
+ return tabWidths[route.key] || TAB_MIN_WIDTH;
74
78
  }
75
79
  const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
76
80
  const paddingTotalWidth = convertPaddingPercentToSize(flattenedPaddingStart, layoutWidth) + convertPaddingPercentToSize(flattenedPaddingEnd, layoutWidth);
77
81
  return (layoutWidth - gapTotalWidth - paddingTotalWidth) / routes.length;
78
82
  };
83
+ const calculateSize = (value, referenceWidth) => {
84
+ if (typeof value === 'number') {
85
+ return value;
86
+ }
87
+ if (typeof value === 'string' && value.endsWith('%')) {
88
+ const parsed = parseFloat(value);
89
+ if (Number.isFinite(parsed)) {
90
+ return referenceWidth * (parsed / 100);
91
+ }
92
+ }
93
+ return undefined;
94
+ };
79
95
  const getMaxScrollDistance = (tabBarWidth, layoutWidth) => tabBarWidth - layoutWidth;
80
- const getTranslateX = (scrollAmount, maxScrollDistance, direction) => Animated.multiply(Platform.OS === 'android' && direction === 'rtl' ? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1)) : scrollAmount, direction === 'rtl' ? 1 : -1);
96
+ const getTranslateX = (scrollAmount, maxScrollDistance, direction) => {
97
+ const amount =
98
+ // Android reports scroll from the opposite side in RTL
99
+ Platform.OS === 'android' && direction === 'rtl' ? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1)) : scrollAmount;
100
+ return Animated.multiply(amount, direction === 'rtl' ? 1 : -1);
101
+ };
81
102
  const getTabBarWidth = ({
82
103
  routes,
83
104
  layoutWidth,
@@ -110,8 +131,7 @@ const normalizeScrollValue = ({
110
131
  value,
111
132
  flattenedTabWidth,
112
133
  flattenedPaddingStart,
113
- flattenedPaddingEnd,
114
- direction
134
+ flattenedPaddingEnd
115
135
  }) => {
116
136
  const tabBarWidth = getTabBarWidth({
117
137
  layoutWidth,
@@ -125,11 +145,6 @@ const normalizeScrollValue = ({
125
145
  });
126
146
  const maxDistance = getMaxScrollDistance(tabBarWidth, layoutWidth);
127
147
  const scrollValue = Math.max(Math.min(value, maxDistance), 0);
128
- if (Platform.OS === 'android' && direction === 'rtl') {
129
- // On Android, scroll value is not applied in reverse in RTL
130
- // so we need to manually adjust it to apply correct value
131
- return maxDistance - scrollValue;
132
- }
133
148
  return scrollValue;
134
149
  };
135
150
  const getScrollAmount = ({
@@ -174,8 +189,7 @@ const getScrollAmount = ({
174
189
  scrollEnabled,
175
190
  flattenedTabWidth,
176
191
  flattenedPaddingStart,
177
- flattenedPaddingEnd,
178
- direction
192
+ flattenedPaddingEnd
179
193
  });
180
194
  };
181
195
  const getLabelTextDefault = ({
@@ -196,8 +210,9 @@ const getTestIdDefault = ({
196
210
 
197
211
  // How many items measurements should we update per batch.
198
212
  // Defaults to 10, since that's whats FlatList is using in initialNumToRender.
199
- const MEASURE_PER_BATCH = 10;
213
+ const RENDER_PER_BATCH = 10;
200
214
  export function TabBar({
215
+ variant = 'primary',
201
216
  renderIndicator = renderIndicatorDefault,
202
217
  gap = 0,
203
218
  scrollEnabled,
@@ -224,18 +239,24 @@ export function TabBar({
224
239
  }) {
225
240
  const containerRef = React.useRef(null);
226
241
  const [layout, onLayout] = useMeasureLayout(containerRef);
227
- const [tabWidths, setTabWidths] = React.useState({});
242
+
243
+ // Prioritize measuring tabs upto focused item
244
+ // Since we need those measurements for calculation
245
+ const priorityKeysForLayout = navigationState.routes.slice(0, navigationState.index + 1).map(r => r.key);
246
+ const [tabWidths, onMeasureTabWidth] = useLayoutWidths(priorityKeysForLayout);
247
+ const [labelWidths, onMeasureLabelWidth] = useLayoutWidths(priorityKeysForLayout);
228
248
  const flatListRef = React.useRef(null);
229
249
  const isFirst = React.useRef(true);
230
- const scrollAmount = useAnimatedValue(0);
231
- const measuredTabWidths = React.useRef({});
232
250
  const {
233
251
  routes
234
252
  } = navigationState;
235
253
  const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
236
- const isWidthDynamic = flattenedTabWidth === 'auto';
254
+ const isWidthAuto = flattenedTabWidth === 'auto';
255
+ const isWidthDynamic = isWidthAuto || scrollEnabled && flattenedTabWidth == null;
237
256
  const flattenedPaddingEnd = getFlattenedPaddingEnd(contentContainerStyle);
238
257
  const flattenedPaddingStart = getFlattenedPaddingStart(contentContainerStyle);
258
+ const paddingEnd = convertPaddingPercentToSize(flattenedPaddingEnd, layout.width);
259
+ const paddingStart = convertPaddingPercentToSize(flattenedPaddingStart, layout.width);
239
260
  const scrollOffset = getScrollAmount({
240
261
  layoutWidth: layout.width,
241
262
  routes,
@@ -254,7 +275,7 @@ export function TabBar({
254
275
  isFirst.current = false;
255
276
  return;
256
277
  }
257
- if (isWidthDynamic && !hasMeasuredTabWidths) {
278
+ if (isWidthAuto && !hasMeasuredTabWidths) {
258
279
  return;
259
280
  }
260
281
  if (scrollEnabled) {
@@ -263,7 +284,7 @@ export function TabBar({
263
284
  animated: true
264
285
  });
265
286
  }
266
- }, [hasMeasuredTabWidths, isWidthDynamic, scrollEnabled, scrollOffset]);
287
+ }, [hasMeasuredTabWidths, isWidthAuto, scrollEnabled, scrollOffset]);
267
288
  const tabBarWidth = getTabBarWidth({
268
289
  layoutWidth: layout.width,
269
290
  routes,
@@ -274,107 +295,55 @@ export function TabBar({
274
295
  flattenedPaddingStart,
275
296
  flattenedPaddingEnd
276
297
  });
277
- const separatorsWidth = Math.max(0, routes.length - 1) * gap;
278
- const paddingsWidth = Math.max(0, convertPaddingPercentToSize(flattenedPaddingStart, layout.width) + convertPaddingPercentToSize(flattenedPaddingEnd, layout.width));
279
- const translateX = React.useMemo(() => getTranslateX(scrollAmount, getMaxScrollDistance(tabBarWidth, layout.width), direction), [direction, layout.width, scrollAmount, tabBarWidth]);
298
+ const maxScrollDistance = getMaxScrollDistance(tabBarWidth, layout.width);
299
+ const scrollAmount = useAnimatedValue(0);
300
+ React.useLayoutEffect(() => {
301
+ scrollAmount.setValue(Platform.OS === 'android' && direction === 'rtl' ? maxScrollDistance : 0);
302
+ }, [direction, maxScrollDistance, scrollAmount]);
303
+ const translateX = React.useMemo(() => getTranslateX(scrollAmount, maxScrollDistance, direction), [direction, maxScrollDistance, scrollAmount]);
304
+ const flattenedTabStyle = StyleSheet.flatten(tabStyle);
305
+ const isTabWidthSet = flattenedTabStyle?.width !== undefined;
306
+
307
+ // Calculate the default width for tab for FlatList to work.
308
+ const defaultTabWidth = !isWidthDynamic ? getComputedTabWidth({
309
+ // When `isWidthDynamic` is false, every tab gets the same width and
310
+ // `getComputedTabWidth` ignores `index`, so we compute it once with index 0.
311
+ index: 0,
312
+ layoutWidth: layout.width,
313
+ routes,
314
+ scrollEnabled,
315
+ tabWidths,
316
+ flattenedTabWidth,
317
+ flattenedPaddingStart,
318
+ flattenedPaddingEnd,
319
+ gap
320
+ }) : undefined;
280
321
  const renderItem = React.useCallback(({
281
322
  item: route,
282
323
  index
283
- }) => {
284
- const {
285
- testID = getTestIdDefault({
286
- route
287
- }),
288
- labelText = getLabelTextDefault({
289
- route
290
- }),
291
- accessible = getAccessibleDefault({
292
- route
293
- }),
294
- accessibilityLabel = getAccessibilityLabelDefault({
295
- route
296
- }),
297
- ...rest
298
- } = options?.[route.key] ?? {};
299
- const onLayout = isWidthDynamic ? e => {
300
- measuredTabWidths.current[route.key] = e.nativeEvent.layout.width;
301
-
302
- // When we have measured widths for all of the tabs, we should updates the state
303
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
304
- // If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
305
- if (routes.length > MEASURE_PER_BATCH && index === MEASURE_PER_BATCH && routes.slice(0, MEASURE_PER_BATCH).every(r => typeof measuredTabWidths.current[r.key] === 'number')) {
306
- setTabWidths({
307
- ...measuredTabWidths.current
308
- });
309
- } else if (routes.every(r => typeof measuredTabWidths.current[r.key] === 'number')) {
310
- // When we have measured widths for all of the tabs, we should updates the state
311
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
312
- setTabWidths({
313
- ...measuredTabWidths.current
314
- });
315
- }
316
- } : undefined;
317
- const onPress = () => {
318
- const event = {
319
- route,
320
- defaultPrevented: false,
321
- preventDefault: () => {
322
- event.defaultPrevented = true;
323
- }
324
- };
325
- onTabPress?.(event);
326
- if (event.defaultPrevented) {
327
- return;
328
- }
329
- jumpTo(route.key);
330
- };
331
- const onLongPress = () => onTabLongPress?.({
332
- route
333
- });
334
-
335
- // Calculate the default width for tab for FlatList to work
336
- const defaultTabWidth = !isWidthDynamic ? getComputedTabWidth({
337
- index,
338
- layoutWidth: layout.width,
339
- routes,
340
- scrollEnabled,
341
- tabWidths,
342
- flattenedTabWidth: getFlattenedTabWidth(tabStyle),
343
- flattenedPaddingStart: getFlattenedPaddingStart(contentContainerStyle),
344
- flattenedPaddingEnd: getFlattenedPaddingEnd(contentContainerStyle),
345
- gap
346
- }) : undefined;
347
- const props = {
348
- ...rest,
349
- position,
350
- route,
351
- navigationState,
352
- testID,
353
- labelText,
354
- accessible,
355
- accessibilityLabel,
356
- activeColor,
357
- inactiveColor,
358
- pressColor,
359
- pressOpacity,
360
- onLayout,
361
- onPress,
362
- onLongPress,
363
- style: tabStyle,
364
- defaultTabWidth,
365
- android_ripple
366
- };
367
- return /*#__PURE__*/_jsxs(_Fragment, {
368
- children: [gap > 0 && index > 0 ? /*#__PURE__*/_jsx(Separator, {
369
- width: gap
370
- }) : null, renderTabBarItem ? renderTabBarItem({
371
- key: route.key,
372
- ...props
373
- }) : /*#__PURE__*/_jsx(TabBarItem, {
374
- ...props
375
- }, route.key)]
376
- });
377
- }, [position, navigationState, options, activeColor, inactiveColor, pressColor, pressOpacity, isWidthDynamic, tabStyle, layout, routes, scrollEnabled, tabWidths, contentContainerStyle, gap, android_ripple, renderTabBarItem, onTabPress, jumpTo, onTabLongPress]);
324
+ }) => /*#__PURE__*/_jsx(MemoizedTabBarItemWrapper, {
325
+ route: route,
326
+ index: index,
327
+ option: options?.[route.key],
328
+ position: position,
329
+ navigationState: navigationState,
330
+ variant: variant,
331
+ activeColor: activeColor,
332
+ inactiveColor: inactiveColor,
333
+ pressColor: pressColor,
334
+ pressOpacity: pressOpacity,
335
+ android_ripple: android_ripple,
336
+ tabStyle: tabStyle,
337
+ defaultTabWidth: defaultTabWidth,
338
+ isWidthSet: isTabWidthSet,
339
+ gap: gap,
340
+ onMeasureTabWidth: onMeasureTabWidth,
341
+ onMeasureLabelWidth: onMeasureLabelWidth,
342
+ onTabPress: onTabPress,
343
+ onTabLongPress: onTabLongPress,
344
+ jumpTo: jumpTo,
345
+ renderTabBarItem: renderTabBarItem
346
+ }), [options, position, navigationState, variant, activeColor, inactiveColor, pressColor, pressOpacity, android_ripple, tabStyle, defaultTabWidth, isTabWidthSet, gap, onMeasureTabWidth, onMeasureLabelWidth, onTabPress, onTabLongPress, jumpTo, renderTabBarItem]);
378
347
  const keyExtractor = React.useCallback(item => item.key, []);
379
348
  const contentContainerStyleMemoized = React.useMemo(() => [styles.tabContent, scrollEnabled ? {
380
349
  width: tabBarWidth
@@ -386,27 +355,83 @@ export function TabBar({
386
355
  }
387
356
  }
388
357
  }], {
389
- useNativeDriver
358
+ useNativeDriver: Platform.OS !== 'web'
390
359
  }), [scrollAmount]);
391
- const handleViewableItemsChanged = useLatestCallback(({
392
- changed
393
- }) => {
394
- if (routes.length <= MEASURE_PER_BATCH) {
395
- return;
360
+ const flattenedIndicatorStyle = StyleSheet.flatten(indicatorStyle);
361
+ const defaultIndicatorStyle = variant === 'primary' ? styles.primaryIndicator : styles.secondaryIndicator;
362
+ const tabWidthByIndex = routes.map((_, i) => getComputedTabWidth({
363
+ index: i,
364
+ layoutWidth: layout.width,
365
+ routes,
366
+ scrollEnabled,
367
+ tabWidths,
368
+ flattenedTabWidth,
369
+ flattenedPaddingEnd,
370
+ flattenedPaddingStart,
371
+ gap
372
+ }));
373
+ const indicatorBaseWidths = tabWidthByIndex.map((tabWidth, i) => {
374
+ const customIndicatorWidth = calculateSize(flattenedIndicatorStyle?.width, tabWidth);
375
+ if (customIndicatorWidth != null) {
376
+ return customIndicatorWidth;
396
377
  }
397
- // Get next vievable item
398
- const item = changed[changed.length - 1];
399
- const index = item?.index || 0;
400
- if (item.isViewable && (index % 10 === 0 || index === navigationState.index || index === routes.length - 1)) {
401
- setTabWidths({
402
- ...measuredTabWidths.current
403
- });
378
+ if (variant === 'primary') {
379
+ const route = routes[i];
380
+ if (route == null) {
381
+ throw new Error(`Couldn't find a route at index ${i}.`);
382
+ }
383
+ const labelWidth = labelWidths[route.key];
384
+ return labelWidth ? Math.max(PRIMARY_INDICATOR_MIN_WIDTH, labelWidth) : 0;
385
+ }
386
+ return tabWidth;
387
+ });
388
+ const indicatorMargins = indicatorBaseWidths.map(width => {
389
+ if (!width) {
390
+ return {
391
+ left: 0,
392
+ right: 0
393
+ };
394
+ }
395
+ const marginHorizontal = flattenedIndicatorStyle?.marginHorizontal ?? flattenedIndicatorStyle?.margin;
396
+ const leftMargin = (direction === 'ltr' ? flattenedIndicatorStyle?.marginStart : flattenedIndicatorStyle?.marginEnd) ?? flattenedIndicatorStyle?.marginLeft ?? marginHorizontal;
397
+ const rightMargin = (direction === 'rtl' ? flattenedIndicatorStyle?.marginStart : flattenedIndicatorStyle?.marginEnd) ?? flattenedIndicatorStyle?.marginRight ?? marginHorizontal;
398
+ return {
399
+ left: calculateSize(leftMargin, width) ?? 0,
400
+ right: calculateSize(rightMargin, width) ?? 0
401
+ };
402
+ });
403
+ const indicatorWidths = indicatorBaseWidths.map((width, i) => {
404
+ if (!width) {
405
+ return 0;
406
+ }
407
+ const margin = indicatorMargins[i];
408
+ if (margin == null) {
409
+ throw new Error(`Couldn't find an indicator margin at index ${i}.`);
404
410
  }
411
+ return Math.max(0, width - margin.left - margin.right);
412
+ });
413
+ const indicatorOffsets = tabWidthByIndex.map((tabWidth, i) => {
414
+ const precedingTabsWidth = tabWidthByIndex.slice(0, i).reduce((sum, width) => sum + width, 0);
415
+ const tabStart = precedingTabsWidth + gap * i;
416
+ const customIndicatorWidth = calculateSize(flattenedIndicatorStyle?.width, tabWidth);
417
+ const margin = indicatorMargins[i];
418
+ if (margin == null) {
419
+ throw new Error(`Couldn't find an indicator margin at index ${i}.`);
420
+ }
421
+ const indicatorBaseWidth = indicatorBaseWidths[i];
422
+ if (indicatorBaseWidth == null) {
423
+ throw new Error(`Couldn't find an indicator width at index ${i}.`);
424
+ }
425
+ const shouldCenterIndicator = variant === 'primary' || customIndicatorWidth != null && (flattenedIndicatorStyle?.margin === 'auto' || flattenedIndicatorStyle?.marginHorizontal === 'auto');
426
+ const baseOffset = shouldCenterIndicator ? (tabWidth - indicatorBaseWidth) / 2 : 0;
427
+ return tabStart + baseOffset + margin.left;
405
428
  });
406
429
  return /*#__PURE__*/_jsxs(Animated.View, {
407
430
  ref: containerRef,
408
431
  onLayout: onLayout,
409
- style: [styles.tabBar, style],
432
+ style: [styles.tabBar, {
433
+ direction
434
+ }, style],
410
435
  children: [/*#__PURE__*/_jsx(Animated.View, {
411
436
  style: [styles.indicatorContainer, scrollEnabled ? {
412
437
  transform: [{
@@ -416,27 +441,17 @@ export function TabBar({
416
441
  width: tabBarWidth
417
442
  } : null, indicatorContainerStyle],
418
443
  children: renderIndicator({
444
+ variant,
419
445
  position,
420
446
  navigationState,
421
447
  jumpTo,
422
448
  direction,
423
- width: isWidthDynamic ? 'auto' : Math.max(0, (tabBarWidth - separatorsWidth - paddingsWidth) / routes.length),
424
- style: [indicatorStyle, {
425
- start: flattenedPaddingStart,
426
- end: flattenedPaddingEnd
427
- }],
428
- getTabWidth: i => getComputedTabWidth({
429
- index: i,
430
- layoutWidth: layout.width,
431
- routes,
432
- scrollEnabled,
433
- tabWidths,
434
- flattenedTabWidth,
435
- flattenedPaddingEnd,
436
- flattenedPaddingStart,
437
- gap
438
- }),
439
- gap
449
+ widths: indicatorWidths,
450
+ offsets: indicatorOffsets,
451
+ style: [defaultIndicatorStyle, indicatorStyle, {
452
+ start: paddingStart,
453
+ end: paddingEnd
454
+ }]
440
455
  })
441
456
  }), /*#__PURE__*/_jsx(View, {
442
457
  style: styles.scroll,
@@ -448,8 +463,7 @@ export function TabBar({
448
463
  keyboardShouldPersistTaps: "handled",
449
464
  scrollEnabled: scrollEnabled,
450
465
  bounces: bounces,
451
- initialNumToRender: MEASURE_PER_BATCH,
452
- onViewableItemsChanged: handleViewableItemsChanged,
466
+ initialNumToRender: RENDER_PER_BATCH,
453
467
  alwaysBounceHorizontal: false,
454
468
  scrollsToTop: false,
455
469
  showsHorizontalScrollIndicator: false,
@@ -466,6 +480,105 @@ export function TabBar({
466
480
  })]
467
481
  });
468
482
  }
483
+ function TabBarItemWrapper({
484
+ route,
485
+ index,
486
+ option,
487
+ position,
488
+ navigationState,
489
+ variant,
490
+ activeColor,
491
+ inactiveColor,
492
+ pressColor,
493
+ pressOpacity,
494
+ android_ripple,
495
+ tabStyle,
496
+ defaultTabWidth,
497
+ isWidthSet,
498
+ gap,
499
+ onMeasureTabWidth,
500
+ onMeasureLabelWidth,
501
+ onTabPress,
502
+ onTabLongPress,
503
+ jumpTo,
504
+ renderTabBarItem
505
+ }) {
506
+ const {
507
+ testID = getTestIdDefault({
508
+ route
509
+ }),
510
+ labelText = getLabelTextDefault({
511
+ route
512
+ }),
513
+ accessible = getAccessibleDefault({
514
+ route
515
+ }),
516
+ accessibilityLabel = getAccessibilityLabelDefault({
517
+ route
518
+ }),
519
+ ...rest
520
+ } = option ?? {};
521
+ const onMeasureLayout = React.useCallback(({
522
+ width
523
+ }) => onMeasureTabWidth(route.key, width), [route.key, onMeasureTabWidth]);
524
+ const onMeasureLabelLayout = React.useCallback(({
525
+ width
526
+ }) => onMeasureLabelWidth(route.key, width), [route.key, onMeasureLabelWidth]);
527
+ const onPress = React.useCallback(() => {
528
+ const event = {
529
+ route,
530
+ defaultPrevented: false,
531
+ preventDefault: () => {
532
+ event.defaultPrevented = true;
533
+ }
534
+ };
535
+ onTabPress?.(event);
536
+ if (event.defaultPrevented) {
537
+ return;
538
+ }
539
+ jumpTo(route.key);
540
+ }, [route, onTabPress, jumpTo]);
541
+ const onLongPress = React.useCallback(() => onTabLongPress?.({
542
+ route
543
+ }), [route, onTabLongPress]);
544
+ const style = React.useMemo(() => [tabStyle, isWidthSet ? null : defaultTabWidth !== undefined ? {
545
+ width: defaultTabWidth
546
+ } : {
547
+ minWidth: TAB_MIN_WIDTH
548
+ }], [tabStyle, isWidthSet, defaultTabWidth]);
549
+ const props = {
550
+ ...rest,
551
+ position,
552
+ route,
553
+ navigationState,
554
+ testID,
555
+ labelText,
556
+ accessible,
557
+ accessibilityLabel,
558
+ variant,
559
+ activeColor,
560
+ inactiveColor,
561
+ pressColor,
562
+ pressOpacity,
563
+ onMeasureLayout,
564
+ onMeasureLabelLayout,
565
+ onPress,
566
+ onLongPress,
567
+ style,
568
+ android_ripple
569
+ };
570
+ return /*#__PURE__*/_jsxs(_Fragment, {
571
+ children: [gap > 0 && index > 0 ? /*#__PURE__*/_jsx(Separator, {
572
+ width: gap
573
+ }) : null, renderTabBarItem ? renderTabBarItem({
574
+ key: route.key,
575
+ ...props
576
+ }) : /*#__PURE__*/_jsx(TabBarItem, {
577
+ ...props
578
+ }, route.key)]
579
+ });
580
+ }
581
+ const MemoizedTabBarItemWrapper = /*#__PURE__*/React.memo(TabBarItemWrapper);
469
582
  const styles = StyleSheet.create({
470
583
  scroll: {
471
584
  overflow: Platform.select({
@@ -475,22 +588,9 @@ const styles = StyleSheet.create({
475
588
  },
476
589
  tabBar: {
477
590
  zIndex: 1,
478
- backgroundColor: '#fff',
479
- elevation: 4,
480
- ...Platform.select({
481
- default: {
482
- shadowColor: 'black',
483
- shadowOpacity: 0.1,
484
- shadowRadius: StyleSheet.hairlineWidth,
485
- shadowOffset: {
486
- height: StyleSheet.hairlineWidth,
487
- width: 0
488
- }
489
- },
490
- web: {
491
- boxShadow: '0 1px 1px rgba(0, 0, 0, 0.1)'
492
- }
493
- })
591
+ backgroundColor: TAB_BAR_BACKGROUND_COLOR,
592
+ borderBottomColor: TAB_BAR_BORDER_COLOR,
593
+ borderBottomWidth: 1
494
594
  },
495
595
  tabContent: {
496
596
  flexGrow: 1,
@@ -504,6 +604,16 @@ const styles = StyleSheet.create({
504
604
  end: 0,
505
605
  bottom: 0,
506
606
  pointerEvents: 'none'
607
+ },
608
+ primaryIndicator: {
609
+ height: 3,
610
+ borderTopLeftRadius: 3,
611
+ borderTopRightRadius: 3
612
+ },
613
+ secondaryIndicator: {
614
+ height: 2,
615
+ borderTopLeftRadius: 0,
616
+ borderTopRightRadius: 0
507
617
  }
508
618
  });
509
619
  //# sourceMappingURL=TabBar.js.map