2019-06-18 10:57:36 -07:00
|
|
|
import React, { Component } from 'react'
|
|
|
|
import PropTypes from 'prop-types'
|
|
|
|
import h from 'react-hyperscript'
|
|
|
|
import { findDOMNode } from 'react-dom'
|
|
|
|
import { CSSTransitionGroup } from 'react-transition-group'
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
export default class MenuDroppo extends Component {
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
constructor (props) {
|
|
|
|
super(props)
|
|
|
|
this.menuDroppoContainer = React.createRef()
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
static propTypes = {
|
|
|
|
speed: PropTypes.string,
|
|
|
|
useCssTransition: PropTypes.bool,
|
|
|
|
zIndex: PropTypes.number,
|
|
|
|
isOpen: PropTypes.bool,
|
|
|
|
innerStyle: PropTypes.object,
|
|
|
|
children: PropTypes.array,
|
|
|
|
onClickOutside: PropTypes.func,
|
|
|
|
style: PropTypes.object,
|
|
|
|
constOverflow: PropTypes.bool,
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
render () {
|
|
|
|
const speed = this.props.speed || '300ms'
|
|
|
|
const useCssTransition = this.props.useCssTransition
|
|
|
|
const zIndex = ('zIndex' in this.props) ? this.props.zIndex : 0
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
this.manageListeners()
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
const style = this.props.style || {}
|
|
|
|
if (!('position' in style)) {
|
|
|
|
style.position = 'fixed'
|
|
|
|
}
|
|
|
|
style.zIndex = zIndex
|
|
|
|
style.overflow = 'hidden'
|
|
|
|
|
|
|
|
return (
|
|
|
|
h('.menu-droppo-container', {
|
|
|
|
ref: this.menuDroppoContainer,
|
|
|
|
style,
|
|
|
|
}, [
|
|
|
|
// this.renderPrimary(),
|
|
|
|
useCssTransition
|
|
|
|
? h(CSSTransitionGroup, {
|
|
|
|
className: 'css-transition-group',
|
|
|
|
transitionName: 'menu-droppo',
|
|
|
|
transitionEnterTimeout: parseInt(speed),
|
|
|
|
transitionLeaveTimeout: parseInt(speed),
|
|
|
|
}, this.renderPrimary())
|
|
|
|
: this.renderPrimary(),
|
|
|
|
])
|
|
|
|
)
|
2018-08-29 05:54:01 -07:00
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
renderPrimary () {
|
|
|
|
const isOpen = this.props.isOpen
|
|
|
|
if (!isOpen) {
|
|
|
|
return null
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
const innerStyle = this.props.innerStyle || {}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
return (
|
|
|
|
h('.menu-droppo', {
|
|
|
|
key: 'menu-droppo-drawer',
|
|
|
|
style: innerStyle,
|
|
|
|
},
|
|
|
|
[ this.props.children ])
|
|
|
|
)
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
manageListeners () {
|
|
|
|
const isOpen = this.props.isOpen
|
|
|
|
const onClickOutside = this.props.onClickOutside
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
if (isOpen) {
|
|
|
|
this.outsideClickHandler = onClickOutside
|
|
|
|
} else if (isOpen) {
|
|
|
|
this.outsideClickHandler = null
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
componentDidMount () {
|
|
|
|
if (this && document.body) {
|
|
|
|
this.globalClickHandler = this.globalClickOccurred.bind(this)
|
|
|
|
document.body.addEventListener('click', this.globalClickHandler)
|
|
|
|
// eslint-disable-next-line react/no-find-dom-node
|
2020-04-14 02:18:53 -07:00
|
|
|
const container = findDOMNode(this)
|
2019-06-18 10:57:36 -07:00
|
|
|
this.container = container
|
|
|
|
}
|
2018-09-10 02:14:04 -07:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
this.transitionStarted = this.transitionstartOccured.bind(this)
|
2018-10-26 10:30:51 -07:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
/*
|
|
|
|
* transitionstart event is not supported in Chrome yet. But it works for Firefox 53+.
|
|
|
|
* We need to handle this event only for FF because for Chrome we've hidden scrolls.
|
|
|
|
*/
|
|
|
|
this.menuDroppoContainer.current.addEventListener('transitionstart', this.transitionStarted)
|
2018-09-10 02:14:04 -07:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
this.transitionEnded = this.transitionendOccured.bind(this)
|
2018-10-26 10:30:51 -07:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
this.menuDroppoContainer.current.addEventListener('transitionend', this.transitionEnded)
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
componentWillUnmount () {
|
|
|
|
if (this && document.body) {
|
|
|
|
document.body.removeEventListener('click', this.globalClickHandler)
|
|
|
|
document.body.removeEventListener('transitionstart', this.transitionStarted)
|
|
|
|
document.body.removeEventListener('transitionend', this.transitionEnded)
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
globalClickOccurred (event) {
|
|
|
|
const target = event.target
|
|
|
|
// eslint-disable-next-line react/no-find-dom-node
|
|
|
|
const container = findDOMNode(this)
|
2017-11-14 08:04:23 -08:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
if (target !== container &&
|
|
|
|
!this.isDescendant(this.container, event.target) &&
|
|
|
|
this.outsideClickHandler) {
|
|
|
|
this.outsideClickHandler(event)
|
|
|
|
}
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
transitionstartOccured (event) {
|
|
|
|
this.menuDroppoContainer.current.style.overflow = 'hidden'
|
|
|
|
}
|
2018-10-26 10:30:51 -07:00
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
transitionendOccured (event) {
|
|
|
|
if (!this.props.constOverflow) {
|
|
|
|
this.menuDroppoContainer.current.style.overflow = 'auto'
|
|
|
|
}
|
2018-10-26 10:30:51 -07:00
|
|
|
}
|
|
|
|
|
2019-06-18 10:57:36 -07:00
|
|
|
isDescendant (parent, child) {
|
2020-04-14 02:18:53 -07:00
|
|
|
let node = child.parentNode
|
2019-06-18 10:57:36 -07:00
|
|
|
while (node !== null) {
|
|
|
|
if (node === parent) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
node = node.parentNode
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
2019-06-18 10:57:36 -07:00
|
|
|
|
|
|
|
return false
|
2017-11-14 08:04:23 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|