@react-navigation/material-top-tabs 8.0.0-alpha.5 → 8.0.0-alpha.51
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/index.js.map +1 -1
- package/lib/module/navigators/createMaterialTopTabNavigator.js +10 -16
- package/lib/module/navigators/createMaterialTopTabNavigator.js.map +1 -1
- package/lib/module/utils/useTabAnimation.js +1 -1
- package/lib/module/utils/useTabAnimation.js.map +1 -1
- package/lib/module/views/MaterialTopTabBar.js +53 -24
- package/lib/module/views/MaterialTopTabBar.js.map +1 -1
- package/lib/module/views/MaterialTopTabView.js +71 -10
- package/lib/module/views/MaterialTopTabView.js.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -5
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigators/createMaterialTopTabNavigator.d.ts +9 -15
- package/lib/typescript/src/navigators/createMaterialTopTabNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +61 -36
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/TabAnimationContext.d.ts +1 -1
- package/lib/typescript/src/utils/TabAnimationContext.d.ts.map +1 -1
- package/lib/typescript/src/utils/useTabAnimation.d.ts +1 -1
- package/lib/typescript/src/utils/useTabAnimation.d.ts.map +1 -1
- package/lib/typescript/src/views/MaterialTopTabBar.d.ts +3 -2
- package/lib/typescript/src/views/MaterialTopTabBar.d.ts.map +1 -1
- package/lib/typescript/src/views/MaterialTopTabView.d.ts +2 -2
- package/lib/typescript/src/views/MaterialTopTabView.d.ts.map +1 -1
- package/package.json +19 -20
- package/src/index.tsx +1 -0
- package/src/navigators/createMaterialTopTabNavigator.tsx +34 -73
- package/src/types.tsx +76 -44
- package/src/utils/useTabAnimation.tsx +1 -1
- package/src/views/MaterialTopTabBar.tsx +76 -34
- package/src/views/MaterialTopTabView.tsx +100 -9
package/src/types.tsx
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Icon } from '@react-navigation/elements';
|
|
1
2
|
import type {
|
|
2
3
|
DefaultNavigatorOptions,
|
|
3
4
|
Descriptor,
|
|
@@ -78,7 +79,7 @@ export type MaterialTopTabNavigationOptions = {
|
|
|
78
79
|
/**
|
|
79
80
|
* Title text for the screen.
|
|
80
81
|
*/
|
|
81
|
-
title?: string;
|
|
82
|
+
title?: string | undefined;
|
|
82
83
|
|
|
83
84
|
/**
|
|
84
85
|
* Title string of a tab displayed in the tab bar
|
|
@@ -92,145 +93,163 @@ export type MaterialTopTabNavigationOptions = {
|
|
|
92
93
|
focused: boolean;
|
|
93
94
|
color: ColorValue;
|
|
94
95
|
children: string;
|
|
95
|
-
}) => React.ReactNode)
|
|
96
|
+
}) => React.ReactNode)
|
|
97
|
+
| undefined;
|
|
96
98
|
|
|
97
99
|
/**
|
|
98
100
|
* Accessibility label for the tab button. This is read by the screen reader when the user taps the tab.
|
|
99
101
|
* It's recommended to set this if you don't have a label for the tab.
|
|
100
102
|
*/
|
|
101
|
-
tabBarAccessibilityLabel?: string;
|
|
103
|
+
tabBarAccessibilityLabel?: string | undefined;
|
|
102
104
|
|
|
103
105
|
/**
|
|
104
106
|
* Whether label font should scale to respect Text Size accessibility settings.
|
|
105
107
|
*/
|
|
106
|
-
tabBarAllowFontScaling?: boolean;
|
|
108
|
+
tabBarAllowFontScaling?: boolean | undefined;
|
|
107
109
|
|
|
108
110
|
/**
|
|
109
111
|
* Whether the tab label should be visible. Defaults to `true`.
|
|
110
112
|
*/
|
|
111
|
-
tabBarShowLabel?: boolean;
|
|
113
|
+
tabBarShowLabel?: boolean | undefined;
|
|
112
114
|
|
|
113
115
|
/**
|
|
114
|
-
*
|
|
116
|
+
* Icon to display for the tab.
|
|
115
117
|
*/
|
|
116
|
-
tabBarIcon?:
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
tabBarIcon?:
|
|
119
|
+
| Icon
|
|
120
|
+
| ((props: {
|
|
121
|
+
focused: boolean;
|
|
122
|
+
color: ColorValue;
|
|
123
|
+
size: number;
|
|
124
|
+
}) => Icon | React.ReactNode);
|
|
120
125
|
|
|
121
126
|
/**
|
|
122
127
|
* Whether the tab icon should be visible. Defaults to `false`.
|
|
123
128
|
*/
|
|
124
|
-
tabBarShowIcon?: boolean;
|
|
129
|
+
tabBarShowIcon?: boolean | undefined;
|
|
125
130
|
|
|
126
131
|
/**
|
|
127
132
|
* Function that returns a React element to use as a badge for the tab.
|
|
128
133
|
*/
|
|
129
|
-
tabBarBadge?: () => React.ReactElement;
|
|
134
|
+
tabBarBadge?: (() => React.ReactElement) | undefined;
|
|
130
135
|
|
|
131
136
|
/**
|
|
132
137
|
* Function that returns a React element as the tab bar indicator.
|
|
133
138
|
*/
|
|
134
|
-
tabBarIndicator?:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
139
|
+
tabBarIndicator?:
|
|
140
|
+
| ((
|
|
141
|
+
props: Omit<
|
|
142
|
+
Parameters<
|
|
143
|
+
NonNullable<React.ComponentProps<typeof TabBar>['renderIndicator']>
|
|
144
|
+
>[0],
|
|
145
|
+
'navigationState'
|
|
146
|
+
> & { state: TabNavigationState<ParamListBase> }
|
|
147
|
+
) => React.ReactNode)
|
|
148
|
+
| undefined;
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Variant of the tab bar.
|
|
152
|
+
*
|
|
153
|
+
* The following variants are available:
|
|
154
|
+
*
|
|
155
|
+
* - `primary`: the indicator matches the label & icon width and is centered under the tab.
|
|
156
|
+
* - `secondary`: the indicator spans the full tab width.
|
|
157
|
+
*
|
|
158
|
+
* Defaults to `primary`.
|
|
159
|
+
*/
|
|
160
|
+
tabBarVariant?: 'primary' | 'secondary' | undefined;
|
|
142
161
|
|
|
143
162
|
/**
|
|
144
163
|
* Style object for the tab bar indicator.
|
|
145
164
|
*/
|
|
146
|
-
tabBarIndicatorStyle?: StyleProp<ViewStyle
|
|
165
|
+
tabBarIndicatorStyle?: StyleProp<ViewStyle> | undefined;
|
|
147
166
|
|
|
148
167
|
/**
|
|
149
168
|
* Style object for the view containing the tab bar indicator.
|
|
150
169
|
*/
|
|
151
|
-
tabBarIndicatorContainerStyle?: StyleProp<ViewStyle
|
|
170
|
+
tabBarIndicatorContainerStyle?: StyleProp<ViewStyle> | undefined;
|
|
152
171
|
|
|
153
172
|
/**
|
|
154
173
|
* ID to locate this tab button in tests.
|
|
155
174
|
*/
|
|
156
|
-
tabBarButtonTestID?: string;
|
|
175
|
+
tabBarButtonTestID?: string | undefined;
|
|
157
176
|
|
|
158
177
|
/**
|
|
159
178
|
* Color for the icon and label in the active tab.
|
|
160
179
|
*/
|
|
161
|
-
tabBarActiveTintColor?: ColorValue;
|
|
180
|
+
tabBarActiveTintColor?: ColorValue | undefined;
|
|
162
181
|
|
|
163
182
|
/**
|
|
164
183
|
* Color for the icon and label in the inactive tabs.
|
|
165
184
|
*/
|
|
166
|
-
tabBarInactiveTintColor?: ColorValue;
|
|
185
|
+
tabBarInactiveTintColor?: ColorValue | undefined;
|
|
167
186
|
|
|
168
187
|
/**
|
|
169
188
|
* Color for material ripple (Android >= 5.0 only).
|
|
170
189
|
*/
|
|
171
|
-
tabBarPressColor?: ColorValue;
|
|
190
|
+
tabBarPressColor?: ColorValue | undefined;
|
|
172
191
|
|
|
173
192
|
/**
|
|
174
193
|
* Opacity for pressed tab (iOS and Android < 5.0 only).
|
|
175
194
|
*/
|
|
176
|
-
tabBarPressOpacity?: number;
|
|
195
|
+
tabBarPressOpacity?: number | undefined;
|
|
177
196
|
|
|
178
197
|
/**
|
|
179
198
|
* Boolean indicating whether the tab bar bounces when overscrolling.
|
|
180
199
|
*/
|
|
181
|
-
tabBarBounces?: boolean;
|
|
200
|
+
tabBarBounces?: boolean | undefined;
|
|
182
201
|
|
|
183
202
|
/**
|
|
184
203
|
* Boolean indicating whether to make the tab bar scrollable.
|
|
185
204
|
*
|
|
186
205
|
* If you set this to `true`, you should also specify a width in `tabBarItemStyle` to improve the performance of initial render.
|
|
187
206
|
*/
|
|
188
|
-
tabBarScrollEnabled?: boolean;
|
|
207
|
+
tabBarScrollEnabled?: boolean | undefined;
|
|
189
208
|
|
|
190
209
|
/**
|
|
191
210
|
* Style object for the tab label.
|
|
192
211
|
*/
|
|
193
|
-
tabBarLabelStyle?: StyleProp<TextStyle
|
|
212
|
+
tabBarLabelStyle?: StyleProp<TextStyle> | undefined;
|
|
194
213
|
|
|
195
214
|
/**
|
|
196
215
|
* Style object for the individual tab items.
|
|
197
216
|
*/
|
|
198
|
-
tabBarItemStyle?: StyleProp<ViewStyle
|
|
217
|
+
tabBarItemStyle?: StyleProp<ViewStyle> | undefined;
|
|
199
218
|
|
|
200
219
|
/**
|
|
201
220
|
* Style object for the view containing the tab items.
|
|
202
221
|
*/
|
|
203
|
-
tabBarContentContainerStyle?: StyleProp<ViewStyle
|
|
222
|
+
tabBarContentContainerStyle?: StyleProp<ViewStyle> | undefined;
|
|
204
223
|
|
|
205
224
|
/**
|
|
206
225
|
* Style object for the the tab bar.
|
|
207
226
|
*/
|
|
208
|
-
tabBarStyle?: StyleProp<ViewStyle
|
|
227
|
+
tabBarStyle?: StyleProp<ViewStyle> | undefined;
|
|
209
228
|
|
|
210
229
|
/**
|
|
211
230
|
* Gap between tabs
|
|
212
231
|
*/
|
|
213
|
-
tabBarGap?: number;
|
|
232
|
+
tabBarGap?: number | undefined;
|
|
214
233
|
|
|
215
234
|
/**
|
|
216
235
|
* Allows to customize the android ripple effect (Android >= 5.0 only).
|
|
217
236
|
*
|
|
218
|
-
* Default: `{ borderless: true }`
|
|
237
|
+
* Default: `{ borderless: false, foreground: true }`
|
|
219
238
|
*/
|
|
220
|
-
tabBarAndroidRipple?: PressableAndroidRippleConfig;
|
|
239
|
+
tabBarAndroidRipple?: PressableAndroidRippleConfig | undefined;
|
|
221
240
|
|
|
222
241
|
/**
|
|
223
242
|
* Whether to enable swipe gestures when this screen is focused.
|
|
224
243
|
* Swipe gestures are enabled by default. Passing `false` will disable swipe gestures,
|
|
225
244
|
* but the user can still switch tabs by pressing the tab bar.
|
|
226
245
|
*/
|
|
227
|
-
swipeEnabled?: boolean;
|
|
246
|
+
swipeEnabled?: boolean | undefined;
|
|
228
247
|
|
|
229
248
|
/**
|
|
230
249
|
* Whether to enable animations when switching between tabs by pressing on the tab bar or programmatically.
|
|
231
250
|
* Switching tab via swipe gesture will still result in an animation.
|
|
232
251
|
*/
|
|
233
|
-
animationEnabled?: boolean;
|
|
252
|
+
animationEnabled?: boolean | undefined;
|
|
234
253
|
|
|
235
254
|
/**
|
|
236
255
|
* Whether this screen should be lazily rendered. When this is set to `true`,
|
|
@@ -243,13 +262,13 @@ export type MaterialTopTabNavigationOptions = {
|
|
|
243
262
|
* when they come into the viewport. You can use the `lazyPlaceholder` prop to customize
|
|
244
263
|
* what the user sees during this short period.
|
|
245
264
|
*/
|
|
246
|
-
lazy?: boolean;
|
|
265
|
+
lazy?: boolean | undefined;
|
|
247
266
|
|
|
248
267
|
/**
|
|
249
268
|
* When `lazy` is enabled, you can specify how many adjacent screens should be preloaded in advance with this prop.
|
|
250
269
|
* This value defaults to `0` which means lazy pages are loaded as they come into the viewport.
|
|
251
270
|
*/
|
|
252
|
-
lazyPreloadDistance?: number;
|
|
271
|
+
lazyPreloadDistance?: number | undefined;
|
|
253
272
|
|
|
254
273
|
/**
|
|
255
274
|
* Function that returns a React element to render if this screen hasn't been rendered yet.
|
|
@@ -259,12 +278,25 @@ export type MaterialTopTabNavigationOptions = {
|
|
|
259
278
|
*
|
|
260
279
|
* By default, this renders `null`.
|
|
261
280
|
*/
|
|
262
|
-
lazyPlaceholder?: () => React.ReactNode;
|
|
281
|
+
lazyPlaceholder?: (() => React.ReactNode) | undefined;
|
|
263
282
|
|
|
264
283
|
/**
|
|
265
284
|
* Style object for the component wrapping the screen content.
|
|
266
285
|
*/
|
|
267
|
-
sceneStyle?: StyleProp<ViewStyle
|
|
286
|
+
sceneStyle?: StyleProp<ViewStyle> | undefined;
|
|
287
|
+
|
|
288
|
+
/**
|
|
289
|
+
* What should happen when screens become inactive.
|
|
290
|
+
* - `pause`: Effects are cleaned up.
|
|
291
|
+
* - `none`: Screen renders normally
|
|
292
|
+
*
|
|
293
|
+
* Defaults to `pause`.
|
|
294
|
+
*
|
|
295
|
+
* If you set `lazy: false` or preload a screen,
|
|
296
|
+
* It won't be paused until after the first time it becomes focused.
|
|
297
|
+
* This makes sure that effects are run to initialize the screen.
|
|
298
|
+
*/
|
|
299
|
+
inactiveBehavior?: ('pause' | 'none') | undefined;
|
|
268
300
|
};
|
|
269
301
|
|
|
270
302
|
export type MaterialTopTabDescriptor = Descriptor<
|
|
@@ -297,11 +329,11 @@ export type MaterialTopTabNavigationConfig = Omit<
|
|
|
297
329
|
/**
|
|
298
330
|
* Function that returns a React element to display as the tab bar.
|
|
299
331
|
*/
|
|
300
|
-
tabBar?: (props: MaterialTopTabBarProps) => React.ReactNode;
|
|
332
|
+
tabBar?: ((props: MaterialTopTabBarProps) => React.ReactNode) | undefined;
|
|
301
333
|
/**
|
|
302
334
|
* Function that returns a React element to override the underlying adapter.
|
|
303
335
|
*/
|
|
304
|
-
adapter?: TabViewProps<Route<string>>['renderAdapter'];
|
|
336
|
+
adapter?: TabViewProps<Route<string>>['renderAdapter'] | undefined;
|
|
305
337
|
};
|
|
306
338
|
|
|
307
339
|
export type MaterialTopTabBarProps = Pick<
|
|
@@ -3,7 +3,7 @@ import * as React from 'react';
|
|
|
3
3
|
import { TabAnimationContext } from './TabAnimationContext';
|
|
4
4
|
|
|
5
5
|
export function useTabAnimation() {
|
|
6
|
-
const animation = React.
|
|
6
|
+
const animation = React.use(TabAnimationContext);
|
|
7
7
|
|
|
8
8
|
if (animation === undefined) {
|
|
9
9
|
throw new Error(
|
|
@@ -1,19 +1,9 @@
|
|
|
1
|
-
import { Text } from '@react-navigation/elements';
|
|
1
|
+
import { PlatformIcon, Text } from '@react-navigation/elements';
|
|
2
2
|
import { Color } from '@react-navigation/elements/internal';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
type TabNavigationState,
|
|
6
|
-
useLinkBuilder,
|
|
7
|
-
useLocale,
|
|
8
|
-
useTheme,
|
|
9
|
-
} from '@react-navigation/native';
|
|
3
|
+
import { useLinkBuilder, useLocale, useTheme } from '@react-navigation/native';
|
|
4
|
+
import * as React from 'react';
|
|
10
5
|
import { type ColorValue, StyleSheet } from 'react-native';
|
|
11
|
-
import {
|
|
12
|
-
type Route,
|
|
13
|
-
TabBar,
|
|
14
|
-
TabBarIndicator,
|
|
15
|
-
type TabDescriptor,
|
|
16
|
-
} from 'react-native-tab-view';
|
|
6
|
+
import { type Route, TabBar, type TabDescriptor } from 'react-native-tab-view';
|
|
17
7
|
|
|
18
8
|
import type { MaterialTopTabBarProps } from '../types';
|
|
19
9
|
|
|
@@ -53,18 +43,34 @@ export function MaterialTopTabBar({
|
|
|
53
43
|
const { direction } = useLocale();
|
|
54
44
|
const { buildHref } = useLinkBuilder();
|
|
55
45
|
|
|
56
|
-
const
|
|
46
|
+
const focusedRoute = state.routes[state.index];
|
|
47
|
+
|
|
48
|
+
if (focusedRoute == null) {
|
|
49
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const focusedOptions = descriptors[focusedRoute.key]?.options ?? {};
|
|
53
|
+
const tabBarVariant = focusedOptions.tabBarVariant ?? 'primary';
|
|
57
54
|
|
|
58
55
|
const activeColor: ColorValue =
|
|
59
|
-
focusedOptions.tabBarActiveTintColor ??
|
|
56
|
+
focusedOptions.tabBarActiveTintColor ??
|
|
57
|
+
(tabBarVariant === 'primary' ? colors.primary : colors.text);
|
|
58
|
+
|
|
60
59
|
const inactiveColor: ColorValue =
|
|
61
60
|
focusedOptions.tabBarInactiveTintColor ??
|
|
62
|
-
Color(
|
|
63
|
-
(dark ? 'rgba(255, 255, 255, 0.
|
|
61
|
+
Color(colors.text)?.alpha(0.68).string() ??
|
|
62
|
+
(dark ? 'rgba(255, 255, 255, 0.68)' : 'rgba(0, 0, 0, 0.68)');
|
|
63
|
+
|
|
64
|
+
const pressColor: ColorValue =
|
|
65
|
+
focusedOptions.tabBarPressColor ??
|
|
66
|
+
Color(tabBarVariant === 'primary' ? colors.primary : colors.text)
|
|
67
|
+
?.alpha(0.12)
|
|
68
|
+
.string() ??
|
|
69
|
+
(dark ? 'rgba(255, 255, 255, 0.12)' : 'rgba(0, 0, 0, 0.12)');
|
|
64
70
|
|
|
65
71
|
const tabBarOptions = Object.fromEntries(
|
|
66
72
|
state.routes.map((route) => {
|
|
67
|
-
const
|
|
73
|
+
const options = descriptors[route.key]?.options ?? {};
|
|
68
74
|
|
|
69
75
|
const {
|
|
70
76
|
title,
|
|
@@ -79,6 +85,41 @@ export function MaterialTopTabBar({
|
|
|
79
85
|
tabBarLabelStyle,
|
|
80
86
|
} = options;
|
|
81
87
|
|
|
88
|
+
let icon;
|
|
89
|
+
|
|
90
|
+
if (tabBarShowIcon === false) {
|
|
91
|
+
icon = undefined;
|
|
92
|
+
} else if (tabBarIcon) {
|
|
93
|
+
icon = ({
|
|
94
|
+
focused,
|
|
95
|
+
color,
|
|
96
|
+
size,
|
|
97
|
+
}: {
|
|
98
|
+
focused: boolean;
|
|
99
|
+
color: ColorValue;
|
|
100
|
+
size: number;
|
|
101
|
+
}) => {
|
|
102
|
+
const iconValue =
|
|
103
|
+
typeof tabBarIcon === 'function'
|
|
104
|
+
? tabBarIcon({ focused, color, size })
|
|
105
|
+
: tabBarIcon;
|
|
106
|
+
|
|
107
|
+
if (React.isValidElement(iconValue)) {
|
|
108
|
+
return iconValue;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
typeof iconValue === 'object' &&
|
|
113
|
+
iconValue != null &&
|
|
114
|
+
'type' in iconValue
|
|
115
|
+
) {
|
|
116
|
+
return <PlatformIcon icon={iconValue} size={size} color={color} />;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return null;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
82
123
|
return [
|
|
83
124
|
route.key,
|
|
84
125
|
{
|
|
@@ -86,14 +127,14 @@ export function MaterialTopTabBar({
|
|
|
86
127
|
testID: tabBarButtonTestID,
|
|
87
128
|
accessibilityLabel: tabBarAccessibilityLabel,
|
|
88
129
|
badge: tabBarBadge,
|
|
89
|
-
icon
|
|
130
|
+
icon,
|
|
90
131
|
label:
|
|
91
132
|
tabBarShowLabel === false
|
|
92
133
|
? undefined
|
|
93
134
|
: typeof tabBarLabel === 'function'
|
|
94
135
|
? ({ labelText, color }: MaterialLabelProps) =>
|
|
95
136
|
tabBarLabel({
|
|
96
|
-
focused:
|
|
137
|
+
focused: focusedRoute.key === route.key,
|
|
97
138
|
color,
|
|
98
139
|
children: labelText ?? route.name,
|
|
99
140
|
})
|
|
@@ -113,6 +154,8 @@ export function MaterialTopTabBar({
|
|
|
113
154
|
})
|
|
114
155
|
);
|
|
115
156
|
|
|
157
|
+
const tabBarIndicator = focusedOptions.tabBarIndicator;
|
|
158
|
+
|
|
116
159
|
return (
|
|
117
160
|
<TabBar
|
|
118
161
|
{...rest}
|
|
@@ -123,7 +166,7 @@ export function MaterialTopTabBar({
|
|
|
123
166
|
bounces={focusedOptions.tabBarBounces}
|
|
124
167
|
activeColor={activeColor}
|
|
125
168
|
inactiveColor={inactiveColor}
|
|
126
|
-
pressColor={
|
|
169
|
+
pressColor={pressColor}
|
|
127
170
|
pressOpacity={focusedOptions.tabBarPressOpacity}
|
|
128
171
|
tabStyle={focusedOptions.tabBarItemStyle}
|
|
129
172
|
indicatorStyle={[
|
|
@@ -131,10 +174,14 @@ export function MaterialTopTabBar({
|
|
|
131
174
|
focusedOptions.tabBarIndicatorStyle,
|
|
132
175
|
]}
|
|
133
176
|
gap={focusedOptions.tabBarGap}
|
|
177
|
+
variant={tabBarVariant}
|
|
134
178
|
android_ripple={focusedOptions.tabBarAndroidRipple}
|
|
135
179
|
indicatorContainerStyle={focusedOptions.tabBarIndicatorContainerStyle}
|
|
136
180
|
contentContainerStyle={focusedOptions.tabBarContentContainerStyle}
|
|
137
|
-
style={[
|
|
181
|
+
style={[
|
|
182
|
+
{ backgroundColor: colors.card, borderBottomColor: colors.border },
|
|
183
|
+
focusedOptions.tabBarStyle,
|
|
184
|
+
]}
|
|
138
185
|
onTabPress={({ route, preventDefault }) => {
|
|
139
186
|
const event = navigation.emit({
|
|
140
187
|
type: 'tabPress',
|
|
@@ -152,16 +199,12 @@ export function MaterialTopTabBar({
|
|
|
152
199
|
target: route.key,
|
|
153
200
|
})
|
|
154
201
|
}
|
|
155
|
-
renderIndicator={
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
) : (
|
|
162
|
-
<TabBarIndicator navigationState={state} {...rest} />
|
|
163
|
-
);
|
|
164
|
-
}}
|
|
202
|
+
renderIndicator={
|
|
203
|
+
tabBarIndicator
|
|
204
|
+
? ({ navigationState: _, ...rest }) =>
|
|
205
|
+
tabBarIndicator({ ...rest, state })
|
|
206
|
+
: undefined
|
|
207
|
+
}
|
|
165
208
|
/>
|
|
166
209
|
);
|
|
167
210
|
}
|
|
@@ -170,7 +213,6 @@ const styles = StyleSheet.create({
|
|
|
170
213
|
label: {
|
|
171
214
|
textAlign: 'center',
|
|
172
215
|
fontSize: 14,
|
|
173
|
-
margin: 4,
|
|
174
216
|
backgroundColor: 'transparent',
|
|
175
217
|
},
|
|
176
218
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ActivityView } from '@react-navigation/elements/internal';
|
|
1
2
|
import {
|
|
2
3
|
CommonActions,
|
|
3
4
|
type ParamListBase,
|
|
@@ -6,6 +7,8 @@ import {
|
|
|
6
7
|
useLocale,
|
|
7
8
|
useTheme,
|
|
8
9
|
} from '@react-navigation/native';
|
|
10
|
+
import { useMemo, useState } from 'react';
|
|
11
|
+
import { type Animated, StyleSheet } from 'react-native';
|
|
9
12
|
import { TabView } from 'react-native-tab-view';
|
|
10
13
|
|
|
11
14
|
import type {
|
|
@@ -13,6 +16,7 @@ import type {
|
|
|
13
16
|
MaterialTopTabDescriptorMap,
|
|
14
17
|
MaterialTopTabNavigationConfig,
|
|
15
18
|
MaterialTopTabNavigationHelpers,
|
|
19
|
+
MaterialTopTabNavigationOptions,
|
|
16
20
|
} from '../types';
|
|
17
21
|
import { TabAnimationContext } from '../utils/TabAnimationContext';
|
|
18
22
|
import { MaterialTopTabBar } from './MaterialTopTabBar';
|
|
@@ -54,7 +58,21 @@ export function MaterialTopTabView({
|
|
|
54
58
|
});
|
|
55
59
|
};
|
|
56
60
|
|
|
57
|
-
const
|
|
61
|
+
const focusedRoute = state.routes[state.index];
|
|
62
|
+
|
|
63
|
+
if (focusedRoute == null) {
|
|
64
|
+
throw new Error(`Couldn't find a route at index ${state.index}.`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const focusedDescriptor = descriptors[focusedRoute.key];
|
|
68
|
+
|
|
69
|
+
if (focusedDescriptor == null) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
`Couldn't find a descriptor for route '${focusedRoute.key}'.`
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const focusedOptions = focusedDescriptor.options;
|
|
58
76
|
|
|
59
77
|
return (
|
|
60
78
|
<TabView
|
|
@@ -62,23 +80,42 @@ export function MaterialTopTabView({
|
|
|
62
80
|
onIndexChange={(index) => {
|
|
63
81
|
const route = state.routes[index];
|
|
64
82
|
|
|
83
|
+
if (route == null) {
|
|
84
|
+
throw new Error(`Couldn't find a route at index ${index}.`);
|
|
85
|
+
}
|
|
86
|
+
|
|
65
87
|
navigation.dispatch({
|
|
66
|
-
...CommonActions.navigate(route),
|
|
88
|
+
...CommonActions.navigate(route.name, route.params),
|
|
67
89
|
target: state.key,
|
|
68
90
|
});
|
|
69
91
|
}}
|
|
70
|
-
renderScene={({ route, position }) =>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
92
|
+
renderScene={({ route, position }) => {
|
|
93
|
+
const descriptor = descriptors[route.key];
|
|
94
|
+
|
|
95
|
+
if (descriptor == null) {
|
|
96
|
+
throw new Error(
|
|
97
|
+
`Couldn't find a descriptor for route '${route.key}'.`
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<SceneContent
|
|
103
|
+
focused={route.key === focusedRoute.key}
|
|
104
|
+
preloaded={state.preloadedRouteKeys.includes(route.key)}
|
|
105
|
+
position={position}
|
|
106
|
+
options={descriptor.options}
|
|
107
|
+
>
|
|
108
|
+
{descriptor.render()}
|
|
109
|
+
</SceneContent>
|
|
110
|
+
);
|
|
111
|
+
}}
|
|
75
112
|
navigationState={state}
|
|
76
113
|
renderTabBar={renderTabBar}
|
|
77
114
|
renderLazyPlaceholder={({ route }) =>
|
|
78
|
-
descriptors[route.key]
|
|
115
|
+
descriptors[route.key]?.options.lazyPlaceholder?.() ?? null
|
|
79
116
|
}
|
|
80
117
|
lazy={({ route }) =>
|
|
81
|
-
descriptors[route.key]
|
|
118
|
+
descriptors[route.key]?.options.lazy === true &&
|
|
82
119
|
!state.preloadedRouteKeys.includes(route.key)
|
|
83
120
|
}
|
|
84
121
|
lazyPreloadDistance={focusedOptions.lazyPreloadDistance}
|
|
@@ -105,3 +142,57 @@ export function MaterialTopTabView({
|
|
|
105
142
|
/>
|
|
106
143
|
);
|
|
107
144
|
}
|
|
145
|
+
|
|
146
|
+
function SceneContent({
|
|
147
|
+
focused,
|
|
148
|
+
preloaded,
|
|
149
|
+
position,
|
|
150
|
+
options,
|
|
151
|
+
children,
|
|
152
|
+
}: {
|
|
153
|
+
focused: boolean;
|
|
154
|
+
preloaded: boolean;
|
|
155
|
+
position: Animated.AnimatedInterpolation<number>;
|
|
156
|
+
options: MaterialTopTabNavigationOptions;
|
|
157
|
+
children: React.ReactNode;
|
|
158
|
+
}) {
|
|
159
|
+
const { inactiveBehavior = 'pause', lazy } = options;
|
|
160
|
+
|
|
161
|
+
const [loaded, setLoaded] = useState(focused);
|
|
162
|
+
|
|
163
|
+
if (focused && !loaded) {
|
|
164
|
+
setLoaded(true);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const animationContext = useMemo(() => ({ position }), [position]);
|
|
168
|
+
|
|
169
|
+
// For preloaded screens and if lazy is false,
|
|
170
|
+
// Keep them active so that the effects can run
|
|
171
|
+
const isActive =
|
|
172
|
+
inactiveBehavior === 'none' ||
|
|
173
|
+
focused ||
|
|
174
|
+
preloaded ||
|
|
175
|
+
(lazy === false && !loaded);
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<TabAnimationContext.Provider value={animationContext}>
|
|
179
|
+
<ActivityView
|
|
180
|
+
mode={isActive ? 'normal' : 'paused'}
|
|
181
|
+
visible={
|
|
182
|
+
// Tabs can be swiped quickly
|
|
183
|
+
// So we keep all tabs visible to avoid flash of blank screen
|
|
184
|
+
true
|
|
185
|
+
}
|
|
186
|
+
style={styles.scene}
|
|
187
|
+
>
|
|
188
|
+
{children}
|
|
189
|
+
</ActivityView>
|
|
190
|
+
</TabAnimationContext.Provider>
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const styles = StyleSheet.create({
|
|
195
|
+
scene: {
|
|
196
|
+
flex: 1,
|
|
197
|
+
},
|
|
198
|
+
});
|