Files
flood/client/source/scripts/components/forms/Dropdown.js
2016-04-23 14:33:40 -07:00

158 lines
3.5 KiB
JavaScript

import _ from 'lodash';
import classnames from 'classnames';
import CSSTransitionGroup from 'react-addons-css-transition-group';
import React from 'react';
const METHODS_TO_BIND = [
'getDropdownButton',
'getDropdownMenu',
'getDropdownMenuItems',
'handleDropdownBlur',
'handleDropdownClick',
'handleDropdownFocus',
'handleItemSelect',
'handleKeyPress'
];
export default class Dropdown extends React.Component {
constructor() {
super();
this.state = {
isExpanded: false
};
METHODS_TO_BIND.forEach((method) => {
this[method] = this[method].bind(this);
});
this.handleKeyPress = _.throttle(this.handleKeyPress, 1000);
}
componentDidMount() {
window.addEventListener('keydown', this.handleKeyPress);
}
componentWillUnmount() {
window.removeEventListener('keydown', this.handleKeyPress);
}
closeDropdown() {
this.refs.dropdown.blur();
}
handleDropdownBlur() {
this.setState({
isExpanded: false
});
}
handleDropdownClick() {
if (this.state.isExpanded) {
this.closeDropdown();
} else {
this.refs.dropdown.focus();
}
}
handleDropdownFocus(event) {
this.setState({
isExpanded: true
});
}
handleItemSelect(item) {
this.closeDropdown();
this.props.handleItemSelect(item);
}
handleKeyPress(event) {
if (this.state.isExpanded && event.keyCode === 27) {
this.closeDropdown();
}
}
getDropdownButton() {
return (
<div className={this.props.dropdownButtonClass} onClick={this.handleDropdownClick}>
{this.props.header}
</div>
);
}
getDropdownMenu(items) {
let dropdownLists = items.map((itemList, index) => {
return (
<div className="dropdown__list" key={index}>
{this.getDropdownMenuItems(itemList)}
</div>
);
}, this);
return (
<div className="dropdown__content menu">
<div className="dropdown__header">
{this.getDropdownButton()}
</div>
<ul className="dropdown__items">
{dropdownLists}
</ul>
</div>
);
}
getDropdownMenuItems(listItems) {
return listItems.map((property, index) => {
let classes = classnames('dropdown__item menu__item', property.className, {
'is-selectable': property.selectable !== false,
'is-selected': property.selected
});
let clickHandler = null;
if (property.selectable !== false) {
clickHandler = this.handleItemSelect.bind(this, property);
}
return (
<li className={classes} key={index} onClick={clickHandler}>
{property.displayName}
</li>
);
}, this);
}
render() {
let dropdownWrapperClass = classnames({
[this.props.dropdownWrapperClass]: true,
'is-expanded': this.state.isExpanded
});
let menu = null;
if (this.state.isExpanded) {
menu = this.getDropdownMenu(this.props.menuItems);
}
return (
<div className={dropdownWrapperClass} onFocus={this.handleDropdownFocus} onBlur={this.handleDropdownBlur} ref="dropdown" tabIndex="0">
{this.getDropdownButton()}
<CSSTransitionGroup
transitionName="menu"
transitionEnterTimeout={250}
transitionLeaveTimeout={250}>
{menu}
</CSSTransitionGroup>
</div>
);
}
}
Dropdown.defaultProps = {
dropdownWrapperClass: 'dropdown',
dropdownButtonClass: 'dropdown__trigger'
};
Dropdown.propTypes = {
menuItems: React.PropTypes.arrayOf(React.PropTypes.arrayOf(React.PropTypes.object)).isRequired
};