editable-cli
is a command line tool piggybacking on observable ↗ internals which provides file-based interactive notebooks.
User has access to new top-level function: def
. This function is used to define constants and functions which are live-reloaded, with persisted state (unless the body of the function has changed):
def("a", 10);
def("b", 20);
def("sum", (a, b) => a + b); // sum is "tied" to defs "a" and "b" here
This environment is ideal for quick sketches and explorations. It provides all that observable exposes, including DOM access and more.
File-based notebooks have few additional nice properties:
prettier
, eslint
, etc.editable-cli
is open sourced: szymonkaliski/editable-cli ↗, and can be installed via npm
: npm install -g editable-cli
.
def("chart", (DOM, data, margin, d3, yAxis, xAxis, y, x, width, height) => {
const svg = d3.select(DOM.svg(width, height));
svg
.append("g")
.attr("fill", "#444")
.selectAll("rect")
.data(data)
.enter()
.append("rect")
.attr("x", x(0))
.attr("y", (d) => y(d.name))
.attr("width", (d) => x(d.value) - x(0))
.attr("height", y.bandwidth());
svg
.append("g")
.attr("fill", "white")
.attr("text-anchor", "end")
.style("font", "12px sans-serif")
.selectAll("text")
.data(data)
.enter()
.append("text")
.attr("x", (d) => x(d.value) - 4)
.attr("y", (d) => y(d.name) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.text((d) => d3.format(".3f")(d.value));
svg.append("g").call(xAxis);
svg.append("g").call(yAxis);
return svg.node();
});
def("margin", { top: 30, right: 0, left: 30, bottom: 10 });
def("height", (data, margin) => data.length * 18 + margin.top + margin.bottom);
def("width", 600);
def("alphabet", (require) => require("@observablehq/alphabet"));
def("d3", (require) => require("d3"));
def("yAxis", (d3, margin, y) => (g) => {
return g
.attr("transform", `translate(${margin.left},0)`)
.call(d3.axisLeft(y).tickSizeOuter(0));
});
def("xAxis", (d3, margin, x, width) => (g) => {
return g
.attr("transform", `translate(0,${margin.top})`)
.call(d3.axisTop(x).ticks(width / 80))
.call((g) => g.select(".domain").remove());
});
def("y", (d3, margin, data, height) => {
return d3
.scaleBand()
.domain(data.map((d) => d.name))
.range([margin.top, height - margin.bottom])
.padding(0.1);
});
def("x", (d3, margin, data, width) => {
return d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d.value)])
.range([margin.left, width - margin.right]);
});
def("data", (alphabet) => {
return (alphabet || [])
.slice()
.sort((a, b) => b.frequency - a.frequency)
.map(({ letter, frequency }) => ({ name: letter, value: frequency }));
});
436 words published on 2018-08-19 — let me know what you think