UNPKG

@syncfusion/ej2-spreadsheet

Version:

Feature-rich JavaScript Spreadsheet (Excel) control with built-in support for selection, editing, formatting, importing and exporting to Excel

98 lines (86 loc) 6.68 kB
# Calculate Module data flow ## What It Does Calculate handles formula parsing, evaluation, and dependency-driven recalculation across sheets. It exposes a formula engine that: parses expressions, resolves named ranges and sheet tokens, computes built-in/custom functions, maintains dependency graphs, caches parsed formulas, and propagates value changes to dependent cells. ## Entry Points - `computeFormula(formulaText: string, isFromComputeExpression?: boolean)` - Parse & compute single formula text. - `computeExpression(formula: string, isFromComputeExpression?: boolean)` - Lower-level expression evaluator. - `valueChanged(grid: string, changeArgs: ValueChangedArgs, ...)` - Triggered when a cell value/formula changes; kicks off refresh and dependent updates. - `refresh(cellRef: string, ...)` - Recalculate dependent cells and refresh random/formula-driven values. - `getCellValueFn(grid, actCell, sheetId, ...)` - Returns a callback that fetches a cell's computed value for formula evaluation. - `defineFunction(formulaName, functionName|Function, description)` - Register custom formula implementations. - `registerGridAsSheet(refName, model, sheetFamilyID)` / `unregisterGridAsSheet(...)` - Wire workbook/worksheet models into the engine. - `getLibraryFormulas()` / `getFunction(libFormula)` - Access built-in formula implementations. - `onFailure` (Event) - Emitted when calculation errors occur. ## ASCII Core Logic Flow User Edit / Programmatic Change ↓ `valueChanged()` receives changeArgs ↓ validate input → `setValueRowCol()` → update cell model ↓ invalidate caches / update `storedData` & dependency tables ↓ `refresh()` called for changed cell ↓ compute path: `getCellValueFn()` used by `computeExpression()` / `computeFormula()` ↓ evaluate functions (library/custom) → `compute*` helpers ↓ write computed values back via `setValueRowCol()` (and undo record) ↓ propagate to dependents (recursive refresh) → UI render/update ↓ emit `onFailure` on errors ## Operations & Key Functions - Parsing & Tokens - `computeExpression()` / `computeFormula()` — tokenize, handle parentheses, operators, references, strings, and errors. - `setTokensForSheets(text: string)` — map sheet names to internal tokens for multi-sheet parsing. - Value Accessors - `getCellValueFn(grid, actCell, sheetId, ...)` — returns `row,col` -> value callback used during evaluation. - `getValueRowCol(grid,row,col)` — raw model read with formatting-awareness. - Dependency Management - `updateDependentCell(cellRef)` — register a dependent formula cell. - `getDependentCells()` / `getDependentFormulaCells()` — read dependency maps. - `clearFormulaDependentCells(cell)` — remove dependencies on cell deletion/overwrite. - Formula Computations (examples) - `computeSumIfAndAvgIf(range,isAvgIf)` — SUMIF/AVERAGEIF logic. - `computeLookup(range)` and `computeVHLookup(range,isVlookup)` — lookup family logic. - `computeIfsFormulas(...)` / `computeCountIfsFormulas(...)` — conditional aggregation. - `processLogical(stack, operator)` — logical operator resolution for AND/OR/NOT. - `computeStoreCells(sCell)` — evaluate stored cell formula sets. - Date/time helpers: `intToDate()`, `fromOADate()`, `toOADate()`, `parseDate()`. - Caching & Tracking - `storedData: Map<string, FormulaInfo>` — parsed+value cache for formulas. - `FormulaInfo` objects store parsed formula, cached value, and calcID. - `randomValues`, `randCollection` — tracking for volatile/random functions. - Registration & Extensibility - `defineFunction()` — register custom function handlers. - `registerGridAsSheet()` — link grid objects into the calculation family (`CalcSheetFamilyItem`). ## Validation & Safety - Syntax & Parse Errors: extensive `formulaErrorStrings` detect invalid tokens, mismatched quotes/parentheses, unknown function names. - Runtime Errors: `#DIV/0!`, `#SPILL!`, `#NAME?`, `#CIRCULARREF!`, `Calculation overflow` are surfaced via `onFailure` and error return values. - Dependency Cycles: circular reference detection and `#CIRCULARREF!` handling; iteration guards for converging formulas. - Bounds Checking: respects `maxRows`/`maxCols` and numeric limits (`minValue`/`maxValue`). - Sheet/Range Validation: checks for missing sheets, invalid named ranges, and invalid references during tokenization. - Protected/Read-only updates: engine expects external checks (grid APIs) to prevent writes to locked cells; valueChanged can be a no-op if caller enforces protection. - Volatile Functions: `RAND`, `RANDBETWEEN` tracked via `randomValues` with controlled refresh to avoid unnecessary churn. ## Desired Outputs User-Facing - Visible cell value updates in grid after formula evaluation. - Formula bar showing formula text and live computed result. - Clear error markers/messages when formulas fail (e.g., `#DIV/0!`, `#NAME?`). - Responsive recalculation: dependent cells update shortly after edits. System-Level - **Model Updates:** cell model values/formulas updated via `setValueRowCol()` and `valueChanged()`. - **Caches & Tables:** `storedData: Map<string,FormulaInfo>` maintained; dependency maps updated (`sheetDependentFormulaCells`). - **Events:** emits `onFailure` for calculation errors; `valueChanged()`/`refresh()` act as implicit event hooks for recalculation flow (integrations can listen to these lifecycle points). - **Undo/Redo:** engine integration should create undo records when `setValueRowCol()` changes persisted cell values or formula text (consumer grid layer records history entries). - **Registration Artifacts:** `CalcSheetFamilyItem` mapping between sheet tokens and parent objects for multi-sheet computations. - **Render Integration:** engine produces final values which the UI layer renders into grid cells (update render pipeline / diff + repaint). - **No Dedicated DOM Classes:** calculation is headless — UI classes remain the responsibility of the grid renderer; engine only supplies updated model values and error flags. ## Notes (Implementation-focused) - Keep formula parsing and evaluation deterministic: separate tokenization/parsing from evaluation to enable caching of parsed ASTs in `FormulaInfo`. - Use `getCellValueFn()` to decouple engine from grid internals; the callback isolates row/col access semantics and makes the compute path testable. - Track `calcID` per formula to avoid re-evaluating unchanged parsed formulas across refresh cycles. - Emit `onFailure` with rich `FormulaError` objects so UI can show helpful diagnostics and allow user correction. ---