import { h, Component, render, toChildArray } from 'preact'; /** Redirect rendering of descendants into the given CSS selector. * @example * *
I am rendered into document.body
*
*/ 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(, this.into, this.remote); } this.into = this.findNode(this.props.into); } this.remote = render( {(show && this.props.children) || null} , 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; } }