react-native-tab-view 4.2.1 → 5.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/module/Pager.android.js +1 -1
- package/lib/module/Pager.android.js.map +1 -1
- package/lib/module/Pager.ios.js +1 -1
- package/lib/module/Pager.ios.js.map +1 -1
- package/lib/module/PagerViewAdapter.js +3 -127
- package/lib/module/PagerViewAdapter.js.map +1 -1
- package/lib/module/PagerViewAdapter.native.js +130 -0
- package/lib/module/PagerViewAdapter.native.js.map +1 -0
- package/lib/module/PanResponderAdapter.js +43 -31
- package/lib/module/PanResponderAdapter.js.map +1 -1
- package/lib/module/PlatformPressable.js.map +1 -1
- package/lib/module/SceneView.js +22 -31
- package/lib/module/SceneView.js.map +1 -1
- package/lib/module/ScrollViewAdapter.js +210 -0
- package/lib/module/ScrollViewAdapter.js.map +1 -0
- package/lib/module/TabBar.js +98 -61
- package/lib/module/TabBar.js.map +1 -1
- package/lib/module/TabBarIndicator.js +6 -9
- package/lib/module/TabBarIndicator.js.map +1 -1
- package/lib/module/TabBarItem.js +4 -4
- package/lib/module/TabBarItem.js.map +1 -1
- package/lib/module/TabView.js +65 -84
- package/lib/module/TabView.js.map +1 -1
- package/lib/module/index.js +3 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/useMeasureLayout.js +36 -0
- package/lib/module/useMeasureLayout.js.map +1 -0
- package/lib/typescript/src/PagerViewAdapter.d.ts +3 -17
- package/lib/typescript/src/PagerViewAdapter.d.ts.map +1 -1
- package/lib/typescript/src/PagerViewAdapter.native.d.ts +6 -0
- package/lib/typescript/src/PagerViewAdapter.native.d.ts.map +1 -0
- package/lib/typescript/src/PanResponderAdapter.d.ts +3 -17
- package/lib/typescript/src/PanResponderAdapter.d.ts.map +1 -1
- package/lib/typescript/src/PlatformPressable.d.ts +2 -2
- package/lib/typescript/src/PlatformPressable.d.ts.map +1 -1
- package/lib/typescript/src/SceneView.d.ts +1 -1
- package/lib/typescript/src/SceneView.d.ts.map +1 -1
- package/lib/typescript/src/ScrollViewAdapter.d.ts +12 -0
- package/lib/typescript/src/ScrollViewAdapter.d.ts.map +1 -0
- package/lib/typescript/src/TabBar.d.ts +7 -7
- package/lib/typescript/src/TabBar.d.ts.map +1 -1
- package/lib/typescript/src/TabBarIndicator.d.ts +1 -1
- package/lib/typescript/src/TabBarIndicator.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItem.d.ts +4 -4
- package/lib/typescript/src/TabBarItem.d.ts.map +1 -1
- package/lib/typescript/src/TabBarItemLabel.d.ts +2 -2
- package/lib/typescript/src/TabBarItemLabel.d.ts.map +1 -1
- package/lib/typescript/src/TabView.d.ts +5 -5
- package/lib/typescript/src/TabView.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +4 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +64 -8
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/useMeasureLayout.d.ts +5 -0
- package/lib/typescript/src/useMeasureLayout.d.ts.map +1 -0
- package/package.json +11 -11
- package/src/PagerViewAdapter.native.tsx +174 -0
- package/src/PagerViewAdapter.tsx +8 -181
- package/src/PanResponderAdapter.tsx +64 -77
- package/src/PlatformPressable.tsx +2 -1
- package/src/SceneView.tsx +23 -45
- package/src/ScrollViewAdapter.tsx +268 -0
- package/src/TabBar.tsx +134 -133
- package/src/TabBarIndicator.tsx +7 -11
- package/src/TabBarItem.tsx +8 -6
- package/src/TabBarItemLabel.tsx +2 -2
- package/src/TabView.tsx +79 -100
- package/src/index.tsx +10 -0
- package/src/types.tsx +75 -17
- package/src/useMeasureLayout.tsx +34 -0
package/src/TabView.tsx
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
2
|
import {
|
|
3
3
|
I18nManager,
|
|
4
|
-
type LayoutChangeEvent,
|
|
5
4
|
Platform,
|
|
6
5
|
type StyleProp,
|
|
7
6
|
StyleSheet,
|
|
@@ -13,28 +12,30 @@ import { Pager } from './Pager';
|
|
|
13
12
|
import { SceneView } from './SceneView';
|
|
14
13
|
import { TabBar } from './TabBar';
|
|
15
14
|
import type {
|
|
16
|
-
|
|
15
|
+
AdapterCommonProps,
|
|
16
|
+
AdapterProps,
|
|
17
|
+
EventEmitterProps,
|
|
17
18
|
LocaleDirection,
|
|
18
19
|
NavigationState,
|
|
19
|
-
PagerProps,
|
|
20
20
|
Route,
|
|
21
21
|
SceneRendererProps,
|
|
22
22
|
TabDescriptor,
|
|
23
23
|
} from './types';
|
|
24
24
|
|
|
25
|
-
export type Props<T extends Route> =
|
|
25
|
+
export type Props<T extends Route> = AdapterCommonProps & {
|
|
26
26
|
onIndexChange: (index: number) => void;
|
|
27
27
|
onTabSelect?: (props: { index: number }) => void;
|
|
28
28
|
navigationState: NavigationState<T>;
|
|
29
29
|
renderLazyPlaceholder?: (props: { route: T }) => React.ReactNode;
|
|
30
30
|
renderTabBar?: (
|
|
31
|
-
props: SceneRendererProps &
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
props: SceneRendererProps &
|
|
32
|
+
EventEmitterProps & {
|
|
33
|
+
navigationState: NavigationState<T>;
|
|
34
|
+
options: Record<string, TabDescriptor<T>> | undefined;
|
|
35
|
+
}
|
|
35
36
|
) => React.ReactNode;
|
|
37
|
+
renderAdapter?: (props: AdapterProps) => React.ReactElement;
|
|
36
38
|
tabBarPosition?: 'top' | 'bottom';
|
|
37
|
-
initialLayout?: Partial<Layout>;
|
|
38
39
|
lazy?: ((props: { route: T }) => boolean) | boolean;
|
|
39
40
|
lazyPreloadDistance?: number;
|
|
40
41
|
direction?: LocaleDirection;
|
|
@@ -52,7 +53,6 @@ export function TabView<T extends Route>({
|
|
|
52
53
|
onTabSelect,
|
|
53
54
|
navigationState,
|
|
54
55
|
renderScene,
|
|
55
|
-
initialLayout,
|
|
56
56
|
keyboardDismissMode = 'auto',
|
|
57
57
|
lazy = false,
|
|
58
58
|
lazyPreloadDistance = 0,
|
|
@@ -61,13 +61,14 @@ export function TabView<T extends Route>({
|
|
|
61
61
|
renderLazyPlaceholder = renderLazyPlaceholderDefault,
|
|
62
62
|
// eslint-disable-next-line @eslint-react/no-unstable-default-props
|
|
63
63
|
renderTabBar = (props) => <TabBar {...props} />,
|
|
64
|
+
// eslint-disable-next-line @eslint-react/no-unstable-default-props
|
|
65
|
+
renderAdapter = (props) => <Pager {...props} />,
|
|
64
66
|
pagerStyle,
|
|
65
67
|
style,
|
|
66
68
|
direction = I18nManager.getConstants().isRTL ? 'rtl' : 'ltr',
|
|
67
69
|
swipeEnabled = true,
|
|
68
70
|
tabBarPosition = 'top',
|
|
69
71
|
animationEnabled = true,
|
|
70
|
-
overScrollMode,
|
|
71
72
|
options: sceneOptions,
|
|
72
73
|
commonOptions,
|
|
73
74
|
}: Props<T>) {
|
|
@@ -82,30 +83,12 @@ export function TabView<T extends Route>({
|
|
|
82
83
|
);
|
|
83
84
|
}
|
|
84
85
|
|
|
85
|
-
const [layout, setLayout] = React.useState({
|
|
86
|
-
width: 0,
|
|
87
|
-
height: 0,
|
|
88
|
-
...initialLayout,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
86
|
const jumpToIndex = (index: number) => {
|
|
92
87
|
if (index !== navigationState.index) {
|
|
93
88
|
onIndexChange(index);
|
|
94
89
|
}
|
|
95
90
|
};
|
|
96
91
|
|
|
97
|
-
const handleLayout = (e: LayoutChangeEvent) => {
|
|
98
|
-
const { height, width } = e.nativeEvent.layout;
|
|
99
|
-
|
|
100
|
-
setLayout((prevLayout) => {
|
|
101
|
-
if (prevLayout.width === width && prevLayout.height === height) {
|
|
102
|
-
return prevLayout;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
return { height, width };
|
|
106
|
-
});
|
|
107
|
-
};
|
|
108
|
-
|
|
109
92
|
const options = Object.fromEntries(
|
|
110
93
|
navigationState.routes.map((route) => [
|
|
111
94
|
route.key,
|
|
@@ -116,82 +99,78 @@ export function TabView<T extends Route>({
|
|
|
116
99
|
])
|
|
117
100
|
);
|
|
118
101
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// This is crucial to optimizing the routes with PureComponent
|
|
138
|
-
const sceneRendererProps = {
|
|
139
|
-
position,
|
|
140
|
-
layout,
|
|
141
|
-
jumpTo,
|
|
142
|
-
};
|
|
102
|
+
const element = renderAdapter({
|
|
103
|
+
navigationState,
|
|
104
|
+
keyboardDismissMode,
|
|
105
|
+
swipeEnabled,
|
|
106
|
+
onSwipeStart,
|
|
107
|
+
onSwipeEnd,
|
|
108
|
+
onIndexChange: jumpToIndex,
|
|
109
|
+
onTabSelect,
|
|
110
|
+
animationEnabled,
|
|
111
|
+
layoutDirection: direction,
|
|
112
|
+
style: pagerStyle,
|
|
113
|
+
children: ({ position, render, subscribe, jumpTo }) => {
|
|
114
|
+
// All the props here must not change between re-renders
|
|
115
|
+
// This is crucial to optimizing the routes with PureComponent
|
|
116
|
+
const sceneRendererProps = {
|
|
117
|
+
position,
|
|
118
|
+
jumpTo,
|
|
119
|
+
};
|
|
143
120
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
121
|
+
return (
|
|
122
|
+
<React.Fragment>
|
|
123
|
+
{tabBarPosition === 'top' &&
|
|
124
|
+
renderTabBar({
|
|
125
|
+
...sceneRendererProps,
|
|
126
|
+
subscribe,
|
|
127
|
+
options,
|
|
128
|
+
navigationState,
|
|
129
|
+
})}
|
|
130
|
+
{render(
|
|
131
|
+
navigationState.routes.map((route, i) => {
|
|
132
|
+
const { sceneStyle } = options?.[route.key] ?? {};
|
|
155
133
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
134
|
+
return (
|
|
135
|
+
<SceneView
|
|
136
|
+
key={route.key}
|
|
137
|
+
{...sceneRendererProps}
|
|
138
|
+
subscribe={subscribe}
|
|
139
|
+
index={i}
|
|
140
|
+
lazy={typeof lazy === 'function' ? lazy({ route }) : lazy}
|
|
141
|
+
lazyPreloadDistance={lazyPreloadDistance}
|
|
142
|
+
navigationState={navigationState}
|
|
143
|
+
style={sceneStyle}
|
|
144
|
+
>
|
|
145
|
+
{({ loading }) =>
|
|
146
|
+
loading
|
|
147
|
+
? renderLazyPlaceholder({ route })
|
|
148
|
+
: renderScene({
|
|
149
|
+
...sceneRendererProps,
|
|
150
|
+
route,
|
|
151
|
+
})
|
|
152
|
+
}
|
|
153
|
+
</SceneView>
|
|
154
|
+
);
|
|
155
|
+
})
|
|
156
|
+
)}
|
|
157
|
+
{tabBarPosition === 'bottom' &&
|
|
158
|
+
renderTabBar({
|
|
159
|
+
...sceneRendererProps,
|
|
160
|
+
subscribe,
|
|
161
|
+
options,
|
|
162
|
+
navigationState,
|
|
163
|
+
})}
|
|
164
|
+
</React.Fragment>
|
|
165
|
+
);
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return <View style={[styles.container, style]}>{element}</View>;
|
|
191
170
|
}
|
|
192
171
|
|
|
193
172
|
const styles = StyleSheet.create({
|
|
194
|
-
|
|
173
|
+
container: {
|
|
195
174
|
flex: 1,
|
|
196
175
|
overflow: 'hidden',
|
|
197
176
|
},
|
package/src/index.tsx
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
|
+
export {
|
|
2
|
+
PagerViewAdapter,
|
|
3
|
+
type PagerViewAdapterProps,
|
|
4
|
+
} from './PagerViewAdapter';
|
|
5
|
+
export { PanResponderAdapter } from './PanResponderAdapter';
|
|
1
6
|
export { SceneMap } from './SceneMap';
|
|
7
|
+
export {
|
|
8
|
+
ScrollViewAdapter,
|
|
9
|
+
type ScrollViewAdapterProps,
|
|
10
|
+
} from './ScrollViewAdapter';
|
|
2
11
|
export type { Props as TabBarProps } from './TabBar';
|
|
3
12
|
export { TabBar } from './TabBar';
|
|
4
13
|
export type { Props as TabBarIndicatorProps } from './TabBarIndicator';
|
|
@@ -8,6 +17,7 @@ export { TabBarItem } from './TabBarItem';
|
|
|
8
17
|
export type { Props as TabViewProps } from './TabView';
|
|
9
18
|
export { TabView } from './TabView';
|
|
10
19
|
export type {
|
|
20
|
+
AdapterProps,
|
|
11
21
|
NavigationState,
|
|
12
22
|
Route,
|
|
13
23
|
SceneRendererProps,
|
package/src/types.tsx
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type {
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
Animated,
|
|
4
|
+
ColorValue,
|
|
5
|
+
StyleProp,
|
|
6
|
+
TextStyle,
|
|
7
|
+
ViewStyle,
|
|
8
|
+
} from 'react-native';
|
|
3
9
|
|
|
4
10
|
export type TabDescriptor<T extends Route> = {
|
|
5
11
|
accessibilityLabel?: string;
|
|
@@ -12,7 +18,7 @@ export type TabDescriptor<T extends Route> = {
|
|
|
12
18
|
route: T;
|
|
13
19
|
labelText?: string;
|
|
14
20
|
focused: boolean;
|
|
15
|
-
color:
|
|
21
|
+
color: ColorValue;
|
|
16
22
|
allowFontScaling?: boolean;
|
|
17
23
|
style?: StyleProp<TextStyle>;
|
|
18
24
|
}) => React.ReactNode;
|
|
@@ -20,7 +26,7 @@ export type TabDescriptor<T extends Route> = {
|
|
|
20
26
|
icon?: (props: {
|
|
21
27
|
route: T;
|
|
22
28
|
focused: boolean;
|
|
23
|
-
color:
|
|
29
|
+
color: ColorValue;
|
|
24
30
|
size: number;
|
|
25
31
|
}) => React.ReactNode;
|
|
26
32
|
badge?: (props: { route: T }) => React.ReactElement;
|
|
@@ -57,31 +63,83 @@ export type Layout = {
|
|
|
57
63
|
height: number;
|
|
58
64
|
};
|
|
59
65
|
|
|
60
|
-
export type Listener = (
|
|
66
|
+
export type Listener = (event: { type: 'enter'; index: number }) => void;
|
|
61
67
|
|
|
62
68
|
export type SceneRendererProps = {
|
|
63
|
-
layout: Layout;
|
|
64
69
|
position: Animated.AnimatedInterpolation<number>;
|
|
65
70
|
jumpTo: (key: string) => void;
|
|
66
71
|
};
|
|
67
72
|
|
|
68
73
|
export type EventEmitterProps = {
|
|
69
|
-
|
|
74
|
+
subscribe: (listener: Listener) => () => void;
|
|
70
75
|
};
|
|
71
76
|
|
|
72
|
-
export type
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
| 'keyboardDismissMode'
|
|
80
|
-
| 'children'
|
|
81
|
-
> & {
|
|
77
|
+
export type AdapterCommonProps = {
|
|
78
|
+
/**
|
|
79
|
+
* Determines whether the keyboard gets dismissed in response to a drag.
|
|
80
|
+
* - 'auto' (default) - the keyboard is dismissed on drag and tab changes
|
|
81
|
+
* - 'on-drag' - the keyboard is dismissed when a drag begins
|
|
82
|
+
* - 'none' - drags and tab changes do not dismiss the keyboard
|
|
83
|
+
*/
|
|
82
84
|
keyboardDismissMode?: 'none' | 'on-drag' | 'auto';
|
|
85
|
+
/**
|
|
86
|
+
* Whether swiping between tabs is enabled.
|
|
87
|
+
*/
|
|
83
88
|
swipeEnabled?: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* Whether the tab switch animation is enabled.
|
|
91
|
+
* If set to false, the tab switch will happen immediately without animation.
|
|
92
|
+
*/
|
|
84
93
|
animationEnabled?: boolean;
|
|
94
|
+
/**
|
|
95
|
+
* Callback that is called when the swipe gesture starts.
|
|
96
|
+
*/
|
|
85
97
|
onSwipeStart?: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Callback that is called when the swipe gesture ends.
|
|
100
|
+
*/
|
|
86
101
|
onSwipeEnd?: () => void;
|
|
102
|
+
/**
|
|
103
|
+
* Callback that is called when a tab is selected.
|
|
104
|
+
* This is called regardless of whether the index has changed or not.
|
|
105
|
+
*/
|
|
106
|
+
onTabSelect?: (props: { index: number }) => void;
|
|
107
|
+
/**
|
|
108
|
+
* Style for the pager adapter.
|
|
109
|
+
*/
|
|
110
|
+
style?: StyleProp<ViewStyle>;
|
|
87
111
|
};
|
|
112
|
+
|
|
113
|
+
export type AdapterRendererProps = {
|
|
114
|
+
/**
|
|
115
|
+
* Callback to call when the index changes.
|
|
116
|
+
*/
|
|
117
|
+
onIndexChange: (index: number) => void;
|
|
118
|
+
/**
|
|
119
|
+
* The current navigation state of the tab view.
|
|
120
|
+
*/
|
|
121
|
+
navigationState: NavigationState<Route>;
|
|
122
|
+
/**
|
|
123
|
+
* The writing direction of the layout.
|
|
124
|
+
* This can be 'ltr' or 'rtl' based on tab view's `direction` prop.
|
|
125
|
+
*/
|
|
126
|
+
layoutDirection?: LocaleDirection;
|
|
127
|
+
/**
|
|
128
|
+
* Render callback that should render the pages of the tab view.
|
|
129
|
+
*/
|
|
130
|
+
children: (
|
|
131
|
+
props: EventEmitterProps & {
|
|
132
|
+
// Animated value which represents the state of current index
|
|
133
|
+
// It can include fractional digits as it represents the intermediate value
|
|
134
|
+
position: Animated.AnimatedInterpolation<number>;
|
|
135
|
+
// Function to actually render the content of the pager
|
|
136
|
+
// The parent component takes care of rendering
|
|
137
|
+
render: (children: React.ReactElement[]) => React.ReactNode;
|
|
138
|
+
// Callback to call when switching the tab
|
|
139
|
+
// The tab switch animation is performed even if the index in state is unchanged
|
|
140
|
+
jumpTo: (key: string) => void;
|
|
141
|
+
}
|
|
142
|
+
) => React.ReactElement;
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
export type AdapterProps = AdapterRendererProps & AdapterCommonProps;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { type LayoutChangeEvent, type View } from 'react-native';
|
|
3
|
+
import useLatestCallback from 'use-latest-callback';
|
|
4
|
+
|
|
5
|
+
import type { Layout } from './types';
|
|
6
|
+
|
|
7
|
+
export function useMeasureLayout(
|
|
8
|
+
ref: React.RefObject<View | null>,
|
|
9
|
+
onMeasure?: (layout: Layout) => void
|
|
10
|
+
) {
|
|
11
|
+
const [layout, setLayout] = React.useState<Layout>({ width: 0, height: 0 });
|
|
12
|
+
|
|
13
|
+
const onMeasureLatest = useLatestCallback(({ width, height }: Layout) => {
|
|
14
|
+
setLayout((layout) =>
|
|
15
|
+
layout.width === width && layout.height === height
|
|
16
|
+
? layout
|
|
17
|
+
: { width, height }
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
onMeasure?.({ width, height });
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
React.useLayoutEffect(() => {
|
|
24
|
+
ref.current?.measure((_x, _y, width, height) => {
|
|
25
|
+
onMeasureLatest({ width, height });
|
|
26
|
+
});
|
|
27
|
+
}, [onMeasureLatest, ref]);
|
|
28
|
+
|
|
29
|
+
const onLayout = useLatestCallback((event: LayoutChangeEvent) => {
|
|
30
|
+
onMeasureLatest(event.nativeEvent.layout);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
return [layout, onLayout] as const;
|
|
34
|
+
}
|