react-navigation-reanimated-top-tabs 0.7.0 → 0.9.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/commonjs/components/ReanimatedTopTab/AnimationHelper.js +21 -26
- package/lib/commonjs/components/ReanimatedTopTab/AnimationHelper.js.map +1 -1
- package/lib/commonjs/components/ReanimatedTopTab/ReanimatedTabView.js +13 -12
- package/lib/commonjs/components/ReanimatedTopTab/ReanimatedTabView.js.map +1 -1
- package/lib/commonjs/components/elements/TabBarBaseComponent.js +42 -25
- package/lib/commonjs/components/elements/TabBarBaseComponent.js.map +1 -1
- package/lib/commonjs/components/elements/TabBarBaseItem.js +55 -0
- package/lib/commonjs/components/elements/TabBarBaseItem.js.map +1 -0
- package/lib/commonjs/components/elements/TabBarLabelBaseComponent.js +3 -3
- package/lib/commonjs/components/elements/TabBarLabelBaseComponent.js.map +1 -1
- package/lib/commonjs/components/wrappers/GestureWrapper.js +40 -11
- package/lib/commonjs/components/wrappers/GestureWrapper.js.map +1 -1
- package/lib/commonjs/components/wrappers/SectionList.js +72 -0
- package/lib/commonjs/components/wrappers/SectionList.js.map +1 -0
- package/lib/commonjs/context/Context.hooks.js +22 -17
- package/lib/commonjs/context/Context.hooks.js.map +1 -1
- package/lib/commonjs/context/Context.js +11 -8
- package/lib/commonjs/context/Context.js.map +1 -1
- package/lib/commonjs/hooks/useHeader.js +2 -10
- package/lib/commonjs/hooks/useHeader.js.map +1 -1
- package/lib/commonjs/hooks/useTabOffset.js +2 -2
- package/lib/commonjs/hooks/useTabOffset.js.map +1 -1
- package/lib/commonjs/index.js +2 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/navigator/createReanimatedTopTabNavigator.js +27 -23
- package/lib/commonjs/navigator/createReanimatedTopTabNavigator.js.map +1 -1
- package/lib/commonjs/types.js.map +1 -1
- package/lib/module/components/ReanimatedTopTab/AnimationHelper.js +23 -28
- package/lib/module/components/ReanimatedTopTab/AnimationHelper.js.map +1 -1
- package/lib/module/components/ReanimatedTopTab/ReanimatedTabView.js +14 -13
- package/lib/module/components/ReanimatedTopTab/ReanimatedTabView.js.map +1 -1
- package/lib/module/components/elements/TabBarBaseComponent.js +44 -27
- package/lib/module/components/elements/TabBarBaseComponent.js.map +1 -1
- package/lib/module/components/elements/TabBarBaseItem.js +48 -0
- package/lib/module/components/elements/TabBarBaseItem.js.map +1 -0
- package/lib/module/components/elements/TabBarLabelBaseComponent.js +3 -3
- package/lib/module/components/elements/TabBarLabelBaseComponent.js.map +1 -1
- package/lib/module/components/wrappers/GestureWrapper.js +41 -12
- package/lib/module/components/wrappers/GestureWrapper.js.map +1 -1
- package/lib/module/components/wrappers/SectionList.js +66 -0
- package/lib/module/components/wrappers/SectionList.js.map +1 -0
- package/lib/module/context/Context.hooks.js +22 -17
- package/lib/module/context/Context.hooks.js.map +1 -1
- package/lib/module/context/Context.js +11 -8
- package/lib/module/context/Context.js.map +1 -1
- package/lib/module/hooks/useHeader.js +3 -11
- package/lib/module/hooks/useHeader.js.map +1 -1
- package/lib/module/hooks/useTabOffset.js +2 -2
- package/lib/module/hooks/useTabOffset.js.map +1 -1
- package/lib/module/index.js +2 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/navigator/createReanimatedTopTabNavigator.js +28 -24
- package/lib/module/navigator/createReanimatedTopTabNavigator.js.map +1 -1
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/AnimationHelper.d.ts.map +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/ReanimatedTabView.d.ts +2 -1
- package/lib/typescript/src/components/ReanimatedTopTab/ReanimatedTabView.d.ts.map +1 -1
- package/lib/typescript/src/components/ReanimatedTopTab/types.d.ts +3 -7
- package/lib/typescript/src/components/ReanimatedTopTab/types.d.ts.map +1 -1
- package/lib/typescript/src/components/elements/TabBarBaseComponent.d.ts +1 -1
- package/lib/typescript/src/components/elements/TabBarBaseComponent.d.ts.map +1 -1
- package/lib/typescript/src/components/elements/TabBarBaseItem.d.ts +12 -0
- package/lib/typescript/src/components/elements/TabBarBaseItem.d.ts.map +1 -0
- package/lib/typescript/src/components/wrappers/GestureWrapper.d.ts +2 -1
- package/lib/typescript/src/components/wrappers/GestureWrapper.d.ts.map +1 -1
- package/lib/typescript/src/components/wrappers/SectionList.d.ts +3 -0
- package/lib/typescript/src/components/wrappers/SectionList.d.ts.map +1 -0
- package/lib/typescript/src/context/Context.d.ts.map +1 -1
- package/lib/typescript/src/context/Context.hooks.d.ts +4 -3
- package/lib/typescript/src/context/Context.hooks.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useHeader.d.ts +0 -6
- package/lib/typescript/src/hooks/useHeader.d.ts.map +1 -1
- package/lib/typescript/src/hooks/useScreenProperties.d.ts +1 -1
- package/lib/typescript/src/index.d.ts +3 -8
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/navigator/createReanimatedTopTabNavigator.d.ts +1 -1
- package/lib/typescript/src/navigator/createReanimatedTopTabNavigator.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +11 -5
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/ReanimatedTopTab/AnimationHelper.ts +24 -32
- package/src/components/ReanimatedTopTab/ReanimatedTabView.tsx +19 -17
- package/src/components/ReanimatedTopTab/types.ts +3 -5
- package/src/components/elements/TabBarBaseComponent.tsx +47 -36
- package/src/components/elements/TabBarBaseItem.tsx +76 -0
- package/src/components/elements/TabBarLabelBaseComponent.tsx +3 -3
- package/src/components/wrappers/GestureWrapper.tsx +41 -14
- package/src/components/wrappers/SectionList.tsx +75 -0
- package/src/context/Context.hooks.ts +26 -16
- package/src/context/Context.tsx +8 -5
- package/src/hooks/useHeader.ts +4 -22
- package/src/hooks/useTabOffset.ts +2 -2
- package/src/index.tsx +2 -0
- package/src/navigator/createReanimatedTopTabNavigator.tsx +31 -26
- package/src/types.ts +21 -5
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TabBarBaseComponent } from './components/elements/TabBarBaseComponent';
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import { type ReactNode } from 'react';
|
|
4
|
-
import { type LayoutRectangle } from 'react-native';
|
|
4
|
+
import { SectionList, type LayoutRectangle, type StyleProp, type ViewStyle } from 'react-native';
|
|
5
5
|
import { type NativeGesture } from 'react-native-gesture-handler';
|
|
6
6
|
import Reanimated, { type SharedValue } from 'react-native-reanimated';
|
|
7
7
|
import { type StackNavigationOptions } from '@react-navigation/stack';
|
|
@@ -17,8 +17,9 @@ export declare namespace ReanimatedTopTabNavigation {
|
|
|
17
17
|
navHeight: SharedValue<number>;
|
|
18
18
|
topTabHeight: SharedValue<number>;
|
|
19
19
|
topTabNativeGesture: NativeGesture;
|
|
20
|
-
|
|
20
|
+
positionX: SharedValue<number>;
|
|
21
21
|
transformationY: SharedValue<number>;
|
|
22
|
+
scrollY: SharedValue<number>;
|
|
22
23
|
context: {
|
|
23
24
|
screen: {
|
|
24
25
|
properties: Record<string, {
|
|
@@ -27,12 +28,12 @@ export declare namespace ReanimatedTopTabNavigation {
|
|
|
27
28
|
outerLayout: SharedValue<LayoutRectangle>;
|
|
28
29
|
scrollY: SharedValue<number>;
|
|
29
30
|
scrollRef: {
|
|
30
|
-
current: Reanimated.ScrollView | null;
|
|
31
|
+
current: Reanimated.ScrollView | SectionList | null;
|
|
31
32
|
};
|
|
32
33
|
}>;
|
|
33
|
-
setRef(key: string, ref: Reanimated.ScrollView | null): void;
|
|
34
|
+
setRef(key: string, ref: Reanimated.ScrollView | SectionList | null): void;
|
|
34
35
|
getRef(key: string): {
|
|
35
|
-
current: Reanimated.ScrollView | null;
|
|
36
|
+
current: Reanimated.ScrollView | SectionList | null;
|
|
36
37
|
};
|
|
37
38
|
};
|
|
38
39
|
route: {
|
|
@@ -43,11 +44,13 @@ export declare namespace ReanimatedTopTabNavigation {
|
|
|
43
44
|
}
|
|
44
45
|
interface TabViewNavigatorProps {
|
|
45
46
|
HeaderComponent?: React.ComponentType<Pick<ContextType, 'transformationY' | 'headerHeight'>>;
|
|
47
|
+
TopComponent?: React.ComponentType<Pick<ContextType, 'transformationY'>>;
|
|
46
48
|
TabBarComponent?: typeof TabBarBaseComponent;
|
|
47
49
|
children: ReactNode;
|
|
48
50
|
config?: string[];
|
|
49
51
|
initialRouteName?: string;
|
|
50
52
|
screenOptions?: StackNavigationOptions;
|
|
53
|
+
bounces?: boolean;
|
|
51
54
|
}
|
|
52
55
|
type TopTabContextProps = Pick<ContextType, 'config' | 'context'> & {
|
|
53
56
|
children: (props: ContextType) => React.ReactNode;
|
|
@@ -59,6 +62,9 @@ export declare namespace ReanimatedTopTabNavigation {
|
|
|
59
62
|
title: string;
|
|
60
63
|
}) => React.ReactNode;
|
|
61
64
|
title?: string;
|
|
65
|
+
tabBarIndicatorStyle?: StyleProp<ViewStyle>;
|
|
66
|
+
tabBarItemStyle?: StyleProp<ViewStyle>;
|
|
67
|
+
tabBarStyle?: StyleProp<ViewStyle>;
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2CAA2C,CAAC;AAChF,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AACvC,OAAO,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
PanGestureChangeEventPayload,
|
|
5
5
|
PanGestureHandlerEventPayload,
|
|
6
6
|
} from 'react-native-gesture-handler';
|
|
7
|
-
import { withTiming
|
|
7
|
+
import { withTiming } from 'react-native-reanimated';
|
|
8
8
|
|
|
9
9
|
type NavigationState = { size: number; index: number };
|
|
10
10
|
|
|
@@ -32,14 +32,18 @@ const getIndex = (
|
|
|
32
32
|
type: 'increment' | 'decrement'
|
|
33
33
|
) => {
|
|
34
34
|
'worklet';
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
const max = navigationState.size - 1;
|
|
36
|
+
const current = navigationState.index;
|
|
37
|
+
|
|
38
|
+
if (type === 'increment' && current < max) {
|
|
39
|
+
return current + 1;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (type === 'decrement' && current > 0) {
|
|
43
|
+
return current - 1;
|
|
39
44
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
: navigationState.index - 1;
|
|
45
|
+
|
|
46
|
+
return current;
|
|
43
47
|
};
|
|
44
48
|
|
|
45
49
|
const onEnd = (
|
|
@@ -49,43 +53,31 @@ const onEnd = (
|
|
|
49
53
|
navigationState: NavigationState
|
|
50
54
|
) => {
|
|
51
55
|
'worklet';
|
|
52
|
-
|
|
56
|
+
const { velocityX, translationX } = event;
|
|
57
|
+
|
|
58
|
+
if (velocityX < -200 || translationX < -minimumValueToChangeView) {
|
|
59
|
+
const newIndex = getIndex(navigationState, 'increment');
|
|
53
60
|
return {
|
|
54
|
-
index:
|
|
55
|
-
value: -width *
|
|
61
|
+
index: newIndex,
|
|
62
|
+
value: -width * newIndex,
|
|
56
63
|
};
|
|
57
64
|
}
|
|
58
|
-
if (
|
|
65
|
+
if (velocityX > 200 || translationX > minimumValueToChangeView) {
|
|
66
|
+
const newIndex = getIndex(navigationState, 'decrement');
|
|
59
67
|
return {
|
|
60
|
-
index:
|
|
61
|
-
value: -width *
|
|
68
|
+
index: newIndex,
|
|
69
|
+
value: -width * newIndex,
|
|
62
70
|
};
|
|
63
71
|
}
|
|
64
|
-
if (event.translationX < 0) {
|
|
65
|
-
if (event.translationX < -minimumValueToChangeView) {
|
|
66
|
-
return {
|
|
67
|
-
index: getIndex(navigationState, 'increment'),
|
|
68
|
-
value: -width * (navigationState.index + 1),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
if (event.translationX > 0) {
|
|
73
|
-
if (event.translationX > minimumValueToChangeView) {
|
|
74
|
-
return {
|
|
75
|
-
index: getIndex(navigationState, 'decrement'),
|
|
76
|
-
value: -width * (navigationState.index - 1),
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
72
|
return {
|
|
81
73
|
index: navigationState.index,
|
|
82
74
|
value: -width * navigationState.index,
|
|
83
75
|
};
|
|
84
76
|
};
|
|
85
77
|
|
|
86
|
-
const animation = (newValue: number, duration =
|
|
78
|
+
const animation = (newValue: number, duration = 350) => {
|
|
87
79
|
'worklet';
|
|
88
|
-
return
|
|
80
|
+
return withTiming(newValue, { duration });
|
|
89
81
|
};
|
|
90
82
|
|
|
91
83
|
export const AnimationHelper = {
|
|
@@ -6,15 +6,16 @@ import {
|
|
|
6
6
|
GestureHandlerRootView,
|
|
7
7
|
} from 'react-native-gesture-handler';
|
|
8
8
|
import Reanimated, {
|
|
9
|
-
Extrapolation,
|
|
10
9
|
interpolate,
|
|
11
10
|
LinearTransition,
|
|
12
11
|
runOnJS,
|
|
13
12
|
runOnUI,
|
|
13
|
+
useAnimatedReaction,
|
|
14
14
|
useAnimatedStyle,
|
|
15
|
-
useDerivedValue,
|
|
16
15
|
useSharedValue,
|
|
17
16
|
} from 'react-native-reanimated';
|
|
17
|
+
import type { ReanimatedTopTabNavigation } from 'react-navigation-reanimated-top-tabs';
|
|
18
|
+
import { useTabContext } from '../../hooks/useTabContext';
|
|
18
19
|
import { AnimationHelper } from './AnimationHelper';
|
|
19
20
|
import type { ReanimatedTabViewTypes } from './types';
|
|
20
21
|
|
|
@@ -23,12 +24,12 @@ export interface ReanimatedTabViewProps {
|
|
|
23
24
|
lazy?: boolean;
|
|
24
25
|
navigationState: ReanimatedTabViewTypes.NavigationState;
|
|
25
26
|
percentageTrigger?: number;
|
|
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
31
|
navigate(index: number): void;
|
|
32
|
+
screenOptions?: ReanimatedTopTabNavigation.NavigationOptions;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
@@ -37,28 +38,29 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
37
38
|
lazy = false,
|
|
38
39
|
navigationState,
|
|
39
40
|
percentageTrigger = 0.4,
|
|
40
|
-
positionInterpolation,
|
|
41
41
|
renderScene,
|
|
42
42
|
renderTabBar,
|
|
43
43
|
navigate,
|
|
44
|
+
screenOptions,
|
|
44
45
|
}) => {
|
|
45
46
|
const { width } = useWindowDimensions();
|
|
46
47
|
const loadedScreens = useRef([
|
|
47
48
|
navigationState.routes[navigationState.index],
|
|
48
49
|
]);
|
|
49
50
|
const scrollPosition = useSharedValue(navigationState.index);
|
|
51
|
+
const { positionX } = useTabContext();
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
useAnimatedReaction(
|
|
54
|
+
() => scrollPosition.value,
|
|
55
|
+
(value) => {
|
|
56
|
+
positionX.value = interpolate(
|
|
57
|
+
value,
|
|
58
|
+
navigationState.routes.map((_, i) => i * -width),
|
|
59
|
+
navigationState.routes.map((_, i) => i),
|
|
60
|
+
'clamp'
|
|
61
|
+
);
|
|
54
62
|
}
|
|
55
|
-
|
|
56
|
-
-scrollPosition.value,
|
|
57
|
-
positionInterpolation.input,
|
|
58
|
-
positionInterpolation.output,
|
|
59
|
-
Extrapolation.CLAMP
|
|
60
|
-
);
|
|
61
|
-
}, [positionInterpolation]);
|
|
63
|
+
);
|
|
62
64
|
|
|
63
65
|
const _navigate = (index: number) => {
|
|
64
66
|
scrollPosition.value = AnimationHelper.animation(-index * width);
|
|
@@ -105,7 +107,7 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
105
107
|
_navigationState.value.index = state.index;
|
|
106
108
|
runOnJS(navigate)(state.index);
|
|
107
109
|
}),
|
|
108
|
-
[minimumValueToChangeView, width
|
|
110
|
+
[minimumValueToChangeView, width]
|
|
109
111
|
);
|
|
110
112
|
|
|
111
113
|
const scrollPositionStyle = useAnimatedStyle(() => ({
|
|
@@ -115,7 +117,7 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
115
117
|
scrollPosition.value,
|
|
116
118
|
[-width * (navigationState.routes.length - 1), 0],
|
|
117
119
|
[-width * (navigationState.routes.length - 1), 0],
|
|
118
|
-
|
|
120
|
+
'clamp'
|
|
119
121
|
),
|
|
120
122
|
},
|
|
121
123
|
],
|
|
@@ -176,8 +178,8 @@ export const ReanimatedTabView = React.memo<ReanimatedTabViewProps>(
|
|
|
176
178
|
{renderTabBar
|
|
177
179
|
? renderTabBar({
|
|
178
180
|
navigationState,
|
|
179
|
-
position,
|
|
180
181
|
navigate: _navigate,
|
|
182
|
+
screenOptions,
|
|
181
183
|
})
|
|
182
184
|
: null}
|
|
183
185
|
<GestureDetector gesture={panGesture}>
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ReanimatedTopTabNavigation } from 'react-navigation-reanimated-top-tabs';
|
|
3
3
|
|
|
4
4
|
export namespace ReanimatedTabViewTypes {
|
|
5
5
|
export interface Route {
|
|
6
6
|
key: string;
|
|
7
7
|
name: string;
|
|
8
|
-
tabBarLabel?: () => React.ReactNode;
|
|
8
|
+
tabBarLabel?: (focused: boolean) => React.ReactNode;
|
|
9
9
|
title?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
@@ -13,14 +13,12 @@ export namespace ReanimatedTabViewTypes {
|
|
|
13
13
|
|
|
14
14
|
export interface RenderTabsParams {
|
|
15
15
|
navigationState: NavigationState;
|
|
16
|
-
position: SharedValue<number>;
|
|
17
16
|
navigate(index: number): void;
|
|
17
|
+
screenOptions?: ReanimatedTopTabNavigation.NavigationOptions;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
export interface SceneProps {
|
|
21
21
|
jumpTo?: (key: string) => void;
|
|
22
22
|
route: Route;
|
|
23
23
|
}
|
|
24
|
-
|
|
25
|
-
export type PositionInterpolation = { input: number[]; output: number[] };
|
|
26
24
|
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import { StyleSheet } from 'react-native';
|
|
3
3
|
import Reanimated, {
|
|
4
|
-
type AnimatedStyle,
|
|
5
4
|
interpolate,
|
|
6
|
-
|
|
5
|
+
useAnimatedStyle,
|
|
6
|
+
useSharedValue,
|
|
7
|
+
type AnimatedStyle,
|
|
7
8
|
} from 'react-native-reanimated';
|
|
8
9
|
|
|
9
10
|
import { useTabContext } from '../../hooks/useTabContext';
|
|
10
11
|
import { type ReanimatedTabViewTypes } from '../ReanimatedTopTab/types';
|
|
12
|
+
import { TabBarBaseItem } from './TabBarBaseItem';
|
|
11
13
|
|
|
12
14
|
export interface TabBarProps extends ReanimatedTabViewTypes.RenderTabsParams {
|
|
13
15
|
children?: React.ReactNode;
|
|
@@ -17,28 +19,32 @@ export interface TabBarProps extends ReanimatedTabViewTypes.RenderTabsParams {
|
|
|
17
19
|
export const TabBarBaseComponent = ({
|
|
18
20
|
children,
|
|
19
21
|
navigationState,
|
|
20
|
-
position,
|
|
21
22
|
style,
|
|
22
23
|
navigate,
|
|
24
|
+
screenOptions,
|
|
23
25
|
}: TabBarProps) => {
|
|
24
|
-
const
|
|
26
|
+
const { topTabHeight, positionX } = useTabContext();
|
|
27
|
+
const width = useSharedValue(0);
|
|
25
28
|
|
|
26
|
-
const {
|
|
29
|
+
const { tabBarIndicatorStyle, tabBarItemStyle, tabBarStyle } =
|
|
30
|
+
screenOptions ?? {};
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
32
|
+
const inputRange = navigationState.routes.map((_, index) => index);
|
|
33
|
+
|
|
34
|
+
const indicatorStyle = useAnimatedStyle(() => {
|
|
35
|
+
const outputRange = navigationState.routes.map(
|
|
36
|
+
(_, index) => index * (width.value / navigationState.routes.length)
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
return {
|
|
40
|
+
transform: [
|
|
41
|
+
{
|
|
42
|
+
translateX: interpolate(positionX.value, inputRange, outputRange),
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
width: width.value / navigationState.routes.length,
|
|
46
|
+
};
|
|
47
|
+
});
|
|
42
48
|
|
|
43
49
|
return (
|
|
44
50
|
<Reanimated.View
|
|
@@ -46,21 +52,26 @@ export const TabBarBaseComponent = ({
|
|
|
46
52
|
topTabHeight.value = nativeEvent.layout.height;
|
|
47
53
|
}}
|
|
48
54
|
>
|
|
49
|
-
<Reanimated.View
|
|
55
|
+
<Reanimated.View
|
|
56
|
+
onLayout={({ nativeEvent }) => {
|
|
57
|
+
width.value = nativeEvent.layout.width;
|
|
58
|
+
}}
|
|
59
|
+
style={[styles.wrapper, tabBarStyle, style]}
|
|
60
|
+
>
|
|
61
|
+
<Reanimated.View
|
|
62
|
+
style={[styles.indicator, indicatorStyle, tabBarIndicatorStyle]}
|
|
63
|
+
/>
|
|
50
64
|
{navigationState.routes.map((route, index) => (
|
|
51
|
-
<
|
|
52
|
-
|
|
65
|
+
<TabBarBaseItem
|
|
66
|
+
index={index}
|
|
67
|
+
inputRange={inputRange}
|
|
53
68
|
key={route.key}
|
|
54
69
|
onPress={() => {
|
|
55
70
|
navigate(index);
|
|
56
71
|
}}
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
) : (
|
|
61
|
-
<Text style={styles.label}>{route.title}</Text>
|
|
62
|
-
)}
|
|
63
|
-
</Pressable>
|
|
72
|
+
route={route}
|
|
73
|
+
style={tabBarItemStyle}
|
|
74
|
+
/>
|
|
64
75
|
))}
|
|
65
76
|
</Reanimated.View>
|
|
66
77
|
{children}
|
|
@@ -75,14 +86,14 @@ const styles = StyleSheet.create({
|
|
|
75
86
|
alignItems: 'center',
|
|
76
87
|
justifyContent: 'center',
|
|
77
88
|
},
|
|
78
|
-
label: {
|
|
79
|
-
color: 'red',
|
|
80
|
-
},
|
|
81
89
|
wrapper: {
|
|
82
|
-
width: '100%',
|
|
83
90
|
flexDirection: 'row',
|
|
84
91
|
backgroundColor: 'white',
|
|
85
|
-
|
|
86
|
-
|
|
92
|
+
},
|
|
93
|
+
indicator: {
|
|
94
|
+
backgroundColor: 'black',
|
|
95
|
+
bottom: 0,
|
|
96
|
+
height: 2,
|
|
97
|
+
position: 'absolute',
|
|
87
98
|
},
|
|
88
99
|
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Pressable,
|
|
3
|
+
StyleSheet,
|
|
4
|
+
Text,
|
|
5
|
+
type StyleProp,
|
|
6
|
+
type ViewStyle,
|
|
7
|
+
} from 'react-native';
|
|
8
|
+
import Animated, {
|
|
9
|
+
interpolate,
|
|
10
|
+
useAnimatedStyle,
|
|
11
|
+
} from 'react-native-reanimated';
|
|
12
|
+
import { useTabContext } from '../../hooks/useTabContext';
|
|
13
|
+
import { type ReanimatedTabViewTypes } from '../ReanimatedTopTab/types';
|
|
14
|
+
|
|
15
|
+
interface TabBarBaseItemProps {
|
|
16
|
+
index: number;
|
|
17
|
+
inputRange: Array<number>;
|
|
18
|
+
onPress: (index: number) => void;
|
|
19
|
+
route: ReanimatedTabViewTypes.Route;
|
|
20
|
+
style?: StyleProp<ViewStyle>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const TabBarBaseItem = ({
|
|
24
|
+
index,
|
|
25
|
+
inputRange,
|
|
26
|
+
onPress,
|
|
27
|
+
route,
|
|
28
|
+
style,
|
|
29
|
+
}: TabBarBaseItemProps) => {
|
|
30
|
+
const { positionX } = useTabContext();
|
|
31
|
+
|
|
32
|
+
const activeStyle = useAnimatedStyle(() => ({
|
|
33
|
+
opacity: interpolate(
|
|
34
|
+
positionX.value,
|
|
35
|
+
inputRange,
|
|
36
|
+
inputRange.map((i) => (i === index ? 1 : 0))
|
|
37
|
+
),
|
|
38
|
+
position: 'absolute',
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
const inactiveStyle = useAnimatedStyle(() => ({
|
|
42
|
+
opacity: interpolate(
|
|
43
|
+
positionX.value,
|
|
44
|
+
inputRange,
|
|
45
|
+
inputRange.map((i) => (i === index ? 0 : 1))
|
|
46
|
+
),
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<Pressable onPress={() => onPress(index)} style={[styles.container, style]}>
|
|
51
|
+
<Animated.View style={activeStyle}>
|
|
52
|
+
{route.tabBarLabel ? (
|
|
53
|
+
route.tabBarLabel(true)
|
|
54
|
+
) : (
|
|
55
|
+
<Text>{route.title}</Text>
|
|
56
|
+
)}
|
|
57
|
+
</Animated.View>
|
|
58
|
+
<Animated.View style={inactiveStyle}>
|
|
59
|
+
{route.tabBarLabel ? (
|
|
60
|
+
route.tabBarLabel(false)
|
|
61
|
+
) : (
|
|
62
|
+
<Text>{route.title}</Text>
|
|
63
|
+
)}
|
|
64
|
+
</Animated.View>
|
|
65
|
+
</Pressable>
|
|
66
|
+
);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const styles = StyleSheet.create({
|
|
70
|
+
container: {
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
flex: 1,
|
|
73
|
+
justifyContent: 'center',
|
|
74
|
+
minHeight: 50,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
@@ -16,12 +16,12 @@ export const TabBarLabelBaseComponent = ({
|
|
|
16
16
|
index,
|
|
17
17
|
...props
|
|
18
18
|
}: TabBarLabelTypes) => {
|
|
19
|
-
const
|
|
19
|
+
const positionX = useTabOffset();
|
|
20
20
|
|
|
21
21
|
const style = useAnimatedStyle(
|
|
22
22
|
() => ({
|
|
23
23
|
backgroundColor: interpolateColor(
|
|
24
|
-
|
|
24
|
+
positionX.value,
|
|
25
25
|
[index - 1, index, index + 1],
|
|
26
26
|
['transparent', 'blue', 'transparent']
|
|
27
27
|
),
|
|
@@ -32,7 +32,7 @@ export const TabBarLabelBaseComponent = ({
|
|
|
32
32
|
const textStyle = useAnimatedStyle(
|
|
33
33
|
() => ({
|
|
34
34
|
color: interpolateColor(
|
|
35
|
-
|
|
35
|
+
positionX.value,
|
|
36
36
|
[index - 1, index, index + 1],
|
|
37
37
|
['black', 'white', 'black']
|
|
38
38
|
),
|
|
@@ -2,7 +2,6 @@ import React, { useMemo, useRef, useState } from 'react';
|
|
|
2
2
|
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
|
|
3
3
|
import Reanimated, {
|
|
4
4
|
Easing,
|
|
5
|
-
interpolate,
|
|
6
5
|
runOnJS,
|
|
7
6
|
useAnimatedReaction,
|
|
8
7
|
useAnimatedStyle,
|
|
@@ -12,12 +11,19 @@ import Reanimated, {
|
|
|
12
11
|
} from 'react-native-reanimated';
|
|
13
12
|
|
|
14
13
|
import { useTabContext } from '../../hooks/useTabContext';
|
|
14
|
+
import { Platform, StyleSheet, View } from 'react-native';
|
|
15
15
|
|
|
16
16
|
interface GestureWrapperProps {
|
|
17
17
|
children: React.ReactNode;
|
|
18
|
+
bounces?: boolean;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
export const GestureWrapper = ({ children }: GestureWrapperProps) => {
|
|
21
|
+
export const GestureWrapper = ({ children, bounces }: GestureWrapperProps) => {
|
|
22
|
+
const _bounces = Platform.select({
|
|
23
|
+
ios: bounces,
|
|
24
|
+
default: false,
|
|
25
|
+
});
|
|
26
|
+
|
|
21
27
|
const {
|
|
22
28
|
context,
|
|
23
29
|
currentScreenIndex,
|
|
@@ -25,6 +31,7 @@ export const GestureWrapper = ({ children }: GestureWrapperProps) => {
|
|
|
25
31
|
gestureEnabled,
|
|
26
32
|
headerHeight,
|
|
27
33
|
transformationY,
|
|
34
|
+
scrollY,
|
|
28
35
|
} = useTabContext();
|
|
29
36
|
|
|
30
37
|
const movedY = useSharedValue(0);
|
|
@@ -105,26 +112,46 @@ export const GestureWrapper = ({ children }: GestureWrapperProps) => {
|
|
|
105
112
|
[_gestureEnabled]
|
|
106
113
|
);
|
|
107
114
|
|
|
108
|
-
|
|
109
|
-
() =>
|
|
115
|
+
useAnimatedReaction(
|
|
116
|
+
() => {
|
|
117
|
+
if (transformationY.value > 0) {
|
|
118
|
+
if (_bounces) return transformationY.value * -0.3;
|
|
119
|
+
else return 0;
|
|
120
|
+
}
|
|
121
|
+
if (transformationY.value < -headerHeight.value) {
|
|
122
|
+
return headerHeight.value;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return -transformationY.value;
|
|
126
|
+
},
|
|
127
|
+
(value) => {
|
|
128
|
+
scrollY.value = value;
|
|
129
|
+
}
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const style = useAnimatedStyle(() => {
|
|
133
|
+
return {
|
|
110
134
|
flex: 1,
|
|
111
135
|
transform: [
|
|
112
136
|
{
|
|
113
|
-
translateY:
|
|
114
|
-
transformationY.value,
|
|
115
|
-
[0, -headerHeight.value],
|
|
116
|
-
[0, -headerHeight.value],
|
|
117
|
-
'clamp'
|
|
118
|
-
),
|
|
137
|
+
translateY: scrollY.value * -1,
|
|
119
138
|
},
|
|
120
139
|
],
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
);
|
|
140
|
+
};
|
|
141
|
+
}, []);
|
|
124
142
|
|
|
125
143
|
return (
|
|
126
144
|
<GestureDetector gesture={gesture}>
|
|
127
|
-
<
|
|
145
|
+
<View style={styles.container}>
|
|
146
|
+
<Reanimated.View style={style}>{children}</Reanimated.View>
|
|
147
|
+
</View>
|
|
128
148
|
</GestureDetector>
|
|
129
149
|
);
|
|
130
150
|
};
|
|
151
|
+
|
|
152
|
+
const styles = StyleSheet.create({
|
|
153
|
+
container: {
|
|
154
|
+
overflow: 'hidden',
|
|
155
|
+
flex: 1,
|
|
156
|
+
},
|
|
157
|
+
});
|