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.
Files changed (51) hide show
  1. package/lib/commonjs/components/ReanimatedTopTab/AnimationHelper.js +2 -2
  2. package/lib/commonjs/components/ReanimatedTopTab/AnimationHelper.js.map +1 -1
  3. package/lib/commonjs/components/ReanimatedTopTab/ReanimatedTabView.js +26 -16
  4. package/lib/commonjs/components/ReanimatedTopTab/ReanimatedTabView.js.map +1 -1
  5. package/lib/commonjs/components/elements/TabBarBaseComponent.js +4 -4
  6. package/lib/commonjs/components/elements/TabBarBaseComponent.js.map +1 -1
  7. package/lib/commonjs/components/wrappers/GestureWrapper.js +5 -6
  8. package/lib/commonjs/components/wrappers/GestureWrapper.js.map +1 -1
  9. package/lib/commonjs/context/Context.hooks.js +30 -1
  10. package/lib/commonjs/context/Context.hooks.js.map +1 -1
  11. package/lib/commonjs/context/Context.js +6 -0
  12. package/lib/commonjs/context/Context.js.map +1 -1
  13. package/lib/commonjs/navigator/createReanimatedTopTabNavigator.js +6 -31
  14. package/lib/commonjs/navigator/createReanimatedTopTabNavigator.js.map +1 -1
  15. package/lib/module/components/ReanimatedTopTab/AnimationHelper.js +2 -2
  16. package/lib/module/components/ReanimatedTopTab/AnimationHelper.js.map +1 -1
  17. package/lib/module/components/ReanimatedTopTab/ReanimatedTabView.js +27 -17
  18. package/lib/module/components/ReanimatedTopTab/ReanimatedTabView.js.map +1 -1
  19. package/lib/module/components/elements/TabBarBaseComponent.js +4 -4
  20. package/lib/module/components/elements/TabBarBaseComponent.js.map +1 -1
  21. package/lib/module/components/wrappers/GestureWrapper.js +5 -6
  22. package/lib/module/components/wrappers/GestureWrapper.js.map +1 -1
  23. package/lib/module/context/Context.hooks.js +30 -1
  24. package/lib/module/context/Context.hooks.js.map +1 -1
  25. package/lib/module/context/Context.js +6 -0
  26. package/lib/module/context/Context.js.map +1 -1
  27. package/lib/module/navigator/createReanimatedTopTabNavigator.js +7 -32
  28. package/lib/module/navigator/createReanimatedTopTabNavigator.js.map +1 -1
  29. package/lib/typescript/src/components/ReanimatedTopTab/AnimationHelper.d.ts +7 -3
  30. package/lib/typescript/src/components/ReanimatedTopTab/AnimationHelper.d.ts.map +1 -1
  31. package/lib/typescript/src/components/ReanimatedTopTab/ReanimatedTabView.d.ts +1 -1
  32. package/lib/typescript/src/components/ReanimatedTopTab/ReanimatedTabView.d.ts.map +1 -1
  33. package/lib/typescript/src/components/ReanimatedTopTab/types.d.ts +1 -3
  34. package/lib/typescript/src/components/ReanimatedTopTab/types.d.ts.map +1 -1
  35. package/lib/typescript/src/components/elements/TabBarBaseComponent.d.ts +4 -18
  36. package/lib/typescript/src/components/elements/TabBarBaseComponent.d.ts.map +1 -1
  37. package/lib/typescript/src/components/wrappers/GestureWrapper.d.ts.map +1 -1
  38. package/lib/typescript/src/context/Context.d.ts.map +1 -1
  39. package/lib/typescript/src/context/Context.hooks.d.ts +1 -0
  40. package/lib/typescript/src/context/Context.hooks.d.ts.map +1 -1
  41. package/lib/typescript/src/index.d.ts +1 -1
  42. package/lib/typescript/src/navigator/createReanimatedTopTabNavigator.d.ts.map +1 -1
  43. package/package.json +1 -1
  44. package/src/components/ReanimatedTopTab/AnimationHelper.ts +7 -6
  45. package/src/components/ReanimatedTopTab/ReanimatedTabView.tsx +33 -25
  46. package/src/components/ReanimatedTopTab/types.ts +1 -4
  47. package/src/components/elements/TabBarBaseComponent.tsx +5 -20
  48. package/src/components/wrappers/GestureWrapper.tsx +5 -8
  49. package/src/context/Context.hooks.ts +34 -0
  50. package/src/context/Context.tsx +7 -0
  51. 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
- useEffect(() => {
64
- scrollPosition.value = AnimationHelper.animation(
65
- -navigationState.index * width
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
- navigationState
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
- navigationState
102
+ _navigationState.value
96
103
  );
97
104
  scrollPosition.value = AnimationHelper.animation(state.value);
98
- runOnJS(onIndexChange)(state.index);
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((r) => r.key === key);
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, onIndexChange, scrollPosition, width]
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 ? renderTabBar({ navigationState, position }) : null}
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 Route {
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(route);
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
- (nextValue) => {
51
- runOnJS(setGestureEnabled)(nextValue);
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(({ state }, stateManager) => {
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
  };
@@ -37,6 +37,13 @@ export const Provider = memo(
37
37
  context,
38
38
  });
39
39
 
40
+ ContextHooks.useApproachingTabChange({
41
+ config,
42
+ gestureEnabled,
43
+ transformationX,
44
+ currentScreenIndex,
45
+ });
46
+
40
47
  return (
41
48
  <Context.Provider
42
49
  value={{
@@ -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 { useCallback, useMemo } from 'react';
19
- import Reanimated, { type SharedValue } from 'react-native-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
- //NOTE: all belows reefers to Tabs component directly. Consider to move it somewhere
55
- const onIndexChange =
56
- (
57
- currentScreenIndex: SharedValue<number>,
58
- gestureEnabled: SharedValue<boolean>
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
- onIndexChange={onIndexChange(
146
- currentScreenIndex,
147
- gestureEnabled
148
- )}
108
+ navigate={navigate}
149
109
  renderScene={renderScene}
150
- renderTabBar={renderTabBar}
110
+ renderTabBar={TabBarComponent}
151
111
  />
152
112
  </GestureWrapper>
153
113
  </NavigationContent>