Published
Edited
Sep 27, 2021
4 forks
Importers
82 stars
Insert cell
Insert cell
Insert cell
md`I ate ${Inputs.bind(Tangle({min: 0, minWidth: "2em"}), viewof cookies)} cookies and ${Inputs.bind(Tangle({min: 0, minWidth: "2em"}), viewof cakes)} cakes. That’s ${totalCalories.toLocaleString("en")} calories!`
Insert cell
md`Here’s a table.

| Food | Quantity | Calories |
|---------|----------|----------|
| Cookies | ${Inputs.bind(Tangle({min: 0}), viewof cookies)} | ${cookieCalories.toLocaleString("en")} |
| Cakes | ${Inputs.bind(Tangle({min: 0}), viewof cakes)} | ${cakeCalories.toLocaleString("en")} |
| Total | ${totalQuantity.toLocaleString("en")} | ${totalCalories.toLocaleString("en")} |`
Insert cell
Insert cell
viewof cookies = Inputs.input(50)
Insert cell
viewof cakes = Inputs.input(4)
Insert cell
Insert cell
cookieCalories = cookies * caloriesPerCookie
Insert cell
cakeCalories = cakes * caloriesPerCake
Insert cell
totalCalories = cookieCalories + cakeCalories
Insert cell
totalQuantity = cookies + cakes
Insert cell
Insert cell
viewof caloriesPerCookie = Tangle({value: 50})
Insert cell
viewof caloriesPerCake = Tangle({value: 1000})
Insert cell
Insert cell
function Tangle({
min = -Infinity,
max = Infinity,
minWidth = undefined,
step = 1,
power = 1.2,
value = 50,
format = x => x.toLocaleString("en")
} = {}) {
const target = document.createElement("span");
Object.defineProperties(target, {
value: {
set(v) {
value = Math.max(min, Math.min(max, Math.floor(v / step) * step));
target.textContent = format(value);
},
get() {
return value;
}
}
});
if (value !== undefined) {
target.value = value;
}
if (minWidth !== undefined) {
target.style.minWidth = minWidth;
target.style.textAlign = "right";
target.style.display = "inline-block";
}
target.style.borderBottom = "dotted 1px currentColor";
target.style.cursor = "ew-resize";
target.style.touchAction = "none";
target.onselectstart = event => event.preventDefault();
target.ontouchstart = event => event.preventDefault();
target.onpointerdown = event => {
const startValue = value;
const {clientX: startX, clientY: startY, pointerId} = event;
const container = target.closest(".observablehq");
const onpointermove = (event) => {
const {clientX, clientY} = event;
const v0 = target.value;
const distance = clientX - startX + startY - clientY;
target.value = startValue + Math.abs(distance) ** power * Math.sign(distance) * step / delta;
const v1 = target.value;
if (v0 !== v1) target.dispatchEvent(new Event("input", {bubbles: true}));
};
const onpointerup = (event) => {
container.releasePointerCapture(pointerId);
container.removeEventListener("pointermove", onpointermove);
container.removeEventListener("pointercancel", onpointercancel);
container.removeEventListener("pointerup", onpointerup);
};
container.addEventListener("pointermove", onpointermove);
container.addEventListener("pointercancel", onpointerup);
container.addEventListener("pointerup", onpointerup);
container.setPointerCapture(pointerId);
};
return target;
}
Insert cell
delta = 6 // the required drag distance between steps
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