react-native-ui-lib
Version:
[](https://travis-ci.org/wix/react-native-ui-lib) [](https://www.npmjs.com/package/react-native-ui-lib) [![NPM Down
156 lines (138 loc) • 4.05 kB
JavaScript
import React from 'react';
import {StyleSheet, ViewPropTypes, Animated} from 'react-native';
import _ from 'lodash';
import PropTypes from 'prop-types';
import {Colors} from '../../style';
import {BaseComponent} from '../../commons';
import View from '../view';
import TabBarItem from './TabBarItem';
/**
* @description: Basic TabBar component
* @gif: https://media.giphy.com/media/3o751YHFZVlv3Ay4k8/giphy.gif
* @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabBarScreen.js
*/
export default class TabBar extends BaseComponent {
static displayName = 'TabBar';
static propTypes = {
...ViewPropTypes.height,
/**
* current selected tab index
*/
selectedIndex: PropTypes.number,
/**
* custom style for the tab bar
*/
style: ViewPropTypes.style,
/**
* custom style for the selected indicator
*/
indicatorStyle: ViewPropTypes.style,
/**
* disable the animated transition of the tab indicator
*/
disableAnimatedTransition: PropTypes.bool,
/**
* callback for when index has change
*/
onChangeIndex: PropTypes.func,
};
static defaultProps = {
selectedIndex: 0,
height: 51,
};
constructor(props) {
super(props);
const childrenCount = React.Children.count(this.props.children);
this.state = {
selectedIndex: props.selectedIndex,
selectedIndicatorPosition: new Animated.Value(this.calcPosition(props.selectedIndex, childrenCount)),
};
}
generateStyles() {
this.styles = createStyles(this.props);
}
calcPosition(index, tabsCount) {
const position = index * (100 / tabsCount);
return position;
}
calcIndicatorWidth() {
const childrenCount = React.Children.count(this.props.children);
if (childrenCount === 0) {
return '0%';
}
const width = Math.floor(100 / childrenCount);
return `${width}%`;
}
onSelectingTab(index) {
const {disableAnimatedTransition} = this.props;
const {selectedIndicatorPosition} = this.state;
const newPosition = this.calcPosition(index, this.props.children.length);
if (disableAnimatedTransition) {
selectedIndicatorPosition.setValue(newPosition);
} else {
Animated.spring(selectedIndicatorPosition, {
toValue: this.calcPosition(index, this.props.children.length),
tension: 30,
friction: 8,
}).start();
}
this.setState({
selectedIndex: index,
});
_.invoke(this.props, 'onChangeIndex', index);
}
renderChildren() {
const {selectedIndex} = this.state;
const children = React.Children.map(this.props.children, (child, index) => {
return React.cloneElement(child, {
selected: selectedIndex === index,
onPress: () => {
this.onSelectingTab(index);
_.invoke(child.props, 'onPress');
},
});
});
return children;
}
renderSelectedIndicator() {
const {indicatorStyle} = this.props;
const {selectedIndicatorPosition} = this.state;
const width = this.calcIndicatorWidth();
const left = selectedIndicatorPosition.interpolate({
inputRange: [0, 100],
outputRange: ['0%', '100%'],
});
return (
<Animated.View
style={[this.styles.selectedIndicator, this.styles.absoluteContainer, {left, width}, indicatorStyle]}
/>
);
}
render() {
const {height, style} = this.props;
return (
<View style={[this.styles.container, style]} bg-white row height={height} useSafeArea>
{this.renderChildren()}
{this.renderSelectedIndicator()}
</View>
);
}
}
function createStyles() {
return StyleSheet.create({
container: {
borderBottomWidth: StyleSheet.hairlineWidth,
borderColor: Colors.dark70,
},
selectedIndicator: {
borderBottomWidth: 1.5,
borderColor: Colors.blue30,
},
absoluteContainer: {
position: 'absolute',
bottom: 0,
left: 0,
},
});
}
TabBar.Item = TabBarItem;