/** * Calculation service for the Summary Row feature. * * Turns the definitions held by {@link SummaryModel} into * {@link SummaryRowSnapshot}s, reading the grid only through * {@link SummaryDataPort}. It owns three things and nothing else: * * 1. **Scope resolution** — mapping a {@link SummaryScope} onto a row array. * 2. **Value extraction** — resolving a column's logical values across a scope. * 3. **Cell computation** — value → aggregation → formatting → tooltip. * * ### Why one pass computes every row * Aggregating N summary rows over M columns naïvely costs `N × M × rows` value * reads. Both expensive intermediates are therefore cached for the duration of a * single {@link compute} call: * * - **Scope row-sets** are resolved once per *distinct* scope, so ten rows all * scoped `Filtered` run one filter pass between them. * - **Column value arrays** are materialized lazily, once per `(scope, colId)` * pair, and shared by every cell that reads them. Three summary rows totalling * the same column scan it once, not three times. * * Laziness matters as much as sharing: a cell with a static * {@link SummaryCellDef.value} never triggers extraction at all, so a label * column costs nothing even over a million rows. * * @packageDocumentation */ import type { SummaryAggregationEngine } from './aggregation-engine'; import type { SummaryDataPort } from './summary-data-port'; import type { SummaryModel } from './summary-model'; import { type SummaryRowSnapshot } from './summary.types'; /** * Computes summary values. * * Stateless between calls — every cache lives for the duration of one * {@link compute}, so a refresh never serves a stale value. */ export declare class SummaryService { private readonly model; private readonly aggregations; private readonly port; constructor(model: SummaryModel, aggregations: SummaryAggregationEngine, port: SummaryDataPort); /** * Recomputes every summary row and stores the result on the model. * * @returns The fresh snapshots, in row declaration order. */ compute(): readonly SummaryRowSnapshot[]; /** Computes one summary row's cells against its scope. */ private computeRow; /** * Computes one cell: resolve the value, format it, resolve the tooltip. * * @param row - The owning summary row. * @param col - The column, or `null` for a non-column cell id. * @param def - The cell definition, when one exists. * @param isLabelCell - Whether the row's convenience label lands here. * @param scopeRows - Rows in the row's scope. * @param valueCache - Per-compute `(scope, colId)` value cache. * @param explicitColId - Cell id when `col` is `null`. */ private computeCell; /** * Builds the factory stored on a snapshot for a cell with a custom renderer. * * Deliberately does **not** close over the compute pass's value cache. Doing so * would keep every materialized column array alive for as long as the snapshot * is held — one array per `(scope, column)` pair, retained across every frame * until the next refresh. Instead the closure captures only the scope row array * (which the grid already owns, so this is a reference, not a copy) and * re-extracts on demand, memoized within the params object it returns. */ private createRendererParamsFactory; /** * Applies the value precedence chain: explicit value → aggregation → the * row's default aggregation → the convenience label → nothing. */ private resolveValue; /** * Formats a computed value using the column's own rules, so a currency * column's total carries the same symbol, locale and precision as its cells. * * Strings pass through untouched — a label must never be run through numeric * or date formatting. */ private formatValue; /** * Builds the context handed to host callbacks. * * `values` is defined as a lazy getter: reading it materializes (and caches) * the column's value array, while a callback that never touches it — a static * label, a `count` over `rows` — costs no scan at all. */ private createContext; /** * Resolves a scope to its row array, memoized for this compute pass so N rows * sharing a scope cost one resolution. * * Group and detail rows are filtered out of every scope: they carry no source * data of their own, and counting them would inflate `count` and skew `avg`. */ private resolveScope; /** * Picks the column the row's convenience {@link SummaryRowDef.label} lands in: * the first column with no explicit cell of its own. * * Returns `null` when every column is explicitly defined — the label is a * convenience, and silently overwriting a cell the host wrote by hand would be * the wrong resolution. */ private resolveLabelColumn; } //# sourceMappingURL=summary-service.d.ts.map