Skip to content

Instantly share code, notes, and snippets.

@bringking
Last active November 21, 2017 00:54
Show Gist options
  • Save bringking/2a2f4469d8c60fe1a347 to your computer and use it in GitHub Desktop.
Save bringking/2a2f4469d8c60fe1a347 to your computer and use it in GitHub Desktop.
Accordion control built with ReactJS and elierotenberg/react-animate
'use strict';
var React = require('react/addons');
var AnimateMixin = require('react-animate');
/**
* Accordion object that maintains a list of content containers and their collapsed or expanded state
* @type {*|Function}
*/
var Accordion = React.createClass({
/**
* Mixin the AnimateMixin
*/
mixins: [AnimateMixin],
/**
* Get the initial state
* @returns {{itemMap: {}}}
*/
getInitialState: function() {
//map item indexes and their initial states
var itemMap = this.props.items.map(function( i, idx ) {
return {
animating: false,
open: idx === 0,
content:i.content,
header:i.header
};
});
return {
itemMap: itemMap
}
},
/**
* Event handler for clicking on an accordion header
* @param idx
* @param event
*/
toggle: function( idx, event ) {
var _this = this, currentHeight = this.getParentHeight(event),
scrollHeight = this.getParentScrollHeight(event), newHeight,
itemMap = this.state.itemMap;
//toggle animation for this item
itemMap[idx].animating = true;
this.setState({itemMap: itemMap});
//choose the right the new height
newHeight = currentHeight >= 25 ? "25px" : scrollHeight + "px";
//send off to the animation library
this.animate(
idx + "toggle",
{height: currentHeight + "px"},
{height: newHeight},
250,
{
//when it's done, toggle animating bool
onComplete: function() {
var newMap = _this.state.itemMap;
newMap[idx].animating = false;
newMap[idx].open = newHeight !== "25px";
_this.setState({itemMap: newMap});
}
}
);
},
/**
* Get the clientHeight of the parent element from a triggered event
* @param event
* @returns {number}
*/
getParentHeight: function( event ) {
return event.target.parentNode.clientHeight;
},
/**
* Get the scrollHeight of the parent element from a trigger event
* @param event
* @returns {number}
*/
getParentScrollHeight: function( event ) {
return event.target.parentNode.scrollHeight;
},
/**
* Define our default header style
* @returns {{height: string, backgroundColor: string, cursor: string}}
*/
getItemHeaderStyle: function() {
return {
height: "25px",
backgroundColor: "#f9f9f9",
cursor: "pointer"
};
},
/**
*The default style for each accordion item
*/
getDefaultItemStyle: function() {
return {
borderRadius: "3px",
marginBottom: "5px",
overflow: "hidden",
border: "1px solid #cecece"
}
},
/**
* Render
* @returns {XML}
*/
render: function() {
var _this = this;
var items = this.props.items;
//add the content to the accordion container
var contents = items.map(function( i, idx ) {
//calculate the current style
var itemStyle = _this.getDefaultItemStyle();
if ( _this.state.itemMap[idx].animating ) {
itemStyle.height = _this.getAnimatedStyle(idx + "toggle").height;
} else {
itemStyle.height = _this.state.itemMap[idx].open ? "auto" : "25px"
}
return <div style={itemStyle} className={_this.props.itemClassName} key={idx}>
<div style={_this.getItemHeaderStyle()} onClick={_this.toggle.bind(_this, idx)}>
{i.header}
</div>
{i.content}
</div>
});
return (
<div className={this.props.className}>
{contents}
</div>
);
}
});
module.exports = Accordion;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment