mirror of
https://github.com/chinchang/web-maker.git
synced 2025-05-11 04:45:42 +02:00
72 lines
1.6 KiB
JavaScript
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;
|
|
}
|
|
}
|