@react-navigation/bottom-tabs 8.0.0-alpha.12 → 8.0.0-alpha.14
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/navigators/createBottomTabNavigator.js +1 -18
- package/lib/module/navigators/createBottomTabNavigator.js.map +1 -1
- package/lib/module/views/BottomTabViewCustom.js +66 -62
- package/lib/module/views/BottomTabViewCustom.js.map +1 -1
- package/lib/module/views/BottomTabViewNativeImpl.js +54 -12
- package/lib/module/views/BottomTabViewNativeImpl.js.map +1 -1
- package/lib/typescript/src/navigators/createBottomTabNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +21 -12
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabItem.d.ts +16 -16
- package/lib/typescript/src/views/BottomTabItem.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabViewCustom.d.ts +1 -1
- package/lib/typescript/src/views/BottomTabViewCustom.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabViewNativeImpl.d.ts.map +1 -1
- package/lib/typescript/src/views/TabBarIcon.d.ts +3 -3
- package/lib/typescript/src/views/TabBarIcon.d.ts.map +1 -1
- package/package.json +12 -12
- package/src/navigators/createBottomTabNavigator.tsx +0 -28
- package/src/types.tsx +22 -13
- package/src/views/BottomTabItem.tsx +16 -16
- package/src/views/BottomTabViewCustom.tsx +127 -114
- package/src/views/BottomTabViewNativeImpl.tsx +92 -21
- package/src/views/TabBarIcon.tsx +3 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
ActivityView,
|
|
3
|
+
Container,
|
|
3
4
|
SafeAreaProviderCompat,
|
|
4
5
|
} from '@react-navigation/elements/internal';
|
|
5
6
|
import {
|
|
@@ -16,7 +17,6 @@ import {
|
|
|
16
17
|
StyleSheet,
|
|
17
18
|
type ViewStyle,
|
|
18
19
|
} from 'react-native';
|
|
19
|
-
import { Screen, ScreenContainer } from 'react-native-screens';
|
|
20
20
|
|
|
21
21
|
import {
|
|
22
22
|
FadeTransition,
|
|
@@ -44,11 +44,6 @@ type Props = BottomTabNavigationConfig & {
|
|
|
44
44
|
descriptors: BottomTabDescriptorMap;
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
const EPSILON = 1e-5;
|
|
48
|
-
const STATE_INACTIVE = 0;
|
|
49
|
-
const STATE_TRANSITIONING_OR_BELOW_TOP = 1;
|
|
50
|
-
const STATE_ON_TOP = 2;
|
|
51
|
-
|
|
52
47
|
const NAMED_TRANSITIONS_PRESETS = {
|
|
53
48
|
fade: FadeTransition,
|
|
54
49
|
shift: ShiftTransition,
|
|
@@ -82,22 +77,43 @@ export function BottomTabViewCustom({
|
|
|
82
77
|
state,
|
|
83
78
|
navigation,
|
|
84
79
|
descriptors,
|
|
85
|
-
detachInactiveScreens = Platform.OS === 'web' ||
|
|
86
|
-
Platform.OS === 'android' ||
|
|
87
|
-
Platform.OS === 'ios',
|
|
88
80
|
}: Props) {
|
|
89
81
|
const { routes } = state;
|
|
90
82
|
const focusedRouteKey = routes[state.index].key;
|
|
91
83
|
|
|
92
|
-
const
|
|
84
|
+
const [loaded, setLoaded] = React.useState([focusedRouteKey]);
|
|
85
|
+
|
|
86
|
+
if (!loaded.includes(focusedRouteKey)) {
|
|
87
|
+
setLoaded([...loaded, focusedRouteKey]);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const [lastUpdate, setLastUpdate] = React.useState<{
|
|
91
|
+
current: string;
|
|
92
|
+
previous?: string;
|
|
93
|
+
}>({
|
|
94
|
+
current: focusedRouteKey,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
if (lastUpdate.current !== focusedRouteKey) {
|
|
98
|
+
setLastUpdate({
|
|
99
|
+
current: focusedRouteKey,
|
|
100
|
+
previous: lastUpdate.current,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
93
104
|
const tabAnims = useAnimatedHashMap(state);
|
|
94
105
|
|
|
106
|
+
const [isAnimating, setIsAnimating] = React.useState(false);
|
|
107
|
+
|
|
108
|
+
const previousRouteKeyRef = React.useRef(focusedRouteKey);
|
|
109
|
+
|
|
95
110
|
React.useEffect(() => {
|
|
96
111
|
const previousRouteKey = previousRouteKeyRef.current;
|
|
97
112
|
|
|
98
113
|
let popToTopAction: NavigationAction | undefined;
|
|
99
114
|
|
|
100
115
|
if (
|
|
116
|
+
previousRouteKey &&
|
|
101
117
|
previousRouteKey !== focusedRouteKey &&
|
|
102
118
|
descriptors[previousRouteKey]?.options.popToTopOnBlur
|
|
103
119
|
) {
|
|
@@ -105,6 +121,12 @@ export function BottomTabViewCustom({
|
|
|
105
121
|
(route) => route.key === previousRouteKey
|
|
106
122
|
);
|
|
107
123
|
|
|
124
|
+
console.log(
|
|
125
|
+
'checking if we need to pop to top for route',
|
|
126
|
+
previousRouteKey,
|
|
127
|
+
focusedRouteKey
|
|
128
|
+
);
|
|
129
|
+
|
|
108
130
|
if (prevRoute?.state?.type === 'stack' && prevRoute.state.key) {
|
|
109
131
|
popToTopAction = {
|
|
110
132
|
...StackActions.popToTop(),
|
|
@@ -113,6 +135,8 @@ export function BottomTabViewCustom({
|
|
|
113
135
|
}
|
|
114
136
|
}
|
|
115
137
|
|
|
138
|
+
let timer: ReturnType<typeof setTimeout>;
|
|
139
|
+
|
|
116
140
|
const animateToIndex = () => {
|
|
117
141
|
if (previousRouteKey !== focusedRouteKey) {
|
|
118
142
|
navigation.emit({
|
|
@@ -121,42 +145,45 @@ export function BottomTabViewCustom({
|
|
|
121
145
|
});
|
|
122
146
|
}
|
|
123
147
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
transitionSpec
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
if
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
148
|
+
const animations = state.routes
|
|
149
|
+
.map((route, index) => {
|
|
150
|
+
const { options } = descriptors[route.key];
|
|
151
|
+
const {
|
|
152
|
+
animation = 'none',
|
|
153
|
+
transitionSpec = NAMED_TRANSITIONS_PRESETS[animation]
|
|
154
|
+
.transitionSpec,
|
|
155
|
+
} = options;
|
|
156
|
+
|
|
157
|
+
let spec = transitionSpec;
|
|
158
|
+
|
|
159
|
+
if (route.key !== previousRouteKey && route.key !== focusedRouteKey) {
|
|
160
|
+
// Don't animate if the screen is not previous one or new one
|
|
161
|
+
// This will avoid flicker for screens not involved in the transition
|
|
162
|
+
spec = NAMED_TRANSITIONS_PRESETS.none.transitionSpec;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
spec = spec ?? NAMED_TRANSITIONS_PRESETS.none.transitionSpec;
|
|
166
|
+
|
|
167
|
+
const toValue =
|
|
168
|
+
index === state.index ? 0 : index >= state.index ? 1 : -1;
|
|
169
|
+
|
|
170
|
+
return Animated[spec.animation](tabAnims[route.key], {
|
|
171
|
+
...spec.config,
|
|
172
|
+
toValue,
|
|
173
|
+
useNativeDriver,
|
|
174
|
+
});
|
|
175
|
+
})
|
|
176
|
+
.filter((anim) => anim != null);
|
|
177
|
+
|
|
178
|
+
if (animations.length) {
|
|
179
|
+
// eslint-disable-next-line @eslint-react/hooks-extra/no-direct-set-state-in-use-effect
|
|
180
|
+
setIsAnimating(true);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
Animated.parallel(animations).start(({ finished }) => {
|
|
158
184
|
if (finished && popToTopAction) {
|
|
159
|
-
|
|
185
|
+
console.log('dispatching pop to top action', popToTopAction);
|
|
186
|
+
// navigation.dispatch(popToTopAction);
|
|
160
187
|
}
|
|
161
188
|
|
|
162
189
|
if (previousRouteKey !== focusedRouteKey) {
|
|
@@ -165,12 +192,24 @@ export function BottomTabViewCustom({
|
|
|
165
192
|
target: focusedRouteKey,
|
|
166
193
|
});
|
|
167
194
|
}
|
|
195
|
+
|
|
196
|
+
if (finished && animations.length) {
|
|
197
|
+
// Delay clearing `isAnimating`
|
|
198
|
+
// This will give time for `popToAction` to get handled before pause
|
|
199
|
+
timer = setTimeout(() => {
|
|
200
|
+
setIsAnimating(false);
|
|
201
|
+
}, 32);
|
|
202
|
+
}
|
|
168
203
|
});
|
|
169
204
|
};
|
|
170
205
|
|
|
171
206
|
animateToIndex();
|
|
172
207
|
|
|
173
208
|
previousRouteKeyRef.current = focusedRouteKey;
|
|
209
|
+
|
|
210
|
+
return () => {
|
|
211
|
+
clearTimeout(timer);
|
|
212
|
+
};
|
|
174
213
|
}, [
|
|
175
214
|
descriptors,
|
|
176
215
|
focusedRouteKey,
|
|
@@ -203,11 +242,6 @@ export function BottomTabViewCustom({
|
|
|
203
242
|
</BottomTabBarHeightCallbackContext.Provider>
|
|
204
243
|
);
|
|
205
244
|
|
|
206
|
-
// If there is no animation, we only have 2 states: visible and invisible
|
|
207
|
-
const hasTwoStates = !routes.some((route) =>
|
|
208
|
-
hasAnimation(descriptors[route.key].options)
|
|
209
|
-
);
|
|
210
|
-
|
|
211
245
|
const tabBarPosition = useTabBarPosition(
|
|
212
246
|
descriptors[focusedRouteKey].options
|
|
213
247
|
);
|
|
@@ -224,12 +258,7 @@ export function BottomTabViewCustom({
|
|
|
224
258
|
{tabBarPosition === 'top' || tabBarPosition === 'left'
|
|
225
259
|
? tabBarElement
|
|
226
260
|
: null}
|
|
227
|
-
<
|
|
228
|
-
key="screens"
|
|
229
|
-
enabled={detachInactiveScreens}
|
|
230
|
-
hasTwoStates={hasTwoStates}
|
|
231
|
-
style={styles.screens}
|
|
232
|
-
>
|
|
261
|
+
<Container key="screens" style={styles.screens}>
|
|
233
262
|
{routes.map((route, index) => {
|
|
234
263
|
const descriptor = descriptors[route.key];
|
|
235
264
|
|
|
@@ -237,6 +266,7 @@ export function BottomTabViewCustom({
|
|
|
237
266
|
|
|
238
267
|
const {
|
|
239
268
|
lazy = true,
|
|
269
|
+
inactiveBehavior = 'pause',
|
|
240
270
|
animation = 'none',
|
|
241
271
|
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
242
272
|
.sceneStyleInterpolator,
|
|
@@ -246,6 +276,16 @@ export function BottomTabViewCustom({
|
|
|
246
276
|
const isFocused = state.index === index;
|
|
247
277
|
const isPreloaded = state.preloadedRouteKeys.includes(route.key);
|
|
248
278
|
|
|
279
|
+
if (
|
|
280
|
+
lazy &&
|
|
281
|
+
!loaded.includes(route.key) &&
|
|
282
|
+
!isFocused &&
|
|
283
|
+
!isPreloaded
|
|
284
|
+
) {
|
|
285
|
+
// Don't render a lazy screen if we've never navigated to it or it wasn't preloaded
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
|
|
249
289
|
const animationEnabled = hasAnimation(descriptor.options);
|
|
250
290
|
|
|
251
291
|
const content = (
|
|
@@ -254,75 +294,48 @@ export function BottomTabViewCustom({
|
|
|
254
294
|
progress={tabAnims[route.key]}
|
|
255
295
|
animationEnabled={animationEnabled}
|
|
256
296
|
sceneStyleInterpolator={sceneStyleInterpolator}
|
|
257
|
-
style={customSceneStyle}
|
|
297
|
+
style={[StyleSheet.absoluteFill, customSceneStyle]}
|
|
258
298
|
>
|
|
259
|
-
<
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
? {
|
|
268
|
-
/**
|
|
269
|
-
* Don't use react-native-screens on web:
|
|
270
|
-
* - It applies display: none as fallback, which triggers `onLayout` events
|
|
271
|
-
* - We still need to hide the view when screens is not enabled
|
|
272
|
-
*/
|
|
273
|
-
...StyleSheet.absoluteFillObject,
|
|
274
|
-
visibility: isFocused ? 'visible' : 'hidden',
|
|
275
|
-
}
|
|
276
|
-
: undefined
|
|
277
|
-
}
|
|
299
|
+
<ScreenContent
|
|
300
|
+
isFocused={isFocused}
|
|
301
|
+
route={route}
|
|
302
|
+
navigation={navigation}
|
|
303
|
+
options={options}
|
|
304
|
+
>
|
|
305
|
+
<BottomTabBarHeightContext.Provider
|
|
306
|
+
value={tabBarPosition === 'bottom' ? tabBarHeight : 0}
|
|
278
307
|
>
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
{render()}
|
|
283
|
-
</BottomTabBarHeightContext.Provider>
|
|
284
|
-
</ScreenContent>
|
|
285
|
-
</Lazy>
|
|
308
|
+
{render()}
|
|
309
|
+
</BottomTabBarHeightContext.Provider>
|
|
310
|
+
</ScreenContent>
|
|
286
311
|
</AnimatedScreenContent>
|
|
287
312
|
);
|
|
288
313
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
314
|
+
const isAnimatingRoute =
|
|
315
|
+
isAnimating &&
|
|
316
|
+
(lastUpdate.previous === route.key ||
|
|
317
|
+
lastUpdate.current === route.key);
|
|
292
318
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
STATE_TRANSITIONING_OR_BELOW_TOP,
|
|
301
|
-
STATE_INACTIVE, // the screen is detached after transition
|
|
302
|
-
],
|
|
303
|
-
extrapolate: 'extend',
|
|
304
|
-
})
|
|
305
|
-
: STATE_INACTIVE;
|
|
319
|
+
// For preloaded screens and if lazy is false,
|
|
320
|
+
// Keep them active so that the effects can run
|
|
321
|
+
const isActive =
|
|
322
|
+
inactiveBehavior === 'none' ||
|
|
323
|
+
isAnimatingRoute ||
|
|
324
|
+
isPreloaded ||
|
|
325
|
+
(lazy === false && !loaded.includes(route.key));
|
|
306
326
|
|
|
307
327
|
return (
|
|
308
|
-
<
|
|
328
|
+
<ActivityView
|
|
309
329
|
key={route.key}
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
zIndex: isFocused ? 0 : -1,
|
|
314
|
-
pointerEvents: isFocused ? 'auto' : 'none',
|
|
315
|
-
},
|
|
316
|
-
]}
|
|
317
|
-
activityState={activityState}
|
|
318
|
-
enabled={detachInactiveScreens}
|
|
319
|
-
shouldFreeze={activityState === STATE_INACTIVE && !isPreloaded}
|
|
330
|
+
mode={isFocused ? 'normal' : isActive ? 'inert' : 'paused'}
|
|
331
|
+
visible={isFocused || isAnimatingRoute}
|
|
332
|
+
style={{ ...StyleSheet.absoluteFill, zIndex: isFocused ? 0 : -1 }}
|
|
320
333
|
>
|
|
321
334
|
{content}
|
|
322
|
-
</
|
|
335
|
+
</ActivityView>
|
|
323
336
|
);
|
|
324
337
|
})}
|
|
325
|
-
</
|
|
338
|
+
</Container>
|
|
326
339
|
{tabBarPosition === 'bottom' || tabBarPosition === 'right'
|
|
327
340
|
? tabBarElement
|
|
328
341
|
: null}
|
|
@@ -339,7 +352,7 @@ function AnimatedScreenContent({
|
|
|
339
352
|
}: {
|
|
340
353
|
progress: Animated.Value;
|
|
341
354
|
animationEnabled: boolean;
|
|
342
|
-
sceneStyleInterpolator?: BottomTabSceneStyleInterpolator;
|
|
355
|
+
sceneStyleInterpolator?: BottomTabSceneStyleInterpolator | undefined;
|
|
343
356
|
children: React.ReactNode;
|
|
344
357
|
style: StyleProp<ViewStyle>;
|
|
345
358
|
}) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getLabel } from '@react-navigation/elements';
|
|
2
2
|
import {
|
|
3
|
+
ActivityView,
|
|
3
4
|
Color,
|
|
4
|
-
Lazy,
|
|
5
5
|
SafeAreaProviderCompat,
|
|
6
6
|
} from '@react-navigation/elements/internal';
|
|
7
7
|
import {
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type ColorValue,
|
|
21
21
|
Platform,
|
|
22
22
|
PlatformColor,
|
|
23
|
+
StyleSheet,
|
|
23
24
|
} from 'react-native';
|
|
24
25
|
import {
|
|
25
26
|
type PlatformIcon,
|
|
@@ -62,6 +63,18 @@ export function BottomTabViewNative({
|
|
|
62
63
|
const { dark, colors, fonts } = useTheme();
|
|
63
64
|
|
|
64
65
|
const focusedRouteKey = state.routes[state.index].key;
|
|
66
|
+
|
|
67
|
+
const [loaded, setLoaded] = React.useState([focusedRouteKey]);
|
|
68
|
+
|
|
69
|
+
if (!loaded.includes(focusedRouteKey)) {
|
|
70
|
+
setLoaded([...loaded, focusedRouteKey]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const [pendingNavigation, setPendingNavigation] = React.useState<{
|
|
74
|
+
from: string;
|
|
75
|
+
to: string;
|
|
76
|
+
} | null>(null);
|
|
77
|
+
|
|
65
78
|
const previousRouteKeyRef = React.useRef(focusedRouteKey);
|
|
66
79
|
|
|
67
80
|
React.useEffect(() => {
|
|
@@ -80,11 +93,26 @@ export function BottomTabViewNative({
|
|
|
80
93
|
...StackActions.popToTop(),
|
|
81
94
|
target: prevRoute.state.key,
|
|
82
95
|
};
|
|
96
|
+
|
|
83
97
|
navigation.dispatch(popToTopAction);
|
|
84
98
|
}
|
|
85
99
|
}
|
|
86
100
|
|
|
87
101
|
previousRouteKeyRef.current = focusedRouteKey;
|
|
102
|
+
|
|
103
|
+
// Delay clearing `isAnimating`
|
|
104
|
+
// This will give time for `popToAction` to get handled before pause
|
|
105
|
+
const timer = setTimeout(() => {
|
|
106
|
+
setPendingNavigation((pending) => {
|
|
107
|
+
if (pending?.to === focusedRouteKey) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return pending;
|
|
112
|
+
});
|
|
113
|
+
}, 32);
|
|
114
|
+
|
|
115
|
+
return () => clearTimeout(timer);
|
|
88
116
|
}, [descriptors, focusedRouteKey, navigation, state.index, state.routes]);
|
|
89
117
|
|
|
90
118
|
const currentOptions = descriptors[state.routes[state.index].key]?.options;
|
|
@@ -106,6 +134,8 @@ export function BottomTabViewNative({
|
|
|
106
134
|
const backgroundColor =
|
|
107
135
|
currentOptions.tabBarStyle?.backgroundColor ?? colors.background;
|
|
108
136
|
|
|
137
|
+
const shouldHideTabBar = currentOptions.tabBarStyle?.display === 'none';
|
|
138
|
+
|
|
109
139
|
let activeIndicatorColor = currentOptions?.tabBarActiveIndicatorColor;
|
|
110
140
|
let activeTintColor = currentOptions.tabBarActiveTintColor;
|
|
111
141
|
let inactiveTintColor = currentOptions.tabBarInactiveTintColor;
|
|
@@ -212,7 +242,7 @@ export function BottomTabViewNative({
|
|
|
212
242
|
? tabBarElement
|
|
213
243
|
: null}
|
|
214
244
|
<Tabs.Host
|
|
215
|
-
tabBarHidden={hasCustomTabBar}
|
|
245
|
+
tabBarHidden={hasCustomTabBar || shouldHideTabBar}
|
|
216
246
|
bottomAccessory={
|
|
217
247
|
bottomAccessory
|
|
218
248
|
? (environment) => bottomAccessory({ placement: environment })
|
|
@@ -263,6 +293,11 @@ export function BottomTabViewNative({
|
|
|
263
293
|
state.routes.findIndex((r) => r.key === route.key);
|
|
264
294
|
|
|
265
295
|
if (!isFocused) {
|
|
296
|
+
setPendingNavigation({
|
|
297
|
+
from: previousRouteKeyRef.current,
|
|
298
|
+
to: route.key,
|
|
299
|
+
});
|
|
300
|
+
|
|
266
301
|
navigation.dispatch({
|
|
267
302
|
...CommonActions.navigate(route.name, route.params),
|
|
268
303
|
target: state.key,
|
|
@@ -279,6 +314,7 @@ export function BottomTabViewNative({
|
|
|
279
314
|
const {
|
|
280
315
|
title,
|
|
281
316
|
lazy = true,
|
|
317
|
+
inactiveBehavior = 'pause',
|
|
282
318
|
tabBarLabel,
|
|
283
319
|
tabBarBadgeStyle,
|
|
284
320
|
tabBarIcon,
|
|
@@ -353,6 +389,15 @@ export function BottomTabViewNative({
|
|
|
353
389
|
const icon = getIcon(false);
|
|
354
390
|
const selectedIcon = getIcon(true);
|
|
355
391
|
|
|
392
|
+
// For preloaded screens and if lazy is false,
|
|
393
|
+
// Keep them active so that the effects can run
|
|
394
|
+
const isActive =
|
|
395
|
+
inactiveBehavior === 'none' ||
|
|
396
|
+
isPreloaded ||
|
|
397
|
+
pendingNavigation?.from === route.key ||
|
|
398
|
+
pendingNavigation?.to === route.key ||
|
|
399
|
+
(lazy === false && !loaded.includes(route.key));
|
|
400
|
+
|
|
356
401
|
return (
|
|
357
402
|
<Tabs.Screen
|
|
358
403
|
onWillAppear={() => onTransitionStart({ route })}
|
|
@@ -387,9 +432,20 @@ export function BottomTabViewNative({
|
|
|
387
432
|
? 'automatic'
|
|
388
433
|
: scrollEdgeEffects?.right,
|
|
389
434
|
}}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
435
|
+
scrollEdgeAppearance={{
|
|
436
|
+
tabBarBackgroundColor,
|
|
437
|
+
tabBarShadowColor,
|
|
438
|
+
tabBarBlurEffect,
|
|
439
|
+
stacked: {
|
|
440
|
+
normal: tabItemAppearance,
|
|
441
|
+
},
|
|
442
|
+
inline: {
|
|
443
|
+
normal: tabItemAppearance,
|
|
444
|
+
},
|
|
445
|
+
compactInline: {
|
|
446
|
+
normal: tabItemAppearance,
|
|
447
|
+
},
|
|
448
|
+
}}
|
|
393
449
|
standardAppearance={{
|
|
394
450
|
tabBarBackgroundColor,
|
|
395
451
|
tabBarShadowColor,
|
|
@@ -415,23 +471,38 @@ export function BottomTabViewNative({
|
|
|
415
471
|
}
|
|
416
472
|
experimental_userInterfaceStyle={dark ? 'dark' : 'light'}
|
|
417
473
|
>
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
474
|
+
{lazy &&
|
|
475
|
+
!loaded.includes(route.key) &&
|
|
476
|
+
!isFocused &&
|
|
477
|
+
!isPreloaded ? null : (
|
|
478
|
+
<ActivityView
|
|
479
|
+
key={route.key}
|
|
480
|
+
mode={isFocused ? 'normal' : isActive ? 'inert' : 'paused'}
|
|
481
|
+
visible={
|
|
482
|
+
// We don't need to hide the content since it's handled natively
|
|
483
|
+
// Hiding may also cause flash due to lag after native tab switch
|
|
484
|
+
// So we leave it always visible
|
|
485
|
+
true
|
|
486
|
+
}
|
|
487
|
+
style={StyleSheet.absoluteFill}
|
|
425
488
|
>
|
|
426
|
-
<
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
489
|
+
<ScreenContent
|
|
490
|
+
isFocused={isFocused}
|
|
491
|
+
route={route}
|
|
492
|
+
navigation={navigation}
|
|
493
|
+
options={options}
|
|
494
|
+
style={sceneStyle}
|
|
495
|
+
>
|
|
496
|
+
<AnimatedScreenContent isFocused={isFocused}>
|
|
497
|
+
<BottomTabBarHeightContext.Provider value={0}>
|
|
498
|
+
<NavigationMetaContext.Provider value={meta}>
|
|
499
|
+
{render()}
|
|
500
|
+
</NavigationMetaContext.Provider>
|
|
501
|
+
</BottomTabBarHeightContext.Provider>
|
|
502
|
+
</AnimatedScreenContent>
|
|
503
|
+
</ScreenContent>
|
|
504
|
+
</ActivityView>
|
|
505
|
+
)}
|
|
435
506
|
</Tabs.Screen>
|
|
436
507
|
);
|
|
437
508
|
})}
|
package/src/views/TabBarIcon.tsx
CHANGED
|
@@ -18,8 +18,8 @@ export type TabBarIconProps = {
|
|
|
18
18
|
route: Route<string>;
|
|
19
19
|
variant: 'uikit' | 'material';
|
|
20
20
|
size: 'compact' | 'regular';
|
|
21
|
-
badge?: string | number;
|
|
22
|
-
badgeStyle?: StyleProp<TextStyle
|
|
21
|
+
badge?: string | number | undefined;
|
|
22
|
+
badgeStyle?: StyleProp<TextStyle> | undefined;
|
|
23
23
|
activeOpacity: number;
|
|
24
24
|
inactiveOpacity: number;
|
|
25
25
|
activeTintColor: ColorValue;
|
|
@@ -31,7 +31,7 @@ export type TabBarIconProps = {
|
|
|
31
31
|
color: ColorValue;
|
|
32
32
|
size: number;
|
|
33
33
|
}) => BottomTabIcon | React.ReactNode);
|
|
34
|
-
allowFontScaling?: boolean;
|
|
34
|
+
allowFontScaling?: boolean | undefined;
|
|
35
35
|
style: StyleProp<ViewStyle>;
|
|
36
36
|
};
|
|
37
37
|
|