Browse Source

Created custom tab bar

Arnaud Vergnet 4 years ago
parent
commit
0385e8ad76

+ 0
- 1
package.json View File

@@ -24,7 +24,6 @@
24 24
     "@react-native-community/masked-view": "0.1.6",
25 25
     "@react-navigation/bottom-tabs": "^5.1.1",
26 26
     "@react-navigation/drawer": "^5.1.1",
27
-    "@react-navigation/material-bottom-tabs": "^5.1.1",
28 27
     "@react-navigation/native": "^5.0.9",
29 28
     "@react-navigation/stack": "^5.1.1",
30 29
     "expo": "^37.0.0",

+ 1
- 1
src/components/Sidebar/Sidebar.js View File

@@ -192,7 +192,7 @@ class SideBar extends React.Component<Props, State> {
192 192
         try {
193 193
             const state = event.data.state.routes[0].state; // Get the Drawer's state if it exists
194 194
             // Get the current route name. This will only show Drawer routes.
195
-            // Tab routes will be shown as 'Main'
195
+            // Tabbar routes will be shown as 'Main'
196 196
             const routeName = state.routeNames[state.index];
197 197
             if (this.state.activeRoute !== routeName)
198 198
                 this.setState({activeRoute: routeName});

+ 81
- 0
src/components/Tabbar/CustomTabBar.js View File

@@ -0,0 +1,81 @@
1
+import * as React from 'react';
2
+import {View} from "react-native";
3
+import {withTheme} from 'react-native-paper';
4
+import TabIcon from "./TabIcon";
5
+import TabHomeIcon from "./TabHomeIcon";
6
+
7
+type Props = {
8
+    state: Object,
9
+    descriptors: Object,
10
+    navigation: Object,
11
+    theme: Object,
12
+}
13
+
14
+const TAB_BAR_HEIGHT = 48;
15
+
16
+/**
17
+ * Abstraction layer for Agenda component, using custom configuration
18
+ */
19
+class CustomTabBar extends React.Component<Props> {
20
+
21
+    render() {
22
+        const state = this.props.state;
23
+        const descriptors = this.props.descriptors;
24
+        const navigation = this.props.navigation;
25
+        return (
26
+            <View style={{
27
+                flexDirection: 'row',
28
+                height: TAB_BAR_HEIGHT,
29
+            }}>
30
+                {state.routes.map((route, index) => {
31
+                    const {options} = descriptors[route.key];
32
+                    const label =
33
+                        options.tabBarLabel !== undefined
34
+                            ? options.tabBarLabel
35
+                            : options.title !== undefined
36
+                            ? options.title
37
+                            : route.name;
38
+
39
+                    const isFocused = state.index === index;
40
+
41
+                    const onPress = () => {
42
+                        const event = navigation.emit({
43
+                            type: 'tabPress',
44
+                            target: route.key,
45
+                            canPreventDefault: true,
46
+                        });
47
+
48
+                        if (!isFocused && !event.defaultPrevented) {
49
+                            navigation.navigate(route.name);
50
+                        }
51
+                    };
52
+
53
+                    const onLongPress = () => {
54
+                        navigation.emit({
55
+                            type: 'tabLongPress',
56
+                            target: route.key,
57
+                        });
58
+                    };
59
+
60
+                    const color = isFocused ? options.activeColor : options.inactiveColor;
61
+                    const iconData = {focused: isFocused, color: color};
62
+                    if (route.name !== "home") {
63
+                        return <TabIcon
64
+                            onPress={onPress}
65
+                            onLongPress={onLongPress}
66
+                            icon={options.tabBarIcon(iconData)}
67
+                            color={color}
68
+                            label={label}
69
+                            focused={isFocused}/>
70
+                    } else
71
+                        return <TabHomeIcon
72
+                            onPress={onPress}
73
+                            onLongPress={onLongPress}
74
+                            focused={isFocused}/>
75
+                })}
76
+            </View>
77
+        );
78
+    }
79
+}
80
+
81
+export default withTheme(CustomTabBar);

+ 79
- 0
src/components/Tabbar/TabHomeIcon.js View File

@@ -0,0 +1,79 @@
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import {View} from "react-native";
5
+import {FAB, withTheme} from 'react-native-paper';
6
+import * as Animatable from "react-native-animatable";
7
+
8
+type Props = {
9
+    focused: boolean,
10
+    onPress: Function,
11
+    onLongPress: Function,
12
+    theme: Object,
13
+}
14
+
15
+const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
16
+
17
+/**
18
+ * Abstraction layer for Agenda component, using custom configuration
19
+ */
20
+class TabHomeIcon extends React.Component<Props> {
21
+
22
+    focusedIcon = require('../../../assets/tab-icon.png');
23
+    unFocusedIcon = require('../../../assets/tab-icon-outline.png');
24
+
25
+    constructor(props) {
26
+        super(props);
27
+        Animatable.initializeRegistryWithDefinitions({
28
+            fabFocusIn: {
29
+                "0": {
30
+                    scale: 1, translateY: 0
31
+                },
32
+                "0.9": {
33
+                    scale: 1.4, translateY: -6
34
+                },
35
+                "1": {
36
+                    scale: 1.3, translateY: -5
37
+                },
38
+            },
39
+            fabFocusOut: {
40
+                "0": {
41
+                    scale: 1.3, translateY: -5
42
+                },
43
+                "1": {
44
+                    scale: 1, translateY: 0
45
+                },
46
+            }
47
+        });
48
+    }
49
+
50
+    shouldComponentUpdate(nextProps: Props): boolean {
51
+        return (nextProps.focused !== this.props.focused);
52
+    }
53
+
54
+    render(): React$Node {
55
+        const props = this.props;
56
+        return (
57
+            <View style={{
58
+                flex: 1,
59
+                justifyContent: 'center',
60
+            }}>
61
+                <AnimatedFAB
62
+                    duration={200}
63
+                    easing={"ease-out"}
64
+                    animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
65
+                    useNativeDriver
66
+                    onPress={props.onPress}
67
+                    onLongPress={props.onLongPress}
68
+                    icon={this.focusedIcon}
69
+                    style={{
70
+                        marginLeft: 'auto',
71
+                        marginRight: 'auto'
72
+                    }}/>
73
+            </View>
74
+        );
75
+    }
76
+
77
+}
78
+
79
+export default withTheme(TabHomeIcon);

+ 111
- 0
src/components/Tabbar/TabIcon.js View File

@@ -0,0 +1,111 @@
1
+// @flow
2
+
3
+import * as React from 'react';
4
+import {View} from "react-native";
5
+import {TouchableRipple, withTheme} from 'react-native-paper';
6
+import {MaterialCommunityIcons} from "@expo/vector-icons";
7
+import * as Animatable from "react-native-animatable";
8
+
9
+type Props = {
10
+    focused: boolean,
11
+    color: string,
12
+    label: string,
13
+    icon: string,
14
+    onPress: Function,
15
+    onLongPress: Function,
16
+    theme: Object,
17
+}
18
+
19
+const AnimatedIcon = Animatable.createAnimatableComponent(MaterialCommunityIcons);
20
+
21
+
22
+/**
23
+ * Abstraction layer for Agenda component, using custom configuration
24
+ */
25
+class TabIcon extends React.Component<Props> {
26
+
27
+    firstRender: boolean;
28
+
29
+    constructor(props) {
30
+        super(props);
31
+        Animatable.initializeRegistryWithDefinitions({
32
+            focusIn: {
33
+                "0": {
34
+                    scale: 1, translateY: 0
35
+                },
36
+                "0.9": {
37
+                    scale: 1.6, translateY: 6
38
+                },
39
+                "1": {
40
+                    scale: 1.5, translateY: 5
41
+                },
42
+            },
43
+            focusOut: {
44
+                "0": {
45
+                    scale: 1.5, translateY: 5
46
+                },
47
+                "1": {
48
+                    scale: 1, translateY: 0
49
+                },
50
+            }
51
+        });
52
+        this.firstRender = true;
53
+    }
54
+
55
+    componentDidMount() {
56
+        this.firstRender = false;
57
+    }
58
+
59
+    shouldComponentUpdate(nextProps: Props): boolean {
60
+        return (nextProps.focused !== this.props.focused)
61
+            || (nextProps.theme.dark !== this.props.theme.dark);
62
+    }
63
+
64
+    render(): React$Node {
65
+        const props = this.props;
66
+        return (
67
+            <TouchableRipple
68
+                onPress={props.onPress}
69
+                onLongPress={props.onLongPress}
70
+                style={{
71
+                    flex: 1,
72
+                    justifyContent: 'center',
73
+                    alignItem: 'center',
74
+                }}
75
+            >
76
+                <View>
77
+                    <Animatable.View
78
+                        duration={200}
79
+                        easing={"ease-out"}
80
+                        animation={props.focused ? "focusIn" : "focusOut"}
81
+                        useNativeDriver
82
+                    >
83
+                        <AnimatedIcon
84
+                            name={props.icon}
85
+                            color={props.color}
86
+                            size={26}
87
+                            style={{
88
+                                marginLeft: 'auto',
89
+                                marginRight: 'auto',
90
+                            }}
91
+                        />
92
+                    </Animatable.View>
93
+                    <Animatable.Text
94
+                        animation={props.focused ? "fadeOutDown" : "fadeIn"}
95
+                        useNativeDriver
96
+
97
+                        style={{
98
+                            color: props.color,
99
+                            marginLeft: 'auto',
100
+                            marginRight: 'auto',
101
+                        }}
102
+                    >
103
+                        {props.label}
104
+                    </Animatable.Text>
105
+                </View>
106
+            </TouchableRipple>
107
+        );
108
+    }
109
+}
110
+
111
+export default withTheme(TabIcon);

+ 10
- 19
src/navigation/MainTabNavigator.js View File

@@ -1,6 +1,6 @@
1 1
 import * as React from 'react';
2 2
 import {createStackNavigator, TransitionPresets} from '@react-navigation/stack';
3
-import {createMaterialBottomTabNavigator} from "@react-navigation/material-bottom-tabs";
3
+import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
4 4
 
5 5
 import HomeScreen from '../screens/HomeScreen';
6 6
 import PlanningScreen from '../screens/Planning/PlanningScreen';
@@ -11,9 +11,8 @@ import ProximoMainScreen from '../screens/Proximo/ProximoMainScreen';
11 11
 import ProximoListScreen from "../screens/Proximo/ProximoListScreen";
12 12
 import ProximoAboutScreen from "../screens/Proximo/ProximoAboutScreen";
13 13
 import PlanexScreen from '../screens/Websites/PlanexScreen';
14
-import {MaterialCommunityIcons} from "@expo/vector-icons";
15 14
 import AsyncStorageManager from "../managers/AsyncStorageManager";
16
-import {FAB, useTheme, withTheme} from 'react-native-paper';
15
+import {useTheme, withTheme} from 'react-native-paper';
17 16
 import i18n from "i18n-js";
18 17
 import ClubDisplayScreen from "../screens/Amicale/Clubs/ClubDisplayScreen";
19 18
 import ScannerScreen from "../screens/ScannerScreen";
@@ -21,6 +20,7 @@ import MaterialHeaderButtons, {Item} from "../components/Custom/HeaderButton";
21 20
 import FeedItemScreen from "../screens/FeedItemScreen";
22 21
 import {createCollapsibleStack} from "react-navigation-collapsible";
23 22
 import GroupSelectionScreen from "../screens/GroupSelectionScreen";
23
+import CustomTabBar from "../components/Tabbar/CustomTabBar";
24 24
 
25 25
 const TAB_ICONS = {
26 26
     home: 'triangle',
@@ -306,7 +306,7 @@ function PlanexStackComponent() {
306 306
     );
307 307
 }
308 308
 
309
-const Tab = createMaterialBottomTabNavigator();
309
+const Tab = createBottomTabNavigator();
310 310
 
311 311
 type Props = {
312 312
     defaultRoute: string | null,
@@ -329,16 +329,6 @@ class TabNavigator extends React.Component<Props> {
329 329
         this.createHomeStackComponent = () => HomeStackComponent(props.defaultRoute, props.defaultData);
330 330
     }
331 331
 
332
-    getHomeButton(focused: boolean) {
333
-        let icon = focused ? require('../../assets/tab-icon.png') : require('../../assets/tab-icon-outline.png')
334
-        return (
335
-                <FAB
336
-                    icon={icon}
337
-                    small
338
-                    style={{position: 'absolute', top: '-25%'}}/>
339
-        );
340
-    }
341
-
342 332
     render() {
343 333
         return (
344 334
             <Tab.Navigator
@@ -349,14 +339,15 @@ class TabNavigator extends React.Component<Props> {
349 339
                         let icon = TAB_ICONS[route.name];
350 340
                         icon = focused ? icon : icon + ('-outline');
351 341
                         if (route.name !== "home")
352
-                            return <MaterialCommunityIcons name={icon} color={color} size={26}/>;
342
+                            return icon;
353 343
                         else
354
-                            return this.getHomeButton(focused);
344
+                            return null;
355 345
                     },
356
-                    tabBarLabel: route.name !== 'home' ? undefined : ''
346
+                    tabBarLabel: route.name !== 'home' ? undefined : '',
347
+                    activeColor: this.props.theme.colors.primary,
348
+                    inactiveColor: this.props.theme.colors.tabIcon
357 349
                 })}
358
-                activeColor={this.props.theme.colors.primary}
359
-                inactiveColor={this.props.theme.colors.tabIcon}
350
+                tabBar={props => <CustomTabBar {...props} />}
360 351
             >
361 352
                 <Tab.Screen
362 353
                     name="proximo"

Loading…
Cancel
Save