1
0
mirror of https://github.com/chinchang/web-maker.git synced 2025-05-11 04:45:42 +02:00
2019-07-16 20:48:08 +05:30

72 lines
1.6 KiB
JavaScript

import { h, Component, render, toChildArray } from 'preact';
/** Redirect rendering of descendants into the given CSS selector.
* @example
* <Portal into="body">
* <div>I am rendered into document.body</div>
* </Portal>
*/
export default class Portal extends Component {
componentDidUpdate(props) {
for (let i in props) {
if (props[i] !== this.props[i]) {
return setTimeout(this.renderLayer);
}
}
}
componentDidMount() {
this.isMounted = true;
this.renderLayer = this.renderLayer.bind(this);
this.renderLayer();
}
componentWillUnmount() {
this.renderLayer(false);
this.isMounted = false;
if (this.remote && this.remote.parentNode)
this.remote.parentNode.removeChild(this.remote);
}
findNode(node) {
return typeof node === 'string' ? document.querySelector(node) : node;
}
renderLayer(show = true) {
if (!this.isMounted) return;
// clean up old node if moving bases:
if (this.props.into !== this.intoPointer) {
this.intoPointer = this.props.into;
if (this.into && this.remote) {
this.remote = render(<PortalProxy />, this.into, this.remote);
}
this.into = this.findNode(this.props.into);
}
this.remote = render(
<PortalProxy context={this.context}>
{(show && this.props.children) || null}
</PortalProxy>,
this.into,
this.remote
);
}
render() {
return null;
}
}
// high-order component that renders its first child if it exists.
// used as a conditional rendering proxy.
class PortalProxy extends Component {
getChildContext() {
return this.props.context;
}
render({ children }) {
const arr = toChildArray(children);
return (arr && arr[0]) || null;
}
}