import type { ChartData, ChartDataset } from './chart-data-transformer'; import type { ChartPanelType } from './chart-panel'; import type { LegendPosition, TextAlign } from './model/chart-model'; export interface ChartRenderOptions { type: ChartPanelType | 'bar' | 'line' | 'pie' | 'doughnut'; width?: number; height?: number; padding?: number; showLegend?: boolean; showGrid?: boolean; showValues?: boolean; barWidth?: number; lineWidth?: number; smooth?: boolean; fontFamily?: string; fontSize?: number; /** * Axis-label / body text color. Empty string means "resolve from the active * theme token" — the renderer fills it in per render. */ textColor?: string; /** Grid-line / axis-line color. Empty string means "resolve from theme". */ gridColor?: string; backgroundColor?: string; animationDuration?: number; /** Main chart title drawn above the plot. Empty string hides the band. */ title?: string; titleColor?: string; titleFontSize?: number; titleAlign?: TextAlign; /** Subtitle drawn beneath the title. Empty string hides it. */ subtitle?: string; subtitleColor?: string; subtitleFontSize?: number; subtitleAlign?: TextAlign; /** Placement of the legend relative to the plot. */ legendPosition?: LegendPosition; /** Horizontal (category) axis title. Empty string hides it. */ xAxisTitle?: string; /** Vertical (value) axis title. Empty string hides it. */ yAxisTitle?: string; axisTitleColor?: string; /** Overrides {@link textColor} for axis tick labels when non-empty. */ axisLabelColor?: string; /** Overrides {@link gridColor} for axis lines when non-empty. */ axisLineColor?: string; showXTicks?: boolean; showYTicks?: boolean; showXLabels?: boolean; showYLabels?: boolean; /** Explicit color per series label; unmapped series use the theme palette. */ seriesColors?: Readonly>; strokeWidth?: number; /** 0–1 fill opacity for area / polar fills. */ fillOpacity?: number; /** * Compact preview mode: collapses every axis / label gutter to a uniform * {@link ChartRenderOptions.padding} so the plot fills the canvas edge-to-edge. * Used for the chart-type gallery thumbnails; has no effect on normal charts. */ compact?: boolean; } /** * Fallback series palette used when a dataset has no resolved color yet (e.g. * before {@link resolveSeriesColors} runs, or a defensive * `?? DEFAULT_SERIES_PALETTE[i]` at a draw site). Kept in sync with the light * palette in `chart-theme.ts` so every code path — bars, lines, and pie slices * — draws from one coherent set. */ export declare const DEFAULT_SERIES_PALETTE: readonly string[]; /** * Resolves the concrete color every dataset will be drawn with. * * Precedence: an explicit override in `seriesColors` (keyed by the dataset * label), then a color already on the dataset, then the theme `palette` cycled * by index. * * This is the **single definition** of that chain. The renderer colors its own * private copy of the data (see {@link assignColors}), which leaves the caller's * `ChartData` untouched — so any UI that has to show the same colors next to the * chart (the panel's interactive HTML legend) must resolve them through here * rather than reading `dataset.color`, which is usually still undefined. * * @param datasets - Datasets in draw order. * @param palette - Theme-resolved series palette; an empty one falls back to * {@link DEFAULT_SERIES_PALETTE}. * @param seriesColors - Per-label color overrides from the chart model. * @returns One CSS color per dataset, index-aligned to `datasets`. */ export declare function resolveSeriesColors(datasets: readonly ChartDataset[], palette: readonly string[], seriesColors?: Readonly>): string[]; export declare class ChartRenderer { private canvas; private ctx; private animProgress; private rafId; private hoverX; private hoverY; private hoverRafId; /** Smoothly lerped cursor-Y used to animate the tooltip like ApexCharts. */ private tooltipSmoothedY; private lastData; private lastOptions; /** Per-dataset scale multiplier used by toggle animations (0 = hidden, 1 = full). */ private seriesScales; /** * Scratch buffers for {@link layoutGroupedSlots} — the start offset and * thickness of each series' slot within a category band, reused across draws * so a 60fps toggle animation allocates nothing per frame. */ private readonly slotOffsets; private readonly slotSizes; /** Active RAF IDs for per-series toggle animations, keyed by dataset index. */ private seriesToggleRafs; /** * Theme colors/palette resolved once per {@link render} / {@link toggleSeries} * call and reused across every animation and hover frame — never re-resolved * per frame (that would force layout via `getComputedStyle`). */ private theme; /** * Geometry of the most recently drawn pie/doughnut, cached so the hover layer * can hit-test slices without recomputing angles. `null` until a pie is drawn. */ private lastPieLayout; /** Row rectangles of the most recently drawn funnel, for hover hit-testing. */ private lastFunnelLayout; /** Spoke geometry of the most recently drawn polar chart, for hover hit-testing. */ private lastPolarLayout; constructor(canvas: HTMLCanvasElement); /** * Resolves theme tokens once and back-fills any color/font option the caller * left blank (empty string) with the theme value. Explicit overrides win over * theme defaults. Mutates `options` in place and caches the resolved theme. */ private prepareOptions; /** Theme palette color for a given series/slice index, cycling as needed. */ private paletteColor; /** * Visibility weight of series `i`: `1` fully shown, `0` fully hidden, and a * value in between only while a legend toggle is mid-animation. Unknown * indices read as fully visible, which is what a series added since the last * {@link render} should be. */ private seriesScale; /** * Lays a grouped chart's per-series slots across one category band, sized in * proportion to each series' {@link seriesScale}. * * This is what makes the remaining bars widen when a series is switched off * in the legend instead of leaving a hole where it used to be: a hidden * series' weight decays to `0`, its slot closes, and the surviving slots * absorb the freed width over the same 280 ms the toggle animates for. * * Results land in {@link slotOffsets} / {@link slotSizes} rather than a fresh * array, because every draw call runs this and draws run per animation frame. * * @param count - Number of datasets in the group. * @param band - Total thickness available to the group, in pixels. */ private layoutGroupedSlots; /** * Painted thickness of one grouped bar inside its slot, or `0` when the slot * belongs to a series that is hidden (or collapsing) and must not be drawn. * * The inter-bar gutter never consumes more than half the slot, so a chart * with many categories still paints a readable sliver per series rather than * losing the narrower ones entirely. * * @param slot - Slot thickness from {@link layoutGroupedSlots}, in pixels. */ private groupedBarSize; render(data: ChartData, opts?: ChartRenderOptions): void; destroy(): void; /** * Animate a single dataset in or out without re-running the full chart animation. * Each dataset's bar heights are multiplied by a per-series scale factor that is * smoothly interpolated from its current value to 0 (hide) or 1 (show). * * @param index - Index in `data.datasets` to animate. * @param toVisible - `true` = grow bars into view; `false` = shrink bars to zero. * @param data - Full chart data including the toggled series. * @param opts - Render options matching the current chart configuration. */ toggleSeries(index: number, toVisible: boolean, data: ChartData, opts?: ChartRenderOptions): void; private attachEvents; private scheduleHoverRedraw; /** * Eases `tooltipSmoothedY` toward the raw cursor Y (`hoverY`) by a lerp * factor that produces an ApexCharts-style lag behind fast movement. * * @returns `true` when another frame is still required to complete the easing. */ private stepTooltipLerp; private drawHover; private drawCartesianHover; private drawBarHover; private drawTooltip; private animate; /** * Vertical space (px) reserved at the top of the canvas for the title and * subtitle bands. Returns 0 when neither is set so no space is wasted. */ private titleBandHeight; /** Whether a shared series legend should be drawn. */ private legendActive; /** * Whether the legend may be placed on a side (left/right). Only cartesian * charts (which route layout through {@link getPlotArea}) reserve side space; * pie/polar/funnel/bar keep the legend on the bottom to avoid overlap. */ private supportsSideLegend; /** Effective legend position, clamped to bottom for non-cartesian charts. */ private effectiveLegendPosition; private static readonly LEGEND_BAND; private static readonly LEGEND_SIDE; private getPlotArea; private draw; /** * Draws the chart title and subtitle in the reserved top band, honoring the * configured color (falling back to theme) and alignment. No-op when both are * empty. */ private drawTitleBlock; /** X coordinate for a given horizontal alignment across the canvas width. */ private alignedX; /** * Draws the x- and y-axis titles (cartesian charts only). The y-axis title is * rotated −90° and centered along the left margin; the x-axis title sits below * the category labels. */ private drawAxisTitles; private drawColumnGrouped; private drawColumnStacked; private drawColumn100Stacked; private drawBarGrouped; private drawBarStacked; private drawBar100Stacked; private drawLine; private drawArea; /** * Computes the full-circle geometry of a pie/doughnut (progress-independent), * so both {@link drawPie} and {@link drawPieHover} share one source of truth * for slice angles, colors and the label/value each slice represents. Negative * values are treated as zero (they have no meaningful slice). */ private computePieLayout; /** Slice separator color: the theme surface when opaque, else white. */ private sliceStroke; private drawPie; /** Hit-tests the hovered slice against the cached pie layout and draws a tooltip. */ private drawPieHover; /** Hit-tests the hovered funnel row and draws a tooltip. */ private drawFunnelHover; /** Hit-tests the nearest polar spoke and draws a tooltip. */ private drawPolarHover; /** * Draws a compact category tooltip (header + colored value row) near the * cursor. Shared by pie/doughnut/funnel/polar. `pct` is appended when non-null. */ private drawCategoricalTooltip; private drawScatter; private drawPolar; private drawFunnel; private drawGridLines; /** * Draws relative (0–100 %) Y-axis gridlines for multi-series charts where each * dataset is normalised to its own maximum, making the shared axis unitless. */ private drawGridLinesRelative; private drawLegend; /** Horizontal legend row along the top or bottom edge. */ private drawLegendHorizontal; /** Vertical legend stack along the left or right edge (cartesian charts only). */ private drawLegendVertical; private drawAxes; private niceMax; private getLabelStep; private easeOutQuart; private truncate; /** * Converts a 0–1 opacity into a two-digit hex alpha suffix for `#rrggbb` + * `aa` color strings. Clamped to the valid range so out-of-bounds model values * never produce a malformed color. */ private alphaHex; private formatNum; } //# sourceMappingURL=chart-renderer.d.ts.map