@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.
57 lines (56 loc) • 2.6 kB
JavaScript
import {
renderChartSvgWithHooks
} from "./svg-renderer.js";
function renderChartSvg(scene, options) {
const gradientIds = new Set(scene.gradients.map((gradient) => gradient.id));
return renderChartSvgWithHooks(scene, options, {
renderDefinitions: (currentScene, idPrefix) => renderGradients(currentScene, sanitizeId(idPrefix)),
renderGroup: (group, idPrefix) => renderClip(group, sanitizeId(idPrefix)),
resolvePaint: (value, idPrefix) => {
const match = /^url\(#([^)]+)\)$/.exec(value);
const id = match?.[1];
return id && gradientIds.has(id) ? `url(#${scopedId(sanitizeId(idPrefix), id)})` : value;
}
});
}
function renderGradients(scene, idPrefix) {
if (!scene.gradients.length) return "";
return `<defs data-ts-key="gradients">${scene.gradients.map(
(gradient) => `<linearGradient data-ts-key="gradient:${escapeAttribute(gradient.id)}" id="${escapeAttribute(scopedId(idPrefix, gradient.id))}" x1="${percent(gradient.x1 ?? 0)}" y1="${percent(gradient.y1 ?? 1)}" x2="${percent(gradient.x2 ?? 0)}" y2="${percent(gradient.y2 ?? 0)}">${gradient.stops.map(
(stop, index) => `<stop data-ts-key="gradient:${escapeAttribute(gradient.id)}:stop:${index}" offset="${percent(stop.offset)}" stop-color="${escapeAttribute(stop.color)}"${stop.opacity === void 0 ? "" : ` stop-opacity="${number(stop.opacity)}"`}/>`
).join("")}</linearGradient>`
).join("")}</defs>`;
}
function renderClip(group, idPrefix) {
if (!group.clip) return void 0;
const id = scopedId(idPrefix, `ts-chart-clip-${stableId(group.key)}`);
return {
attributes: ` clip-path="url(#${id})"`,
content: `<defs data-ts-key="${escapeAttribute(`${group.key}:clip-defs`)}"><clipPath id="${id}"><rect x="${number(group.clip.x)}" y="${number(group.clip.y)}" width="${number(group.clip.width)}" height="${number(group.clip.height)}"/></clipPath></defs>`
};
}
function scopedId(prefix, id) {
return prefix ? `${prefix}-${id}` : id;
}
function sanitizeId(value) {
return value.replaceAll(/[^a-zA-Z0-9_-]/g, "");
}
function percent(value) {
return `${number(Math.max(0, Math.min(1, value)) * 100)}%`;
}
function stableId(value) {
let hash = 2166136261;
for (let index = 0; index < value.length; index += 1) {
hash = Math.imul(hash ^ value.charCodeAt(index), 16777619);
}
return (hash >>> 0).toString(36);
}
function number(value) {
return String(Math.round(value * 100) / 100);
}
function escapeAttribute(value) {
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """);
}
export {
renderChartSvg
};