@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.
- package/README.md +2 -646
- package/SystemBundles/6f915df16edbc21a601928d99e844a99397b9428146027dcf0d86b2a798f21f4.bundle +8563 -0
- package/SystemBundles/6f915df16edbc21a601928d99e844a99397b9428146027dcf0d86b2a798f21f4.map +3217 -0
- package/SystemModules/amzn__react-navigation__react-native-tab-view-8.modules.txt +12 -0
- package/SystemModules/amzn__react-navigation__react-native-tab-view-8.private.modules.txt +63 -0
- package/SystemModules/keplerscript-system-bundle-config.json +4 -4
- package/kepler-compatibility.json +2 -2
- package/lib/module/Pager.android.js +3 -1
- package/lib/module/Pager.android.js.map +1 -1
- package/lib/module/Pager.ios.js +3 -1
- package/lib/module/Pager.ios.js.map +1 -1
- package/lib/module/Pager.js +3 -1
- package/lib/module/Pager.js.map +1 -1
- package/lib/module/PagerViewAdapter.js +29 -22
- package/lib/module/PagerViewAdapter.js.map +1 -1
- package/lib/module/PanResponderAdapter.js +46 -32
- package/lib/module/PanResponderAdapter.js.map +1 -1
- package/lib/module/PlatformPressable.js +38 -12
- package/lib/module/PlatformPressable.js.map +1 -1
- package/lib/module/SceneMap.js +7 -4
- package/lib/module/SceneMap.js.map +1 -1
- package/lib/module/SceneView.js +16 -14
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/TabBar.js +232 -156
- package/lib/module/TabBar.js.map +1 -1
- package/lib/module/TabBarIndicator.js +47 -26
- package/lib/module/TabBarIndicator.js.map +1 -1
- package/lib/module/TabBarItem.js +106 -107
- package/lib/module/TabBarItem.js.map +1 -1
- package/lib/module/TabBarItemLabel.js +33 -0
- package/lib/module/TabBarItemLabel.js.map +1 -0
- package/lib/module/TabView.js +91 -63
- package/lib/module/TabView.js.map +1 -1
- package/lib/module/index.js +7 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -1
- package/lib/module/useAnimatedValue.js +4 -2
- package/lib/module/useAnimatedValue.js.map +1 -1
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/Pager.android.d.ts +1 -1
- package/lib/typescript/src/Pager.android.d.ts.map +1 -1
- package/lib/typescript/src/Pager.d.ts +1 -1
- package/lib/typescript/src/Pager.d.ts.map +1 -1
- package/lib/typescript/src/Pager.ios.d.ts +1 -1
- package/lib/typescript/src/Pager.ios.d.ts.map +1 -1
- package/lib/typescript/src/PagerViewAdapter.d.ts +4 -1
- package/lib/typescript/src/PagerViewAdapter.d.ts.map +1 -1
- package/lib/typescript/src/PanResponderAdapter.d.ts +4 -1
- package/lib/typescript/src/PanResponderAdapter.d.ts.map +1 -1
- package/lib/typescript/src/PlatformPressable.d.ts +6 -8
- package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
- package/lib/typescript/src/SceneMap.d.ts +2 -2
- package/lib/typescript/src/SceneMap.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +2 -2
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/TabBar.d.ts +7 -19
- package/lib/typescript/src/TabBar.d.ts.map +1 -1
- package/lib/typescript/src/TabBarIndicator.d.ts +8 -6
- package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItem.d.ts +4 -22
- package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItemLabel.d.ts +11 -0
- package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -0
- package/lib/typescript/src/TabView.d.ts +14 -8
- package/lib/typescript/src/TabView.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -6
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +29 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useAnimatedValue.d.ts +1 -1
- package/lib/typescript/src/useAnimatedValue.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/Pager.android.tsx +1 -1
- package/src/Pager.ios.tsx +1 -1
- package/src/Pager.tsx +1 -1
- package/src/PagerViewAdapter.tsx +26 -21
- package/src/PanResponderAdapter.tsx +32 -28
- package/src/PlatformPressable.tsx +59 -10
- package/src/SceneMap.tsx +3 -3
- package/src/SceneView.tsx +6 -7
- package/src/TabBar.tsx +282 -193
- package/src/TabBarIndicator.tsx +65 -28
- package/src/TabBarItem.tsx +119 -130
- package/src/TabBarItemLabel.tsx +42 -0
- package/src/TabView.tsx +59 -16
- package/src/index.tsx +11 -6
- package/src/types.tsx +29 -1
- package/src/useAnimatedValue.tsx +2 -2
- package/SystemBundles/0c296a88d0023a3995e190db70259767d1ef39c9da6cd47f3d76cd128687f1f6.bundle +0 -14497
- package/SystemBundles/0c296a88d0023a3995e190db70259767d1ef39c9da6cd47f3d76cd128687f1f6.map +0 -1
- package/SystemModules/amzn__react-navigation__react-native-tab-view-7.modules.txt +0 -41
- package/SystemModules/amzn__react-navigation__react-native-tab-view-7.private.modules.txt +0 -98
- package/THIRD_PARTY_NOTICES.txt +0 -24
- package/lib/commonjs/Pager.android.js +0 -14
- package/lib/commonjs/Pager.android.js.map +0 -1
- package/lib/commonjs/Pager.ios.js +0 -14
- package/lib/commonjs/Pager.ios.js.map +0 -1
- package/lib/commonjs/Pager.js +0 -14
- package/lib/commonjs/Pager.js.map +0 -1
- package/lib/commonjs/PagerViewAdapter.js +0 -132
- package/lib/commonjs/PagerViewAdapter.js.map +0 -1
- package/lib/commonjs/PanResponderAdapter.js +0 -204
- package/lib/commonjs/PanResponderAdapter.js.map +0 -1
- package/lib/commonjs/PlatformPressable.js +0 -43
- package/lib/commonjs/PlatformPressable.js.map +0 -1
- package/lib/commonjs/SceneMap.js +0 -29
- package/lib/commonjs/SceneMap.js.map +0 -1
- package/lib/commonjs/SceneView.js +0 -78
- package/lib/commonjs/SceneView.js.map +0 -1
- package/lib/commonjs/TabBar.js +0 -405
- package/lib/commonjs/TabBar.js.map +0 -1
- package/lib/commonjs/TabBarIndicator.js +0 -110
- package/lib/commonjs/TabBarIndicator.js.map +0 -1
- package/lib/commonjs/TabBarItem.js +0 -227
- package/lib/commonjs/TabBarItem.js.map +0 -1
- package/lib/commonjs/TabView.js +0 -122
- package/lib/commonjs/TabView.js.map +0 -1
- package/lib/commonjs/index.js +0 -42
- package/lib/commonjs/index.js.map +0 -1
- package/lib/commonjs/types.js +0 -6
- package/lib/commonjs/types.js.map +0 -1
- package/lib/commonjs/useAnimatedValue.js +0 -18
- 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
|
|
21
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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' &&
|
|
162
|
+
Platform.OS === 'android' && direction === 'rtl'
|
|
125
163
|
? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1))
|
|
126
164
|
: scrollAmount,
|
|
127
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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' &&
|
|
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:
|
|
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
|
-
(
|
|
220
|
-
|
|
221
|
-
: tabWidth + (gap ?? 0))
|
|
292
|
+
(i > 0 ? (gap ?? 0) : 0) +
|
|
293
|
+
(navigationState.index === i ? tabWidth / 2 : tabWidth)
|
|
222
294
|
);
|
|
223
|
-
},
|
|
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
|
-
|
|
248
|
-
|
|
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
|
|
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
|
-
|
|
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>(
|
|
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
|
|
355
|
-
|
|
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
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
route
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
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
|
-
|
|
407
|
-
|
|
408
|
-
|
|
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
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
-
|
|
495
|
+
onTabPress?.(event);
|
|
422
496
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
497
|
+
if (event.defaultPrevented) {
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
426
500
|
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
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
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|
|
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
|
|
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
|
-
:
|
|
567
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
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
|
-
|
|
636
|
-
|
|
724
|
+
start: 0,
|
|
725
|
+
end: 0,
|
|
637
726
|
bottom: 0,
|
|
638
727
|
},
|
|
639
728
|
});
|