diff options
Diffstat (limited to 'src/lustre.ffi.mjs')
-rw-r--r-- | src/lustre.ffi.mjs | 85 |
1 files changed, 23 insertions, 62 deletions
diff --git a/src/lustre.ffi.mjs b/src/lustre.ffi.mjs index fa022d1..3a6556e 100644 --- a/src/lustre.ffi.mjs +++ b/src/lustre.ffi.mjs @@ -1,10 +1,6 @@ -import { innerHTML, createTree } from "./runtime.ffi.mjs"; -import { Ok, Error, List } from "./gleam.mjs"; -import { - Some, - None, - map as option_map, -} from "../gleam_stdlib/gleam/option.mjs"; +import { morphdom } from "./runtime.ffi.mjs"; +import { Ok, Error } from "./gleam.mjs"; +import { map } from "./lustre/element.mjs"; // RUNTIME --------------------------------------------------------------------- @@ -18,33 +14,30 @@ export class App { #willUpdate = false; #didUpdate = false; - // These are the three functions that the user provides to the runtime. - #__init; - #__update; - #__render; - constructor(init, update, render) { - this.#__init = init; - this.#__update = update; - this.#__render = render; + this.#init = init; + this.#update = update; + this.#view = render; } start(selector = "body") { if (this.#root) return this; try { - this.#root = document.querySelector(selector); + const el = document.querySelector(selector); + const [next, cmds] = this.#init(); + + this.#root = el; + this.#state = next; + this.#commands = cmds[0].toArray(); + this.#didUpdate = true; + + window.requestAnimationFrame(() => this.#tick()); + + return new Ok((msg) => this.dispatch(msg)); } catch (_) { return new Error(undefined); } - - const [next, cmds] = this.#__init(); - this.#state = next; - this.#commands = cmds[0].toArray(); - this.#didUpdate = true; - - window.requestAnimationFrame(() => this.#tick()); - return new Ok((msg) => this.dispatch(msg)); } dispatch(msg) { @@ -55,22 +48,23 @@ export class App { } #render() { - const node = this.#__render(this.#state); - const tree = createTree(map(node, (msg) => this.dispatch(msg))); + const node = this.#view(this.#state); + const vdom = map(node, (msg) => this.dispatch(msg)); - innerHTML(this.#root, tree); + morphdom(this.#root.firstChild, vdom); } #tick() { this.#flush(); this.#didUpdate && this.#render(); + this.#didUpdate = false; this.#willUpdate = false; } #flush(times = 0) { if (this.#queue.length) { while (this.#queue.length) { - const [next, cmds] = this.#__update(this.#state, this.#queue.shift()); + const [next, cmds] = this.#update(this.#state, this.#queue.shift()); this.#state = next; this.#commands.concat(cmds[0].toArray()); @@ -92,37 +86,4 @@ export class App { } export const setup = (init, update, render) => new App(init, update, render); -export const start = (app, selector = "body") => app.start(selector); - -// VDOM ------------------------------------------------------------------------ - -export const node = (tag, attrs, children) => - createTree(tag, Object.fromEntries(attrs.toArray()), children.toArray()); -export const text = (content) => content; -export const attr = (key, value) => { - if (value instanceof List) return [key, value.toArray()]; - if (value instanceof Some) return [key, value[0]]; - if (value instanceof None) return [key, undefined]; - - return [key, value]; -}; -export const on = (event, handler) => [`on${event}`, handler]; -export const map = (node, f) => ({ - ...node, - attributes: Object.entries(node.attributes).reduce((attrs, [key, value]) => { - // It's safe to mutate the `attrs` object here because we created it at - // the start of the reduce: it's not shared with any other code. - - // If the attribute is an event handler, wrap it in a function that - // transforms - if (key.startsWith("on") && typeof value === "function") { - attrs[key] = (e) => option_map(value(e), f); - } else { - attrs[key] = value; - } - - return attrs; - }, {}), - childNodes: node.childNodes.map((child) => map(child, f)), -}); -export const styles = (list) => Object.fromEntries(list.toArray()); +export const start = (app, selector) => app.start(selector); |