@amazon-devices/react-navigation__react-native-tab-view 7.0.1759135970 → 8.0.9000000000

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 (125) hide show
  1. package/README.md +2 -646
  2. package/SystemBundles/6f915df16edbc21a601928d99e844a99397b9428146027dcf0d86b2a798f21f4.bundle +8563 -0
  3. package/SystemBundles/6f915df16edbc21a601928d99e844a99397b9428146027dcf0d86b2a798f21f4.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/package.json +1 -1
  75. package/src/Pager.android.tsx +1 -1
  76. package/src/Pager.ios.tsx +1 -1
  77. package/src/Pager.tsx +1 -1
  78. package/src/PagerViewAdapter.tsx +26 -21
  79. package/src/PanResponderAdapter.tsx +32 -28
  80. package/src/PlatformPressable.tsx +59 -10
  81. package/src/SceneMap.tsx +3 -3
  82. package/src/SceneView.tsx +6 -7
  83. package/src/TabBar.tsx +282 -193
  84. package/src/TabBarIndicator.tsx +65 -28
  85. package/src/TabBarItem.tsx +119 -130
  86. package/src/TabBarItemLabel.tsx +42 -0
  87. package/src/TabView.tsx +59 -16
  88. package/src/index.tsx +11 -6
  89. package/src/types.tsx +29 -1
  90. package/src/useAnimatedValue.tsx +2 -2
  91. package/SystemBundles/0c296a88d0023a3995e190db70259767d1ef39c9da6cd47f3d76cd128687f1f6.bundle +0 -14497
  92. package/SystemBundles/0c296a88d0023a3995e190db70259767d1ef39c9da6cd47f3d76cd128687f1f6.map +0 -1
  93. package/SystemModules/amzn__react-navigation__react-native-tab-view-7.modules.txt +0 -41
  94. package/SystemModules/amzn__react-navigation__react-native-tab-view-7.private.modules.txt +0 -98
  95. package/THIRD_PARTY_NOTICES.txt +0 -24
  96. package/lib/commonjs/Pager.android.js +0 -14
  97. package/lib/commonjs/Pager.android.js.map +0 -1
  98. package/lib/commonjs/Pager.ios.js +0 -14
  99. package/lib/commonjs/Pager.ios.js.map +0 -1
  100. package/lib/commonjs/Pager.js +0 -14
  101. package/lib/commonjs/Pager.js.map +0 -1
  102. package/lib/commonjs/PagerViewAdapter.js +0 -132
  103. package/lib/commonjs/PagerViewAdapter.js.map +0 -1
  104. package/lib/commonjs/PanResponderAdapter.js +0 -204
  105. package/lib/commonjs/PanResponderAdapter.js.map +0 -1
  106. package/lib/commonjs/PlatformPressable.js +0 -43
  107. package/lib/commonjs/PlatformPressable.js.map +0 -1
  108. package/lib/commonjs/SceneMap.js +0 -29
  109. package/lib/commonjs/SceneMap.js.map +0 -1
  110. package/lib/commonjs/SceneView.js +0 -78
  111. package/lib/commonjs/SceneView.js.map +0 -1
  112. package/lib/commonjs/TabBar.js +0 -405
  113. package/lib/commonjs/TabBar.js.map +0 -1
  114. package/lib/commonjs/TabBarIndicator.js +0 -110
  115. package/lib/commonjs/TabBarIndicator.js.map +0 -1
  116. package/lib/commonjs/TabBarItem.js +0 -227
  117. package/lib/commonjs/TabBarItem.js.map +0 -1
  118. package/lib/commonjs/TabView.js +0 -122
  119. package/lib/commonjs/TabView.js.map +0 -1
  120. package/lib/commonjs/index.js +0 -42
  121. package/lib/commonjs/index.js.map +0 -1
  122. package/lib/commonjs/types.js +0 -6
  123. package/lib/commonjs/types.js.map +0 -1
  124. package/lib/commonjs/useAnimatedValue.js +0 -18
  125. package/lib/commonjs/useAnimatedValue.js.map +0 -1
@@ -1,40 +1,63 @@
1
1
  import * as React from 'react';
2
2
  import {
3
3
  Animated,
4
- DimensionValue,
5
4
  Easing,
6
- I18nManager,
7
5
  Platform,
8
- StyleProp,
6
+ type StyleProp,
9
7
  StyleSheet,
10
- ViewStyle,
8
+ type ViewStyle,
11
9
  } from 'react-native';
12
10
 
13
- import type { NavigationState, Route, SceneRendererProps } from './types';
14
- import useAnimatedValue from './useAnimatedValue';
11
+ import type {
12
+ LocaleDirection,
13
+ NavigationState,
14
+ Route,
15
+ SceneRendererProps,
16
+ } from './types';
17
+ import { useAnimatedValue } from './useAnimatedValue';
15
18
 
16
19
  export type GetTabWidth = (index: number) => number;
17
20
 
18
21
  export type Props<T extends Route> = SceneRendererProps & {
19
22
  navigationState: NavigationState<T>;
20
- width: string | number;
21
- style?: StyleProp<ViewStyle>;
23
+ width: 'auto' | `${number}%` | number;
22
24
  getTabWidth: GetTabWidth;
25
+ direction: LocaleDirection;
26
+ style?: StyleProp<ViewStyle>;
23
27
  gap?: number;
28
+ children?: React.ReactNode;
24
29
  };
25
30
 
31
+ const useNativeDriver = Platform.OS !== 'web';
32
+
26
33
  const getTranslateX = (
27
34
  position: Animated.AnimatedInterpolation<number>,
28
35
  routes: Route[],
29
36
  getTabWidth: GetTabWidth,
30
- gap?: number
37
+ direction: LocaleDirection,
38
+ gap?: number,
39
+ width?: number | string
31
40
  ) => {
32
41
  const inputRange = routes.map((_, i) => i);
33
42
 
34
43
  // every index contains widths at all previous indices
35
44
  const outputRange = routes.reduce<number[]>((acc, _, i) => {
36
- if (i === 0) return [0];
37
- return [...acc, acc[i - 1] + getTabWidth(i - 1) + (gap ?? 0)];
45
+ if (typeof width === 'number') {
46
+ if (i === 0) return [getTabWidth(i) / 2 - width / 2];
47
+
48
+ let sumTabWidth = 0;
49
+ for (let j = 0; j < i; j++) {
50
+ sumTabWidth += getTabWidth(j);
51
+ }
52
+
53
+ return [
54
+ ...acc,
55
+ sumTabWidth + getTabWidth(i) / 2 + (gap ? gap * i : 0) - width / 2,
56
+ ];
57
+ } else {
58
+ if (i === 0) return [0];
59
+ return [...acc, acc[i - 1] + getTabWidth(i - 1) + (gap ?? 0)];
60
+ }
38
61
  }, []);
39
62
 
40
63
  const translateX = position.interpolate({
@@ -43,17 +66,19 @@ const getTranslateX = (
43
66
  extrapolate: 'clamp',
44
67
  });
45
68
 
46
- return Animated.multiply(translateX, I18nManager.isRTL ? -1 : 1);
69
+ return Animated.multiply(translateX, direction === 'rtl' ? -1 : 1);
47
70
  };
48
71
 
49
- export default function TabBarIndicator<T extends Route>({
72
+ export function TabBarIndicator<T extends Route>({
50
73
  getTabWidth,
51
74
  layout,
52
75
  navigationState,
53
76
  position,
54
77
  width,
78
+ direction,
55
79
  gap,
56
80
  style,
81
+ children,
57
82
  }: Props<T>) {
58
83
  const isIndicatorShown = React.useRef(false);
59
84
  const isWidthDynamic = width === 'auto';
@@ -81,7 +106,7 @@ export default function TabBarIndicator<T extends Route>({
81
106
  toValue: 1,
82
107
  duration: 150,
83
108
  easing: Easing.in(Easing.linear),
84
- useNativeDriver: true,
109
+ useNativeDriver,
85
110
  }).start();
86
111
  }
87
112
  };
@@ -97,7 +122,9 @@ export default function TabBarIndicator<T extends Route>({
97
122
 
98
123
  if (layout.width) {
99
124
  const translateX =
100
- routes.length > 1 ? getTranslateX(position, routes, getTabWidth, gap) : 0;
125
+ routes.length > 1
126
+ ? getTranslateX(position, routes, getTabWidth, direction, gap, width)
127
+ : 0;
101
128
 
102
129
  transform.push({ translateX });
103
130
  }
@@ -117,7 +144,23 @@ export default function TabBarIndicator<T extends Route>({
117
144
  })
118
145
  : outputRange[0],
119
146
  },
120
- { translateX: 0.5 }
147
+ { translateX: direction === 'rtl' ? -0.5 : 0.5 }
148
+ );
149
+ }
150
+
151
+ const styleList: StyleProp<ViewStyle> = [];
152
+
153
+ // scaleX doesn't work properly on chrome and opera for linux and android
154
+ if (Platform.OS === 'web' && width === 'auto') {
155
+ styleList.push(
156
+ { width: transform[1].scaleX },
157
+ { left: transform[0].translateX }
158
+ );
159
+ } else {
160
+ styleList.push(
161
+ { width: width === 'auto' ? 1 : width },
162
+ { start: `${(100 / routes.length) * navigationState.index}%` },
163
+ { transform }
121
164
  );
122
165
  }
123
166
 
@@ -125,19 +168,13 @@ export default function TabBarIndicator<T extends Route>({
125
168
  <Animated.View
126
169
  style={[
127
170
  styles.indicator,
128
- { width: (width === 'auto' ? 1 : width) as DimensionValue | undefined },
129
- // If layout is not available, use `left` property for positioning the indicator
130
- // This avoids rendering delay until we are able to calculate translateX
131
- // If platform is macos use `left` property as `transform` is broken at the moment.
132
- // See: https://github.com/microsoft/react-native-macos/issues/280
133
- layout.width && Platform.OS !== 'macos'
134
- ? { left: 0 }
135
- : { left: `${(100 / routes.length) * navigationState.index}%` },
136
- { transform },
171
+ styleList,
137
172
  width === 'auto' ? { opacity: opacity } : null,
138
173
  style,
139
174
  ]}
140
- />
175
+ >
176
+ {children}
177
+ </Animated.View>
141
178
  );
142
179
  }
143
180
 
@@ -145,9 +182,9 @@ const styles = StyleSheet.create({
145
182
  indicator: {
146
183
  backgroundColor: '#ffeb3b',
147
184
  position: 'absolute',
148
- left: 0,
185
+ start: 0,
149
186
  bottom: 0,
150
- right: 0,
187
+ end: 0,
151
188
  height: 2,
152
189
  },
153
190
  });
@@ -1,20 +1,21 @@
1
1
  import * as React from 'react';
2
2
  import {
3
3
  Animated,
4
- LayoutChangeEvent,
5
- PressableAndroidRippleConfig,
6
- StyleProp,
4
+ type LayoutChangeEvent,
5
+ Platform,
6
+ type PressableAndroidRippleConfig,
7
+ type StyleProp,
7
8
  StyleSheet,
8
- TextStyle,
9
9
  View,
10
- ViewStyle,
10
+ type ViewStyle,
11
11
  } from 'react-native';
12
12
  import useLatestCallback from 'use-latest-callback';
13
13
 
14
- import PlatformPressable from './PlatformPressable';
15
- import type { NavigationState, Route, Scene } from './types';
14
+ import { PlatformPressable } from './PlatformPressable';
15
+ import { TabBarItemLabel } from './TabBarItemLabel';
16
+ import type { NavigationState, Route, TabDescriptor } from './types';
16
17
 
17
- export type Props<T extends Route> = {
18
+ export type Props<T extends Route> = TabDescriptor<T> & {
18
19
  position: Animated.AnimatedInterpolation<number>;
19
20
  route: T;
20
21
  navigationState: NavigationState<T>;
@@ -22,32 +23,17 @@ export type Props<T extends Route> = {
22
23
  inactiveColor?: string;
23
24
  pressColor?: string;
24
25
  pressOpacity?: number;
25
- getLabelText: (scene: Scene<T>) => string | undefined;
26
- getAccessible: (scene: Scene<T>) => boolean | undefined;
27
- getAccessibilityLabel: (scene: Scene<T>) => string | undefined;
28
- getTestID: (scene: Scene<T>) => string | undefined;
29
- renderLabel?: (scene: {
30
- route: T;
31
- focused: boolean;
32
- color: string;
33
- }) => React.ReactNode;
34
- renderIcon?: (scene: {
35
- route: T;
36
- focused: boolean;
37
- color: string;
38
- }) => React.ReactNode;
39
- renderBadge?: (scene: Scene<T>) => React.ReactNode;
40
26
  onLayout?: (event: LayoutChangeEvent) => void;
41
27
  onPress: () => void;
42
28
  onLongPress: () => void;
43
29
  defaultTabWidth?: number;
44
- labelStyle?: StyleProp<TextStyle>;
45
30
  style: StyleProp<ViewStyle>;
46
31
  android_ripple?: PressableAndroidRippleConfig;
47
32
  };
48
33
 
49
34
  const DEFAULT_ACTIVE_COLOR = 'rgba(255, 255, 255, 1)';
50
35
  const DEFAULT_INACTIVE_COLOR = 'rgba(255, 255, 255, 0.7)';
36
+ const ICON_SIZE = 24;
51
37
 
52
38
  const getActiveOpacity = (
53
39
  position: Animated.AnimatedInterpolation<number>,
@@ -85,23 +71,29 @@ const getInactiveOpacity = (
85
71
 
86
72
  type TabBarItemInternalProps<T extends Route> = Omit<
87
73
  Props<T>,
88
- 'navigationState'
74
+ | 'navigationState'
75
+ | 'getAccessibilityLabel'
76
+ | 'getLabelText'
77
+ | 'getTestID'
78
+ | 'getAccessible'
79
+ | 'options'
89
80
  > & {
90
81
  isFocused: boolean;
91
82
  index: number;
92
83
  routesLength: number;
93
- };
84
+ } & TabDescriptor<T>;
85
+
86
+ const ANDROID_RIPPLE_DEFAULT = { borderless: true };
94
87
 
95
88
  const TabBarItemInternal = <T extends Route>({
96
- getAccessibilityLabel,
97
- getAccessible,
98
- getLabelText,
99
- getTestID,
89
+ accessibilityLabel,
90
+ accessible,
91
+ label: customlabel,
92
+ testID,
100
93
  onLongPress,
101
94
  onPress,
102
95
  isFocused,
103
96
  position,
104
- route,
105
97
  style,
106
98
  inactiveColor: inactiveColorCustom,
107
99
  activeColor: activeColorCustom,
@@ -110,12 +102,15 @@ const TabBarItemInternal = <T extends Route>({
110
102
  index: tabIndex,
111
103
  pressColor,
112
104
  pressOpacity,
113
- renderBadge,
114
- renderIcon,
115
105
  defaultTabWidth,
106
+ icon: customIcon,
107
+ badge: customBadge,
108
+ href,
109
+ labelText,
116
110
  routesLength,
117
- renderLabel: renderLabelCustom,
118
- android_ripple = { borderless: true },
111
+ android_ripple = ANDROID_RIPPLE_DEFAULT,
112
+ labelAllowFontScaling,
113
+ route,
119
114
  }: TabBarItemInternalProps<T>) => {
120
115
  const labelColorFromStyle = StyleSheet.flatten(labelStyle || {}).color;
121
116
 
@@ -123,98 +118,88 @@ const TabBarItemInternal = <T extends Route>({
123
118
  activeColorCustom !== undefined
124
119
  ? activeColorCustom
125
120
  : typeof labelColorFromStyle === 'string'
126
- ? labelColorFromStyle
127
- : DEFAULT_ACTIVE_COLOR;
121
+ ? labelColorFromStyle
122
+ : DEFAULT_ACTIVE_COLOR;
128
123
  const inactiveColor =
129
124
  inactiveColorCustom !== undefined
130
125
  ? inactiveColorCustom
131
126
  : typeof labelColorFromStyle === 'string'
132
- ? labelColorFromStyle
133
- : DEFAULT_INACTIVE_COLOR;
127
+ ? labelColorFromStyle
128
+ : DEFAULT_INACTIVE_COLOR;
134
129
 
135
130
  const activeOpacity = getActiveOpacity(position, routesLength, tabIndex);
136
131
  const inactiveOpacity = getInactiveOpacity(position, routesLength, tabIndex);
137
132
 
138
- let icon: React.ReactNode | null = null;
139
- let label: React.ReactNode | null = null;
133
+ const icon = React.useMemo(() => {
134
+ if (!customIcon) {
135
+ return null;
136
+ }
140
137
 
141
- if (renderIcon) {
142
- const activeIcon = renderIcon({
143
- route,
144
- focused: true,
145
- color: activeColor,
146
- });
147
- const inactiveIcon = renderIcon({
148
- route,
138
+ const inactiveIcon = customIcon({
149
139
  focused: false,
150
140
  color: inactiveColor,
141
+ size: ICON_SIZE,
142
+ route,
151
143
  });
152
144
 
153
- if (inactiveIcon != null && activeIcon != null) {
154
- icon = (
155
- <View style={styles.icon}>
156
- <Animated.View style={{ opacity: inactiveOpacity }}>
157
- {inactiveIcon}
158
- </Animated.View>
159
- <Animated.View
160
- style={[StyleSheet.absoluteFill, { opacity: activeOpacity }]}
161
- >
162
- {activeIcon}
163
- </Animated.View>
164
- </View>
165
- );
166
- }
167
- }
168
-
169
- const renderLabel =
170
- renderLabelCustom !== undefined
171
- ? renderLabelCustom
172
- : (labelProps: { route: T; color: string }) => {
173
- const labelText = getLabelText({ route: labelProps.route });
174
-
175
- if (typeof labelText === 'string') {
176
- return (
177
- <Animated.Text
178
- style={[
179
- styles.label,
180
- icon ? { marginTop: 0 } : null,
181
- labelStyle,
182
- { color: labelProps.color },
183
- ]}
184
- >
185
- {labelText}
186
- </Animated.Text>
187
- );
188
- }
189
-
190
- return labelText;
191
- };
192
-
193
- if (renderLabel) {
194
- const activeLabel = renderLabel({
195
- route,
145
+ const activeIcon = customIcon({
196
146
  focused: true,
197
147
  color: activeColor,
198
- });
199
- const inactiveLabel = renderLabel({
148
+ size: ICON_SIZE,
200
149
  route,
201
- focused: false,
202
- color: inactiveColor,
203
150
  });
204
151
 
205
- label = (
206
- <View>
152
+ return (
153
+ <View style={styles.icon}>
207
154
  <Animated.View style={{ opacity: inactiveOpacity }}>
208
- {inactiveLabel}
155
+ {inactiveIcon}
209
156
  </Animated.View>
210
157
  <Animated.View
211
158
  style={[StyleSheet.absoluteFill, { opacity: activeOpacity }]}
212
159
  >
213
- {activeLabel}
160
+ {activeIcon}
214
161
  </Animated.View>
215
162
  </View>
216
163
  );
217
- }
164
+ }, [
165
+ activeColor,
166
+ activeOpacity,
167
+ customIcon,
168
+ inactiveColor,
169
+ inactiveOpacity,
170
+ route,
171
+ ]);
172
+
173
+ const renderLabel = React.useCallback(
174
+ (focused: boolean) =>
175
+ customlabel ? (
176
+ customlabel({
177
+ focused,
178
+ color: focused ? activeColor : inactiveColor,
179
+ style: labelStyle,
180
+ labelText,
181
+ allowFontScaling: labelAllowFontScaling,
182
+ route,
183
+ })
184
+ ) : (
185
+ <TabBarItemLabel
186
+ color={focused ? activeColor : inactiveColor}
187
+ icon={icon}
188
+ label={labelText}
189
+ style={labelStyle}
190
+ />
191
+ ),
192
+ [
193
+ customlabel,
194
+ activeColor,
195
+ labelStyle,
196
+ labelText,
197
+ labelAllowFontScaling,
198
+ route,
199
+ inactiveColor,
200
+ icon,
201
+ ]
202
+ );
218
203
 
219
204
  const tabStyle = StyleSheet.flatten(style);
220
205
  const isWidthSet = tabStyle?.width !== undefined;
@@ -223,39 +208,41 @@ const TabBarItemInternal = <T extends Route>({
223
208
  ? null
224
209
  : { width: defaultTabWidth };
225
210
 
226
- const scene = { route };
227
-
228
- let accessibilityLabel = getAccessibilityLabel(scene);
229
-
230
- accessibilityLabel =
231
- typeof accessibilityLabel !== 'undefined'
232
- ? accessibilityLabel
233
- : getLabelText(scene);
234
-
235
- const badge = renderBadge ? renderBadge(scene) : null;
211
+ const ariaLabel =
212
+ typeof accessibilityLabel !== 'undefined' ? accessibilityLabel : labelText;
236
213
 
237
214
  return (
238
215
  <PlatformPressable
239
216
  android_ripple={android_ripple}
240
- testID={getTestID(scene)}
241
- accessible={getAccessible(scene)}
242
- accessibilityLabel={accessibilityLabel}
243
- accessibilityRole="tab"
244
- accessibilityState={{ selected: isFocused }}
245
- // @ts-ignore: this is to support older React Native versions
246
- accessibilityStates={isFocused ? ['selected'] : []}
217
+ testID={testID}
218
+ accessible={accessible}
219
+ role="tab"
220
+ aria-label={ariaLabel}
221
+ aria-selected={isFocused}
247
222
  pressColor={pressColor}
248
223
  pressOpacity={pressOpacity}
249
- delayPressIn={0}
224
+ unstable_pressDelay={0}
250
225
  onLayout={onLayout}
251
226
  onPress={onPress}
252
227
  onLongPress={onLongPress}
228
+ href={href}
253
229
  style={[styles.pressable, tabContainerStyle]}
254
230
  >
255
231
  <View pointerEvents="none" style={[styles.item, tabStyle]}>
256
232
  {icon}
257
- {label}
258
- {badge != null ? <View style={styles.badge}>{badge}</View> : null}
233
+ <View>
234
+ <Animated.View style={{ opacity: inactiveOpacity }}>
235
+ {renderLabel(false)}
236
+ </Animated.View>
237
+ <Animated.View
238
+ style={[StyleSheet.absoluteFill, { opacity: activeOpacity }]}
239
+ >
240
+ {renderLabel(true)}
241
+ </Animated.View>
242
+ </View>
243
+ {customBadge != null ? (
244
+ <View style={styles.badge}>{customBadge({ route })}</View>
245
+ ) : null}
259
246
  </View>
260
247
  </PlatformPressable>
261
248
  );
@@ -265,9 +252,10 @@ const MemoizedTabBarItemInternal = React.memo(
265
252
  TabBarItemInternal
266
253
  ) as typeof TabBarItemInternal;
267
254
 
268
- function TabBarItem<T extends Route>(props: Props<T>) {
255
+ export function TabBarItem<T extends Route>(props: Props<T>) {
269
256
  const { onPress, onLongPress, onLayout, navigationState, route, ...rest } =
270
257
  props;
258
+
271
259
  const onPressLatest = useLatestCallback(onPress);
272
260
  const onLongPressLatest = useLatestCallback(onLongPress);
273
261
  const onLayoutLatest = useLatestCallback(onLayout ? onLayout : () => {});
@@ -288,14 +276,7 @@ function TabBarItem<T extends Route>(props: Props<T>) {
288
276
  );
289
277
  }
290
278
 
291
- export default TabBarItem;
292
-
293
279
  const styles = StyleSheet.create({
294
- label: {
295
- margin: 4,
296
- backgroundColor: 'transparent',
297
- textTransform: 'uppercase',
298
- },
299
280
  icon: {
300
281
  margin: 2,
301
282
  },
@@ -309,11 +290,19 @@ const styles = StyleSheet.create({
309
290
  badge: {
310
291
  position: 'absolute',
311
292
  top: 0,
312
- right: 0,
293
+ end: 0,
313
294
  },
314
295
  pressable: {
315
296
  // The label is not pressable on Windows
316
297
  // Adding backgroundColor: 'transparent' seems to fix it
317
298
  backgroundColor: 'transparent',
299
+ ...Platform.select({
300
+ // Roundness for iPad hover effect
301
+ ios: {
302
+ borderRadius: 10,
303
+ borderCurve: 'continuous',
304
+ },
305
+ default: null,
306
+ }),
318
307
  },
319
308
  });
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ import { Animated, StyleSheet } from 'react-native';
4
+
5
+ interface TabBarItemLabelProps {
6
+ color: string;
7
+ label?: string;
8
+ style: StyleProp<ViewStyle>;
9
+ icon: React.ReactNode;
10
+ }
11
+
12
+ export const TabBarItemLabel = React.memo(
13
+ ({ color, label, style, icon }: TabBarItemLabelProps) => {
14
+ if (!label) {
15
+ return null;
16
+ }
17
+
18
+ return (
19
+ <Animated.Text
20
+ style={[
21
+ styles.label,
22
+ icon ? { marginTop: 0 } : null,
23
+ style,
24
+ { color: color },
25
+ ]}
26
+ >
27
+ {label}
28
+ </Animated.Text>
29
+ );
30
+ }
31
+ );
32
+
33
+ TabBarItemLabel.displayName = 'TabBarItemLabel';
34
+
35
+ const styles = StyleSheet.create({
36
+ label: {
37
+ margin: 4,
38
+ fontSize: 14,
39
+ fontWeight: '500',
40
+ backgroundColor: 'transparent',
41
+ },
42
+ });