BasicsConfiguration: Understanding HyptiotesExample: Hooks (DOM API)Example: Observable (DOM API)Example: Hyptiotes x ReactExample: Demo Wrapper (Markup)
Sources
index.js
const hyptiotes = require("hyptiotes"); const configuration = require("./configuration"); const App = require("./app"); hyptiotes.setElementInitializer(configuration.elementInitializer); hyptiotes.setItemHandlers(configuration.itemHandlers); hyptiotes.setAttributeHandlers(configuration.attributeHandlers); hyptiotes.setElementFinalizer(configuration.elementFinalizer); const domTree = hyptiotes.castWeb(App); document.getElementById("root").appendChild(domTree);
configuration.js
const hyptiotes = require("hyptiotes"); // Hyptiotes base configuration with added function hook interface module.exports = { elementInitializer: hyptiotes.DEFAULT_ELEMENT_INITIALIZER, itemHandlers: [ hyptiotes.PLUGINS.functionToHookInserts, ...hyptiotes.DEFAULT_ITEM_HANDLERS, ], attributeHandlers: hyptiotes.DEFAULT_ATTRIBUTE_HANDLERS, elementFinalizer: hyptiotes.DEFAULT_ELEMENT_FINALIZER, };
app.js
const TodoStore = require("../TodoStore"); module.exports = [ ":div", { id: "main-content" }, [":h1", "Hyptiotes To-Do"], TodoList, AddTodo, ]; function TodoList({ recast, onCleanup }) { onCleanup(TodoStore.subscribe(recast)); return [ ":ul", ...TodoStore.get().map((todo) => { return [":li", todo]; }), ]; } function AddTodo({ recast }) { let inputValue = ""; return [ ":div", [ ":input", { onchange: (e) => { inputValue = e.target.value; }, }, ], [ ":button", { onclick: () => { TodoStore.add(inputValue); recast(); }, }, "Add", ], ]; }
Hook Function Plugin (functionToHookInserts.js)
// Interface to integrate dynamic function powered dom // Function items are called with functional hooks provided as params // These hooks (recast, onRefChange, onCleanup) handle dom access module.exports = { test: (item) => typeof item === "function", handler: ({ item, parent, hyptiotes }) => { contentGenerator(item, parent, hyptiotes.castWeb); }, }; function contentGenerator(generate, parent, cast) { let onUpdate; let cleanupCbs = []; let refCb = null; // Define exposed hooks const hooks = { recast: (v) => onUpdate(v), onRefChange: (cb) => { if (refCb !== null) console.warn("Called onRefChange twice, only last is called"); refCb = (element) => { cb(element); refCb = null; }; }, onCleanup: (fn) => cleanupCbs.push(fn), }; let element = null; function renderer() { onUpdate = lockedUpdate; cleanupCbs.forEach((fn) => fn()); // this should be a mutationobserver to catch removed in subtree cleanupCbs = []; const model = generate(hooks); const updatedElement = cast(model); const next = element && element.nextSibling; if (element) parent.removeChild(element); parent.insertBefore(updatedElement, next); if (refCb) refCb(updatedElement); onUpdate = renderer; element = updatedElement; } function lockedUpdate() { throw new Error("Called update inside render (update is cyclical)"); } // Initial render renderer(); }
Full Configuration
{ "elementInitializer": (tag) => document.createElement(tag.slice(1)), "itemHandlers": [ { "test": (item) => typeof item === "function", "handler": ({ item, parent, hyptiotes }) => { contentGenerator(item, parent, hyptiotes.castWeb); } }, { "test": (item) => item === null || item === undefined, "handler": () => {} }, { "test": (item) => item instanceof HTMLElement, "handler": ({item, parent}) => parent.appendChild(item) }, { "test": item => typeof item === "string", "handler": ({ item, parent }) => { const textNode = document.createTextNode(item); parent.appendChild(textNode); } }, { "test": item => Array.isArray(item) && typeof item[0] === "string" && item[0][0] === ":", "handler": ({item, parent, hyptiotes}) => { const nested = hyptiotes.castWeb(item); if (nested) parent.appendChild(nested); } }, { "test": item => Array.isArray(item) && (typeof item[0] !== "string" || item[0][0] !== ":"), "handler": ({item, parent, hyptiotes}) => { item.forEach(i => { const child = typeof i === "string" ? document.createTextNode(item) : hyptiotes.castWeb(i); parent.appendChild(child); }); } }, { "test": (item) => typeof item === "function", "handler": ({ item, parent, index, hyptiotes }) => { hyptiotes.mapItem(item({ parent, hyptiotes }), parent, index); } } ], "attributeHandlers": [ { "test": key => key === "style", "handler": ({value, parent}) => { parent.setAttribute('style', stringifyStyleObject(value)); } }, { "test": (_, value) => typeof value === "function", "handler": ({key, value, parent}) => { parent[key] = value; } }, { "test": () => true, "handler": ({key, value, parent}) => { parent.setAttribute(key, value); } } ], "elementFinalizer": (x) => x }
Example: Hooks (DOM API) Rendered