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.

178 lines (177 loc) 6 kB
import { channelValues, createMark, inferredKeyValues, isChartKey, isChartValue, visualValue } from "./mark.js"; import { createMarkWithScaleValues } from "./mark-with-scale-values.js"; import { minimumMappedSpacing } from "./mapped-spacing-internal.js"; import { valueKey } from "./scales.js"; function bandX(source, options) { const data = Array.isArray(source) ? source : Array.from(source); const resolved = options ?? {}; return createMarkWithScaleValues(({ markIndex }) => { const id = resolved.id ?? `band-x-${markIndex}`; const values = channelValues(data, resolved.x, (_datum, { index }) => index); const zValues = channelValues(data, resolved.z, () => null); const colorValues = resolved.color === void 0 ? zValues : channelValues(data, resolved.color, () => null); const keys = inferredKeyValues(data, resolved.key, { groups: zValues, candidates: [values], markId: id, warningIdentity: resolved }); return { id, channels: { x: { scale: "x", values: values.filter(isChartValue) }, color: { scale: "color", values: colorValues.filter(isChartKey) } }, render: ({ chart, scales, color }) => { const width = Number.isFinite(resolved.width) ? Math.max(0, resolved.width) : scales.x.bandwidth || inferBandwidth(scales.x, values, chart.width, data.length); const inset = Number.isFinite(resolved.inset) ? resolved.inset : 0; const nodes = []; data.forEach((datum, index) => { const xValue = values[index]; if (!isChartValue(xValue)) return; const x = scales.x.map(xValue); const fill = visualValue( resolved.fill, datum, index, data, color(colorValues[index]) ); const group = zValues[index] ?? null; const key = `${id}:${valueKey(group)}:${valueKey(keys[index])}`; const left = x - width / 2 + inset; const paintedWidth = Math.max(0, width - inset * 2); const point = { key, markId: id, group, groupLabel: group == null ? id : String(group), datum, datumIndex: index, xValue, yValue: 0, x, y: chart.y + chart.height / 2, color: fill }; nodes.push({ kind: "rect", key, x: left, y: chart.y, width: paintedWidth, height: chart.height, radius: resolved.radius, interaction: { point, affinity: "x" }, style: { fill, fillOpacity: resolved.fillOpacity } }); }); return { nodes: [ { kind: "group", key: id, className: "ts-chart__band ts-chart__band-x", ariaHidden: true, children: nodes } ] }; } }; }, resolved.motion); } function bandY(source, options) { const data = Array.isArray(source) ? source : Array.from(source); const resolved = options ?? {}; return createMark(({ markIndex }) => { const id = resolved.id ?? `band-y-${markIndex}`; const values = channelValues(data, resolved.y, (_datum, { index }) => index); const zValues = channelValues(data, resolved.z, () => null); const colorValues = resolved.color === void 0 ? zValues : channelValues(data, resolved.color, () => null); const keys = inferredKeyValues(data, resolved.key, { groups: zValues, candidates: [values], markId: id, warningIdentity: resolved }); return { id, channels: { y: { scale: "y", values: values.filter(isChartValue) }, color: { scale: "color", values: colorValues.filter(isChartKey) } }, render: ({ chart, scales, color }) => { const height = Number.isFinite(resolved.height) ? Math.max(0, resolved.height) : scales.y.bandwidth || inferBandwidth(scales.y, values, chart.height, data.length); const inset = Number.isFinite(resolved.inset) ? resolved.inset : 0; const nodes = []; data.forEach((datum, index) => { const yValue = values[index]; if (!isChartValue(yValue)) return; const y = scales.y.map(yValue); const fill = visualValue( resolved.fill, datum, index, data, color(colorValues[index]) ); const group = zValues[index] ?? null; const key = `${id}:${valueKey(group)}:${valueKey(keys[index])}`; const top = y - height / 2 + inset; const paintedHeight = Math.max(0, height - inset * 2); const point = { key, markId: id, group, groupLabel: group == null ? id : String(group), datum, datumIndex: index, xValue: 0, yValue, x: chart.x + chart.width / 2, y, color: fill }; nodes.push({ kind: "rect", key, x: chart.x, y: top, width: chart.width, height: paintedHeight, radius: resolved.radius, interaction: { point, affinity: "y" }, style: { fill, fillOpacity: resolved.fillOpacity } }); }); return { nodes: [ { kind: "group", key: id, className: "ts-chart__band ts-chart__band-y", ariaHidden: true, children: nodes } ] }; } }; }, resolved.motion); } function inferBandwidth(scale, values, span, count) { const spacing = minimumMappedSpacing(scale, values); return spacing !== void 0 ? spacing * 0.8 : Math.min(48, span / Math.max(2, count + 1) * 0.8); } export { bandX, bandY };