@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.
112 lines (111 loc) • 3.79 kB
JavaScript
import {
channelValues,
createMark,
inferredKeyValues,
isChartKey,
isChartValue,
isNonnegativeFiniteNumber,
visualValue
} from "./mark.js";
import { resolveNumericScale } from "./scale-input.js";
import { valueKey } from "./scales.js";
function hexagon(source, options = {}) {
const data = Array.isArray(source) ? source : Array.from(source);
return createMark(({ markIndex }) => {
const id = options.id ?? `hexagon-${markIndex}`;
const xValues = channelValues(data, options.x, (_datum, { index }) => index);
const yValues = channelValues(
data,
options.y,
(datum) => typeof datum === "number" ? datum : void 0
);
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 });
const radiusOption = options.r;
const rawRadii = typeof radiusOption === "number" ? data.map(() => radiusOption) : channelValues(data, radiusOption, () => 6);
const radiusMapper = resolveNumericScale(options.rScale, rawRadii);
const radii = radiusMapper ? rawRadii.map(
(radius) => isNonnegativeFiniteNumber(radius) ? radiusMapper(radius) : Number.NaN
) : rawRadii;
return {
id,
channels: {
x: { scale: "x", values: xValues.filter(isChartValue) },
y: { scale: "y", values: yValues.filter(isChartValue) },
color: { scale: "color", values: colorValues.filter(isChartKey) }
},
render: ({ scales, color: resolveColor }) => {
const nodes = [];
data.forEach((datum, datumIndex) => {
const xValue = xValues[datumIndex];
const yValue = yValues[datumIndex];
const radius = radii[datumIndex];
if (!isChartValue(xValue) || !isChartValue(yValue) || !isNonnegativeFiniteNumber(radius)) {
return;
}
const x = scales.x.map(xValue);
const y = scales.y.map(yValue);
const group = zValues[datumIndex] ?? null;
const fallback = resolveColor(colorValues[datumIndex] ?? null);
const fill = visualValue(
options.fill,
datum,
datumIndex,
data,
fallback
);
const stroke = options.stroke === void 0 ? void 0 : visualValue(options.stroke, datum, datumIndex, data, fallback);
const key = `${id}:${valueKey(group)}:${valueKey(keys[datumIndex])}`;
const vertices = Array.from({ length: 6 }, (_, vertex) => {
const angle = (vertex * 60 - 90) * Math.PI / 180;
return [
x + Math.cos(angle) * radius,
y + Math.sin(angle) * radius
];
});
const point = {
key,
markId: id,
group,
groupLabel: group == null ? id : String(group),
datum,
datumIndex,
xValue,
yValue,
x,
y,
color: fill
};
nodes.push({
kind: "area",
key,
points: vertices,
interaction: { point },
style: {
fill,
fillOpacity: options.fillOpacity,
stroke,
strokeOpacity: options.strokeOpacity,
strokeWidth: options.strokeWidth
}
});
});
return {
nodes: [
{
kind: "group",
key: id,
className: "ts-chart__hexagon",
ariaHidden: true,
children: nodes
}
]
};
}
};
}, options.motion);
}
export {
hexagon
};