y11n-spreadsheet 1.0.1 → 1.1.0

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.
package/README.md CHANGED
@@ -4,10 +4,13 @@ An accessible spreadsheet web component built with [Lit 3.0](https://lit.dev). I
4
4
 
5
5
  ## Features
6
6
 
7
- - Full keyboard navigation (arrows, Tab, Enter, Escape, Ctrl+A)
7
+ - Full keyboard navigation (arrows, Ctrl+Arrow data-edge jumps, Tab, Enter, Escape, Ctrl+A)
8
+ - Row/column selection via headers (click, Shift+click, drag) or Ctrl+Space / Shift+Space
9
+ - Fill down/right (Ctrl+D / Ctrl+R) and multi-cell entry (Ctrl+Enter)
10
+ - CSV/TSV import and export
8
11
  - Copy/cut/paste with TSV clipboard format
9
- - Undo/redo with Ctrl+Z / Ctrl+Shift+Z
10
- - Formula engine with 20+ built-in functions and custom function support
12
+ - Undo/redo with Ctrl+Z / Ctrl+Shift+Z (or Ctrl+Y)
13
+ - Formula engine with 50+ built-in functions and custom function support
11
14
  - Arrow key cell reference insertion during formula editing
12
15
  - Virtual rendering for large datasets (1000+ rows)
13
16
  - Dual-mode formula bar (raw / formatted)
@@ -74,8 +77,36 @@ sheet.setData(data);
74
77
  sheet.getData() // Returns a copy of the grid data
75
78
  sheet.setData(data) // Replaces grid data (clears undo/redo)
76
79
  sheet.registerFunction('DOUBLE', (ctx, val) => Number(val) * 2)
80
+
81
+ // Formatting
82
+ sheet.setCellFormat('0:0', { bold: true, verticalAlign: 'top', wrap: true })
83
+ sheet.setRangeFormat({ start: { row: 1, col: 1 }, end: { row: 9, col: 1 } },
84
+ { numberFormat: { type: 'date', datePattern: 'DD/MM/YYYY' } })
85
+
86
+ // CSV / TSV
87
+ sheet.toCSV() // display values, A1 → last used cell
88
+ sheet.toCSV({ values: 'raw', delimiter: '\t' }) // formulas, as TSV
89
+ sheet.toCSV({ range: { start: { row: 0, col: 0 }, end: { row: 9, col: 3 } } })
90
+ sheet.fromCSV(text) // write at A1 (undoable)
91
+ sheet.fromCSV(text, { start: { row: 5, col: 0 }, delimiter: ';' })
77
92
  ```
78
93
 
94
+ `fromCSV` drops values outside `rows` × `cols` (and throws a `RangeError` for a negative or non-integer `start`); use the exported `parseDelimited(text)` to size the grid first if needed.
95
+
96
+ ### Cell formats
97
+
98
+ | Property | Type | Description |
99
+ |----------|------|-------------|
100
+ | `bold`, `italic`, `underline`, `strikethrough` | `boolean` | Text styling |
101
+ | `textColor`, `backgroundColor` | `string` | CSS colors |
102
+ | `textAlign` | `'left' \| 'center' \| 'right'` | Horizontal alignment |
103
+ | `verticalAlign` | `'top' \| 'middle' \| 'bottom'` | Vertical alignment (default middle) |
104
+ | `wrap` | `boolean` | Wrap text instead of truncating. Rows keep their fixed height, so only the lines that fit are shown |
105
+ | `fontSize` | `number` | Font size in px |
106
+ | `numberFormat` | `NumberFormatOptions` | `{ type: 'number' \| 'currency' \| 'percent' \| 'scientific' \| 'date', decimals?, currencySymbol?, thousandsSep?, datePattern? }` |
107
+
108
+ The `date` type renders date serial numbers (as returned by `DATE`, `TODAY`, `NOW`) using `datePattern` (default `YYYY-MM-DD`; tokens `YYYY YY MM M DD D HH H mm ss`).
109
+
79
110
  ### Events
80
111
 
81
112
  ```js
@@ -89,7 +120,7 @@ sheet.addEventListener('selection-change', (e) => {
89
120
  console.log(e.detail.range); // { start: { row, col }, end: { row, col } }
90
121
  });
91
122
 
92
- // Bulk operation (paste, cut, clear, undo, redo)
123
+ // Bulk operation (paste, cut, clear, fill, import, undo, redo)
93
124
  sheet.addEventListener('data-change', (e) => {
94
125
  console.log(e.detail.updates, e.detail.source, e.detail.operation);
95
126
  });
@@ -107,10 +138,15 @@ Formulas start with `=`. Supported syntax:
107
138
 
108
139
  | Category | Functions |
109
140
  |----------|-----------|
110
- | Aggregates | `SUM`, `AVERAGE`, `MIN`, `MAX`, `COUNT`, `COUNTA` |
111
- | Conditional | `IF(condition, trueVal, falseVal)` |
112
- | Math | `ABS`, `ROUND` |
113
- | String | `CONCAT`, `UPPER`, `LOWER`, `LEN`, `TRIM` |
141
+ | Aggregates | `SUM`, `AVERAGE`, `MIN`, `MAX`, `COUNT`, `COUNTA`, `PRODUCT`, `MEDIAN` |
142
+ | Conditional aggregates | `SUMIF`, `COUNTIF`, `AVERAGEIF`, `SUMIFS`, `COUNTIFS`, `AVERAGEIFS` |
143
+ | Logic | `IF`, `IFS`, `IFERROR`, `AND`, `OR`, `NOT` |
144
+ | Lookup | `VLOOKUP`, `HLOOKUP`, `XLOOKUP`, `INDEX`, `MATCH` |
145
+ | Math | `ABS`, `ROUND`, `ROUNDUP`, `ROUNDDOWN`, `INT`, `MOD`, `POWER`, `SQRT`, `CEILING`, `FLOOR` |
146
+ | Text | `CONCAT`, `TEXTJOIN`, `UPPER`, `LOWER`, `LEN`, `TRIM`, `LEFT`, `RIGHT`, `MID`, `SUBSTITUTE`, `FIND`, `TEXT`, `VALUE` |
147
+ | Date | `DATE`, `TODAY`, `NOW`, `YEAR`, `MONTH`, `DAY`, `WEEKDAY` |
148
+
149
+ Omitted arguments use their defaults, e.g. `=XLOOKUP(75, A1:A9, B1:B9, , -1)`. Criteria in `SUMIF`/`COUNTIF`/`AVERAGEIF` and the `*IFS` functions accept operators (`">5"`, `"<>x"`) and wildcards (`*`, `?`, `~` to escape). `XLOOKUP` supports match modes `0`, `-1`, `1` (not wildcard mode `2`) and search modes `1`/`-1`; it returns a single value (no spilling).
114
150
 
115
151
  **Custom functions**:
116
152
 
@@ -163,6 +199,7 @@ y11n-spreadsheet {
163
199
  | `--ls-formula-mode-active-bg` | `#dbeafe` | Active mode toggle background |
164
200
  | `--ls-ref-highlight-border` | `#1a73e8` | Reference highlight border |
165
201
  | `--ls-ref-highlight-bg` | `rgba(26, 115, 232, 0.15)` | Reference highlight fill |
202
+ | `--ls-header-selected-bg` | `#dbe7fb` | Row/column header background for selected rows/columns |
166
203
 
167
204
  </details>
168
205
 
@@ -180,14 +217,22 @@ y11n-spreadsheet::part(editor) {
180
217
  |-----|--------|
181
218
  | Arrow keys | Move active cell |
182
219
  | Shift + Arrow | Extend selection |
220
+ | Ctrl/Cmd + Arrow | Jump to the edge of the data region |
221
+ | Ctrl/Cmd + Shift + Arrow | Extend selection to the edge of the data region |
222
+ | Ctrl + Space | Select entire column(s) |
223
+ | Shift + Space | Select entire row(s) |
224
+ | Ctrl + Shift + Space, Ctrl+A | Select all |
183
225
  | Tab / Shift+Tab | Move right / left |
184
- | Enter | Start editing / commit edit |
226
+ | Enter / Shift+Enter (editing) | Commit edit and move down / up |
227
+ | Ctrl/Cmd + Enter (editing) | Commit the value into every selected cell |
228
+ | Enter, F2 | Start editing |
185
229
  | Escape | Cancel edit / clear selection |
186
230
  | Delete, Backspace | Clear selected cells |
187
- | Ctrl+A | Select all |
231
+ | Ctrl/Cmd + D | Fill down (copy the top row of the selection into the rest) |
232
+ | Ctrl/Cmd + R | Fill right (copy the left column of the selection into the rest) |
188
233
  | Ctrl+C / X / V | Copy / Cut / Paste |
189
234
  | Ctrl+Z | Undo |
190
- | Ctrl+Shift+Z | Redo |
235
+ | Ctrl+Shift+Z / Ctrl+Y | Redo |
191
236
  | Any printable character | Start editing with that character |
192
237
 
193
238
  During formula editing, arrow keys insert cell references instead of navigating.
@@ -210,7 +255,7 @@ import type {
210
255
  } from 'y11n-spreadsheet';
211
256
  ```
212
257
 
213
- Utility functions for coordinate conversion:
258
+ Utility functions for coordinate conversion, dates, and delimited text:
214
259
 
215
260
  ```ts
216
261
  import { cellKey, parseKey, colToLetter, letterToCol, refToCoord, coordToRef } from 'y11n-spreadsheet';
@@ -221,6 +266,12 @@ colToLetter(0) // "A"
221
266
  letterToCol("AA") // 26
222
267
  refToCoord("B3") // { row: 2, col: 1 }
223
268
  coordToRef({row:0, col:0}) // "A1"
269
+
270
+ import { dateToSerial, dateSerialToDate, parseDelimited, serializeDelimited } from 'y11n-spreadsheet';
271
+
272
+ dateToSerial(2024, 0, 1) // 45292 (month is 0-indexed)
273
+ parseDelimited('a,"b,c"\n1,2') // [['a', 'b,c'], ['1', '2']]
274
+ serializeDelimited([['a', 'b,c']]) // 'a,"b,c"'
224
275
  ```
225
276
 
226
277
  ## Exports
@@ -282,9 +333,11 @@ src/
282
333
  components/y11n-formula-bar.ts # Formula bar sub-component
283
334
  controllers/
284
335
  selection-manager.ts # Selection state (Lit ReactiveController)
285
- clipboard-manager.ts # Clipboard operations (TSV format)
336
+ clipboard-manager.ts # Clipboard operations (TSV + HTML formats)
286
337
  engine/
287
338
  formula-engine.ts # Recursive descent formula parser
339
+ utils/
340
+ delimited.ts # CSV/TSV parsing and serialization
288
341
  __tests__/ # Unit tests colocated with source
289
342
  e2e/ # Playwright E2E tests
290
343
  stories/ # Storybook stories
@@ -1,13 +1,17 @@
1
1
  import { LitElement } from 'lit';
2
+ /** Detail for the `format-action` custom event dispatched by the format toolbar. */
2
3
  export interface FormatActionDetail {
4
+ /** The formatting action to perform */
3
5
  action: 'bold' | 'italic' | 'underline' | 'strikethrough' | 'textColor' | 'backgroundColor' | 'textAlign' | 'fontSize' | 'clearFormat';
6
+ /** The new value for the action (e.g. true/false for toggles, color string, font size number) */
4
7
  value?: string | number | boolean;
5
8
  }
6
9
  /**
7
- * <y11n-format-toolbar> - A companion toolbar for cell formatting.
10
+ * `<y11n-format-toolbar>` -- A companion toolbar for cell formatting.
8
11
  *
9
- * Connected to <y11n-spreadsheet> via properties and events.
10
- * Consumer wires them together:
12
+ * This component does not manage state internally. The consumer wires it
13
+ * to `<y11n-spreadsheet>` by binding selection-derived properties and
14
+ * handling the `format-action` event:
11
15
  *
12
16
  * ```html
13
17
  * <y11n-format-toolbar
@@ -15,6 +19,8 @@ export interface FormatActionDetail {
15
19
  * @format-action=${handle}
16
20
  * ></y11n-format-toolbar>
17
21
  * ```
22
+ *
23
+ * @fires format-action - Dispatched when the user clicks a formatting button or changes a value
18
24
  */
19
25
  export declare class Y11nFormatToolbar extends LitElement {
20
26
  bold: boolean;
@@ -31,7 +37,14 @@ export declare class Y11nFormatToolbar extends LitElement {
31
37
  private _handleToolbarKeydown;
32
38
  render(): import("lit-html").TemplateResult<1>;
33
39
  }
34
- /** Compute consensus format from a selection range */
40
+ /**
41
+ * Compute the consensus format from all cells in a selection range.
42
+ * For each property, returns the common value if all cells agree, or undefined if mixed.
43
+ *
44
+ * @param getData - Callback to retrieve cell data by key
45
+ * @param range - The selection range to inspect
46
+ * @returns A CellFormat with only the unanimously shared properties set
47
+ */
35
48
  export declare function computeSelectionFormat(getData: (cellId: string) => {
36
49
  format?: import('../types.js').CellFormat;
37
50
  } | undefined, range: import('../types.js').SelectionRange): import('../types.js').CellFormat;
@@ -1 +1 @@
1
- {"version":3,"file":"y11n-format-toolbar.d.ts","sourceRoot":"","sources":["../../src/components/y11n-format-toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,MAAM,WAAW,kBAAkB;IACjC,MAAM,EACF,MAAM,GACN,QAAQ,GACR,WAAW,GACX,eAAe,GACf,WAAW,GACX,iBAAiB,GACjB,WAAW,GACX,UAAU,GACV,aAAa,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,qBACa,iBAAkB,SAAQ,UAAU;IAGlB,IAAI,UAAS;IACb,MAAM,UAAS;IACf,SAAS,UAAS;IAClB,aAAa,UAAS;IACvB,SAAS,SAAa;IACtB,eAAe,SAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAU;IAChD,QAAQ,SAAM;IAC0B,QAAQ,UAAS;IAIrF,MAAM,CAAC,MAAM,0BAiKX;IAIF,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,qBAAqB;IAgC7B,MAAM;CAuJP;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;IAAE,MAAM,CAAC,EAAE,OAAO,aAAa,EAAE,UAAU,CAAA;CAAE,GAAG,SAAS,EACtF,KAAK,EAAE,OAAO,aAAa,EAAE,cAAc,GAC1C,OAAO,aAAa,EAAE,UAAU,CAkDlC;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,qBAAqB,EAAE,iBAAiB,CAAC;KAC1C;CACF"}
1
+ {"version":3,"file":"y11n-format-toolbar.d.ts","sourceRoot":"","sources":["../../src/components/y11n-format-toolbar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,oFAAoF;AACpF,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,MAAM,EACF,MAAM,GACN,QAAQ,GACR,WAAW,GACX,eAAe,GACf,WAAW,GACX,iBAAiB,GACjB,WAAW,GACX,UAAU,GACV,aAAa,CAAC;IAClB,iGAAiG;IACjG,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CACnC;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBACa,iBAAkB,SAAQ,UAAU;IAGlB,IAAI,UAAS;IACb,MAAM,UAAS;IACf,SAAS,UAAS;IAClB,aAAa,UAAS;IACvB,SAAS,SAAa;IACtB,eAAe,SAAa;IAC5B,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,CAAU;IAChD,QAAQ,SAAM;IAC0B,QAAQ,UAAS;IAIrF,MAAM,CAAC,MAAM,0BAiKX;IAIF,OAAO,CAAC,SAAS;IAYjB,OAAO,CAAC,qBAAqB;IAgC7B,MAAM,yCAsJL;CACF;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK;IAAE,MAAM,CAAC,EAAE,OAAO,aAAa,EAAE,UAAU,CAAA;CAAE,GAAG,SAAS,EACtF,KAAK,EAAE,OAAO,aAAa,EAAE,cAAc,GAC1C,OAAO,aAAa,EAAE,UAAU,CAkDlC;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,qBAAqB,EAAE,iBAAiB,CAAC;KAC1C;CACF"}
@@ -1,8 +1,16 @@
1
1
  import { LitElement } from 'lit';
2
+ /** Display mode for the formula bar input. */
2
3
  export type FormulaBarMode = 'raw' | 'formatted';
3
4
  /**
4
- * Reusable formula bar that supports raw/formatted value display.
5
- * Emits commit + mode-change events and leaves data persistence to parent components.
5
+ * `<y11n-formula-bar>` -- Reusable formula bar sub-component.
6
+ *
7
+ * Displays the active cell reference and an input for editing cell values.
8
+ * Supports toggling between raw (formula) and formatted (display) modes.
9
+ * All data persistence is handled by the parent component; this component
10
+ * communicates via two custom events:
11
+ *
12
+ * @fires formula-bar-commit - Fired on Enter or blur when the draft differs from the source value
13
+ * @fires formula-bar-mode-change - Fired when the user toggles between raw and formatted mode
6
14
  */
7
15
  export declare class Y11nFormulaBar extends LitElement {
8
16
  cellRef: string;
@@ -11,7 +19,6 @@ export declare class Y11nFormulaBar extends LitElement {
11
19
  mode: FormulaBarMode;
12
20
  readOnly: boolean;
13
21
  private _draft;
14
- private _suppressNextBlur;
15
22
  static styles: import("lit").CSSResult;
16
23
  willUpdate(changedProps: Map<string, unknown>): void;
17
24
  private _sourceValue;
@@ -1 +1 @@
1
- {"version":3,"file":"y11n-formula-bar.d.ts","sourceRoot":"","sources":["../../src/components/y11n-formula-bar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,WAAW,CAAC;AAUjD;;;GAGG;AACH,qBACa,cAAe,SAAQ,UAAU;IACO,OAAO,SAAQ;IACd,QAAQ,SAAM;IACV,YAAY,SAAM;IAC9C,IAAI,EAAE,cAAc,CAAS;IACW,QAAQ,UAAS;IAE5E,OAAO,CAAC,MAAM,CAAM;IAC7B,OAAO,CAAC,iBAAiB,CAAS;IAElC,MAAM,CAAC,MAAM,0BAuEX;IAEF,UAAU,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAUpD,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,OAAO;IAQf,MAAM;CA8BP;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
1
+ {"version":3,"file":"y11n-formula-bar.d.ts","sourceRoot":"","sources":["../../src/components/y11n-formula-bar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAa,MAAM,KAAK,CAAC;AAG5C,8CAA8C;AAC9C,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,WAAW,CAAC;AAgBjD;;;;;;;;;;GAUG;AACH,qBACa,cAAe,SAAQ,UAAU;IACO,OAAO,SAAQ;IACd,QAAQ,SAAM;IACV,YAAY,SAAM;IAC9C,IAAI,EAAE,cAAc,CAAS;IACW,QAAQ,UAAS;IAE5E,OAAO,CAAC,MAAM,CAAM;IAE7B,MAAM,CAAC,MAAM,0BAuEX;IAEF,UAAU,CAAC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQnD;IAED,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,QAAQ;IAWhB,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,OAAO;IAUf,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,OAAO;IAMf,MAAM,yCA6BL;CACF;AAED,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,qBAAqB;QAC7B,kBAAkB,EAAE,cAAc,CAAC;KACpC;CACF"}
@@ -1,7 +1,11 @@
1
1
  import { type CellFormat, type GridData, type SelectionRange } from '../types.js';
2
+ /** A single cell update produced by a paste operation. */
2
3
  export interface PasteUpdate {
4
+ /** Cell key in `"row:col"` format */
3
5
  id: string;
6
+ /** Raw value to write to the cell */
4
7
  value: string;
8
+ /** Optional format to apply (from HTML or internal paste) */
5
9
  format?: CellFormat;
6
10
  }
7
11
  /**
@@ -12,20 +16,35 @@ export declare class ClipboardManager {
12
16
  /** Source range and data from the last internal copy/cut, used for reference adjustment on paste */
13
17
  private _copySourceRange;
14
18
  private _copySourceData;
19
+ /** The TSV text we last wrote to the clipboard, used to detect external overwrites */
20
+ private _lastWrittenText;
15
21
  /**
16
- * Copy the selected range to the clipboard as both HTML table and TSV.
17
- * Falls back to text-only if ClipboardItem API is unavailable.
22
+ * Copy the selected range to the system clipboard.
23
+ * Writes two formats: `text/html` (table with inline styles and data-format attributes)
24
+ * and `text/plain` (TSV). Falls back to text-only if ClipboardItem API is unavailable.
25
+ *
26
+ * @param data - The grid data to read cell values from
27
+ * @param range - The rectangular range to copy
18
28
  */
19
29
  copy(data: GridData, range: SelectionRange): Promise<void>;
20
30
  /**
21
- * Cut: copy to clipboard and return the keys that should be cleared.
31
+ * Cut: copy to clipboard and return the cell keys that should be cleared by the caller.
32
+ *
33
+ * @param data - The grid data to read cell values from
34
+ * @param range - The rectangular range to cut
35
+ * @returns Array of cell keys (in `"row:col"` format) that were included in the cut
22
36
  */
23
37
  cut(data: GridData, range: SelectionRange): Promise<string[]>;
24
38
  /**
25
- * Parse clipboard content and return the updates to apply.
26
- * If data was copied internally, uses raw values with reference adjustment.
27
- * Otherwise tries HTML table format first, then falls back to TSV text.
28
- * The target is the top-left cell where paste begins.
39
+ * Read from the system clipboard and return cell updates for pasting.
40
+ * Priority chain: internal copy data (with formula reference adjustment)
41
+ * -> HTML table -> plain text TSV.
42
+ *
43
+ * @param targetRow - 0-indexed row of the top-left paste destination
44
+ * @param targetCol - 0-indexed column of the top-left paste destination
45
+ * @param maxRows - Grid row count (for bounds clamping)
46
+ * @param maxCols - Grid column count (for bounds clamping)
47
+ * @returns Array of cell updates to apply, or null if clipboard is empty/inaccessible
29
48
  */
30
49
  paste(targetRow: number, targetCol: number, maxRows: number, maxCols: number): Promise<PasteUpdate[] | null>;
31
50
  /**
@@ -49,28 +68,32 @@ export declare class ClipboardManager {
49
68
  * Parse a TSV string and generate cell updates.
50
69
  * Handles RFC 4180-style quoting: fields containing tabs, newlines, or
51
70
  * double-quotes are wrapped in double-quotes, with internal quotes escaped
52
- * as "". This is the format Excel and Google Sheets use on the clipboard.
71
+ * as `""`. This is the format Excel and Google Sheets use on the clipboard.
53
72
  *
54
- * Exported for testability with paste events where text is already available.
73
+ * @param text - The TSV text to parse
74
+ * @param targetRow - 0-indexed row of the top-left paste destination
75
+ * @param targetCol - 0-indexed column of the top-left paste destination
76
+ * @param maxRows - Grid row count (for bounds clamping)
77
+ * @param maxCols - Grid column count (for bounds clamping)
78
+ * @returns Array of cell updates with keys and values
55
79
  */
56
80
  parseTSV(text: string, targetRow: number, targetCol: number, maxRows: number, maxCols: number): Array<{
57
81
  id: string;
58
82
  value: string;
59
83
  }>;
60
- /**
61
- * Parse TSV text into a 2D array of strings, respecting quoted fields.
62
- * Quoted fields may contain tabs, newlines, and escaped quotes ("").
63
- */
64
- private _parseTSVRows;
65
84
  /**
66
85
  * Adjust cell references in a formula by the given row/col offset.
67
- * Absolute references ($A$1) are not adjusted.
68
- * Mixed references ($A1, A$1) only adjust the non-absolute part.
86
+ * Absolute references (`$A$1`) are not adjusted.
87
+ * Mixed references (`$A1`, `A$1`) only adjust the non-absolute component.
88
+ * References inside string literals are left untouched.
89
+ *
90
+ * @param formula - The formula string (must start with "=")
91
+ * @param rowOffset - Number of rows to shift relative references
92
+ * @param colOffset - Number of columns to shift relative references
93
+ * @returns The adjusted formula string, with `#REF!` for out-of-bounds refs
69
94
  */
70
95
  adjustFormulaReferences(formula: string, rowOffset: number, colOffset: number): string;
71
96
  private serializeRange;
72
- /** Quote a TSV field if it contains tabs, newlines, or double-quotes */
73
- private _quoteTSVField;
74
97
  /**
75
98
  * Convert a selection range to an HTML table string.
76
99
  * Includes inline styles for visual formatting and data-format for lossless round-trip.
@@ -82,6 +105,7 @@ export declare class ClipboardManager {
82
105
  private _parseStyleToFormat;
83
106
  /** Validate and sanitize a parsed CellFormat from an external source */
84
107
  private _sanitizeFormat;
108
+ private _sanitizeNumberFormat;
85
109
  private _extractStyleProp;
86
110
  /** Escape special HTML characters */
87
111
  private _escapeHTML;
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard-manager.d.ts","sourceRoot":"","sources":["../../src/controllers/clipboard-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,QAAQ,EACb,KAAK,cAAc,EAIpB,MAAM,aAAa,CAAC;AAErB,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,oGAAoG;IACpG,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,eAAe,CAAyB;IAEhD;;;OAGG;IACG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBhE;;OAEG;IACG,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAWnE;;;;;OAKG;IACG,KAAK,CACT,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC;IAmChC;;;OAGG;IACH,OAAO,CAAC,cAAc;IAuCtB;;;OAGG;IACH,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC,GAAG,IAAI;IA4CnG;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAuBlC;;;;;;;OAOG;IACH,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAkBvC;;;OAGG;IACH,OAAO,CAAC,aAAa;IAqErB;;;;OAIG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM;IA4BtF,OAAO,CAAC,cAAc;IAgBtB,wEAAwE;IACxE,OAAO,CAAC,cAAc;IAOtB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAgC7B,sDAAsD;IACtD,OAAO,CAAC,oBAAoB;IAe5B,2FAA2F;IAC3F,OAAO,CAAC,mBAAmB;IAsC3B,wEAAwE;IACxE,OAAO,CAAC,eAAe;IA6BvB,OAAO,CAAC,iBAAiB;IAQzB,qCAAqC;IACrC,OAAO,CAAC,WAAW;IAUnB,wIAAwI;IACxI,OAAO,CAAC,YAAY;CAerB"}
1
+ {"version":3,"file":"clipboard-manager.d.ts","sourceRoot":"","sources":["../../src/controllers/clipboard-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EACf,KAAK,QAAQ,EAEb,KAAK,cAAc,EAIpB,MAAM,aAAa,CAAC;AAGrB,0DAA0D;AAC1D,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,6DAA6D;IAC7D,MAAM,CAAC,EAAE,UAAU,CAAC;CACrB;AAED;;;GAGG;AACH,qBAAa,gBAAgB;IAC3B,oGAAoG;IACpG,OAAO,CAAC,gBAAgB,CAA+B;IACvD,OAAO,CAAC,eAAe,CAAyB;IAChD,sFAAsF;IACtF,OAAO,CAAC,gBAAgB,CAAuB;IAE/C;;;;;;;OAOG;IACG,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC/D;IAED;;;;;;OAMG;IACG,GAAG,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CASlE;IAED;;;;;;;;;;OAUG;IACG,KAAK,CACT,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,CA+C/B;IAED;;;OAGG;IACH,OAAO,CAAC,cAAc;IAuCtB;;;OAGG;IACH,wBAAwB,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,CAAC,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,UAAU,CAAA;KAAE,CAAC,CAAC,GAAG,IAAI,CA0ClG;IAED;;OAEG;IACH,OAAO,CAAC,0BAA0B;IAuBlC;;;;;;;;;;;;OAYG;IACH,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,GACd,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAgBtC;IAID;;;;;;;;;;OAUG;IACH,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CA+DrF;IAID,OAAO,CAAC,cAAc;IAgBtB;;;OAGG;IACH,OAAO,CAAC,qBAAqB;IAgC7B,sDAAsD;IACtD,OAAO,CAAC,oBAAoB;IAiB5B,2FAA2F;IAC3F,OAAO,CAAC,mBAAmB;IA0C3B,wEAAwE;IACxE,OAAO,CAAC,eAAe;IAoCvB,OAAO,CAAC,qBAAqB;IAqB7B,OAAO,CAAC,iBAAiB;IAQzB,qCAAqC;IACrC,OAAO,CAAC,WAAW;IAUnB,wIAAwI;IACxI,OAAO,CAAC,YAAY;CAerB"}
@@ -14,6 +14,12 @@ export declare class SelectionManager implements ReactiveController {
14
14
  anchor: CellCoord;
15
15
  /** The current end of the selection */
16
16
  head: CellCoord;
17
+ /**
18
+ * Active cell when it differs from `head` (e.g. after selecting whole
19
+ * rows/columns, where the active cell stays put while the range spans the
20
+ * full grid). `null` means the active cell is `head`.
21
+ */
22
+ private _active;
17
23
  /** Whether a drag selection is in progress */
18
24
  private _isDragging;
19
25
  /** Grid bounds */
@@ -26,27 +32,106 @@ export declare class SelectionManager implements ReactiveController {
26
32
  setBounds(maxRows: number, maxCols: number): void;
27
33
  /** Get the normalized selection range (top-left to bottom-right) */
28
34
  get range(): SelectionRange;
29
- /** Get the currently active cell (head of selection) */
35
+ /** The active-cell override, or `null` when the active cell follows the head. */
36
+ get activeOverride(): CellCoord | null;
37
+ /** Get the currently active cell (the head, unless rows/columns were selected) */
30
38
  get activeCell(): CellCoord;
31
39
  /** Check if a cell is within the current selection */
32
40
  isCellSelected(row: number, col: number): boolean;
33
41
  /** Check if a cell is the active cell (head) */
34
42
  isCellActive(row: number, col: number): boolean;
35
- /** Called on mousedown/pointerdown on a cell */
43
+ /**
44
+ * Begin a new selection (mousedown/pointerdown on a cell).
45
+ *
46
+ * @param row - Row index of the clicked cell
47
+ * @param col - Column index of the clicked cell
48
+ * @param extend - If true (Shift+click), keeps the anchor and moves the head
49
+ */
36
50
  startSelection(row: number, col: number, extend?: boolean): void;
37
- /** Called on mousemove during drag */
51
+ /**
52
+ * Extend the selection during a drag (mousemove while dragging).
53
+ *
54
+ * @param row - Row index the mouse is over
55
+ * @param col - Column index the mouse is over
56
+ */
38
57
  extendSelection(row: number, col: number): void;
39
- /** Called on mouseup */
58
+ /** Finalize the drag selection (mouseup). Clears the dragging flag. */
40
59
  endSelection(): void;
41
60
  get isDragging(): boolean;
42
- /** Move the active cell by delta. If extend=true, extends selection. */
61
+ /**
62
+ * Move the active cell by a delta offset.
63
+ *
64
+ * @param dRow - Row delta (-1 = up, +1 = down)
65
+ * @param dCol - Column delta (-1 = left, +1 = right)
66
+ * @param extend - If true, keeps the anchor and extends the selection (Shift+arrow)
67
+ */
43
68
  move(dRow: number, dCol: number, extend?: boolean): void;
44
- /** Move to a specific cell */
69
+ /**
70
+ * Move to a specific cell by absolute position.
71
+ *
72
+ * @param row - Target row index
73
+ * @param col - Target column index
74
+ * @param extend - If true, keeps the anchor and extends the selection
75
+ */
45
76
  moveTo(row: number, col: number, extend?: boolean): void;
77
+ /**
78
+ * Restore a full selection state (e.g. from an undo snapshot).
79
+ *
80
+ * @param anchor - Selection anchor
81
+ * @param head - Selection head
82
+ * @param active - Active-cell override; omit or pass `null` for the head
83
+ */
84
+ setSelection(anchor: CellCoord, head: CellCoord, active?: CellCoord | null): void;
46
85
  /** Select all cells */
47
86
  selectAll(): void;
87
+ /**
88
+ * Select every row of the columns between `fromCol` and `toCol` (inclusive).
89
+ * The head sits on `toCol`, so Shift+Left/Right keeps growing whole columns.
90
+ *
91
+ * @param fromCol - Column the selection is anchored at
92
+ * @param toCol - Column the selection extends to
93
+ * @param active - Cell to keep active; defaults to the current active row in `toCol`
94
+ */
95
+ selectColumns(fromCol: number, toCol: number, active?: CellCoord): void;
96
+ /**
97
+ * Select every column of the rows between `fromRow` and `toRow` (inclusive).
98
+ * The head sits on `toRow`, so Shift+Up/Down keeps growing whole rows.
99
+ *
100
+ * @param fromRow - Row the selection is anchored at
101
+ * @param toRow - Row the selection extends to
102
+ * @param active - Cell to keep active; defaults to the current active column in `toRow`
103
+ */
104
+ selectRows(fromRow: number, toRow: number, active?: CellCoord): void;
48
105
  /** Clear selection (collapse to active cell) */
49
106
  clearSelection(): void;
50
- private clamp;
107
+ /**
108
+ * Keep the active-cell override only while extending and only while it is
109
+ * still inside the range; otherwise the head is the active cell again.
110
+ */
111
+ private _syncActive;
112
+ /**
113
+ * Clamp a coordinate to the valid grid bounds.
114
+ *
115
+ * @param coord - The coordinate to clamp
116
+ * @returns A new coordinate within `[0, maxRows-1]` x `[0, maxCols-1]`
117
+ */
118
+ clamp(coord: CellCoord): CellCoord;
51
119
  }
120
+ /**
121
+ * Find the target of a Ctrl/Cmd+Arrow jump, following spreadsheet semantics:
122
+ *
123
+ * - From a filled cell whose neighbour is also filled: move to the last
124
+ * filled cell of that contiguous run.
125
+ * - Otherwise: move to the next filled cell in that direction, or to the
126
+ * grid edge if there is none.
127
+ *
128
+ * @param from - Starting cell
129
+ * @param dRow - Row direction (-1, 0, or 1)
130
+ * @param dCol - Column direction (-1, 0, or 1)
131
+ * @param rows - Grid row count
132
+ * @param cols - Grid column count
133
+ * @param isFilled - Whether the cell at (row, col) has content
134
+ * @returns The cell to jump to (equal to `from` when already at the edge)
135
+ */
136
+ export declare function findDataEdge(from: CellCoord, dRow: number, dCol: number, rows: number, cols: number, isFilled: (row: number, col: number) => boolean): CellCoord;
52
137
  //# sourceMappingURL=selection-manager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../src/controllers/selection-manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,sBAAsB,EAAE,MAAM,KAAK,CAAC;AAC3E,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAElE;;;;;;;GAOG;AACH,qBAAa,gBAAiB,YAAW,kBAAkB;IACzD,IAAI,EAAE,sBAAsB,CAAC;IAE7B,iDAAiD;IACjD,MAAM,EAAE,SAAS,CAAsB;IAEvC,uCAAuC;IACvC,IAAI,EAAE,SAAS,CAAsB;IAErC,8CAA8C;IAC9C,OAAO,CAAC,WAAW,CAAS;IAE5B,kBAAkB;IAClB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;gBAEb,IAAI,EAAE,sBAAsB,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;IAO1E,aAAa,IAAI,IAAI;IAIrB,gBAAgB,IAAI,IAAI;IAIxB,kEAAkE;IAClE,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI;IAKjD,oEAAoE;IACpE,IAAI,KAAK,IAAI,cAAc,CAW1B;IAED,wDAAwD;IACxD,IAAI,UAAU,IAAI,SAAS,CAE1B;IAED,sDAAsD;IACtD,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAKjD,gDAAgD;IAChD,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAM/C,gDAAgD;IAChD,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,IAAI;IAa9D,sCAAsC;IACtC,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAM/C,wBAAwB;IACxB,YAAY,IAAI,IAAI;IAIpB,IAAI,UAAU,IAAI,OAAO,CAExB;IAID,wEAAwE;IACxE,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,IAAI;IAatD,8BAA8B;IAC9B,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,UAAQ,GAAG,IAAI;IAStD,uBAAuB;IACvB,SAAS,IAAI,IAAI;IAMjB,gDAAgD;IAChD,cAAc,IAAI,IAAI;IAOtB,OAAO,CAAC,KAAK;CAMd"}
1
+ {"version":3,"file":"selection-manager.d.ts","sourceRoot":"","sources":["../../src/controllers/selection-manager.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,8 @@
1
1
  import { type GridData, type FormulaFunction } from '../types.js';
2
+ type EvalResult = {
3
+ displayValue: string;
4
+ type: 'text' | 'number' | 'boolean' | 'error';
5
+ };
2
6
  /**
3
7
  * Represents a 2D range of values with shape information.
4
8
  * Used by lookup functions (VLOOKUP, INDEX, etc.) that need row/col structure.
@@ -14,6 +18,7 @@ export declare class RangeValue {
14
18
  }
15
19
  export declare class FormulaEngine {
16
20
  private _functions;
21
+ private _builtinFunctionNames;
17
22
  private _data;
18
23
  private _evaluating;
19
24
  private _evalDepth;
@@ -21,9 +26,29 @@ export declare class FormulaEngine {
21
26
  private _reverseDeps;
22
27
  private _trackingCellKey;
23
28
  private _cache;
29
+ /**
30
+ * Values of referenced formula cells computed during the current
31
+ * evaluation pass (one top-level `evaluate()`, or a whole `recalculate()` /
32
+ * `recalculateAffected()`). Avoids re-evaluating shared precedents, which is
33
+ * otherwise exponential, and lets deep chains be resolved incrementally.
34
+ * Null outside a pass, so standalone `evaluate()` calls always see fresh data.
35
+ */
36
+ private _memo;
24
37
  constructor();
25
- /** Register a user-defined function */
38
+ /**
39
+ * Register a user-defined formula function.
40
+ * The name is normalized to uppercase for case-insensitive lookup.
41
+ *
42
+ * @param name - Function name (e.g. "MYFUNC"); stored as uppercase
43
+ * @param fn - The function implementation
44
+ */
26
45
  registerFunction(name: string, fn: FormulaFunction): void;
46
+ /**
47
+ * Replace the entire set of user-defined functions while keeping built-ins.
48
+ *
49
+ * @param functions - New custom functions keyed by name
50
+ */
51
+ setFunctions(functions: Record<string, FormulaFunction>): void;
27
52
  /** Update the data reference for evaluation (full reset of dep graph and cache) */
28
53
  setData(data: GridData): void;
29
54
  /**
@@ -33,23 +58,42 @@ export declare class FormulaEngine {
33
58
  */
34
59
  updateData(data: GridData, changedKeys: string[]): void;
35
60
  /**
36
- * Evaluate a raw value. If it starts with '=', parse and execute the formula.
37
- * Otherwise, return the value as-is (possibly coerced).
61
+ * Evaluate a raw value. If it starts with `=`, parse and execute the formula.
62
+ * Otherwise, return the value as-is (possibly coerced to number/boolean).
38
63
  *
39
- * @param forCellKey - If provided, tracks formula dependencies for this cell key.
64
+ * @param rawValue - The raw cell value (formula or literal)
65
+ * @param forCellKey - If provided, registers dependencies for this cell key
66
+ * (side-effect: updates the forward/reverse dep graph and result cache)
67
+ * @returns The display value and resolved type
68
+ */
69
+ evaluate(rawValue: string, forCellKey?: string): EvalResult;
70
+ private _evaluateDeferred;
71
+ /** Run `fn` inside an evaluation pass, creating the pass memo if needed. */
72
+ private _withMemo;
73
+ /**
74
+ * Evaluate a raw value as the top-level formula of `forCellKey`.
75
+ * Throws only EvalDepthError; every other failure becomes an error result.
76
+ */
77
+ private _evaluateTopLevel;
78
+ /**
79
+ * Convert a formula result to its display string and type based on the
80
+ * JS type of the result, so text results like "007" or TEXT() output stay text.
40
81
  */
41
- evaluate(rawValue: string, forCellKey?: string): {
42
- displayValue: string;
43
- type: 'text' | 'number' | 'boolean' | 'error';
44
- };
82
+ private _resultToDisplay;
45
83
  /**
46
84
  * Re-evaluate all formula cells in the grid.
47
- * Rebuilds the dependency graph. Returns a set of cell keys that changed.
85
+ * Clears and rebuilds the entire dependency graph and result cache.
86
+ *
87
+ * @returns Set of cell keys whose display value or type changed
48
88
  */
49
89
  recalculate(): Set<string>;
50
90
  /**
51
91
  * Recalculate only formulas affected by the given changed cell keys.
52
- * Uses the dependency graph for targeted recalculation (BFS order).
92
+ * Uses BFS traversal of the reverse dependency graph for targeted recalculation.
93
+ * Falls back to full `recalculate()` if the dep graph is empty.
94
+ *
95
+ * @param changedKeys - Cell keys whose raw values changed
96
+ * @returns Set of cell keys whose display value or type changed
53
97
  */
54
98
  recalculateAffected(changedKeys: string[]): Set<string>;
55
99
  private _clearDepsFor;
@@ -66,10 +110,35 @@ export declare class FormulaEngine {
66
110
  private _parseUnary;
67
111
  private _parsePrimary;
68
112
  private _parseFunction;
113
+ /**
114
+ * Parse IFERROR(value, fallback) with error trapping on the first argument.
115
+ * Called after LPAREN has already been consumed.
116
+ */
117
+ private _parseIFERROR;
118
+ /**
119
+ * Parse IF(condition, then, else), evaluating only the selected branch.
120
+ * Called after LPAREN has already been consumed.
121
+ */
122
+ private _parseIF;
123
+ /**
124
+ * Parse IFS(cond1, value1, cond2, value2, ...). Conditions are evaluated in
125
+ * order until one is true; only that condition's value is evaluated.
126
+ * No true condition → #N/A. Called after LPAREN has already been consumed.
127
+ */
128
+ private _parseIFS;
129
+ /** Advance past one function argument without evaluating it. */
130
+ private _skipArgument;
69
131
  private _compareValues;
70
132
  private _resolveRef;
133
+ /**
134
+ * Evaluate a referenced formula cell, reusing its value if it was already
135
+ * computed in this evaluation pass. Keeps `_trackingCellKey` so transitive
136
+ * dependencies are recorded (e.g. C1→B1→A1 means C1 depends on A1 too).
137
+ */
138
+ private _evaluateFormulaCell;
71
139
  private _resolveRange;
72
140
  private _createContext;
73
141
  private registerBuiltins;
74
142
  }
143
+ export {};
75
144
  //# sourceMappingURL=formula-engine.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formula-engine.d.ts","sourceRoot":"","sources":["../../src/engine/formula-engine.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,QAAQ,EAEb,KAAK,eAAe,EAGrB,MAAM,aAAa,CAAC;AAoDrB;;;GAGG;AACH,qBAAa,UAAU;IACrB,QAAQ,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;IAC3B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;gBAEV,MAAM,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAMzD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;IAItC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,EAAE;IAK9B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,EAAE;CAO/B;AA4DD,qBAAa,aAAa;IACxB,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,KAAK,CAAuB;IACpC,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,UAAU,CAAK;IAGvB,OAAO,CAAC,KAAK,CAAuC;IACpD,OAAO,CAAC,YAAY,CAAuC;IAC3D,OAAO,CAAC,gBAAgB,CAAuB;IAG/C,OAAO,CAAC,MAAM,CAAmG;;IAMjH,uCAAuC;IACvC,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,eAAe,GAAG,IAAI;IAIzD,mFAAmF;IACnF,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI;IAO7B;;;;OAIG;IACH,UAAU,CAAC,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,IAAI;IAOvD;;;;;OAKG;IACH,QAAQ,CACN,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAA;KAAE;IA6D1E;;;OAGG;IACH,WAAW,IAAI,GAAG,CAAC,MAAM,CAAC;IAqB1B;;;OAGG;IACH,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC;IAuEvD,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,SAAS;IAoBjB,OAAO,CAAC,WAAW;IAqBnB,OAAO,CAAC,QAAQ;IA+JhB,OAAO,CAAC,KAAK;IAIb,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,mBAAmB;IAY3B,OAAO,CAAC,YAAY;IAgBpB,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,aAAa;IAsCrB,OAAO,CAAC,cAAc;IA4CtB,OAAO,CAAC,cAAc;IA8CtB,OAAO,CAAC,WAAW;IAoCnB,OAAO,CAAC,aAAa;IA4DrB,OAAO,CAAC,cAAc;IAuCtB,OAAO,CAAC,gBAAgB;CA+azB"}
1
+ {"version":3,"file":"formula-engine.d.ts","sourceRoot":"","sources":["../../src/engine/formula-engine.ts"],"names":[],"mappings":"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"}