An experimental, declarative DOM library that models change and I/O using Observables.
MIT License
An experimental, declarative DOM library that models change and I/O with reactive streams.
The purpose of this package is to explore the following:
The current version of stream-dom
is based on most.js, and some of the following examples use most.js combinators. The examples also use JSX, though a reasonable node declaration function is planned.
Components are reusable trees of elements and other components that explicitly declare the shape of their input, structure, and output.
const Example = component({ input, structure, output })
Component input is declared as a hash of name/type pairs.
input: {
label: types.string,
initialValue: types.number
}
Component structure is a tree of component and DOM nodes.
Each component declares its structure as a tree of component and DOM nodes. Nodes may be named for use in the output declaration. Note uses of the node-name
attribute.
structure: input => (
<form node-name="formNode">
<label>
{input.label}: <input node-name="inputNode" value={input.initialValue} />
</label>
<button>Enter</button>
</form>
)
Components declare their output with a hash of name/value pairs. A named element provides direct access to its DOM node. A named component, shown later, provides access to its output which may be incorporated into the declared output of its parent component. Most often, component outputs will be reactive streams, but component outputs may also be static values.
output: namedNodes => {
const { formNode, inputNode } = namedNodes
const submit$ = tap(e => e.preventDefault(), domEvent('submit', formNode))
const value$ = startWith(
inputNode.value,
map(() => inputNode.value, submit$)
)
return { value$ }
}
A component can model a feedback loop by declaring feedback input that is satisfied by the component's declared output.
const ClickCount = component({
// Declare feedback input
input: { count$: types.feedback },
// Use feedback input
structure: ({ count$ }) => <span node-name="domNode">Click count: {count$}</span>,
// Declare feedback output
output: ({ domNode }) => ({
count$: scan(x => x + 1, 0, domEvent('click', domNode))
})
})
Dynamic content is explicitly declared. In stream-dom
, an input that changes over time should be represented by a reactive stream. All other content is treated as static content.
const StaticAndDynamic = component({
input: {
staticValue: types.number,
dynamicValue: types.stream
},
structure: ({ staticValue, dynamicValue }) => <table>
<tr><th scope="row">Will not change</th><td>{staticValue}</td></tr>
<tr><th scope="row">May change</th><td>{dynamicValue}</td></tr>
</table>
})
A single component or DOM node forms the root of a stream-dom UI. Use the mount
function to create and insert a stream-dom
UI into the DOM. The mount
function returns a handle with a dispose
method that may be used to remove the UI from the DOM and perform necessary cleanup.
// Create and insert
const handle = mount(document.body, null, <p>Hello, DOM!</p>)
// Remove and dispose
handle.dispose()
Strive to model the truth for the sake of simplicity and a better developer outcome.