react-native-tab-view 3.5.2 → 4.0.0-alpha.0
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/PanResponderAdapter.js +6 -5
- package/lib/commonjs/PanResponderAdapter.js.map +1 -1
- package/lib/commonjs/TabBar.js +79 -32
- package/lib/commonjs/TabBar.js.map +1 -1
- package/lib/commonjs/TabBarIndicator.js +8 -6
- package/lib/commonjs/TabBarIndicator.js.map +1 -1
- package/lib/commonjs/TabView.js +6 -1
- package/lib/commonjs/TabView.js.map +1 -1
- package/lib/module/PanResponderAdapter.js +7 -6
- package/lib/module/PanResponderAdapter.js.map +1 -1
- package/lib/module/TabBar.js +79 -32
- 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/TabView.js +7 -2
- package/lib/module/TabView.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/TabBar.d.ts +3 -2
- package/lib/typescript/src/TabBar.d.ts.map +1 -1
- package/lib/typescript/src/TabBarIndicator.d.ts +6 -3
- package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
- package/lib/typescript/src/TabView.d.ts +4 -3
- 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 +3 -3
- package/src/PanResponderAdapter.tsx +7 -5
- package/src/TabBar.tsx +139 -44
- package/src/TabBarIndicator.tsx +20 -7
- package/src/TabView.tsx +18 -1
- package/src/types.tsx +2 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import { StyleProp, ViewStyle } from 'react-native';
|
|
3
|
-
import type { Layout, NavigationState, PagerProps, Route, SceneRendererProps } from './types';
|
|
4
|
-
export type Props<T extends Route> = PagerProps & {
|
|
3
|
+
import type { Layout, LocaleDirection, NavigationState, PagerProps, Route, SceneRendererProps } from './types';
|
|
4
|
+
export type Props<T extends Route> = Omit<PagerProps, 'layoutDirection'> & {
|
|
5
5
|
onIndexChange: (index: number) => void;
|
|
6
6
|
navigationState: NavigationState<T>;
|
|
7
7
|
renderScene: (props: SceneRendererProps & {
|
|
@@ -20,8 +20,9 @@ export type Props<T extends Route> = PagerProps & {
|
|
|
20
20
|
}) => boolean) | boolean;
|
|
21
21
|
lazyPreloadDistance?: number;
|
|
22
22
|
sceneContainerStyle?: StyleProp<ViewStyle>;
|
|
23
|
+
direction?: LocaleDirection;
|
|
23
24
|
pagerStyle?: StyleProp<ViewStyle>;
|
|
24
25
|
style?: StyleProp<ViewStyle>;
|
|
25
26
|
};
|
|
26
|
-
export declare function TabView<T extends Route>({ onIndexChange, navigationState, renderScene, initialLayout, keyboardDismissMode, lazy, lazyPreloadDistance, onSwipeStart, onSwipeEnd, renderLazyPlaceholder, renderTabBar, sceneContainerStyle, pagerStyle, style, swipeEnabled, tabBarPosition, animationEnabled, overScrollMode, }: Props<T>): JSX.Element;
|
|
27
|
+
export declare function TabView<T extends Route>({ onIndexChange, navigationState, renderScene, initialLayout, keyboardDismissMode, lazy, lazyPreloadDistance, onSwipeStart, onSwipeEnd, renderLazyPlaceholder, renderTabBar, sceneContainerStyle, pagerStyle, style, direction, swipeEnabled, tabBarPosition, animationEnabled, overScrollMode, }: Props<T>): JSX.Element;
|
|
27
28
|
//# sourceMappingURL=TabView.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabView.d.ts","sourceRoot":"","sources":["../../../src/TabView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,
|
|
1
|
+
{"version":3,"file":"TabView.d.ts","sourceRoot":"","sources":["../../../src/TabView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAIL,SAAS,EAGT,SAAS,EACV,MAAM,cAAc,CAAC;AAKtB,OAAO,KAAK,EACV,MAAM,EACN,eAAe,EACf,eAAe,EACf,UAAU,EACV,KAAK,EACL,kBAAkB,EACnB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,IAAI,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,GAAG;IACzE,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC,CAAC;IACpC,WAAW,EAAE,CAAC,KAAK,EAAE,kBAAkB,GAAG;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IAC3E,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,KAAK,CAAC,SAAS,CAAC;IACjE,YAAY,CAAC,EAAE,CACb,KAAK,EAAE,kBAAkB,GAAG;QAAE,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;KAAE,KAChE,KAAK,CAAC,SAAS,CAAC;IACrB,cAAc,CAAC,EAAE,KAAK,GAAG,QAAQ,CAAC;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,CAAC,CAAA;KAAE,KAAK,OAAO,CAAC,GAAG,OAAO,CAAC;IACpD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,mBAAmB,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC3C,SAAS,CAAC,EAAE,eAAe,CAAC;IAC5B,UAAU,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAClC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;CAC9B,CAAC;AAEF,wBAAgB,OAAO,CAAC,CAAC,SAAS,KAAK,EAAE,EACvC,aAAa,EACb,eAAe,EACf,WAAW,EACX,aAAa,EACb,mBAA4B,EAC5B,IAAY,EACZ,mBAAuB,EACvB,YAAY,EACZ,UAAU,EACV,qBAAkC,EAClC,YAA+C,EAC/C,mBAAmB,EACnB,UAAU,EACV,KAAK,EACL,SAA4D,EAC5D,YAAmB,EACnB,cAAsB,EACtB,gBAAuB,EACvB,cAAc,GACf,EAAE,KAAK,CAAC,CAAC,CAAC,eAuGV"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,IAAI,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,IAAI;IACnC,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,KAAK,IAAI;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,IAAI,CAAC;CACtD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,IAAI,CAC3B,cAAc,EACZ,aAAa,GACb,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,0BAA0B,GAC1B,qBAAqB,GACrB,UAAU,CACb,GAAG;IACF,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,KAAK,CAAC;AAE5C,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,cAAc,IAAI,IAAI,CAAC;CACxB,CAAC;AAEF,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,KAAK,IAAI;IACnC,KAAK,EAAE,CAAC,CAAC;CACV,CAAC;AAEF,MAAM,MAAM,eAAe,CAAC,CAAC,SAAS,KAAK,IAAI;IAC7C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,CAAC,EAAE,CAAC;CACb,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gBAAgB,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,IAAI,CAAC;CACtD,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,IAAI,CAC3B,cAAc,EACZ,aAAa,GACb,eAAe,GACf,cAAc,GACd,gBAAgB,GAChB,0BAA0B,GAC1B,qBAAqB,GACrB,UAAU,CACb,GAAG;IACF,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IAClD,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;CACzB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-tab-view",
|
|
3
3
|
"description": "Tab view component for React Native",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "4.0.0-alpha.0",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react-native-component",
|
|
7
7
|
"react-component",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"del-cli": "^5.0.0",
|
|
46
46
|
"react": "18.2.0",
|
|
47
47
|
"react-native": "0.71.8",
|
|
48
|
-
"react-native-builder-bob": "^0.
|
|
48
|
+
"react-native-builder-bob": "^0.21.0",
|
|
49
49
|
"react-native-pager-view": "6.1.2",
|
|
50
50
|
"typescript": "^4.9.4"
|
|
51
51
|
},
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
]
|
|
69
69
|
]
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "ddc5705b979d62bb3a293f221ec954cbad3dfba4"
|
|
72
72
|
}
|
|
@@ -2,7 +2,6 @@ import * as React from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
GestureResponderEvent,
|
|
5
|
-
I18nManager,
|
|
6
5
|
Keyboard,
|
|
7
6
|
PanResponder,
|
|
8
7
|
PanResponderGestureState,
|
|
@@ -60,6 +59,7 @@ export function PanResponderAdapter<T extends Route>({
|
|
|
60
59
|
children,
|
|
61
60
|
style,
|
|
62
61
|
animationEnabled = false,
|
|
62
|
+
layoutDirection = 'ltr',
|
|
63
63
|
}: Props<T>) {
|
|
64
64
|
const { routes, index } = navigationState;
|
|
65
65
|
|
|
@@ -147,7 +147,8 @@ export function PanResponderAdapter<T extends Route>({
|
|
|
147
147
|
return false;
|
|
148
148
|
}
|
|
149
149
|
|
|
150
|
-
const diffX =
|
|
150
|
+
const diffX =
|
|
151
|
+
layoutDirection === 'rtl' ? -gestureState.dx : gestureState.dx;
|
|
151
152
|
|
|
152
153
|
return (
|
|
153
154
|
isMovingHorizontally(event, gestureState) &&
|
|
@@ -172,7 +173,8 @@ export function PanResponderAdapter<T extends Route>({
|
|
|
172
173
|
_: GestureResponderEvent,
|
|
173
174
|
gestureState: PanResponderGestureState
|
|
174
175
|
) => {
|
|
175
|
-
const diffX =
|
|
176
|
+
const diffX =
|
|
177
|
+
layoutDirection === 'rtl' ? -gestureState.dx : gestureState.dx;
|
|
176
178
|
|
|
177
179
|
if (
|
|
178
180
|
// swiping left
|
|
@@ -222,7 +224,7 @@ export function PanResponderAdapter<T extends Route>({
|
|
|
222
224
|
Math.min(
|
|
223
225
|
Math.max(
|
|
224
226
|
0,
|
|
225
|
-
|
|
227
|
+
layoutDirection === 'rtl'
|
|
226
228
|
? currentIndex + gestureState.dx / Math.abs(gestureState.dx)
|
|
227
229
|
: currentIndex - gestureState.dx / Math.abs(gestureState.dx)
|
|
228
230
|
),
|
|
@@ -281,7 +283,7 @@ export function PanResponderAdapter<T extends Route>({
|
|
|
281
283
|
outputRange: [-maxTranslate, 0],
|
|
282
284
|
extrapolate: 'clamp',
|
|
283
285
|
}),
|
|
284
|
-
|
|
286
|
+
layoutDirection === 'rtl' ? -1 : 1
|
|
285
287
|
);
|
|
286
288
|
|
|
287
289
|
const position = React.useMemo(
|
package/src/TabBar.tsx
CHANGED
|
@@ -21,6 +21,7 @@ import { Props as TabBarItemProps, TabBarItem } from './TabBarItem';
|
|
|
21
21
|
import type {
|
|
22
22
|
Event,
|
|
23
23
|
Layout,
|
|
24
|
+
LocaleDirection,
|
|
24
25
|
NavigationState,
|
|
25
26
|
Route,
|
|
26
27
|
Scene,
|
|
@@ -65,12 +66,14 @@ export type Props<T extends Route> = SceneRendererProps & {
|
|
|
65
66
|
labelStyle?: StyleProp<TextStyle>;
|
|
66
67
|
contentContainerStyle?: StyleProp<ViewStyle>;
|
|
67
68
|
style?: StyleProp<ViewStyle>;
|
|
69
|
+
direction?: LocaleDirection;
|
|
68
70
|
gap?: number;
|
|
69
71
|
testID?: string;
|
|
70
72
|
android_ripple?: PressableAndroidRippleConfig;
|
|
71
73
|
};
|
|
72
74
|
|
|
73
75
|
type FlattenedTabWidth = string | number | undefined;
|
|
76
|
+
type FlattenedTabPadding = string | number | undefined;
|
|
74
77
|
|
|
75
78
|
const Separator = ({ width }: { width: number }) => {
|
|
76
79
|
return <View style={{ width }} />;
|
|
@@ -82,13 +85,50 @@ const getFlattenedTabWidth = (style: StyleProp<ViewStyle>) => {
|
|
|
82
85
|
return tabStyle?.width;
|
|
83
86
|
};
|
|
84
87
|
|
|
88
|
+
const getFlattenedPaddingLeft = (style: StyleProp<ViewStyle>) => {
|
|
89
|
+
const flattenStyle = StyleSheet.flatten(style);
|
|
90
|
+
|
|
91
|
+
return flattenStyle
|
|
92
|
+
? flattenStyle.paddingLeft || flattenStyle.paddingHorizontal || 0
|
|
93
|
+
: 0;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
const getFlattenedPaddingRight = (style: StyleProp<ViewStyle>) => {
|
|
97
|
+
const flattenStyle = StyleSheet.flatten(style);
|
|
98
|
+
|
|
99
|
+
return flattenStyle
|
|
100
|
+
? flattenStyle.paddingRight || flattenStyle.paddingHorizontal || 0
|
|
101
|
+
: 0;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const convertPaddingPercentToSize = (
|
|
105
|
+
value: FlattenedTabPadding,
|
|
106
|
+
layout: Layout
|
|
107
|
+
): number => {
|
|
108
|
+
switch (typeof value) {
|
|
109
|
+
case 'number':
|
|
110
|
+
return value;
|
|
111
|
+
case 'string':
|
|
112
|
+
if (value.endsWith('%')) {
|
|
113
|
+
const width = parseFloat(value);
|
|
114
|
+
if (Number.isFinite(width)) {
|
|
115
|
+
return layout.width * (width / 100);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return 0;
|
|
120
|
+
};
|
|
121
|
+
|
|
85
122
|
const getComputedTabWidth = (
|
|
86
123
|
index: number,
|
|
87
124
|
layout: Layout,
|
|
88
125
|
routes: Route[],
|
|
89
126
|
scrollEnabled: boolean | undefined,
|
|
90
127
|
tabWidths: { [key: string]: number },
|
|
91
|
-
flattenedWidth: FlattenedTabWidth
|
|
128
|
+
flattenedWidth: FlattenedTabWidth,
|
|
129
|
+
flattenedPaddingLeft: FlattenedTabPadding,
|
|
130
|
+
flattenedPaddingRight: FlattenedTabPadding,
|
|
131
|
+
gap?: number
|
|
92
132
|
) => {
|
|
93
133
|
if (flattenedWidth === 'auto') {
|
|
94
134
|
return tabWidths[routes[index].key] || 0;
|
|
@@ -109,7 +149,13 @@ const getComputedTabWidth = (
|
|
|
109
149
|
if (scrollEnabled) {
|
|
110
150
|
return (layout.width / 5) * 2;
|
|
111
151
|
}
|
|
112
|
-
|
|
152
|
+
|
|
153
|
+
const gapTotalWidth = (gap ?? 0) * (routes.length - 1);
|
|
154
|
+
const paddingTotalWidth =
|
|
155
|
+
convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
|
|
156
|
+
convertPaddingPercentToSize(flattenedPaddingRight, layout);
|
|
157
|
+
|
|
158
|
+
return (layout.width - gapTotalWidth - paddingTotalWidth) / routes.length;
|
|
113
159
|
};
|
|
114
160
|
|
|
115
161
|
const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
|
|
@@ -117,13 +163,14 @@ const getMaxScrollDistance = (tabBarWidth: number, layoutWidth: number) =>
|
|
|
117
163
|
|
|
118
164
|
const getTranslateX = (
|
|
119
165
|
scrollAmount: Animated.Value,
|
|
120
|
-
maxScrollDistance: number
|
|
166
|
+
maxScrollDistance: number,
|
|
167
|
+
direction: LocaleDirection
|
|
121
168
|
) =>
|
|
122
169
|
Animated.multiply(
|
|
123
|
-
Platform.OS === 'android' &&
|
|
170
|
+
Platform.OS === 'android' && direction === 'rtl'
|
|
124
171
|
? Animated.add(maxScrollDistance, Animated.multiply(scrollAmount, -1))
|
|
125
172
|
: scrollAmount,
|
|
126
|
-
|
|
173
|
+
direction === 'rtl' ? 1 : -1
|
|
127
174
|
);
|
|
128
175
|
|
|
129
176
|
const getTabBarWidth = <T extends Route>({
|
|
@@ -132,13 +179,23 @@ const getTabBarWidth = <T extends Route>({
|
|
|
132
179
|
gap,
|
|
133
180
|
scrollEnabled,
|
|
134
181
|
flattenedTabWidth,
|
|
182
|
+
flattenedPaddingLeft,
|
|
183
|
+
flattenedPaddingRight,
|
|
135
184
|
tabWidths,
|
|
136
185
|
}: Pick<Props<T>, 'navigationState' | 'gap' | 'layout' | 'scrollEnabled'> & {
|
|
137
186
|
tabWidths: Record<string, number>;
|
|
187
|
+
flattenedPaddingLeft: FlattenedTabPadding;
|
|
188
|
+
flattenedPaddingRight: FlattenedTabPadding;
|
|
138
189
|
flattenedTabWidth: FlattenedTabWidth;
|
|
139
190
|
}) => {
|
|
140
191
|
const { routes } = navigationState;
|
|
141
192
|
|
|
193
|
+
const paddingsWidth = Math.max(
|
|
194
|
+
0,
|
|
195
|
+
convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
|
|
196
|
+
convertPaddingPercentToSize(flattenedPaddingRight, layout)
|
|
197
|
+
);
|
|
198
|
+
|
|
142
199
|
return routes.reduce<number>(
|
|
143
200
|
(acc, _, i) =>
|
|
144
201
|
acc +
|
|
@@ -149,9 +206,12 @@ const getTabBarWidth = <T extends Route>({
|
|
|
149
206
|
routes,
|
|
150
207
|
scrollEnabled,
|
|
151
208
|
tabWidths,
|
|
152
|
-
flattenedTabWidth
|
|
209
|
+
flattenedTabWidth,
|
|
210
|
+
flattenedPaddingLeft,
|
|
211
|
+
flattenedPaddingRight,
|
|
212
|
+
gap
|
|
153
213
|
),
|
|
154
|
-
|
|
214
|
+
paddingsWidth
|
|
155
215
|
);
|
|
156
216
|
};
|
|
157
217
|
|
|
@@ -163,10 +223,16 @@ const normalizeScrollValue = <T extends Route>({
|
|
|
163
223
|
tabWidths,
|
|
164
224
|
value,
|
|
165
225
|
flattenedTabWidth,
|
|
226
|
+
flattenedPaddingLeft,
|
|
227
|
+
flattenedPaddingRight,
|
|
228
|
+
direction,
|
|
166
229
|
}: Pick<Props<T>, 'layout' | 'navigationState' | 'gap' | 'scrollEnabled'> & {
|
|
167
230
|
tabWidths: Record<string, number>;
|
|
168
231
|
value: number;
|
|
169
232
|
flattenedTabWidth: FlattenedTabWidth;
|
|
233
|
+
flattenedPaddingLeft: FlattenedTabPadding;
|
|
234
|
+
flattenedPaddingRight: FlattenedTabPadding;
|
|
235
|
+
direction: LocaleDirection;
|
|
170
236
|
}) => {
|
|
171
237
|
const tabBarWidth = getTabBarWidth({
|
|
172
238
|
layout,
|
|
@@ -175,11 +241,13 @@ const normalizeScrollValue = <T extends Route>({
|
|
|
175
241
|
gap,
|
|
176
242
|
scrollEnabled,
|
|
177
243
|
flattenedTabWidth,
|
|
244
|
+
flattenedPaddingLeft,
|
|
245
|
+
flattenedPaddingRight,
|
|
178
246
|
});
|
|
179
247
|
const maxDistance = getMaxScrollDistance(tabBarWidth, layout.width);
|
|
180
248
|
const scrollValue = Math.max(Math.min(value, maxDistance), 0);
|
|
181
249
|
|
|
182
|
-
if (Platform.OS === 'android' &&
|
|
250
|
+
if (Platform.OS === 'android' && direction === 'rtl') {
|
|
183
251
|
// On Android, scroll value is not applied in reverse in RTL
|
|
184
252
|
// so we need to manually adjust it to apply correct value
|
|
185
253
|
return maxDistance - scrollValue;
|
|
@@ -195,10 +263,21 @@ const getScrollAmount = <T extends Route>({
|
|
|
195
263
|
scrollEnabled,
|
|
196
264
|
flattenedTabWidth,
|
|
197
265
|
tabWidths,
|
|
266
|
+
flattenedPaddingLeft,
|
|
267
|
+
flattenedPaddingRight,
|
|
268
|
+
direction,
|
|
198
269
|
}: Pick<Props<T>, 'layout' | 'navigationState' | 'scrollEnabled' | 'gap'> & {
|
|
199
270
|
tabWidths: Record<string, number>;
|
|
200
271
|
flattenedTabWidth: FlattenedTabWidth;
|
|
272
|
+
flattenedPaddingLeft: FlattenedTabPadding;
|
|
273
|
+
flattenedPaddingRight: FlattenedTabPadding;
|
|
274
|
+
direction: LocaleDirection;
|
|
201
275
|
}) => {
|
|
276
|
+
const paddingInitial =
|
|
277
|
+
direction === 'rtl'
|
|
278
|
+
? convertPaddingPercentToSize(flattenedPaddingRight, layout)
|
|
279
|
+
: convertPaddingPercentToSize(flattenedPaddingLeft, layout);
|
|
280
|
+
|
|
202
281
|
const centerDistance = Array.from({
|
|
203
282
|
length: navigationState.index + 1,
|
|
204
283
|
}).reduce<number>((total, _, i) => {
|
|
@@ -208,18 +287,20 @@ const getScrollAmount = <T extends Route>({
|
|
|
208
287
|
navigationState.routes,
|
|
209
288
|
scrollEnabled,
|
|
210
289
|
tabWidths,
|
|
211
|
-
flattenedTabWidth
|
|
290
|
+
flattenedTabWidth,
|
|
291
|
+
flattenedPaddingLeft,
|
|
292
|
+
flattenedPaddingRight,
|
|
293
|
+
gap
|
|
212
294
|
);
|
|
213
295
|
|
|
214
296
|
// To get the current index centered we adjust scroll amount by width of indexes
|
|
215
297
|
// 0 through (i - 1) and add half the width of current index i
|
|
216
298
|
return (
|
|
217
299
|
total +
|
|
218
|
-
(
|
|
219
|
-
|
|
220
|
-
: tabWidth + (gap ?? 0))
|
|
300
|
+
(i > 0 ? gap ?? 0 : 0) +
|
|
301
|
+
(navigationState.index === i ? tabWidth / 2 : tabWidth)
|
|
221
302
|
);
|
|
222
|
-
},
|
|
303
|
+
}, paddingInitial);
|
|
223
304
|
|
|
224
305
|
const scrollAmount = centerDistance - layout.width / 2;
|
|
225
306
|
|
|
@@ -231,6 +312,9 @@ const getScrollAmount = <T extends Route>({
|
|
|
231
312
|
gap,
|
|
232
313
|
scrollEnabled,
|
|
233
314
|
flattenedTabWidth,
|
|
315
|
+
flattenedPaddingLeft,
|
|
316
|
+
flattenedPaddingRight,
|
|
317
|
+
direction,
|
|
234
318
|
});
|
|
235
319
|
};
|
|
236
320
|
|
|
@@ -281,22 +365,27 @@ export function TabBar<T extends Route>({
|
|
|
281
365
|
renderBadge,
|
|
282
366
|
renderIcon,
|
|
283
367
|
renderLabel,
|
|
368
|
+
direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
|
|
284
369
|
renderTabBarItem,
|
|
285
370
|
style,
|
|
286
371
|
tabStyle,
|
|
372
|
+
layout: propLayout,
|
|
287
373
|
testID,
|
|
288
374
|
android_ripple,
|
|
289
375
|
}: Props<T>) {
|
|
290
|
-
const [layout, setLayout] = React.useState<Layout>(
|
|
376
|
+
const [layout, setLayout] = React.useState<Layout>(
|
|
377
|
+
propLayout ?? { width: 0, height: 0 }
|
|
378
|
+
);
|
|
291
379
|
const [tabWidths, setTabWidths] = React.useState<Record<string, number>>({});
|
|
292
380
|
const flatListRef = React.useRef<FlatList | null>(null);
|
|
293
381
|
const isFirst = React.useRef(true);
|
|
294
382
|
const scrollAmount = useAnimatedValue(0);
|
|
295
383
|
const measuredTabWidths = React.useRef<Record<string, number>>({});
|
|
296
|
-
|
|
297
384
|
const { routes } = navigationState;
|
|
298
385
|
const flattenedTabWidth = getFlattenedTabWidth(tabStyle);
|
|
299
386
|
const isWidthDynamic = flattenedTabWidth === 'auto';
|
|
387
|
+
const flattenedPaddingRight = getFlattenedPaddingRight(contentContainerStyle);
|
|
388
|
+
const flattenedPaddingLeft = getFlattenedPaddingLeft(contentContainerStyle);
|
|
300
389
|
const scrollOffset = getScrollAmount({
|
|
301
390
|
layout,
|
|
302
391
|
navigationState,
|
|
@@ -304,6 +393,9 @@ export function TabBar<T extends Route>({
|
|
|
304
393
|
gap,
|
|
305
394
|
scrollEnabled,
|
|
306
395
|
flattenedTabWidth,
|
|
396
|
+
flattenedPaddingLeft,
|
|
397
|
+
flattenedPaddingRight,
|
|
398
|
+
direction,
|
|
307
399
|
});
|
|
308
400
|
|
|
309
401
|
const hasMeasuredTabWidths =
|
|
@@ -347,19 +439,25 @@ export function TabBar<T extends Route>({
|
|
|
347
439
|
gap,
|
|
348
440
|
scrollEnabled,
|
|
349
441
|
flattenedTabWidth,
|
|
442
|
+
flattenedPaddingLeft,
|
|
443
|
+
flattenedPaddingRight,
|
|
350
444
|
});
|
|
351
445
|
|
|
352
446
|
const separatorsWidth = Math.max(0, routes.length - 1) * gap;
|
|
353
|
-
const
|
|
354
|
-
|
|
447
|
+
const paddingsWidth = Math.max(
|
|
448
|
+
0,
|
|
449
|
+
convertPaddingPercentToSize(flattenedPaddingLeft, layout) +
|
|
450
|
+
convertPaddingPercentToSize(flattenedPaddingRight, layout)
|
|
451
|
+
);
|
|
355
452
|
|
|
356
453
|
const translateX = React.useMemo(
|
|
357
454
|
() =>
|
|
358
455
|
getTranslateX(
|
|
359
456
|
scrollAmount,
|
|
360
|
-
getMaxScrollDistance(tabBarWidth, layout.width)
|
|
457
|
+
getMaxScrollDistance(tabBarWidth, layout.width),
|
|
458
|
+
direction
|
|
361
459
|
),
|
|
362
|
-
[layout.width, scrollAmount, tabBarWidth]
|
|
460
|
+
[direction, layout.width, scrollAmount, tabBarWidth]
|
|
363
461
|
);
|
|
364
462
|
|
|
365
463
|
const renderItem = React.useCallback(
|
|
@@ -436,7 +534,10 @@ export function TabBar<T extends Route>({
|
|
|
436
534
|
routes,
|
|
437
535
|
scrollEnabled,
|
|
438
536
|
tabWidths,
|
|
439
|
-
getFlattenedTabWidth(tabStyle)
|
|
537
|
+
getFlattenedTabWidth(tabStyle),
|
|
538
|
+
getFlattenedPaddingRight(contentContainerStyle),
|
|
539
|
+
getFlattenedPaddingLeft(contentContainerStyle),
|
|
540
|
+
gap
|
|
440
541
|
)
|
|
441
542
|
: undefined,
|
|
442
543
|
android_ripple,
|
|
@@ -479,6 +580,7 @@ export function TabBar<T extends Route>({
|
|
|
479
580
|
routes,
|
|
480
581
|
scrollEnabled,
|
|
481
582
|
tabStyle,
|
|
583
|
+
contentContainerStyle,
|
|
482
584
|
tabWidths,
|
|
483
585
|
]
|
|
484
586
|
);
|
|
@@ -488,21 +590,10 @@ export function TabBar<T extends Route>({
|
|
|
488
590
|
const contentContainerStyleMemoized = React.useMemo(
|
|
489
591
|
() => [
|
|
490
592
|
styles.tabContent,
|
|
491
|
-
scrollEnabled
|
|
492
|
-
? {
|
|
493
|
-
width:
|
|
494
|
-
tabBarWidth > separatorsWidth ? tabBarWidth : tabBarWidthPercent,
|
|
495
|
-
}
|
|
496
|
-
: styles.container,
|
|
593
|
+
scrollEnabled ? { width: tabBarWidth } : null,
|
|
497
594
|
contentContainerStyle,
|
|
498
595
|
],
|
|
499
|
-
[
|
|
500
|
-
contentContainerStyle,
|
|
501
|
-
scrollEnabled,
|
|
502
|
-
separatorsWidth,
|
|
503
|
-
tabBarWidth,
|
|
504
|
-
tabBarWidthPercent,
|
|
505
|
-
]
|
|
596
|
+
[contentContainerStyle, scrollEnabled, tabBarWidth]
|
|
506
597
|
);
|
|
507
598
|
|
|
508
599
|
const handleScroll = React.useMemo(
|
|
@@ -546,11 +637,7 @@ export function TabBar<T extends Route>({
|
|
|
546
637
|
style={[
|
|
547
638
|
styles.indicatorContainer,
|
|
548
639
|
scrollEnabled ? { transform: [{ translateX }] as any } : null,
|
|
549
|
-
tabBarWidth
|
|
550
|
-
? { width: tabBarWidth - separatorsWidth }
|
|
551
|
-
: scrollEnabled
|
|
552
|
-
? { width: tabBarWidthPercent }
|
|
553
|
-
: null,
|
|
640
|
+
scrollEnabled ? { width: tabBarWidth } : null,
|
|
554
641
|
indicatorContainerStyle,
|
|
555
642
|
]}
|
|
556
643
|
>
|
|
@@ -559,10 +646,17 @@ export function TabBar<T extends Route>({
|
|
|
559
646
|
layout,
|
|
560
647
|
navigationState,
|
|
561
648
|
jumpTo,
|
|
649
|
+
direction,
|
|
562
650
|
width: isWidthDynamic
|
|
563
651
|
? 'auto'
|
|
564
|
-
:
|
|
565
|
-
|
|
652
|
+
: Math.max(
|
|
653
|
+
0,
|
|
654
|
+
(tabBarWidth - separatorsWidth - paddingsWidth) / routes.length
|
|
655
|
+
),
|
|
656
|
+
style: [
|
|
657
|
+
indicatorStyle,
|
|
658
|
+
{ left: flattenedPaddingLeft, right: flattenedPaddingRight },
|
|
659
|
+
],
|
|
566
660
|
getTabWidth: (i: number) =>
|
|
567
661
|
getComputedTabWidth(
|
|
568
662
|
i,
|
|
@@ -570,7 +664,10 @@ export function TabBar<T extends Route>({
|
|
|
570
664
|
routes,
|
|
571
665
|
scrollEnabled,
|
|
572
666
|
tabWidths,
|
|
573
|
-
flattenedTabWidth
|
|
667
|
+
flattenedTabWidth,
|
|
668
|
+
flattenedPaddingRight,
|
|
669
|
+
flattenedPaddingLeft,
|
|
670
|
+
gap
|
|
574
671
|
),
|
|
575
672
|
gap,
|
|
576
673
|
})}
|
|
@@ -605,9 +702,6 @@ export function TabBar<T extends Route>({
|
|
|
605
702
|
}
|
|
606
703
|
|
|
607
704
|
const styles = StyleSheet.create({
|
|
608
|
-
container: {
|
|
609
|
-
flex: 1,
|
|
610
|
-
},
|
|
611
705
|
scroll: {
|
|
612
706
|
overflow: Platform.select({ default: 'scroll', web: undefined }),
|
|
613
707
|
},
|
|
@@ -624,6 +718,7 @@ const styles = StyleSheet.create({
|
|
|
624
718
|
zIndex: 1,
|
|
625
719
|
},
|
|
626
720
|
tabContent: {
|
|
721
|
+
flexGrow: 1,
|
|
627
722
|
flexDirection: 'row',
|
|
628
723
|
flexWrap: 'nowrap',
|
|
629
724
|
},
|
package/src/TabBarIndicator.tsx
CHANGED
|
@@ -2,14 +2,18 @@ import * as React from 'react';
|
|
|
2
2
|
import {
|
|
3
3
|
Animated,
|
|
4
4
|
Easing,
|
|
5
|
-
I18nManager,
|
|
6
5
|
Platform,
|
|
7
6
|
StyleProp,
|
|
8
7
|
StyleSheet,
|
|
9
8
|
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;
|
|
@@ -17,15 +21,18 @@ export type GetTabWidth = (index: number) => number;
|
|
|
17
21
|
export type Props<T extends Route> = SceneRendererProps & {
|
|
18
22
|
navigationState: NavigationState<T>;
|
|
19
23
|
width: string | number;
|
|
20
|
-
style?: StyleProp<ViewStyle>;
|
|
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/TabView.tsx
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
|
+
I18nManager,
|
|
3
4
|
LayoutChangeEvent,
|
|
5
|
+
Platform,
|
|
4
6
|
StyleProp,
|
|
5
7
|
StyleSheet,
|
|
6
8
|
View,
|
|
@@ -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
|