preact-markup demo
by Arif Hasan
HTML
<script src="https://npmcdn.com/preact@latest"></script>
<script src="https://npmcdn.com/preact-markup@latest"></script>
<script src="https://unpkg.com/linkstate/dist/polyfill.umd.js"></script>
SCSS
.placeholder-component {
position: relative;
display: inline-block;
min-width: 200px;
min-height: 140px;
padding: 15px 5px;
background: #EEE;
border: 1px solid #CCC;
border-radius: 3px;
color: #999;
text-align: center;
& > h4 {
position: absolute;
left: 5px;
top: 5px;
margin: 0;
}
& > button {
position: absolute;
right: 5px;
top: 5px;
opacity: 0.5;
}
& > pre {
text-align: left;
font-size: 11px;
}
& > .inner {
padding: 5px;
text-align: left;
background: #F4F4F4;
}
}
aside.sidebar {
display: block;
float: right;
width: 200px;
min-height: 100%;
background: #DDD;
margin: 0 0 0 10px;
padding: 10px;
}
header.toolbar {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 56px;
background: #acd;
color: white;
h1 { margin: 0; padding:10px; font-weight:300; }
& ~ * {
margin-top: 56px;
}
}
* {
position: relative;
overflow: hidden;
box-sizing: border-box;
}
li { overflow:visible; }
html,body,#app {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font: 14px/1.21 'Helvetica Neue',arial,helvetica,sans-serif;
}
.toolbar {
height: 40px;
background: #EEE;
border-bottom: 1px solid #DDD;
button {
margin: 4px;
padding: 8px;
background: #E4E4E4;
border: 1px solid #CCC;
color: #555;
font-weight: bold;
border-radius: 3px;
cursor: pointer;
outline: none;
&:hover, &:active { background:#FFF; }
}
}
.editor {
position: absolute;
left: 0;
top: 0;
height: 100%;
width: 50%;
border-right: 1px solid #BBB;
textarea {
position: absolute;
left: 0;
top: 40px;
width: 100%;
bottom: 0;
padding: 10px;
border: none;
background: #FFF;
font: 14px/1.21 'Source Code Pro', menlo, monaco, courier, monospace;
resize: none;
outline: none;
overflow: auto;
}
}
.preview {
position: absolute;
left: 50%;
top: 0;
width: 50%;
height: 100%;
background: #FAFAFA;
overflow: auto;
.padding {
padding: 10px;
}
}
Babel + JSX
const { h, Component, render } = preact;
const Markup = preactMarkup;
/**@jsx h*/
// Demo: textarea on the left, <Markup> on the right. Renders on change.
class App extends Component {
componentDidMount() {
this.loadExample();
}
// loads HTML example from a Gist
loadExample() {
fetch('https://gist.githubusercontent.com/developit/2c6aa8e40bd3ab251968/raw/c470e7e82fcc3eaeea1e3bc26d0829b7592ebb06/example.html')
.then( r => r.text() )
.then( markup => this.setState({ type:'html', markup }) );
}
// click on the error button to show the error
showError = () => {
alert(this.state.error);
};
// clear error on success (<Markup> fires no event for success)
componentWillUpdate({ }, { markup, type }) {
if (markup!==this.lastMarkup || type!==this.lastType) {
this.state.error = null;
this.lastMarkup = markup;
this.lastType = type;
}
}
render({ }, { markup, type, error }) {
// components to use as custom elements:
let components = {
Sidebar,
Toolbar,
WidgetA,
OtherWidget
};
return (
<div id="app">
<div class="editor">
<div class="toolbar">
<button onClick={::this.loadExample}>Load Example Gist</button>
<select value={type} onChange={this.linkState('type')}>
<option value="xml">XML (Strict)</option>
<option value="html">HTML5</option>
</select>
{ error ? (
<button onClick={::this.showError}>Error</button>
) : null }
</div>
<textarea value={markup} onInput={this.linkState('markup')} />
</div>
<div class="preview">
<Markup
type={type}
markup={markup}
components={components}
onError={this.linkState('error', 'error')} />
</div>
</div>
);
}
}
// creates a dummy component that just
// shows its name and some debug info:
const createPlaceholderComponent = name => ({ children, ...props }) => (
<div class="placeholder-component" data-component={name} {...props}>
<h4>{ name }</h4>
<pre>{...