@thi.ng/rdom v1.3.3
This project is part of the @thi.ng/umbrella monorepo.
About
Lightweight, reactive, VDOM-less UI/DOM components with async lifecycle and @thi.ng/hiccup compatible.
From hdom to rdom: Reactive UIs without virtual DOMs
In many ways this package is the direct successor of
@thi.ng/hdom,
which for several years was my preferred way of building UIs. hdom eschewed
using a virtual DOM to represent and maintain a dynamic tree of (UI) components
and instead only required a previous and current component tree in
@thi.ng/hiccup
format (aka nested, plain JS arrays w/ optional support for embedded other JS
data types, like ES6 iterables, @thi.ng/api
interfaces, etc.)
to perform its UI updates. Yet, whilst hiccup trees are plain, simple, user
defined data structures, which can be very easily composed without any
libraries, hdom itself was still heavily influenced by the general vDOM
approach and therefore a centralized update cycle and computing differences
between the trees were necessary evils core tasks. In short, hdom allowed
the illusion of declarative components with reactive state updates, but had to
use a complex and recursive diff to realize those updates.
In contrast, @thi.ng/rdom directly supports embedding reactive values/components in the hiccup tree and compiles them in such a way that their value changes directly target underlying DOM nodes without having to resort to any other intermediate processing (no diffing, vDOM updates etc.). @thi.ng/rdom is entirely vDOM-free. It supports declarative component definitions via @thi.ng/hiccup, @thi.ng/rstream, ES6 classes, direct DOM manipulation (incl. provided helpers) and/or any mixture of these approaches.
Targetted, isolated updates
If a reactive value is used for an element attribute, a value change will
trigger an update of only that attribute (there's special handling for event
listeners, CSS classes, data attributes and style
attribs). If a reactive
value is used as (text) body of an element (or an element/component itself),
only that body/subtree in the target DOM will be impacted/updated directly...
The package provides an interface
IComponent
(with a super simple life cycle API), a base component class
Component
for stubbing and a
number of fundamental control constructs & component-wrappers for composing more
complex components and to reduce boilerplate for various situations. Whilst
targetting a standard JS DOM by default, each component can decide for itself
what kind of target data structure (apart from a browser DOM) it manages. rdom
components themselves have no mandatory knowledge of a browser DOM. As an
example, similar to
@thi.ng/hdom-canvas,
the
@thi.ng/rdom-canvas
wrapper provides a component which subscribes to a stream of hiccup-based scene
descriptions (trees) and then translates each scene-value into HTML Canvas API
draw calls.
Async updates, scheduling & life cycle methods
Since there's no central coordination in rdom (neither explicitly nor
implicitly), each component can (and does) update whenever its state value has
changed. Likewise, components are free to directly manipulate the DOM through
other means, as hinted at earlier. Various rdom control constructs are dispatching component updates via a central scheduler. By default this is only a dummy implementation which processes tasks immediately. However, as usual rdom only relies on the IScheduler
interface and so supports other implementations, like RAFScheduler
.
The IComponent
interface is at the heart of rdom. It defines three lifecycle methods to:
.mount()
, .unmount()
and .update()
a component. The first two are always
async
to allow for more complex component initialization procedures (e.g.
preloaders, WASM init, other async ops...). Several of the higher-order
controller components/constructs too demand async
functions for the same
reasons.
Because rdom itself relies for most reactive features, stream composition and
reactive value transformations on other packages, i.e.
@thi.ng/rstream
and
@thi.ng/transducers,
please consult the docs for these packages to learn more about the available
constructs and patterns. Most of rdom only deals with either subscribing to
reactive values and/or wrapping/transforming existing subscriptions, either
explicitly using the provided control components (e.g.
$sub()
) or using
$compile()
to
auto-wrap such values embedded in an hiccup tree.
Status
BETA - possibly breaking changes forthcoming
Search or submit any issues for this package
HIC SUNT DRACONES
This is still a young project. Even though most of the overall approach, component lifecycle and API are fairly stable by now (after ~75 commits) and used in production, so far there's only brief documentation and only few public examples. This is being worked & improved on...
@thi.ng/atom integration
For the sake of deduplication of functionality and to keep the number of dependencies to a minimum, direct @thi.ng/atom integration has been removed in favor of using relevant @thi.ng/rstream constructs, which can be used as lightweight adapters, i.e.:
Support packages
- @thi.ng/rdom-canvas - @thi.ng/rdom component wrapper for @thi.ng/hiccup-canvas and declarative canvas drawing
- @thi.ng/rdom-components - Collection of unstyled, customizable components for @thi.ng/rdom
Related packages
- @thi.ng/hdom - Lightweight vanilla ES6 UI component trees with customizable branch-local behaviors
- @thi.ng/hiccup - HTML/SVG/XML serialization of nested data structures, iterables & closures
- @thi.ng/hiccup-html - 100+ type-checked HTML5 element functions for @thi.ng/hiccup related infrastructure
- @thi.ng/hiccup-svg - SVG element functions for @thi.ng/hiccup & @thi.ng/hdom
- @thi.ng/transducers - Lightweight transducer implementations for ES6 / TypeScript
Installation
yarn add @thi.ng/rdom
ES module import:
<script type="module" src="https://cdn.skypack.dev/@thi.ng/rdom"></script>
For Node.js REPL:
# with flag only for < v16
node --experimental-repl-await
> const rdom = await import("@thi.ng/rdom");
Package sizes (gzipped, pre-treeshake): ESM: 3.98 KB
Dependencies
- @thi.ng/api
- @thi.ng/checks
- @thi.ng/errors
- @thi.ng/hiccup
- @thi.ng/paths
- @thi.ng/prefixes
- @thi.ng/rstream
- @thi.ng/strings
Usage examples
Several demos in this repo's /examples directory are using this package.
A selection:
Screenshot | Description | Live demo | Source |
---|---|---|---|
Probabilistic color theme generator | Demo | Source | |
Color palette generation via dominant color extraction from uploaded images | Demo | Source | |
Interactive visualization of closest points on ellipses | Demo | Source | |
Parser grammar livecoding editor/playground & codegen | Demo | Source | |
Interactive pixel sorting tool using thi.ng/color & thi.ng/pixel | Demo | Source | |
Demonstates various rdom usage patterns | Demo | Source | |
Dynamically loaded images w/ preloader state | Demo | Source | |
rdom drag & drop example | Demo | Source | |
rdom & hiccup-canvas interop test | Demo | Source | |
Full umbrella repo doc string search w/ paginated results | Demo | Source | |
rdom powered SVG graph with draggable nodes | Demo | Source |
API
TODO
Currently, documentation only exists in the form of small examples and various doc strings (incomplete). I'm working to alleviate this situation ASAP... In that respect, PRs are welcome as well!
import { $compile } from "@thi.ng/rdom";
import { reactive } from "@thi.ng/rstream";
import { cycle, map } from "@thi.ng/transducers";
// reactive value
const bg = reactive("gray");
// color options (infinite iterable)
const colors = cycle(["magenta", "yellow", "cyan"]);
// event handler
const nextColor = () => bg.next(<string>colors.next().value);
// define component tree in hiccup syntax, compile & mount component.
// each time `bg` value changes, only subscribed bits will be updated
// i.e. title, the button's `style.background` and its label
// Note: instead of direct hiccup syntax, you could also use the
// element functions provided by https://thi.ng/hiccup-html
$compile([
"div",
{},
// transformed color as title (aka derived view)
["h1", {}, bg.transform(map((col) => `Hello, ${col}!`))],
[
// tag with Emmet-style ID & classes
"button#foo.w4.pa3.bn",
{
// reactive CSS background property
style: { background: bg },
onclick: nextColor,
},
// reactive button label
bg,
],
]).mount(document.body);
Authors
Karsten Schmidt
If this project contributes to an academic publication, please cite it as:
@misc{thing-rdom,
title = "@thi.ng/rdom",
author = "Karsten Schmidt",
note = "https://thi.ng/rdom",
year = 2020
}
License
© 2020 - 2021 Karsten Schmidt // Apache Software License 2.0
5 days ago
6 days ago
9 days ago
11 days ago
20 days ago
23 days ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
6 months ago
5 months ago
5 months ago
7 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
9 months ago
9 months ago
10 months ago
9 months ago
9 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
9 months ago
9 months ago
11 months ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
1 year ago
1 year ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago