react-native-tab-view 3.5.2 → 4.0.0-alpha.1
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/lib/commonjs/Pager.android.js.map +1 -1
- package/lib/commonjs/Pager.ios.js.map +1 -1
- package/lib/commonjs/Pager.js.map +1 -1
- package/lib/commonjs/PagerViewAdapter.js +9 -13
- package/lib/commonjs/PagerViewAdapter.js.map +1 -1
- package/lib/commonjs/PanResponderAdapter.js +10 -9
- package/lib/commonjs/PanResponderAdapter.js.map +1 -1
- package/lib/commonjs/PlatformPressable.js +2 -2
- package/lib/commonjs/PlatformPressable.js.map +1 -1
- package/lib/commonjs/SceneMap.js +2 -2
- package/lib/commonjs/SceneMap.js.map +1 -1
- package/lib/commonjs/SceneView.js +3 -4
- package/lib/commonjs/SceneView.js.map +1 -1
- package/lib/commonjs/TabBar.js +86 -40
- package/lib/commonjs/TabBar.js.map +1 -1
- package/lib/commonjs/TabBarIndicator.js +10 -8
- package/lib/commonjs/TabBarIndicator.js.map +1 -1
- package/lib/commonjs/TabBarItem.js +5 -8
- package/lib/commonjs/TabBarItem.js.map +1 -1
- package/lib/commonjs/TabBarItemLabel.js +1 -2
- package/lib/commonjs/TabBarItemLabel.js.map +1 -1
- package/lib/commonjs/TabView.js +8 -3
- package/lib/commonjs/TabView.js.map +1 -1
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/commonjs/useAnimatedValue.js +2 -2
- package/lib/commonjs/useAnimatedValue.js.map +1 -1
- package/lib/module/Pager.android.js.map +1 -1
- package/lib/module/Pager.ios.js.map +1 -1
- package/lib/module/Pager.js.map +1 -1
- package/lib/module/PagerViewAdapter.js +7 -11
- package/lib/module/PagerViewAdapter.js.map +1 -1
- package/lib/module/PanResponderAdapter.js +9 -8
- package/lib/module/PanResponderAdapter.js.map +1 -1
- package/lib/module/PlatformPressable.js.map +1 -1
- package/lib/module/SceneMap.js.map +1 -1
- package/lib/module/SceneView.js +1 -2
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/TabBar.js +84 -38
- package/lib/module/TabBar.js.map +1 -1
- package/lib/module/TabBarIndicator.js +9 -7
- package/lib/module/TabBarIndicator.js.map +1 -1
- package/lib/module/TabBarItem.js +3 -6
- package/lib/module/TabBarItem.js.map +1 -1
- package/lib/module/TabBarItemLabel.js.map +1 -1
- package/lib/module/TabView.js +7 -2
- package/lib/module/TabView.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/module/useAnimatedValue.js.map +1 -1
- package/lib/typescript/src/PanResponderAdapter.d.ts +1 -1
- package/lib/typescript/src/PanResponderAdapter.d.ts.map +1 -1
- package/lib/typescript/src/PlatformPressable.d.ts +2 -2
- 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 +6 -5
- package/lib/typescript/src/TabBar.d.ts.map +1 -1
- package/lib/typescript/src/TabBarIndicator.d.ts +8 -5
- package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItem.d.ts +2 -2
- package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItemLabel.d.ts +1 -1
- package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -1
- package/lib/typescript/src/TabView.d.ts +5 -4
- package/lib/typescript/src/TabView.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/PagerViewAdapter.tsx +2 -2
- package/src/PanResponderAdapter.tsx +9 -7
- package/src/PlatformPressable.tsx +1 -1
- package/src/SceneMap.tsx +1 -3
- package/src/SceneView.tsx +2 -2
- package/src/TabBar.tsx +154 -58
- package/src/TabBarIndicator.tsx +23 -10
- package/src/TabBarItem.tsx +6 -8
- package/src/TabView.tsx +21 -4
- package/src/types.tsx +2 -0
package/src/TabBar.tsx
CHANGED
|
@@ -1,26 +1,31 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
|
+
type DimensionValue,
|
|
4
5
|
FlatList,
|
|
5
6
|
I18nManager,
|
|
6
|
-
LayoutChangeEvent,
|
|
7
|
-
ListRenderItemInfo,
|
|
7
|
+
type LayoutChangeEvent,
|
|
8
|
+
type ListRenderItemInfo,
|
|
8
9
|
Platform,
|
|
9
|
-
PressableAndroidRippleConfig,
|
|
10
|
-
StyleProp,
|
|
10
|
+
type PressableAndroidRippleConfig,
|
|
11
|
+
type StyleProp,
|
|
11
12
|
StyleSheet,
|
|
12
|
-
TextStyle,
|
|
13
|
+
type TextStyle,
|
|
13
14
|
View,
|
|
14
|
-
ViewStyle,
|
|
15
|
-
ViewToken,
|
|
15
|
+
type ViewStyle,
|
|
16
|
+
type ViewToken,
|
|
16
17
|
} from 'react-native';
|
|
17
18
|
import useLatestCallback from 'use-latest-callback';
|
|
18
19
|
|
|
19
|
-
import {
|
|
20
|
-
|
|
20
|
+
import {
|
|
21
|
+
type Props as IndicatorProps,
|
|
22
|
+
TabBarIndicator,
|
|
23
|
+
} from './TabBarIndicator';
|
|
24
|
+
import { type Props as TabBarItemProps, TabBarItem } from './TabBarItem';
|
|
21
25
|
import type {
|
|
22
26
|
Event,
|
|
23
27
|
Layout,
|
|
28
|
+
LocaleDirection,
|
|
24
29
|
NavigationState,
|
|
25
30
|
Route,
|
|
26
31
|
Scene,
|
|
@@ -65,13 +70,12 @@ export type Props<T extends Route> = SceneRendererProps & {
|
|
|
65
70
|
labelStyle?: StyleProp<TextStyle>;
|
|
66
71
|
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
67
72
|
style?: StyleProp<ViewStyle>;
|
|
73
|
+
direction?: LocaleDirection;
|
|
68
74
|
gap?: number;
|
|
69
75
|
testID?: string;
|
|
70
76
|
android_ripple?: PressableAndroidRippleConfig;
|
|
71
77
|
};
|
|
72
78
|
|
|
73
|
-
type FlattenedTabWidth = string | number | undefined;
|
|
74
|
-
|
|
75
79
|
const Separator = ({ width }: { width: number }) => {
|
|
76
80
|
return <View style={{ width }} />;
|
|
77
81
|
};
|
|
@@ -82,13 +86,50 @@ const getFlattenedTabWidth = (style: StyleProp<ViewStyle>) => {
|
|
|
82
86
|
return tabStyle?.width;
|
|
83
87
|
};
|
|
84
88
|
|
|
89
|
+
const getFlattenedPaddingLeft = (style: StyleProp<ViewStyle>) => {
|
|
90
|
+
const flattenStyle = StyleSheet.flatten(style);
|
|
91
|
+
|
|
92
|
+
return flattenStyle
|
|
93
|
+
? flattenStyle.paddingLeft || flattenStyle.paddingHorizontal || 0
|
|
94
|
+
: 0;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const getFlattenedPaddingRight = (style: StyleProp<ViewStyle>) => {
|
|
98
|
+
const flattenStyle = StyleSheet.flatten(style);
|
|
99
|
+
|
|
100
|
+
return flattenStyle
|
|
101
|
+
? flattenStyle.paddingRight || flattenStyle.paddingHorizontal || 0
|
|
102
|
+
: 0;
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const convertPaddingPercentToSize = (
|
|
106
|
+
value: DimensionValue | undefined,
|
|
107
|
+
layout: Layout
|
|
108
|
+
): number => {
|
|
109
|
+
switch (typeof value) {
|
|
110
|
+
case 'number':
|
|
111
|
+
return value;
|
|
112
|
+
case 'string':
|
|
113
|
+
if (value.endsWith('%')) {
|
|
114
|
+
const width = parseFloat(value);
|
|
115
|
+
if (Number.isFinite(width)) {
|
|
116
|
+
return layout.width * (width / 100);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return 0;
|
|
121
|
+
};
|
|
122
|
+
|
|
85
123
|
const getComputedTabWidth = (
|
|
86
124
|
index: number,
|
|
87
125
|
layout: Layout,
|
|
88
126
|
routes: Route[],
|
|
89
127
|
scrollEnabled: boolean | undefined,
|
|
90
128
|
tabWidths: { [key: string]: number },
|
|
91
|
-
flattenedWidth:
|
|
129
|
+
flattenedWidth: DimensionValue | undefined,
|
|
130
|
+
flattenedPaddingLeft: DimensionValue | undefined,
|
|
131
|
+
flattenedPaddingRight: DimensionValue | undefined,
|
|
132
|
+
gap?: number
|
|
92
133
|
) => {
|
|
93
134
|
if (flattenedWidth === 'auto') {
|
|
94
135
|
return tabWidths[routes[index].key] || 0;
|
|
@@ -109,7 +150,13 @@ const getComputedTabWidth = (
|
|
|
109
150
|
if (scrollEnabled) {
|
|
110
151
|
return (layout.width / 5) * 2;
|
|
111
152
|
}
|
|
112
|
-
|
|
153
|
+
|
|
154
|
+
const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
|
|
155
|
+
const paddingTotalWidth =
|
|
156
|
+
convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
|
|
157
|
+
convertPaddingPercentToSize(flattenedPaddingRight, layout);
|
|
158
|
+
|
|
159
|
+
return (layout.width - gapTotalWidth - paddingTotalWidth) / routes.length;
|
|
113
160
|
};
|
|
114
161
|
|
|
115
162
|
const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
|
|
@@ -117,13 +164,14 @@ const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
|
|
|
117
164
|
|
|
118
165
|
const getTranslateX = (
|
|
119
166
|
scrollAmount: Animated.Value,
|
|
120
|
-
maxScrollDistance: number
|
|
167
|
+
maxScrollDistance: number,
|
|
168
|
+
direction: LocaleDirection
|
|
121
169
|
) =>
|
|
122
170
|
Animated.multiply(
|
|
123
|
-
Platform.OS === 'android' &&
|
|
171
|
+
Platform.OS === 'android' && direction === 'rtl'
|
|
124
172
|
? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1))
|
|
125
173
|
: scrollAmount,
|
|
126
|
-
|
|
174
|
+
direction === 'rtl' ? 1 : -1
|
|
127
175
|
);
|
|
128
176
|
|
|
129
177
|
const getTabBarWidth = <T extends Route>({
|
|
@@ -132,13 +180,23 @@ const getTabBarWidth = <T extends Route>({
|
|
|
132
180
|
gap,
|
|
133
181
|
scrollEnabled,
|
|
134
182
|
flattenedTabWidth,
|
|
183
|
+
flattenedPaddingLeft,
|
|
184
|
+
flattenedPaddingRight,
|
|
135
185
|
tabWidths,
|
|
136
186
|
}: Pick<Props<T>, 'navigationState' | 'gap' | 'layout' | 'scrollEnabled'> & {
|
|
137
187
|
tabWidths: Record<string, number>;
|
|
138
|
-
|
|
188
|
+
flattenedPaddingLeft: DimensionValue | undefined;
|
|
189
|
+
flattenedPaddingRight: DimensionValue | undefined;
|
|
190
|
+
flattenedTabWidth: DimensionValue | undefined;
|
|
139
191
|
}) => {
|
|
140
192
|
const { routes } = navigationState;
|
|
141
193
|
|
|
194
|
+
const paddingsWidth = Math.max(
|
|
195
|
+
0,
|
|
196
|
+
convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
|
|
197
|
+
convertPaddingPercentToSize(flattenedPaddingRight, layout)
|
|
198
|
+
);
|
|
199
|
+
|
|
142
200
|
return routes.reduce<number>(
|
|
143
201
|
(acc, _, i) =>
|
|
144
202
|
acc +
|
|
@@ -149,9 +207,12 @@ const getTabBarWidth = <T extends Route>({
|
|
|
149
207
|
routes,
|
|
150
208
|
scrollEnabled,
|
|
151
209
|
tabWidths,
|
|
152
|
-
flattenedTabWidth
|
|
210
|
+
flattenedTabWidth,
|
|
211
|
+
flattenedPaddingLeft,
|
|
212
|
+
flattenedPaddingRight,
|
|
213
|
+
gap
|
|
153
214
|
),
|
|
154
|
-
|
|
215
|
+
paddingsWidth
|
|
155
216
|
);
|
|
156
217
|
};
|
|
157
218
|
|
|
@@ -163,10 +224,16 @@ const normalizeScrollValue = <T extends Route>({
|
|
|
163
224
|
tabWidths,
|
|
164
225
|
value,
|
|
165
226
|
flattenedTabWidth,
|
|
227
|
+
flattenedPaddingLeft,
|
|
228
|
+
flattenedPaddingRight,
|
|
229
|
+
direction,
|
|
166
230
|
}: Pick<Props<T>, 'layout' | 'navigationState' | 'gap' | 'scrollEnabled'> & {
|
|
167
231
|
tabWidths: Record<string, number>;
|
|
168
232
|
value: number;
|
|
169
|
-
flattenedTabWidth:
|
|
233
|
+
flattenedTabWidth: DimensionValue | undefined;
|
|
234
|
+
flattenedPaddingLeft: DimensionValue | undefined;
|
|
235
|
+
flattenedPaddingRight: DimensionValue | undefined;
|
|
236
|
+
direction: LocaleDirection;
|
|
170
237
|
}) => {
|
|
171
238
|
const tabBarWidth = getTabBarWidth({
|
|
172
239
|
layout,
|
|
@@ -175,11 +242,13 @@ const normalizeScrollValue = <T extends Route>({
|
|
|
175
242
|
gap,
|
|
176
243
|
scrollEnabled,
|
|
177
244
|
flattenedTabWidth,
|
|
245
|
+
flattenedPaddingLeft,
|
|
246
|
+
flattenedPaddingRight,
|
|
178
247
|
});
|
|
179
248
|
const maxDistance = getMaxScrollDistance(tabBarWidth, layout.width);
|
|
180
249
|
const scrollValue = Math.max(Math.min(value, maxDistance), 0);
|
|
181
250
|
|
|
182
|
-
if (Platform.OS === 'android' &&
|
|
251
|
+
if (Platform.OS === 'android' && direction === 'rtl') {
|
|
183
252
|
// On Android, scroll value is not applied in reverse in RTL
|
|
184
253
|
// so we need to manually adjust it to apply correct value
|
|
185
254
|
return maxDistance - scrollValue;
|
|
@@ -195,10 +264,21 @@ const getScrollAmount = <T extends Route>({
|
|
|
195
264
|
scrollEnabled,
|
|
196
265
|
flattenedTabWidth,
|
|
197
266
|
tabWidths,
|
|
267
|
+
flattenedPaddingLeft,
|
|
268
|
+
flattenedPaddingRight,
|
|
269
|
+
direction,
|
|
198
270
|
}: Pick<Props<T>, 'layout' | 'navigationState' | 'scrollEnabled' | 'gap'> & {
|
|
199
271
|
tabWidths: Record<string, number>;
|
|
200
|
-
flattenedTabWidth:
|
|
272
|
+
flattenedTabWidth: DimensionValue | undefined;
|
|
273
|
+
flattenedPaddingLeft: DimensionValue | undefined;
|
|
274
|
+
flattenedPaddingRight: DimensionValue | undefined;
|
|
275
|
+
direction: LocaleDirection;
|
|
201
276
|
}) => {
|
|
277
|
+
const paddingInitial =
|
|
278
|
+
direction === 'rtl'
|
|
279
|
+
? convertPaddingPercentToSize(flattenedPaddingRight, layout)
|
|
280
|
+
: convertPaddingPercentToSize(flattenedPaddingLeft, layout);
|
|
281
|
+
|
|
202
282
|
const centerDistance = Array.from({
|
|
203
283
|
length: navigationState.index + 1,
|
|
204
284
|
}).reduce<number>((total, _, i) => {
|
|
@@ -208,18 +288,20 @@ const getScrollAmount = <T extends Route>({
|
|
|
208
288
|
navigationState.routes,
|
|
209
289
|
scrollEnabled,
|
|
210
290
|
tabWidths,
|
|
211
|
-
flattenedTabWidth
|
|
291
|
+
flattenedTabWidth,
|
|
292
|
+
flattenedPaddingLeft,
|
|
293
|
+
flattenedPaddingRight,
|
|
294
|
+
gap
|
|
212
295
|
);
|
|
213
296
|
|
|
214
297
|
// To get the current index centered we adjust scroll amount by width of indexes
|
|
215
298
|
// 0 through (i - 1) and add half the width of current index i
|
|
216
299
|
return (
|
|
217
300
|
total +
|
|
218
|
-
(
|
|
219
|
-
|
|
220
|
-
: tabWidth + (gap ?? 0))
|
|
301
|
+
(i > 0 ? gap ?? 0 : 0) +
|
|
302
|
+
(navigationState.index === i ? tabWidth / 2 : tabWidth)
|
|
221
303
|
);
|
|
222
|
-
},
|
|
304
|
+
}, paddingInitial);
|
|
223
305
|
|
|
224
306
|
const scrollAmount = centerDistance - layout.width / 2;
|
|
225
307
|
|
|
@@ -231,6 +313,9 @@ const getScrollAmount = <T extends Route>({
|
|
|
231
313
|
gap,
|
|
232
314
|
scrollEnabled,
|
|
233
315
|
flattenedTabWidth,
|
|
316
|
+
flattenedPaddingLeft,
|
|
317
|
+
flattenedPaddingRight,
|
|
318
|
+
direction,
|
|
234
319
|
});
|
|
235
320
|
};
|
|
236
321
|
|
|
@@ -281,22 +366,27 @@ export function TabBar<T extends Route>({
|
|
|
281
366
|
renderBadge,
|
|
282
367
|
renderIcon,
|
|
283
368
|
renderLabel,
|
|
369
|
+
direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
|
|
284
370
|
renderTabBarItem,
|
|
285
371
|
style,
|
|
286
372
|
tabStyle,
|
|
373
|
+
layout: propLayout,
|
|
287
374
|
testID,
|
|
288
375
|
android_ripple,
|
|
289
376
|
}: Props<T>) {
|
|
290
|
-
const [layout, setLayout] = React.useState<Layout>(
|
|
377
|
+
const [layout, setLayout] = React.useState<Layout>(
|
|
378
|
+
propLayout ?? { width: 0, height: 0 }
|
|
379
|
+
);
|
|
291
380
|
const [tabWidths, setTabWidths] = React.useState<Record<string, number>>({});
|
|
292
381
|
const flatListRef = React.useRef<FlatList | null>(null);
|
|
293
382
|
const isFirst = React.useRef(true);
|
|
294
383
|
const scrollAmount = useAnimatedValue(0);
|
|
295
384
|
const measuredTabWidths = React.useRef<Record<string, number>>({});
|
|
296
|
-
|
|
297
385
|
const { routes } = navigationState;
|
|
298
386
|
const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
|
|
299
387
|
const isWidthDynamic = flattenedTabWidth === 'auto';
|
|
388
|
+
const flattenedPaddingRight = getFlattenedPaddingRight(contentContainerStyle);
|
|
389
|
+
const flattenedPaddingLeft = getFlattenedPaddingLeft(contentContainerStyle);
|
|
300
390
|
const scrollOffset = getScrollAmount({
|
|
301
391
|
layout,
|
|
302
392
|
navigationState,
|
|
@@ -304,6 +394,9 @@ export function TabBar<T extends Route>({
|
|
|
304
394
|
gap,
|
|
305
395
|
scrollEnabled,
|
|
306
396
|
flattenedTabWidth,
|
|
397
|
+
flattenedPaddingLeft,
|
|
398
|
+
flattenedPaddingRight,
|
|
399
|
+
direction,
|
|
307
400
|
});
|
|
308
401
|
|
|
309
402
|
const hasMeasuredTabWidths =
|
|
@@ -347,19 +440,25 @@ export function TabBar<T extends Route>({
|
|
|
347
440
|
gap,
|
|
348
441
|
scrollEnabled,
|
|
349
442
|
flattenedTabWidth,
|
|
443
|
+
flattenedPaddingLeft,
|
|
444
|
+
flattenedPaddingRight,
|
|
350
445
|
});
|
|
351
446
|
|
|
352
447
|
const separatorsWidth = Math.max(0, routes.length - 1) * gap;
|
|
353
|
-
const
|
|
354
|
-
|
|
448
|
+
const paddingsWidth = Math.max(
|
|
449
|
+
0,
|
|
450
|
+
convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
|
|
451
|
+
convertPaddingPercentToSize(flattenedPaddingRight, layout)
|
|
452
|
+
);
|
|
355
453
|
|
|
356
454
|
const translateX = React.useMemo(
|
|
357
455
|
() =>
|
|
358
456
|
getTranslateX(
|
|
359
457
|
scrollAmount,
|
|
360
|
-
getMaxScrollDistance(tabBarWidth, layout.width)
|
|
458
|
+
getMaxScrollDistance(tabBarWidth, layout.width),
|
|
459
|
+
direction
|
|
361
460
|
),
|
|
362
|
-
[layout.width, scrollAmount, tabBarWidth]
|
|
461
|
+
[direction, layout.width, scrollAmount, tabBarWidth]
|
|
363
462
|
);
|
|
364
463
|
|
|
365
464
|
const renderItem = React.useCallback(
|
|
@@ -436,7 +535,10 @@ export function TabBar<T extends Route>({
|
|
|
436
535
|
routes,
|
|
437
536
|
scrollEnabled,
|
|
438
537
|
tabWidths,
|
|
439
|
-
getFlattenedTabWidth(tabStyle)
|
|
538
|
+
getFlattenedTabWidth(tabStyle),
|
|
539
|
+
getFlattenedPaddingRight(contentContainerStyle),
|
|
540
|
+
getFlattenedPaddingLeft(contentContainerStyle),
|
|
541
|
+
gap
|
|
440
542
|
)
|
|
441
543
|
: undefined,
|
|
442
544
|
android_ripple,
|
|
@@ -479,6 +581,7 @@ export function TabBar<T extends Route>({
|
|
|
479
581
|
routes,
|
|
480
582
|
scrollEnabled,
|
|
481
583
|
tabStyle,
|
|
584
|
+
contentContainerStyle,
|
|
482
585
|
tabWidths,
|
|
483
586
|
]
|
|
484
587
|
);
|
|
@@ -488,21 +591,10 @@ export function TabBar<T extends Route>({
|
|
|
488
591
|
const contentContainerStyleMemoized = React.useMemo(
|
|
489
592
|
() => [
|
|
490
593
|
styles.tabContent,
|
|
491
|
-
scrollEnabled
|
|
492
|
-
? {
|
|
493
|
-
width:
|
|
494
|
-
tabBarWidth > separatorsWidth ? tabBarWidth : tabBarWidthPercent,
|
|
495
|
-
}
|
|
496
|
-
: styles.container,
|
|
594
|
+
scrollEnabled ? { width: tabBarWidth } : null,
|
|
497
595
|
contentContainerStyle,
|
|
498
596
|
],
|
|
499
|
-
[
|
|
500
|
-
contentContainerStyle,
|
|
501
|
-
scrollEnabled,
|
|
502
|
-
separatorsWidth,
|
|
503
|
-
tabBarWidth,
|
|
504
|
-
tabBarWidthPercent,
|
|
505
|
-
]
|
|
597
|
+
[contentContainerStyle, scrollEnabled, tabBarWidth]
|
|
506
598
|
);
|
|
507
599
|
|
|
508
600
|
const handleScroll = React.useMemo(
|
|
@@ -546,11 +638,7 @@ export function TabBar<T extends Route>({
|
|
|
546
638
|
style={[
|
|
547
639
|
styles.indicatorContainer,
|
|
548
640
|
scrollEnabled ? { transform: [{ translateX }] as any } : null,
|
|
549
|
-
tabBarWidth
|
|
550
|
-
? { width: tabBarWidth - separatorsWidth }
|
|
551
|
-
: scrollEnabled
|
|
552
|
-
? { width: tabBarWidthPercent }
|
|
553
|
-
: null,
|
|
641
|
+
scrollEnabled ? { width: tabBarWidth } : null,
|
|
554
642
|
indicatorContainerStyle,
|
|
555
643
|
]}
|
|
556
644
|
>
|
|
@@ -559,10 +647,17 @@ export function TabBar<T extends Route>({
|
|
|
559
647
|
layout,
|
|
560
648
|
navigationState,
|
|
561
649
|
jumpTo,
|
|
650
|
+
direction,
|
|
562
651
|
width: isWidthDynamic
|
|
563
652
|
? 'auto'
|
|
564
|
-
:
|
|
565
|
-
|
|
653
|
+
: Math.max(
|
|
654
|
+
0,
|
|
655
|
+
(tabBarWidth - separatorsWidth - paddingsWidth) / routes.length
|
|
656
|
+
),
|
|
657
|
+
style: [
|
|
658
|
+
indicatorStyle,
|
|
659
|
+
{ left: flattenedPaddingLeft, right: flattenedPaddingRight },
|
|
660
|
+
],
|
|
566
661
|
getTabWidth: (i: number) =>
|
|
567
662
|
getComputedTabWidth(
|
|
568
663
|
i,
|
|
@@ -570,7 +665,10 @@ export function TabBar<T extends Route>({
|
|
|
570
665
|
routes,
|
|
571
666
|
scrollEnabled,
|
|
572
667
|
tabWidths,
|
|
573
|
-
flattenedTabWidth
|
|
668
|
+
flattenedTabWidth,
|
|
669
|
+
flattenedPaddingRight,
|
|
670
|
+
flattenedPaddingLeft,
|
|
671
|
+
gap
|
|
574
672
|
),
|
|
575
673
|
gap,
|
|
576
674
|
})}
|
|
@@ -605,9 +703,6 @@ export function TabBar<T extends Route>({
|
|
|
605
703
|
}
|
|
606
704
|
|
|
607
705
|
const styles = StyleSheet.create({
|
|
608
|
-
container: {
|
|
609
|
-
flex: 1,
|
|
610
|
-
},
|
|
611
706
|
scroll: {
|
|
612
707
|
overflow: Platform.select({ default: 'scroll', web: undefined }),
|
|
613
708
|
},
|
|
@@ -624,6 +719,7 @@ const styles = StyleSheet.create({
|
|
|
624
719
|
zIndex: 1,
|
|
625
720
|
},
|
|
626
721
|
tabContent: {
|
|
722
|
+
flexGrow: 1,
|
|
627
723
|
flexDirection: 'row',
|
|
628
724
|
flexWrap: 'nowrap',
|
|
629
725
|
},
|
package/src/TabBarIndicator.tsx
CHANGED
|
@@ -2,30 +2,37 @@ import * as React from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Easing,
|
|
5
|
-
I18nManager,
|
|
6
5
|
Platform,
|
|
7
|
-
StyleProp,
|
|
6
|
+
type StyleProp,
|
|
8
7
|
StyleSheet,
|
|
9
|
-
ViewStyle,
|
|
8
|
+
type ViewStyle,
|
|
10
9
|
} from 'react-native';
|
|
11
10
|
|
|
12
|
-
import type {
|
|
11
|
+
import type {
|
|
12
|
+
LocaleDirection,
|
|
13
|
+
NavigationState,
|
|
14
|
+
Route,
|
|
15
|
+
SceneRendererProps,
|
|
16
|
+
} from './types';
|
|
13
17
|
import { useAnimatedValue } from './useAnimatedValue';
|
|
14
18
|
|
|
15
19
|
export type GetTabWidth = (index: number) => number;
|
|
16
20
|
|
|
17
21
|
export type Props<T extends Route> = SceneRendererProps & {
|
|
18
22
|
navigationState: NavigationState<T>;
|
|
19
|
-
width:
|
|
20
|
-
style?: StyleProp<ViewStyle>;
|
|
23
|
+
width: 'auto' | `${number}%` | number;
|
|
21
24
|
getTabWidth: GetTabWidth;
|
|
25
|
+
direction: LocaleDirection;
|
|
26
|
+
style?: StyleProp<ViewStyle>;
|
|
22
27
|
gap?: number;
|
|
28
|
+
children?: React.ReactNode;
|
|
23
29
|
};
|
|
24
30
|
|
|
25
31
|
const getTranslateX = (
|
|
26
32
|
position: Animated.AnimatedInterpolation<number>,
|
|
27
33
|
routes: Route[],
|
|
28
34
|
getTabWidth: GetTabWidth,
|
|
35
|
+
direction: LocaleDirection,
|
|
29
36
|
gap?: number
|
|
30
37
|
) => {
|
|
31
38
|
const inputRange = routes.map((_, i) => i);
|
|
@@ -42,7 +49,7 @@ const getTranslateX = (
|
|
|
42
49
|
extrapolate: 'clamp',
|
|
43
50
|
});
|
|
44
51
|
|
|
45
|
-
return Animated.multiply(translateX,
|
|
52
|
+
return Animated.multiply(translateX, direction === 'rtl' ? -1 : 1);
|
|
46
53
|
};
|
|
47
54
|
|
|
48
55
|
export function TabBarIndicator<T extends Route>({
|
|
@@ -51,8 +58,10 @@ export function TabBarIndicator<T extends Route>({
|
|
|
51
58
|
navigationState,
|
|
52
59
|
position,
|
|
53
60
|
width,
|
|
61
|
+
direction,
|
|
54
62
|
gap,
|
|
55
63
|
style,
|
|
64
|
+
children,
|
|
56
65
|
}: Props<T>) {
|
|
57
66
|
const isIndicatorShown = React.useRef(false);
|
|
58
67
|
const isWidthDynamic = width === 'auto';
|
|
@@ -96,7 +105,9 @@ export function TabBarIndicator<T extends Route>({
|
|
|
96
105
|
|
|
97
106
|
if (layout.width) {
|
|
98
107
|
const translateX =
|
|
99
|
-
routes.length > 1
|
|
108
|
+
routes.length > 1
|
|
109
|
+
? getTranslateX(position, routes, getTabWidth, direction, gap)
|
|
110
|
+
: 0;
|
|
100
111
|
|
|
101
112
|
transform.push({ translateX });
|
|
102
113
|
}
|
|
@@ -116,7 +127,7 @@ export function TabBarIndicator<T extends Route>({
|
|
|
116
127
|
})
|
|
117
128
|
: outputRange[0],
|
|
118
129
|
},
|
|
119
|
-
{ translateX: 0.5 }
|
|
130
|
+
{ translateX: direction === 'rtl' ? -0.5 : 0.5 }
|
|
120
131
|
);
|
|
121
132
|
}
|
|
122
133
|
|
|
@@ -136,7 +147,9 @@ export function TabBarIndicator<T extends Route>({
|
|
|
136
147
|
width === 'auto' ? { opacity: opacity } : null,
|
|
137
148
|
style,
|
|
138
149
|
]}
|
|
139
|
-
|
|
150
|
+
>
|
|
151
|
+
{children}
|
|
152
|
+
</Animated.View>
|
|
140
153
|
);
|
|
141
154
|
}
|
|
142
155
|
|
package/src/TabBarItem.tsx
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
|
-
LayoutChangeEvent,
|
|
5
|
-
PressableAndroidRippleConfig,
|
|
6
|
-
StyleProp,
|
|
4
|
+
type LayoutChangeEvent,
|
|
5
|
+
type PressableAndroidRippleConfig,
|
|
6
|
+
type StyleProp,
|
|
7
7
|
StyleSheet,
|
|
8
|
-
TextStyle,
|
|
8
|
+
type TextStyle,
|
|
9
9
|
View,
|
|
10
|
-
ViewStyle,
|
|
10
|
+
type ViewStyle,
|
|
11
11
|
} from 'react-native';
|
|
12
12
|
import useLatestCallback from 'use-latest-callback';
|
|
13
13
|
|
|
@@ -234,11 +234,9 @@ const TabBarItemInternal = <T extends Route>({
|
|
|
234
234
|
accessibilityLabel={accessibilityLabel}
|
|
235
235
|
accessibilityRole="tab"
|
|
236
236
|
accessibilityState={{ selected: isFocused }}
|
|
237
|
-
// @ts-ignore: this is to support older React Native versions
|
|
238
|
-
accessibilityStates={isFocused ? ['selected'] : []}
|
|
239
237
|
pressColor={pressColor}
|
|
240
238
|
pressOpacity={pressOpacity}
|
|
241
|
-
|
|
239
|
+
unstable_pressDelay={0}
|
|
242
240
|
onLayout={onLayout}
|
|
243
241
|
onPress={onPress}
|
|
244
242
|
onLongPress={onLongPress}
|
package/src/TabView.tsx
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
I18nManager,
|
|
4
|
+
type LayoutChangeEvent,
|
|
5
|
+
Platform,
|
|
6
|
+
type StyleProp,
|
|
5
7
|
StyleSheet,
|
|
6
8
|
View,
|
|
7
|
-
ViewStyle,
|
|
9
|
+
type ViewStyle,
|
|
8
10
|
} from 'react-native';
|
|
9
11
|
|
|
10
12
|
import { Pager } from './Pager';
|
|
@@ -12,13 +14,14 @@ import { SceneView } from './SceneView';
|
|
|
12
14
|
import { TabBar } from './TabBar';
|
|
13
15
|
import type {
|
|
14
16
|
Layout,
|
|
17
|
+
LocaleDirection,
|
|
15
18
|
NavigationState,
|
|
16
19
|
PagerProps,
|
|
17
20
|
Route,
|
|
18
21
|
SceneRendererProps,
|
|
19
22
|
} from './types';
|
|
20
23
|
|
|
21
|
-
export type Props<T extends Route> = PagerProps & {
|
|
24
|
+
export type Props<T extends Route> = Omit<PagerProps, 'layoutDirection'> & {
|
|
22
25
|
onIndexChange: (index: number) => void;
|
|
23
26
|
navigationState: NavigationState<T>;
|
|
24
27
|
renderScene: (props: SceneRendererProps & { route: T }) => React.ReactNode;
|
|
@@ -31,6 +34,7 @@ export type Props<T extends Route> = PagerProps & {
|
|
|
31
34
|
lazy?: ((props: { route: T }) => boolean) | boolean;
|
|
32
35
|
lazyPreloadDistance?: number;
|
|
33
36
|
sceneContainerStyle?: StyleProp<ViewStyle>;
|
|
37
|
+
direction?: LocaleDirection;
|
|
34
38
|
pagerStyle?: StyleProp<ViewStyle>;
|
|
35
39
|
style?: StyleProp<ViewStyle>;
|
|
36
40
|
};
|
|
@@ -50,11 +54,23 @@ export function TabView<T extends Route>({
|
|
|
50
54
|
sceneContainerStyle,
|
|
51
55
|
pagerStyle,
|
|
52
56
|
style,
|
|
57
|
+
direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
|
|
53
58
|
swipeEnabled = true,
|
|
54
59
|
tabBarPosition = 'top',
|
|
55
60
|
animationEnabled = true,
|
|
56
61
|
overScrollMode,
|
|
57
62
|
}: Props<T>) {
|
|
63
|
+
if (
|
|
64
|
+
Platform.OS !== 'web' &&
|
|
65
|
+
direction !== (I18nManager.getConstants().isRTL ? 'rtl' : 'ltr')
|
|
66
|
+
) {
|
|
67
|
+
console.warn(
|
|
68
|
+
`The 'direction' prop is set to '${direction}' but the effective value is '${
|
|
69
|
+
I18nManager.getConstants().isRTL ? 'rtl' : 'ltr'
|
|
70
|
+
}'. This is not supported. Please use I18nManager.forceRTL to change the layout direction.`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
58
74
|
const [layout, setLayout] = React.useState({
|
|
59
75
|
width: 0,
|
|
60
76
|
height: 0,
|
|
@@ -92,6 +108,7 @@ export function TabView<T extends Route>({
|
|
|
92
108
|
animationEnabled={animationEnabled}
|
|
93
109
|
overScrollMode={overScrollMode}
|
|
94
110
|
style={pagerStyle}
|
|
111
|
+
layoutDirection={direction}
|
|
95
112
|
>
|
|
96
113
|
{({ position, render, addEnterListener, jumpTo }) => {
|
|
97
114
|
// All of the props here must not change between re-renders
|