khufu-runtime
Version:
A runtime support library for khufu template engine
92 lines (83 loc) • 2.92 kB
JavaScript
import {store_handler, cleanup_stores} from './stores'
import {patch, attributes, notifications, symbols} from 'incremental-dom'
import {elementOpen, elementClose, elementVoid, text} from 'incremental-dom'
import {applyAttr, applyProp} from 'incremental-dom'
import stores, {CANCEL} from './stores'
import {add_style} from './style'
import {item} from './dom'
import {SuppressedError} from './errors'
export {CANCEL, add_style, item, SuppressedError,
elementOpen, elementClose, elementVoid, text}
/// Things that can only be assigned as properties
const PROPERTIES = {
"value": "value",
}
// This is different from incremental-dom default, because it sets boolean
// attributes as property instead of attribute. This works better for
// properties like `checked`. May need better heuristics though.
//
// Also some things like "value" do nothing when not applied as properties
function applyAttribute(el, name, value) {
let type = typeof value
let prop = PROPERTIES[name]
if(prop) {
applyProp(el, prop, value)
} else if (type === 'object' || type === 'function' || type == 'boolean') {
applyProp(el, name, value)
} else {
applyAttr(el, name, value)
}
}
function set_global_state(params) {
var old = {
stores: attributes.__stores,
applyAttr: attributes[symbols.default],
deleted: notifications.nodesDeleted,
}
attributes.__stores = store_handler(params)
attributes[symbols.default] = applyAttribute
notifications.nodesDeleted = cleanup_stores
return old
}
function clean_global_state(old) {
notifications.nodesDeleted = old.deleted
attributes[symbols.default] = old.applyAttr
attributes.__stores = old.stores
}
export function attach(element, template, settings) {
if(typeof settings.store !== 'function') {
throw Error("Third argument to khufu must be a settings object " +
"and has `store` function (see http://bit.ly/store_cons)")
}
let params = {...settings, render: queue_render }
let queued = false;
function queue_render() {
if(!queued) {
queued = true;
window.requestAnimationFrame(render)
}
}
function render() {
queued = false;
let obj = set_global_state(params)
try {
patch(element, template)
} catch(e) {
if(e instanceof SuppressedError) {
console.error("Render error (suppressed)", e.original)
try {
patch(element, template)
} catch(e) {
console.error("Successive render error", e)
}
} else {
console.error("Render error", e)
}
}
clean_global_state(obj)
}
render() // Immediate render works better with hot reload
return {
queue_render
}
}