@react-navigation/bottom-tabs 7.19.2 → 7.21.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/unstable/Deferred.js +3 -10
- package/lib/module/unstable/Deferred.js.map +1 -1
- package/lib/module/unstable/NativeBottomTabView.native.js +309 -198
- package/lib/module/unstable/NativeBottomTabView.native.js.map +1 -1
- package/lib/module/views/BottomTabBar.js +111 -58
- package/lib/module/views/BottomTabBar.js.map +1 -1
- package/lib/module/views/BottomTabItem.js +2 -0
- package/lib/module/views/BottomTabItem.js.map +1 -1
- package/lib/module/views/BottomTabView.js +81 -60
- package/lib/module/views/BottomTabView.js.map +1 -1
- package/lib/module/views/TabBarIcon.js +6 -2
- package/lib/module/views/TabBarIcon.js.map +1 -1
- package/lib/typescript/src/types.d.ts +26 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/unstable/Deferred.d.ts.map +1 -1
- package/lib/typescript/src/unstable/NativeBottomTabView.native.d.ts.map +1 -1
- package/lib/typescript/src/unstable/types.d.ts +148 -5
- package/lib/typescript/src/unstable/types.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabBar.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabItem.d.ts +5 -1
- package/lib/typescript/src/views/BottomTabItem.d.ts.map +1 -1
- package/lib/typescript/src/views/BottomTabView.d.ts.map +1 -1
- package/lib/typescript/src/views/TabBarIcon.d.ts +2 -1
- package/lib/typescript/src/views/TabBarIcon.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/types.tsx +36 -2
- package/src/unstable/Deferred.tsx +3 -11
- package/src/unstable/NativeBottomTabView.native.tsx +376 -217
- package/src/unstable/types.tsx +164 -6
- package/src/views/BottomTabBar.tsx +153 -72
- package/src/views/BottomTabItem.tsx +6 -0
- package/src/views/BottomTabView.tsx +101 -69
- package/src/views/TabBarIcon.tsx +9 -2
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
Screen,
|
|
6
6
|
} from '@react-navigation/elements';
|
|
7
7
|
import {
|
|
8
|
+
type NavigationRoute,
|
|
8
9
|
type ParamListBase,
|
|
9
10
|
StackActions,
|
|
10
11
|
type TabNavigationState,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
} from '../TransitionConfigs/TransitionPresets';
|
|
20
21
|
import type {
|
|
21
22
|
BottomTabBarProps,
|
|
23
|
+
BottomTabDescriptor,
|
|
22
24
|
BottomTabDescriptorMap,
|
|
23
25
|
BottomTabHeaderProps,
|
|
24
26
|
BottomTabNavigationConfig,
|
|
@@ -187,9 +189,7 @@ export function BottomTabView(props: Props) {
|
|
|
187
189
|
// Delay clearing so the previous screen stays attached
|
|
188
190
|
// This will give time for any native logic to run
|
|
189
191
|
timer = setTimeout(() => {
|
|
190
|
-
setLastUpdate(
|
|
191
|
-
update.animating ? { ...update, animating: false } : update
|
|
192
|
-
);
|
|
192
|
+
setLastUpdate({ current: focusedRouteKey, animating: false });
|
|
193
193
|
}, 32);
|
|
194
194
|
}
|
|
195
195
|
});
|
|
@@ -285,12 +285,7 @@ export function BottomTabView(props: Props) {
|
|
|
285
285
|
>
|
|
286
286
|
{routes.map((route, index) => {
|
|
287
287
|
const descriptor = descriptors[route.key];
|
|
288
|
-
const {
|
|
289
|
-
lazy = true,
|
|
290
|
-
animation = 'none',
|
|
291
|
-
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
292
|
-
.sceneStyleInterpolator,
|
|
293
|
-
} = descriptor.options;
|
|
288
|
+
const { lazy = true } = descriptor.options;
|
|
294
289
|
const isFocused = state.index === index;
|
|
295
290
|
const isPreloaded = state.preloadedRouteKeys.includes(route.key);
|
|
296
291
|
|
|
@@ -304,73 +299,23 @@ export function BottomTabView(props: Props) {
|
|
|
304
299
|
return null;
|
|
305
300
|
}
|
|
306
301
|
|
|
307
|
-
const {
|
|
308
|
-
freezeOnBlur,
|
|
309
|
-
header = ({ layout, options }: BottomTabHeaderProps) => (
|
|
310
|
-
<Header
|
|
311
|
-
{...options}
|
|
312
|
-
layout={layout}
|
|
313
|
-
title={getHeaderTitle(options, route.name)}
|
|
314
|
-
/>
|
|
315
|
-
),
|
|
316
|
-
headerShown,
|
|
317
|
-
headerStatusBarHeight,
|
|
318
|
-
headerTransparent,
|
|
319
|
-
sceneStyle: customSceneStyle,
|
|
320
|
-
} = descriptor.options;
|
|
321
|
-
|
|
322
|
-
const { sceneStyle } =
|
|
323
|
-
sceneStyleInterpolator?.({
|
|
324
|
-
current: {
|
|
325
|
-
progress: tabAnims[route.key],
|
|
326
|
-
},
|
|
327
|
-
}) ?? {};
|
|
328
|
-
|
|
329
|
-
const animationEnabled = hasAnimation(descriptor.options);
|
|
330
302
|
const isAnimatingRoute =
|
|
331
303
|
lastUpdate.animating &&
|
|
332
304
|
(lastUpdate.previous === route.key ||
|
|
333
305
|
lastUpdate.current === route.key);
|
|
334
306
|
|
|
335
|
-
const activityState = isFocused
|
|
336
|
-
? STATE_ON_TOP // the screen is on top after the transition
|
|
337
|
-
: animationEnabled && isAnimatingRoute
|
|
338
|
-
? STATE_TRANSITIONING_OR_BELOW_TOP // screen visible during transition
|
|
339
|
-
: STATE_INACTIVE;
|
|
340
|
-
|
|
341
307
|
return (
|
|
342
|
-
<
|
|
308
|
+
<TabScene
|
|
343
309
|
key={route.key}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
>
|
|
354
|
-
<Screen
|
|
355
|
-
focused={isFocused}
|
|
356
|
-
route={descriptor.route}
|
|
357
|
-
navigation={descriptor.navigation}
|
|
358
|
-
headerShown={headerShown}
|
|
359
|
-
headerStatusBarHeight={headerStatusBarHeight}
|
|
360
|
-
headerTransparent={headerTransparent}
|
|
361
|
-
header={header({
|
|
362
|
-
layout: dimensions,
|
|
363
|
-
route: descriptor.route,
|
|
364
|
-
navigation:
|
|
365
|
-
descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
|
|
366
|
-
options: descriptor.options,
|
|
367
|
-
})}
|
|
368
|
-
style={[customSceneStyle, animationEnabled && sceneStyle]}
|
|
369
|
-
>
|
|
370
|
-
{descriptor.render()}
|
|
371
|
-
</Screen>
|
|
372
|
-
</BottomTabBarHeightContext.Provider>
|
|
373
|
-
</MaybeScreen>
|
|
310
|
+
route={route}
|
|
311
|
+
descriptor={descriptor}
|
|
312
|
+
isFocused={isFocused}
|
|
313
|
+
isPreloaded={isPreloaded}
|
|
314
|
+
isAnimatingRoute={isAnimatingRoute}
|
|
315
|
+
tabAnimation={tabAnims[route.key]}
|
|
316
|
+
tabBarHeight={tabBarPosition === 'bottom' ? tabBarHeight : 0}
|
|
317
|
+
detachInactiveScreens={detachInactiveScreens}
|
|
318
|
+
/>
|
|
374
319
|
);
|
|
375
320
|
})}
|
|
376
321
|
</MaybeScreenContainer>
|
|
@@ -381,6 +326,93 @@ export function BottomTabView(props: Props) {
|
|
|
381
326
|
);
|
|
382
327
|
}
|
|
383
328
|
|
|
329
|
+
type TabSceneProps = {
|
|
330
|
+
route: NavigationRoute<ParamListBase, string>;
|
|
331
|
+
descriptor: BottomTabDescriptor;
|
|
332
|
+
isFocused: boolean;
|
|
333
|
+
isPreloaded: boolean;
|
|
334
|
+
isAnimatingRoute: boolean;
|
|
335
|
+
tabAnimation: Animated.Value;
|
|
336
|
+
tabBarHeight: number;
|
|
337
|
+
detachInactiveScreens: boolean;
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
const TabScene = React.memo(function TabScene({
|
|
341
|
+
route,
|
|
342
|
+
descriptor,
|
|
343
|
+
isFocused,
|
|
344
|
+
isPreloaded,
|
|
345
|
+
isAnimatingRoute,
|
|
346
|
+
tabAnimation,
|
|
347
|
+
tabBarHeight,
|
|
348
|
+
detachInactiveScreens,
|
|
349
|
+
}: TabSceneProps) {
|
|
350
|
+
const {
|
|
351
|
+
animation = 'none',
|
|
352
|
+
sceneStyleInterpolator = NAMED_TRANSITIONS_PRESETS[animation]
|
|
353
|
+
.sceneStyleInterpolator,
|
|
354
|
+
freezeOnBlur,
|
|
355
|
+
header = ({ layout, options }: BottomTabHeaderProps) => (
|
|
356
|
+
<Header
|
|
357
|
+
{...options}
|
|
358
|
+
layout={layout}
|
|
359
|
+
title={getHeaderTitle(options, route.name)}
|
|
360
|
+
/>
|
|
361
|
+
),
|
|
362
|
+
headerShown,
|
|
363
|
+
headerStatusBarHeight,
|
|
364
|
+
headerTransparent,
|
|
365
|
+
sceneStyle: customSceneStyle,
|
|
366
|
+
} = descriptor.options;
|
|
367
|
+
|
|
368
|
+
const { sceneStyle } =
|
|
369
|
+
sceneStyleInterpolator?.({
|
|
370
|
+
current: {
|
|
371
|
+
progress: tabAnimation,
|
|
372
|
+
},
|
|
373
|
+
}) ?? {};
|
|
374
|
+
|
|
375
|
+
const animationEnabled = hasAnimation(descriptor.options);
|
|
376
|
+
|
|
377
|
+
const activityState = isFocused
|
|
378
|
+
? STATE_ON_TOP // the screen is on top after the transition
|
|
379
|
+
: animationEnabled && isAnimatingRoute
|
|
380
|
+
? STATE_TRANSITIONING_OR_BELOW_TOP // screen visible during transition
|
|
381
|
+
: STATE_INACTIVE;
|
|
382
|
+
|
|
383
|
+
return (
|
|
384
|
+
<MaybeScreen
|
|
385
|
+
style={[StyleSheet.absoluteFill, { zIndex: isFocused ? 0 : -1 }]}
|
|
386
|
+
active={activityState}
|
|
387
|
+
enabled={detachInactiveScreens}
|
|
388
|
+
freezeOnBlur={freezeOnBlur}
|
|
389
|
+
shouldFreeze={activityState === STATE_INACTIVE && !isPreloaded}
|
|
390
|
+
pointerEvents={isFocused ? 'box-none' : 'none'}
|
|
391
|
+
>
|
|
392
|
+
<BottomTabBarHeightContext.Provider value={tabBarHeight}>
|
|
393
|
+
<Screen
|
|
394
|
+
focused={isFocused}
|
|
395
|
+
route={descriptor.route}
|
|
396
|
+
navigation={descriptor.navigation}
|
|
397
|
+
headerShown={headerShown}
|
|
398
|
+
headerStatusBarHeight={headerStatusBarHeight}
|
|
399
|
+
headerTransparent={headerTransparent}
|
|
400
|
+
header={header({
|
|
401
|
+
layout: SafeAreaProviderCompat.initialMetrics.frame,
|
|
402
|
+
route: descriptor.route,
|
|
403
|
+
navigation:
|
|
404
|
+
descriptor.navigation as BottomTabNavigationProp<ParamListBase>,
|
|
405
|
+
options: descriptor.options,
|
|
406
|
+
})}
|
|
407
|
+
style={[customSceneStyle, animationEnabled && sceneStyle]}
|
|
408
|
+
>
|
|
409
|
+
{descriptor.render()}
|
|
410
|
+
</Screen>
|
|
411
|
+
</BottomTabBarHeightContext.Provider>
|
|
412
|
+
</MaybeScreen>
|
|
413
|
+
);
|
|
414
|
+
});
|
|
415
|
+
|
|
384
416
|
const styles = StyleSheet.create({
|
|
385
417
|
screens: {
|
|
386
418
|
flex: 1,
|
package/src/views/TabBarIcon.tsx
CHANGED
|
@@ -13,6 +13,7 @@ type Props = {
|
|
|
13
13
|
route: Route<string>;
|
|
14
14
|
variant: 'uikit' | 'material';
|
|
15
15
|
size: 'compact' | 'regular';
|
|
16
|
+
iconSize?: number;
|
|
16
17
|
badge?: string | number;
|
|
17
18
|
badgeStyle?: StyleProp<TextStyle>;
|
|
18
19
|
activeOpacity: number;
|
|
@@ -44,6 +45,7 @@ export function TabBarIcon({
|
|
|
44
45
|
route: _,
|
|
45
46
|
variant,
|
|
46
47
|
size,
|
|
48
|
+
iconSize: iconSizeOverride,
|
|
47
49
|
badge,
|
|
48
50
|
badgeStyle,
|
|
49
51
|
activeOpacity,
|
|
@@ -55,11 +57,12 @@ export function TabBarIcon({
|
|
|
55
57
|
style,
|
|
56
58
|
}: Props) {
|
|
57
59
|
const iconSize =
|
|
58
|
-
|
|
60
|
+
iconSizeOverride ??
|
|
61
|
+
(variant === 'material'
|
|
59
62
|
? ICON_SIZE_MATERIAL
|
|
60
63
|
: size === 'compact'
|
|
61
64
|
? ICON_SIZE_ROUND_COMPACT
|
|
62
|
-
: ICON_SIZE_ROUND;
|
|
65
|
+
: ICON_SIZE_ROUND);
|
|
63
66
|
|
|
64
67
|
// We render the icon twice at the same position on top of each other:
|
|
65
68
|
// active and inactive one, so we can fade between them.
|
|
@@ -71,6 +74,10 @@ export function TabBarIcon({
|
|
|
71
74
|
: size === 'compact'
|
|
72
75
|
? styles.wrapperUikitCompact
|
|
73
76
|
: styles.wrapperUikit,
|
|
77
|
+
iconSizeOverride != null && {
|
|
78
|
+
width: iconSizeOverride,
|
|
79
|
+
height: iconSizeOverride,
|
|
80
|
+
},
|
|
74
81
|
style,
|
|
75
82
|
]}
|
|
76
83
|
>
|