Preact Portal Example
by Jason Miller
HTML
<script src="https://npmcdn.com/preact"></script>
<script src="https://npmcdn.com/preact-portal"></script>
CSS
html, body {
font: 14px/1.5 'Helvetica Neue', helvetica, arial, sans-serif;
background: #F4F5F9;
}
h-box {
display: flex;
}
h-box > * {
flex: 1;
}
section {
position: relative;
border: 1px solid #CCC;
background: #EEE;
margin: 5px;
padding: 5px;
}
.menu {
position: absolute;
left: 5px;
top: 5px;
display: inline-block;
margin: 5px;
padding: 0;
background: #FFF;
border-radius: 3px;
box-shadow: 0 1px 5px rgba(0,0,0,0.3);
z-index: 100;
transform-origin: 0 0;
animation: in forwards 250ms ease;
}
@keyframes in {
from { opacity:0; transform:scale(0.01); }
}
.menu li {
padding: 5px 40px 5px 20px;
color: #555;
list-style: none;
}
.menu li:hover {
background: #DDD;
}
Babel + JSX
const { h, Component, render } = preact;
const Portal = preactPortal;
/** @jsx h */
// we want to render this component elsewhere in the DOM
class Menu extends Component {
render({ left, top, items }) {
return (
<ul class="menu" style={{left, top}}>
{ items.map( item => (
<li>{ item }</li>
)) }
</ul>
);
}
}
class Toolbar extends Component {
state = {
open: false,
into: 'body',
items: ['Item 1', 'Item 2', 'Item 3']
};
toggle = () => {
this.setState({ open: !this.state.open, left:null, top:null });
};
openPinned = e => {
this.setState({
open: true, into:'body',
left:e.pageX, top:e.pageY
});
};
moveInto(into) {
return () => this.setState({ open:true, into, left:null, top:null });
}
render({ }, { items, open, into, left, top }) {
return (
<div>
<button onClick={this.toggle}>Toggle Menu</button>
<button onClick={this.openPinned}>Open Pinned</button>
<div>
Into:
<button onClick={this.moveInto('body')}>{'into="body"'}</button>
<button onClick={this.moveInto('#a')}>{'into="#a"'}</button>
<button onClick={this.moveInto('#b')}>{'into="#b"'}</button>
</div>
{ open ? (
<Portal into={into}>
<Menu {...{left,top,items}} />
</Portal>
) : null }
</div>
);
}
}
class App extends Component {
render() {
return (
<div>
<h1>{'preact-portal Example'}</h1>
<p>The <Portal> component lets you render an arbitrary part of your JSX tree elsewhere in the DOM, without giving up diffing, mounting, etc</p>
<Toolbar />
<h-box>
<section id="a">
<h2>A</h2>
</section>
<section id="b">
<h2>B</h2>
</section>
</h-box>
<h2>How it works:</h2>
<pre>{
`<div>
<p>I am rendered normally, wherever mounted.</p>
<Portal into="body">
<p>I am rendered into document.body</p>
</Portal>
</div>`
}</pre>
</div>
);
}
}
render(<App />, document.body);