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.41 kB
import { isChartKey, valueKey } from "./scales.js"; const warnedKeyFallbacks = /* @__PURE__ */ new WeakSet(); function isChartValue(value) { return typeof value === "string" || value instanceof Date && Number.isFinite(value.getTime()) || isFiniteNumber(value); } function isFiniteNumber(value) { return typeof value === "number" && Number.isFinite(value); } function isNonnegativeFiniteNumber(value) { return isFiniteNumber(value) && value >= 0; } function createMark(initialize, motion, renderer) { const normalizedInitialize = (context) => { const initialized = normalizeMarkInitialization(initialize(context)); const withMotion = motion === void 0 || initialized.motion !== void 0 ? initialized : { ...initialized, motion }; return renderer === void 0 ? withMotion : applyMarkRenderer(withMotion, renderer); }; return { initialize: normalizedInitialize, ...motion === void 0 ? {} : { motion }, ...renderer === void 0 ? {} : { renderer } }; } function applyMarkRenderer(initialized, renderer) { const render = initialized.render; const resolveLayout = initialized.resolveLayout; return { ...initialized, render: (context) => applyMarkRendererToScene(render(context), renderer), ...resolveLayout ? { resolveLayout(context) { const resolved = resolveLayout(context); return { ...resolved, render: (renderContext) => applyMarkRendererToScene( resolved.render(renderContext), renderer ) }; } } : {} }; } function applyMarkRendererToScene(scene, renderer) { return { ...scene, nodes: scene.nodes.map((node) => ({ ...node, renderer })), ...scene.focusGuides ? { focusGuides: scene.focusGuides.map((guide) => ({ ...guide, renderer })) } : {} }; } function normalizeMarkInitialization(initialized) { if (typeof initialized.render === "function") return initialized; return { ...initialized, render: () => { throw new TypeError( `Mark "${initialized.id}" must resolve its layout before rendering` ); } }; } function markStates(data, definitions) { return definitions?.length ? { data, definitions } : void 0; } function visualValue(channel, datum, index, data, fallback) { return typeof channel === "function" ? channel(datum, { index, data }) : channel ?? fallback; } function channelValues(data, channel, fallback) { if (typeof channel === "function") { return data.map((datum, index) => channel(datum, { index, data })); } if (channel !== void 0) { return data.map( (datum) => datum != null && typeof datum === "object" ? datum[channel] : void 0 ); } return data.map((datum, index) => fallback(datum, { index, data })); } function inferredKeyValues(data, key, options = {}) { if (key !== void 0) { return channelValues(data, key, (_datum, { index }) => index); } const candidates = [ data.map( (datum) => datum != null && typeof datum === "object" ? datum.id : void 0 ), data.map((datum) => { if (datum == null || typeof datum !== "object") return void 0; const nested = datum.data; return nested != null && typeof nested === "object" ? nested.id : void 0; }), ...options.candidates ?? [] ]; for (const candidate of candidates) { if (candidate.length !== data.length) continue; const normalized = candidate.map(normalizeInferredKey); if (normalized.every((value) => value !== void 0) && keysAreUniqueWithinGroups(normalized, options.groups)) { return normalized; } } warnAboutKeyFallback( options.markId, options.candidates, options.warningIdentity ); return data.map((_datum, index) => index); } function compositeKeyValues(...channels) { const length = channels[0]?.length ?? 0; return Array.from({ length }, (_value, index) => { const parts = channels.map( (channel) => normalizeInferredKey(channel[index]) ); return parts.every((part) => part !== void 0) ? JSON.stringify(parts.map(valueKey)) : void 0; }); } function normalizeInferredKey(value) { if (isChartKey(value)) return value; if (value instanceof Date && Number.isFinite(value.getTime())) { return `date:${value.getTime()}`; } return void 0; } function keysAreUniqueWithinGroups(keys, groups) { const seen = /* @__PURE__ */ new Set(); for (let index = 0; index < keys.length; index += 1) { const identity = JSON.stringify([ valueKey(groups?.[index] ?? null), valueKey(keys[index]) ]); if (seen.has(identity)) return false; seen.add(identity); } return true; } function warnAboutKeyFallback(markId, candidates, warningIdentity) { if (!markId || !candidates?.length || !warningIdentity || warnedKeyFallbacks.has(warningIdentity) || typeof process === "undefined" || process.env.NODE_ENV === "production") { return; } warnedKeyFallbacks.add(warningIdentity); console.warn( `TanStack Charts could not infer a unique key for mark "${markId}". Using row position; supply key for stable identity across updates.` ); } export { applyMarkRenderer, applyMarkRendererToScene, channelValues, compositeKeyValues, createMark, inferredKeyValues, isChartKey, isChartValue, isFiniteNumber, isNonnegativeFiniteNumber, markStates, normalizeMarkInitialization, visualValue };