JSFiddle - React, Tailwind, and code Playground
by mcsf
HTML
<meta charset=utf8>
<title>Counter</title>
<body>
<p data-setup="counter"></p>
<button data-setup="countIncrementer">count++</button>
<button data-setup="multiplierIncrementer">multiplier++</button>
<script>
console.log('out')
window.addEventListener('framework-ready', () => {
console.log('in')
const count = new Datum(2)
const multiplier = new Datum(5)
const product = new DerivedDatum([count, multiplier], (a, b) => a * b)
const setup = {
counter(element) {
element.append(...tpl `${count} Ă— ${multiplier} = ${product}`)
},
countIncrementer(element) {
element.onclick = () => count.set(count.get() + 1)
},
multiplierIncrementer(element) {
element.onclick = () => multiplier.set(multiplier.get() + 1)
},
}
})
</script>
</body>
JavaScript
class Datum {
constructor(value) {
this.value = value
this.listeners = []
}
subscribe(callback) {
this.listeners.push(callback)
}
get() {
return this.value
}
set(value) {
this.value = value
this.listeners.forEach(l => l(value))
}
}
class DerivedDatum extends Datum {
constructor(deps, valueFn) {
function getValue() {
return valueFn(...deps.map(d => d.get()))
}
super(getValue())
deps.forEach(d => d.subscribe(() => {
const value = getValue()
this.value = value
this.listeners.forEach(l => l(value))
}))
}
set(value) {
throw new TypeError('Unsupported method')
}
}
const tpl = (() => {
const T = value => document.createTextNode(value)
const D = datum => {
const node = document.createTextNode(datum.get())
datum.subscribe(value => node.textContent = value)
return node
}
return function tpl(strings, ...datums) {
return strings.flatMap((string, i) => [
T(string),
i < strings.length - 1 ? D(datums[i]) : undefined,
].filter(Boolean))
}
})()
window.addEventListener('load', () => {
for (const element of document.querySelectorAll('[data-setup]')) {
const methodName = element.dataset.setup
setup[methodName](element)
}
})