Public
Edited
Mar 13
Paused
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
withRuntime(async({$cell, $values, $value, $inspect, $raw, $state, $render}) => {
const foo = $cell(Inputs.text({value: "foo", submit: true}));
const bar = $cell(Inputs.text({value: "bar"}));
const barName = String(bar);
return $render`<div>
<p>Inputs:</p>
<ul>
<li>${ foo }</li>
<li>${ bar }</li>
</ul>
<p>Values:</p>
<ul>
<li>${ $cell([$values(foo)], d => document.createTextNode(d)) }</li>
<li>${ $values(barName) }</li>
<li>${ $cell([foo, bar].map($values), (...d) => d) }</li>
`;
}, {invalidation})
Insert cell
Insert cell
withRuntime(async({$cell, $values, $value, $inspect, $raw, $state, $render}) => {
const [value, setValue] = $state("foo");
setValue("foo updated sync");
Promises.delay(2000).then(() => setValue("foo updated async"));
const view1 = $cell([value], value => Inputs.radio([value, "bar"], {value}));
const view2 = $cell([$values(view1)], value => Inputs.text({value}));

return $render`<div>
<div>${ value }</div>
${ view1 }
${ view2 }
${ $values(view2) }
<div>Val: ${ $value(view2).then(d => d.value) }</div>
${($cell(async function*() {
while(true) { yield new Date().toLocaleString(); await Promises.tick(1000); }
}))}
`;
}, {invalidation})
Insert cell
Insert cell
Insert cell
Insert cell
/**
*
*/
function withRuntime(callback, {
invalidation,
runtime: runtimeInstance,
identifier = () => DOM.uid("cell").id.replace(/-/g, "_"),
Cell = Symbol.for("Cell"),
} = {}) {
const runtime = runtimeInstance ?? new Runtime();
if(!runtimeInstance) invalidation?.then(() => runtime.dispose());
const module = runtime.module();
const names = new WeakMap();
return callback({$name, $cell, $value, $values, $inspect, $raw, $state, $render});

/**
* Returns a placeholder Node and replaces it with the resolved Node.
*
* @param {Promise<Node> | Node} d
* @returns {Node}
*/
function placeholder(d) {
if(!(d instanceof Promise)) return d;
const node = document.createComment("");
d.then(d => {
if(!node.parentElement) return;
if(!(d instanceof Node)) d = document.createTextNode(String(d));
node.parentElement.replaceChild(d, node);
});
return node;
}

/**
* Returns a cell's name.
*
* @param {Cell | string} d
* @returns {string}
*/
function $name(d) {
if(typeof d === "string") return d;
if(!names.has(d)) names.set(d, identifier());
return names.get(d);
}
/**
* Returns a cell's last value.
*
* @param {Cell | string} d
* @returns {Promise}
*/
function $value(d) {
return module.value(String(d));
}

/**
* Subscribes to a view's values.
*
* @param {Cell | string} cell
* @returns
*/
function $values(cell) {
return $cell([String(cell)], view => Generators.input(view));
}
/**
* Defines a new cell.
*/
function $cell(...args) {
const inputs = args.length < 2 ? [] : args[0];
const fn = args.length < 2 ? args[0] : args[1];
const v = module.variable();
const name = $name(v);
v.define($name(v), inputs.map($name), fn);
return Object.defineProperties(v, {
[Cell]: { value: true },
toString: { value: () => name }
});
}
/**
* Returns Inspector output for a cell.
*
* @param {Cell | string} cell
* @returns {HTMLSpanElement}
*/
function $inspect(cell) {
const wrap = htl.html`<span>`;
module.variable(new Inspector(wrap)).define([String(cell)], d => d);
return wrap;
}
/**
*
*
* @param {Cell | string} cell
* @returns {HTMLSpanElement}
*/
function $raw(cell) {
const wrap = htl.html`<span>`;
const update = d => wrap.replaceChildren(d);
module.variable({fulfilled: update, rejected: update}).define([String(cell)], d => d);
return wrap;
}
/**
*
* @param strings
* @param values
* @returns {HTMLElement}
*/
function $render(strings, ...values) {
const resolve = d => d[Cell] ? $inspect(d) : d;
const map = d => d instanceof Promise
? placeholder(d.then(resolve))
: resolve(d);
return htl.html(strings, ...values.map(map));
}

/**
* @typeParam T
* @param {T} initial
* @returns {[Cell, (newState: T) => T]}
*/
function $state(initial) {
const cell = $cell(initial);
return [cell, d => cell.define(String(cell), d)];
}
}
Insert cell

One platform to build and deploy the best data apps

Experiment and prototype by building visualizations in live JavaScript notebooks. Collaborate with your team and decide which concepts to build out.
Use Observable Framework to build data apps locally. Use data loaders to build in any language or library, including Python, SQL, and R.
Seamlessly deploy to Observable. Test before you ship, use automatic deploy-on-commit, and ensure your projects are always up-to-date.
Learn more