Public
Edited
Oct 19, 2022
Importers
1 star
Insert cell
Insert cell
function newDomTemplate(template) {
const newReactiveNode = newReactiveNodeTemplate();
return (strings = [], ...args) => {
const [values, start, stop] = toReactiveNodes(args, newReactiveNode);
requestAnimationFrame(start);
const result = template(strings, ...values);
Inputs.disposal(result).then(stop);
return result;
};
}
Insert cell
function toReactiveNodes(args, newReactiveNode) {
const toStart = [];
const toStop = [];
args = args.reduce((list, v) => {
let handled = false;
if (v && typeof v === "object") {
let toNode;
if (typeof v.next === "function") {
toNode = newReactiveNode;
} else if (Array.isArray(v)) {
toNode = (v) => toReactiveNodes(v, newReactiveNode);
}
if (toNode) {
const [node, start, stop] = toNode(v);
list.push(node);
toStart.push(start);
toStop.push(stop);
handled = true;
}
}
if (!handled) {
list.push(v);
}
return list;
}, []);
const start = () => toStart.forEach((s) => s());
const stop = () => toStop.forEach((s) => s());
return [args, start, stop];
}
Insert cell
function newReactiveNodeTemplate({
createPlaceholder = () => document.createComment(""),
createNode = toDomNode,
updatePlaceholder = (placeholder, node, prev) => {
if (!placeholder.isConnected) return false;
if (prev) prev.parentElement.removeChild(prev);
if (node) placeholder.parentElement.insertBefore(node, placeholder);
return true;
}
} = {}) {
return (iterator) => {
const placeholder = createPlaceholder();
let stopped = false;
let resolve;
const call = (m) =>
new Promise(async (r) => {
let result;
try {
resolve = r;
result = await m();
} catch (error) {
stopped = true;
}
resolve(result);
resolve = undefined;
});
const stop = () => {
stopped = true;
iterator.return && iterator.return();
resolve && resolve();
};
const start = async () => {
let node;
try {
const next = iterator.next.bind(iterator);
while (!stopped) {
const slot = await call(next);
if (stopped || !slot || slot.done) break;
const value = await call(() => slot.value);
if (stopped) break;
const newNode = createNode(value);
if (newNode !== node) {
if (!updatePlaceholder(placeholder, newNode, node)) {
break;
}
node = newNode;
}
}
} finally {
stop();
}
};
return [placeholder, start, stop];
};
}
Insert cell
function toDomNode(value, doc = document, containerElm = "span") {
if (value === undefined) return;
if (value === null) value = "";
if (typeof value === "number" || typeof value === "boolean")
value = String(value);
if (typeof value === "object" && !(value instanceof Node)) {
if (value !== null && value[Symbol.iterator]) {
const elm = doc.createElement(containerElm);
for (let val of value) {
elm.appendChild(toDomNode(val, doc));
}
value = elm;
} else {
value = Object.prototype.toString.call(value);
}
}
if (typeof value === "string") value = doc.createTextNode(value);
return value;
}
Insert cell
async function* listen(element, eventKey, transform) {
if (typeof element === "function") {
element = await Promise.resolve().then(element);
}
transform = transform || ((elm, ev) => ev);
yield* Generators.observe((notify) => {
const handler = (ev) => {
const value = transform(element, ev);
value !== undefined && notify(value);
};
element.addEventListener(eventKey, handler);
handler(); // Defines the initial value
return () => element.removeEventListener(eventKey, handler);
});
}
Insert cell
async function* values(input, transform = (v) => v) {
yield* listen(input, "input", (inp) => transform(inp.value));
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more