redbadger/pride-london-app

View on GitHub
src/components/TopTabBar.js

Summary

Maintainability
C
1 day
Test Coverage
// @flow
import React, { PureComponent } from "react";
import { Animated, StyleSheet, View } from "react-native";
import type { TabScene, _TabBarBottomProps } from "react-navigation";
import Text from "./Text";
import Touchable from "./Touchable";
import {
  filterButtonTextColor,
  filterButtonsBgColor,
  whiteColor,
  topTabBarDividerColor
} from "../constants/colors";

class TopTabBar extends PureComponent<_TabBarBottomProps> {
  constructor() {
    super();
    this.tabBarWidth = new Animated.Value(1);
    this.tabLayouts = {};
    this.activeTabX = new Animated.Value(0);
    this.activeTabWidth = new Animated.Value(0);
  }

  componentDidUpdate() {
    this.updateActiveTabLine();
  }

  handleTabPress = (key: string) => {
    this.props.jumpTo(key);
  };

  tabBarLayout: any;
  tabBarWidth: Animated.Value;
  tabLayouts: { [number]: any };
  activeTabX: Animated.Value;
  activeTabWidth: Animated.Value;

  // TODO function duplicated in NavigationTabBar
  updateActiveTabLine = () => {
    const { navigation } = this.props;
    const currentIndex = navigation.state.index;
    const layout = this.tabLayouts[currentIndex];
    if (!layout || !this.tabBarLayout) {
      return;
    }

    Animated.timing(this.tabBarWidth, {
      toValue: this.tabBarLayout.width,
      duration: 150,
      useNativeDriver: true
    }).start();
    Animated.timing(this.activeTabX, {
      toValue: layout.x,
      duration: 150,
      useNativeDriver: true
    }).start();
    Animated.timing(this.activeTabWidth, {
      toValue: layout.width,
      duration: 150,
      useNativeDriver: true
    }).start();
  };

  renderLabel = (scene: TabScene) => {
    const { focused } = scene;
    const color = focused ? filterButtonTextColor : whiteColor;
    const label = this.props.getLabelText(scene);

    return (
      <Text
        numberOfLines={1}
        style={[{ color }, styles.buttonText]}
        type="small"
      >
        {label}
      </Text>
    );
  };

  render() {
    const { navigation, getTabTestID } = this.props;
    const { routes } = navigation.state;

    const activeTabLineScale = Animated.divide(
      this.activeTabWidth,
      this.tabBarWidth
    );
    const activeTabLineTranslate = Animated.add(
      this.activeTabX,
      Animated.add(
        Animated.divide(this.tabBarWidth, -2),
        Animated.divide(this.activeTabWidth, 2)
      )
    );

    return (
      <View
        onLayout={event => {
          this.tabBarLayout = event.nativeEvent.layout;
          this.updateActiveTabLine();
        }}
        style={styles.tabBar}
        testID="top-tab-bar"
      >
        {routes.map((route, index) => {
          const focused = index === navigation.state.index;
          const scene = { route, index, focused };
          return (
            <Touchable
              key={route.key}
              testID={getTabTestID(route.routeName)}
              onLayout={event => {
                this.tabLayouts[index] = event.nativeEvent.layout;
                this.updateActiveTabLine();
              }}
              onPress={() => this.handleTabPress(route.key)}
              style={[index !== 0 && styles.dividerLine, styles.tab]}
            >
              {this.renderLabel(scene)}
            </Touchable>
          );
        })}
        <Animated.View
          style={[
            styles.activeTabLine,
            {
              transform: [
                { translateX: activeTabLineTranslate },
                { scaleX: activeTabLineScale }
              ]
            }
          ]}
        />
      </View>
    );
  }
}

const styles = StyleSheet.create({
  activeTabLine: {
    backgroundColor: filterButtonTextColor,
    height: 3,
    position: "absolute",
    left: 0,
    top: 0,
    width: "100%"
  },
  tabBar: {
    flexDirection: "row",
    height: 48,
    width: "100%",
    maxWidth: 440,
    alignSelf: "center",
    backgroundColor: filterButtonsBgColor,
    borderTopWidth: StyleSheet.hairlineWidth,
    borderTopColor: filterButtonsBgColor,
    alignItems: "center"
  },
  tab: {
    alignItems: "center",
    flexDirection: "row",
    minWidth: 48,
    flexGrow: 1,
    flexBasis: 1,
    paddingHorizontal: 4,
    paddingTop: 4,
    paddingBottom: 2
  },
  buttonText: {
    fontFamily: "Roboto-Medium",
    textAlign: "center"
  },
  dividerLine: {
    borderLeftWidth: StyleSheet.hairlineWidth,
    borderColor: topTabBarDividerColor,
    height: 48
  }
});

export default TopTabBar;