Skip to content

Instantly share code, notes, and snippets.

@tornqvist
Last active May 19, 2020 19:02
Show Gist options
  • Save tornqvist/78af058bd9e0c8a455f053703510bf74 to your computer and use it in GitHub Desktop.
Save tornqvist/78af058bd9e0c8a455f053703510bf74 to your computer and use it in GitHub Desktop.
A series of examples illustrating a new component API I've been working on in conjunction with changes to nanohtml
var { html, render } = require('nanohtml')
var { Component, memo, onupdate } = require('nanohtml/component')
// This example illustrates how memo can be used to maintain form state.
// On first render, getInitialValues is called and read values either
// from the fields argument or from local storage.
// Whenever a field changes its value is persisted to local storage.
var Form = Component(function (fields, values = memo(getInitialValues)) {
var update = onupdate(function (fields, values) {
if (values) {
for (const [key, value] of Object.entries(values)) {
window.localStorage.set(key, value)
}
}
})
return html`
<form>
${fields.map((attrs) => html`
<input ${attrs} value="${values[attrs.name]}" oninput=${(event) => update(fields, { ...values, [attrs.name]: event.target.value })}>
`)}
</form>
`
})
function getInitialValues (fields) {
return fields.reduce(function (acc, attrs) {
acc[attrs.name] = attrs.value || window.localStorage.get(attrs.name) || ''
return acc
}, {})
}
render(html`
<body>
${Form([
{ type: 'text', name: 'firstname' },
{ type: 'text', name: 'lastname' }
])}
</body>
`, document.body)
var mapboxgl = require('mapbox-gl')
var { html, render } = require('nanohtml')
var { Component, memo, onupdate, onload } = require('nanohtml/component')
// This example uses onload to render an interactive map.
// The map instance is memoized as the second argument. The
// instance can accessed with the memo utility.
// It compares the coordinates on update, and if changed,
// pans the map to the new coordinates.
var Map = Component(function ([lng, lat]) {
var update = onupdate(function (prev) {
return function beforeupdate (next, map = memo()) {
// Compare the arguments used to issue an update with the arguments
// used for the previous update to determine if the coordinates have
// changed. If so, pan the map to the new coordinates.
if (map && prev.join() !== next.join()) map.panTo(next)
}
})
onload(function (el) {
var map = new mapboxgl.Map({
container: el,
center: [lng, lat],
style: 'mapbox://styles/mapbox/streets-v11'
})
var onresize = map.resize.bind(map)
// Issue an update to memoize the map instance
update([lng, lat], map)
window.addEventListener('resize', onresize)
return function unload () {
map.remove()
window.removeEventListener('resize', onresize)
}
})
return html`<div></div>`
})
render(html`
<body>
${Map([17.841, 59.326])}
</body>
`, document.body)
var { html, render } = require('nanohtml')
var { Component, memo, onupdate } = require('nanohtml/component')
// This example illustrates how to use the onupdate hook.
// Whenever the component is rendered/updated it starts a timer
// which when expiring renders a "Too slow!" message.
// Clicking the button will reset the timer.
// The time can be overriden by the caller at any time.
var Awake = Component(function (time = memo(5000)) {
var update = onupdate(function (time = memo()) {
if (time > 0) {
var timeout = setTimeout(function () {
update(time - 1000)
}, 1000)
}
// This is called before the next update is executed
return function cleanup () {
clearTimeout(timeout)
}
})
return html`
<h1>Stay awake!</h1>
<p>Click button within ${Math.floor(time / 1000)} seconds</p>
${time > 0 ? html`
<button onclick=${() => update(5000)}>Click me!</button>
` : html`
<p>Too slow!</p>
`}
`
})
render(html`
<body>
${Awake()}
</body>
`, document.body)
var { html, render } = require('nanohtml')
var { Component, Ref } = require('nanohtml/component')
// This example uses a Ref to start playing a video when clicking a button
// The Ref is a proxied _live_ HTMLCollection which serializes to a string.
// By assinging the serialized string as class to child elements, they
// can be accessed by index or id.
var Player = Component(function (uri) {
var ref = new Ref()
return html`
<video src="${uri}" id="player" class="${ref}" type="video/mp4"></video>
<button onclick=${() => ref.player.play()}>Play</button>
`
})
render(html`
<body>
${Player('http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4')}
</body>
`, document.body)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment