@x12i/charts 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/spec.ts","../src/renderer.ts"],"names":[],"mappings":";AAOO,IAAM,UAAA,GAAa;AAAA;AAAA,EAExB,KAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA;AAAA,EAEA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,aAAA;AAAA,EACA,MAAA;AAAA;AAAA,EAEA,WAAA;AAAA,EACA,UAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA;AAAA,EAEA,OAAA;AAAA,EACA,SAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA;AAAA,EAEA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,mBAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA;AAAA,EAEA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,eAAA;AAAA;AAAA,EAEA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,UAAA;AAAA;AAAA,EAEA,YAAA;AAAA,EACA,WAAA;AAAA,EACA,YAAA;AAAA,EACA,UAAA;AAAA;AAAA,EAEA,OAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA,EACA,iBAAA;AAAA,EACA;AACF;AA2FO,SAAS,gBAAgB,IAAA,EAA4B;AAC1D,EAAA,OAAO,IAAA;AACT;;;ACxJO,IAAM,eAAA,GAAkB;AAG/B,IAAM,WAAA,uBAA0C,GAAA,CAAe;AAAA,EAC7D,aAAA;AAAA,EACA,MAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAC,CAAA;AAeM,SAAS,eAAA,CAAgB;AAAA,EAC9B,QAAA,GAAW,MAAA;AAAA,EACX,IAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA,GAAS;AACX,CAAA,EAA2C;AACzC,EAAA,IAAI,MAAA,KAAW,UAAU,OAAO,KAAA;AAChC,EAAA,IAAI,QAAA,KAAa,QAAQ,OAAO,QAAA;AAChC,EAAA,IAAI,WAAA,CAAY,GAAA,CAAI,IAAI,CAAA,EAAG,OAAO,QAAA;AAClC,EAAA,OAAO,UAAA,GAAa,kBAAkB,QAAA,GAAW,KAAA;AACnD","file":"index.js","sourcesContent":["/**\r\n * ChartSpec: a JSON-serializable description of one chart (spec section 8).\r\n * The grammar is deliberately smaller than Vega-Lite. Renderers read a spec and\r\n * draw it with kit tokens, so an engine can change without breaking apps.\r\n */\r\n\r\n/** Chart types by family. Entries marked 1.x in the spec are included so specs written today stay valid. */\r\nexport const chartTypes = [\r\n // Comparison\r\n \"bar\",\r\n \"lollipop\",\r\n \"dot-plot\",\r\n \"bullet\",\r\n \"slope\",\r\n \"dumbbell\",\r\n // Trend\r\n \"line\",\r\n \"area\",\r\n \"step\",\r\n \"sparkline\",\r\n \"range-band\",\r\n \"candlestick\",\r\n \"ohlc\",\r\n // Distribution\r\n \"histogram\",\r\n \"box-plot\",\r\n \"density\",\r\n \"strip\",\r\n \"beeswarm\",\r\n \"violin\",\r\n \"ecdf\",\r\n // Composition\r\n \"donut\",\r\n \"treemap\",\r\n \"waterfall\",\r\n \"funnel\",\r\n \"waffle\",\r\n \"sunburst\",\r\n \"marimekko\",\r\n // Relationship\r\n \"scatter\",\r\n \"bubble\",\r\n \"connected-scatter\",\r\n \"heatmap\",\r\n \"hexbin\",\r\n \"contour\",\r\n // KPI\r\n \"stat\",\r\n \"gauge\",\r\n \"progress-ring\",\r\n // Flow\r\n \"sankey\",\r\n \"chord\",\r\n \"alluvial\",\r\n // Geo\r\n \"choropleth\",\r\n \"point-map\",\r\n \"heat-layer\",\r\n \"flow-map\",\r\n // Schedule and process\r\n \"gantt\",\r\n \"timeline\",\r\n \"burndown\",\r\n \"cumulative-flow\",\r\n \"calendar-heatmap\",\r\n] as const;\r\n\r\nexport type ChartType = (typeof chartTypes)[number];\r\n\r\nexport type FieldType = \"category\" | \"number\" | \"time\" | \"boolean\";\r\n\r\n/** Where the rows come from: a named dataset resolved by the app, or inline values. */\r\nexport type ChartData =\r\n | { ref: string }\r\n | { values: ReadonlyArray<Record<string, unknown>> };\r\n\r\nexport interface Encoding {\r\n field: string;\r\n type: FieldType;\r\n /** A named format such as `compact` or `percent`, or an ICU pattern. */\r\n format?: string;\r\n /** Axis or legend title. Axis titles carry units. */\r\n title?: string;\r\n}\r\n\r\n/** Transforms run in order before drawing. */\r\nexport type Transform =\r\n | { type: \"aggregate\"; groupBy: string[]; op: \"sum\" | \"mean\" | \"min\" | \"max\" | \"count\"; field?: string; as: string }\r\n | { type: \"bin\"; field: string; maxBins?: number; as: string }\r\n | { type: \"stack\"; field: string; groupBy?: string[]; offset?: \"zero\" | \"normalize\" }\r\n | { type: \"percent-of-total\"; field: string; as: string }\r\n | { type: \"sort\"; field: string; order?: \"asc\" | \"desc\" }\r\n | { type: \"top-n\"; field: string; n: number; otherLabel?: string }\r\n | { type: \"rolling-average\"; field: string; window: number; as: string }\r\n | { type: \"cumulative-sum\"; field: string; as: string }\r\n | { type: \"period-change\"; field: string; periods?: number; as: string }\r\n | { type: \"downsample\"; method: \"lttb\"; field: string; threshold: number };\r\n\r\n/** Targets, limits and forecasts are always labeled marks, never unlabeled lines. */\r\nexport type Annotation =\r\n | { type: \"reference-line\"; x?: number | string; y?: number | string; label: string }\r\n | { type: \"reference-band\"; from: number | string; to: number | string; axis: \"x\" | \"y\"; label: string }\r\n | { type: \"text\"; x: number | string; y: number | string; text: string }\r\n | { type: \"forecast\"; from: number | string; label: string };\r\n\r\nexport interface ChartInteractions {\r\n tooltip?: boolean;\r\n /** Brush to filter other charts and grids through the shared filter store. */\r\n brush?: boolean;\r\n /** Click a legend entry to isolate a series. */\r\n legendIsolate?: boolean;\r\n zoom?: boolean;\r\n /** Click to drill down through the shared filter store. */\r\n drilldown?: boolean;\r\n}\r\n\r\n/** Accessibility fields. Every chart has an accessible name, a description and a table view. */\r\nexport interface ChartA11y {\r\n /** The long description read to screen reader users. */\r\n description: string;\r\n /** Show the underlying data as a table behind a \"View as table\" toggle. Defaults to true. */\r\n table?: boolean;\r\n}\r\n\r\nexport type RendererChoice = \"svg\" | \"canvas\" | \"auto\";\r\n\r\nexport interface ChartSpec {\r\n /** Schema version, so saved specs can be migrated. */\r\n version?: 1;\r\n /** The finding: a sentence that states what the chart shows. */\r\n title: string;\r\n /** Scope and units. */\r\n subtitle?: string;\r\n /** Where the data came from. Required in reports. */\r\n source?: string;\r\n data: ChartData;\r\n mark: ChartType;\r\n x?: Encoding;\r\n y?: Encoding;\r\n /** Series that differ in kind use the categorical palette, capped at 8 hues. */\r\n color?: Encoding;\r\n size?: Encoding;\r\n /** One accent on what the title is about, for example `week == 'W4'`. */\r\n highlight?: { where: string };\r\n transforms?: Transform[];\r\n annotations?: Annotation[];\r\n interactions?: ChartInteractions;\r\n /** Dual axes only behind an explicit flag, which adds a visible warning. */\r\n dualAxis?: boolean;\r\n renderer?: RendererChoice;\r\n /** Whether axes mirror in right-to-left locales. */\r\n direction?: \"ltr\" | \"rtl\" | \"auto\";\r\n a11y: ChartA11y;\r\n}\r\n\r\n/** Identity helper that gives authoring-time type checking and autocomplete. */\r\nexport function defineChartSpec(spec: ChartSpec): ChartSpec {\r\n return spec;\r\n}\r\n","import type { ChartType, RendererChoice } from \"./spec\";\r\n\r\n/**\r\n * The SVG renderer is comfortable up to roughly this many points per chart\r\n * (spec section 8: about 1,000 to 5,000). Beyond it, auto mode uses canvas.\r\n */\r\nexport const SVG_POINT_LIMIT = 5000;\r\n\r\n/** Chart types that only the canvas renderer draws. */\r\nconst CANVAS_ONLY: ReadonlySet<ChartType> = new Set<ChartType>([\r\n \"candlestick\",\r\n \"ohlc\",\r\n \"sunburst\",\r\n \"marimekko\",\r\n \"hexbin\",\r\n \"contour\",\r\n \"sankey\",\r\n \"chord\",\r\n \"alluvial\",\r\n]);\r\n\r\nexport type RenderTarget = \"screen\" | \"print\" | \"report\";\r\n\r\nexport interface ResolveRendererInput {\r\n renderer?: RendererChoice;\r\n mark: ChartType;\r\n pointCount: number;\r\n target?: RenderTarget;\r\n}\r\n\r\n/**\r\n * Picks a renderer for a chart. Reports and print always use SVG so the output\r\n * stays vector; on screen, `auto` switches to canvas by point count and type.\r\n */\r\nexport function resolveRenderer({\r\n renderer = \"auto\",\r\n mark,\r\n pointCount,\r\n target = \"screen\",\r\n}: ResolveRendererInput): \"svg\" | \"canvas\" {\r\n if (target !== \"screen\") return \"svg\";\r\n if (renderer !== \"auto\") return renderer;\r\n if (CANVAS_ONLY.has(mark)) return \"canvas\";\r\n return pointCount > SVG_POINT_LIMIT ? \"canvas\" : \"svg\";\r\n}\r\n"]}
1
+ {"version":3,"sources":["../src/spec.ts","../src/validate.ts"],"sourcesContent":["/**\r\n * ChartSpec: a JSON-serializable description of one chart (spec section 8).\r\n * The grammar is deliberately smaller than Vega-Lite. Renderers read a spec and\r\n * draw it with kit tokens, so an engine can change without breaking apps.\r\n *\r\n * Everything added after the first release is optional, so specs written\r\n * against the first release stay valid.\r\n */\r\n\r\n/** Chart types by family. Entries marked 1.x in the spec are included so specs written today stay valid. */\r\nexport const chartTypes = [\r\n // Comparison\r\n \"bar\",\r\n \"lollipop\",\r\n \"dot-plot\",\r\n \"bullet\",\r\n \"slope\",\r\n \"dumbbell\",\r\n // Trend\r\n \"line\",\r\n \"area\",\r\n \"step\",\r\n \"sparkline\",\r\n \"range-band\",\r\n \"candlestick\",\r\n \"ohlc\",\r\n // Distribution\r\n \"histogram\",\r\n \"box-plot\",\r\n \"density\",\r\n \"strip\",\r\n \"beeswarm\",\r\n \"violin\",\r\n \"ecdf\",\r\n // Composition\r\n \"donut\",\r\n \"pie\",\r\n \"treemap\",\r\n \"waterfall\",\r\n \"funnel\",\r\n \"waffle\",\r\n \"sunburst\",\r\n \"marimekko\",\r\n // Relationship\r\n \"scatter\",\r\n \"bubble\",\r\n \"connected-scatter\",\r\n \"heatmap\",\r\n \"hexbin\",\r\n \"contour\",\r\n // KPI\r\n \"stat\",\r\n \"gauge\",\r\n \"progress-ring\",\r\n // Flow\r\n \"sankey\",\r\n \"chord\",\r\n \"alluvial\",\r\n // Geo\r\n \"choropleth\",\r\n \"point-map\",\r\n \"heat-layer\",\r\n \"flow-map\",\r\n // Schedule and process\r\n \"gantt\",\r\n \"timeline\",\r\n \"burndown\",\r\n \"cumulative-flow\",\r\n \"calendar-heatmap\",\r\n // Mixed marks on one plot (bars with lines, optionally on two axes)\r\n \"combo\",\r\n] as const;\r\n\r\nexport type ChartType = (typeof chartTypes)[number];\r\n\r\nexport type FieldType = \"category\" | \"number\" | \"time\" | \"boolean\";\r\n\r\n/** One data row. Values are looked up by field name. */\r\nexport type Row = Record<string, unknown>;\r\n\r\n/** Where the rows come from: a named dataset resolved by the app, or inline values. */\r\nexport type ChartData =\r\n | { ref: string }\r\n | { values: ReadonlyArray<Record<string, unknown>> };\r\n\r\nexport interface Encoding {\r\n field: string;\r\n type: FieldType;\r\n /**\r\n * A named format (`compact`, `percent`, `integer`, `decimal`, `currency:USD`,\r\n * `date`, `datetime`, `time`, `month`, `year`), a d3-format specifier such as\r\n * `,.2f`, or a d3-time-format pattern such as `%b %Y`.\r\n */\r\n format?: string;\r\n /** Axis or legend title. Axis titles carry units. */\r\n title?: string;\r\n}\r\n\r\n/** Transforms run in order before drawing. */\r\nexport type Transform =\r\n | { type: \"aggregate\"; groupBy: string[]; op: \"sum\" | \"mean\" | \"min\" | \"max\" | \"count\"; field?: string; as: string }\r\n | { type: \"bin\"; field: string; maxBins?: number; as: string }\r\n | { type: \"stack\"; field: string; groupBy?: string[]; offset?: \"zero\" | \"normalize\" }\r\n | { type: \"percent-of-total\"; field: string; as: string }\r\n | { type: \"sort\"; field: string; order?: \"asc\" | \"desc\" }\r\n | { type: \"top-n\"; field: string; n: number; otherLabel?: string }\r\n | { type: \"rolling-average\"; field: string; window: number; as: string }\r\n | { type: \"cumulative-sum\"; field: string; as: string }\r\n | { type: \"period-change\"; field: string; periods?: number; as: string }\r\n | { type: \"downsample\"; method: \"lttb\"; field: string; threshold: number };\r\n\r\n/** Meaning of a labeled mark. Status tones use muted status colors and a label, never color alone. */\r\nexport type AnnotationTone = \"neutral\" | \"good\" | \"warning\" | \"critical\";\r\n\r\n/** Targets, limits and forecasts are always labeled marks, never unlabeled lines. */\r\nexport type Annotation =\r\n | { type: \"reference-line\"; x?: number | string; y?: number | string; label: string; tone?: AnnotationTone; axis?: \"y\" | \"y2\" }\r\n | { type: \"reference-band\"; from: number | string; to: number | string; axis: \"x\" | \"y\"; label: string; tone?: AnnotationTone }\r\n | { type: \"text\"; x: number | string; y: number | string; text: string }\r\n | { type: \"forecast\"; from: number | string; label: string }\r\n /** A labeled point on the plot, for example a peak or an incident. */\r\n | { type: \"point\"; x: number | string; y: number | string; label: string; tone?: AnnotationTone };\r\n\r\nexport interface ChartInteractions {\r\n tooltip?: boolean;\r\n /** Brush to filter other charts and grids through the shared filter store. */\r\n brush?: boolean;\r\n /** Click a legend entry to isolate a series. */\r\n legendIsolate?: boolean;\r\n zoom?: boolean;\r\n /** Click to drill down through the shared filter store. */\r\n drilldown?: boolean;\r\n}\r\n\r\n/** Accessibility fields. Every chart has an accessible name, a description and a table view. */\r\nexport interface ChartA11y {\r\n /** The long description read to screen reader users. */\r\n description: string;\r\n /** Show the underlying data as a table behind a \"View as table\" toggle. Defaults to true. */\r\n table?: boolean;\r\n}\r\n\r\nexport type RendererChoice = \"svg\" | \"canvas\" | \"auto\";\r\n\r\n/* ------------------------------------------------------------------ */\r\n/* Additions: series, scales, stacks, axes, thresholds, legend, gauge */\r\n/* ------------------------------------------------------------------ */\r\n\r\n/**\r\n * - `linear`, `log`, `sqrt`: quantitative.\r\n * - `time`: a UTC time axis.\r\n * - `band`: one slot per category, for bars.\r\n * - `ordinal`: one position per category, for lines and points.\r\n */\r\nexport type ScaleType = \"linear\" | \"log\" | \"sqrt\" | \"time\" | \"band\" | \"ordinal\";\r\n\r\nexport interface ScaleSpec {\r\n type?: ScaleType;\r\n /**\r\n * Quantitative and time scales: `[min, max]` (time values are ISO strings or\r\n * epoch milliseconds). Band and ordinal scales: the category order.\r\n */\r\n domain?: ReadonlyArray<number | string>;\r\n /** Round the domain to nice values. Defaults to true for quantitative scales. */\r\n nice?: boolean;\r\n /** Force zero into a quantitative domain. Bars always include zero. */\r\n zero?: boolean;\r\n /** Log base. Defaults to 10. */\r\n base?: number;\r\n /** Flip the direction of the scale. */\r\n reverse?: boolean;\r\n /** Fraction of a band left empty between bands, from 0 to 0.9. */\r\n padding?: number;\r\n}\r\n\r\n/** `stacked` adds up, `normalized` shows shares of 100%, `diverging` stacks negatives below zero. */\r\nexport type StackMode = \"none\" | \"stacked\" | \"normalized\" | \"diverging\";\r\n\r\nexport interface AxisSpec {\r\n /** Hide the axis entirely. */\r\n show?: boolean;\r\n title?: string;\r\n /** Draw gridlines for this axis. Defaults to true for the value axis only. */\r\n grid?: boolean;\r\n /** Approximate tick count; the scale picks nice values near it. */\r\n ticks?: number;\r\n /** Explicit tick values, which override `ticks`. */\r\n tickValues?: ReadonlyArray<number | string>;\r\n format?: string;\r\n /** Label angle in degrees, or `auto` to rotate only when labels do not fit. */\r\n labelAngle?: number | \"auto\";\r\n /** Longest tick label in characters before it is shortened with an ellipsis. */\r\n maxLabelLength?: number;\r\n}\r\n\r\nexport type MarkerShape = \"circle\" | \"square\" | \"diamond\" | \"triangle\" | \"triangle-down\" | \"cross\" | \"plus\";\r\n\r\nexport type DashStyle = \"solid\" | \"dashed\" | \"dotted\" | \"dash-dot\";\r\n\r\n/** Named series colors. Everything resolves to a kit token at render time. */\r\nexport type SeriesTone =\r\n | \"accent\"\r\n | \"muted\"\r\n | \"reference\"\r\n | \"good\"\r\n | \"warning\"\r\n | \"critical\"\r\n | \"categorical-1\"\r\n | \"categorical-2\"\r\n | \"categorical-3\"\r\n | \"categorical-4\"\r\n | \"categorical-5\"\r\n | \"categorical-6\"\r\n | \"categorical-7\"\r\n | \"categorical-8\";\r\n\r\n/** Marks a single series can draw in a combo chart. */\r\nexport type SeriesMark = \"bar\" | \"line\" | \"area\" | \"step\" | \"scatter\";\r\n\r\nexport interface SeriesPoint {\r\n /** Category label, number, epoch milliseconds, or an ISO date string. */\r\n x: string | number;\r\n y: number | null;\r\n /** Bubble size. */\r\n size?: number;\r\n /** Overrides the x label in tooltips, tables and announcements. */\r\n label?: string;\r\n}\r\n\r\nexport interface SeriesSpec {\r\n /** Stable identifier used by legend toggles and callbacks. */\r\n id: string;\r\n /** Display name. Defaults to the id. */\r\n label?: string;\r\n /** In a combo chart, which mark this series draws. Defaults to `bar`. */\r\n mark?: SeriesMark;\r\n /** Wide format: read this column of the shared rows as the y value. */\r\n field?: string;\r\n /** Own points, instead of reading shared rows. */\r\n points?: ReadonlyArray<SeriesPoint>;\r\n /** Which value axis the series uses. `y2` needs `dualAxis: true`. */\r\n axis?: \"y\" | \"y2\";\r\n tone?: SeriesTone;\r\n /** Point marker shape. Defaults to a different shape per series. */\r\n marker?: MarkerShape;\r\n /** Line dash pattern. Defaults to a different pattern per series. */\r\n dash?: DashStyle;\r\n /** Series with the same stack key stack together. */\r\n stack?: string;\r\n /** Start hidden. The legend can show it. */\r\n hidden?: boolean;\r\n /** Number format for this series in tooltips and tables. */\r\n format?: string;\r\n}\r\n\r\nexport interface LegendSpec {\r\n /** `auto` shows the legend when there is more than one series (or more than one slice). */\r\n show?: boolean | \"auto\";\r\n position?: \"top\" | \"bottom\";\r\n}\r\n\r\n/** A labeled limit or target. Breaching points get an extra mark and tooltip note, not only a color. */\r\nexport interface Threshold {\r\n value: number | string;\r\n label: string;\r\n axis?: \"x\" | \"y\" | \"y2\";\r\n tone?: AnnotationTone;\r\n /** `above` flags values over the threshold, `below` flags values under it. */\r\n breach?: \"above\" | \"below\";\r\n}\r\n\r\nexport interface GaugeSpec {\r\n /** A literal value. Without it, the first row of the `value` (or `y`) encoding is used. */\r\n value?: number;\r\n min?: number;\r\n max?: number;\r\n target?: number;\r\n /** Ranges behind the value arc, for example good, warning and critical. */\r\n bands?: ReadonlyArray<{ from: number; to: number; tone: AnnotationTone; label?: string }>;\r\n /**\r\n * Number format for the value, minimum, maximum and target. `percent` shows\r\n * the share of the min to max range: 64 of 100 and 0.64 of 1 both read 64%.\r\n * When `max` is omitted, a percent gauge with a value from 0 to 1 ends at 1.\r\n */\r\n format?: string;\r\n}\r\n\r\nexport interface DonutSpec {\r\n /** Inner radius as a fraction of the outer radius, from 0 to 0.9. Defaults to 0.62 for `donut`, 0 for `pie`. */\r\n innerRadius?: number;\r\n /** Slices beyond this count merge into \"Other\". Defaults to 5, the spec's donut limit. */\r\n maxSlices?: number;\r\n /** Center label. Defaults to the total of all slices. */\r\n centerLabel?: string;\r\n /** Caption under the center value. */\r\n centerCaption?: string;\r\n}\r\n\r\nexport interface WaterfallSpec {\r\n /** Categories (x values) that are totals: their bars run from zero to the running sum. */\r\n totals?: ReadonlyArray<string>;\r\n}\r\n\r\nexport interface ChartLabels {\r\n /** Label line and area series at their last point instead of relying on the legend. `auto` does so for 2 to 5 series. */\r\n direct?: boolean | \"auto\";\r\n /** Print values on bars and slices. */\r\n values?: boolean;\r\n}\r\n\r\nexport interface BinSpec {\r\n /** Approximate number of bins. */\r\n count?: number;\r\n /** Explicit bin boundaries. */\r\n thresholds?: ReadonlyArray<number>;\r\n}\r\n\r\nexport interface ChartSpec {\r\n /** Schema version, so saved specs can be migrated. */\r\n version?: 1;\r\n /** The finding: a sentence that states what the chart shows. */\r\n title: string;\r\n /** Scope and units. */\r\n subtitle?: string;\r\n /** Where the data came from. Required in reports. */\r\n source?: string;\r\n data: ChartData;\r\n mark: ChartType;\r\n x?: Encoding;\r\n y?: Encoding;\r\n /** Series that differ in kind use the categorical palette, capped at 8 hues. */\r\n color?: Encoding;\r\n size?: Encoding;\r\n /** The measure for heatmaps and gauges. Heatmaps fall back to `color`. */\r\n value?: Encoding;\r\n /** One accent on what the title is about, for example `week == 'W4'`. */\r\n highlight?: { where: string };\r\n transforms?: Transform[];\r\n annotations?: Annotation[];\r\n interactions?: ChartInteractions;\r\n /** Dual axes only behind an explicit flag, which adds a visible warning. */\r\n dualAxis?: boolean;\r\n renderer?: RendererChoice;\r\n /** Whether axes mirror in right-to-left locales. */\r\n direction?: \"ltr\" | \"rtl\" | \"auto\";\r\n a11y: ChartA11y;\r\n\r\n /** Explicit series. Without them, series come from `y`, or from the groups of `color`. */\r\n series?: SeriesSpec[];\r\n /** How series combine on one axis. Defaults to `none` (grouped bars, overlapping lines). */\r\n stack?: StackMode;\r\n /** `horizontal` bars put the categories on the vertical axis. */\r\n orientation?: \"vertical\" | \"horizontal\";\r\n scales?: { x?: ScaleSpec; y?: ScaleSpec; y2?: ScaleSpec };\r\n axes?: { x?: AxisSpec; y?: AxisSpec; y2?: AxisSpec };\r\n thresholds?: Threshold[];\r\n legend?: LegendSpec;\r\n labels?: ChartLabels;\r\n /** Histogram bins. */\r\n bins?: BinSpec;\r\n gauge?: GaugeSpec;\r\n donut?: DonutSpec;\r\n waterfall?: WaterfallSpec;\r\n /** IANA time zone used to format time values. Time axes are laid out in UTC. Defaults to `UTC`. */\r\n timeZone?: string;\r\n}\r\n\r\n/** Identity helper that gives authoring-time type checking and autocomplete. */\r\nexport function defineChartSpec(spec: ChartSpec): ChartSpec {\r\n return spec;\r\n}\r\n","/**\n * Spec validation. `validateChartSpec` accepts `unknown` so it can check JSON\n * from files, databases or an AI, and returns a list of issues instead of\n * throwing. Messages are for developers (English); the renderers never show\n * them.\n */\nimport { resolveRows, readField, toNumber, type Datasets } from \"./data\";\nimport { checkWhere } from \"./expr\";\nimport { isSupportedMark } from \"./model\";\nimport { SVG_POINT_LIMIT } from \"./renderer\";\nimport { chartTypes, type ChartSpec, type ChartType } from \"./spec\";\n\nexport type IssueSeverity = \"error\" | \"warning\";\n\nexport interface ChartIssue {\n severity: IssueSeverity;\n /** Stable code, for example `title-missing`. */\n code: string;\n /** Where in the spec, for example `series[1].axis`. */\n path: string;\n message: string;\n}\n\nexport interface ValidateOptions {\n /** Datasets for `data.ref`. With them, missing refs are errors and fields are checked against rows. */\n datasets?: Datasets | undefined;\n}\n\nconst FIELD_TYPES = [\"category\", \"number\", \"time\", \"boolean\"];\nconst SCALE_TYPES = [\"linear\", \"log\", \"sqrt\", \"time\", \"band\", \"ordinal\"];\nconst STACK_MODES = [\"none\", \"stacked\", \"normalized\", \"diverging\"];\nconst CARTESIAN: ReadonlySet<string> = new Set([\"bar\", \"lollipop\", \"line\", \"area\", \"step\", \"sparkline\", \"combo\", \"scatter\", \"bubble\", \"waterfall\"]);\nconst MAX_SERIES = 8;\nconst PROBE_ROWS = 200;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\n/** Checks a spec and returns every problem found. An empty list means the spec is valid. */\nexport function validateChartSpec(input: unknown, options: ValidateOptions = {}): ChartIssue[] {\n const issues: ChartIssue[] = [];\n const add = (severity: IssueSeverity, code: string, path: string, message: string): void => {\n issues.push({ severity, code, path, message });\n };\n if (!isRecord(input)) {\n add(\"error\", \"spec-not-object\", \"\", \"A chart spec must be an object.\");\n return issues;\n }\n const spec = input as unknown as Partial<ChartSpec> & Record<string, unknown>;\n\n if (typeof spec.title !== \"string\" || spec.title.trim() === \"\") {\n add(\"error\", \"title-missing\", \"title\", \"Every chart needs a title that states the finding.\");\n }\n if (!isRecord(spec.a11y) || typeof spec.a11y.description !== \"string\" || spec.a11y.description.trim() === \"\") {\n add(\"error\", \"a11y-description-missing\", \"a11y.description\", \"Every chart needs an accessibility description.\");\n }\n if (spec.version !== undefined && spec.version !== 1) {\n add(\"warning\", \"version-unknown\", \"version\", `Unknown schema version ${String(spec.version)}.`);\n }\n\n const mark = spec.mark as ChartType | undefined;\n if (typeof mark !== \"string\" || !(chartTypes as ReadonlyArray<string>).includes(mark)) {\n add(\"error\", \"mark-invalid\", \"mark\", `Unknown chart type \"${String(mark)}\".`);\n } else if (!isSupportedMark(mark)) {\n add(\"warning\", \"mark-unsupported\", \"mark\", `The ${mark} chart type is not drawn by the SVG renderer; it needs a different renderer.`);\n }\n\n /* ----------------------------------------------------------- data */\n let rows: ReadonlyArray<Record<string, unknown>> | undefined;\n const data = spec.data as unknown;\n if (!isRecord(data)) {\n add(\"error\", \"data-missing\", \"data\", \"A spec needs `data`: `{ values }` or `{ ref }`.\");\n } else if (\"values\" in data) {\n if (!Array.isArray(data.values)) add(\"error\", \"data-invalid\", \"data.values\", \"`data.values` must be an array of rows.\");\n else rows = data.values as ReadonlyArray<Record<string, unknown>>;\n } else if (typeof data.ref === \"string\") {\n if (options.datasets) {\n rows = options.datasets[data.ref];\n if (!rows) add(\"error\", \"dataset-missing\", \"data.ref\", `Dataset \"${data.ref}\" was not provided.`);\n }\n } else {\n add(\"error\", \"data-invalid\", \"data\", \"`data` needs `values` or `ref`.\");\n }\n\n const hasOwnSeriesData =\n Array.isArray(spec.series) && spec.series.length > 0 && spec.series.every((series) => isRecord(series) && Array.isArray(series.points));\n const literalGauge =\n (spec.mark === \"gauge\" || spec.mark === \"progress-ring\") && isRecord(spec.gauge) && typeof spec.gauge.value === \"number\";\n if (rows && rows.length === 0 && !hasOwnSeriesData && !literalGauge) {\n add(\"warning\", \"data-empty\", \"data\", \"The dataset has no rows; the chart will show its empty state.\");\n }\n\n /* -------------------------------------------------- encodings/fields */\n const fieldExists = (field: string): boolean => {\n if (!rows || rows.length === 0) return true;\n return rows.slice(0, PROBE_ROWS).some((row) => isRecord(row) && readField(row, field) !== undefined);\n };\n const checkEncoding = (name: \"x\" | \"y\" | \"color\" | \"size\" | \"value\"): void => {\n const encoding = spec[name] as unknown;\n if (encoding === undefined) return;\n if (!isRecord(encoding) || typeof encoding.field !== \"string\" || encoding.field === \"\") {\n add(\"error\", \"encoding-invalid\", name, `\\`${name}\\` needs a \\`field\\`.`);\n return;\n }\n if (typeof encoding.type !== \"string\" || !FIELD_TYPES.includes(encoding.type)) {\n add(\"error\", \"encoding-type-invalid\", `${name}.type`, `\\`${name}.type\\` must be one of ${FIELD_TYPES.join(\", \")}.`);\n }\n if (!fieldExists(encoding.field)) {\n add(\"warning\", \"field-unknown\", `${name}.field`, `No row has the field \"${encoding.field}\".`);\n }\n if ((name === \"y\" || name === \"size\") && encoding.type !== undefined && encoding.type !== \"number\" && mark && CARTESIAN.has(mark)) {\n add(\"warning\", \"encoding-type-unexpected\", `${name}.type`, `\\`${name}.type\\` should be \"number\" for ${mark} charts.`);\n }\n };\n ([\"x\", \"y\", \"color\", \"size\", \"value\"] as const).forEach(checkEncoding);\n\n const series = Array.isArray(spec.series) ? (spec.series as unknown[]) : undefined;\n const seriesList = series?.filter(isRecord) ?? [];\n const hasSeries = seriesList.length > 0;\n const seriesNeedY = hasSeries && seriesList.every((entry) => typeof entry.field === \"string\" || Array.isArray(entry.points));\n\n if (mark) {\n const needX = CARTESIAN.has(mark) || mark === \"histogram\" || mark === \"heatmap\" || mark === \"pie\" || mark === \"donut\";\n if (needX && !spec.x && !hasOwnSeriesData) add(\"error\", \"encoding-x-missing\", \"x\", `A ${mark} chart needs an \\`x\\` encoding.`);\n const needY = CARTESIAN.has(mark) || mark === \"pie\" || mark === \"donut\" || mark === \"heatmap\";\n if (needY && !spec.y && !seriesNeedY) add(\"error\", \"encoding-y-missing\", \"y\", `A ${mark} chart needs a \\`y\\` encoding or series with a \\`field\\` or \\`points\\`.`);\n if (mark === \"heatmap\" && !spec.value && !spec.color) add(\"error\", \"encoding-value-missing\", \"value\", \"A heatmap needs a `value` encoding for the cell color.\");\n if (mark === \"bubble\" && !spec.size && !seriesList.some((entry) => Array.isArray(entry.points) && entry.points.some((p) => isRecord(p) && p.size !== undefined))) {\n add(\"warning\", \"bubble-size-missing\", \"size\", \"A bubble chart should have a `size` encoding; without it every bubble is the same size.\");\n }\n if ((mark === \"gauge\" || mark === \"progress-ring\") && !(isRecord(spec.gauge) && typeof spec.gauge.value === \"number\") && !spec.value && !spec.y) {\n add(\"error\", \"gauge-value-missing\", \"gauge.value\", \"A gauge needs `gauge.value` or a `value` encoding.\");\n }\n }\n\n /* --------------------------------------------------------- series */\n const seenIds = new Set<string>();\n seriesList.forEach((entry, index) => {\n const path = `series[${index}]`;\n if (typeof entry.id !== \"string\" || entry.id === \"\") {\n add(\"error\", \"series-id-missing\", `${path}.id`, \"Every series needs an `id`.\");\n } else if (seenIds.has(entry.id)) {\n add(\"error\", \"series-id-duplicate\", `${path}.id`, `Series id \"${entry.id}\" is used twice.`);\n } else {\n seenIds.add(entry.id);\n }\n if (typeof entry.field !== \"string\" && !Array.isArray(entry.points) && !spec.y) {\n add(\"error\", \"series-data-missing\", path, \"A series needs a `field` or its own `points`.\");\n }\n if (typeof entry.field === \"string\" && !fieldExists(entry.field)) {\n add(\"warning\", \"field-unknown\", `${path}.field`, `No row has the field \"${entry.field}\".`);\n }\n if (entry.axis === \"y2\" && spec.dualAxis !== true) {\n add(\"error\", \"series-axis-needs-dual-axis\", `${path}.axis`, \"A second value axis needs `dualAxis: true`, which adds a visible warning to the chart.\");\n }\n if (entry.mark !== undefined && mark !== \"combo\") {\n add(\"warning\", \"series-mark-ignored\", `${path}.mark`, \"`series.mark` only applies when the chart type is `combo`.\");\n }\n });\n if (spec.dualAxis === true && !seriesList.some((entry) => entry.axis === \"y2\")) {\n add(\"warning\", \"dual-axis-unused\", \"dualAxis\", \"`dualAxis` is set but no series uses `axis: \\\"y2\\\"`.\");\n }\n if (mark === \"combo\" && hasSeries) {\n const marks = new Set(seriesList.map((entry) => (typeof entry.mark === \"string\" ? entry.mark : \"bar\")));\n if (marks.size < 2) add(\"warning\", \"combo-single-mark\", \"series\", \"A combo chart mixes marks, for example `bar` and `line`; all series use the same one.\");\n }\n let colorGroups = 0;\n if (rows && isRecord(spec.color) && typeof spec.color.field === \"string\") {\n const field = spec.color.field;\n colorGroups = new Set(rows.slice(0, 5000).map((row) => (isRecord(row) ? String(readField(row, field)) : \"\"))).size;\n }\n const seriesCount = hasSeries ? seriesList.length : colorGroups;\n if (seriesCount > MAX_SERIES && mark !== \"pie\" && mark !== \"donut\" && mark !== \"heatmap\") {\n add(\"warning\", \"too-many-series\", hasSeries ? \"series\" : \"color\", `${seriesCount} series; charts use at most ${MAX_SERIES} hues. Group the rest as \"Other\" (a top-n transform does this).`);\n }\n\n /* --------------------------------------------------------- scales */\n if (spec.scales !== undefined && !isRecord(spec.scales)) add(\"error\", \"scales-invalid\", \"scales\", \"`scales` must be an object.\");\n for (const axis of [\"x\", \"y\", \"y2\"] as const) {\n const scale = isRecord(spec.scales) ? spec.scales[axis] : undefined;\n if (scale === undefined) continue;\n const path = `scales.${axis}`;\n if (!isRecord(scale)) {\n add(\"error\", \"scale-invalid\", path, \"A scale must be an object.\");\n continue;\n }\n if (scale.type !== undefined && (typeof scale.type !== \"string\" || !SCALE_TYPES.includes(scale.type))) {\n add(\"error\", \"scale-type-invalid\", `${path}.type`, `Scale type must be one of ${SCALE_TYPES.join(\", \")}.`);\n }\n if ((axis === \"y\" || axis === \"y2\") && (scale.type === \"band\" || scale.type === \"ordinal\" || scale.type === \"time\")) {\n add(\"error\", \"scale-type-mismatch\", `${path}.type`, `The value axis needs a quantitative scale (linear, log or sqrt), not ${String(scale.type)}.`);\n }\n if (scale.domain !== undefined) {\n const quantitative = scale.type === undefined || [\"linear\", \"log\", \"sqrt\", \"time\"].includes(String(scale.type));\n if (!Array.isArray(scale.domain) || (quantitative && scale.domain.length !== 2)) {\n add(\"error\", \"scale-domain-invalid\", `${path}.domain`, \"A quantitative scale domain is `[min, max]`.\");\n }\n }\n if (scale.padding !== undefined && (typeof scale.padding !== \"number\" || scale.padding < 0 || scale.padding > 0.9)) {\n add(\"error\", \"scale-padding-range\", `${path}.padding`, \"Band padding must be between 0 and 0.9.\");\n }\n if (scale.type === \"log\" && rows) {\n const field = axis === \"y\" ? (spec.y as { field?: string } | undefined)?.field : undefined;\n if (field && rows.slice(0, 5000).some((row) => {\n const value = toNumber(isRecord(row) ? readField(row, field) : undefined);\n return value !== null && value <= 0;\n })) {\n add(\"error\", \"scale-log-nonpositive\", path, \"A log scale cannot show zero or negative values.\");\n }\n }\n if (axis === \"y\" && scale.type === undefined && Array.isArray(scale.domain) && typeof scale.domain[0] === \"number\" && scale.domain[0] > 0 && (mark === \"bar\" || mark === \"lollipop\" || mark === \"histogram\" || mark === \"area\")) {\n add(\"warning\", \"bar-baseline\", `${path}.domain`, \"Bars and areas should start at zero; a truncated baseline exaggerates differences.\");\n }\n }\n if (isRecord(spec.scales) && isRecord(spec.scales.y) && spec.scales.y.type === \"log\" && (mark === \"bar\" || mark === \"area\" || mark === \"histogram\")) {\n add(\"warning\", \"bar-baseline\", \"scales.y.type\", \"Bars and areas on a log scale have no zero baseline.\");\n }\n\n /* ---------------------------------------------------------- stack */\n if (spec.stack !== undefined) {\n if (typeof spec.stack !== \"string\" || !STACK_MODES.includes(spec.stack)) {\n add(\"error\", \"stack-invalid\", \"stack\", `\\`stack\\` must be one of ${STACK_MODES.join(\", \")}.`);\n } else if (spec.stack !== \"none\" && mark && ![\"bar\", \"area\", \"combo\"].includes(mark)) {\n add(\"warning\", \"stack-unsupported\", \"stack\", `Stacking applies to bar, area and combo charts; ${mark} ignores it.`);\n }\n }\n if (spec.orientation !== undefined && spec.orientation !== \"vertical\" && spec.orientation !== \"horizontal\") {\n add(\"error\", \"orientation-invalid\", \"orientation\", \"`orientation` must be vertical or horizontal.\");\n }\n if (spec.orientation === \"horizontal\" && mark && ![\"bar\", \"lollipop\", \"waterfall\"].includes(mark)) {\n add(\"warning\", \"orientation-unsupported\", \"orientation\", `Horizontal orientation applies to bar, lollipop and waterfall charts; ${mark} ignores it.`);\n }\n\n /* ----------------------------------------------------------- axes */\n if (isRecord(spec.axes)) {\n for (const axis of [\"x\", \"y\", \"y2\"] as const) {\n const config = spec.axes[axis];\n if (!isRecord(config)) continue;\n if (config.ticks !== undefined && (typeof config.ticks !== \"number\" || config.ticks < 1)) {\n add(\"error\", \"axis-ticks-invalid\", `axes.${axis}.ticks`, \"Tick count must be at least 1.\");\n }\n if (config.labelAngle !== undefined && config.labelAngle !== \"auto\" && (typeof config.labelAngle !== \"number\" || config.labelAngle < -90 || config.labelAngle > 90)) {\n add(\"error\", \"axis-label-angle\", `axes.${axis}.labelAngle`, \"Label angle is `auto` or a number from -90 to 90.\");\n }\n }\n }\n\n /* ---------------------------------------------------- annotations */\n const annotations = Array.isArray(spec.annotations) ? spec.annotations : [];\n annotations.forEach((annotation, index) => {\n const path = `annotations[${index}]`;\n if (!isRecord(annotation) || typeof annotation.type !== \"string\") {\n add(\"error\", \"annotation-invalid\", path, \"An annotation needs a `type`.\");\n return;\n }\n const label = annotation.type === \"text\" ? annotation.text : annotation.label;\n if (typeof label !== \"string\" || label.trim() === \"\") {\n add(\"error\", \"annotation-label-missing\", path, \"Targets, limits and forecasts are always labeled marks: add a label.\");\n }\n if (annotation.type === \"reference-line\" && annotation.x === undefined && annotation.y === undefined) {\n add(\"error\", \"annotation-position-missing\", path, \"A reference line needs `x` or `y`.\");\n }\n if (annotation.type === \"reference-band\" && (annotation.axis !== \"x\" && annotation.axis !== \"y\")) {\n add(\"error\", \"annotation-axis-invalid\", `${path}.axis`, \"A reference band needs `axis: \\\"x\\\"` or `\\\"y\\\"`.\");\n }\n if (![\"reference-line\", \"reference-band\", \"text\", \"forecast\", \"point\"].includes(annotation.type)) {\n add(\"error\", \"annotation-type-invalid\", `${path}.type`, `Unknown annotation type \"${annotation.type}\".`);\n }\n });\n const thresholds = Array.isArray(spec.thresholds) ? spec.thresholds : [];\n thresholds.forEach((threshold, index) => {\n const path = `thresholds[${index}]`;\n if (!isRecord(threshold)) {\n add(\"error\", \"threshold-invalid\", path, \"A threshold must be an object.\");\n return;\n }\n if (typeof threshold.label !== \"string\" || threshold.label.trim() === \"\") {\n add(\"error\", \"threshold-label-missing\", `${path}.label`, \"Thresholds are always labeled.\");\n }\n if (threshold.value === undefined || (typeof threshold.value !== \"number\" && typeof threshold.value !== \"string\")) {\n add(\"error\", \"threshold-value-invalid\", `${path}.value`, \"A threshold needs a numeric `value`.\");\n }\n if (threshold.breach !== undefined && threshold.breach !== \"above\" && threshold.breach !== \"below\") {\n add(\"error\", \"threshold-breach-invalid\", `${path}.breach`, \"`breach` is `above` or `below`.\");\n }\n if (threshold.axis === \"y2\" && spec.dualAxis !== true) {\n add(\"error\", \"series-axis-needs-dual-axis\", `${path}.axis`, \"A threshold on the second axis needs `dualAxis: true`.\");\n }\n });\n\n /* ------------------------------------------------------ transforms */\n const transforms: unknown[] = Array.isArray(spec.transforms) ? spec.transforms : [];\n transforms.forEach((transform, index) => {\n const path = `transforms[${index}]`;\n if (!isRecord(transform) || typeof transform.type !== \"string\") {\n add(\"error\", \"transform-invalid\", path, \"A transform needs a `type`.\");\n return;\n }\n const known = [\"aggregate\", \"bin\", \"stack\", \"percent-of-total\", \"sort\", \"top-n\", \"rolling-average\", \"cumulative-sum\", \"period-change\", \"downsample\"];\n if (!known.includes(transform.type)) {\n add(\"error\", \"transform-invalid\", `${path}.type`, `Unknown transform \"${transform.type}\".`);\n return;\n }\n const positiveInt = (key: string, min: number): void => {\n const value = transform[key];\n if (typeof value !== \"number\" || !Number.isFinite(value) || value < min) {\n add(\"error\", \"transform-parameter-invalid\", `${path}.${key}`, `\\`${key}\\` must be a number of at least ${min}.`);\n }\n };\n if (transform.type === \"aggregate\") {\n if (!Array.isArray(transform.groupBy) || transform.groupBy.length === 0) add(\"error\", \"transform-parameter-invalid\", `${path}.groupBy`, \"`groupBy` needs at least one field.\");\n if (![\"sum\", \"mean\", \"min\", \"max\", \"count\"].includes(String(transform.op))) add(\"error\", \"transform-parameter-invalid\", `${path}.op`, \"`op` must be sum, mean, min, max or count.\");\n if (transform.op !== \"count\" && typeof transform.field !== \"string\") add(\"error\", \"transform-parameter-invalid\", `${path}.field`, \"Only `count` works without a `field`.\");\n }\n if (transform.type === \"rolling-average\") positiveInt(\"window\", 1);\n if (transform.type === \"top-n\") positiveInt(\"n\", 1);\n if (transform.type === \"downsample\") positiveInt(\"threshold\", 3);\n if (transform.type === \"period-change\" && transform.periods !== undefined) positiveInt(\"periods\", 1);\n if (transform.type === \"bin\" && transform.maxBins !== undefined) positiveInt(\"maxBins\", 1);\n if (typeof transform.field === \"string\" && transform.type !== \"aggregate\" && !fieldExists(transform.field)) {\n add(\"warning\", \"field-unknown\", `${path}.field`, `No row has the field \"${transform.field}\".`);\n }\n });\n\n /* ------------------------------------------------------- highlight */\n if (isRecord(spec.highlight)) {\n if (typeof spec.highlight.where !== \"string\") {\n add(\"error\", \"highlight-invalid\", \"highlight.where\", \"`highlight.where` must be an expression string.\");\n } else {\n const problem = checkWhere(spec.highlight.where);\n if (problem) add(\"error\", \"highlight-expression-invalid\", \"highlight.where\", `Highlight expression does not parse: ${problem}.`);\n }\n }\n\n /* ----------------------------------------------- gauge, donut, misc */\n if (isRecord(spec.gauge)) {\n const { min, max, value } = spec.gauge as { min?: number; max?: number; value?: number };\n if (min !== undefined && max !== undefined && !(max > min)) add(\"error\", \"gauge-range-invalid\", \"gauge\", \"`gauge.max` must be greater than `gauge.min`.\");\n if (typeof value === \"number\" && min !== undefined && max !== undefined && (value < min || value > max)) {\n add(\"warning\", \"gauge-value-out-of-range\", \"gauge.value\", \"The gauge value is outside its range; the arc is clamped.\");\n }\n }\n if (isRecord(spec.donut)) {\n const radius = spec.donut.innerRadius;\n if (radius !== undefined && (typeof radius !== \"number\" || radius < 0 || radius > 0.9)) {\n add(\"error\", \"donut-inner-radius-range\", \"donut.innerRadius\", \"`innerRadius` is a fraction from 0 to 0.9.\");\n }\n if (typeof spec.donut.maxSlices === \"number\" && spec.donut.maxSlices > 5) {\n add(\"warning\", \"too-many-slices\", \"donut.maxSlices\", \"Donuts read best with at most five slices.\");\n }\n }\n if ((mark === \"pie\" || mark === \"donut\") && rows && isRecord(spec.y) && typeof spec.y.field === \"string\") {\n const field = spec.y.field;\n if (rows.slice(0, 5000).some((row) => (toNumber(isRecord(row) ? readField(row, field) : undefined) ?? 0) < 0)) {\n add(\"error\", \"pie-negative\", \"y\", \"A part-to-whole chart cannot show negative values.\");\n }\n }\n if (spec.renderer !== undefined && spec.renderer !== \"svg\" && spec.renderer !== \"canvas\" && spec.renderer !== \"auto\") {\n add(\"error\", \"renderer-invalid\", \"renderer\", \"`renderer` must be svg, canvas or auto.\");\n }\n if (spec.direction !== undefined && ![\"ltr\", \"rtl\", \"auto\"].includes(String(spec.direction))) {\n add(\"error\", \"direction-invalid\", \"direction\", \"`direction` must be ltr, rtl or auto.\");\n }\n if (isRecord(spec.interactions)) {\n for (const name of [\"brush\", \"zoom\", \"drilldown\"] as const) {\n if (spec.interactions[name] === true) {\n add(\"warning\", \"interaction-unsupported\", `interactions.${name}`, `\\`interactions.${name}\\` is part of the spec but this renderer does not implement it; it is ignored.`);\n }\n }\n }\n if (spec.renderer === \"svg\" && isRecord(data)) {\n const count = rows ? resolveRows(spec as ChartSpec, options.datasets)?.length ?? 0 : 0;\n if (count > SVG_POINT_LIMIT) {\n add(\"warning\", \"svg-point-limit\", \"renderer\", `${count} rows is above the SVG comfort limit of ${SVG_POINT_LIMIT}; consider \\`renderer: \"auto\"\\` or a downsample transform.`);\n }\n }\n\n return issues;\n}\n\n/** True when `validateChartSpec` finds no errors (warnings are allowed). */\nexport function isValidChartSpec(input: unknown, options: ValidateOptions = {}): input is ChartSpec {\n return !validateChartSpec(input, options).some((issue) => issue.severity === \"error\");\n}\n"],"mappings":"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","names":["series"]}
@@ -0,0 +1,251 @@
1
+ import * as react from 'react';
2
+ import { PointerEvent, KeyboardEvent, CSSProperties, ReactNode, MouseEvent } from 'react';
3
+ import { t as ChartPointInfo } from './echarts-option-DHFfEvN1.js';
4
+ import { ChartSpec, Datasets, ChartMessagesInput, RendererChoice, RenderTarget, ChartType, LegendEntry, ChartThemeColors } from './index.js';
5
+ import { ThemeName } from '@x12i/tokens';
6
+
7
+ /**
8
+ * The small part of the ECharts API the canvas renderer uses, typed locally so
9
+ * the declaration files do not depend on `echarts` being installed.
10
+ */
11
+ interface EChartsInstance {
12
+ setOption(option: object, opts?: {
13
+ notMerge?: boolean;
14
+ lazyUpdate?: boolean;
15
+ }): void;
16
+ resize(size?: {
17
+ width?: number;
18
+ height?: number;
19
+ }): void;
20
+ dispose(): void;
21
+ on?(event: string, handler: (params: unknown) => void): void;
22
+ }
23
+ interface EChartsModule {
24
+ init(element: HTMLElement, theme?: string | object | null, options?: {
25
+ renderer?: "canvas" | "svg";
26
+ width?: number;
27
+ height?: number;
28
+ }): EChartsInstance;
29
+ }
30
+ /** Resolves the `echarts` module. Pass your own to control bundling or to stub it in tests. */
31
+ type EChartsLoader = () => Promise<EChartsModule>;
32
+ /**
33
+ * The default loader: a lazy `import("echarts")`, so apps that only draw SVG
34
+ * never download it. A bundler resolves this import at build time, so install
35
+ * `echarts` (an optional peer dependency) or pass `loadEcharts`.
36
+ */
37
+ declare const defaultEChartsLoader: EChartsLoader;
38
+
39
+ /** Height used when `height` is not set. */
40
+ declare function defaultChartHeight(mark: ChartType): number;
41
+ interface ChartProps {
42
+ /** The chart definition. Memoize it (or define it outside the component) so it is not rebuilt on every render. */
43
+ spec: ChartSpec;
44
+ /** Named datasets for `spec.data.ref`. Falls back to `ChartConfigProvider`. */
45
+ datasets?: Datasets | undefined;
46
+ /** Width in layout units (CSS pixels). By default the chart fills its container and follows its size. */
47
+ width?: number | undefined;
48
+ /** Height in layout units. Defaults by chart type (320, a sparkline is 48). */
49
+ height?: number | undefined;
50
+ /** BCP 47 locale for numbers, dates and plural rules. */
51
+ locale?: string | undefined;
52
+ /** Message overrides for every word the chart shows or announces. */
53
+ messages?: ChartMessagesInput | undefined;
54
+ /** Text direction. Time and category axes mirror in `rtl`. Defaults to the page, then the locale. */
55
+ dir?: "ltr" | "rtl" | undefined;
56
+ /** Overrides `spec.renderer`. `auto` picks canvas for very large data. */
57
+ renderer?: RendererChoice | undefined;
58
+ /** `print` and `report` always render SVG. */
59
+ target?: RenderTarget | undefined;
60
+ /** Loads `echarts` for the canvas renderer. */
61
+ loadEcharts?: EChartsLoader | undefined;
62
+ /** Show the loading state, keeping the chart's height. */
63
+ loading?: boolean | undefined;
64
+ /** Truthy shows the error state. */
65
+ error?: unknown;
66
+ /** Adds a retry button to the error state. */
67
+ onRetry?: (() => void) | undefined;
68
+ /** Controlled list of hidden series (or slice) ids. */
69
+ hiddenSeries?: ReadonlyArray<string> | undefined;
70
+ defaultHiddenSeries?: ReadonlyArray<string> | undefined;
71
+ onHiddenSeriesChange?: ((ids: string[]) => void) | undefined;
72
+ /** False draws a static, non-focusable image with a text description. Defaults to true. */
73
+ interactive?: boolean | undefined;
74
+ /** Tooltip on pointer hover and keyboard focus. Defaults to `spec.interactions.tooltip !== false`. */
75
+ tooltip?: boolean | undefined;
76
+ /** Called on click, Enter or Space on a point. */
77
+ onPointActivate?: ((info: ChartPointInfo, event: PointerEvent<SVGSVGElement> | KeyboardEvent<SVGSVGElement>) => void) | undefined;
78
+ /** Called when the hovered or focused point changes. */
79
+ onActivePointChange?: ((info: ChartPointInfo | null) => void) | undefined;
80
+ className?: string | undefined;
81
+ style?: CSSProperties | undefined;
82
+ id?: string | undefined;
83
+ }
84
+ /**
85
+ * One chart from a `ChartSpec`: an accessible SVG plot (or an ECharts canvas
86
+ * for very large data) with legend, tooltip, keyboard navigation, states and a
87
+ * generated text description. Wrap it in `ChartFrame` or `ChartCard` for a
88
+ * title, toolbar and data table.
89
+ */
90
+ declare function Chart(props: ChartProps): react.JSX.Element;
91
+ /** The SVG renderer only: always vector, always keyboard navigable. */
92
+ declare function SvgChart(props: Omit<ChartProps, "renderer">): react.JSX.Element;
93
+ /**
94
+ * The ECharts canvas renderer: `echarts` loads lazily on first render, colors
95
+ * come from the `--kit-*` variables at runtime and follow `data-theme`. Falls
96
+ * back to SVG, with a note, when echarts is not available.
97
+ */
98
+ declare function EChartsChart(props: Omit<ChartProps, "renderer">): react.JSX.Element;
99
+
100
+ type ChartView = "chart" | "table";
101
+ interface ChartFrameProps extends Omit<ChartProps, "className" | "style" | "id"> {
102
+ /** Heading level of the title, 2 to 6. Defaults to 3. */
103
+ headingLevel?: 2 | 3 | 4 | 5 | 6;
104
+ /** Show the toolbar with the table toggle and CSV download. Defaults to true. */
105
+ toolbar?: boolean;
106
+ /** Extra buttons placed after the built-in actions. */
107
+ actions?: ReactNode;
108
+ /** Controlled view. */
109
+ view?: ChartView;
110
+ defaultView?: ChartView;
111
+ onViewChange?: (view: ChartView) => void;
112
+ /** Rows shown in the table. The CSV always has every row. Defaults to 200. */
113
+ tableMaxRows?: number;
114
+ /** File name for the CSV download, without extension. Defaults to the title. */
115
+ csvFileName?: string;
116
+ /** Replaces the browser download, for example to send the CSV to your own handler. */
117
+ onDownload?: (csv: string, fileName: string) => void;
118
+ /** Visual treatment: plain, or a bordered card surface. */
119
+ variant?: "plain" | "card";
120
+ className?: string;
121
+ style?: CSSProperties;
122
+ id?: string;
123
+ }
124
+ /**
125
+ * A chart as a figure: a heading and subtitle in a `<figcaption>`, a toolbar
126
+ * (view as table, download CSV), the chart, the data source, and the data table.
127
+ * The table is always in the document: visible in table view, otherwise hidden
128
+ * visually but available to assistive technology as the full text alternative.
129
+ */
130
+ declare function ChartFrame({ headingLevel, toolbar, actions, view: viewProp, defaultView, onViewChange, tableMaxRows, csvFileName, onDownload, variant, className, style, id, ...chartProps }: ChartFrameProps): react.JSX.Element;
131
+ /** A `ChartFrame` on a bordered card surface. */
132
+ declare function ChartCard(props: Omit<ChartFrameProps, "variant">): react.JSX.Element;
133
+
134
+ interface ChartDataTableProps {
135
+ spec: ChartSpec;
136
+ datasets?: Datasets | undefined;
137
+ locale?: string | undefined;
138
+ messages?: ChartMessagesInput | undefined;
139
+ /** Rows to render. The CSV export always has every row. Defaults to 200. */
140
+ maxRows?: number;
141
+ /** Keep the table in the accessibility tree but out of sight. */
142
+ hidden?: boolean;
143
+ className?: string;
144
+ id?: string;
145
+ }
146
+ /**
147
+ * The data behind a chart, as a real table: `<caption>`, column headers,
148
+ * row headers, tabular numbers. It is the full text alternative to the plot, so
149
+ * `ChartFrame` always renders it, visible or visually hidden.
150
+ */
151
+ declare function ChartDataTable({ spec, datasets, locale, messages, maxRows, hidden, className, id }: ChartDataTableProps): react.JSX.Element;
152
+
153
+ interface ChartGridProps {
154
+ children?: ReactNode;
155
+ /** Fixed number of columns. Without it, columns fill the width and wrap. */
156
+ columns?: number;
157
+ /** Accessible name when the grid is a meaningful group, for example "Key metrics". */
158
+ label?: string;
159
+ className?: string;
160
+ style?: CSSProperties;
161
+ }
162
+ /**
163
+ * Lays several charts or cards out in a responsive grid. Columns wrap to the
164
+ * container width unless `columns` fixes them.
165
+ */
166
+ declare function ChartGrid({ children, columns, label, className, style }: ChartGridProps): react.JSX.Element;
167
+
168
+ interface ChartLegendProps {
169
+ entries: ReadonlyArray<LegendEntry>;
170
+ /** Accessible name of the legend group. */
171
+ label: string;
172
+ position: "top" | "bottom";
173
+ patternPrefix: string;
174
+ /** Called when a toggleable entry is clicked. */
175
+ onToggle?: ((entry: LegendEntry, event: MouseEvent<HTMLButtonElement>) => void) | undefined;
176
+ }
177
+ /**
178
+ * The legend. Series entries are buttons with `aria-pressed`, so toggling is
179
+ * keyboard and screen reader friendly. Keys that explain a mark (a waterfall's
180
+ * increase and decrease) are plain text. Each glyph also encodes the series by
181
+ * shape, dash or pattern, so color is never the only channel.
182
+ */
183
+ declare function ChartLegend({ entries, label, position, patternPrefix, onToggle }: ChartLegendProps): react.JSX.Element | null;
184
+ interface ColorScaleLegendProps {
185
+ kind: "sequential" | "diverging";
186
+ min: string;
187
+ max: string;
188
+ /** Accessible name, for example the value title. */
189
+ label: string;
190
+ }
191
+ /** The ramp behind a heatmap, with its lowest and highest value. */
192
+ declare function ColorScaleLegend({ kind, min, max, label }: ColorScaleLegendProps): react.JSX.Element;
193
+
194
+ type ChartStateKind = "loading" | "empty" | "error" | "unsupported" | "missing-data";
195
+ interface ChartStateProps {
196
+ kind: ChartStateKind;
197
+ message: string;
198
+ /** Height to reserve so the layout does not jump when data arrives. */
199
+ height: number;
200
+ /** Shown as a button in the error state. */
201
+ onRetry?: (() => void) | undefined;
202
+ retryLabel?: string | undefined;
203
+ /** Draw over the plot area instead of in the flow (the canvas loading state). */
204
+ overlay?: boolean;
205
+ }
206
+ /**
207
+ * Loading, empty, error, unsupported and missing-data states. They keep the
208
+ * chart's height, announce politely (`role="status"`), and the error state
209
+ * announces assertively (`role="alert"`) with an optional retry.
210
+ */
211
+ declare function ChartState({ kind, message, height, onRetry, retryLabel, overlay }: ChartStateProps): react.JSX.Element;
212
+
213
+ /** Defaults that every chart below a `ChartConfigProvider` inherits. */
214
+ interface ChartConfig {
215
+ /** BCP 47 locale for numbers, dates, lists and plural rules. */
216
+ locale?: string | undefined;
217
+ /** Message overrides, merged over `defaultMessages`. */
218
+ messages?: ChartMessagesInput | undefined;
219
+ /** Text direction. Without it, charts follow the page (`dir` attribute), then the locale. */
220
+ direction?: "ltr" | "rtl" | undefined;
221
+ /** Named datasets for specs that use `data.ref`. */
222
+ datasets?: Datasets | undefined;
223
+ /** Loads `echarts` for the canvas renderer. Defaults to `import("echarts")`. */
224
+ loadEcharts?: EChartsLoader | undefined;
225
+ /** `print` and `report` always use the vector SVG renderer. */
226
+ target?: RenderTarget | undefined;
227
+ }
228
+ /** Merges two message overrides; the second wins, and `markNames` merge key by key. */
229
+ declare function mergeMessages(base: ChartMessagesInput | undefined, override: ChartMessagesInput | undefined): ChartMessagesInput | undefined;
230
+ interface ChartConfigProviderProps extends ChartConfig {
231
+ children?: ReactNode;
232
+ }
233
+ /**
234
+ * Sets locale, messages, direction, datasets and the echarts loader for every
235
+ * chart inside. Nested providers merge, and the inner one wins.
236
+ */
237
+ declare function ChartConfigProvider({ children, locale, messages, direction, datasets, loadEcharts, target }: ChartConfigProviderProps): react.JSX.Element;
238
+ /** The inherited chart defaults. */
239
+ declare function useChartConfig(): ChartConfig;
240
+
241
+ /** The theme in effect at an element: the nearest `data-theme`, else the root's, else light. */
242
+ declare function themeAt(element: Element | null): ThemeName;
243
+ /**
244
+ * Reads the `--kit-*` custom properties that apply at an element, so a canvas
245
+ * follows the active theme. Where a variable is missing (the stylesheet is not
246
+ * loaded, or a test environment), the token constants for the current theme fill in.
247
+ */
248
+ declare function readThemeColors(element: Element | null): ChartThemeColors;
249
+
250
+ export { Chart, ChartCard, ChartConfigProvider, ChartDataTable, ChartFrame, ChartGrid, ChartLegend, ChartPointInfo, ChartState, ColorScaleLegend, EChartsChart, SvgChart, defaultChartHeight, defaultEChartsLoader, mergeMessages, readThemeColors, themeAt, useChartConfig };
251
+ export type { ChartConfig, ChartConfigProviderProps, ChartDataTableProps, ChartFrameProps, ChartGridProps, ChartLegendProps, ChartProps, ChartStateKind, ChartStateProps, ChartView, ColorScaleLegendProps, EChartsInstance, EChartsLoader, EChartsModule };