@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.
137 lines (136 loc) • 4.43 kB
JavaScript
import {
channelValues,
compositeKeyValues,
createMark,
inferredKeyValues,
isChartKey,
isChartValue,
markStates,
visualValue
} from "./mark.js";
import { valueKey } from "./scales.js";
function text(source, options = {}) {
const data = Array.isArray(source) ? source : Array.from(source);
return createMark(({ markIndex }) => {
const id = options.id ?? `text-${markIndex}`;
const xValues = channelValues(data, options.x, (_datum, { index }) => index);
const yValues = channelValues(
data,
options.y,
(datum) => typeof datum === "number" ? datum : void 0
);
const textValues = channelValues(
data,
options.text,
(datum) => datum == null ? "" : String(datum)
);
const zValues = channelValues(data, options.z, () => null);
const colorValues = options.color === void 0 ? zValues : channelValues(data, options.color, () => null);
const keys = inferredKeyValues(data, options.key, {
groups: zValues,
candidates: [xValues, yValues, compositeKeyValues(xValues, yValues)],
markId: id,
warningIdentity: options
});
const anchors = data.map(
(datum, datumIndex) => visualValue(options.anchor, datum, datumIndex, data, "middle")
);
const dxValues = data.map(
(datum, datumIndex) => visualValue(options.dx, datum, datumIndex, data, 0)
);
const dyValues = data.map(
(datum, datumIndex) => visualValue(options.dy, datum, datumIndex, data, 0)
);
const rotateValues = options.rotate === void 0 ? void 0 : data.map(
(datum, datumIndex) => visualValue(options.rotate, datum, datumIndex, data, 0)
);
const resolveLabel = (context, datumIndex) => {
const xValue = xValues[datumIndex];
const yValue = yValues[datumIndex];
const textValue = textValues[datumIndex];
if (!isChartValue(xValue) || !isChartValue(yValue) || textValue == null) {
return void 0;
}
const group = zValues[datumIndex] ?? null;
const colorValue = colorValues[datumIndex] ?? null;
const node = {
kind: "label",
key: `${id}:${valueKey(group)}:${valueKey(keys[datumIndex])}`,
x: context.scales.x.map(xValue) + (dxValues[datumIndex] ?? 0),
y: context.scales.y.map(yValue) + (dyValues[datumIndex] ?? 0),
text: String(textValue),
anchor: anchors[datumIndex],
baseline: "middle",
rotate: rotateValues?.[datumIndex],
fontSize: options.fontSize,
fontWeight: options.fontWeight
};
return { node, xValue, yValue, group, colorValue };
};
return {
id,
states: markStates(data, options.states),
channels: {
x: { scale: "x", values: xValues.filter(isChartValue) },
y: { scale: "y", values: yValues.filter(isChartValue) },
color: {
scale: "color",
values: colorValues.filter(isChartKey)
}
},
layoutLabels: (context) => data.flatMap((_datum, datumIndex) => {
const resolved = resolveLabel(context, datumIndex);
return resolved ? [resolved.node] : [];
}),
render: (context) => {
const { theme, color: resolveColor } = context;
const nodes = [];
const points = [];
data.forEach((datum, datumIndex) => {
const resolved = resolveLabel(context, datumIndex);
if (!resolved) return;
const { node, xValue, yValue, group, colorValue } = resolved;
const color = visualValue(
options.fill,
datum,
datumIndex,
data,
colorValue == null ? theme.foreground : resolveColor(colorValue)
);
nodes.push({
...node,
style: { fill: color }
});
points.push({
key: node.key,
markId: id,
group,
groupLabel: group == null ? id : String(group),
datum,
datumIndex,
xValue,
yValue,
x: node.x,
y: node.y,
color
});
});
return {
nodes: [
{
kind: "group",
key: id,
className: "ts-chart__text",
ariaHidden: true,
children: nodes
}
],
points
};
}
};
}, options.motion);
}
export {
text
};