src/react/dropdown/DropDown.js
// @flow
import React from 'react'
import DropDownTitle from './DropDownTitle'
import DropDownContent from './DropDownContent'
import PropTypes from 'prop-types'
export default class DropDown extends React.Component<{className?: string, children: *}, *> {
state = {open: false}
onToggle = () => this.toggle()
ref: ?HTMLElement
refPuller = (r: ?HTMLElement) => {
this.ref = r
}
clickListener = (event: Event) => {
let target = event.target
while (target && target.classList) {
if (target === this.ref) return
target = target.parentNode || null
}
this.close()
}
escListener = (event: Event) => {
if (event.keyCode !== 27) return
this.close()
}
getChildContext () {
return {closeDropDown: () => this.close()}
}
componentWillUnmount () {
document.removeEventListener('click', this.clickListener)
document.removeEventListener('keyup', this.escListener)
}
componentDidMount () {
document.addEventListener('click', this.clickListener)
document.addEventListener('keyup', this.escListener)
}
close () {
this.setState({open: false})
}
toggle () {
this.setState(({open}) => ({open: !open}))
}
render () {
const children = React.Children.toArray(this.props.children)
const title = children.find(i => i.type === DropDownTitle)
if (!title) throw new Error('Drop-down title not provided')
const content = children.find(i => i.type === DropDownContent)
if (!content) throw new Error('Drop-down content not found')
return <div
ref={this.refPuller}
className={'dropDown ' + (this.state.open ? 'open ' : '') + (this.props.className ? this.props.className : '')}>
{React.cloneElement(title, {onClick: this.onToggle})}
{this.state.open ? content : null}
</div>
}
}
DropDown.childContextTypes = {
closeDropDown: PropTypes.func.isRequired
}