@amazon-devices/react-navigation__react-native-tab-view 7.0.9000000000 → 8.0.9000000000-rn-83

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 (127) hide show
  1. package/README.md +2 -646
  2. package/SystemBundles/adbcd18d13758315355457809fdb5587894f9f3c2c9838769a5a47bf860685c8.bundle +8564 -0
  3. package/SystemBundles/adbcd18d13758315355457809fdb5587894f9f3c2c9838769a5a47bf860685c8.map +3217 -0
  4. package/SystemModules/amzn__react-navigation__react-native-tab-view-8.modules.txt +12 -0
  5. package/SystemModules/amzn__react-navigation__react-native-tab-view-8.private.modules.txt +63 -0
  6. package/SystemModules/keplerscript-system-bundle-config.json +4 -4
  7. package/kepler-compatibility.json +2 -2
  8. package/lib/module/Pager.android.js +3 -1
  9. package/lib/module/Pager.android.js.map +1 -1
  10. package/lib/module/Pager.ios.js +3 -1
  11. package/lib/module/Pager.ios.js.map +1 -1
  12. package/lib/module/Pager.js +3 -1
  13. package/lib/module/Pager.js.map +1 -1
  14. package/lib/module/PagerViewAdapter.js +29 -22
  15. package/lib/module/PagerViewAdapter.js.map +1 -1
  16. package/lib/module/PanResponderAdapter.js +46 -32
  17. package/lib/module/PanResponderAdapter.js.map +1 -1
  18. package/lib/module/PlatformPressable.js +38 -12
  19. package/lib/module/PlatformPressable.js.map +1 -1
  20. package/lib/module/SceneMap.js +7 -4
  21. package/lib/module/SceneMap.js.map +1 -1
  22. package/lib/module/SceneView.js +16 -14
  23. package/lib/module/SceneView.js.map +1 -1
  24. package/lib/module/TabBar.js +232 -156
  25. package/lib/module/TabBar.js.map +1 -1
  26. package/lib/module/TabBarIndicator.js +47 -26
  27. package/lib/module/TabBarIndicator.js.map +1 -1
  28. package/lib/module/TabBarItem.js +106 -107
  29. package/lib/module/TabBarItem.js.map +1 -1
  30. package/lib/module/TabBarItemLabel.js +33 -0
  31. package/lib/module/TabBarItemLabel.js.map +1 -0
  32. package/lib/module/TabView.js +91 -63
  33. package/lib/module/TabView.js.map +1 -1
  34. package/lib/module/index.js +7 -5
  35. package/lib/module/index.js.map +1 -1
  36. package/lib/module/package.json +1 -0
  37. package/lib/module/types.js +2 -0
  38. package/lib/module/types.js.map +1 -1
  39. package/lib/module/useAnimatedValue.js +4 -2
  40. package/lib/module/useAnimatedValue.js.map +1 -1
  41. package/lib/typescript/package.json +1 -0
  42. package/lib/typescript/src/Pager.android.d.ts +1 -1
  43. package/lib/typescript/src/Pager.android.d.ts.map +1 -1
  44. package/lib/typescript/src/Pager.d.ts +1 -1
  45. package/lib/typescript/src/Pager.d.ts.map +1 -1
  46. package/lib/typescript/src/Pager.ios.d.ts +1 -1
  47. package/lib/typescript/src/Pager.ios.d.ts.map +1 -1
  48. package/lib/typescript/src/PagerViewAdapter.d.ts +4 -1
  49. package/lib/typescript/src/PagerViewAdapter.d.ts.map +1 -1
  50. package/lib/typescript/src/PanResponderAdapter.d.ts +4 -1
  51. package/lib/typescript/src/PanResponderAdapter.d.ts.map +1 -1
  52. package/lib/typescript/src/PlatformPressable.d.ts +6 -8
  53. package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
  54. package/lib/typescript/src/SceneMap.d.ts +2 -2
  55. package/lib/typescript/src/SceneMap.d.ts.map +1 -1
  56. package/lib/typescript/src/SceneView.d.ts +2 -2
  57. package/lib/typescript/src/SceneView.d.ts.map +1 -1
  58. package/lib/typescript/src/TabBar.d.ts +7 -19
  59. package/lib/typescript/src/TabBar.d.ts.map +1 -1
  60. package/lib/typescript/src/TabBarIndicator.d.ts +8 -6
  61. package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
  62. package/lib/typescript/src/TabBarItem.d.ts +4 -22
  63. package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
  64. package/lib/typescript/src/TabBarItemLabel.d.ts +11 -0
  65. package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -0
  66. package/lib/typescript/src/TabView.d.ts +14 -8
  67. package/lib/typescript/src/TabView.d.ts.map +1 -1
  68. package/lib/typescript/src/index.d.ts +6 -6
  69. package/lib/typescript/src/index.d.ts.map +1 -1
  70. package/lib/typescript/src/types.d.ts +29 -1
  71. package/lib/typescript/src/types.d.ts.map +1 -1
  72. package/lib/typescript/src/useAnimatedValue.d.ts +1 -1
  73. package/lib/typescript/src/useAnimatedValue.d.ts.map +1 -1
  74. package/oss-attribution/attribution.txt +0 -0
  75. package/oss-attribution/licenseInfos.json +1 -0
  76. package/package.json +93 -1
  77. package/src/Pager.android.tsx +1 -1
  78. package/src/Pager.ios.tsx +1 -1
  79. package/src/Pager.tsx +1 -1
  80. package/src/PagerViewAdapter.tsx +26 -21
  81. package/src/PanResponderAdapter.tsx +32 -28
  82. package/src/PlatformPressable.tsx +59 -10
  83. package/src/SceneMap.tsx +3 -3
  84. package/src/SceneView.tsx +6 -7
  85. package/src/TabBar.tsx +282 -193
  86. package/src/TabBarIndicator.tsx +65 -28
  87. package/src/TabBarItem.tsx +119 -130
  88. package/src/TabBarItemLabel.tsx +42 -0
  89. package/src/TabView.tsx +59 -16
  90. package/src/index.tsx +11 -6
  91. package/src/types.tsx +29 -1
  92. package/src/useAnimatedValue.tsx +2 -2
  93. package/SystemBundles/df77923df6c02233e2967296227684cd416c9a363cc8fc6fd81f573829bc74e9.bundle +0 -13783
  94. package/SystemBundles/df77923df6c02233e2967296227684cd416c9a363cc8fc6fd81f573829bc74e9.map +0 -4787
  95. package/SystemModules/amzn__react-navigation__react-native-tab-view-7.modules.txt +0 -41
  96. package/SystemModules/amzn__react-navigation__react-native-tab-view-7.private.modules.txt +0 -96
  97. package/THIRD_PARTY_NOTICES.txt +0 -24
  98. package/lib/commonjs/Pager.android.js +0 -14
  99. package/lib/commonjs/Pager.android.js.map +0 -1
  100. package/lib/commonjs/Pager.ios.js +0 -14
  101. package/lib/commonjs/Pager.ios.js.map +0 -1
  102. package/lib/commonjs/Pager.js +0 -14
  103. package/lib/commonjs/Pager.js.map +0 -1
  104. package/lib/commonjs/PagerViewAdapter.js +0 -132
  105. package/lib/commonjs/PagerViewAdapter.js.map +0 -1
  106. package/lib/commonjs/PanResponderAdapter.js +0 -204
  107. package/lib/commonjs/PanResponderAdapter.js.map +0 -1
  108. package/lib/commonjs/PlatformPressable.js +0 -43
  109. package/lib/commonjs/PlatformPressable.js.map +0 -1
  110. package/lib/commonjs/SceneMap.js +0 -29
  111. package/lib/commonjs/SceneMap.js.map +0 -1
  112. package/lib/commonjs/SceneView.js +0 -78
  113. package/lib/commonjs/SceneView.js.map +0 -1
  114. package/lib/commonjs/TabBar.js +0 -405
  115. package/lib/commonjs/TabBar.js.map +0 -1
  116. package/lib/commonjs/TabBarIndicator.js +0 -110
  117. package/lib/commonjs/TabBarIndicator.js.map +0 -1
  118. package/lib/commonjs/TabBarItem.js +0 -227
  119. package/lib/commonjs/TabBarItem.js.map +0 -1
  120. package/lib/commonjs/TabView.js +0 -122
  121. package/lib/commonjs/TabView.js.map +0 -1
  122. package/lib/commonjs/index.js +0 -42
  123. package/lib/commonjs/index.js.map +0 -1
  124. package/lib/commonjs/types.js +0 -6
  125. package/lib/commonjs/types.js.map +0 -1
  126. package/lib/commonjs/useAnimatedValue.js +0 -18
  127. package/lib/commonjs/useAnimatedValue.js.map +0 -1
package/src/TabBar.tsx CHANGED
@@ -1,33 +1,37 @@
1
1
  import * as React from 'react';
2
2
  import {
3
3
  Animated,
4
- DimensionValue,
4
+ type DimensionValue,
5
5
  FlatList,
6
6
  I18nManager,
7
- LayoutChangeEvent,
8
- ListRenderItemInfo,
7
+ type LayoutChangeEvent,
8
+ type ListRenderItemInfo,
9
9
  Platform,
10
- PressableAndroidRippleConfig,
11
- StyleProp,
10
+ type PressableAndroidRippleConfig,
11
+ type StyleProp,
12
12
  StyleSheet,
13
- TextStyle,
14
13
  View,
15
- ViewStyle,
16
- ViewToken,
14
+ type ViewStyle,
15
+ type ViewToken,
17
16
  } from 'react-native';
18
17
  import useLatestCallback from 'use-latest-callback';
19
18
 
20
- import TabBarIndicator, { Props as IndicatorProps } from './TabBarIndicator';
21
- import TabBarItem, { Props as TabBarItemProps } from './TabBarItem';
19
+ import {
20
+ type Props as IndicatorProps,
21
+ TabBarIndicator,
22
+ } from './TabBarIndicator';
23
+ import { type Props as TabBarItemProps, TabBarItem } from './TabBarItem';
22
24
  import type {
23
25
  Event,
24
26
  Layout,
27
+ LocaleDirection,
25
28
  NavigationState,
26
29
  Route,
27
30
  Scene,
28
31
  SceneRendererProps,
32
+ TabDescriptor,
29
33
  } from './types';
30
- import useAnimatedValue from './useAnimatedValue';
34
+ import { useAnimatedValue } from './useAnimatedValue';
31
35
 
32
36
  export type Props<T extends Route> = SceneRendererProps & {
33
37
  navigationState: NavigationState<T>;
@@ -37,23 +41,7 @@ export type Props<T extends Route> = SceneRendererProps & {
37
41
  inactiveColor?: string;
38
42
  pressColor?: string;
39
43
  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;
44
+ options?: Record<string, TabDescriptor<T>>;
57
45
  renderIndicator?: (props: IndicatorProps<T>) => React.ReactNode;
58
46
  renderTabBarItem?: (
59
47
  props: TabBarItemProps<T> & { key: string }
@@ -63,15 +51,15 @@ export type Props<T extends Route> = SceneRendererProps & {
63
51
  tabStyle?: StyleProp<ViewStyle>;
64
52
  indicatorStyle?: StyleProp<ViewStyle>;
65
53
  indicatorContainerStyle?: StyleProp<ViewStyle>;
66
- labelStyle?: StyleProp<TextStyle>;
67
54
  contentContainerStyle?: StyleProp<ViewStyle>;
68
55
  style?: StyleProp<ViewStyle>;
56
+ direction?: LocaleDirection;
69
57
  gap?: number;
70
58
  testID?: string;
71
59
  android_ripple?: PressableAndroidRippleConfig;
72
60
  };
73
61
 
74
- type FlattenedTabWidth = DimensionValue | undefined;
62
+ const useNativeDriver = Platform.OS !== 'web';
75
63
 
76
64
  const Separator = ({ width }: { width: number }) => {
77
65
  return <View style={{ width }} />;
@@ -83,13 +71,56 @@ const getFlattenedTabWidth = (style: StyleProp<ViewStyle>) => {
83
71
  return tabStyle?.width;
84
72
  };
85
73
 
74
+ const getFlattenedPaddingStart = (style: StyleProp<ViewStyle>) => {
75
+ const flattenStyle = StyleSheet.flatten(style);
76
+
77
+ return flattenStyle
78
+ ? flattenStyle.paddingLeft ||
79
+ flattenStyle.paddingStart ||
80
+ flattenStyle.paddingHorizontal ||
81
+ 0
82
+ : 0;
83
+ };
84
+
85
+ const getFlattenedPaddingEnd = (style: StyleProp<ViewStyle>) => {
86
+ const flattenStyle = StyleSheet.flatten(style);
87
+
88
+ return flattenStyle
89
+ ? flattenStyle.paddingRight ||
90
+ flattenStyle.paddingEnd ||
91
+ flattenStyle.paddingHorizontal ||
92
+ 0
93
+ : 0;
94
+ };
95
+
96
+ const convertPaddingPercentToSize = (
97
+ value: DimensionValue | undefined,
98
+ layout: Layout
99
+ ): number => {
100
+ switch (typeof value) {
101
+ case 'number':
102
+ return value;
103
+ case 'string':
104
+ if (value.endsWith('%')) {
105
+ const width = parseFloat(value);
106
+ if (Number.isFinite(width)) {
107
+ return layout.width * (width / 100);
108
+ }
109
+ }
110
+ }
111
+ return 0;
112
+ };
113
+
86
114
  const getComputedTabWidth = (
87
115
  index: number,
88
116
  layout: Layout,
89
117
  routes: Route[],
90
118
  scrollEnabled: boolean | undefined,
91
119
  tabWidths: { [key: string]: number },
92
- flattenedWidth: FlattenedTabWidth
120
+ flattenedWidth: DimensionValue | undefined,
121
+ flattenedPaddingStart: DimensionValue | undefined,
122
+ flattenedPaddingEnd: DimensionValue | undefined,
123
+ gap?: number
93
124
  ) => {
94
125
  if (flattenedWidth === 'auto') {
95
126
  return tabWidths[routes[index].key] || 0;
@@ -110,7 +141,13 @@ const getComputedTabWidth = (
110
141
  if (scrollEnabled) {
111
142
  return (layout.width / 5) * 2;
112
143
  }
113
- return layout.width / routes.length;
144
+
145
+ const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
146
+ const paddingTotalWidth =
147
+ convertPaddingPercentToSize(flattenedPaddingStart, layout) +
148
+ convertPaddingPercentToSize(flattenedPaddingEnd, layout);
149
+
150
+ return (layout.width - gapTotalWidth - paddingTotalWidth) / routes.length;
114
151
  };
115
152
 
116
153
  const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
@@ -118,13 +155,14 @@ const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
118
155
 
119
156
  const getTranslateX = (
120
157
  scrollAmount: Animated.Value,
121
- maxScrollDistance: number
158
+ maxScrollDistance: number,
159
+ direction: LocaleDirection
122
160
  ) =>
123
161
  Animated.multiply(
124
- Platform.OS === 'android' && I18nManager.isRTL
162
+ Platform.OS === 'android' && direction === 'rtl'
125
163
  ? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1))
126
164
  : scrollAmount,
127
- I18nManager.isRTL ? 1 : -1
165
+ direction === 'rtl' ? 1 : -1
128
166
  );
129
167
 
130
168
  const getTabBarWidth = <T extends Route>({
@@ -133,26 +171,39 @@ const getTabBarWidth = <T extends Route>({
133
171
  gap,
134
172
  scrollEnabled,
135
173
  flattenedTabWidth,
174
+ flattenedPaddingStart,
175
+ flattenedPaddingEnd,
136
176
  tabWidths,
137
177
  }: Pick<Props<T>, 'navigationState' | 'gap' | 'layout' | 'scrollEnabled'> & {
138
178
  tabWidths: Record<string, number>;
139
- flattenedTabWidth: FlattenedTabWidth;
179
+ flattenedPaddingStart: DimensionValue | undefined;
180
+ flattenedPaddingEnd: DimensionValue | undefined;
181
+ flattenedTabWidth: DimensionValue | undefined;
140
182
  }) => {
141
183
  const { routes } = navigationState;
142
184
 
185
+ const paddingsWidth = Math.max(
186
+ 0,
187
+ convertPaddingPercentToSize(flattenedPaddingStart, layout) +
188
+ convertPaddingPercentToSize(flattenedPaddingEnd, layout)
189
+ );
190
+
143
191
  return routes.reduce<number>(
144
192
  (acc, _, i) =>
145
193
  acc +
146
- (i > 0 ? gap ?? 0 : 0) +
194
+ (i > 0 ? (gap ?? 0) : 0) +
147
195
  getComputedTabWidth(
148
196
  i,
149
197
  layout,
150
198
  routes,
151
199
  scrollEnabled,
152
200
  tabWidths,
153
- flattenedTabWidth
201
+ flattenedTabWidth,
202
+ flattenedPaddingStart,
203
+ flattenedPaddingEnd,
204
+ gap
154
205
  ),
155
- 0
206
+ paddingsWidth
156
207
  );
157
208
  };
158
209
 
@@ -164,10 +215,16 @@ const normalizeScrollValue = <T extends Route>({
164
215
  tabWidths,
165
216
  value,
166
217
  flattenedTabWidth,
218
+ flattenedPaddingStart,
219
+ flattenedPaddingEnd,
220
+ direction,
167
221
  }: Pick<Props<T>, 'layout' | 'navigationState' | 'gap' | 'scrollEnabled'> & {
168
222
  tabWidths: Record<string, number>;
169
223
  value: number;
170
- flattenedTabWidth: FlattenedTabWidth;
224
+ flattenedTabWidth: DimensionValue | undefined;
225
+ flattenedPaddingStart: DimensionValue | undefined;
226
+ flattenedPaddingEnd: DimensionValue | undefined;
227
+ direction: LocaleDirection;
171
228
  }) => {
172
229
  const tabBarWidth = getTabBarWidth({
173
230
  layout,
@@ -176,11 +233,13 @@ const normalizeScrollValue = <T extends Route>({
176
233
  gap,
177
234
  scrollEnabled,
178
235
  flattenedTabWidth,
236
+ flattenedPaddingStart,
237
+ flattenedPaddingEnd,
179
238
  });
180
239
  const maxDistance = getMaxScrollDistance(tabBarWidth, layout.width);
181
240
  const scrollValue = Math.max(Math.min(value, maxDistance), 0);
182
241
 
183
- if (Platform.OS === 'android' && I18nManager.isRTL) {
242
+ if (Platform.OS === 'android' && direction === 'rtl') {
184
243
  // On Android, scroll value is not applied in reverse in RTL
185
244
  // so we need to manually adjust it to apply correct value
186
245
  return maxDistance - scrollValue;
@@ -196,10 +255,21 @@ const getScrollAmount = <T extends Route>({
196
255
  scrollEnabled,
197
256
  flattenedTabWidth,
198
257
  tabWidths,
258
+ flattenedPaddingStart,
259
+ flattenedPaddingEnd,
260
+ direction,
199
261
  }: Pick<Props<T>, 'layout' | 'navigationState' | 'scrollEnabled' | 'gap'> & {
200
262
  tabWidths: Record<string, number>;
201
- flattenedTabWidth: FlattenedTabWidth;
263
+ flattenedTabWidth: DimensionValue | undefined;
264
+ flattenedPaddingStart: DimensionValue | undefined;
265
+ flattenedPaddingEnd: DimensionValue | undefined;
266
+ direction: LocaleDirection;
202
267
  }) => {
268
+ const paddingInitial =
269
+ direction === 'rtl'
270
+ ? convertPaddingPercentToSize(flattenedPaddingEnd, layout)
271
+ : convertPaddingPercentToSize(flattenedPaddingStart, layout);
272
+
203
273
  const centerDistance = Array.from({
204
274
  length: navigationState.index + 1,
205
275
  }).reduce<number>((total, _, i) => {
@@ -209,18 +279,20 @@ const getScrollAmount = <T extends Route>({
209
279
  navigationState.routes,
210
280
  scrollEnabled,
211
281
  tabWidths,
212
- flattenedTabWidth
282
+ flattenedTabWidth,
283
+ flattenedPaddingStart,
284
+ flattenedPaddingEnd,
285
+ gap
213
286
  );
214
287
 
215
288
  // To get the current index centered we adjust scroll amount by width of indexes
216
289
  // 0 through (i - 1) and add half the width of current index i
217
290
  return (
218
291
  total +
219
- (navigationState.index === i
220
- ? (tabWidth + (gap ?? 0)) / 2
221
- : tabWidth + (gap ?? 0))
292
+ (i > 0 ? (gap ?? 0) : 0) +
293
+ (navigationState.index === i ? tabWidth / 2 : tabWidth)
222
294
  );
223
- }, 0);
295
+ }, paddingInitial);
224
296
 
225
297
  const scrollAmount = centerDistance - layout.width / 2;
226
298
 
@@ -232,9 +304,11 @@ const getScrollAmount = <T extends Route>({
232
304
  gap,
233
305
  scrollEnabled,
234
306
  flattenedTabWidth,
307
+ flattenedPaddingStart,
308
+ flattenedPaddingEnd,
309
+ direction,
235
310
  });
236
311
  };
237
-
238
312
  const getLabelTextDefault = ({ route }: Scene<Route>) => route.title;
239
313
 
240
314
  const getAccessibleDefault = ({ route }: Scene<Route>) =>
@@ -244,8 +318,8 @@ const getAccessibilityLabelDefault = ({ route }: Scene<Route>) =>
244
318
  typeof route.accessibilityLabel === 'string'
245
319
  ? route.accessibilityLabel
246
320
  : typeof route.title === 'string'
247
- ? route.title
248
- : undefined;
321
+ ? route.title
322
+ : undefined;
249
323
 
250
324
  const renderIndicatorDefault = (props: IndicatorProps<Route>) => (
251
325
  <TabBarIndicator {...props} />
@@ -257,11 +331,7 @@ const getTestIdDefault = ({ route }: Scene<Route>) => route.testID;
257
331
  // Defaults to 10, since that's whats FlatList is using in initialNumToRender.
258
332
  const MEASURE_PER_BATCH = 10;
259
333
 
260
- export default function TabBar<T extends Route>({
261
- getLabelText = getLabelTextDefault,
262
- getAccessible = getAccessibleDefault,
263
- getAccessibilityLabel = getAccessibilityLabelDefault,
264
- getTestID = getTestIdDefault,
334
+ export function TabBar<T extends Route>({
265
335
  renderIndicator = renderIndicatorDefault,
266
336
  gap = 0,
267
337
  scrollEnabled,
@@ -274,30 +344,32 @@ export default function TabBar<T extends Route>({
274
344
  inactiveColor,
275
345
  indicatorContainerStyle,
276
346
  indicatorStyle,
277
- labelStyle,
278
347
  onTabLongPress,
279
348
  onTabPress,
280
349
  pressColor,
281
350
  pressOpacity,
282
- renderBadge,
283
- renderIcon,
284
- renderLabel,
351
+ direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
285
352
  renderTabBarItem,
286
353
  style,
287
354
  tabStyle,
355
+ layout: propLayout,
288
356
  testID,
289
357
  android_ripple,
358
+ options,
290
359
  }: Props<T>) {
291
- const [layout, setLayout] = React.useState<Layout>({ width: 0, height: 0 });
360
+ const [layout, setLayout] = React.useState<Layout>(
361
+ propLayout ?? { width: 0, height: 0 }
362
+ );
292
363
  const [tabWidths, setTabWidths] = React.useState<Record<string, number>>({});
293
364
  const flatListRef = React.useRef<FlatList | null>(null);
294
365
  const isFirst = React.useRef(true);
295
366
  const scrollAmount = useAnimatedValue(0);
296
367
  const measuredTabWidths = React.useRef<Record<string, number>>({});
297
-
298
368
  const { routes } = navigationState;
299
369
  const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
300
370
  const isWidthDynamic = flattenedTabWidth === 'auto';
371
+ const flattenedPaddingEnd = getFlattenedPaddingEnd(contentContainerStyle);
372
+ const flattenedPaddingStart = getFlattenedPaddingStart(contentContainerStyle);
301
373
  const scrollOffset = getScrollAmount({
302
374
  layout,
303
375
  navigationState,
@@ -305,6 +377,9 @@ export default function TabBar<T extends Route>({
305
377
  gap,
306
378
  scrollEnabled,
307
379
  flattenedTabWidth,
380
+ flattenedPaddingStart,
381
+ flattenedPaddingEnd,
382
+ direction,
308
383
  });
309
384
 
310
385
  const hasMeasuredTabWidths =
@@ -348,139 +423,154 @@ export default function TabBar<T extends Route>({
348
423
  gap,
349
424
  scrollEnabled,
350
425
  flattenedTabWidth,
426
+ flattenedPaddingStart,
427
+ flattenedPaddingEnd,
351
428
  });
352
429
 
353
430
  const separatorsWidth = Math.max(0, routes.length - 1) * gap;
354
- const separatorPercent = (separatorsWidth / tabBarWidth) * 100;
355
- const tabBarWidthPercent = `${routes.length * 40}%`;
431
+ const paddingsWidth = Math.max(
432
+ 0,
433
+ convertPaddingPercentToSize(flattenedPaddingStart, layout) +
434
+ convertPaddingPercentToSize(flattenedPaddingEnd, layout)
435
+ );
356
436
 
357
437
  const translateX = React.useMemo(
358
438
  () =>
359
439
  getTranslateX(
360
440
  scrollAmount,
361
- getMaxScrollDistance(tabBarWidth, layout.width)
441
+ getMaxScrollDistance(tabBarWidth, layout.width),
442
+ direction
362
443
  ),
363
- [layout.width, scrollAmount, tabBarWidth]
444
+ [direction, layout.width, scrollAmount, tabBarWidth]
364
445
  );
365
446
 
366
447
  const renderItem = React.useCallback(
367
448
  ({ item: route, index }: ListRenderItemInfo<T>) => {
368
- const props: TabBarItemProps<T> & { key: string } = {
369
- key: route.key,
370
- position: position,
371
- route: route,
372
- navigationState: navigationState,
373
- getAccessibilityLabel: getAccessibilityLabel,
374
- getAccessible: getAccessible,
375
- getLabelText: getLabelText,
376
- getTestID: getTestID,
377
- renderBadge: renderBadge,
378
- renderIcon: renderIcon,
379
- renderLabel: renderLabel,
380
- activeColor: activeColor,
381
- inactiveColor: inactiveColor,
382
- pressColor: pressColor,
383
- pressOpacity: pressOpacity,
384
- onLayout: isWidthDynamic
385
- ? (e: LayoutChangeEvent) => {
386
- measuredTabWidths.current[route.key] = e.nativeEvent.layout.width;
387
-
388
- // When we have measured widths for all of the tabs, we should updates the state
389
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
390
- // If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
391
- if (
392
- routes.length > MEASURE_PER_BATCH &&
393
- index === MEASURE_PER_BATCH &&
394
- routes
395
- .slice(0, MEASURE_PER_BATCH)
396
- .every(
397
- (r) => typeof measuredTabWidths.current[r.key] === 'number'
398
- )
399
- ) {
400
- setTabWidths({ ...measuredTabWidths.current });
401
- } else if (
402
- routes.every(
449
+ const {
450
+ testID = getTestIdDefault({ route }),
451
+ labelText = getLabelTextDefault({ route }),
452
+ accessible = getAccessibleDefault({ route }),
453
+ accessibilityLabel = getAccessibilityLabelDefault({ route }),
454
+ ...rest
455
+ } = options?.[route.key] ?? {};
456
+
457
+ const onLayout = isWidthDynamic
458
+ ? (e: LayoutChangeEvent) => {
459
+ measuredTabWidths.current[route.key] = e.nativeEvent.layout.width;
460
+
461
+ // When we have measured widths for all of the tabs, we should updates the state
462
+ // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
463
+ // If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
464
+ if (
465
+ routes.length > MEASURE_PER_BATCH &&
466
+ index === MEASURE_PER_BATCH &&
467
+ routes
468
+ .slice(0, MEASURE_PER_BATCH)
469
+ .every(
403
470
  (r) => typeof measuredTabWidths.current[r.key] === 'number'
404
471
  )
405
- ) {
406
- // When we have measured widths for all of the tabs, we should updates the state
407
- // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
408
- setTabWidths({ ...measuredTabWidths.current });
409
- }
472
+ ) {
473
+ setTabWidths({ ...measuredTabWidths.current });
474
+ } else if (
475
+ routes.every(
476
+ (r) => typeof measuredTabWidths.current[r.key] === 'number'
477
+ )
478
+ ) {
479
+ // When we have measured widths for all of the tabs, we should updates the state
480
+ // We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
481
+ setTabWidths({ ...measuredTabWidths.current });
410
482
  }
411
- : undefined,
412
- onPress: () => {
413
- const event: Scene<T> & Event = {
414
- route,
415
- defaultPrevented: false,
416
- preventDefault: () => {
417
- event.defaultPrevented = true;
418
- },
419
- };
483
+ }
484
+ : undefined;
485
+
486
+ const onPress = () => {
487
+ const event: Scene<T> & Event = {
488
+ route,
489
+ defaultPrevented: false,
490
+ preventDefault: () => {
491
+ event.defaultPrevented = true;
492
+ },
493
+ };
420
494
 
421
- onTabPress?.(event);
495
+ onTabPress?.(event);
422
496
 
423
- if (event.defaultPrevented) {
424
- return;
425
- }
497
+ if (event.defaultPrevented) {
498
+ return;
499
+ }
426
500
 
427
- jumpTo(route.key);
428
- },
429
- onLongPress: () => onTabLongPress?.({ route }),
430
- labelStyle: labelStyle,
501
+ jumpTo(route.key);
502
+ };
503
+
504
+ const onLongPress = () => onTabLongPress?.({ route });
505
+
506
+ // Calculate the default width for tab for FlatList to work
507
+ const defaultTabWidth = !isWidthDynamic
508
+ ? getComputedTabWidth(
509
+ index,
510
+ layout,
511
+ routes,
512
+ scrollEnabled,
513
+ tabWidths,
514
+ getFlattenedTabWidth(tabStyle),
515
+ getFlattenedPaddingEnd(contentContainerStyle),
516
+ getFlattenedPaddingStart(contentContainerStyle),
517
+ gap
518
+ )
519
+ : undefined;
520
+
521
+ const props = {
522
+ ...rest,
523
+ position,
524
+ route,
525
+ navigationState,
526
+ testID,
527
+ labelText,
528
+ accessible,
529
+ accessibilityLabel,
530
+ activeColor,
531
+ inactiveColor,
532
+ pressColor,
533
+ pressOpacity,
534
+ onLayout,
535
+ onPress,
536
+ onLongPress,
431
537
  style: tabStyle,
432
- // Calculate the deafult width for tab for FlatList to work
433
- defaultTabWidth: !isWidthDynamic
434
- ? getComputedTabWidth(
435
- index,
436
- layout,
437
- routes,
438
- scrollEnabled,
439
- tabWidths,
440
- getFlattenedTabWidth(tabStyle)
441
- )
442
- : undefined,
538
+ defaultTabWidth,
443
539
  android_ripple,
444
- };
540
+ } satisfies TabBarItemProps<T>;
445
541
 
446
542
  return (
447
543
  <>
448
544
  {gap > 0 && index > 0 ? <Separator width={gap} /> : null}
449
545
  {renderTabBarItem ? (
450
- renderTabBarItem(props)
546
+ renderTabBarItem({ key: route.key, ...props })
451
547
  ) : (
452
- <TabBarItem {...props} />
548
+ <TabBarItem key={route.key} {...props} />
453
549
  )}
454
550
  </>
455
551
  );
456
552
  },
457
553
  [
554
+ position,
555
+ navigationState,
556
+ options,
458
557
  activeColor,
459
- android_ripple,
460
- gap,
461
- getAccessibilityLabel,
462
- getAccessible,
463
- getLabelText,
464
- getTestID,
465
558
  inactiveColor,
466
- isWidthDynamic,
467
- jumpTo,
468
- labelStyle,
469
- layout,
470
- navigationState,
471
- onTabLongPress,
472
- onTabPress,
473
- position,
474
559
  pressColor,
475
560
  pressOpacity,
476
- renderBadge,
477
- renderIcon,
478
- renderLabel,
479
- renderTabBarItem,
561
+ isWidthDynamic,
562
+ tabStyle,
563
+ layout,
480
564
  routes,
481
565
  scrollEnabled,
482
- tabStyle,
483
566
  tabWidths,
567
+ contentContainerStyle,
568
+ gap,
569
+ android_ripple,
570
+ renderTabBarItem,
571
+ onTabPress,
572
+ jumpTo,
573
+ onTabLongPress,
484
574
  ]
485
575
  );
486
576
 
@@ -489,21 +579,10 @@ export default function TabBar<T extends Route>({
489
579
  const contentContainerStyleMemoized = React.useMemo(
490
580
  () => [
491
581
  styles.tabContent,
492
- scrollEnabled
493
- ? {
494
- width:
495
- tabBarWidth > separatorsWidth ? tabBarWidth : tabBarWidthPercent,
496
- }
497
- : styles.container,
582
+ scrollEnabled ? { width: tabBarWidth } : null,
498
583
  contentContainerStyle,
499
584
  ],
500
- [
501
- contentContainerStyle,
502
- scrollEnabled,
503
- separatorsWidth,
504
- tabBarWidth,
505
- tabBarWidthPercent,
506
- ]
585
+ [contentContainerStyle, scrollEnabled, tabBarWidth]
507
586
  );
508
587
 
509
588
  const handleScroll = React.useMemo(
@@ -516,7 +595,7 @@ export default function TabBar<T extends Route>({
516
595
  },
517
596
  },
518
597
  ],
519
- { useNativeDriver: true }
598
+ { useNativeDriver }
520
599
  ),
521
600
  [scrollAmount]
522
601
  );
@@ -540,7 +619,6 @@ export default function TabBar<T extends Route>({
540
619
  }
541
620
  );
542
621
 
543
- // FIXME: Not sure why i have to as-any here (x3) ..
544
622
  return (
545
623
  <Animated.View onLayout={handleLayout} style={[styles.tabBar, style]}>
546
624
  <Animated.View
@@ -548,11 +626,7 @@ export default function TabBar<T extends Route>({
548
626
  style={[
549
627
  styles.indicatorContainer,
550
628
  scrollEnabled ? { transform: [{ translateX }] as any } : null,
551
- tabBarWidth > separatorsWidth
552
- ? { width: tabBarWidth - separatorsWidth as any }
553
- : scrollEnabled
554
- ? { width: tabBarWidthPercent as any }
555
- : null,
629
+ scrollEnabled ? { width: tabBarWidth } : null,
556
630
  indicatorContainerStyle,
557
631
  ]}
558
632
  >
@@ -561,10 +635,17 @@ export default function TabBar<T extends Route>({
561
635
  layout,
562
636
  navigationState,
563
637
  jumpTo,
638
+ direction,
564
639
  width: isWidthDynamic
565
640
  ? 'auto'
566
- : `${(100 - separatorPercent) / routes.length}%`,
567
- style: indicatorStyle,
641
+ : Math.max(
642
+ 0,
643
+ (tabBarWidth - separatorsWidth - paddingsWidth) / routes.length
644
+ ),
645
+ style: [
646
+ indicatorStyle,
647
+ { start: flattenedPaddingStart, end: flattenedPaddingEnd },
648
+ ],
568
649
  getTabWidth: (i: number) =>
569
650
  getComputedTabWidth(
570
651
  i,
@@ -572,7 +653,10 @@ export default function TabBar<T extends Route>({
572
653
  routes,
573
654
  scrollEnabled,
574
655
  tabWidths,
575
- flattenedTabWidth
656
+ flattenedTabWidth,
657
+ flattenedPaddingEnd,
658
+ flattenedPaddingStart,
659
+ gap
576
660
  ),
577
661
  gap,
578
662
  })}
@@ -582,7 +666,7 @@ export default function TabBar<T extends Route>({
582
666
  data={routes as Animated.WithAnimatedValue<T>[]}
583
667
  keyExtractor={keyExtractor}
584
668
  horizontal
585
- accessibilityRole="tablist"
669
+ role="tablist"
586
670
  keyboardShouldPersistTaps="handled"
587
671
  scrollEnabled={scrollEnabled}
588
672
  bounces={bounces}
@@ -594,7 +678,7 @@ export default function TabBar<T extends Route>({
594
678
  showsVerticalScrollIndicator={false}
595
679
  automaticallyAdjustContentInsets={false}
596
680
  overScrollMode="never"
597
- contentContainerStyle={contentContainerStyleMemoized as any}
681
+ contentContainerStyle={contentContainerStyleMemoized}
598
682
  scrollEventThrottle={16}
599
683
  renderItem={renderItem}
600
684
  onScroll={handleScroll}
@@ -607,33 +691,38 @@ export default function TabBar<T extends Route>({
607
691
  }
608
692
 
609
693
  const styles = StyleSheet.create({
610
- container: {
611
- flex: 1,
612
- },
613
694
  scroll: {
614
695
  overflow: Platform.select({ default: 'scroll', web: undefined }),
615
696
  },
616
697
  tabBar: {
698
+ zIndex: 1,
617
699
  backgroundColor: '#2196f3',
618
700
  elevation: 4,
619
- shadowColor: 'black',
620
- shadowOpacity: 0.1,
621
- shadowRadius: StyleSheet.hairlineWidth,
622
- shadowOffset: {
623
- height: StyleSheet.hairlineWidth,
624
- width: 0,
625
- },
626
- zIndex: 1,
701
+ ...Platform.select({
702
+ default: {
703
+ shadowColor: 'black',
704
+ shadowOpacity: 0.1,
705
+ shadowRadius: StyleSheet.hairlineWidth,
706
+ shadowOffset: {
707
+ height: StyleSheet.hairlineWidth,
708
+ width: 0,
709
+ },
710
+ },
711
+ web: {
712
+ boxShadow: '0 1px 1px rgba(0, 0, 0, 0.1)',
713
+ },
714
+ }),
627
715
  },
628
716
  tabContent: {
717
+ flexGrow: 1,
629
718
  flexDirection: 'row',
630
719
  flexWrap: 'nowrap',
631
720
  },
632
721
  indicatorContainer: {
633
722
  position: 'absolute',
634
723
  top: 0,
635
- left: 0,
636
- right: 0,
724
+ start: 0,
725
+ end: 0,
637
726
  bottom: 0,
638
727
  },
639
728
  });