react-navigation-reanimated-top-tabs 0.6.0 → 0.6.2
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/components/ReanimatedTopTab/AnimationHelper.js +2 -2
- package/lib/commonjs/components/ReanimatedTopTab/AnimationHelper.js.map +1 -1
- package/lib/commonjs/components/ReanimatedTopTab/ReanimatedTabView.js +26 -16
- package/lib/commonjs/components/ReanimatedTopTab/ReanimatedTabView.js.map +1 -1
- package/lib/commonjs/components/elements/TabBarBaseComponent.js +4 -4
- package/lib/commonjs/components/elements/TabBarBaseComponent.js.map +1 -1
- package/lib/commonjs/components/wrappers/GestureWrapper.js +5 -6
- package/lib/commonjs/components/wrappers/GestureWrapper.js.map +1 -1
- package/lib/commonjs/context/Context.hooks.js +30 -1
- package/lib/commonjs/context/Context.hooks.js.map +1 -1
- package/lib/commonjs/context/Context.js +6 -0
- package/lib/commonjs/context/Context.js.map +1 -1
- package/lib/commonjs/navigator/createReanimatedTopTabNavigator.js +6 -31
- package/lib/commonjs/navigator/createReanimatedTopTabNavigator.js.map +1 -1
- package/lib/module/components/ReanimatedTopTab/AnimationHelper.js +2 -2
- package/lib/module/components/ReanimatedTopTab/AnimationHelper.js.map +1 -1
- package/lib/module/components/ReanimatedTopTab/ReanimatedTabView.js +27 -17
- package/lib/module/components/ReanimatedTopTab/ReanimatedTabView.js.map +1 -1
- package/lib/module/components/elements/TabBarBaseComponent.js +4 -4
- package/lib/module/components/elements/TabBarBaseComponent.js.map +1 -1
- package/lib/module/components/wrappers/GestureWrapper.js +5 -6
- package/lib/module/components/wrappers/GestureWrapper.js.map +1 -1
- package/lib/module/context/Context.hooks.js +30 -1
- package/lib/module/context/Context.hooks.js.map +1 -1
- package/lib/module/context/Context.js +6 -0
- package/lib/module/context/Context.js.map +1 -1
- package/lib/module/navigator/createReanimatedTopTabNavigator.js +7 -32
- package/lib/module/navigator/createReanimatedTopTabNavigator.js.map +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/AnimationHelper.d.ts +7 -3
- package/lib/typescript/src/components/ReanimatedTopTab/AnimationHelper.d.ts.map +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/ReanimatedTabView.d.ts +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/ReanimatedTabView.d.ts.map +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/types.d.ts +1 -3
- package/lib/typescript/src/components/ReanimatedTopTab/types.d.ts.map +1 -1
- package/lib/typescript/src/components/elements/TabBarBaseComponent.d.ts +4 -18
- package/lib/typescript/src/components/elements/TabBarBaseComponent.d.ts.map +1 -1
- package/lib/typescript/src/components/wrappers/GestureWrapper.d.ts.map +1 -1
- package/lib/typescript/src/context/Context.d.ts.map +1 -1
- package/lib/typescript/src/context/Context.hooks.d.ts +1 -0
- package/lib/typescript/src/context/Context.hooks.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/navigator/createReanimatedTopTabNavigator.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ReanimatedTopTab/AnimationHelper.ts +7 -6
- package/src/components/ReanimatedTopTab/ReanimatedTabView.tsx +33 -25
- package/src/components/ReanimatedTopTab/types.ts +1 -4
- package/src/components/elements/TabBarBaseComponent.tsx +5 -20
- package/src/components/wrappers/GestureWrapper.tsx +5 -8
- package/src/context/Context.hooks.ts +34 -0
- package/src/context/Context.tsx +7 -0
- package/src/navigator/createReanimatedTopTabNavigator.tsx +11 -51
|
@@ -10,6 +10,7 @@ import Reanimated, {
|
|
|
10
10
|
interpolate,
|
|
11
11
|
LinearTransition,
|
|
12
12
|
runOnJS,
|
|
13
|
+
runOnUI,
|
|
13
14
|
useAnimatedStyle,
|
|
14
15
|
useDerivedValue,
|
|
15
16
|
useSharedValue,
|
|
@@ -21,13 +22,13 @@ export interface ReanimatedTabViewProps {
|
|
|
21
22
|
LazyPlaceholder?: () => React.ReactNode;
|
|
22
23
|
lazy?: boolean;
|
|
23
24
|
navigationState: ReanimatedTabViewTypes.NavigationState;
|
|
24
|
-
onIndexChange: (index: number) => void;
|
|
25
25
|
percentageTrigger?: number;
|
|
26
26
|
positionInterpolation?: ReanimatedTabViewTypes.PositionInterpolation;
|
|
27
27
|
renderScene: (params: ReanimatedTabViewTypes.SceneProps) => React.ReactNode;
|
|
28
28
|
renderTabBar?: (
|
|
29
29
|
params: ReanimatedTabViewTypes.RenderTabsParams
|
|
30
30
|
) => React.ReactNode;
|
|
31
|
+
navigate(index: number): void;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
@@ -35,17 +36,16 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
35
36
|
LazyPlaceholder = () => null,
|
|
36
37
|
lazy = false,
|
|
37
38
|
navigationState,
|
|
38
|
-
onIndexChange,
|
|
39
39
|
percentageTrigger = 0.4,
|
|
40
40
|
positionInterpolation,
|
|
41
41
|
renderScene,
|
|
42
42
|
renderTabBar,
|
|
43
|
+
navigate,
|
|
43
44
|
}) => {
|
|
44
45
|
const { width } = useWindowDimensions();
|
|
45
46
|
const loadedScreens = useRef([
|
|
46
47
|
navigationState.routes[navigationState.index],
|
|
47
48
|
]);
|
|
48
|
-
// const width = 160;
|
|
49
49
|
const scrollPosition = useSharedValue(navigationState.index);
|
|
50
50
|
|
|
51
51
|
const position = useDerivedValue(() => {
|
|
@@ -60,22 +60,29 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
60
60
|
);
|
|
61
61
|
}, [positionInterpolation]);
|
|
62
62
|
|
|
63
|
-
|
|
64
|
-
scrollPosition.value = AnimationHelper.animation(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}, [navigationState.index, scrollPosition, width]);
|
|
63
|
+
const _navigate = (index: number) => {
|
|
64
|
+
scrollPosition.value = AnimationHelper.animation(-index * width);
|
|
65
|
+
navigate(index);
|
|
66
|
+
};
|
|
68
67
|
|
|
69
68
|
const minimumValueToChangeView = useMemo(
|
|
70
69
|
() => width * percentageTrigger,
|
|
71
70
|
[percentageTrigger, width]
|
|
72
71
|
);
|
|
73
72
|
|
|
73
|
+
const _navigationState = useSharedValue({ size: 0, index: 0 });
|
|
74
|
+
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
runOnUI(() => {
|
|
77
|
+
_navigationState.value.size = navigationState.routes.length;
|
|
78
|
+
_navigationState.value.index = navigationState.index;
|
|
79
|
+
})();
|
|
80
|
+
}, [navigationState]);
|
|
81
|
+
|
|
74
82
|
const panGesture = React.useMemo(
|
|
75
83
|
() =>
|
|
76
84
|
Gesture.Pan()
|
|
77
|
-
.failOffsetY(-10)
|
|
78
|
-
.failOffsetY(10)
|
|
85
|
+
.failOffsetY([-10, 10])
|
|
79
86
|
.activeOffsetX([-20, 20])
|
|
80
87
|
.onChange((event) => {
|
|
81
88
|
'worklet';
|
|
@@ -83,7 +90,7 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
83
90
|
event,
|
|
84
91
|
scrollPosition.value,
|
|
85
92
|
width,
|
|
86
|
-
|
|
93
|
+
_navigationState.value
|
|
87
94
|
);
|
|
88
95
|
})
|
|
89
96
|
.onEnd((event) => {
|
|
@@ -92,18 +99,13 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
92
99
|
event,
|
|
93
100
|
minimumValueToChangeView,
|
|
94
101
|
width,
|
|
95
|
-
|
|
102
|
+
_navigationState.value
|
|
96
103
|
);
|
|
97
104
|
scrollPosition.value = AnimationHelper.animation(state.value);
|
|
98
|
-
|
|
105
|
+
_navigationState.value.index = state.index;
|
|
106
|
+
runOnJS(navigate)(state.index);
|
|
99
107
|
}),
|
|
100
|
-
[
|
|
101
|
-
minimumValueToChangeView,
|
|
102
|
-
navigationState,
|
|
103
|
-
onIndexChange,
|
|
104
|
-
scrollPosition,
|
|
105
|
-
width,
|
|
106
|
-
]
|
|
108
|
+
[minimumValueToChangeView, width, _navigationState]
|
|
107
109
|
);
|
|
108
110
|
|
|
109
111
|
const scrollPositionStyle = useAnimatedStyle(() => ({
|
|
@@ -121,15 +123,15 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
121
123
|
|
|
122
124
|
const jumpTo = React.useCallback(
|
|
123
125
|
(key: string) => {
|
|
124
|
-
const newIndex = navigationState.routes.findIndex(
|
|
126
|
+
const newIndex = navigationState.routes.findIndex(
|
|
127
|
+
(route) => route.key === key
|
|
128
|
+
);
|
|
125
129
|
if (newIndex === -1) {
|
|
126
|
-
// throw
|
|
127
130
|
return;
|
|
128
131
|
}
|
|
129
132
|
scrollPosition.value = AnimationHelper.animation(-newIndex * width);
|
|
130
|
-
onIndexChange(newIndex);
|
|
131
133
|
},
|
|
132
|
-
[navigationState.routes,
|
|
134
|
+
[navigationState.routes, scrollPosition, width]
|
|
133
135
|
);
|
|
134
136
|
|
|
135
137
|
const chooseRender = useCallback(
|
|
@@ -171,7 +173,13 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
171
173
|
return (
|
|
172
174
|
<GestureHandlerRootView style={defaultStyles.flex}>
|
|
173
175
|
<Reanimated.View layout={LinearTransition} style={defaultStyles.flex}>
|
|
174
|
-
{renderTabBar
|
|
176
|
+
{renderTabBar
|
|
177
|
+
? renderTabBar({
|
|
178
|
+
navigationState,
|
|
179
|
+
position,
|
|
180
|
+
navigate: _navigate,
|
|
181
|
+
})
|
|
182
|
+
: null}
|
|
175
183
|
<GestureDetector gesture={panGesture}>
|
|
176
184
|
<Reanimated.View
|
|
177
185
|
layout={LinearTransition}
|
|
@@ -14,6 +14,7 @@ export namespace ReanimatedTabViewTypes {
|
|
|
14
14
|
export interface RenderTabsParams {
|
|
15
15
|
navigationState: NavigationState;
|
|
16
16
|
position: SharedValue<number>;
|
|
17
|
+
navigate(index: number): void;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export interface SceneProps {
|
|
@@ -21,9 +22,5 @@ export namespace ReanimatedTabViewTypes {
|
|
|
21
22
|
route: Route;
|
|
22
23
|
}
|
|
23
24
|
|
|
24
|
-
export interface SceneMapRoutes {
|
|
25
|
-
[key: string]: React.ReactNode;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
25
|
export type PositionInterpolation = { input: number[]; output: number[] };
|
|
29
26
|
}
|
|
@@ -3,38 +3,23 @@ import { Pressable, StyleSheet, Text, useWindowDimensions } from 'react-native';
|
|
|
3
3
|
import Reanimated, {
|
|
4
4
|
type AnimatedStyle,
|
|
5
5
|
interpolate,
|
|
6
|
-
type SharedValue,
|
|
7
6
|
useAnimatedReaction,
|
|
8
7
|
} from 'react-native-reanimated';
|
|
9
8
|
|
|
10
9
|
import { useTabContext } from '../../hooks/useTabContext';
|
|
10
|
+
import { type ReanimatedTabViewTypes } from '../ReanimatedTopTab/types';
|
|
11
11
|
|
|
12
|
-
export interface
|
|
13
|
-
key: string;
|
|
14
|
-
name?: string;
|
|
15
|
-
tabBarLabel?: () => React.ReactNode;
|
|
16
|
-
title?: string;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export type NavigationState = { index: number; routes: Route[] };
|
|
20
|
-
|
|
21
|
-
export interface RenderTabsParams {
|
|
22
|
-
navigationState: NavigationState;
|
|
23
|
-
position: SharedValue<number>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface TabBarProps extends RenderTabsParams {
|
|
12
|
+
export interface TabBarProps extends ReanimatedTabViewTypes.RenderTabsParams {
|
|
27
13
|
children?: React.ReactNode;
|
|
28
|
-
navigate: (route: Route) => void;
|
|
29
14
|
style?: AnimatedStyle;
|
|
30
15
|
}
|
|
31
16
|
|
|
32
17
|
export const TabBarBaseComponent = ({
|
|
33
18
|
children,
|
|
34
|
-
navigate,
|
|
35
19
|
navigationState,
|
|
36
20
|
position,
|
|
37
21
|
style,
|
|
22
|
+
navigate,
|
|
38
23
|
}: TabBarProps) => {
|
|
39
24
|
const layout = useWindowDimensions();
|
|
40
25
|
|
|
@@ -62,12 +47,12 @@ export const TabBarBaseComponent = ({
|
|
|
62
47
|
}}
|
|
63
48
|
>
|
|
64
49
|
<Reanimated.View style={[styles.wrapper, style]}>
|
|
65
|
-
{navigationState.routes.map((route) => (
|
|
50
|
+
{navigationState.routes.map((route, index) => (
|
|
66
51
|
<Pressable
|
|
67
52
|
style={styles.labelWrapper}
|
|
68
53
|
key={route.key}
|
|
69
54
|
onPress={() => {
|
|
70
|
-
navigate(
|
|
55
|
+
navigate(index);
|
|
71
56
|
}}
|
|
72
57
|
>
|
|
73
58
|
{route.tabBarLabel ? (
|
|
@@ -47,8 +47,9 @@ export const GestureWrapper = ({ children }: GestureWrapperProps) => {
|
|
|
47
47
|
|
|
48
48
|
useAnimatedReaction(
|
|
49
49
|
() => gestureEnabled.value,
|
|
50
|
-
(
|
|
51
|
-
|
|
50
|
+
(next, prev) => {
|
|
51
|
+
if (next === prev) return;
|
|
52
|
+
runOnJS(setGestureEnabled)(next);
|
|
52
53
|
},
|
|
53
54
|
[]
|
|
54
55
|
);
|
|
@@ -57,13 +58,9 @@ export const GestureWrapper = ({ children }: GestureWrapperProps) => {
|
|
|
57
58
|
() =>
|
|
58
59
|
Gesture.Pan()
|
|
59
60
|
.activeOffsetY([-10, 10])
|
|
60
|
-
.onTouchesMove((
|
|
61
|
+
.onTouchesMove((_, stateManager) => {
|
|
61
62
|
'worklet';
|
|
62
|
-
if (
|
|
63
|
-
currentScreenScrollOffset.value > 0 &&
|
|
64
|
-
state !== 4 &&
|
|
65
|
-
state !== 3
|
|
66
|
-
) {
|
|
63
|
+
if (currentScreenScrollOffset.value > 0 || !gestureEnabled.value) {
|
|
67
64
|
stateManager.fail();
|
|
68
65
|
}
|
|
69
66
|
})
|
|
@@ -276,8 +276,42 @@ const useTransformHeaderOnTabChange = ({
|
|
|
276
276
|
};
|
|
277
277
|
};
|
|
278
278
|
|
|
279
|
+
const useApproachingTabChange = ({
|
|
280
|
+
config,
|
|
281
|
+
transformationX,
|
|
282
|
+
gestureEnabled,
|
|
283
|
+
currentScreenIndex,
|
|
284
|
+
}: Pick<
|
|
285
|
+
ReanimatedTopTabNavigation.ContextType,
|
|
286
|
+
'config' | 'transformationX' | 'currentScreenIndex' | 'gestureEnabled'
|
|
287
|
+
>) => {
|
|
288
|
+
const onIndexApproach = (index: number) => {
|
|
289
|
+
'worklet';
|
|
290
|
+
currentScreenIndex.value = index;
|
|
291
|
+
switch (config[index]) {
|
|
292
|
+
case 'normal': {
|
|
293
|
+
gestureEnabled.value = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
case 'minimalized': {
|
|
297
|
+
gestureEnabled.value = false;
|
|
298
|
+
break;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
useAnimatedReaction(
|
|
304
|
+
() => transformationX.value,
|
|
305
|
+
(current, previous) => {
|
|
306
|
+
const approachingIndex = ContextHelpers.getTargetIndex(current, previous);
|
|
307
|
+
onIndexApproach(approachingIndex);
|
|
308
|
+
}
|
|
309
|
+
);
|
|
310
|
+
};
|
|
311
|
+
|
|
279
312
|
export const ContextHooks = {
|
|
280
313
|
useResetApproachingScreenScrollOffset,
|
|
281
314
|
usePrepareContext,
|
|
282
315
|
useTransformHeaderOnTabChange,
|
|
316
|
+
useApproachingTabChange,
|
|
283
317
|
};
|
package/src/context/Context.tsx
CHANGED
|
@@ -8,15 +8,12 @@ import {
|
|
|
8
8
|
} from '@react-navigation/native';
|
|
9
9
|
import type { DefaultRouterOptions } from '@react-navigation/routers';
|
|
10
10
|
import { GestureWrapper } from '../components/wrappers/GestureWrapper';
|
|
11
|
-
import {
|
|
12
|
-
type RenderTabsParams,
|
|
13
|
-
TabBarBaseComponent,
|
|
14
|
-
} from '../components/elements/TabBarBaseComponent';
|
|
11
|
+
import { TabBarBaseComponent } from '../components/elements/TabBarBaseComponent';
|
|
15
12
|
import { Provider } from '../context/Context';
|
|
16
13
|
import { type ReanimatedTopTabNavigation } from '../types';
|
|
17
14
|
import { omit } from 'lodash';
|
|
18
|
-
import {
|
|
19
|
-
import Reanimated
|
|
15
|
+
import { useMemo } from 'react';
|
|
16
|
+
import Reanimated from 'react-native-reanimated';
|
|
20
17
|
import type { ReanimatedTabViewTypes } from '../components/ReanimatedTopTab/types';
|
|
21
18
|
import { ReanimatedTabView } from '../components/ReanimatedTopTab/ReanimatedTabView';
|
|
22
19
|
import { ContextHooks } from '../context/Context.hooks';
|
|
@@ -51,26 +48,11 @@ const TabViewNavigator = ({
|
|
|
51
48
|
|
|
52
49
|
const context = ContextHooks.usePrepareContext({ state });
|
|
53
50
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
) =>
|
|
60
|
-
(index: number) => {
|
|
61
|
-
navigation.navigate(state.routes[index]?.name ?? '');
|
|
62
|
-
currentScreenIndex.value = index;
|
|
63
|
-
switch (_config[index]) {
|
|
64
|
-
case 'normal': {
|
|
65
|
-
gestureEnabled.value = true;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
case 'minimalized': {
|
|
69
|
-
gestureEnabled.value = false;
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
};
|
|
51
|
+
const navigate = (index: number) => {
|
|
52
|
+
const route = state.routes[index];
|
|
53
|
+
if (!route) return;
|
|
54
|
+
navigation.dispatch(TabActions.jumpTo(route.name));
|
|
55
|
+
};
|
|
74
56
|
|
|
75
57
|
const navigationState = useMemo(
|
|
76
58
|
() => ({
|
|
@@ -100,31 +82,12 @@ const TabViewNavigator = ({
|
|
|
100
82
|
[state]
|
|
101
83
|
);
|
|
102
84
|
|
|
103
|
-
const navigate = (route: any) => {
|
|
104
|
-
navigation.dispatch({
|
|
105
|
-
...TabActions.jumpTo(route.name, route.params),
|
|
106
|
-
target: state.key,
|
|
107
|
-
});
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
const renderTabBar = useCallback(
|
|
111
|
-
(props: RenderTabsParams) => (
|
|
112
|
-
<TabBarComponent {...props} navigate={navigate} />
|
|
113
|
-
),
|
|
114
|
-
[]
|
|
115
|
-
);
|
|
116
|
-
|
|
117
85
|
const renderScene = ({ route }: ReanimatedTabViewTypes.SceneProps) =>
|
|
118
86
|
descriptors[route.key]?.render();
|
|
119
87
|
|
|
120
88
|
return (
|
|
121
89
|
<Provider config={_config} context={context}>
|
|
122
|
-
{({
|
|
123
|
-
currentScreenIndex,
|
|
124
|
-
gestureEnabled,
|
|
125
|
-
headerHeight,
|
|
126
|
-
transformationY,
|
|
127
|
-
}) => (
|
|
90
|
+
{({ headerHeight, transformationY }) => (
|
|
128
91
|
<>
|
|
129
92
|
<Reanimated.View
|
|
130
93
|
onLayout={({ nativeEvent }) => {
|
|
@@ -142,12 +105,9 @@ const TabViewNavigator = ({
|
|
|
142
105
|
<GestureWrapper>
|
|
143
106
|
<ReanimatedTabView
|
|
144
107
|
navigationState={navigationState}
|
|
145
|
-
|
|
146
|
-
currentScreenIndex,
|
|
147
|
-
gestureEnabled
|
|
148
|
-
)}
|
|
108
|
+
navigate={navigate}
|
|
149
109
|
renderScene={renderScene}
|
|
150
|
-
renderTabBar={
|
|
110
|
+
renderTabBar={TabBarComponent}
|
|
151
111
|
/>
|
|
152
112
|
</GestureWrapper>
|
|
153
113
|
</NavigationContent>
|