react-native-side-reveal-menu
Version:
Animated menu component for React Native.
170 lines (150 loc) • 5.41 kB
JavaScript
import React from 'react';
import { View, StyleSheet, Animated, Easing, Text } from 'react-native';
import { shadeColor } from './utils/colorUtils';
import MenuItem from './menuItem';
export default class Menu extends React.Component {
static propTypes = {
onShow: React.PropTypes.func,
onHide: React.PropTypes.func,
borderRadius: React.PropTypes.number,
showActiveItem: React.PropTypes.bool,
showItemsSeparator: React.PropTypes.bool,
inactiveItemColor: React.PropTypes.string,
activeItemColor: React.PropTypes.string,
itemsDistribution: React.PropTypes.oneOf(['top', 'center', 'bottom', 'space-between', 'space-around']),
itemAnimDuration: React.PropTypes.number,
itemAnimDelay: React.PropTypes.number,
itemAnimEasing: React.PropTypes.func,
}
static defaultProps = {
onShow: () => { },
onHide: () => { },
borderRadius: null,
showActiveItem: true,
showItemsSeparator: true,
inactiveItemColor: '#33334C',
activeItemColor: '#D64A73',
itemsDistribution: 'top',
itemAnimDuration: 150,
itemAnimDelay: 50,
itemAnimEasing: Easing.inOut(Easing.ease)
}
constructor(props) {
super(props);
this.state = {
activeItemIndex: null,
isOpened: true
}
}
componentDidMount() {
this.setState({
isOpened: this.props.isOpened
});
}
componentDidUpdate(prevProps, prevState) {
if (this.props.isOpened != this.state.isOpened) {
this.setState({
isOpened: this.props.isOpened
});
this.props.isOpened ? this.show() : this.hide();
}
}
startAnimation(_toValue) {
let _self = this;
this.children.forEach(function (child, index) {
this.refs[child.ref].animate(
_toValue,
index * this.props.itemAnimDelay,
this.props.itemAnimDuration,
this.props.itemAnimEasing);
}, this);
}
show() {
this.startAnimation(0);
this.props.onShow();
}
hide() {
this.startAnimation(90);
this.props.onHide();
}
onItemPress(pressedChildRef) {
this.children.forEach(function (child, index) {
if (child.ref === pressedChildRef) {
this.setState({
activeItemIndex: index
});
}
}, this);
}
computeStyle() {
let style = {};
switch (this.props.itemsDistribution) {
case 'top':
style = {
top: 0
}
break;
case 'center':
style = {
top: 0,
bottom: 0,
justifyContent: 'center'
}
break;
case 'bottom':
style = {
bottom: 0
}
break;
case 'space-between':
style = {
top: 0,
bottom: 0,
justifyContent: 'space-between'
}
break;
case 'space-around':
style = {
top: 0,
bottom: 0,
justifyContent: 'space-around'
}
break;
}
return style;
}
render() {
//child elements are created dynamically so we need to clone them and add ref and key props so we can reference them later on
this.children = this.props.children.map((child, index) => {
return React.cloneElement(child, {
ref: 'item' + index,
key: 'item' + index,
isFirst: index == 0,
isLast: index == this.props.children.length - 1,
isActive: this.props.showActiveItem && index === this.state.activeItemIndex,
roundAll: this.props.itemsDistribution === 'space-between' || this.props.itemsDistribution == 'space-around',
onItemPress: child => this.onItemPress('item' + index),
isOpened: this.props.isOpened,
borderRadius: this.props.borderRadius,
showItemsSeparator: this.props.showItemsSeparator,
activeItemColor: this.props.activeItemColor,
inactiveItemColor: this.props.inactiveItemColor,
inactiveItemColorLight: shadeColor(this.props.inactiveItemColor, 5),
inactiveItemColorDark: shadeColor(this.props.inactiveItemColor, -10),
activeItemColorLight: shadeColor(this.props.activeItemColor, 5),
activeItemColorDark: shadeColor(this.props.activeItemColor, -10),
});
});
return (
<View style={[styles.menu, this.computeStyle()]} ref={component => this.menu = component}>
{this.children}
</View>
);
}
}
const styles = StyleSheet.create({
menu: {
position: 'absolute',
left: 0,
}
});