2020-04-09 00:29:13 +03:00
|
|
|
import * as React from "react";
|
|
|
|
|
|
|
|
import { isMobileBrowser } from "~/common/window";
|
|
|
|
|
|
|
|
export default class WebRectBoundary extends React.PureComponent {
|
|
|
|
static defaultProps = {
|
|
|
|
className: undefined,
|
|
|
|
captureResize: true,
|
|
|
|
captureScroll: true,
|
|
|
|
children: null,
|
|
|
|
enabled: false,
|
|
|
|
isDataMenuCaptured: false,
|
|
|
|
onOutsideRectEvent: () => {},
|
|
|
|
};
|
|
|
|
|
|
|
|
_root = undefined;
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
if (!this.props.enabled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._addListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
|
|
|
this._removeListeners();
|
|
|
|
}
|
|
|
|
|
|
|
|
UNSAFE_componentWillReceiveProps(props) {
|
|
|
|
if (props.enabled) {
|
|
|
|
this._addListeners();
|
|
|
|
} else {
|
|
|
|
this._removeListeners();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_addListeners = () => {
|
|
|
|
this._removeListeners();
|
|
|
|
|
|
|
|
window.setTimeout(() => {
|
|
|
|
if (this.props.onOutsideRectEvent) {
|
|
|
|
if (isMobileBrowser()) {
|
|
|
|
window.addEventListener("touchstart", this._handleOutsideClick);
|
|
|
|
} else {
|
|
|
|
window.addEventListener("click", this._handleOutsideClick);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (this.props.captureResize) {
|
|
|
|
window.addEventListener("resize", this._handleWindowResize);
|
|
|
|
}
|
|
|
|
if (this.props.captureScroll) {
|
|
|
|
window.addEventListener("scroll", this._handleWindowScroll);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
_handleOutsideClick = (e) => {
|
|
|
|
// NOTE(jim): anything with `data-menu` is also ignored...
|
|
|
|
if (!e.target) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
this.props.isDataMenuCaptured &&
|
|
|
|
typeof e.target.hasAttribute === "function" &&
|
|
|
|
e.target.hasAttribute("data-menu")
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (
|
|
|
|
this.props.isDataMenuCaptured &&
|
|
|
|
e.target.parentNode &&
|
|
|
|
typeof e.target.parentNode.hasAttribute === "function" &&
|
|
|
|
e.target.parentNode.hasAttribute("data-menu")
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._root && !this._root.contains(e.target)) {
|
|
|
|
this._handleOutsideRectEvent(e);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
_handleWindowResize = (e) => this._handleOutsideRectEvent(e);
|
|
|
|
_handleWindowScroll = (e) => this._handleOutsideRectEvent(e);
|
|
|
|
|
|
|
|
_removeListeners = () => {
|
|
|
|
window.removeEventListener("touchstart", this._handleOutsideClick);
|
|
|
|
window.removeEventListener("click", this._handleOutsideClick);
|
|
|
|
window.removeEventListener("resize", this._handleWindowResize);
|
|
|
|
window.removeEventListener("scroll", this._handleWindowScroll);
|
|
|
|
};
|
|
|
|
|
|
|
|
_handleOutsideRectEvent = (e) => {
|
|
|
|
this.props.onOutsideRectEvent(e);
|
|
|
|
};
|
|
|
|
|
|
|
|
render() {
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className={this.props.className}
|
|
|
|
ref={(c) => {
|
|
|
|
this._root = c;
|
|
|
|
}}
|
|
|
|
style={this.props.style}
|
2020-08-20 08:45:43 +03:00
|
|
|
onClick={this.props.onClick}
|
|
|
|
>
|
2020-04-09 00:29:13 +03:00
|
|
|
{this.props.children}
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|