Browse Source

Improve tab components to match linter

Arnaud Vergnet 1 year ago
parent
commit
aa992d20b2
3 changed files with 417 additions and 350 deletions
  1. 199
    162
      src/components/Tabbar/CustomTabBar.js
  2. 117
    90
      src/components/Tabbar/TabHomeIcon.js
  3. 101
    98
      src/components/Tabbar/TabIcon.js

+ 199
- 162
src/components/Tabbar/CustomTabBar.js View File

@@ -2,179 +2,216 @@
2 2
 
3 3
 import * as React from 'react';
4 4
 import {withTheme} from 'react-native-paper';
5
-import TabIcon from "./TabIcon";
6
-import TabHomeIcon from "./TabHomeIcon";
7
-import {Animated} from 'react-native';
8
-import {Collapsible} from "react-navigation-collapsible";
9
-
10
-type Props = {
11
-    state: Object,
12
-    descriptors: Object,
13
-    navigation: Object,
14
-    theme: Object,
15
-    collapsibleStack: Object,
16
-}
17
-
18
-type State = {
19
-    translateY: AnimatedValue,
20
-    barSynced: boolean,
21
-}
5
+import Animated from 'react-native-reanimated';
6
+import {Collapsible} from 'react-navigation-collapsible';
7
+import {StackNavigationProp} from '@react-navigation/stack';
8
+import TabIcon from './TabIcon';
9
+import TabHomeIcon from './TabHomeIcon';
10
+import type {CustomTheme} from '../../managers/ThemeManager';
11
+
12
+type RouteType = {
13
+  name: string,
14
+  key: string,
15
+  params: {collapsible: Collapsible},
16
+  state: {
17
+    index: number,
18
+    routes: Array<RouteType>,
19
+  },
20
+};
22 21
 
23
-const TAB_ICONS = {
24
-    proxiwash: 'tshirt-crew',
25
-    services: 'account-circle',
26
-    planning: 'calendar-range',
27
-    planex: 'clock',
22
+type PropsType = {
23
+  state: {
24
+    index: number,
25
+    routes: Array<RouteType>,
26
+  },
27
+  descriptors: {
28
+    [key: string]: {
29
+      options: {
30
+        tabBarLabel: string,
31
+        title: string,
32
+      },
33
+    },
34
+  },
35
+  navigation: StackNavigationProp,
36
+  theme: CustomTheme,
28 37
 };
29 38
 
30
-class CustomTabBar extends React.Component<Props, State> {
39
+type StateType = {
40
+  // eslint-disable-next-line flowtype/no-weak-types
41
+  translateY: any,
42
+};
31 43
 
32
-    static TAB_BAR_HEIGHT = 48;
44
+const TAB_ICONS = {
45
+  proxiwash: 'tshirt-crew',
46
+  services: 'account-circle',
47
+  planning: 'calendar-range',
48
+  planex: 'clock',
49
+};
33 50
 
34
-    state = {
35
-        translateY: new Animated.Value(0),
36
-    }
51
+class CustomTabBar extends React.Component<PropsType, StateType> {
52
+  static TAB_BAR_HEIGHT = 48;
37 53
 
38
-    syncTabBar = (route, index) => {
39
-        const state = this.props.state;
40
-        const isFocused = state.index === index;
41
-        if (isFocused) {
42
-            const stackState = route.state;
43
-            const stackRoute = stackState ? stackState.routes[stackState.index] : undefined;
44
-            const params: { collapsible: Collapsible } = stackRoute ? stackRoute.params : undefined;
45
-            const collapsible = params ? params.collapsible : undefined;
46
-            if (collapsible) {
47
-                this.setState({
48
-                    translateY: Animated.multiply(-1.5, collapsible.translateY), // Hide tab bar faster than header bar
49
-                });
50
-            }
51
-        }
54
+  constructor() {
55
+    super();
56
+    this.state = {
57
+      translateY: new Animated.Value(0),
52 58
     };
53
-
54
-    /**
55
-     * Navigates to the given route if it is different from the current one
56
-     *
57
-     * @param route Destination route
58
-     * @param currentIndex The current route index
59
-     * @param destIndex The destination route index
60
-     */
61
-    onItemPress(route: Object, currentIndex: number, destIndex: number) {
62
-        const event = this.props.navigation.emit({
63
-            type: 'tabPress',
64
-            target: route.key,
65
-            canPreventDefault: true,
66
-        });
67
-        if (currentIndex !== destIndex && !event.defaultPrevented)
68
-            this.props.navigation.navigate(route.name);
69
-    }
70
-
71
-    /**
72
-     * Navigates to tetris screen on home button long press
73
-     *
74
-     * @param route
75
-     */
76
-    onItemLongPress(route: Object) {
77
-        const event = this.props.navigation.emit({
78
-            type: 'tabLongPress',
79
-            target: route.key,
80
-            canPreventDefault: true,
81
-        });
82
-        if (route.name === "home" && !event.defaultPrevented)
83
-            this.props.navigation.navigate('game-start');
84
-    }
85
-
86
-    /**
87
-     * Gets an icon for the given route if it is not the home one as it uses a custom button
88
-     *
89
-     * @param route
90
-     * @param focused
91
-     * @returns {null}
92
-     */
93
-    tabBarIcon = (route, focused) => {
94
-        let icon = TAB_ICONS[route.name];
95
-        icon = focused ? icon : icon + ('-outline');
96
-        if (route.name !== "home")
97
-            return icon;
98
-        else
99
-            return null;
59
+  }
60
+
61
+  /**
62
+   * Navigates to the given route if it is different from the current one
63
+   *
64
+   * @param route Destination route
65
+   * @param currentIndex The current route index
66
+   * @param destIndex The destination route index
67
+   */
68
+  onItemPress(route: RouteType, currentIndex: number, destIndex: number) {
69
+    const {navigation} = this.props;
70
+    const event = navigation.emit({
71
+      type: 'tabPress',
72
+      target: route.key,
73
+      canPreventDefault: true,
74
+    });
75
+    if (currentIndex !== destIndex && !event.defaultPrevented)
76
+      navigation.navigate(route.name);
77
+  }
78
+
79
+  /**
80
+   * Navigates to tetris screen on home button long press
81
+   *
82
+   * @param route
83
+   */
84
+  onItemLongPress(route: RouteType) {
85
+    const {navigation} = this.props;
86
+    const event = navigation.emit({
87
+      type: 'tabLongPress',
88
+      target: route.key,
89
+      canPreventDefault: true,
90
+    });
91
+    if (route.name === 'home' && !event.defaultPrevented)
92
+      navigation.navigate('game-start');
93
+  }
94
+
95
+  /**
96
+   * Finds the active route and syncs the tab bar animation with the header bar
97
+   */
98
+  onRouteChange = () => {
99
+    const {props} = this;
100
+    props.state.routes.map(this.syncTabBar);
101
+  };
102
+
103
+  /**
104
+   * Gets an icon for the given route if it is not the home one as it uses a custom button
105
+   *
106
+   * @param route
107
+   * @param focused
108
+   * @returns {null}
109
+   */
110
+  getTabBarIcon = (route: RouteType, focused: boolean): React.Node => {
111
+    let icon = TAB_ICONS[route.name];
112
+    icon = focused ? icon : `${icon}-outline`;
113
+    if (route.name !== 'home') return icon;
114
+    return null;
115
+  };
116
+
117
+  /**
118
+   * Gets a tab icon render.
119
+   * If the given route is focused, it syncs the tab bar and header bar animations together
120
+   *
121
+   * @param route The route for the icon
122
+   * @param index The index of the current route
123
+   * @returns {*}
124
+   */
125
+  getRenderIcon = (route: RouteType, index: number): React.Node => {
126
+    const {props} = this;
127
+    const {state} = props;
128
+    const {options} = props.descriptors[route.key];
129
+    let label;
130
+    if (options.tabBarLabel != null) label = options.tabBarLabel;
131
+    else if (options.title != null) label = options.title;
132
+    else label = route.name;
133
+
134
+    const onPress = () => {
135
+      this.onItemPress(route, state.index, index);
100 136
     };
101
-
102
-    /**
103
-     * Finds the active route and syncs the tab bar animation with the header bar
104
-     */
105
-    onRouteChange = () => {
106
-        this.props.state.routes.map(this.syncTabBar)
107
-    }
108
-
109
-    /**
110
-     * Gets a tab icon render.
111
-     * If the given route is focused, it syncs the tab bar and header bar animations together
112
-     *
113
-     * @param route The route for the icon
114
-     * @param index The index of the current route
115
-     * @returns {*}
116
-     */
117
-    renderIcon = (route, index) => {
118
-        const state = this.props.state;
119
-        const {options} = this.props.descriptors[route.key];
120
-        const label =
121
-            options.tabBarLabel != null
122
-                ? options.tabBarLabel
123
-                : options.title != null
124
-                ? options.title
125
-                : route.name;
126
-
127
-        const onPress = () => this.onItemPress(route, state.index, index);
128
-        const onLongPress = () => this.onItemLongPress(route);
129
-        const isFocused = state.index === index;
130
-
131
-        const color = isFocused ? this.props.theme.colors.primary : this.props.theme.colors.tabIcon;
132
-        if (route.name !== "home") {
133
-            return <TabIcon
134
-                onPress={onPress}
135
-                onLongPress={onLongPress}
136
-                icon={this.tabBarIcon(route, isFocused)}
137
-                color={color}
138
-                label={label}
139
-                focused={isFocused}
140
-                extraData={state.index > index}
141
-                key={route.key}
142
-            />
143
-        } else
144
-            return <TabHomeIcon
145
-                onPress={onPress}
146
-                onLongPress={onLongPress}
147
-                focused={isFocused}
148
-                key={route.key}
149
-                tabBarHeight={CustomTabBar.TAB_BAR_HEIGHT}
150
-            />
137
+    const onLongPress = () => {
138
+      this.onItemLongPress(route);
151 139
     };
152
-
153
-    getIcons() {
154
-        return this.props.state.routes.map(this.renderIcon);
140
+    const isFocused = state.index === index;
141
+
142
+    const color = isFocused
143
+      ? props.theme.colors.primary
144
+      : props.theme.colors.tabIcon;
145
+    if (route.name !== 'home') {
146
+      return (
147
+        <TabIcon
148
+          onPress={onPress}
149
+          onLongPress={onLongPress}
150
+          icon={this.getTabBarIcon(route, isFocused)}
151
+          color={color}
152
+          label={label}
153
+          focused={isFocused}
154
+          extraData={state.index > index}
155
+          key={route.key}
156
+        />
157
+      );
155 158
     }
156
-
157
-    render() {
158
-        this.props.navigation.addListener('state', this.onRouteChange);
159
-        const icons = this.getIcons();
160
-        return (
161
-            <Animated.View
162
-                useNativeDriver
163
-                style={{
164
-                    flexDirection: 'row',
165
-                    height: CustomTabBar.TAB_BAR_HEIGHT,
166
-                    width: '100%',
167
-                    position: 'absolute',
168
-                    bottom: 0,
169
-                    left: 0,
170
-                    backgroundColor: this.props.theme.colors.surface,
171
-                    transform: [{translateY: this.state.translateY}],
172
-                }}
173
-            >
174
-                {icons}
175
-            </Animated.View>
176
-        );
159
+    return (
160
+      <TabHomeIcon
161
+        onPress={onPress}
162
+        onLongPress={onLongPress}
163
+        focused={isFocused}
164
+        key={route.key}
165
+        tabBarHeight={CustomTabBar.TAB_BAR_HEIGHT}
166
+      />
167
+    );
168
+  };
169
+
170
+  getIcons(): React.Node {
171
+    const {props} = this;
172
+    return props.state.routes.map(this.getRenderIcon);
173
+  }
174
+
175
+  syncTabBar = (route: RouteType, index: number) => {
176
+    const {state} = this.props;
177
+    const isFocused = state.index === index;
178
+    if (isFocused) {
179
+      const stackState = route.state;
180
+      const stackRoute =
181
+        stackState != null ? stackState.routes[stackState.index] : null;
182
+      const params: {collapsible: Collapsible} | null =
183
+        stackRoute != null ? stackRoute.params : null;
184
+      const collapsible = params != null ? params.collapsible : null;
185
+      if (collapsible != null) {
186
+        this.setState({
187
+          translateY: Animated.multiply(-1.5, collapsible.translateY), // Hide tab bar faster than header bar
188
+        });
189
+      }
177 190
     }
191
+  };
192
+
193
+  render(): React.Node {
194
+    const {props, state} = this;
195
+    props.navigation.addListener('state', this.onRouteChange);
196
+    const icons = this.getIcons();
197
+    // $FlowFixMe
198
+    return (
199
+      <Animated.View
200
+        useNativeDriver
201
+        style={{
202
+          flexDirection: 'row',
203
+          height: CustomTabBar.TAB_BAR_HEIGHT,
204
+          width: '100%',
205
+          position: 'absolute',
206
+          bottom: 0,
207
+          left: 0,
208
+          backgroundColor: props.theme.colors.surface,
209
+          transform: [{translateY: state.translateY}],
210
+        }}>
211
+        {icons}
212
+      </Animated.View>
213
+    );
214
+  }
178 215
 }
179 216
 
180 217
 export default withTheme(CustomTabBar);

+ 117
- 90
src/components/Tabbar/TabHomeIcon.js View File

@@ -1,106 +1,133 @@
1 1
 // @flow
2 2
 
3 3
 import * as React from 'react';
4
-import {Image, Platform, View} from "react-native";
4
+import {Image, Platform, View} from 'react-native';
5 5
 import {FAB, TouchableRipple, withTheme} from 'react-native-paper';
6
-import * as Animatable from "react-native-animatable";
6
+import * as Animatable from 'react-native-animatable';
7
+import FOCUSED_ICON from '../../../assets/tab-icon.png';
8
+import UNFOCUSED_ICON from '../../../assets/tab-icon-outline.png';
9
+import type {CustomTheme} from '../../managers/ThemeManager';
7 10
 
8
-type Props = {
9
-    focused: boolean,
10
-    onPress: Function,
11
-    onLongPress: Function,
12
-    theme: Object,
13
-    tabBarHeight: number,
14
-}
11
+type PropsType = {
12
+  focused: boolean,
13
+  onPress: () => void,
14
+  onLongPress: () => void,
15
+  theme: CustomTheme,
16
+  tabBarHeight: number,
17
+};
15 18
 
16 19
 const AnimatedFAB = Animatable.createAnimatableComponent(FAB);
17 20
 
18 21
 /**
19 22
  * Abstraction layer for Agenda component, using custom configuration
20 23
  */
21
-class TabHomeIcon extends React.Component<Props> {
22
-
23
-    focusedIcon = require('../../../assets/tab-icon.png');
24
-    unFocusedIcon = require('../../../assets/tab-icon-outline.png');
25
-
26
-    constructor(props) {
27
-        super(props);
28
-        Animatable.initializeRegistryWithDefinitions({
29
-            fabFocusIn: {
30
-                "0": {
31
-                    scale: 1, translateY: 0
32
-                },
33
-                "0.9": {
34
-                    scale: 1.2, translateY: -9
35
-                },
36
-                "1": {
37
-                    scale: 1.1, translateY: -7
38
-                },
39
-            },
40
-            fabFocusOut: {
41
-                "0": {
42
-                    scale: 1.1, translateY: -6
43
-                },
44
-                "1": {
45
-                    scale: 1, translateY: 0
46
-                },
47
-            }
48
-        });
49
-    }
50
-
51
-    iconRender = ({size, color}) =>
52
-        this.props.focused
53
-            ? <Image
54
-                source={this.focusedIcon}
55
-                style={{width: size, height: size, tintColor: color}}
56
-            />
57
-            : <Image
58
-                source={this.unFocusedIcon}
59
-                style={{width: size, height: size, tintColor: color}}
60
-            />;
61
-
62
-    shouldComponentUpdate(nextProps: Props): boolean {
63
-        return (nextProps.focused !== this.props.focused);
64
-    }
24
+class TabHomeIcon extends React.Component<PropsType> {
25
+  constructor(props: PropsType) {
26
+    super(props);
27
+    Animatable.initializeRegistryWithDefinitions({
28
+      fabFocusIn: {
29
+        '0': {
30
+          scale: 1,
31
+          translateY: 0,
32
+        },
33
+        '0.9': {
34
+          scale: 1.2,
35
+          translateY: -9,
36
+        },
37
+        '1': {
38
+          scale: 1.1,
39
+          translateY: -7,
40
+        },
41
+      },
42
+      fabFocusOut: {
43
+        '0': {
44
+          scale: 1.1,
45
+          translateY: -6,
46
+        },
47
+        '1': {
48
+          scale: 1,
49
+          translateY: 0,
50
+        },
51
+      },
52
+    });
53
+  }
65 54
 
66
-    render(): React$Node {
67
-        const props = this.props;
68
-        return (
69
-            <View
70
-                style={{
71
-                    flex: 1,
72
-                    justifyContent: 'center',
73
-                }}>
74
-                <TouchableRipple
75
-                    onPress={props.onPress}
76
-                    onLongPress={props.onLongPress}
77
-                    borderless={true}
78
-                    rippleColor={Platform.OS === 'android' ? this.props.theme.colors.primary : 'transparent'}
79
-                    style={{
80
-                        position: 'absolute',
81
-                        bottom: 0,
82
-                        left: 0,
83
-                        width: '100%',
84
-                        height: this.props.tabBarHeight + 30,
85
-                        marginBottom: -15,
86
-                    }}
87
-                >
88
-                    <AnimatedFAB
89
-                        duration={200}
90
-                        easing={"ease-out"}
91
-                        animation={props.focused ? "fabFocusIn" : "fabFocusOut"}
92
-                        icon={this.iconRender}
93
-                        style={{
94
-                            marginTop: 15,
95
-                            marginLeft: 'auto',
96
-                            marginRight: 'auto'
97
-                        }}/>
98
-                </TouchableRipple>
99
-            </View>
55
+  shouldComponentUpdate(nextProps: PropsType): boolean {
56
+    const {focused} = this.props;
57
+    return nextProps.focused !== focused;
58
+  }
100 59
 
101
-        );
102
-    }
60
+  getIconRender = ({
61
+    size,
62
+    color,
63
+  }: {
64
+    size: number,
65
+    color: string,
66
+  }): React.Node => {
67
+    const {focused} = this.props;
68
+    if (focused)
69
+      return (
70
+        <Image
71
+          source={FOCUSED_ICON}
72
+          style={{
73
+            width: size,
74
+            height: size,
75
+            tintColor: color,
76
+          }}
77
+        />
78
+      );
79
+    return (
80
+      <Image
81
+        source={UNFOCUSED_ICON}
82
+        style={{
83
+          width: size,
84
+          height: size,
85
+          tintColor: color,
86
+        }}
87
+      />
88
+    );
89
+  };
103 90
 
91
+  render(): React.Node {
92
+    const {props} = this;
93
+    return (
94
+      <View
95
+        style={{
96
+          flex: 1,
97
+          justifyContent: 'center',
98
+        }}>
99
+        <TouchableRipple
100
+          onPress={props.onPress}
101
+          onLongPress={props.onLongPress}
102
+          borderless
103
+          rippleColor={
104
+            Platform.OS === 'android'
105
+              ? props.theme.colors.primary
106
+              : 'transparent'
107
+          }
108
+          style={{
109
+            position: 'absolute',
110
+            bottom: 0,
111
+            left: 0,
112
+            width: '100%',
113
+            height: props.tabBarHeight + 30,
114
+            marginBottom: -15,
115
+          }}>
116
+          <AnimatedFAB
117
+            duration={200}
118
+            easing="ease-out"
119
+            animation={props.focused ? 'fabFocusIn' : 'fabFocusOut'}
120
+            icon={this.getIconRender}
121
+            style={{
122
+              marginTop: 15,
123
+              marginLeft: 'auto',
124
+              marginRight: 'auto',
125
+            }}
126
+          />
127
+        </TouchableRipple>
128
+      </View>
129
+    );
130
+  }
104 131
 }
105 132
 
106
-export default withTheme(TabHomeIcon);
133
+export default withTheme(TabHomeIcon);

+ 101
- 98
src/components/Tabbar/TabIcon.js View File

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

Loading…
Cancel
Save