@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.
168 lines (167 loc) • 5.81 kB
JavaScript
import {
channelValues,
createMark,
inferredKeyValues,
isChartKey,
isChartValue,
isFiniteNumber,
markStates,
visualValue
} from "./mark.js";
import { valueKey } from "./scales.js";
import { stackValues } from "./stack-internal.js";
function areaX(source, options = {}) {
const data = Array.isArray(source) ? source : Array.from(source);
return createMark(({ markIndex }) => {
const id = options.id ?? `area-x-${markIndex}`;
const rawX = options.x ?? options.x2;
const rawXValues = typeof rawX === "number" ? data.map(() => rawX) : channelValues(
data,
rawX,
(datum) => typeof datum === "number" ? datum : void 0
);
const yValues = channelValues(data, options.y, (_datum, { index }) => index);
const zValues = channelValues(data, options.z, () => null);
const colorValues = options.color === void 0 ? zValues : channelValues(data, options.color, () => null);
const groupValues = options.z === void 0 && options.color !== void 0 ? colorValues : zValues;
const explicitExtent = options.x1 !== void 0 || options.x2 !== void 0;
if (explicitExtent && options.layout) {
throw new TypeError(
"An area with explicit x1 or x2 endpoints cannot also configure a stack layout"
);
}
const stacked = explicitExtent ? void 0 : stackValues(yValues, rawXValues, groupValues, options.layout);
const x1Values = explicitExtent ? typeof options.x1 === "number" ? data.map(() => options.x1) : channelValues(data, options.x1, () => 0) : stacked.starts;
const x2Values = explicitExtent ? typeof options.x2 === "number" ? data.map(() => options.x2) : channelValues(data, options.x2 ?? options.x, () => void 0) : stacked.ends;
const keys = inferredKeyValues(data, options.key, {
groups: groupValues,
candidates: [yValues],
markId: id,
warningIdentity: options
});
const groups = /* @__PURE__ */ new Map();
groupValues.forEach((value, index) => {
const key = valueKey(value ?? null);
const group = groups.get(key);
if (group) group.push(index);
else groups.set(key, [index]);
});
return {
id,
states: markStates(data, options.states),
seriesFromColor: options.z === void 0 && options.color !== void 0,
channels: {
x: {
scale: "x",
values: [
...x2Values.filter(isFiniteNumber),
...x1Values.filter(isFiniteNumber)
],
includeZero: options.x1 === void 0
},
y: { scale: "y", values: yValues.filter(isChartValue) },
color: {
scale: "color",
values: colorValues.filter(isChartKey)
}
},
render: ({ scales, color: resolveColor }) => {
const nodes = [];
for (const [groupKey, indices] of groups) {
const firstIndex = indices[0];
if (firstIndex === void 0) continue;
const group = groupValues[firstIndex] ?? null;
const datum = data[firstIndex];
const resolvedColor = resolveColor(colorValues[firstIndex] ?? null);
const fill = visualValue(
options.fill,
datum,
firstIndex,
data,
resolvedColor
);
const stroke = options.stroke === void 0 ? void 0 : visualValue(
options.stroke,
datum,
firstIndex,
data,
resolvedColor
);
let right = [];
let left = [];
let segmentPoints = [];
let segmentIndex = 0;
const flush = () => {
if (!right.length) return;
const lower = [...left].reverse();
const path = options.curve?.areaX(right, left);
nodes.push({
kind: "area",
key: `${id}:${groupKey}:segment:${segmentIndex}`,
points: [...right, ...lower],
path,
interaction: { points: segmentPoints, affinity: "y" },
style: {
fill,
fillOpacity: options.fillOpacity ?? 0.2,
stroke,
strokeWidth: options.strokeWidth
}
});
right = [];
left = [];
segmentPoints = [];
segmentIndex += 1;
};
for (const datumIndex of indices) {
const xValue = rawXValues[datumIndex];
const x1Value = x1Values[datumIndex];
const x2Value = x2Values[datumIndex];
const yValue = yValues[datumIndex];
if (!isFiniteNumber(xValue) || !isFiniteNumber(x1Value) || !isFiniteNumber(x2Value) || !isChartValue(yValue)) {
flush();
continue;
}
const x = scales.x.map(x2Value);
const y = scales.y.map(yValue);
right.push([x, y]);
left.push([scales.x.map(x1Value), y]);
const key = `${id}:${groupKey}:${valueKey(keys[datumIndex])}`;
const point = {
key,
markId: id,
group,
groupLabel: group == null ? id : String(group),
datum: data[datumIndex],
datumIndex,
xValue,
yValue,
x1Value,
x2Value,
xInterval: "difference",
x,
y,
color: fill
};
segmentPoints.push(point);
}
flush();
}
return {
nodes: [
{
kind: "group",
key: id,
className: "ts-chart__area",
ariaHidden: true,
children: nodes
}
]
};
}
};
}, options.motion);
}
export {
areaX
};