react-native-tab-view 5.0.0-alpha.1 → 5.0.0-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/module/DefaultAdapter.android.js +4 -0
- package/lib/module/DefaultAdapter.android.js.map +1 -0
- package/lib/module/DefaultAdapter.ios.js +4 -0
- package/lib/module/DefaultAdapter.ios.js.map +1 -0
- package/lib/module/DefaultAdapter.js +5 -0
- package/lib/module/DefaultAdapter.js.map +1 -0
- package/lib/module/PagerViewAdapter.native.js +29 -13
- package/lib/module/PagerViewAdapter.native.js.map +1 -1
- package/lib/module/PanResponderAdapter.js +3 -0
- package/lib/module/PanResponderAdapter.js.map +1 -1
- package/lib/module/PlatformPressable.js +1 -1
- package/lib/module/PlatformPressable.js.map +1 -1
- package/lib/module/SceneMap.js +12 -6
- package/lib/module/SceneMap.js.map +1 -1
- package/lib/module/SceneView.js +25 -1
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/ScrollViewAdapter.js +49 -18
- package/lib/module/ScrollViewAdapter.js.map +1 -1
- package/lib/module/TabBar.js +279 -169
- package/lib/module/TabBar.js.map +1 -1
- package/lib/module/TabBarIndicator.js +314 -90
- package/lib/module/TabBarIndicator.js.map +1 -1
- package/lib/module/TabBarItem.js +94 -44
- package/lib/module/TabBarItem.js.map +1 -1
- package/lib/module/TabBarItemLabel.js +3 -2
- package/lib/module/TabBarItemLabel.js.map +1 -1
- package/lib/module/TabView.js +2 -2
- package/lib/module/TabView.js.map +1 -1
- package/lib/module/constants.js +10 -0
- package/lib/module/constants.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/useLayoutWidths.js +46 -0
- package/lib/module/useLayoutWidths.js.map +1 -0
- package/lib/typescript/src/DefaultAdapter.android.d.ts +2 -0
- package/lib/typescript/src/DefaultAdapter.android.d.ts.map +1 -0
- package/lib/typescript/src/DefaultAdapter.d.ts +6 -0
- package/lib/typescript/src/DefaultAdapter.d.ts.map +1 -0
- package/lib/typescript/src/DefaultAdapter.ios.d.ts +2 -0
- package/lib/typescript/src/DefaultAdapter.ios.d.ts.map +1 -0
- package/lib/typescript/src/PagerViewAdapter.d.ts +1 -1
- package/lib/typescript/src/PagerViewAdapter.d.ts.map +1 -1
- package/lib/typescript/src/PagerViewAdapter.native.d.ts +2 -2
- package/lib/typescript/src/PagerViewAdapter.native.d.ts.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 +5 -5
- 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/ScrollViewAdapter.d.ts +2 -3
- package/lib/typescript/src/ScrollViewAdapter.d.ts.map +1 -1
- package/lib/typescript/src/TabBar.d.ts +26 -25
- package/lib/typescript/src/TabBar.d.ts.map +1 -1
- package/lib/typescript/src/TabBarIndicator.d.ts +5 -7
- package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItem.d.ts +17 -10
- package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItemLabel.d.ts +4 -3
- package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -1
- package/lib/typescript/src/TabView.d.ts +17 -17
- package/lib/typescript/src/TabView.d.ts.map +1 -1
- package/lib/typescript/src/constants.d.ts +8 -0
- package/lib/typescript/src/constants.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +13 -12
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +31 -31
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useLayoutWidths.d.ts +2 -0
- package/lib/typescript/src/useLayoutWidths.d.ts.map +1 -0
- package/lib/typescript/src/useMeasureLayout.d.ts +1 -1
- package/lib/typescript/src/useMeasureLayout.d.ts.map +1 -1
- package/package.json +13 -12
- package/src/DefaultAdapter.android.tsx +1 -0
- package/src/DefaultAdapter.ios.tsx +1 -0
- package/src/DefaultAdapter.tsx +13 -0
- package/src/PagerViewAdapter.native.tsx +36 -18
- package/src/PanResponderAdapter.tsx +4 -0
- package/src/PlatformPressable.tsx +11 -7
- package/src/SceneMap.tsx +17 -9
- package/src/SceneView.tsx +41 -2
- package/src/ScrollViewAdapter.tsx +88 -22
- package/src/TabBar.tsx +440 -235
- package/src/TabBarIndicator.tsx +389 -124
- package/src/TabBarItem.tsx +107 -46
- package/src/TabBarItemLabel.tsx +5 -4
- package/src/TabView.tsx +24 -20
- package/src/constants.tsx +8 -0
- package/src/index.tsx +5 -1
- package/src/types.tsx +40 -36
- package/src/useLayoutWidths.tsx +51 -0
- package/lib/module/Pager.android.js +0 -4
- package/lib/module/Pager.android.js.map +0 -1
- package/lib/module/Pager.ios.js +0 -4
- package/lib/module/Pager.ios.js.map +0 -1
- package/lib/module/Pager.js +0 -4
- package/lib/module/Pager.js.map +0 -1
- package/lib/typescript/src/Pager.android.d.ts +0 -2
- package/lib/typescript/src/Pager.android.d.ts.map +0 -1
- package/lib/typescript/src/Pager.d.ts +0 -2
- package/lib/typescript/src/Pager.d.ts.map +0 -1
- package/lib/typescript/src/Pager.ios.d.ts +0 -2
- package/lib/typescript/src/Pager.ios.d.ts.map +0 -1
- package/src/Pager.android.tsx +0 -1
- package/src/Pager.ios.tsx +0 -1
- package/src/Pager.tsx +0 -1
package/src/TabBar.tsx
CHANGED
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
type DimensionValue,
|
|
6
6
|
FlatList,
|
|
7
7
|
I18nManager,
|
|
8
|
-
type LayoutChangeEvent,
|
|
9
8
|
type ListRenderItemInfo,
|
|
10
9
|
Platform,
|
|
11
10
|
type PressableAndroidRippleConfig,
|
|
@@ -13,10 +12,14 @@ import {
|
|
|
13
12
|
StyleSheet,
|
|
14
13
|
View,
|
|
15
14
|
type ViewStyle,
|
|
16
|
-
type ViewToken,
|
|
17
15
|
} from 'react-native';
|
|
18
|
-
import useLatestCallback from 'use-latest-callback';
|
|
19
16
|
|
|
17
|
+
import {
|
|
18
|
+
PRIMARY_INDICATOR_MIN_WIDTH,
|
|
19
|
+
TAB_BAR_BACKGROUND_COLOR,
|
|
20
|
+
TAB_BAR_BORDER_COLOR,
|
|
21
|
+
TAB_MIN_WIDTH,
|
|
22
|
+
} from './constants';
|
|
20
23
|
import {
|
|
21
24
|
type Props as IndicatorProps,
|
|
22
25
|
TabBarIndicator,
|
|
@@ -33,33 +36,37 @@ import type {
|
|
|
33
36
|
TabDescriptor,
|
|
34
37
|
} from './types';
|
|
35
38
|
import { useAnimatedValue } from './useAnimatedValue';
|
|
39
|
+
import { useLayoutWidths } from './useLayoutWidths';
|
|
36
40
|
import { useMeasureLayout } from './useMeasureLayout';
|
|
37
41
|
|
|
38
42
|
export type Props<T extends Route> = SceneRendererProps &
|
|
39
43
|
EventEmitterProps & {
|
|
44
|
+
variant?: 'primary' | 'secondary' | undefined;
|
|
40
45
|
navigationState: NavigationState<T>;
|
|
41
|
-
scrollEnabled?: boolean;
|
|
42
|
-
bounces?: boolean;
|
|
43
|
-
activeColor?: ColorValue;
|
|
44
|
-
inactiveColor?: ColorValue;
|
|
45
|
-
pressColor?: ColorValue;
|
|
46
|
-
pressOpacity?: number;
|
|
47
|
-
options?: Record<string, TabDescriptor<T
|
|
48
|
-
renderIndicator?:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
46
|
+
scrollEnabled?: boolean | undefined;
|
|
47
|
+
bounces?: boolean | undefined;
|
|
48
|
+
activeColor?: ColorValue | undefined;
|
|
49
|
+
inactiveColor?: ColorValue | undefined;
|
|
50
|
+
pressColor?: ColorValue | undefined;
|
|
51
|
+
pressOpacity?: number | undefined;
|
|
52
|
+
options?: Record<string, TabDescriptor<T>> | undefined;
|
|
53
|
+
renderIndicator?:
|
|
54
|
+
| ((props: IndicatorProps<T>) => React.ReactNode)
|
|
55
|
+
| undefined;
|
|
56
|
+
renderTabBarItem?:
|
|
57
|
+
| ((props: TabBarItemProps<T> & { key: string }) => React.ReactElement)
|
|
58
|
+
| undefined;
|
|
59
|
+
onTabPress?: ((scene: Scene<T> & Event) => void) | undefined;
|
|
60
|
+
onTabLongPress?: ((scene: Scene<T>) => void) | undefined;
|
|
61
|
+
tabStyle?: StyleProp<ViewStyle> | undefined;
|
|
62
|
+
indicatorStyle?: StyleProp<ViewStyle> | undefined;
|
|
63
|
+
indicatorContainerStyle?: StyleProp<ViewStyle> | undefined;
|
|
64
|
+
contentContainerStyle?: StyleProp<ViewStyle> | undefined;
|
|
65
|
+
style?: StyleProp<ViewStyle> | undefined;
|
|
66
|
+
direction?: LocaleDirection | undefined;
|
|
67
|
+
gap?: number | undefined;
|
|
68
|
+
testID?: string | undefined;
|
|
69
|
+
android_ripple?: PressableAndroidRippleConfig | undefined;
|
|
63
70
|
};
|
|
64
71
|
|
|
65
72
|
type CalculationOptions = {
|
|
@@ -72,8 +79,6 @@ type CalculationOptions = {
|
|
|
72
79
|
flattenedTabWidth: DimensionValue | undefined;
|
|
73
80
|
};
|
|
74
81
|
|
|
75
|
-
const useNativeDriver = Platform.OS !== 'web';
|
|
76
|
-
|
|
77
82
|
const Separator = ({ width }: { width: number }) => {
|
|
78
83
|
return <View style={{ width }} />;
|
|
79
84
|
};
|
|
@@ -138,8 +143,14 @@ const getComputedTabWidth = ({
|
|
|
138
143
|
index: number;
|
|
139
144
|
routes: Route[];
|
|
140
145
|
}) => {
|
|
146
|
+
const route = routes[index];
|
|
147
|
+
|
|
148
|
+
if (route == null) {
|
|
149
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
150
|
+
}
|
|
151
|
+
|
|
141
152
|
if (flattenedTabWidth === 'auto') {
|
|
142
|
-
return tabWidths[
|
|
153
|
+
return tabWidths[route.key] || 0;
|
|
143
154
|
}
|
|
144
155
|
|
|
145
156
|
switch (typeof flattenedTabWidth) {
|
|
@@ -155,7 +166,7 @@ const getComputedTabWidth = ({
|
|
|
155
166
|
}
|
|
156
167
|
|
|
157
168
|
if (scrollEnabled) {
|
|
158
|
-
return
|
|
169
|
+
return tabWidths[route.key] || TAB_MIN_WIDTH;
|
|
159
170
|
}
|
|
160
171
|
|
|
161
172
|
const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
|
|
@@ -166,6 +177,25 @@ const getComputedTabWidth = ({
|
|
|
166
177
|
return (layoutWidth - gapTotalWidth - paddingTotalWidth) / routes.length;
|
|
167
178
|
};
|
|
168
179
|
|
|
180
|
+
const calculateSize = (
|
|
181
|
+
value: ViewStyle['width'] | undefined,
|
|
182
|
+
referenceWidth: number
|
|
183
|
+
): number | undefined => {
|
|
184
|
+
if (typeof value === 'number') {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
if (typeof value === 'string' && value.endsWith('%')) {
|
|
189
|
+
const parsed = parseFloat(value);
|
|
190
|
+
|
|
191
|
+
if (Number.isFinite(parsed)) {
|
|
192
|
+
return referenceWidth * (parsed / 100);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return undefined;
|
|
197
|
+
};
|
|
198
|
+
|
|
169
199
|
const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
|
|
170
200
|
tabBarWidth - layoutWidth;
|
|
171
201
|
|
|
@@ -173,13 +203,15 @@ const getTranslateX = (
|
|
|
173
203
|
scrollAmount: Animated.Value,
|
|
174
204
|
maxScrollDistance: number,
|
|
175
205
|
direction: LocaleDirection
|
|
176
|
-
) =>
|
|
177
|
-
|
|
206
|
+
) => {
|
|
207
|
+
const amount =
|
|
208
|
+
// Android reports scroll from the opposite side in RTL
|
|
178
209
|
Platform.OS === 'android' && direction === 'rtl'
|
|
179
210
|
? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1))
|
|
180
|
-
: scrollAmount
|
|
181
|
-
|
|
182
|
-
);
|
|
211
|
+
: scrollAmount;
|
|
212
|
+
|
|
213
|
+
return Animated.multiply(amount, direction === 'rtl' ? 1 : -1);
|
|
214
|
+
};
|
|
183
215
|
|
|
184
216
|
const getTabBarWidth = <T extends Route>({
|
|
185
217
|
routes,
|
|
@@ -228,11 +260,9 @@ const normalizeScrollValue = <T extends Route>({
|
|
|
228
260
|
flattenedTabWidth,
|
|
229
261
|
flattenedPaddingStart,
|
|
230
262
|
flattenedPaddingEnd,
|
|
231
|
-
direction,
|
|
232
263
|
}: CalculationOptions & {
|
|
233
264
|
routes: T[];
|
|
234
265
|
value: number;
|
|
235
|
-
direction: LocaleDirection;
|
|
236
266
|
}) => {
|
|
237
267
|
const tabBarWidth = getTabBarWidth({
|
|
238
268
|
layoutWidth,
|
|
@@ -247,12 +277,6 @@ const normalizeScrollValue = <T extends Route>({
|
|
|
247
277
|
const maxDistance = getMaxScrollDistance(tabBarWidth, layoutWidth);
|
|
248
278
|
const scrollValue = Math.max(Math.min(value, maxDistance), 0);
|
|
249
279
|
|
|
250
|
-
if (Platform.OS === 'android' && direction === 'rtl') {
|
|
251
|
-
// On Android, scroll value is not applied in reverse in RTL
|
|
252
|
-
// so we need to manually adjust it to apply correct value
|
|
253
|
-
return maxDistance - scrollValue;
|
|
254
|
-
}
|
|
255
|
-
|
|
256
280
|
return scrollValue;
|
|
257
281
|
};
|
|
258
282
|
|
|
@@ -311,7 +335,6 @@ const getScrollAmount = <T extends Route>({
|
|
|
311
335
|
flattenedTabWidth,
|
|
312
336
|
flattenedPaddingStart,
|
|
313
337
|
flattenedPaddingEnd,
|
|
314
|
-
direction,
|
|
315
338
|
});
|
|
316
339
|
};
|
|
317
340
|
const getLabelTextDefault = ({ route }: Scene<Route>) => route.title;
|
|
@@ -334,9 +357,10 @@ const getTestIdDefault = ({ route }: Scene<Route>) => route.testID;
|
|
|
334
357
|
|
|
335
358
|
// How many items measurements should we update per batch.
|
|
336
359
|
// Defaults to 10, since that's whats FlatList is using in initialNumToRender.
|
|
337
|
-
const
|
|
360
|
+
const RENDER_PER_BATCH = 10;
|
|
338
361
|
|
|
339
362
|
export function TabBar<T extends Route>({
|
|
363
|
+
variant = 'primary',
|
|
340
364
|
renderIndicator = renderIndicatorDefault,
|
|
341
365
|
gap = 0,
|
|
342
366
|
scrollEnabled,
|
|
@@ -364,17 +388,40 @@ export function TabBar<T extends Route>({
|
|
|
364
388
|
const containerRef = React.useRef<View>(null);
|
|
365
389
|
const [layout, onLayout] = useMeasureLayout(containerRef);
|
|
366
390
|
|
|
367
|
-
|
|
391
|
+
// Prioritize measuring tabs upto focused item
|
|
392
|
+
// Since we need those measurements for calculation
|
|
393
|
+
const priorityKeysForLayout = navigationState.routes
|
|
394
|
+
.slice(0, navigationState.index + 1)
|
|
395
|
+
.map((r) => r.key);
|
|
396
|
+
|
|
397
|
+
const [tabWidths, onMeasureTabWidth] = useLayoutWidths(priorityKeysForLayout);
|
|
398
|
+
const [labelWidths, onMeasureLabelWidth] = useLayoutWidths(
|
|
399
|
+
priorityKeysForLayout
|
|
400
|
+
);
|
|
401
|
+
|
|
368
402
|
const flatListRef = React.useRef<FlatList | null>(null);
|
|
369
403
|
const isFirst = React.useRef(true);
|
|
370
|
-
|
|
371
|
-
const measuredTabWidths = React.useRef<Record<string, number>>({});
|
|
404
|
+
|
|
372
405
|
const { routes } = navigationState;
|
|
406
|
+
|
|
373
407
|
const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
|
|
374
|
-
const
|
|
408
|
+
const isWidthAuto = flattenedTabWidth === 'auto';
|
|
409
|
+
const isWidthDynamic =
|
|
410
|
+
isWidthAuto || (scrollEnabled && flattenedTabWidth == null);
|
|
411
|
+
|
|
375
412
|
const flattenedPaddingEnd = getFlattenedPaddingEnd(contentContainerStyle);
|
|
376
413
|
const flattenedPaddingStart = getFlattenedPaddingStart(contentContainerStyle);
|
|
377
414
|
|
|
415
|
+
const paddingEnd = convertPaddingPercentToSize(
|
|
416
|
+
flattenedPaddingEnd,
|
|
417
|
+
layout.width
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const paddingStart = convertPaddingPercentToSize(
|
|
421
|
+
flattenedPaddingStart,
|
|
422
|
+
layout.width
|
|
423
|
+
);
|
|
424
|
+
|
|
378
425
|
const scrollOffset = getScrollAmount({
|
|
379
426
|
layoutWidth: layout.width,
|
|
380
427
|
routes,
|
|
@@ -400,7 +447,7 @@ export function TabBar<T extends Route>({
|
|
|
400
447
|
return;
|
|
401
448
|
}
|
|
402
449
|
|
|
403
|
-
if (
|
|
450
|
+
if (isWidthAuto && !hasMeasuredTabWidths) {
|
|
404
451
|
return;
|
|
405
452
|
}
|
|
406
453
|
|
|
@@ -410,7 +457,7 @@ export function TabBar<T extends Route>({
|
|
|
410
457
|
animated: true,
|
|
411
458
|
});
|
|
412
459
|
}
|
|
413
|
-
}, [hasMeasuredTabWidths,
|
|
460
|
+
}, [hasMeasuredTabWidths, isWidthAuto, scrollEnabled, scrollOffset]);
|
|
414
461
|
|
|
415
462
|
const tabBarWidth = getTabBarWidth({
|
|
416
463
|
layoutWidth: layout.width,
|
|
@@ -423,152 +470,86 @@ export function TabBar<T extends Route>({
|
|
|
423
470
|
flattenedPaddingEnd,
|
|
424
471
|
});
|
|
425
472
|
|
|
426
|
-
const
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
473
|
+
const maxScrollDistance = getMaxScrollDistance(tabBarWidth, layout.width);
|
|
474
|
+
const scrollAmount = useAnimatedValue(0);
|
|
475
|
+
|
|
476
|
+
React.useLayoutEffect(() => {
|
|
477
|
+
scrollAmount.setValue(
|
|
478
|
+
Platform.OS === 'android' && direction === 'rtl' ? maxScrollDistance : 0
|
|
479
|
+
);
|
|
480
|
+
}, [direction, maxScrollDistance, scrollAmount]);
|
|
432
481
|
|
|
433
482
|
const translateX = React.useMemo(
|
|
434
|
-
() =>
|
|
435
|
-
|
|
436
|
-
scrollAmount,
|
|
437
|
-
getMaxScrollDistance(tabBarWidth, layout.width),
|
|
438
|
-
direction
|
|
439
|
-
),
|
|
440
|
-
[direction, layout.width, scrollAmount, tabBarWidth]
|
|
483
|
+
() => getTranslateX(scrollAmount, maxScrollDistance, direction),
|
|
484
|
+
[direction, maxScrollDistance, scrollAmount]
|
|
441
485
|
);
|
|
442
486
|
|
|
443
|
-
const
|
|
444
|
-
|
|
445
|
-
const {
|
|
446
|
-
testID = getTestIdDefault({ route }),
|
|
447
|
-
labelText = getLabelTextDefault({ route }),
|
|
448
|
-
accessible = getAccessibleDefault({ route }),
|
|
449
|
-
accessibilityLabel = getAccessibilityLabelDefault({ route }),
|
|
450
|
-
...rest
|
|
451
|
-
} = options?.[route.key] ?? {};
|
|
452
|
-
|
|
453
|
-
const onLayout = isWidthDynamic
|
|
454
|
-
? (e: LayoutChangeEvent) => {
|
|
455
|
-
measuredTabWidths.current[route.key] = e.nativeEvent.layout.width;
|
|
456
|
-
|
|
457
|
-
// When we have measured widths for all of the tabs, we should updates the state
|
|
458
|
-
// We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
|
|
459
|
-
// If we have more than 10 routes divide updating tabWidths into multiple batches. Here we update only first batch of 10 items.
|
|
460
|
-
if (
|
|
461
|
-
routes.length > MEASURE_PER_BATCH &&
|
|
462
|
-
index === MEASURE_PER_BATCH &&
|
|
463
|
-
routes
|
|
464
|
-
.slice(0, MEASURE_PER_BATCH)
|
|
465
|
-
.every(
|
|
466
|
-
(r) => typeof measuredTabWidths.current[r.key] === 'number'
|
|
467
|
-
)
|
|
468
|
-
) {
|
|
469
|
-
setTabWidths({ ...measuredTabWidths.current });
|
|
470
|
-
} else if (
|
|
471
|
-
routes.every(
|
|
472
|
-
(r) => typeof measuredTabWidths.current[r.key] === 'number'
|
|
473
|
-
)
|
|
474
|
-
) {
|
|
475
|
-
// When we have measured widths for all of the tabs, we should updates the state
|
|
476
|
-
// We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
|
|
477
|
-
setTabWidths({ ...measuredTabWidths.current });
|
|
478
|
-
}
|
|
479
|
-
}
|
|
480
|
-
: undefined;
|
|
481
|
-
|
|
482
|
-
const onPress = () => {
|
|
483
|
-
const event: Scene<T> & Event = {
|
|
484
|
-
route,
|
|
485
|
-
defaultPrevented: false,
|
|
486
|
-
preventDefault: () => {
|
|
487
|
-
event.defaultPrevented = true;
|
|
488
|
-
},
|
|
489
|
-
};
|
|
490
|
-
|
|
491
|
-
onTabPress?.(event);
|
|
487
|
+
const flattenedTabStyle = StyleSheet.flatten(tabStyle);
|
|
488
|
+
const isTabWidthSet = flattenedTabStyle?.width !== undefined;
|
|
492
489
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
490
|
+
// Calculate the default width for tab for FlatList to work.
|
|
491
|
+
const defaultTabWidth = !isWidthDynamic
|
|
492
|
+
? getComputedTabWidth({
|
|
493
|
+
// When `isWidthDynamic` is false, every tab gets the same width and
|
|
494
|
+
// `getComputedTabWidth` ignores `index`, so we compute it once with index 0.
|
|
495
|
+
index: 0,
|
|
496
|
+
layoutWidth: layout.width,
|
|
497
|
+
routes,
|
|
498
|
+
scrollEnabled,
|
|
499
|
+
tabWidths,
|
|
500
|
+
flattenedTabWidth,
|
|
501
|
+
flattenedPaddingStart,
|
|
502
|
+
flattenedPaddingEnd,
|
|
503
|
+
gap,
|
|
504
|
+
})
|
|
505
|
+
: undefined;
|
|
496
506
|
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
navigationState,
|
|
524
|
-
testID,
|
|
525
|
-
labelText,
|
|
526
|
-
accessible,
|
|
527
|
-
accessibilityLabel,
|
|
528
|
-
activeColor,
|
|
529
|
-
inactiveColor,
|
|
530
|
-
pressColor,
|
|
531
|
-
pressOpacity,
|
|
532
|
-
onLayout,
|
|
533
|
-
onPress,
|
|
534
|
-
onLongPress,
|
|
535
|
-
style: tabStyle,
|
|
536
|
-
defaultTabWidth,
|
|
537
|
-
android_ripple,
|
|
538
|
-
} satisfies TabBarItemProps<T>;
|
|
539
|
-
|
|
540
|
-
return (
|
|
541
|
-
<>
|
|
542
|
-
{gap > 0 && index > 0 ? <Separator width={gap} /> : null}
|
|
543
|
-
{renderTabBarItem ? (
|
|
544
|
-
renderTabBarItem({ key: route.key, ...props })
|
|
545
|
-
) : (
|
|
546
|
-
<TabBarItem key={route.key} {...props} />
|
|
547
|
-
)}
|
|
548
|
-
</>
|
|
549
|
-
);
|
|
550
|
-
},
|
|
507
|
+
const renderItem = React.useCallback(
|
|
508
|
+
({ item: route, index }: ListRenderItemInfo<T>) => (
|
|
509
|
+
<MemoizedTabBarItemWrapper
|
|
510
|
+
route={route}
|
|
511
|
+
index={index}
|
|
512
|
+
option={options?.[route.key]}
|
|
513
|
+
position={position}
|
|
514
|
+
navigationState={navigationState}
|
|
515
|
+
variant={variant}
|
|
516
|
+
activeColor={activeColor}
|
|
517
|
+
inactiveColor={inactiveColor}
|
|
518
|
+
pressColor={pressColor}
|
|
519
|
+
pressOpacity={pressOpacity}
|
|
520
|
+
android_ripple={android_ripple}
|
|
521
|
+
tabStyle={tabStyle}
|
|
522
|
+
defaultTabWidth={defaultTabWidth}
|
|
523
|
+
isWidthSet={isTabWidthSet}
|
|
524
|
+
gap={gap}
|
|
525
|
+
onMeasureTabWidth={onMeasureTabWidth}
|
|
526
|
+
onMeasureLabelWidth={onMeasureLabelWidth}
|
|
527
|
+
onTabPress={onTabPress}
|
|
528
|
+
onTabLongPress={onTabLongPress}
|
|
529
|
+
jumpTo={jumpTo}
|
|
530
|
+
renderTabBarItem={renderTabBarItem}
|
|
531
|
+
/>
|
|
532
|
+
),
|
|
551
533
|
[
|
|
534
|
+
options,
|
|
552
535
|
position,
|
|
553
536
|
navigationState,
|
|
554
|
-
|
|
537
|
+
variant,
|
|
555
538
|
activeColor,
|
|
556
539
|
inactiveColor,
|
|
557
540
|
pressColor,
|
|
558
541
|
pressOpacity,
|
|
559
|
-
|
|
542
|
+
android_ripple,
|
|
560
543
|
tabStyle,
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
scrollEnabled,
|
|
564
|
-
tabWidths,
|
|
565
|
-
contentContainerStyle,
|
|
544
|
+
defaultTabWidth,
|
|
545
|
+
isTabWidthSet,
|
|
566
546
|
gap,
|
|
567
|
-
|
|
568
|
-
|
|
547
|
+
onMeasureTabWidth,
|
|
548
|
+
onMeasureLabelWidth,
|
|
569
549
|
onTabPress,
|
|
570
|
-
jumpTo,
|
|
571
550
|
onTabLongPress,
|
|
551
|
+
jumpTo,
|
|
552
|
+
renderTabBarItem,
|
|
572
553
|
]
|
|
573
554
|
);
|
|
574
555
|
|
|
@@ -593,35 +574,138 @@ export function TabBar<T extends Route>({
|
|
|
593
574
|
},
|
|
594
575
|
},
|
|
595
576
|
],
|
|
596
|
-
{ useNativeDriver }
|
|
577
|
+
{ useNativeDriver: Platform.OS !== 'web' }
|
|
597
578
|
),
|
|
598
579
|
[scrollAmount]
|
|
599
580
|
);
|
|
600
581
|
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
582
|
+
const flattenedIndicatorStyle = StyleSheet.flatten(indicatorStyle);
|
|
583
|
+
const defaultIndicatorStyle =
|
|
584
|
+
variant === 'primary' ? styles.primaryIndicator : styles.secondaryIndicator;
|
|
585
|
+
|
|
586
|
+
const tabWidthByIndex = routes.map((_, i) =>
|
|
587
|
+
getComputedTabWidth({
|
|
588
|
+
index: i,
|
|
589
|
+
layoutWidth: layout.width,
|
|
590
|
+
routes,
|
|
591
|
+
scrollEnabled,
|
|
592
|
+
tabWidths,
|
|
593
|
+
flattenedTabWidth,
|
|
594
|
+
flattenedPaddingEnd,
|
|
595
|
+
flattenedPaddingStart,
|
|
596
|
+
gap,
|
|
597
|
+
})
|
|
598
|
+
);
|
|
599
|
+
|
|
600
|
+
const indicatorBaseWidths = tabWidthByIndex.map((tabWidth, i) => {
|
|
601
|
+
const customIndicatorWidth = calculateSize(
|
|
602
|
+
flattenedIndicatorStyle?.width,
|
|
603
|
+
tabWidth
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
if (customIndicatorWidth != null) {
|
|
607
|
+
return customIndicatorWidth;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (variant === 'primary') {
|
|
611
|
+
const route = routes[i];
|
|
612
|
+
|
|
613
|
+
if (route == null) {
|
|
614
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
616
615
|
}
|
|
616
|
+
|
|
617
|
+
const labelWidth = labelWidths[route.key];
|
|
618
|
+
|
|
619
|
+
return labelWidth ? Math.max(PRIMARY_INDICATOR_MIN_WIDTH, labelWidth) : 0;
|
|
617
620
|
}
|
|
618
|
-
|
|
621
|
+
|
|
622
|
+
return tabWidth;
|
|
623
|
+
});
|
|
624
|
+
|
|
625
|
+
const indicatorMargins = indicatorBaseWidths.map((width) => {
|
|
626
|
+
if (!width) {
|
|
627
|
+
return { left: 0, right: 0 };
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
const marginHorizontal =
|
|
631
|
+
flattenedIndicatorStyle?.marginHorizontal ??
|
|
632
|
+
flattenedIndicatorStyle?.margin;
|
|
633
|
+
|
|
634
|
+
const leftMargin =
|
|
635
|
+
(direction === 'ltr'
|
|
636
|
+
? flattenedIndicatorStyle?.marginStart
|
|
637
|
+
: flattenedIndicatorStyle?.marginEnd) ??
|
|
638
|
+
flattenedIndicatorStyle?.marginLeft ??
|
|
639
|
+
marginHorizontal;
|
|
640
|
+
|
|
641
|
+
const rightMargin =
|
|
642
|
+
(direction === 'rtl'
|
|
643
|
+
? flattenedIndicatorStyle?.marginStart
|
|
644
|
+
: flattenedIndicatorStyle?.marginEnd) ??
|
|
645
|
+
flattenedIndicatorStyle?.marginRight ??
|
|
646
|
+
marginHorizontal;
|
|
647
|
+
|
|
648
|
+
return {
|
|
649
|
+
left: calculateSize(leftMargin, width) ?? 0,
|
|
650
|
+
right: calculateSize(rightMargin, width) ?? 0,
|
|
651
|
+
};
|
|
652
|
+
});
|
|
653
|
+
|
|
654
|
+
const indicatorWidths = indicatorBaseWidths.map((width, i) => {
|
|
655
|
+
if (!width) {
|
|
656
|
+
return 0;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
const margin = indicatorMargins[i];
|
|
660
|
+
|
|
661
|
+
if (margin == null) {
|
|
662
|
+
throw new Error(`Couldn't find an indicator margin at index ${i}.`);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
return Math.max(0, width - margin.left - margin.right);
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
const indicatorOffsets = tabWidthByIndex.map((tabWidth, i) => {
|
|
669
|
+
const precedingTabsWidth = tabWidthByIndex
|
|
670
|
+
.slice(0, i)
|
|
671
|
+
.reduce((sum, width) => sum + width, 0);
|
|
672
|
+
|
|
673
|
+
const tabStart = precedingTabsWidth + gap * i;
|
|
674
|
+
const customIndicatorWidth = calculateSize(
|
|
675
|
+
flattenedIndicatorStyle?.width,
|
|
676
|
+
tabWidth
|
|
677
|
+
);
|
|
678
|
+
|
|
679
|
+
const margin = indicatorMargins[i];
|
|
680
|
+
|
|
681
|
+
if (margin == null) {
|
|
682
|
+
throw new Error(`Couldn't find an indicator margin at index ${i}.`);
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
const indicatorBaseWidth = indicatorBaseWidths[i];
|
|
686
|
+
|
|
687
|
+
if (indicatorBaseWidth == null) {
|
|
688
|
+
throw new Error(`Couldn't find an indicator width at index ${i}.`);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
const shouldCenterIndicator =
|
|
692
|
+
variant === 'primary' ||
|
|
693
|
+
(customIndicatorWidth != null &&
|
|
694
|
+
(flattenedIndicatorStyle?.margin === 'auto' ||
|
|
695
|
+
flattenedIndicatorStyle?.marginHorizontal === 'auto'));
|
|
696
|
+
|
|
697
|
+
const baseOffset = shouldCenterIndicator
|
|
698
|
+
? (tabWidth - indicatorBaseWidth) / 2
|
|
699
|
+
: 0;
|
|
700
|
+
|
|
701
|
+
return tabStart + baseOffset + margin.left;
|
|
702
|
+
});
|
|
619
703
|
|
|
620
704
|
return (
|
|
621
705
|
<Animated.View
|
|
622
706
|
ref={containerRef}
|
|
623
707
|
onLayout={onLayout}
|
|
624
|
-
style={[styles.tabBar, style]}
|
|
708
|
+
style={[styles.tabBar, { direction }, style]}
|
|
625
709
|
>
|
|
626
710
|
<Animated.View
|
|
627
711
|
style={[
|
|
@@ -632,33 +716,18 @@ export function TabBar<T extends Route>({
|
|
|
632
716
|
]}
|
|
633
717
|
>
|
|
634
718
|
{renderIndicator({
|
|
719
|
+
variant,
|
|
635
720
|
position,
|
|
636
721
|
navigationState,
|
|
637
722
|
jumpTo,
|
|
638
723
|
direction,
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
: Math.max(
|
|
642
|
-
0,
|
|
643
|
-
(tabBarWidth - separatorsWidth - paddingsWidth) / routes.length
|
|
644
|
-
),
|
|
724
|
+
widths: indicatorWidths,
|
|
725
|
+
offsets: indicatorOffsets,
|
|
645
726
|
style: [
|
|
727
|
+
defaultIndicatorStyle,
|
|
646
728
|
indicatorStyle,
|
|
647
|
-
{ start:
|
|
729
|
+
{ start: paddingStart, end: paddingEnd },
|
|
648
730
|
],
|
|
649
|
-
getTabWidth: (i: number) =>
|
|
650
|
-
getComputedTabWidth({
|
|
651
|
-
index: i,
|
|
652
|
-
layoutWidth: layout.width,
|
|
653
|
-
routes,
|
|
654
|
-
scrollEnabled,
|
|
655
|
-
tabWidths,
|
|
656
|
-
flattenedTabWidth,
|
|
657
|
-
flattenedPaddingEnd,
|
|
658
|
-
flattenedPaddingStart,
|
|
659
|
-
gap,
|
|
660
|
-
}),
|
|
661
|
-
gap,
|
|
662
731
|
})}
|
|
663
732
|
</Animated.View>
|
|
664
733
|
<View style={styles.scroll}>
|
|
@@ -670,8 +739,7 @@ export function TabBar<T extends Route>({
|
|
|
670
739
|
keyboardShouldPersistTaps="handled"
|
|
671
740
|
scrollEnabled={scrollEnabled}
|
|
672
741
|
bounces={bounces}
|
|
673
|
-
initialNumToRender={
|
|
674
|
-
onViewableItemsChanged={handleViewableItemsChanged}
|
|
742
|
+
initialNumToRender={RENDER_PER_BATCH}
|
|
675
743
|
alwaysBounceHorizontal={false}
|
|
676
744
|
scrollsToTop={false}
|
|
677
745
|
showsHorizontalScrollIndicator={false}
|
|
@@ -690,28 +758,155 @@ export function TabBar<T extends Route>({
|
|
|
690
758
|
);
|
|
691
759
|
}
|
|
692
760
|
|
|
761
|
+
type TabBarItemWrapperProps<T extends Route> = {
|
|
762
|
+
route: T;
|
|
763
|
+
index: number;
|
|
764
|
+
option: TabDescriptor<T> | undefined;
|
|
765
|
+
position: Animated.AnimatedInterpolation<number>;
|
|
766
|
+
navigationState: NavigationState<T>;
|
|
767
|
+
variant: 'primary' | 'secondary';
|
|
768
|
+
activeColor: ColorValue | undefined;
|
|
769
|
+
inactiveColor: ColorValue | undefined;
|
|
770
|
+
pressColor: ColorValue | undefined;
|
|
771
|
+
pressOpacity: number | undefined;
|
|
772
|
+
android_ripple: PressableAndroidRippleConfig | undefined;
|
|
773
|
+
tabStyle: StyleProp<ViewStyle>;
|
|
774
|
+
defaultTabWidth: number | undefined;
|
|
775
|
+
isWidthSet: boolean;
|
|
776
|
+
gap: number;
|
|
777
|
+
onMeasureTabWidth: (key: string, width: number) => void;
|
|
778
|
+
onMeasureLabelWidth: (key: string, width: number) => void;
|
|
779
|
+
onTabPress: ((scene: Scene<T> & Event) => void) | undefined;
|
|
780
|
+
onTabLongPress: ((scene: Scene<T>) => void) | undefined;
|
|
781
|
+
jumpTo: (key: string) => void;
|
|
782
|
+
renderTabBarItem:
|
|
783
|
+
| ((props: TabBarItemProps<T> & { key: string }) => React.ReactElement)
|
|
784
|
+
| undefined;
|
|
785
|
+
};
|
|
786
|
+
|
|
787
|
+
function TabBarItemWrapper<T extends Route>({
|
|
788
|
+
route,
|
|
789
|
+
index,
|
|
790
|
+
option,
|
|
791
|
+
position,
|
|
792
|
+
navigationState,
|
|
793
|
+
variant,
|
|
794
|
+
activeColor,
|
|
795
|
+
inactiveColor,
|
|
796
|
+
pressColor,
|
|
797
|
+
pressOpacity,
|
|
798
|
+
android_ripple,
|
|
799
|
+
tabStyle,
|
|
800
|
+
defaultTabWidth,
|
|
801
|
+
isWidthSet,
|
|
802
|
+
gap,
|
|
803
|
+
onMeasureTabWidth,
|
|
804
|
+
onMeasureLabelWidth,
|
|
805
|
+
onTabPress,
|
|
806
|
+
onTabLongPress,
|
|
807
|
+
jumpTo,
|
|
808
|
+
renderTabBarItem,
|
|
809
|
+
}: TabBarItemWrapperProps<T>) {
|
|
810
|
+
const {
|
|
811
|
+
testID = getTestIdDefault({ route }),
|
|
812
|
+
labelText = getLabelTextDefault({ route }),
|
|
813
|
+
accessible = getAccessibleDefault({ route }),
|
|
814
|
+
accessibilityLabel = getAccessibilityLabelDefault({ route }),
|
|
815
|
+
...rest
|
|
816
|
+
} = option ?? {};
|
|
817
|
+
|
|
818
|
+
const onMeasureLayout = React.useCallback(
|
|
819
|
+
({ width }: { width: number }) => onMeasureTabWidth(route.key, width),
|
|
820
|
+
[route.key, onMeasureTabWidth]
|
|
821
|
+
);
|
|
822
|
+
|
|
823
|
+
const onMeasureLabelLayout = React.useCallback(
|
|
824
|
+
({ width }: { width: number }) => onMeasureLabelWidth(route.key, width),
|
|
825
|
+
[route.key, onMeasureLabelWidth]
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
const onPress = React.useCallback(() => {
|
|
829
|
+
const event: Scene<T> & Event = {
|
|
830
|
+
route,
|
|
831
|
+
defaultPrevented: false,
|
|
832
|
+
preventDefault: () => {
|
|
833
|
+
event.defaultPrevented = true;
|
|
834
|
+
},
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
onTabPress?.(event);
|
|
838
|
+
|
|
839
|
+
if (event.defaultPrevented) {
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
jumpTo(route.key);
|
|
844
|
+
}, [route, onTabPress, jumpTo]);
|
|
845
|
+
|
|
846
|
+
const onLongPress = React.useCallback(
|
|
847
|
+
() => onTabLongPress?.({ route }),
|
|
848
|
+
[route, onTabLongPress]
|
|
849
|
+
);
|
|
850
|
+
|
|
851
|
+
const style = React.useMemo(
|
|
852
|
+
() => [
|
|
853
|
+
tabStyle,
|
|
854
|
+
isWidthSet
|
|
855
|
+
? null
|
|
856
|
+
: defaultTabWidth !== undefined
|
|
857
|
+
? { width: defaultTabWidth }
|
|
858
|
+
: { minWidth: TAB_MIN_WIDTH },
|
|
859
|
+
],
|
|
860
|
+
[tabStyle, isWidthSet, defaultTabWidth]
|
|
861
|
+
);
|
|
862
|
+
|
|
863
|
+
const props = {
|
|
864
|
+
...rest,
|
|
865
|
+
position,
|
|
866
|
+
route,
|
|
867
|
+
navigationState,
|
|
868
|
+
testID,
|
|
869
|
+
labelText,
|
|
870
|
+
accessible,
|
|
871
|
+
accessibilityLabel,
|
|
872
|
+
variant,
|
|
873
|
+
activeColor,
|
|
874
|
+
inactiveColor,
|
|
875
|
+
pressColor,
|
|
876
|
+
pressOpacity,
|
|
877
|
+
onMeasureLayout,
|
|
878
|
+
onMeasureLabelLayout,
|
|
879
|
+
onPress,
|
|
880
|
+
onLongPress,
|
|
881
|
+
style,
|
|
882
|
+
android_ripple,
|
|
883
|
+
} satisfies TabBarItemProps<T>;
|
|
884
|
+
|
|
885
|
+
return (
|
|
886
|
+
<>
|
|
887
|
+
{gap > 0 && index > 0 ? <Separator width={gap} /> : null}
|
|
888
|
+
{renderTabBarItem ? (
|
|
889
|
+
renderTabBarItem({ key: route.key, ...props })
|
|
890
|
+
) : (
|
|
891
|
+
<TabBarItem key={route.key} {...props} />
|
|
892
|
+
)}
|
|
893
|
+
</>
|
|
894
|
+
);
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
const MemoizedTabBarItemWrapper = React.memo(
|
|
898
|
+
TabBarItemWrapper
|
|
899
|
+
) as typeof TabBarItemWrapper;
|
|
900
|
+
|
|
693
901
|
const styles = StyleSheet.create({
|
|
694
902
|
scroll: {
|
|
695
903
|
overflow: Platform.select({ default: 'scroll', web: undefined }),
|
|
696
904
|
},
|
|
697
905
|
tabBar: {
|
|
698
906
|
zIndex: 1,
|
|
699
|
-
backgroundColor:
|
|
700
|
-
|
|
701
|
-
|
|
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
|
-
}),
|
|
907
|
+
backgroundColor: TAB_BAR_BACKGROUND_COLOR,
|
|
908
|
+
borderBottomColor: TAB_BAR_BORDER_COLOR,
|
|
909
|
+
borderBottomWidth: 1,
|
|
715
910
|
},
|
|
716
911
|
tabContent: {
|
|
717
912
|
flexGrow: 1,
|
|
@@ -726,4 +921,14 @@ const styles = StyleSheet.create({
|
|
|
726
921
|
bottom: 0,
|
|
727
922
|
pointerEvents: 'none',
|
|
728
923
|
},
|
|
924
|
+
primaryIndicator: {
|
|
925
|
+
height: 3,
|
|
926
|
+
borderTopLeftRadius: 3,
|
|
927
|
+
borderTopRightRadius: 3,
|
|
928
|
+
},
|
|
929
|
+
secondaryIndicator: {
|
|
930
|
+
height: 2,
|
|
931
|
+
borderTopLeftRadius: 0,
|
|
932
|
+
borderTopRightRadius: 0,
|
|
933
|
+
},
|
|
729
934
|
});
|