react-native-tab-view 5.0.0-alpha.0 → 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 +111 -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 +118 -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/lib/module/TabBar.js
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
import * as React from 'react';
|
|
4
4
|
import { Animated, I18nManager, Platform, StyleSheet, View } from 'react-native';
|
|
5
|
-
import
|
|
5
|
+
import { PRIMARY_INDICATOR_MIN_WIDTH, TAB_BAR_BACKGROUND_COLOR, TAB_BAR_BORDER_COLOR, TAB_MIN_WIDTH } from "./constants.js";
|
|
6
6
|
import { TabBarIndicator } from "./TabBarIndicator.js";
|
|
7
7
|
import { TabBarItem } from "./TabBarItem.js";
|
|
8
8
|
import { useAnimatedValue } from "./useAnimatedValue.js";
|
|
9
|
+
import { useLayoutWidths } from "./useLayoutWidths.js";
|
|
9
10
|
import { useMeasureLayout } from "./useMeasureLayout.js";
|
|
10
|
-
import { jsx as _jsx,
|
|
11
|
-
const useNativeDriver = Platform.OS !== 'web';
|
|
11
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
12
12
|
const Separator = ({
|
|
13
13
|
width
|
|
14
14
|
}) => {
|
|
@@ -55,8 +55,12 @@ const getComputedTabWidth = ({
|
|
|
55
55
|
flattenedPaddingEnd,
|
|
56
56
|
gap
|
|
57
57
|
}) => {
|
|
58
|
+
const route = routes[index];
|
|
59
|
+
if (route == null) {
|
|
60
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
61
|
+
}
|
|
58
62
|
if (flattenedTabWidth === 'auto') {
|
|
59
|
-
return tabWidths[
|
|
63
|
+
return tabWidths[route.key] || 0;
|
|
60
64
|
}
|
|
61
65
|
switch (typeof flattenedTabWidth) {
|
|
62
66
|
case 'number':
|
|
@@ -70,14 +74,31 @@ const getComputedTabWidth = ({
|
|
|
70
74
|
}
|
|
71
75
|
}
|
|
72
76
|
if (scrollEnabled) {
|
|
73
|
-
return
|
|
77
|
+
return tabWidths[route.key] || TAB_MIN_WIDTH;
|
|
74
78
|
}
|
|
75
79
|
const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
|
|
76
80
|
const paddingTotalWidth = convertPaddingPercentToSize(flattenedPaddingStart, layoutWidth) + convertPaddingPercentToSize(flattenedPaddingEnd, layoutWidth);
|
|
77
81
|
return (layoutWidth - gapTotalWidth - paddingTotalWidth) / routes.length;
|
|
78
82
|
};
|
|
83
|
+
const calculateSize = (value, referenceWidth) => {
|
|
84
|
+
if (typeof value === 'number') {
|
|
85
|
+
return value;
|
|
86
|
+
}
|
|
87
|
+
if (typeof value === 'string' && value.endsWith('%')) {
|
|
88
|
+
const parsed = parseFloat(value);
|
|
89
|
+
if (Number.isFinite(parsed)) {
|
|
90
|
+
return referenceWidth * (parsed / 100);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return undefined;
|
|
94
|
+
};
|
|
79
95
|
const getMaxScrollDistance = (tabBarWidth, layoutWidth) => tabBarWidth - layoutWidth;
|
|
80
|
-
const getTranslateX = (scrollAmount, maxScrollDistance, direction) =>
|
|
96
|
+
const getTranslateX = (scrollAmount, maxScrollDistance, direction) => {
|
|
97
|
+
const amount =
|
|
98
|
+
// Android reports scroll from the opposite side in RTL
|
|
99
|
+
Platform.OS === 'android' && direction === 'rtl' ? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1)) : scrollAmount;
|
|
100
|
+
return Animated.multiply(amount, direction === 'rtl' ? 1 : -1);
|
|
101
|
+
};
|
|
81
102
|
const getTabBarWidth = ({
|
|
82
103
|
routes,
|
|
83
104
|
layoutWidth,
|
|
@@ -110,8 +131,7 @@ const normalizeScrollValue = ({
|
|
|
110
131
|
value,
|
|
111
132
|
flattenedTabWidth,
|
|
112
133
|
flattenedPaddingStart,
|
|
113
|
-
flattenedPaddingEnd
|
|
114
|
-
direction
|
|
134
|
+
flattenedPaddingEnd
|
|
115
135
|
}) => {
|
|
116
136
|
const tabBarWidth = getTabBarWidth({
|
|
117
137
|
layoutWidth,
|
|
@@ -125,11 +145,6 @@ const normalizeScrollValue = ({
|
|
|
125
145
|
});
|
|
126
146
|
const maxDistance = getMaxScrollDistance(tabBarWidth, layoutWidth);
|
|
127
147
|
const scrollValue = Math.max(Math.min(value, maxDistance), 0);
|
|
128
|
-
if (Platform.OS === 'android' && direction === 'rtl') {
|
|
129
|
-
// On Android, scroll value is not applied in reverse in RTL
|
|
130
|
-
// so we need to manually adjust it to apply correct value
|
|
131
|
-
return maxDistance - scrollValue;
|
|
132
|
-
}
|
|
133
148
|
return scrollValue;
|
|
134
149
|
};
|
|
135
150
|
const getScrollAmount = ({
|
|
@@ -174,8 +189,7 @@ const getScrollAmount = ({
|
|
|
174
189
|
scrollEnabled,
|
|
175
190
|
flattenedTabWidth,
|
|
176
191
|
flattenedPaddingStart,
|
|
177
|
-
flattenedPaddingEnd
|
|
178
|
-
direction
|
|
192
|
+
flattenedPaddingEnd
|
|
179
193
|
});
|
|
180
194
|
};
|
|
181
195
|
const getLabelTextDefault = ({
|
|
@@ -196,8 +210,9 @@ const getTestIdDefault = ({
|
|
|
196
210
|
|
|
197
211
|
// How many items measurements should we update per batch.
|
|
198
212
|
// Defaults to 10, since that's whats FlatList is using in initialNumToRender.
|
|
199
|
-
const
|
|
213
|
+
const RENDER_PER_BATCH = 10;
|
|
200
214
|
export function TabBar({
|
|
215
|
+
variant = 'primary',
|
|
201
216
|
renderIndicator = renderIndicatorDefault,
|
|
202
217
|
gap = 0,
|
|
203
218
|
scrollEnabled,
|
|
@@ -224,18 +239,24 @@ export function TabBar({
|
|
|
224
239
|
}) {
|
|
225
240
|
const containerRef = React.useRef(null);
|
|
226
241
|
const [layout, onLayout] = useMeasureLayout(containerRef);
|
|
227
|
-
|
|
242
|
+
|
|
243
|
+
// Prioritize measuring tabs upto focused item
|
|
244
|
+
// Since we need those measurements for calculation
|
|
245
|
+
const priorityKeysForLayout = navigationState.routes.slice(0, navigationState.index + 1).map(r => r.key);
|
|
246
|
+
const [tabWidths, onMeasureTabWidth] = useLayoutWidths(priorityKeysForLayout);
|
|
247
|
+
const [labelWidths, onMeasureLabelWidth] = useLayoutWidths(priorityKeysForLayout);
|
|
228
248
|
const flatListRef = React.useRef(null);
|
|
229
249
|
const isFirst = React.useRef(true);
|
|
230
|
-
const scrollAmount = useAnimatedValue(0);
|
|
231
|
-
const measuredTabWidths = React.useRef({});
|
|
232
250
|
const {
|
|
233
251
|
routes
|
|
234
252
|
} = navigationState;
|
|
235
253
|
const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
|
|
236
|
-
const
|
|
254
|
+
const isWidthAuto = flattenedTabWidth === 'auto';
|
|
255
|
+
const isWidthDynamic = isWidthAuto || scrollEnabled && flattenedTabWidth == null;
|
|
237
256
|
const flattenedPaddingEnd = getFlattenedPaddingEnd(contentContainerStyle);
|
|
238
257
|
const flattenedPaddingStart = getFlattenedPaddingStart(contentContainerStyle);
|
|
258
|
+
const paddingEnd = convertPaddingPercentToSize(flattenedPaddingEnd, layout.width);
|
|
259
|
+
const paddingStart = convertPaddingPercentToSize(flattenedPaddingStart, layout.width);
|
|
239
260
|
const scrollOffset = getScrollAmount({
|
|
240
261
|
layoutWidth: layout.width,
|
|
241
262
|
routes,
|
|
@@ -254,7 +275,7 @@ export function TabBar({
|
|
|
254
275
|
isFirst.current = false;
|
|
255
276
|
return;
|
|
256
277
|
}
|
|
257
|
-
if (
|
|
278
|
+
if (isWidthAuto && !hasMeasuredTabWidths) {
|
|
258
279
|
return;
|
|
259
280
|
}
|
|
260
281
|
if (scrollEnabled) {
|
|
@@ -263,7 +284,7 @@ export function TabBar({
|
|
|
263
284
|
animated: true
|
|
264
285
|
});
|
|
265
286
|
}
|
|
266
|
-
}, [hasMeasuredTabWidths,
|
|
287
|
+
}, [hasMeasuredTabWidths, isWidthAuto, scrollEnabled, scrollOffset]);
|
|
267
288
|
const tabBarWidth = getTabBarWidth({
|
|
268
289
|
layoutWidth: layout.width,
|
|
269
290
|
routes,
|
|
@@ -274,107 +295,55 @@ export function TabBar({
|
|
|
274
295
|
flattenedPaddingStart,
|
|
275
296
|
flattenedPaddingEnd
|
|
276
297
|
});
|
|
277
|
-
const
|
|
278
|
-
const
|
|
279
|
-
|
|
298
|
+
const maxScrollDistance = getMaxScrollDistance(tabBarWidth, layout.width);
|
|
299
|
+
const scrollAmount = useAnimatedValue(0);
|
|
300
|
+
React.useLayoutEffect(() => {
|
|
301
|
+
scrollAmount.setValue(Platform.OS === 'android' && direction === 'rtl' ? maxScrollDistance : 0);
|
|
302
|
+
}, [direction, maxScrollDistance, scrollAmount]);
|
|
303
|
+
const translateX = React.useMemo(() => getTranslateX(scrollAmount, maxScrollDistance, direction), [direction, maxScrollDistance, scrollAmount]);
|
|
304
|
+
const flattenedTabStyle = StyleSheet.flatten(tabStyle);
|
|
305
|
+
const isTabWidthSet = flattenedTabStyle?.width !== undefined;
|
|
306
|
+
|
|
307
|
+
// Calculate the default width for tab for FlatList to work.
|
|
308
|
+
const defaultTabWidth = !isWidthDynamic ? getComputedTabWidth({
|
|
309
|
+
// When `isWidthDynamic` is false, every tab gets the same width and
|
|
310
|
+
// `getComputedTabWidth` ignores `index`, so we compute it once with index 0.
|
|
311
|
+
index: 0,
|
|
312
|
+
layoutWidth: layout.width,
|
|
313
|
+
routes,
|
|
314
|
+
scrollEnabled,
|
|
315
|
+
tabWidths,
|
|
316
|
+
flattenedTabWidth,
|
|
317
|
+
flattenedPaddingStart,
|
|
318
|
+
flattenedPaddingEnd,
|
|
319
|
+
gap
|
|
320
|
+
}) : undefined;
|
|
280
321
|
const renderItem = React.useCallback(({
|
|
281
322
|
item: route,
|
|
282
323
|
index
|
|
283
|
-
}) => {
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
setTabWidths({
|
|
307
|
-
...measuredTabWidths.current
|
|
308
|
-
});
|
|
309
|
-
} else if (routes.every(r => typeof measuredTabWidths.current[r.key] === 'number')) {
|
|
310
|
-
// When we have measured widths for all of the tabs, we should updates the state
|
|
311
|
-
// We avoid doing separate setState for each layout since it triggers multiple renders and slows down app
|
|
312
|
-
setTabWidths({
|
|
313
|
-
...measuredTabWidths.current
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
} : undefined;
|
|
317
|
-
const onPress = () => {
|
|
318
|
-
const event = {
|
|
319
|
-
route,
|
|
320
|
-
defaultPrevented: false,
|
|
321
|
-
preventDefault: () => {
|
|
322
|
-
event.defaultPrevented = true;
|
|
323
|
-
}
|
|
324
|
-
};
|
|
325
|
-
onTabPress?.(event);
|
|
326
|
-
if (event.defaultPrevented) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
jumpTo(route.key);
|
|
330
|
-
};
|
|
331
|
-
const onLongPress = () => onTabLongPress?.({
|
|
332
|
-
route
|
|
333
|
-
});
|
|
334
|
-
|
|
335
|
-
// Calculate the default width for tab for FlatList to work
|
|
336
|
-
const defaultTabWidth = !isWidthDynamic ? getComputedTabWidth({
|
|
337
|
-
index,
|
|
338
|
-
layoutWidth: layout.width,
|
|
339
|
-
routes,
|
|
340
|
-
scrollEnabled,
|
|
341
|
-
tabWidths,
|
|
342
|
-
flattenedTabWidth: getFlattenedTabWidth(tabStyle),
|
|
343
|
-
flattenedPaddingStart: getFlattenedPaddingStart(contentContainerStyle),
|
|
344
|
-
flattenedPaddingEnd: getFlattenedPaddingEnd(contentContainerStyle),
|
|
345
|
-
gap
|
|
346
|
-
}) : undefined;
|
|
347
|
-
const props = {
|
|
348
|
-
...rest,
|
|
349
|
-
position,
|
|
350
|
-
route,
|
|
351
|
-
navigationState,
|
|
352
|
-
testID,
|
|
353
|
-
labelText,
|
|
354
|
-
accessible,
|
|
355
|
-
accessibilityLabel,
|
|
356
|
-
activeColor,
|
|
357
|
-
inactiveColor,
|
|
358
|
-
pressColor,
|
|
359
|
-
pressOpacity,
|
|
360
|
-
onLayout,
|
|
361
|
-
onPress,
|
|
362
|
-
onLongPress,
|
|
363
|
-
style: tabStyle,
|
|
364
|
-
defaultTabWidth,
|
|
365
|
-
android_ripple
|
|
366
|
-
};
|
|
367
|
-
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
368
|
-
children: [gap > 0 && index > 0 ? /*#__PURE__*/_jsx(Separator, {
|
|
369
|
-
width: gap
|
|
370
|
-
}) : null, renderTabBarItem ? renderTabBarItem({
|
|
371
|
-
key: route.key,
|
|
372
|
-
...props
|
|
373
|
-
}) : /*#__PURE__*/_jsx(TabBarItem, {
|
|
374
|
-
...props
|
|
375
|
-
}, route.key)]
|
|
376
|
-
});
|
|
377
|
-
}, [position, navigationState, options, activeColor, inactiveColor, pressColor, pressOpacity, isWidthDynamic, tabStyle, layout, routes, scrollEnabled, tabWidths, contentContainerStyle, gap, android_ripple, renderTabBarItem, onTabPress, jumpTo, onTabLongPress]);
|
|
324
|
+
}) => /*#__PURE__*/_jsx(MemoizedTabBarItemWrapper, {
|
|
325
|
+
route: route,
|
|
326
|
+
index: index,
|
|
327
|
+
option: options?.[route.key],
|
|
328
|
+
position: position,
|
|
329
|
+
navigationState: navigationState,
|
|
330
|
+
variant: variant,
|
|
331
|
+
activeColor: activeColor,
|
|
332
|
+
inactiveColor: inactiveColor,
|
|
333
|
+
pressColor: pressColor,
|
|
334
|
+
pressOpacity: pressOpacity,
|
|
335
|
+
android_ripple: android_ripple,
|
|
336
|
+
tabStyle: tabStyle,
|
|
337
|
+
defaultTabWidth: defaultTabWidth,
|
|
338
|
+
isWidthSet: isTabWidthSet,
|
|
339
|
+
gap: gap,
|
|
340
|
+
onMeasureTabWidth: onMeasureTabWidth,
|
|
341
|
+
onMeasureLabelWidth: onMeasureLabelWidth,
|
|
342
|
+
onTabPress: onTabPress,
|
|
343
|
+
onTabLongPress: onTabLongPress,
|
|
344
|
+
jumpTo: jumpTo,
|
|
345
|
+
renderTabBarItem: renderTabBarItem
|
|
346
|
+
}), [options, position, navigationState, variant, activeColor, inactiveColor, pressColor, pressOpacity, android_ripple, tabStyle, defaultTabWidth, isTabWidthSet, gap, onMeasureTabWidth, onMeasureLabelWidth, onTabPress, onTabLongPress, jumpTo, renderTabBarItem]);
|
|
378
347
|
const keyExtractor = React.useCallback(item => item.key, []);
|
|
379
348
|
const contentContainerStyleMemoized = React.useMemo(() => [styles.tabContent, scrollEnabled ? {
|
|
380
349
|
width: tabBarWidth
|
|
@@ -386,27 +355,83 @@ export function TabBar({
|
|
|
386
355
|
}
|
|
387
356
|
}
|
|
388
357
|
}], {
|
|
389
|
-
useNativeDriver
|
|
358
|
+
useNativeDriver: Platform.OS !== 'web'
|
|
390
359
|
}), [scrollAmount]);
|
|
391
|
-
const
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
360
|
+
const flattenedIndicatorStyle = StyleSheet.flatten(indicatorStyle);
|
|
361
|
+
const defaultIndicatorStyle = variant === 'primary' ? styles.primaryIndicator : styles.secondaryIndicator;
|
|
362
|
+
const tabWidthByIndex = routes.map((_, i) => getComputedTabWidth({
|
|
363
|
+
index: i,
|
|
364
|
+
layoutWidth: layout.width,
|
|
365
|
+
routes,
|
|
366
|
+
scrollEnabled,
|
|
367
|
+
tabWidths,
|
|
368
|
+
flattenedTabWidth,
|
|
369
|
+
flattenedPaddingEnd,
|
|
370
|
+
flattenedPaddingStart,
|
|
371
|
+
gap
|
|
372
|
+
}));
|
|
373
|
+
const indicatorBaseWidths = tabWidthByIndex.map((tabWidth, i) => {
|
|
374
|
+
const customIndicatorWidth = calculateSize(flattenedIndicatorStyle?.width, tabWidth);
|
|
375
|
+
if (customIndicatorWidth != null) {
|
|
376
|
+
return customIndicatorWidth;
|
|
396
377
|
}
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
378
|
+
if (variant === 'primary') {
|
|
379
|
+
const route = routes[i];
|
|
380
|
+
if (route == null) {
|
|
381
|
+
throw new Error(`Couldn't find a route at index ${i}.`);
|
|
382
|
+
}
|
|
383
|
+
const labelWidth = labelWidths[route.key];
|
|
384
|
+
return labelWidth ? Math.max(PRIMARY_INDICATOR_MIN_WIDTH, labelWidth) : 0;
|
|
385
|
+
}
|
|
386
|
+
return tabWidth;
|
|
387
|
+
});
|
|
388
|
+
const indicatorMargins = indicatorBaseWidths.map(width => {
|
|
389
|
+
if (!width) {
|
|
390
|
+
return {
|
|
391
|
+
left: 0,
|
|
392
|
+
right: 0
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
const marginHorizontal = flattenedIndicatorStyle?.marginHorizontal ?? flattenedIndicatorStyle?.margin;
|
|
396
|
+
const leftMargin = (direction === 'ltr' ? flattenedIndicatorStyle?.marginStart : flattenedIndicatorStyle?.marginEnd) ?? flattenedIndicatorStyle?.marginLeft ?? marginHorizontal;
|
|
397
|
+
const rightMargin = (direction === 'rtl' ? flattenedIndicatorStyle?.marginStart : flattenedIndicatorStyle?.marginEnd) ?? flattenedIndicatorStyle?.marginRight ?? marginHorizontal;
|
|
398
|
+
return {
|
|
399
|
+
left: calculateSize(leftMargin, width) ?? 0,
|
|
400
|
+
right: calculateSize(rightMargin, width) ?? 0
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
const indicatorWidths = indicatorBaseWidths.map((width, i) => {
|
|
404
|
+
if (!width) {
|
|
405
|
+
return 0;
|
|
406
|
+
}
|
|
407
|
+
const margin = indicatorMargins[i];
|
|
408
|
+
if (margin == null) {
|
|
409
|
+
throw new Error(`Couldn't find an indicator margin at index ${i}.`);
|
|
404
410
|
}
|
|
411
|
+
return Math.max(0, width - margin.left - margin.right);
|
|
412
|
+
});
|
|
413
|
+
const indicatorOffsets = tabWidthByIndex.map((tabWidth, i) => {
|
|
414
|
+
const precedingTabsWidth = tabWidthByIndex.slice(0, i).reduce((sum, width) => sum + width, 0);
|
|
415
|
+
const tabStart = precedingTabsWidth + gap * i;
|
|
416
|
+
const customIndicatorWidth = calculateSize(flattenedIndicatorStyle?.width, tabWidth);
|
|
417
|
+
const margin = indicatorMargins[i];
|
|
418
|
+
if (margin == null) {
|
|
419
|
+
throw new Error(`Couldn't find an indicator margin at index ${i}.`);
|
|
420
|
+
}
|
|
421
|
+
const indicatorBaseWidth = indicatorBaseWidths[i];
|
|
422
|
+
if (indicatorBaseWidth == null) {
|
|
423
|
+
throw new Error(`Couldn't find an indicator width at index ${i}.`);
|
|
424
|
+
}
|
|
425
|
+
const shouldCenterIndicator = variant === 'primary' || customIndicatorWidth != null && (flattenedIndicatorStyle?.margin === 'auto' || flattenedIndicatorStyle?.marginHorizontal === 'auto');
|
|
426
|
+
const baseOffset = shouldCenterIndicator ? (tabWidth - indicatorBaseWidth) / 2 : 0;
|
|
427
|
+
return tabStart + baseOffset + margin.left;
|
|
405
428
|
});
|
|
406
429
|
return /*#__PURE__*/_jsxs(Animated.View, {
|
|
407
430
|
ref: containerRef,
|
|
408
431
|
onLayout: onLayout,
|
|
409
|
-
style: [styles.tabBar,
|
|
432
|
+
style: [styles.tabBar, {
|
|
433
|
+
direction
|
|
434
|
+
}, style],
|
|
410
435
|
children: [/*#__PURE__*/_jsx(Animated.View, {
|
|
411
436
|
style: [styles.indicatorContainer, scrollEnabled ? {
|
|
412
437
|
transform: [{
|
|
@@ -416,27 +441,17 @@ export function TabBar({
|
|
|
416
441
|
width: tabBarWidth
|
|
417
442
|
} : null, indicatorContainerStyle],
|
|
418
443
|
children: renderIndicator({
|
|
444
|
+
variant,
|
|
419
445
|
position,
|
|
420
446
|
navigationState,
|
|
421
447
|
jumpTo,
|
|
422
448
|
direction,
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
index: i,
|
|
430
|
-
layoutWidth: layout.width,
|
|
431
|
-
routes,
|
|
432
|
-
scrollEnabled,
|
|
433
|
-
tabWidths,
|
|
434
|
-
flattenedTabWidth,
|
|
435
|
-
flattenedPaddingEnd,
|
|
436
|
-
flattenedPaddingStart,
|
|
437
|
-
gap
|
|
438
|
-
}),
|
|
439
|
-
gap
|
|
449
|
+
widths: indicatorWidths,
|
|
450
|
+
offsets: indicatorOffsets,
|
|
451
|
+
style: [defaultIndicatorStyle, indicatorStyle, {
|
|
452
|
+
start: paddingStart,
|
|
453
|
+
end: paddingEnd
|
|
454
|
+
}]
|
|
440
455
|
})
|
|
441
456
|
}), /*#__PURE__*/_jsx(View, {
|
|
442
457
|
style: styles.scroll,
|
|
@@ -448,8 +463,7 @@ export function TabBar({
|
|
|
448
463
|
keyboardShouldPersistTaps: "handled",
|
|
449
464
|
scrollEnabled: scrollEnabled,
|
|
450
465
|
bounces: bounces,
|
|
451
|
-
initialNumToRender:
|
|
452
|
-
onViewableItemsChanged: handleViewableItemsChanged,
|
|
466
|
+
initialNumToRender: RENDER_PER_BATCH,
|
|
453
467
|
alwaysBounceHorizontal: false,
|
|
454
468
|
scrollsToTop: false,
|
|
455
469
|
showsHorizontalScrollIndicator: false,
|
|
@@ -466,6 +480,105 @@ export function TabBar({
|
|
|
466
480
|
})]
|
|
467
481
|
});
|
|
468
482
|
}
|
|
483
|
+
function TabBarItemWrapper({
|
|
484
|
+
route,
|
|
485
|
+
index,
|
|
486
|
+
option,
|
|
487
|
+
position,
|
|
488
|
+
navigationState,
|
|
489
|
+
variant,
|
|
490
|
+
activeColor,
|
|
491
|
+
inactiveColor,
|
|
492
|
+
pressColor,
|
|
493
|
+
pressOpacity,
|
|
494
|
+
android_ripple,
|
|
495
|
+
tabStyle,
|
|
496
|
+
defaultTabWidth,
|
|
497
|
+
isWidthSet,
|
|
498
|
+
gap,
|
|
499
|
+
onMeasureTabWidth,
|
|
500
|
+
onMeasureLabelWidth,
|
|
501
|
+
onTabPress,
|
|
502
|
+
onTabLongPress,
|
|
503
|
+
jumpTo,
|
|
504
|
+
renderTabBarItem
|
|
505
|
+
}) {
|
|
506
|
+
const {
|
|
507
|
+
testID = getTestIdDefault({
|
|
508
|
+
route
|
|
509
|
+
}),
|
|
510
|
+
labelText = getLabelTextDefault({
|
|
511
|
+
route
|
|
512
|
+
}),
|
|
513
|
+
accessible = getAccessibleDefault({
|
|
514
|
+
route
|
|
515
|
+
}),
|
|
516
|
+
accessibilityLabel = getAccessibilityLabelDefault({
|
|
517
|
+
route
|
|
518
|
+
}),
|
|
519
|
+
...rest
|
|
520
|
+
} = option ?? {};
|
|
521
|
+
const onMeasureLayout = React.useCallback(({
|
|
522
|
+
width
|
|
523
|
+
}) => onMeasureTabWidth(route.key, width), [route.key, onMeasureTabWidth]);
|
|
524
|
+
const onMeasureLabelLayout = React.useCallback(({
|
|
525
|
+
width
|
|
526
|
+
}) => onMeasureLabelWidth(route.key, width), [route.key, onMeasureLabelWidth]);
|
|
527
|
+
const onPress = React.useCallback(() => {
|
|
528
|
+
const event = {
|
|
529
|
+
route,
|
|
530
|
+
defaultPrevented: false,
|
|
531
|
+
preventDefault: () => {
|
|
532
|
+
event.defaultPrevented = true;
|
|
533
|
+
}
|
|
534
|
+
};
|
|
535
|
+
onTabPress?.(event);
|
|
536
|
+
if (event.defaultPrevented) {
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
jumpTo(route.key);
|
|
540
|
+
}, [route, onTabPress, jumpTo]);
|
|
541
|
+
const onLongPress = React.useCallback(() => onTabLongPress?.({
|
|
542
|
+
route
|
|
543
|
+
}), [route, onTabLongPress]);
|
|
544
|
+
const style = React.useMemo(() => [tabStyle, isWidthSet ? null : defaultTabWidth !== undefined ? {
|
|
545
|
+
width: defaultTabWidth
|
|
546
|
+
} : {
|
|
547
|
+
minWidth: TAB_MIN_WIDTH
|
|
548
|
+
}], [tabStyle, isWidthSet, defaultTabWidth]);
|
|
549
|
+
const props = {
|
|
550
|
+
...rest,
|
|
551
|
+
position,
|
|
552
|
+
route,
|
|
553
|
+
navigationState,
|
|
554
|
+
testID,
|
|
555
|
+
labelText,
|
|
556
|
+
accessible,
|
|
557
|
+
accessibilityLabel,
|
|
558
|
+
variant,
|
|
559
|
+
activeColor,
|
|
560
|
+
inactiveColor,
|
|
561
|
+
pressColor,
|
|
562
|
+
pressOpacity,
|
|
563
|
+
onMeasureLayout,
|
|
564
|
+
onMeasureLabelLayout,
|
|
565
|
+
onPress,
|
|
566
|
+
onLongPress,
|
|
567
|
+
style,
|
|
568
|
+
android_ripple
|
|
569
|
+
};
|
|
570
|
+
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
571
|
+
children: [gap > 0 && index > 0 ? /*#__PURE__*/_jsx(Separator, {
|
|
572
|
+
width: gap
|
|
573
|
+
}) : null, renderTabBarItem ? renderTabBarItem({
|
|
574
|
+
key: route.key,
|
|
575
|
+
...props
|
|
576
|
+
}) : /*#__PURE__*/_jsx(TabBarItem, {
|
|
577
|
+
...props
|
|
578
|
+
}, route.key)]
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
const MemoizedTabBarItemWrapper = /*#__PURE__*/React.memo(TabBarItemWrapper);
|
|
469
582
|
const styles = StyleSheet.create({
|
|
470
583
|
scroll: {
|
|
471
584
|
overflow: Platform.select({
|
|
@@ -475,22 +588,9 @@ const styles = StyleSheet.create({
|
|
|
475
588
|
},
|
|
476
589
|
tabBar: {
|
|
477
590
|
zIndex: 1,
|
|
478
|
-
backgroundColor:
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
default: {
|
|
482
|
-
shadowColor: 'black',
|
|
483
|
-
shadowOpacity: 0.1,
|
|
484
|
-
shadowRadius: StyleSheet.hairlineWidth,
|
|
485
|
-
shadowOffset: {
|
|
486
|
-
height: StyleSheet.hairlineWidth,
|
|
487
|
-
width: 0
|
|
488
|
-
}
|
|
489
|
-
},
|
|
490
|
-
web: {
|
|
491
|
-
boxShadow: '0 1px 1px rgba(0, 0, 0, 0.1)'
|
|
492
|
-
}
|
|
493
|
-
})
|
|
591
|
+
backgroundColor: TAB_BAR_BACKGROUND_COLOR,
|
|
592
|
+
borderBottomColor: TAB_BAR_BORDER_COLOR,
|
|
593
|
+
borderBottomWidth: 1
|
|
494
594
|
},
|
|
495
595
|
tabContent: {
|
|
496
596
|
flexGrow: 1,
|
|
@@ -504,6 +604,16 @@ const styles = StyleSheet.create({
|
|
|
504
604
|
end: 0,
|
|
505
605
|
bottom: 0,
|
|
506
606
|
pointerEvents: 'none'
|
|
607
|
+
},
|
|
608
|
+
primaryIndicator: {
|
|
609
|
+
height: 3,
|
|
610
|
+
borderTopLeftRadius: 3,
|
|
611
|
+
borderTopRightRadius: 3
|
|
612
|
+
},
|
|
613
|
+
secondaryIndicator: {
|
|
614
|
+
height: 2,
|
|
615
|
+
borderTopLeftRadius: 0,
|
|
616
|
+
borderTopRightRadius: 0
|
|
507
617
|
}
|
|
508
618
|
});
|
|
509
619
|
//# sourceMappingURL=TabBar.js.map
|