Duckweed counter demo
http://bit.ly/duckweed-framework
by Hajime Yamasaki Vukelic
HTML
<script src="https://npm-cdn.herokuapp.com/duckweed@1.0.0/dist/duckweed.js"></script>
<div id="app">
</div>
CSS
body {
font-family: Arial, sans-serif;
font-size: 18px;
text-align: center;
padding: 1rem;
}
.root {
padding: 10px;
background: #ddd;
display: inline-block;
border-radius: 0.5rem;
margin-right: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.inc,
.dec {
vertical-align: middle;
border: 0;
padding: 0.6rem 1rem;
width: 3rem;
height: 3rem;
font-size: 120%;
margin: 0 0.4rem;
color: white;
cursor: pointer;
border-radius: 5rem;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
outline: 0;
}
.inc {
background: #a9c11d;
}
.dec {
background: #ea4141;
}
.disp {
display: inline-block;
margin: 0 0.4rem;
font-size: 160%;
vertical-align: middle;
font-weight: bold;
color: #44aaee;
}
.sum {
font-size: 160%;
}
img {
width: 200px;
}
Babel + JSX
/** @jsx duckweed.html */
// We'll simulate modules using IIFE and module pattern
const wrap = obj => ({
map: fn => wrap(fn(obj)),
unwrap: () => obj,
});
const counter = (() => {
const init = () => ({count: 0});
const ACTIONS = {
increment: 'increment',
decrement: 'decrement',
};
const addCount = (n, model) => ({...model, count: model.count + n});
const update = (model, address, ...args) => {
switch (address) {
case ACTIONS.increment:
return addCount(1, model);
case ACTIONS.decrement:
return addCount(-1, model);
default:
return model;
}
};
const view = ({model, act, transitionDelay = 0}) => (
<div class="root" style={{opacity: 0, delayed: {opacity: 1}, transition: `opacity ${transitionDelay}s`}}>
<button class="dec" on-click={act("decrement")}>-</button>
<span class="disp">{"" + model.count}</span>
<button class="inc" on-click={act("increment")}>+</button>
</div>
);
return {
init,
update,
view,
};
})();
const list = (() => {
const init = () => ({
counters: [
counter.init(),
counter.init(),
counter.init(),
],
sum: 0,
avg: 0,
});
const update = (model, counterId, ...args) =>
wrap(model)
.map(duckweed.scoped.transform.bind(null,
['counters', counterId],
(counterModel) => counter.update(counterModel, ...args)
))
.map(model1 =>
(sum => ({
...model1,
sum: sum,
avg: (sum / model1.counters.length).toFixed(2),
}))(model1.counters.reduce((tot, cnt) => tot + cnt.count, 0)))
.unwrap();
const view = ({model, act}) => (
<div class="view">
<h1>Duckweed counters</h1>
<div>
{model.counters.map((cnt, id) =>
<counter.view model={cnt} act={act.bind(null, id)} transitionDelay={1 + id * 2} />
)}
</div>
<p class="sum">
...