diff --git a/.gitignore b/.gitignore index b0e3907..61c8fa4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ node_modules npm-debug.log .DS_Store +.idea diff --git a/package.json b/package.json index 9e625de..614fb8b 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ }, "homepage": "https://github.com/naoufal/react-native-accordion", "dependencies": { - "react-tween-state": "0.0.5" + "react-tween-state": "0.1.5", + "lodash": "^4.16.6" } } diff --git a/src/index.js b/src/index.js index 8da182b..0b6ea93 100644 --- a/src/index.js +++ b/src/index.js @@ -4,121 +4,168 @@ import React, { PropTypes } from 'react'; import tweenState from 'react-tween-state'; import { - StyleSheet, - TouchableHighlight, - View, - Text + StyleSheet, + TouchableHighlight, + View, + Text } from 'react-native'; +var _ = require('lodash'); + var Accordion = React.createClass({ - mixins: [tweenState.Mixin], - - propTypes: { - activeOpacity: React.PropTypes.number, - animationDuration: React.PropTypes.number, - content: React.PropTypes.element.isRequired, - easing: React.PropTypes.string, - expanded: React.PropTypes.bool, - header: React.PropTypes.element.isRequired, - onPress: React.PropTypes.func, - underlayColor: React.PropTypes.string, - style: React.PropTypes.object - }, - - getDefaultProps() { - return { - activeOpacity: 1, - animationDuration: 300, - easing: 'linear', - expanded: false, - underlayColor: '#000', - style: {} - }; - }, - - getInitialState() { - return { - is_visible: false, - height: 0, - content_height: 0 - }; - }, - - close() { - this.state.is_visible && this.toggle(); - }, - - open() { - !this.state.is_visible && this.toggle(); - }, - - toggle() { - this.state.is_visible = !this.state.is_visible; - - this.tweenState('height', { - easing: tweenState.easingTypes[this.props.easing], - duration: this.props.animationDuration, - endValue: this.state.height === 0 ? this.state.content_height : 0 - }); - }, - - _onPress() { - this.toggle(); - - if (this.props.onPress) { - this.props.onPress.call(this); - } - }, - - _getContentHeight() { - if (this.refs.AccordionContent) { - this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => { - // Sets content height in state - this.setState({ - height: this.props.expanded ? height : 0, - content_height: height + mixins: [tweenState.Mixin], + + propTypes: { + activeOpacity: React.PropTypes.number, + animationDuration: React.PropTypes.number, + content: React.PropTypes.element.isRequired, + easing: React.PropTypes.string, + expanded: React.PropTypes.bool, + header: React.PropTypes.element.isRequired, + headerOpen: React.PropTypes.element, + onPress: React.PropTypes.func, + underlayColor: React.PropTypes.string, + style: React.PropTypes.object, + styleOpen: React.PropTypes.object, + }, + + getDefaultProps() { + return { + activeOpacity: 1, + animationDuration: 300, + easing: 'linear', + expanded: false, + underlayColor: '#000', + style: {} + }; + }, + + getInitialState() { + return { + is_visible: this.props.expanded, + height: 0, + content_height: 0 + }; + }, + + close() { + this.state.is_visible && this.toggle(); + }, + + open() { + !this.state.is_visible && this.toggle(); + }, + + toggle() { + this.state.is_visible = !this.state.is_visible; + + this.tweenState('height', { + easing: tweenState.easingTypes[this.props.easing], + duration: this.props.animationDuration, + endValue: this.state.height === 0 ? this.state.content_height : 0, + onEnd: () => {this.props.onEnd && this.props.onEnd()} }); - }); + }, + + _onPress() { + this.toggle(); + + if (this.props.onPress) { + this.props.onPress.call(this); + } + }, + + _onLongPress() { + if (this.props.onLongPress) { + this.props.onLongPress.call(this); + } + }, + + _getContentHeight(onlyOpen = false) { + if (this.refs.AccordionContent) { + this.refs.AccordionContent.measure((ox, oy, width, height, px, py) => { + // Sets content height in state + if(onlyOpen) + { + this.setState({ + height: height, + content_height: height + }); + } + else + { + this.setState({ + height: this.props.expanded ? height : 0, + content_height: height + }); + } + + }); + } + }, + + componentWillReceiveProps(nextProps) { + // Recalculate the height only if the Accordion is open + if(!this.state.is_visible) + { + setTimeout(this._getContentHeight); + } + }, + + + /** + * Check if our accordion has received new content (children). If yes, and the current item is + * open recalculate the height of that open item + * + * @param prevProps + * @param prevState + */ + componentDidUpdate(prevProps, prevState) { + if(this.state.is_visible && !_.isEqual(prevProps.content, this.props.content)){ + setTimeout(() => this._getContentHeight(true)); + } + }, + + componentDidMount() { + // Gets content height when component mounts + // without setTimeout, measure returns 0 for every value. + // See https://github.com/facebook/react-native/issues/953 + setTimeout(this._getContentHeight); + }, + + render() { + return ( + /*jshint ignore:start */ + + + {this.state.is_visible && this.props.headerOpen ? this.props.headerOpen : this.props.header} + + {}} + > + {}}> + {this.props.content} + + + + /*jshint ignore:end */ + ); } - }, - - componentDidMount() { - // Gets content height when component mounts - // without setTimeout, measure returns 0 for every value. - // See https://github.com/facebook/react-native/issues/953 - setTimeout(this._getContentHeight); - }, - - render() { - return ( - /*jshint ignore:start */ - - - {this.props.header} - - - - {this.props.content} - - - - /*jshint ignore:end */ - ); - } }); module.exports = Accordion;