@tanstack/charts
Version:
A chart grammar for TypeScript and JavaScript. Marks consume your data directly, channels describe visual encodings, and the engine compiles them into a renderer-neutral keyed scene. TanStack's compact scales cover common numeric and categorical mappings.
94 lines (93 loc) • 2.98 kB
JavaScript
import { tickIncrement, ticks as createTicks, tickStep } from "./ticks.js";
function scaleLinear(first, second) {
let domain = [0, 1];
let range = [0, 1];
let clamped = false;
const scale = ((value) => {
if (value == null || !Number.isFinite(Number(value))) return void 0;
return interpolate(Number(value), domain, range, clamped);
});
scale.domain = ((values) => {
if (values === void 0) return [...domain];
domain = pair(values, "domain");
return scale;
});
scale.range = ((values) => {
if (values === void 0) return [...range];
range = pair(values, "range");
return scale;
});
scale.invert = (value) => interpolate(value, range, domain, clamped);
scale.clamp = ((value) => {
if (value === void 0) return clamped;
clamped = Boolean(value);
return scale;
});
scale.ticks = (count = 10) => createTicks(domain[0], domain[1], count);
scale.tickFormat = (count = 10) => {
const step = Math.abs(tickStep(domain[0], domain[1], count));
const digits = step > 0 && step < 1 ? Math.min(20, Math.max(0, -Math.floor(Math.log10(step)))) : 0;
return (value) => {
const formatted = digits ? value.toFixed(digits) : String(value);
return formatted === "-0" ? "0" : formatted;
};
};
scale.nice = (count = 10) => {
let start = domain[0];
let stop = domain[1];
let startIndex = 0;
let stopIndex = 1;
if (stop < start) {
;
[start, stop] = [stop, start];
[startIndex, stopIndex] = [stopIndex, startIndex];
}
let previousStep;
for (let remaining = 10; remaining > 0; remaining--) {
const step = tickIncrement(start, stop, count);
if (step === previousStep) {
const next = [...domain];
next[startIndex] = start;
next[stopIndex] = stop;
domain = next;
break;
}
if (step > 0) {
start = Math.floor(start / step) * step;
stop = Math.ceil(stop / step) * step;
} else if (step < 0) {
start = Math.ceil(start * step) / step;
stop = Math.floor(stop * step) / step;
} else {
break;
}
previousStep = step;
}
return scale;
};
scale.copy = () => scaleLinear(domain, range).clamp(clamped);
if (second !== void 0) {
scale.domain(first).range(second);
} else if (first !== void 0) {
scale.range(first);
}
return scale;
}
function interpolate(value, domain, range, clamped) {
const span = domain[1] - domain[0];
let ratio = span ? (value - domain[0]) / span : 0.5;
if (clamped) ratio = Math.max(0, Math.min(1, ratio));
return range[0] + ratio * (range[1] - range[0]);
}
function pair(values, name) {
const resolved = Array.from(values, Number);
if (resolved.length !== 2 || resolved.some((value) => !Number.isFinite(value))) {
throw new TypeError(
`A linear scale ${name} requires exactly two finite numbers`
);
}
return [resolved[0], resolved[1]];
}
export {
scaleLinear
};