UNPKG

@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
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 };