Browse Source

Improved tab bar hiding logic

Arnaud Vergnet 3 years ago
parent
commit
a9caca9969
1 changed files with 59 additions and 30 deletions
  1. 59
    30
      src/components/Tabbar/CustomTabBar.js

+ 59
- 30
src/components/Tabbar/CustomTabBar.js View File

3
 import TabIcon from "./TabIcon";
3
 import TabIcon from "./TabIcon";
4
 import TabHomeIcon from "./TabHomeIcon";
4
 import TabHomeIcon from "./TabHomeIcon";
5
 import {Animated} from 'react-native';
5
 import {Animated} from 'react-native';
6
+import {Collapsible} from "react-navigation-collapsible";
6
 
7
 
7
 type Props = {
8
 type Props = {
8
     state: Object,
9
     state: Object,
24
     planex: 'clock',
25
     planex: 'clock',
25
 };
26
 };
26
 
27
 
27
-/**
28
- * Abstraction layer for Agenda component, using custom configuration
29
- */
30
 class CustomTabBar extends React.Component<Props, State> {
28
 class CustomTabBar extends React.Component<Props, State> {
31
 
29
 
32
     static TAB_BAR_HEIGHT = 48;
30
     static TAB_BAR_HEIGHT = 48;
33
 
31
 
34
     state = {
32
     state = {
35
         translateY: new Animated.Value(0),
33
         translateY: new Animated.Value(0),
36
-        barSynced: false,// Is the bar synced with the header for animations?
37
     }
34
     }
38
 
35
 
36
+    syncTabBar = (route, index) => {
37
+        const state = this.props.state;
38
+        const isFocused = state.index === index;
39
+        if (isFocused) {
40
+            const stackState = route.state;
41
+            const stackRoute = stackState ? stackState.routes[stackState.index] : undefined;
42
+            const params: { collapsible: Collapsible } = stackRoute ? stackRoute.params : undefined;
43
+            const collapsible = params ? params.collapsible : undefined;
44
+            if (collapsible) {
45
+                this.setState({
46
+                    translateY: Animated.multiply(-1.5, collapsible.translateY), // Hide tab bar faster than header bar
47
+                });
48
+            }
49
+        }
50
+    };
51
+
52
+    /**
53
+     * Navigates to the given route if it is different from the current one
54
+     *
55
+     * @param route Destination route
56
+     * @param currentIndex The current route index
57
+     * @param destIndex The destination route index
58
+     */
39
     onItemPress(route: Object, currentIndex: number, destIndex: number) {
59
     onItemPress(route: Object, currentIndex: number, destIndex: number) {
40
         const event = this.props.navigation.emit({
60
         const event = this.props.navigation.emit({
41
             type: 'tabPress',
61
             type: 'tabPress',
42
             target: route.key,
62
             target: route.key,
43
             canPreventDefault: true,
63
             canPreventDefault: true,
44
         });
64
         });
45
-        if (currentIndex !== destIndex && !event.defaultPrevented) {
46
-            this.state.translateY = new Animated.Value(0);
65
+        if (currentIndex !== destIndex && !event.defaultPrevented)
47
             this.props.navigation.navigate(route.name);
66
             this.props.navigation.navigate(route.name);
48
-        }
49
     }
67
     }
50
 
68
 
69
+    /**
70
+     * Navigates to tetris screen on home button long press
71
+     *
72
+     * @param route
73
+     */
51
     onItemLongPress(route: Object) {
74
     onItemLongPress(route: Object) {
52
         const event = this.props.navigation.emit({
75
         const event = this.props.navigation.emit({
53
             type: 'tabLongPress',
76
             type: 'tabLongPress',
54
             target: route.key,
77
             target: route.key,
55
             canPreventDefault: true,
78
             canPreventDefault: true,
56
         });
79
         });
57
-        if (route.name === "home" && !event.defaultPrevented) {
80
+        if (route.name === "home" && !event.defaultPrevented)
58
             this.props.navigation.navigate('tetris');
81
             this.props.navigation.navigate('tetris');
59
-        }
60
     }
82
     }
61
 
83
 
84
+    /**
85
+     * Gets an icon for the given route if it is not the home one as it uses a custom button
86
+     *
87
+     * @param route
88
+     * @param focused
89
+     * @returns {null}
90
+     */
62
     tabBarIcon = (route, focused) => {
91
     tabBarIcon = (route, focused) => {
63
         let icon = TAB_ICONS[route.name];
92
         let icon = TAB_ICONS[route.name];
64
         icon = focused ? icon : icon + ('-outline');
93
         icon = focused ? icon : icon + ('-outline');
68
             return null;
97
             return null;
69
     };
98
     };
70
 
99
 
71
-
100
+    /**
101
+     * Finds the active route and syncs the tab bar animation with the header bar
102
+     */
72
     onRouteChange = () => {
103
     onRouteChange = () => {
73
-        this.setState({barSynced: false});
104
+        this.props.state.routes.map(this.syncTabBar)
74
     }
105
     }
75
 
106
 
107
+    /**
108
+     * Gets a tab icon render.
109
+     * If the given route is focused, it syncs the tab bar and header bar animations together
110
+     *
111
+     * @param route The route for the icon
112
+     * @param index The index of the current route
113
+     * @returns {*}
114
+     */
76
     renderIcon = (route, index) => {
115
     renderIcon = (route, index) => {
77
         const state = this.props.state;
116
         const state = this.props.state;
78
         const {options} = this.props.descriptors[route.key];
117
         const {options} = this.props.descriptors[route.key];
79
         const label =
118
         const label =
80
-            options.tabBarLabel !== undefined
119
+            options.tabBarLabel != null
81
                 ? options.tabBarLabel
120
                 ? options.tabBarLabel
82
-                : options.title !== undefined
121
+                : options.title != null
83
                 ? options.title
122
                 ? options.title
84
                 : route.name;
123
                 : route.name;
85
 
124
 
86
-        const isFocused = state.index === index;
87
-
88
         const onPress = () => this.onItemPress(route, state.index, index);
125
         const onPress = () => this.onItemPress(route, state.index, index);
89
-
90
         const onLongPress = () => this.onItemLongPress(route);
126
         const onLongPress = () => this.onItemLongPress(route);
91
-
92
-        if (isFocused) {
93
-            const stackState = route.state;
94
-            const stackRoute = route.state ? stackState.routes[stackState.index] : undefined;
95
-            const params = stackRoute ? stackRoute.params : undefined;
96
-            const collapsible = params ? params.collapsible : undefined;
97
-            if (collapsible && !this.state.barSynced) {
98
-                this.setState({
99
-                    translateY: Animated.multiply(-1.5, collapsible.translateY),
100
-                    barSynced: true,
101
-                });
102
-            }
103
-        }
127
+        const isFocused = state.index === index;
104
 
128
 
105
         const color = isFocused ? this.props.theme.colors.primary : this.props.theme.colors.tabIcon;
129
         const color = isFocused ? this.props.theme.colors.primary : this.props.theme.colors.tabIcon;
106
         if (route.name !== "home") {
130
         if (route.name !== "home") {
124
             />
148
             />
125
     };
149
     };
126
 
150
 
151
+    getIcons() {
152
+        return this.props.state.routes.map(this.renderIcon);
153
+    }
154
+
127
     render() {
155
     render() {
128
         this.props.navigation.addListener('state', this.onRouteChange);
156
         this.props.navigation.addListener('state', this.onRouteChange);
157
+        const icons = this.getIcons();
129
         return (
158
         return (
130
             <Animated.View
159
             <Animated.View
131
                 useNativeDriver
160
                 useNativeDriver
140
                     transform: [{translateY: this.state.translateY}],
169
                     transform: [{translateY: this.state.translateY}],
141
                 }}
170
                 }}
142
             >
171
             >
143
-                {this.props.state.routes.map(this.renderIcon)}
172
+                {icons}
144
             </Animated.View>
173
             </Animated.View>
145
         );
174
         );
146
     }
175
     }

Loading…
Cancel
Save