Public
Edited
Aug 1, 2024
Fork of Tangle
Insert cell
Insert cell
Insert cell
md`I ate ${Inputs.bind(Tangle({min: 0, step: 0.1, 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

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