Lcl · Core · Packages · Libraries


Dom

namespace Dom

Dom

A declarative DOM library for Lcl in the browser: describe the page as a tree of plain values, let Dom:: render it and, when the description changes, patch the real DOM to match. The same trees render to HTML text with Dom::html, so the description is equally at home on a server.

Requirements

Nodes

A node is a dict #{tag ... attrs #{...} children (...)} built by Dom::h or by the tag procedures in Html:: (Html::div, Html::p, … one per common HTML element). Strings and numbers are text nodes. Children are ordinary arguments; a list argument is spliced, so [List::map $items $render_item] drops straight in, and an empty string is skipped, so [if $cond { ... } else { "" }] is a conditional child.

eval [Dom::html_tags]          ;; div, p, ul, ... as bare names

proc view {todos} {
    div #{class app} \
        [h1 "Todos ([len $todos])"] \
        [ul [List::map $todos [lambda {t} { li #{key $t} $t }]]] \
        [if [== [len $todos] 0] { p #{class empty} "Nothing to do" } else { "" }]
}

Attributes are a dict given first. class, value, checked, disabled, selected and readonly set the element’s property; style takes a dict of CSS properties; on<event> takes a procedure that receives the event (as a Js::ref) – take a rest parameter if you do not need it; key identifies a child across renders so reordering moves elements instead of rewriting them; everything else is setAttribute.

Rendering

Dom::mount renders a node into a container and returns a mount handle; Dom::update patches what is mounted to a new node, touching only what changed. Dom::app wraps the common shape – some state, a render procedure, re-render on Dom::set_state:

Dom::app [Js::global document body] #{count 0} [lambda {state app} {
    let n [get $state count]
    div [h1 "Clicked $n times"] \
        [button #{onclick [lambda {*_} { Dom::set_state $app #{count [+ $n 1]} }]} "+1"]
}]

Examples:

>> Dom::h p #{class note} "hi " [Dom::h b "there"]
#{"children" ("hi " #{"children" ("there") "tag" "b" "attrs" #{}}) "tag" "p" "attrs" #{"class" "note"}}
>> Dom::html [Dom::h ul [List::map (a b) [lambda {x} { Dom::h li $x }]]]
"<ul><li>a</li><li>b</li></ul>"

proc Dom::node? v

Is v an element node (as opposed to text)?

Examples:

>> Dom::node? [Dom::h p]
1
>> Dom::node? "text"
0

proc Dom::h tag *args

Build an element node: tag, an optional attribute dict, then children – nodes, text, or lists of either (spliced). Numbers become text; empty strings are dropped.

Examples:

>> Dom::h br
#{"children" () "tag" "br" "attrs" #{}}
>> get [Dom::h a #{href /x} "link"] attrs
#{"href" "/x"}
>> get [Dom::h p "n = " 42 "" ("x" "y")] children
("n = " "42" "x" "y")

let Dom::TAGS

The common HTML elements, defined as procedures in Html::.

proc Dom::html_tags (names "")

Lcl source that defines the Html:: tag procedures as bare names in the scope that evaluates it – eval [Dom::html_tags] at the top of a file, or [Dom::html_tags (div p ul li)] for a chosen few. Bare a, b, i, s, u and q are deliberately short; take only what you use if that bites.

Examples:

>> eval [Dom::html_tags (article p)]
>> Dom::html [article [p "one"] [p "two"]]
"<article><p>one</p><p>two</p></article>"

proc Dom::escape s

HTML-escape text.

Examples:

>> Dom::escape {a < b & "c"}
"a &lt; b &amp; &quot;c&quot;"

proc Dom::html node

Render a node to HTML text: attributes in sorted order, style dicts as CSS text, event handlers and key omitted, void elements unclosed, text escaped.

Examples:

>> Dom::html [Dom::h input #{type text value "a<b" disabled 1}]
"<input disabled=\"1\" type=\"text\" value=\"a&lt;b\">"
>> Dom::html [Dom::h div #{style #{color red margin 0} onclick x key k} "t"]
"<div style=\"color:red;margin:0\">t</div>"
>> Dom::html "1 < 2"
"1 &lt; 2"

proc Dom::mount container node

Render node into container (a Js::ref element, appended after whatever is there) and return a mount handle.

let m [Dom::mount [Js::global document body] [Html::p "hello"]]
Dom::update $m [Html::p "hello again"]

proc Dom::update handle node

Patch a mount to show node, changing only what differs from the node it currently shows.

proc Dom::unmount handle

Remove a mount’s element from the document.

proc Dom::element handle

The element a mount rendered (a Js::ref).

proc Dom::app container state render

Mount [apply $render $state $app] into container, where app is the handle returned; Dom::set_state re-renders with new state and Dom::refresh with the same. render receives the state and the handle.

proc Dom::set_state handle state

Replace an app’s state and re-render.

proc Dom::state handle

An app’s current state.

proc Dom::refresh handle

Re-render an app from its current state.

namespace Html