/** * The formula store, dependency graph, and recalc controller (F3). * * Two layers: * - {@link FormulaStore} is the pure recalc core: it holds which cells are * formulas, the reverse dependency graph, and a topological recompute with * cycle detection. It reads and writes values through an injected * {@link FormulaEngineHost}, so it is unit-testable against an in-memory grid. * - {@link FormulaController} binds that core to a real grid: it implements the * host over `grid.data` / `grid.columns`, listens for `cellValueChanged` to * recompute dependents, writes computed values back into `row[key]` (the * canonical value), and requests one pipeline pass so sort/filter/aggregates * see fresh values. * * Addresses are 0-based: `row` indexes `grid.data` (source order), `col` is a * stable A1 letter index (see {@link FormulaController} column handling). The * computed value is the canonical cell value (decision D1), so display and every * value-reading feature work unchanged. */ import type { GridLocaleKey } from 'apex-grid'; import { type CellDecoration, type CellDecoratorContext, type GridFeatureModule, type GridHost } from 'apex-grid/internal'; import type { ReactiveController } from 'lit'; import { type CellValue } from './errors.js'; import { type FormulaFn } from './functions.js'; import { type CellAddress, type RangeAddress } from './refs.js'; export declare const FORMULA_MODULE_ID = "formula"; /** The grid binding the {@link FormulaStore} needs to read, write, and validate cells. */ export interface FormulaEngineHost { /** Current canonical value at an address (`row[columnKey]`), or null. */ readValue(address: CellAddress): CellValue; /** Write a computed value back into the canonical cell. */ writeValue(address: CellAddress, value: CellValue): void; /** Whether an address is within the current data + columns. */ isValidAddress(address: CellAddress): boolean; /** Available functions, keyed by upper-case name. */ functions: Map; } /** A cell whose computed value changed during a recalc pass. */ export interface RecalcChange { address: CellAddress; value: CellValue; } /** * Pure recalc core: the formula set, the dependency graph, and a topological * recompute with cycle detection. No DOM and no grid knowledge beyond the * injected {@link FormulaEngineHost}. */ export declare class FormulaStore { #private; private readonly host; constructor(host: FormulaEngineHost); /** Whether a formula is stored at the address. */ has(address: CellAddress): boolean; /** The formula source at the address, if any. */ get(address: CellAddress): string | undefined; /** Every stored formula as `{ address, src }`, for persistence. */ list(): Array<{ address: CellAddress; src: string; }>; /** Number of stored formulas. */ get size(): number; /** * Store (or replace) a formula at the address and recompute it plus its * dependents. Throws `ParseError` for malformed input (nothing is mutated). */ set(address: CellAddress, src: string): RecalcChange[]; /** * Remove the formula at the address (if any) and recompute its dependents * (which now read whatever literal value the cell holds). */ clear(address: CellAddress): RecalcChange[]; /** Recompute every stored formula (e.g. after the data array is replaced). */ recalcAll(): RecalcChange[]; /** Drop every formula and dependency edge (values are left untouched). */ clearAll(): void; /** * Recompute the formula cells affected by changes to `seeds`. When * `includeSeeds` is true, seed cells that are themselves formulas are * recomputed as well (used when a formula is set or on a full recompute). */ recalc(seeds: CellAddress[], includeSeeds: boolean): RecalcChange[]; } /** * Host-bound recalc controller. Adapts a grid to the {@link FormulaStore}: * resolves A1 column indices to data keys with a stable letter order, reads and * writes `row[key]`, recomputes dependents on `cellValueChanged`, and settles a * pass with one pipeline update. Computed values are the canonical cell values * (D1); recalc writes are not recorded in edit history. */ export declare class FormulaController implements ReactiveController, FormulaEngineHost { #private; /** Per-instance registry, so custom functions do not leak across grids. */ readonly functions: Map; constructor(host: GridHost, state?: { bumpDecoration(): void; }); hostConnected(): void; hostDisconnected(): void; /** After each grid update, a new data array means a positional full recompute. */ hostUpdated(): void; isValidAddress(address: CellAddress): boolean; readValue(address: CellAddress): CellValue; writeValue(address: CellAddress, value: CellValue): void; /** Set a formula on a cell and recompute it plus its dependents. */ setFormula(row: T, columnKey: keyof T & string, src: string): void; /** The formula source on a cell, if any. */ getFormula(row: T, columnKey: keyof T & string): string | undefined; /** Remove a formula from a cell and recompute its dependents. */ clearFormula(row: T, columnKey: keyof T & string): void; /** * Copy the formula from a source cell to a target cell, shifting its * **relative** references by the data-row / column-letter delta between the * two cells; absolute (`$`) axes are preserved. Returns `false` without * writing when the source holds no formula, so a caller (the fill handle or an * intra-grid paste) can fall back to copying the literal value. */ fillFormula(sourceRow: T, sourceKey: keyof T & string, targetRow: T, targetKey: keyof T & string): boolean; /** Recompute every stored formula. */ recalculate(): void; /** Register a custom function (upper-cased) for this grid. */ registerFormulaFunction(name: string, fn: FormulaFn): void; /** * Every available function name (built-ins + custom + `IF`), upper-case and * sorted, for the editor's autocomplete. `IF` is added explicitly because the * evaluator implements it directly rather than via the registry. */ functionNames(): string[]; /** * The A1 reference for a cell, resolved over the grid data and the stable * letter order. `absolute` emits a fully-absolute `$A$1` (shift-click insert); * otherwise a relative `A1`. Returns `undefined` when the row or column is not * in the grid. Used by the editor's click-to-insert. */ referenceFor(row: T, columnKey: keyof T & string, absolute?: boolean): string | undefined; /** * The A1 {@link CellAddress} of a cell (data-row index + stable letter index), * or `null` when the row/column is not in the grid. The editor uses it to * resolve the anchor/focus cells of a click or drag into a reference. */ addressFor(row: T, columnKey: keyof T & string): CellAddress | null; /** * Format the A1 reference spanning `anchor`→`focus`: a single cell when the * two coincide, else a normalized `A1:C3` range. `absolute` fixes both axes on * every corner (`$A$1`), matching the Shift modifier of click/drag insert. * Drives the mouse drag-to-insert-range reference entry. */ rangeReferenceForAddresses(anchor: CellAddress, focus: CellAddress, absolute?: boolean): string; /** * Show (or clear) the point-mode marquee over the cell/range the user is * currently pointing at while editing a formula. Precomputes each cell's edge * tokens once (so {@link decorateCell} is a cheap per-cell lookup), mirroring * the reference-highlight machinery. `null` clears it. */ setPointer(range: RangeAddress | null): void; /** * Reflect the cells the formula being edited references as cell decoration * (the editor's live reference highlighting). Each distinct reference cycles a * small palette so it reads as its own color, spreadsheet-style. Tolerates * partial/invalid input (keeps the last good highlight); `null`, empty, or a * non-formula clears. Drives the {@link decorateCell} seam. */ highlightReferences(src: string | null): void; /** * Decorate a cell for the formula editor: its palette slot when the formula * being edited references it (`data-formula-ref`), and/or the point-mode * marquee when the user is currently pointing at it (`data-formula-pointer` * plus per-side `data-formula-pointer-edge`). Both merge into one decoration. */ decorateCell(ctx: CellDecoratorContext): CellDecoration | null; /** Localize a grid string (delegates to the host), used by the editor. */ localize(key: GridLocaleKey): string; /** * The stored formulas as `(row object, column key, src)`, for state * serialization. The grid maps each row object to a durable row reference. */ listFormulas(): Array<{ row: T; columnKey: string; src: string; }>; /** Replace all formulas with the given `(row object, column key, src)` entries. */ restoreFormulas(entries: ReadonlyArray<{ row: T; columnKey: string; src: string; }>): void; /** The underlying store, for state serialization (F5). */ get store(): FormulaStore; } /** Feature module registered on the enterprise grid (wired into the set in F5). */ export declare const formulaModule: GridFeatureModule;