@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.
81 lines (80 loc) • 2.65 kB
JavaScript
import {
materializeGroups,
toArray,
transformValues
} from "./transform-internal.js";
import {
assertTransformOutputNames,
prepareOutputs,
reducePreparedOutputs
} from "./transform-reduce-internal.js";
function binTimeX(source, options) {
return binTime(source, options, "x");
}
function binTimeY(source, options) {
return binTime(source, options, "y");
}
function binTime(source, options, axis) {
const data = toArray(source);
const values = transformValues(data, options.value);
const valid = values.flatMap(
(value, index) => value instanceof Date && Number.isFinite(value.getTime()) ? [{ value, index }] : []
);
if (!valid.length && !options.domain) return [];
const domainStart = options.domain?.[0] ?? new Date(Math.min(...valid.map(({ value }) => value.getTime())));
const domainEnd = options.domain?.[1] ?? new Date(Math.max(...valid.map(({ value }) => value.getTime())));
const minimum = domainStart < domainEnd ? domainStart : domainEnd;
const maximum = domainStart < domainEnd ? domainEnd : domainStart;
const start = options.interval.floor(new Date(minimum));
const stop = options.interval.offset(
options.interval.floor(new Date(maximum)),
1
);
const boundaries = options.interval.range(
start,
options.interval.offset(stop, 1)
);
const outputs = options.outputs ?? { value: { reduce: "count" } };
const groups = materializeGroups(data, options.by);
assertTransformOutputNames(
outputs,
[
...Object.keys(groups[0]?.group ?? {}),
axis,
`${axis}1`,
`${axis}2`,
"source",
"sourceIndexes"
],
"binTime"
);
const prepared = prepareOutputs(data, outputs);
return groups.flatMap(({ group, indexes }) => {
const binIndexes = /* @__PURE__ */ new Map();
for (const index of indexes) {
const value = values[index];
if (!(value instanceof Date)) continue;
const identity = options.interval.floor(value).getTime();
const bucket = binIndexes.get(identity);
if (bucket) bucket.push(index);
else binIndexes.set(identity, [index]);
}
return boundaries.slice(0, -1).map((lower, position) => {
const upper = boundaries[position + 1];
const sourceIndexes = binIndexes.get(lower.getTime()) ?? [];
return {
...group,
[axis]: new Date((lower.getTime() + upper.getTime()) / 2),
[`${axis}1`]: lower,
[`${axis}2`]: upper,
source: sourceIndexes.map((index) => data[index]),
sourceIndexes,
...reducePreparedOutputs(data, sourceIndexes, group, prepared)
};
});
});
}
export {
binTimeX,
binTimeY
};