@ricsam/formula-engine 0.0.5 → 0.0.6

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.
Files changed (36) hide show
  1. package/dist/cjs/core/engine.cjs +62 -2
  2. package/dist/cjs/core/engine.cjs.map +3 -3
  3. package/dist/cjs/core/managers/dependency-manager.cjs +32 -39
  4. package/dist/cjs/core/managers/dependency-manager.cjs.map +3 -3
  5. package/dist/cjs/core/managers/evaluation-manager.cjs +31 -34
  6. package/dist/cjs/core/managers/evaluation-manager.cjs.map +3 -3
  7. package/dist/cjs/core/managers/style-manager.cjs +341 -0
  8. package/dist/cjs/core/managers/style-manager.cjs.map +10 -0
  9. package/dist/cjs/core/types.cjs.map +1 -1
  10. package/dist/cjs/core/utils/color-utils.cjs +137 -0
  11. package/dist/cjs/core/utils/color-utils.cjs.map +10 -0
  12. package/dist/cjs/evaluator/dependency-nodes/virtual-cell-value-node.cjs +55 -0
  13. package/dist/cjs/evaluator/dependency-nodes/virtual-cell-value-node.cjs.map +10 -0
  14. package/dist/cjs/package.json +1 -1
  15. package/dist/mjs/core/engine.mjs +62 -2
  16. package/dist/mjs/core/engine.mjs.map +3 -3
  17. package/dist/mjs/core/managers/dependency-manager.mjs +37 -41
  18. package/dist/mjs/core/managers/dependency-manager.mjs.map +3 -3
  19. package/dist/mjs/core/managers/evaluation-manager.mjs +31 -34
  20. package/dist/mjs/core/managers/evaluation-manager.mjs.map +3 -3
  21. package/dist/mjs/core/managers/style-manager.mjs +315 -0
  22. package/dist/mjs/core/managers/style-manager.mjs.map +10 -0
  23. package/dist/mjs/core/types.mjs.map +1 -1
  24. package/dist/mjs/core/utils/color-utils.mjs +107 -0
  25. package/dist/mjs/core/utils/color-utils.mjs.map +10 -0
  26. package/dist/mjs/evaluator/dependency-nodes/virtual-cell-value-node.mjs +25 -0
  27. package/dist/mjs/evaluator/dependency-nodes/virtual-cell-value-node.mjs.map +10 -0
  28. package/dist/mjs/package.json +1 -1
  29. package/dist/types/core/engine.d.ts +39 -1
  30. package/dist/types/core/managers/dependency-manager.d.ts +8 -7
  31. package/dist/types/core/managers/evaluation-manager.d.ts +13 -3
  32. package/dist/types/core/managers/style-manager.d.ts +92 -0
  33. package/dist/types/core/types.d.ts +42 -0
  34. package/dist/types/core/utils/color-utils.d.ts +32 -0
  35. package/dist/types/evaluator/dependency-nodes/virtual-cell-value-node.d.ts +11 -0
  36. package/package.json +1 -1
@@ -2,7 +2,7 @@
2
2
  * Main FormulaEngine class
3
3
  * Core API implementation for spreadsheet calculations
4
4
  */
5
- import { type CellAddress, type NamedExpression, type RangeAddress, type SerializedCellValue, type SingleEvaluationResult, type SpreadsheetRange, type SpreadsheetRangeEnd, type TableDefinition } from "./types";
5
+ import { type CellAddress, type CellStyle, type ConditionalStyle, type DirectCellStyle, type NamedExpression, type RangeAddress, type SerializedCellValue, type SingleEvaluationResult, type SpreadsheetRange, type SpreadsheetRangeEnd, type TableDefinition } from "./types";
6
6
  import type { FillDirection } from "@ricsam/selection-manager";
7
7
  import { AutoFill } from "./autofill-utils";
8
8
  import { WorkbookManager } from "./managers/workbook-manager";
@@ -11,6 +11,7 @@ import { TableManager } from "./managers/table-manager";
11
11
  import { EventManager } from "./managers/event-manager";
12
12
  import { EvaluationManager } from "./managers/evaluation-manager";
13
13
  import { DependencyManager } from "./managers/dependency-manager";
14
+ import { StyleManager } from "./managers/style-manager";
14
15
  /**
15
16
  * Main FormulaEngine class
16
17
  */
@@ -22,6 +23,7 @@ export declare class FormulaEngine {
22
23
  private evaluationManager;
23
24
  private autoFillManager;
24
25
  private dependencyManager;
26
+ private styleManager;
25
27
  /**
26
28
  * Public access to the store manager for testing
27
29
  */
@@ -32,6 +34,7 @@ export declare class FormulaEngine {
32
34
  _evaluationManager: EvaluationManager;
33
35
  _autoFillManager: AutoFill;
34
36
  _dependencyManager: DependencyManager;
37
+ _styleManager: StyleManager;
35
38
  constructor();
36
39
  /**
37
40
  * Static factory method to build an empty engine
@@ -39,6 +42,11 @@ export declare class FormulaEngine {
39
42
  static buildEmpty(): FormulaEngine;
40
43
  getCellEvaluationResult(cellAddress: CellAddress): SingleEvaluationResult | undefined;
41
44
  getCellValue(cellAddress: CellAddress, debug?: boolean): SerializedCellValue;
45
+ evaluateFormula(
46
+ /**
47
+ * formula without the leading = sign
48
+ */
49
+ formula: string, cellAddress: CellAddress): SerializedCellValue;
42
50
  getCellDependents(address: CellAddress | SpreadsheetRange): (SpreadsheetRange | CellAddress)[];
43
51
  getCellPrecedents(address: CellAddress | SpreadsheetRange): (SpreadsheetRange | CellAddress)[];
44
52
  addNamedExpression({ expression, expressionName, sheetName, workbookName, }: {
@@ -103,6 +111,34 @@ export declare class FormulaEngine {
103
111
  resetTables(tables: Map<string, Map<string, TableDefinition>>): void;
104
112
  getTables(workbookName: string): Map<string, TableDefinition>;
105
113
  isCellInTable(cellAddress: CellAddress): TableDefinition | undefined;
114
+ /**
115
+ * Add a conditional style rule
116
+ */
117
+ addConditionalStyle(style: ConditionalStyle): void;
118
+ /**
119
+ * Remove a conditional style rule by index
120
+ */
121
+ removeConditionalStyle(workbookName: string, index: number): boolean;
122
+ /**
123
+ * Get all conditional styles for a workbook
124
+ */
125
+ getConditionalStyles(workbookName: string): ConditionalStyle[];
126
+ /**
127
+ * Get the computed style for a specific cell
128
+ */
129
+ getCellStyle(cellAddress: CellAddress): CellStyle | undefined;
130
+ /**
131
+ * Add a direct cell style rule
132
+ */
133
+ addCellStyle(style: DirectCellStyle): void;
134
+ /**
135
+ * Remove a direct cell style rule by index
136
+ */
137
+ removeCellStyle(workbookName: string, index: number): boolean;
138
+ /**
139
+ * Get all direct cell styles for a workbook
140
+ */
141
+ getCellStyles(workbookName: string): DirectCellStyle[];
106
142
  addSheet(opts: {
107
143
  workbookName: string;
108
144
  sheetName: string;
@@ -181,6 +217,8 @@ export declare class FormulaEngine {
181
217
  globalExpressions: Map<string, NamedExpression>;
182
218
  };
183
219
  tables: Map<string, Map<string, TableDefinition>>;
220
+ conditionalStyles: ConditionalStyle[];
221
+ cellStyles: DirectCellStyle[];
184
222
  };
185
223
  onUpdate(listener: () => void): () => void;
186
224
  serializeEngine(): string;
@@ -1,5 +1,5 @@
1
1
  import { type ContextDependency } from "../../evaluator/evaluation-context";
2
- import { type CellAddress, type EvaluationOrder, type SpilledValue } from "../types";
2
+ import { type CellAddress, type EvaluationOrder, type SerializedCellValue, type SpilledValue } from "../types";
3
3
  import { AstEvaluationNode } from "../../evaluator/dependency-nodes/ast-evaluation-node";
4
4
  import { CellValueNode } from "../../evaluator/dependency-nodes/cell-value-node";
5
5
  import { EmptyCellEvaluationNode } from "../../evaluator/dependency-nodes/empty-cell-evaluation-node";
@@ -7,8 +7,9 @@ import { SpillMetaNode } from "../../evaluator/dependency-nodes/spill-meta-node"
7
7
  import { RangeEvaluationNode } from "../../evaluator/range-evaluation-node";
8
8
  import type { ASTNode } from "../../parser/ast";
9
9
  import { CacheManager } from "./cache-manager";
10
- import type { CellNodeKeyDictionary, CellNodeType, DependencyNode } from "./dependency-node";
10
+ import type { DependencyNode } from "./dependency-node";
11
11
  import { WorkbookManager } from "./workbook-manager";
12
+ import { VirtualCellValueNode } from "../../evaluator/dependency-nodes/virtual-cell-value-node";
12
13
  export interface DependencyTreeNode {
13
14
  type: "cell" | "range" | "empty";
14
15
  circular?: boolean;
@@ -33,11 +34,12 @@ export declare class DependencyManager {
33
34
  private cacheManager;
34
35
  private workbookManager;
35
36
  /**
36
- * The dependency graph
37
+ * The dependency graph AKA cellNodes
37
38
  */
38
- private evaluatedNodes;
39
+ private cellNodes;
39
40
  private spillMetaNodes;
40
41
  private emptyCells;
42
+ private virtualCellValueNodes;
41
43
  /**
42
44
  * registry of spilled values
43
45
  */
@@ -69,7 +71,7 @@ export declare class DependencyManager {
69
71
  getCellValueNode(nodeKey: string): CellValueNode;
70
72
  getCellValueOrEmptyCellNode(nodeKey: string): CellValueNode | EmptyCellEvaluationNode;
71
73
  getSpillMetaOrEmptySpillMetaNode(nodeKey: string): SpillMetaNode | EmptyCellEvaluationNode;
72
- lookupCellNode<T extends CellNodeType>(nodeKey: string, types: T[]): CellNodeKeyDictionary[T][];
74
+ getVirtualCellValueNode(cellAddress: CellAddress, cellValue: SerializedCellValue): VirtualCellValueNode;
73
75
  getRangeNode(rangeKey: string): RangeEvaluationNode;
74
76
  asts: Map<
75
77
  /**
@@ -96,7 +98,6 @@ export declare class DependencyManager {
96
98
  * only resolved ast nodes can be saved
97
99
  */
98
100
  private saveAstNode;
99
- getEvaluatedNodes(): Map<string, CellValueNode>;
100
101
  /**
101
102
  * Get transitive dependencies and transitive frontier dependencies
102
103
  * This is only used by buildEvaluationOrder, so we'll optimize it there
@@ -113,7 +114,7 @@ export declare class DependencyManager {
113
114
  * 5. For each SCC, create internal evaluation order with cycle breaking
114
115
  * 6. Join the sorted SCC evaluation orders to create final evaluation order
115
116
  */
116
- buildEvaluationOrder(node: CellValueNode | EmptyCellEvaluationNode): EvaluationOrder;
117
+ buildEvaluationOrder(node: CellValueNode | EmptyCellEvaluationNode | VirtualCellValueNode): EvaluationOrder;
117
118
  /**
118
119
  * Find strongly connected components using Tarjan's algorithm
119
120
  * @param nodes - Map of nodes to analyze
@@ -7,6 +7,8 @@ import type { WorkbookManager } from "./workbook-manager";
7
7
  import { SpillMetaNode } from "../../evaluator/dependency-nodes/spill-meta-node";
8
8
  import { EmptyCellEvaluationNode } from "../../evaluator/dependency-nodes/empty-cell-evaluation-node";
9
9
  import type { TableManager } from "./table-manager";
10
+ import type { DependencyNode } from "./dependency-node";
11
+ import { VirtualCellValueNode } from "../../evaluator/dependency-nodes/virtual-cell-value-node";
10
12
  export declare class EvaluationManager {
11
13
  private workbookManager;
12
14
  private tableManager;
@@ -18,12 +20,20 @@ export declare class EvaluationManager {
18
20
  evaluationResultToSerializedValue(evaluation: SingleEvaluationResult, cellAddress: CellAddress, debug?: boolean): SerializedCellValue;
19
21
  evaluateEmptyCell(node: EmptyCellEvaluationNode): void;
20
22
  evaluateRangeNode(node: RangeEvaluationNode): void;
21
- evaluateCellNode(node: CellValueNode | SpillMetaNode): void;
22
- evaluateDependencyNode(dependencyKey: string): void;
23
+ evaluateCellNode(node: CellValueNode | SpillMetaNode | VirtualCellValueNode): void;
24
+ evaluateDependencyNode(dependency: DependencyNode): void;
25
+ /**
26
+ * User exposed method to evaluate a formula
27
+ */
28
+ evaluateFormula(
29
+ /**
30
+ * formula for example
31
+ */
32
+ cellValue: SerializedCellValue, cellAddress: CellAddress): SerializedCellValue;
23
33
  /**
24
34
  * Evaluates a cell by building the evaluation order and evaluating the dependencies in order
25
35
  */
26
- evaluateCell(node: CellValueNode | EmptyCellEvaluationNode): void;
36
+ evaluateCell(node: CellValueNode | EmptyCellEvaluationNode | VirtualCellValueNode): void;
27
37
  convertScalarValueToCellValue(val: SerializedCellValue): CellValue;
28
38
  canSpill(spillCandidate: CellAddress, spillArea: SpreadsheetRange): boolean;
29
39
  getCellEvaluationResult(cellAddress: CellAddress): SingleEvaluationResult | undefined;
@@ -0,0 +1,92 @@
1
+ /**
2
+ * StyleManager - Manages conditional styling for cells
3
+ */
4
+ import type { CellAddress, CellStyle, ConditionalStyle, DirectCellStyle } from "../types";
5
+ import type { WorkbookManager } from "./workbook-manager";
6
+ import type { EvaluationManager } from "./evaluation-manager";
7
+ export declare class StyleManager {
8
+ private workbookManager;
9
+ private evaluationManager;
10
+ private conditionalStyles;
11
+ private cellStyles;
12
+ constructor(workbookManager: WorkbookManager, evaluationManager: EvaluationManager);
13
+ /**
14
+ * Add a conditional style rule
15
+ */
16
+ addConditionalStyle(style: ConditionalStyle): void;
17
+ /**
18
+ * Remove a conditional style rule by index for a specific workbook
19
+ */
20
+ removeConditionalStyle(workbookName: string, index: number): boolean;
21
+ /**
22
+ * Get all conditional styles for a workbook
23
+ */
24
+ getConditionalStyles(workbookName: string): ConditionalStyle[];
25
+ /**
26
+ * Add a direct cell style rule
27
+ */
28
+ addCellStyle(style: DirectCellStyle): void;
29
+ /**
30
+ * Remove a direct cell style rule by index for a specific workbook
31
+ */
32
+ removeCellStyle(workbookName: string, index: number): boolean;
33
+ /**
34
+ * Get all direct cell styles for a workbook
35
+ */
36
+ getCellStyles(workbookName: string): DirectCellStyle[];
37
+ /**
38
+ * Get all conditional styles across all workbooks (for serialization)
39
+ */
40
+ getAllConditionalStyles(): ConditionalStyle[];
41
+ /**
42
+ * Get all cell styles (for serialization)
43
+ */
44
+ getAllCellStyles(): DirectCellStyle[];
45
+ /**
46
+ * Reset all styles (for deserialization)
47
+ */
48
+ resetStyles(conditionalStyles?: ConditionalStyle[], cellStyles?: DirectCellStyle[]): void;
49
+ /**
50
+ * Remove all styles for a workbook
51
+ */
52
+ removeWorkbookStyles(workbookName: string): void;
53
+ /**
54
+ * Update workbook name in all style references
55
+ */
56
+ updateWorkbookName(oldName: string, newName: string): void;
57
+ /**
58
+ * Update sheet name in style references
59
+ */
60
+ updateSheetName(workbookName: string, oldSheetName: string, newSheetName: string): void;
61
+ /**
62
+ * Remove styles that reference a deleted sheet
63
+ */
64
+ removeSheetStyles(workbookName: string, sheetName: string): void;
65
+ /**
66
+ * Get the style for a specific cell
67
+ * Returns the first matching style (first match wins)
68
+ * Checks cellStyles first, then conditionalStyles
69
+ */
70
+ getCellStyle(cellAddress: CellAddress): CellStyle | undefined;
71
+ /**
72
+ * Evaluate a formula-based style condition
73
+ */
74
+ private evaluateFormulaCondition;
75
+ /**
76
+ * Evaluate a gradient-based style condition
77
+ */
78
+ private evaluateGradientCondition;
79
+ /**
80
+ * Calculate min and max bounds for a gradient
81
+ */
82
+ private calculateGradientBounds;
83
+ /**
84
+ * Get a range reference string from a RangeAddress
85
+ * Follows CANONICAL_RANGES.md format:
86
+ * - Closed: A5:D10
87
+ * - Row-bounded (col-open): A5:10
88
+ * - Col-bounded (row-open): A5:D
89
+ * - Open both: A5:INFINITY
90
+ */
91
+ private getRangeReference;
92
+ }
@@ -270,3 +270,45 @@ export type EvaluationOrder = {
270
270
  hash: string;
271
271
  sccDAG?: SCCDAG;
272
272
  };
273
+ export interface LCHColor {
274
+ l: number;
275
+ c: number;
276
+ h: number;
277
+ }
278
+ export interface FormulaStyleCondition {
279
+ type: "formula";
280
+ formula: string;
281
+ color: LCHColor;
282
+ }
283
+ export interface GradientStyleCondition {
284
+ type: "gradient";
285
+ min: {
286
+ type: "lowest_value";
287
+ color: LCHColor;
288
+ } | {
289
+ type: "number";
290
+ color: LCHColor;
291
+ valueFormula: string;
292
+ };
293
+ max: {
294
+ type: "highest_value";
295
+ color: LCHColor;
296
+ } | {
297
+ type: "number";
298
+ color: LCHColor;
299
+ valueFormula: string;
300
+ };
301
+ }
302
+ export type StyleCondition = FormulaStyleCondition | GradientStyleCondition;
303
+ export interface ConditionalStyle {
304
+ area: RangeAddress;
305
+ condition: StyleCondition;
306
+ }
307
+ export interface DirectCellStyle {
308
+ area: RangeAddress;
309
+ style: CellStyle;
310
+ }
311
+ export interface CellStyle {
312
+ backgroundColor?: string;
313
+ color?: string;
314
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Color utilities for conditional styling
3
+ * Handles LCH color space conversions and interpolation
4
+ */
5
+ import type { LCHColor } from "../types";
6
+ /**
7
+ * Convert LCH color to hex string
8
+ * LCH uses the CIELCH color space which provides perceptually uniform colors
9
+ */
10
+ export declare function lchToHex(color: LCHColor): string;
11
+ /**
12
+ * Convert hex color string to LCH color
13
+ * @param hex Hex color string (e.g., "#FF0000" or "FF0000")
14
+ * @returns LCH color
15
+ */
16
+ export declare function hexToLch(hex: string): LCHColor;
17
+ /**
18
+ * Interpolate between two LCH colors
19
+ * @param color1 Starting color
20
+ * @param color2 Ending color
21
+ * @param t Interpolation factor (0-1)
22
+ * @returns Interpolated LCH color
23
+ */
24
+ export declare function interpolateLCH(color1: LCHColor, color2: LCHColor, t: number): LCHColor;
25
+ /**
26
+ * Calculate interpolation factor for a value within a range
27
+ * @param value Current value
28
+ * @param min Minimum value
29
+ * @param max Maximum value
30
+ * @returns Factor between 0 and 1
31
+ */
32
+ export declare function calculateGradientFactor(value: number, min: number, max: number): number;
@@ -0,0 +1,11 @@
1
+ import type { CellAddress, SerializedCellValue, SingleEvaluationResult } from "../../core/types";
2
+ import { BaseEvalNode } from "./base-eval-node";
3
+ import type { SpillMetaNode } from "./spill-meta-node";
4
+ export declare class VirtualCellValueNode extends BaseEvalNode<SingleEvaluationResult> {
5
+ readonly cellAddress: CellAddress;
6
+ readonly cellValue: SerializedCellValue;
7
+ constructor(key: string, cellAddress: CellAddress, cellValue: SerializedCellValue);
8
+ toString(): string;
9
+ spillMeta: SpillMetaNode | undefined;
10
+ setSpillMetaNode(node: SpillMetaNode): void;
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ricsam/formula-engine",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "module": "./dist/mjs/lib.mjs",
5
5
  "scripts": {
6
6
  "test": "bun test",