import React from 'react'; export default class DropDownBase extends React.PureComponent { static instances = {}; static currentVisible = null; state = { visible: false, top: 0, left: 0, calloutLeft: null, selectedItem: -1 }; componentDidMount() { if (!DropDownBase.setBodyListener) { window.addEventListener('click', DropDownBase.hideAll); window.addEventListener('resize', DropDownBase.repositionCurrent); DropDownBase.setBodyListener = true; } DropDownBase.instances[this.props.id] = this; } static hideAll() { for (let i in DropDownBase.instances) DropDownBase.instances[i].hide(); } static repositionCurrent() { if (DropDownBase.currentVisible) DropDownBase.currentVisible[0].showAt(DropDownBase.currentVisible[1], DropDownBase.currentVisible[0].onClose); } componentWillUnmount() { delete DropDownBase.instances[this.props.id]; if (DropDownBase.currentVisible[0] == this) DropDownBase.currentVisible = null; } onClick = (ev) => { ev.stopPropagation(); } isVisible = () => { return this.state.visible; } hide = () => { this.setState({ visible: false }); DropDownBase.currentVisible = null; if (this.onClose) { this.onClose(); delete this.onClose; } } showAt = (el, onClose) => { if (this.onClose && this.onClose != onClose) { this.onClose(); delete this.onClose; } DropDownBase.currentVisible = [ this, el ]; let p = getOffset(el); let left = p.left, top = p.top+el.offsetHeight, calloutLeft = null; this.setState({ visible: true, top: top, left: left, selectedItem: -1 }); this.refs.dd.style.display = 'block'; if (this.props.window) { left = Math.round(p.left+el.offsetWidth/2-this.refs.dd.offsetWidth/2); top = p.top+el.offsetHeight+3; } let ww = window.innerWidth || de.clientWidth || db.clientWidth; let wh = window.innerHeight || de.clientHeight || db.clientHeight; if (left + this.refs.dd.offsetWidth > ww) { left = ww-this.refs.dd.offsetWidth; calloutLeft = Math.round(p.left+el.offsetWidth/2-left); } if (top + this.refs.dd.offsetHeight > wh) top = wh-this.refs.dd.offsetHeight; this.refs.dd.style.display = ''; this.setState({ visible: true, top: top, left: left, calloutLeft: calloutLeft }); this.refs.dd.focus(); this.onClose = onClose; } } function getOffset(elem) { if (elem.getBoundingClientRect) { let box = elem.getBoundingClientRect(); let body = document.body; let docElem = document.documentElement; let scrollTop = window.pageYOffset || docElem.scrollTop || body.scrollTop; let scrollLeft = window.pageXOffset || docElem.scrollLeft || body.scrollLeft; let clientTop = docElem.clientTop || body.clientTop || 0; let clientLeft = docElem.clientLeft || body.clientLeft || 0; let top = box.top + scrollTop - clientTop; let left = box.left + scrollLeft - clientLeft; return { top: Math.round(top), left: Math.round(left) }; } else { let top = 0, left = 0; while (elem) { top = top + parseInt(elem.offsetTop); left = left + parseInt(elem.offsetLeft); elem = elem.offsetParent; } return { top: top, left: left }; } }