likeopera-frontend/DropDownBase.js

121 lines
3.6 KiB
JavaScript

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 };
}
}