@alaarab/ogrid-react 2.3.0 → 2.4.1

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/dist/esm/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as React5 from 'react';
2
- import { useRef, useState, useCallback, useEffect, useMemo, useImperativeHandle, useLayoutEffect, forwardRef } from 'react';
3
- import { createPortal, flushSync } from 'react-dom';
2
+ import { memo as memo$1, useState, useRef, useCallback, useEffect, useMemo, useImperativeHandle, useLayoutEffect, forwardRef } from 'react';
4
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
+ import { createPortal, flushSync } from 'react-dom';
5
5
 
6
6
  // ../core/dist/esm/index.js
7
7
  function toUserLike(u) {
@@ -86,6 +86,17 @@ function getCellValue(item, col) {
86
86
  function isColumnEditable(col, item) {
87
87
  return col.editable === true || typeof col.editable === "function" && col.editable(item);
88
88
  }
89
+ function createGridDataAccessor(items, flatColumns) {
90
+ return {
91
+ getCellValue: (col, row) => {
92
+ if (row < 0 || row >= items.length) return null;
93
+ if (col < 0 || col >= flatColumns.length) return null;
94
+ return getCellValue(items[row], flatColumns[col]);
95
+ },
96
+ getRowCount: () => items.length,
97
+ getColumnCount: () => flatColumns.length
98
+ };
99
+ }
89
100
  function isColumnGroupDef(c) {
90
101
  return "children" in c && Array.isArray(c.children);
91
102
  }
@@ -1086,7 +1097,7 @@ var _CellDescriptorCache = class _CellDescriptorCache2 {
1086
1097
  const sr = input.selectionRange;
1087
1098
  const cr = input.cutRange;
1088
1099
  const cp = input.copyRange;
1089
- return (ec ? `${String(ec.rowId)}\0${ec.columnId}` : "") + "" + (ac ? `${ac.rowIndex}\0${ac.columnIndex}` : "") + "" + (sr ? `${sr.startRow}\0${sr.startCol}\0${sr.endRow}\0${sr.endCol}` : "") + "" + (cr ? `${cr.startRow}\0${cr.startCol}\0${cr.endRow}\0${cr.endCol}` : "") + "" + (cp ? `${cp.startRow}\0${cp.startCol}\0${cp.endRow}\0${cp.endCol}` : "") + "" + (input.isDragging ? "1" : "0") + "" + (input.editable !== false ? "1" : "0") + "" + (input.onCellValueChanged ? "1" : "0");
1100
+ return (ec ? `${String(ec.rowId)}\0${ec.columnId}` : "") + "" + (ac ? `${ac.rowIndex}\0${ac.columnIndex}` : "") + "" + (sr ? `${sr.startRow}\0${sr.startCol}\0${sr.endRow}\0${sr.endCol}` : "") + "" + (cr ? `${cr.startRow}\0${cr.startCol}\0${cr.endRow}\0${cr.endCol}` : "") + "" + (cp ? `${cp.startRow}\0${cp.startCol}\0${cp.endRow}\0${cp.endCol}` : "") + "" + (input.isDragging ? "1" : "0") + "" + (input.editable !== false ? "1" : "0") + "" + (input.onCellValueChanged ? "1" : "0") + "" + (input.formulaVersion ?? 0);
1090
1101
  }
1091
1102
  /**
1092
1103
  * Get a cached descriptor or compute a new one.
@@ -1157,6 +1168,7 @@ function computeCellDescriptor(item, col, rowIndex, colIdx, input) {
1157
1168
  const isPinned = col.pinned != null;
1158
1169
  const pinnedSide = col.pinned ?? void 0;
1159
1170
  const cellValue = getCellValue(item, col);
1171
+ const formulaDisplay = input.hasFormula?.(colIdx, rowIndex) ? input.getFormulaValue?.(colIdx, rowIndex) : void 0;
1160
1172
  let mode = "display";
1161
1173
  let editorType;
1162
1174
  if (isEditing && canEditInline) {
@@ -1188,7 +1200,8 @@ function computeCellDescriptor(item, col, rowIndex, colIdx, input) {
1188
1200
  globalColIndex,
1189
1201
  rowId,
1190
1202
  rowIndex,
1191
- displayValue: cellValue
1203
+ displayValue: formulaDisplay !== void 0 ? formulaDisplay : cellValue,
1204
+ columnType: col.type
1192
1205
  };
1193
1206
  }
1194
1207
  function resolveCellDisplayContent(col, item, displayValue) {
@@ -1203,7 +1216,7 @@ function resolveCellDisplayContent(col, item, displayValue) {
1203
1216
  if (displayValue == null) return null;
1204
1217
  if (col.type === "date") {
1205
1218
  const d = new Date(String(displayValue));
1206
- if (!Number.isNaN(d.getTime())) return d.toLocaleDateString();
1219
+ if (!Number.isNaN(d.getTime())) return d.toLocaleDateString(void 0, { timeZone: "UTC" });
1207
1220
  }
1208
1221
  if (col.type === "boolean") {
1209
1222
  return displayValue ? "True" : "False";
@@ -1302,6 +1315,8 @@ var CHECKBOX_COLUMN_WIDTH = 48;
1302
1315
  var ROW_NUMBER_COLUMN_WIDTH = 50;
1303
1316
  var DEFAULT_MIN_COLUMN_WIDTH = 80;
1304
1317
  var CELL_PADDING = 16;
1318
+ var ROW_NUMBER_COLUMN_MIN_WIDTH = 30;
1319
+ var ROW_NUMBER_COLUMN_ID = "__row_number__";
1305
1320
  var GRID_BORDER_RADIUS = 6;
1306
1321
  var AUTOSIZE_EXTRA_PX = 16;
1307
1322
  var AUTOSIZE_MAX_PX = 520;
@@ -1944,8 +1959,6 @@ function validateRowIds(items, getRowId) {
1944
1959
  ids.add(id);
1945
1960
  }
1946
1961
  }
1947
- var DEFAULT_DEBOUNCE_MS = 300;
1948
- var PEOPLE_SEARCH_DEBOUNCE_MS = DEFAULT_DEBOUNCE_MS;
1949
1962
  var CELL_REF_PATTERN = /^\$?[A-Za-z]+\$?\d+$/;
1950
1963
  var SINGLE_CHAR_OPERATORS = {
1951
1964
  "+": "PLUS",
@@ -2067,6 +2080,12 @@ function tokenize(input) {
2067
2080
  while (pos < input.length && (input[pos] >= "A" && input[pos] <= "Z" || input[pos] >= "a" && input[pos] <= "z" || input[pos] >= "0" && input[pos] <= "9" || input[pos] === "$" || input[pos] === "_")) {
2068
2081
  pos++;
2069
2082
  }
2083
+ if (pos < input.length && input[pos] === "." && pos + 1 < input.length && (input[pos + 1] >= "A" && input[pos + 1] <= "Z" || input[pos + 1] >= "a" && input[pos + 1] <= "z")) {
2084
+ pos++;
2085
+ while (pos < input.length && (input[pos] >= "A" && input[pos] <= "Z" || input[pos] >= "a" && input[pos] <= "z" || input[pos] >= "0" && input[pos] <= "9" || input[pos] === "_")) {
2086
+ pos++;
2087
+ }
2088
+ }
2070
2089
  const word = input.slice(start, pos);
2071
2090
  if (pos < input.length && input[pos] === "!") {
2072
2091
  pos++;
@@ -2094,6 +2113,133 @@ function tokenize(input) {
2094
2113
  tokens.push({ type: "EOF", value: "", position: pos });
2095
2114
  return tokens;
2096
2115
  }
2116
+ var CELL_REF_RE2 = /^\$?([A-Za-z]+)\$?(\d+)$/;
2117
+ function parseCellRefCoords(ref) {
2118
+ const m = ref.match(CELL_REF_RE2);
2119
+ if (!m) return null;
2120
+ return { col: columnLetterToIndex(m[1]), row: parseInt(m[2], 10) - 1 };
2121
+ }
2122
+ function handleFormulaBarKeyDown(key, preventDefault, onCommit, onCancel) {
2123
+ if (key === "Enter") {
2124
+ preventDefault();
2125
+ onCommit();
2126
+ } else if (key === "Escape") {
2127
+ preventDefault();
2128
+ onCancel();
2129
+ }
2130
+ }
2131
+ function processFormulaBarCommit(text, col, row, setFormula, onCellValueChanged) {
2132
+ const trimmed = text.trim();
2133
+ if (trimmed.startsWith("=")) {
2134
+ setFormula(col, row, trimmed);
2135
+ } else {
2136
+ setFormula(col, row, null);
2137
+ onCellValueChanged?.(col, row, trimmed);
2138
+ }
2139
+ }
2140
+ function deriveFormulaBarText(col, row, getFormula, getRawValue) {
2141
+ if (col == null || row == null) return "";
2142
+ const formula = getFormula?.(col, row);
2143
+ if (formula) return formula;
2144
+ const raw = getRawValue?.(col, row);
2145
+ return raw != null ? String(raw) : "";
2146
+ }
2147
+ function extractFormulaReferences(formula) {
2148
+ if (!formula || formula[0] !== "=") return [];
2149
+ const refs = [];
2150
+ let colorIdx = 0;
2151
+ try {
2152
+ const tokens = tokenize(formula.substring(1));
2153
+ for (let i = 0; i < tokens.length; i++) {
2154
+ const tok = tokens[i];
2155
+ if (tok.type === "CELL_REF") {
2156
+ if (i + 2 < tokens.length && tokens[i + 1].type === "COLON" && tokens[i + 2].type === "CELL_REF") {
2157
+ const start = parseCellRefCoords(tok.value);
2158
+ const end = parseCellRefCoords(tokens[i + 2].value);
2159
+ if (start && end) {
2160
+ refs.push({
2161
+ type: "range",
2162
+ col: start.col,
2163
+ row: start.row,
2164
+ endCol: end.col,
2165
+ endRow: end.row,
2166
+ colorIndex: colorIdx++ % 6
2167
+ });
2168
+ i += 2;
2169
+ continue;
2170
+ }
2171
+ }
2172
+ const coords = parseCellRefCoords(tok.value);
2173
+ if (coords) {
2174
+ refs.push({
2175
+ type: "cell",
2176
+ col: coords.col,
2177
+ row: coords.row,
2178
+ colorIndex: colorIdx++ % 6
2179
+ });
2180
+ }
2181
+ }
2182
+ }
2183
+ } catch {
2184
+ }
2185
+ return refs;
2186
+ }
2187
+ var DEFAULT_DEBOUNCE_MS = 300;
2188
+ var PEOPLE_SEARCH_DEBOUNCE_MS = DEFAULT_DEBOUNCE_MS;
2189
+ var FORMULA_REF_COLORS = [
2190
+ "var(--ogrid-formula-ref-0, #4285f4)",
2191
+ "var(--ogrid-formula-ref-1, #ea4335)",
2192
+ "var(--ogrid-formula-ref-2, #34a853)",
2193
+ "var(--ogrid-formula-ref-3, #9334e6)",
2194
+ "var(--ogrid-formula-ref-4, #ff6d01)",
2195
+ "var(--ogrid-formula-ref-5, #46bdc6)"
2196
+ ];
2197
+ var FORMULA_BAR_STYLES = {
2198
+ bar: {
2199
+ display: "flex",
2200
+ alignItems: "center",
2201
+ borderBottom: "1px solid var(--ogrid-border, #e0e0e0)",
2202
+ background: "var(--ogrid-bg, #fff)",
2203
+ minHeight: "28px",
2204
+ fontSize: "13px"
2205
+ },
2206
+ nameBox: {
2207
+ fontFamily: "monospace",
2208
+ fontSize: "12px",
2209
+ fontWeight: 500,
2210
+ padding: "2px 8px",
2211
+ borderRight: "1px solid var(--ogrid-border, #e0e0e0)",
2212
+ background: "var(--ogrid-bg, #fff)",
2213
+ color: "var(--ogrid-fg, #242424)",
2214
+ minWidth: "52px",
2215
+ textAlign: "center",
2216
+ lineHeight: "24px",
2217
+ userSelect: "none",
2218
+ whiteSpace: "nowrap"
2219
+ },
2220
+ fxLabel: {
2221
+ padding: "2px 8px",
2222
+ fontStyle: "italic",
2223
+ fontWeight: 600,
2224
+ color: "var(--ogrid-muted-fg, #888)",
2225
+ userSelect: "none",
2226
+ borderRight: "1px solid var(--ogrid-border, #e0e0e0)",
2227
+ lineHeight: "24px",
2228
+ fontSize: "12px"
2229
+ },
2230
+ input: {
2231
+ flex: 1,
2232
+ border: "none",
2233
+ outline: "none",
2234
+ padding: "2px 8px",
2235
+ fontFamily: "monospace",
2236
+ fontSize: "12px",
2237
+ lineHeight: "24px",
2238
+ background: "transparent",
2239
+ color: "var(--ogrid-fg, #242424)",
2240
+ minWidth: 0
2241
+ }
2242
+ };
2097
2243
  function parse(tokens, namedRanges) {
2098
2244
  let pos = 0;
2099
2245
  function peek() {
@@ -2728,7 +2874,7 @@ var DependencyGraph = class {
2728
2874
  if (cellDependents) {
2729
2875
  for (const dependent of cellDependents) {
2730
2876
  if (affected.has(dependent)) {
2731
- const newDegree = inDegree.get(dependent) - 1;
2877
+ const newDegree = (inDegree.get(dependent) ?? 0) - 1;
2732
2878
  inDegree.set(dependent, newDegree);
2733
2879
  if (newDegree === 0) {
2734
2880
  queue.push(dependent);
@@ -3038,7 +3184,7 @@ function registerMathFunctions(registry) {
3038
3184
  registry.set("SUMPRODUCT", {
3039
3185
  minArgs: 1,
3040
3186
  maxArgs: -1,
3041
- evaluate(args, context, evaluator) {
3187
+ evaluate(args, context, _evaluator) {
3042
3188
  const arrays = [];
3043
3189
  for (const arg of args) {
3044
3190
  if (arg.kind !== "range") {
@@ -3257,6 +3403,162 @@ function registerMathFunctions(registry) {
3257
3403
  return Math.floor(Math.random() * (hi - lo + 1)) + lo;
3258
3404
  }
3259
3405
  });
3406
+ registry.set("MROUND", {
3407
+ minArgs: 2,
3408
+ maxArgs: 2,
3409
+ evaluate(args, context, evaluator) {
3410
+ const rawNum = evaluator.evaluate(args[0], context);
3411
+ if (rawNum instanceof FormulaError) return rawNum;
3412
+ const num = toNumber(rawNum);
3413
+ if (num instanceof FormulaError) return num;
3414
+ const rawMul = evaluator.evaluate(args[1], context);
3415
+ if (rawMul instanceof FormulaError) return rawMul;
3416
+ const multiple = toNumber(rawMul);
3417
+ if (multiple instanceof FormulaError) return multiple;
3418
+ if (multiple === 0) return 0;
3419
+ if (num > 0 && multiple < 0 || num < 0 && multiple > 0) {
3420
+ return new FormulaError("#NUM!", "MROUND: number and multiple must have the same sign");
3421
+ }
3422
+ return Math.round(num / multiple) * multiple;
3423
+ }
3424
+ });
3425
+ registry.set("QUOTIENT", {
3426
+ minArgs: 2,
3427
+ maxArgs: 2,
3428
+ evaluate(args, context, evaluator) {
3429
+ const rawNum = evaluator.evaluate(args[0], context);
3430
+ if (rawNum instanceof FormulaError) return rawNum;
3431
+ const num = toNumber(rawNum);
3432
+ if (num instanceof FormulaError) return num;
3433
+ const rawDen = evaluator.evaluate(args[1], context);
3434
+ if (rawDen instanceof FormulaError) return rawDen;
3435
+ const den = toNumber(rawDen);
3436
+ if (den instanceof FormulaError) return den;
3437
+ if (den === 0) return new FormulaError("#DIV/0!", "QUOTIENT: division by zero");
3438
+ return Math.trunc(num / den);
3439
+ }
3440
+ });
3441
+ registry.set("COMBIN", {
3442
+ minArgs: 2,
3443
+ maxArgs: 2,
3444
+ evaluate(args, context, evaluator) {
3445
+ const rawN = evaluator.evaluate(args[0], context);
3446
+ if (rawN instanceof FormulaError) return rawN;
3447
+ const n = toNumber(rawN);
3448
+ if (n instanceof FormulaError) return n;
3449
+ const rawK = evaluator.evaluate(args[1], context);
3450
+ if (rawK instanceof FormulaError) return rawK;
3451
+ const k = toNumber(rawK);
3452
+ if (k instanceof FormulaError) return k;
3453
+ const ni = Math.trunc(n);
3454
+ const ki = Math.trunc(k);
3455
+ if (ni < 0 || ki < 0) return new FormulaError("#NUM!", "COMBIN: n and k must be non-negative");
3456
+ if (ki > ni) return new FormulaError("#NUM!", "COMBIN: k must be <= n");
3457
+ if (ki === 0 || ki === ni) return 1;
3458
+ const kk = Math.min(ki, ni - ki);
3459
+ let result = 1;
3460
+ for (let i = 0; i < kk; i++) {
3461
+ result = result * (ni - i) / (i + 1);
3462
+ }
3463
+ return Math.round(result);
3464
+ }
3465
+ });
3466
+ registry.set("PERMUT", {
3467
+ minArgs: 2,
3468
+ maxArgs: 2,
3469
+ evaluate(args, context, evaluator) {
3470
+ const rawN = evaluator.evaluate(args[0], context);
3471
+ if (rawN instanceof FormulaError) return rawN;
3472
+ const n = toNumber(rawN);
3473
+ if (n instanceof FormulaError) return n;
3474
+ const rawK = evaluator.evaluate(args[1], context);
3475
+ if (rawK instanceof FormulaError) return rawK;
3476
+ const k = toNumber(rawK);
3477
+ if (k instanceof FormulaError) return k;
3478
+ const ni = Math.trunc(n);
3479
+ const ki = Math.trunc(k);
3480
+ if (ni < 0 || ki < 0) return new FormulaError("#NUM!", "PERMUT: n and k must be non-negative");
3481
+ if (ki > ni) return new FormulaError("#NUM!", "PERMUT: k must be <= n");
3482
+ let result = 1;
3483
+ for (let i = 0; i < ki; i++) {
3484
+ result *= ni - i;
3485
+ }
3486
+ return result;
3487
+ }
3488
+ });
3489
+ registry.set("FACT", {
3490
+ minArgs: 1,
3491
+ maxArgs: 1,
3492
+ evaluate(args, context, evaluator) {
3493
+ const rawVal = evaluator.evaluate(args[0], context);
3494
+ if (rawVal instanceof FormulaError) return rawVal;
3495
+ const num = toNumber(rawVal);
3496
+ if (num instanceof FormulaError) return num;
3497
+ const n = Math.trunc(num);
3498
+ if (n < 0) return new FormulaError("#NUM!", "FACT: argument must be non-negative");
3499
+ if (n > 170) return new FormulaError("#NUM!", "FACT: argument too large (>170)");
3500
+ let result = 1;
3501
+ for (let i = 2; i <= n; i++) {
3502
+ result *= i;
3503
+ }
3504
+ return result;
3505
+ }
3506
+ });
3507
+ registry.set("GCD", {
3508
+ minArgs: 1,
3509
+ maxArgs: -1,
3510
+ evaluate(args, context, evaluator) {
3511
+ const nums = [];
3512
+ for (const arg of args) {
3513
+ const rawVal = evaluator.evaluate(arg, context);
3514
+ if (rawVal instanceof FormulaError) return rawVal;
3515
+ const v = toNumber(rawVal);
3516
+ if (v instanceof FormulaError) return v;
3517
+ const n = Math.trunc(Math.abs(v));
3518
+ nums.push(n);
3519
+ }
3520
+ if (nums.length === 0) return new FormulaError("#NUM!", "GCD: no arguments");
3521
+ let result = nums[0];
3522
+ for (let i = 1; i < nums.length; i++) {
3523
+ result = gcdTwo(result, nums[i]);
3524
+ }
3525
+ return result;
3526
+ }
3527
+ });
3528
+ registry.set("LCM", {
3529
+ minArgs: 1,
3530
+ maxArgs: -1,
3531
+ evaluate(args, context, evaluator) {
3532
+ const nums = [];
3533
+ for (const arg of args) {
3534
+ const rawVal = evaluator.evaluate(arg, context);
3535
+ if (rawVal instanceof FormulaError) return rawVal;
3536
+ const v = toNumber(rawVal);
3537
+ if (v instanceof FormulaError) return v;
3538
+ const n = Math.trunc(Math.abs(v));
3539
+ nums.push(n);
3540
+ }
3541
+ if (nums.length === 0) return new FormulaError("#NUM!", "LCM: no arguments");
3542
+ let result = nums[0];
3543
+ for (let i = 1; i < nums.length; i++) {
3544
+ const g = gcdTwo(result, nums[i]);
3545
+ if (g === 0) {
3546
+ result = 0;
3547
+ break;
3548
+ }
3549
+ result = result / g * nums[i];
3550
+ }
3551
+ return result;
3552
+ }
3553
+ });
3554
+ }
3555
+ function gcdTwo(a, b) {
3556
+ while (b !== 0) {
3557
+ const t = b;
3558
+ b = a % b;
3559
+ a = t;
3560
+ }
3561
+ return a;
3260
3562
  }
3261
3563
  function flattenArgs2(args, context, evaluator) {
3262
3564
  const result = [];
@@ -4091,6 +4393,152 @@ function registerTextFunctions(registry) {
4091
4393
  return parts.join(delimiter);
4092
4394
  }
4093
4395
  });
4396
+ registry.set("DOLLAR", {
4397
+ minArgs: 1,
4398
+ maxArgs: 2,
4399
+ evaluate(args, context, evaluator) {
4400
+ const rawNum = evaluator.evaluate(args[0], context);
4401
+ if (rawNum instanceof FormulaError) return rawNum;
4402
+ const num = toNumber(rawNum);
4403
+ if (num instanceof FormulaError) return num;
4404
+ let decimals = 2;
4405
+ if (args.length >= 2) {
4406
+ const rawDec = evaluator.evaluate(args[1], context);
4407
+ if (rawDec instanceof FormulaError) return rawDec;
4408
+ const d = toNumber(rawDec);
4409
+ if (d instanceof FormulaError) return d;
4410
+ decimals = Math.trunc(d);
4411
+ }
4412
+ const absNum = Math.abs(num);
4413
+ const rounded = decimals >= 0 ? absNum.toFixed(decimals) : (Math.round(absNum / Math.pow(10, -decimals)) * Math.pow(10, -decimals)).toFixed(0);
4414
+ const [intPart, decPart] = rounded.split(".");
4415
+ const withCommas = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4416
+ const formatted = decPart !== void 0 ? `${withCommas}.${decPart}` : withCommas;
4417
+ return num < 0 ? `($${formatted})` : `$${formatted}`;
4418
+ }
4419
+ });
4420
+ registry.set("FIXED", {
4421
+ minArgs: 1,
4422
+ maxArgs: 3,
4423
+ evaluate(args, context, evaluator) {
4424
+ const rawNum = evaluator.evaluate(args[0], context);
4425
+ if (rawNum instanceof FormulaError) return rawNum;
4426
+ const num = toNumber(rawNum);
4427
+ if (num instanceof FormulaError) return num;
4428
+ let decimals = 2;
4429
+ if (args.length >= 2) {
4430
+ const rawDec = evaluator.evaluate(args[1], context);
4431
+ if (rawDec instanceof FormulaError) return rawDec;
4432
+ const d = toNumber(rawDec);
4433
+ if (d instanceof FormulaError) return d;
4434
+ decimals = Math.trunc(d);
4435
+ }
4436
+ let noCommas = false;
4437
+ if (args.length >= 3) {
4438
+ const rawNoCommas = evaluator.evaluate(args[2], context);
4439
+ if (rawNoCommas instanceof FormulaError) return rawNoCommas;
4440
+ noCommas = !!rawNoCommas;
4441
+ }
4442
+ const absNum = Math.abs(num);
4443
+ const rounded = decimals >= 0 ? absNum.toFixed(decimals) : (Math.round(absNum / Math.pow(10, -decimals)) * Math.pow(10, -decimals)).toFixed(0);
4444
+ if (noCommas) {
4445
+ return num < 0 ? `-${rounded}` : rounded;
4446
+ }
4447
+ const [intPart, decPart] = rounded.split(".");
4448
+ const withCommas = intPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
4449
+ const formatted = decPart !== void 0 ? `${withCommas}.${decPart}` : withCommas;
4450
+ return num < 0 ? `-${formatted}` : formatted;
4451
+ }
4452
+ });
4453
+ registry.set("T", {
4454
+ minArgs: 1,
4455
+ maxArgs: 1,
4456
+ evaluate(args, context, evaluator) {
4457
+ const val = evaluator.evaluate(args[0], context);
4458
+ if (val instanceof FormulaError) return val;
4459
+ return typeof val === "string" ? val : "";
4460
+ }
4461
+ });
4462
+ registry.set("N", {
4463
+ minArgs: 1,
4464
+ maxArgs: 1,
4465
+ evaluate(args, context, evaluator) {
4466
+ const val = evaluator.evaluate(args[0], context);
4467
+ if (val instanceof FormulaError) return val;
4468
+ if (typeof val === "number") return val;
4469
+ if (typeof val === "boolean") return val ? 1 : 0;
4470
+ if (val instanceof Date) return val.getTime();
4471
+ return 0;
4472
+ }
4473
+ });
4474
+ registry.set("FORMULATEXT", {
4475
+ minArgs: 1,
4476
+ maxArgs: 1,
4477
+ evaluate(args, context, _evaluator) {
4478
+ const arg = args[0];
4479
+ if (arg.kind !== "cellRef") {
4480
+ return new FormulaError("#N/A", "FORMULATEXT requires a cell reference");
4481
+ }
4482
+ if (!context.getCellFormula) {
4483
+ return new FormulaError("#N/A", "FORMULATEXT not supported in this context");
4484
+ }
4485
+ const formula = context.getCellFormula(arg.address);
4486
+ if (formula === void 0) {
4487
+ return new FormulaError("#N/A", "Cell does not contain a formula");
4488
+ }
4489
+ return formula;
4490
+ }
4491
+ });
4492
+ registry.set("NUMBERVALUE", {
4493
+ minArgs: 1,
4494
+ maxArgs: 3,
4495
+ evaluate(args, context, evaluator) {
4496
+ const rawText = evaluator.evaluate(args[0], context);
4497
+ if (rawText instanceof FormulaError) return rawText;
4498
+ if (typeof rawText === "number") return rawText;
4499
+ let text = toString(rawText).trim();
4500
+ let decimalSep = ".";
4501
+ let groupSep = ",";
4502
+ const hasDecimalArg = args.length >= 2;
4503
+ const hasGroupArg = args.length >= 3;
4504
+ if (hasDecimalArg) {
4505
+ const rawDec = evaluator.evaluate(args[1], context);
4506
+ if (rawDec instanceof FormulaError) return rawDec;
4507
+ decimalSep = toString(rawDec);
4508
+ if (decimalSep.length !== 1) return new FormulaError("#VALUE!", "NUMBERVALUE decimal separator must be 1 character");
4509
+ if (!hasGroupArg) {
4510
+ groupSep = decimalSep === "," ? "." : ",";
4511
+ }
4512
+ }
4513
+ if (hasGroupArg) {
4514
+ const rawGrp = evaluator.evaluate(args[2], context);
4515
+ if (rawGrp instanceof FormulaError) return rawGrp;
4516
+ groupSep = toString(rawGrp);
4517
+ if (groupSep.length !== 1) return new FormulaError("#VALUE!", "NUMBERVALUE group separator must be 1 character");
4518
+ }
4519
+ if (decimalSep === groupSep) return new FormulaError("#VALUE!", "NUMBERVALUE separators must be different");
4520
+ const isPercent = text.endsWith("%");
4521
+ if (isPercent) text = text.slice(0, -1).trim();
4522
+ const escapedGroup = groupSep.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4523
+ text = text.replace(new RegExp(escapedGroup, "g"), "");
4524
+ if (decimalSep !== ".") {
4525
+ const escapedDec = decimalSep.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4526
+ text = text.replace(new RegExp(escapedDec), ".");
4527
+ }
4528
+ const n = Number(text);
4529
+ if (isNaN(n)) return new FormulaError("#VALUE!", `NUMBERVALUE cannot parse "${toString(rawText)}"`);
4530
+ return isPercent ? n / 100 : n;
4531
+ }
4532
+ });
4533
+ registry.set("PHONETIC", {
4534
+ minArgs: 1,
4535
+ maxArgs: 1,
4536
+ evaluate(args, context, evaluator) {
4537
+ const val = evaluator.evaluate(args[0], context);
4538
+ if (val instanceof FormulaError) return val;
4539
+ return toString(val);
4540
+ }
4541
+ });
4094
4542
  }
4095
4543
  function toDate(val) {
4096
4544
  if (val instanceof FormulaError) return val;
@@ -4338,70 +4786,375 @@ function registerDateFunctions(registry) {
4338
4786
  return count * sign;
4339
4787
  }
4340
4788
  });
4341
- }
4342
- function makeCriteria(op, value) {
4343
- return { op, value, valueLower: typeof value === "string" ? value.toLowerCase() : null };
4344
- }
4345
- function parseCriteria(criteria) {
4346
- if (typeof criteria === "number") {
4347
- return makeCriteria("=", criteria);
4348
- }
4349
- if (typeof criteria !== "string") {
4350
- return makeCriteria("=", criteria);
4351
- }
4352
- const str = criteria.trim();
4353
- if (str.startsWith(">=")) {
4354
- return makeCriteria(">=", parseNumericOrString(str.substring(2).trim()));
4355
- }
4356
- if (str.startsWith("<=")) {
4357
- return makeCriteria("<=", parseNumericOrString(str.substring(2).trim()));
4358
- }
4359
- if (str.startsWith("<>")) {
4360
- return makeCriteria("<>", parseNumericOrString(str.substring(2).trim()));
4361
- }
4362
- if (str.startsWith(">")) {
4363
- return makeCriteria(">", parseNumericOrString(str.substring(1).trim()));
4364
- }
4365
- if (str.startsWith("<")) {
4366
- return makeCriteria("<", parseNumericOrString(str.substring(1).trim()));
4367
- }
4368
- if (str.startsWith("=")) {
4369
- return makeCriteria("=", parseNumericOrString(str.substring(1).trim()));
4370
- }
4371
- return makeCriteria("=", parseNumericOrString(str));
4372
- }
4373
- function parseNumericOrString(s) {
4374
- const n = Number(s);
4375
- if (!isNaN(n) && s !== "") return n;
4376
- return s;
4377
- }
4378
- function matchesCriteria(cellValue, criteria) {
4379
- const { op, value } = criteria;
4380
- let comparableCell = cellValue;
4381
- if (typeof value === "number" && typeof cellValue !== "number") {
4382
- const n = toNumber(cellValue);
4383
- if (n instanceof FormulaError) return false;
4384
- comparableCell = n;
4385
- }
4386
- if (typeof comparableCell === "string" && criteria.valueLower !== null) {
4387
- const a = comparableCell.toLowerCase();
4388
- const b = criteria.valueLower;
4389
- switch (op) {
4390
- case "=":
4391
- return a === b;
4392
- case "<>":
4393
- return a !== b;
4394
- case ">":
4395
- return a > b;
4396
- case "<":
4397
- return a < b;
4398
- case ">=":
4399
- return a >= b;
4400
- case "<=":
4401
- return a <= b;
4789
+ registry.set("DAYS", {
4790
+ minArgs: 2,
4791
+ maxArgs: 2,
4792
+ evaluate(args, context, evaluator) {
4793
+ const rawEnd = evaluator.evaluate(args[0], context);
4794
+ if (rawEnd instanceof FormulaError) return rawEnd;
4795
+ const endDate = toDate(rawEnd);
4796
+ if (endDate instanceof FormulaError) return endDate;
4797
+ const rawStart = evaluator.evaluate(args[1], context);
4798
+ if (rawStart instanceof FormulaError) return rawStart;
4799
+ const startDate = toDate(rawStart);
4800
+ if (startDate instanceof FormulaError) return startDate;
4801
+ const endMs = Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
4802
+ const startMs = Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
4803
+ return Math.round((endMs - startMs) / 864e5);
4402
4804
  }
4403
- }
4404
- if (typeof comparableCell === "number" && typeof value === "number") {
4805
+ });
4806
+ registry.set("DAYS360", {
4807
+ minArgs: 2,
4808
+ maxArgs: 3,
4809
+ evaluate(args, context, evaluator) {
4810
+ const rawStart = evaluator.evaluate(args[0], context);
4811
+ if (rawStart instanceof FormulaError) return rawStart;
4812
+ const startDate = toDate(rawStart);
4813
+ if (startDate instanceof FormulaError) return startDate;
4814
+ const rawEnd = evaluator.evaluate(args[1], context);
4815
+ if (rawEnd instanceof FormulaError) return rawEnd;
4816
+ const endDate = toDate(rawEnd);
4817
+ if (endDate instanceof FormulaError) return endDate;
4818
+ let method = false;
4819
+ if (args.length >= 3) {
4820
+ const rawMethod = evaluator.evaluate(args[2], context);
4821
+ if (rawMethod instanceof FormulaError) return rawMethod;
4822
+ method = !!rawMethod;
4823
+ }
4824
+ const sm = startDate.getMonth() + 1;
4825
+ const em = endDate.getMonth() + 1;
4826
+ let sd = startDate.getDate();
4827
+ let ed = endDate.getDate();
4828
+ const sy = startDate.getFullYear();
4829
+ const ey = endDate.getFullYear();
4830
+ if (!method) {
4831
+ if (sd === 31) sd = 30;
4832
+ if (ed === 31 && sd === 30) ed = 30;
4833
+ } else {
4834
+ if (sd === 31) sd = 30;
4835
+ if (ed === 31) ed = 30;
4836
+ }
4837
+ return (ey - sy) * 360 + (em - sm) * 30 + (ed - sd);
4838
+ }
4839
+ });
4840
+ registry.set("ISOWEEKNUM", {
4841
+ minArgs: 1,
4842
+ maxArgs: 1,
4843
+ evaluate(args, context, evaluator) {
4844
+ const rawDate = evaluator.evaluate(args[0], context);
4845
+ if (rawDate instanceof FormulaError) return rawDate;
4846
+ const date = toDate(rawDate);
4847
+ if (date instanceof FormulaError) return date;
4848
+ const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
4849
+ const day = d.getUTCDay() || 7;
4850
+ d.setUTCDate(d.getUTCDate() + 4 - day);
4851
+ const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
4852
+ return Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7);
4853
+ }
4854
+ });
4855
+ registry.set("YEARFRAC", {
4856
+ minArgs: 2,
4857
+ maxArgs: 3,
4858
+ evaluate(args, context, evaluator) {
4859
+ const rawStart = evaluator.evaluate(args[0], context);
4860
+ if (rawStart instanceof FormulaError) return rawStart;
4861
+ const startDate = toDate(rawStart);
4862
+ if (startDate instanceof FormulaError) return startDate;
4863
+ const rawEnd = evaluator.evaluate(args[1], context);
4864
+ if (rawEnd instanceof FormulaError) return rawEnd;
4865
+ const endDate = toDate(rawEnd);
4866
+ if (endDate instanceof FormulaError) return endDate;
4867
+ let basis = 0;
4868
+ if (args.length >= 3) {
4869
+ const rawBasis = evaluator.evaluate(args[2], context);
4870
+ if (rawBasis instanceof FormulaError) return rawBasis;
4871
+ const b = toNumber(rawBasis);
4872
+ if (b instanceof FormulaError) return b;
4873
+ basis = Math.trunc(b);
4874
+ }
4875
+ const sy = startDate.getFullYear();
4876
+ const sm = startDate.getMonth() + 1;
4877
+ const sd = startDate.getDate();
4878
+ const ey = endDate.getFullYear();
4879
+ const em = endDate.getMonth() + 1;
4880
+ const ed = endDate.getDate();
4881
+ switch (basis) {
4882
+ case 0: {
4883
+ const startDay = sd === 31 ? 30 : sd;
4884
+ const endDay = ed === 31 && startDay === 30 ? 30 : ed;
4885
+ const days360 = (ey - sy) * 360 + (em - sm) * 30 + (endDay - startDay);
4886
+ return days360 / 360;
4887
+ }
4888
+ case 1: {
4889
+ const diffMs = Date.UTC(ey, em - 1, ed) - Date.UTC(sy, sm - 1, sd);
4890
+ const diffDays = diffMs / 864e5;
4891
+ const avgYear = ey === sy ? isLeapYear(sy) ? 366 : 365 : (Date.UTC(ey + 1, 0, 1) - Date.UTC(sy, 0, 1)) / 864e5 / (ey - sy + 1);
4892
+ return diffDays / avgYear;
4893
+ }
4894
+ case 3: {
4895
+ const diffMs = Date.UTC(ey, em - 1, ed) - Date.UTC(sy, sm - 1, sd);
4896
+ return diffMs / 864e5 / 365;
4897
+ }
4898
+ default:
4899
+ return new FormulaError("#VALUE!", "YEARFRAC basis must be 0, 1, or 3");
4900
+ }
4901
+ }
4902
+ });
4903
+ registry.set("DATEVALUE", {
4904
+ minArgs: 1,
4905
+ maxArgs: 1,
4906
+ evaluate(args, context, evaluator) {
4907
+ const rawVal = evaluator.evaluate(args[0], context);
4908
+ if (rawVal instanceof FormulaError) return rawVal;
4909
+ const str = typeof rawVal === "string" ? rawVal : String(rawVal);
4910
+ const d = new Date(str);
4911
+ if (isNaN(d.getTime())) return new FormulaError("#VALUE!", `DATEVALUE cannot parse "${str}"`);
4912
+ return new Date(d.getFullYear(), d.getMonth(), d.getDate());
4913
+ }
4914
+ });
4915
+ registry.set("TIMEVALUE", {
4916
+ minArgs: 1,
4917
+ maxArgs: 1,
4918
+ evaluate(args, context, evaluator) {
4919
+ const rawVal = evaluator.evaluate(args[0], context);
4920
+ if (rawVal instanceof FormulaError) return rawVal;
4921
+ const str = typeof rawVal === "string" ? rawVal : String(rawVal);
4922
+ const match = str.match(/^(\d{1,2}):(\d{2})(?::(\d{2}))?(?:\s*(AM|PM))?$/i);
4923
+ if (!match) return new FormulaError("#VALUE!", `TIMEVALUE cannot parse "${str}"`);
4924
+ let hours = parseInt(match[1], 10);
4925
+ const minutes = parseInt(match[2], 10);
4926
+ const seconds = match[3] ? parseInt(match[3], 10) : 0;
4927
+ const ampm = match[4] ? match[4].toUpperCase() : null;
4928
+ if (ampm === "PM" && hours < 12) hours += 12;
4929
+ if (ampm === "AM" && hours === 12) hours = 0;
4930
+ if (hours > 23 || minutes > 59 || seconds > 59) {
4931
+ return new FormulaError("#VALUE!", "TIMEVALUE: invalid time component");
4932
+ }
4933
+ return (hours * 3600 + minutes * 60 + seconds) / 86400;
4934
+ }
4935
+ });
4936
+ registry.set("TIME", {
4937
+ minArgs: 3,
4938
+ maxArgs: 3,
4939
+ evaluate(args, context, evaluator) {
4940
+ const rawH = evaluator.evaluate(args[0], context);
4941
+ if (rawH instanceof FormulaError) return rawH;
4942
+ const h = toNumber(rawH);
4943
+ if (h instanceof FormulaError) return h;
4944
+ const rawM = evaluator.evaluate(args[1], context);
4945
+ if (rawM instanceof FormulaError) return rawM;
4946
+ const m = toNumber(rawM);
4947
+ if (m instanceof FormulaError) return m;
4948
+ const rawS = evaluator.evaluate(args[2], context);
4949
+ if (rawS instanceof FormulaError) return rawS;
4950
+ const s = toNumber(rawS);
4951
+ if (s instanceof FormulaError) return s;
4952
+ const totalSeconds = Math.trunc(h) * 3600 + Math.trunc(m) * 60 + Math.trunc(s);
4953
+ return totalSeconds % 86400 / 86400;
4954
+ }
4955
+ });
4956
+ registry.set("WORKDAY", {
4957
+ minArgs: 2,
4958
+ maxArgs: 3,
4959
+ evaluate(args, context, evaluator) {
4960
+ const rawStart = evaluator.evaluate(args[0], context);
4961
+ if (rawStart instanceof FormulaError) return rawStart;
4962
+ const startDate = toDate(rawStart);
4963
+ if (startDate instanceof FormulaError) return startDate;
4964
+ const rawDays = evaluator.evaluate(args[1], context);
4965
+ if (rawDays instanceof FormulaError) return rawDays;
4966
+ const daysNum = toNumber(rawDays);
4967
+ if (daysNum instanceof FormulaError) return daysNum;
4968
+ const days = Math.trunc(daysNum);
4969
+ const holidaySet = /* @__PURE__ */ new Set();
4970
+ if (args.length >= 3) {
4971
+ const rawHol = args[2];
4972
+ let holVals;
4973
+ if (rawHol.kind === "range") {
4974
+ holVals = context.getRangeValues({ start: rawHol.start, end: rawHol.end }).flat();
4975
+ } else {
4976
+ holVals = [evaluator.evaluate(rawHol, context)];
4977
+ }
4978
+ for (const hv of holVals) {
4979
+ if (hv === null || hv === void 0) continue;
4980
+ const hd = toDate(hv);
4981
+ if (!(hd instanceof FormulaError)) {
4982
+ holidaySet.add(`${hd.getFullYear()}-${hd.getMonth()}-${hd.getDate()}`);
4983
+ }
4984
+ }
4985
+ }
4986
+ const current = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
4987
+ const step = days >= 0 ? 1 : -1;
4988
+ let remaining = Math.abs(days);
4989
+ while (remaining > 0) {
4990
+ current.setDate(current.getDate() + step);
4991
+ const dow = current.getDay();
4992
+ if (dow === 0 || dow === 6) continue;
4993
+ const key = `${current.getFullYear()}-${current.getMonth()}-${current.getDate()}`;
4994
+ if (holidaySet.has(key)) continue;
4995
+ remaining--;
4996
+ }
4997
+ return current;
4998
+ }
4999
+ });
5000
+ registry.set("WORKDAY.INTL", {
5001
+ minArgs: 2,
5002
+ maxArgs: 4,
5003
+ evaluate(args, context, evaluator) {
5004
+ const rawStart = evaluator.evaluate(args[0], context);
5005
+ if (rawStart instanceof FormulaError) return rawStart;
5006
+ const startDate = toDate(rawStart);
5007
+ if (startDate instanceof FormulaError) return startDate;
5008
+ const rawDays = evaluator.evaluate(args[1], context);
5009
+ if (rawDays instanceof FormulaError) return rawDays;
5010
+ const daysNum = toNumber(rawDays);
5011
+ if (daysNum instanceof FormulaError) return daysNum;
5012
+ const days = Math.trunc(daysNum);
5013
+ let weekendMask = [false, false, false, false, false, true, true];
5014
+ if (args.length >= 3) {
5015
+ const rawWeekend = evaluator.evaluate(args[2], context);
5016
+ if (rawWeekend instanceof FormulaError) return rawWeekend;
5017
+ if (typeof rawWeekend === "string" && /^[01]{7}$/.test(rawWeekend)) {
5018
+ weekendMask = rawWeekend.split("").map((c) => c === "1");
5019
+ } else {
5020
+ const wn = toNumber(rawWeekend);
5021
+ if (wn instanceof FormulaError) return wn;
5022
+ const parsed = parseWeekendNumber(Math.trunc(wn));
5023
+ if (!parsed) return new FormulaError("#VALUE!", "WORKDAY.INTL invalid weekend number");
5024
+ weekendMask = parsed;
5025
+ }
5026
+ }
5027
+ const holidaySet = /* @__PURE__ */ new Set();
5028
+ if (args.length >= 4) {
5029
+ const rawHol = args[3];
5030
+ let holVals;
5031
+ if (rawHol.kind === "range") {
5032
+ holVals = context.getRangeValues({ start: rawHol.start, end: rawHol.end }).flat();
5033
+ } else {
5034
+ holVals = [evaluator.evaluate(rawHol, context)];
5035
+ }
5036
+ for (const hv of holVals) {
5037
+ if (hv === null || hv === void 0) continue;
5038
+ const hd = toDate(hv);
5039
+ if (!(hd instanceof FormulaError)) {
5040
+ holidaySet.add(`${hd.getFullYear()}-${hd.getMonth()}-${hd.getDate()}`);
5041
+ }
5042
+ }
5043
+ }
5044
+ const current = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
5045
+ const step = days >= 0 ? 1 : -1;
5046
+ let remaining = Math.abs(days);
5047
+ while (remaining > 0) {
5048
+ current.setDate(current.getDate() + step);
5049
+ const dow = current.getDay();
5050
+ const maskIndex = dow === 0 ? 6 : dow - 1;
5051
+ if (weekendMask[maskIndex]) continue;
5052
+ const key = `${current.getFullYear()}-${current.getMonth()}-${current.getDate()}`;
5053
+ if (holidaySet.has(key)) continue;
5054
+ remaining--;
5055
+ }
5056
+ return current;
5057
+ }
5058
+ });
5059
+ }
5060
+ function isLeapYear(year) {
5061
+ return year % 4 === 0 && year % 100 !== 0 || year % 400 === 0;
5062
+ }
5063
+ function parseWeekendNumber(n) {
5064
+ const twoDay = [
5065
+ [5, 6],
5066
+ // 1: Sat+Sun
5067
+ [6, 0],
5068
+ // 2: Sun+Mon (Sun=index6, Mon=index0)
5069
+ [0, 1],
5070
+ // 3: Mon+Tue
5071
+ [1, 2],
5072
+ // 4: Tue+Wed
5073
+ [2, 3],
5074
+ // 5: Wed+Thu
5075
+ [3, 4],
5076
+ // 6: Thu+Fri
5077
+ [4, 5]
5078
+ // 7: Fri+Sat
5079
+ ];
5080
+ if (n >= 1 && n <= 7) {
5081
+ const mask = [false, false, false, false, false, false, false];
5082
+ const [a, b] = twoDay[n - 1];
5083
+ mask[a] = true;
5084
+ mask[b] = true;
5085
+ return mask;
5086
+ }
5087
+ if (n >= 11 && n <= 17) {
5088
+ const mask = [false, false, false, false, false, false, false];
5089
+ const singleDay = [6, 0, 1, 2, 3, 4, 5];
5090
+ mask[singleDay[n - 11]] = true;
5091
+ return mask;
5092
+ }
5093
+ return null;
5094
+ }
5095
+ function makeCriteria(op, value) {
5096
+ return { op, value, valueLower: typeof value === "string" ? value.toLowerCase() : null };
5097
+ }
5098
+ function parseCriteria(criteria) {
5099
+ if (typeof criteria === "number") {
5100
+ return makeCriteria("=", criteria);
5101
+ }
5102
+ if (typeof criteria !== "string") {
5103
+ return makeCriteria("=", criteria);
5104
+ }
5105
+ const str = criteria.trim();
5106
+ if (str.startsWith(">=")) {
5107
+ return makeCriteria(">=", parseNumericOrString(str.substring(2).trim()));
5108
+ }
5109
+ if (str.startsWith("<=")) {
5110
+ return makeCriteria("<=", parseNumericOrString(str.substring(2).trim()));
5111
+ }
5112
+ if (str.startsWith("<>")) {
5113
+ return makeCriteria("<>", parseNumericOrString(str.substring(2).trim()));
5114
+ }
5115
+ if (str.startsWith(">")) {
5116
+ return makeCriteria(">", parseNumericOrString(str.substring(1).trim()));
5117
+ }
5118
+ if (str.startsWith("<")) {
5119
+ return makeCriteria("<", parseNumericOrString(str.substring(1).trim()));
5120
+ }
5121
+ if (str.startsWith("=")) {
5122
+ return makeCriteria("=", parseNumericOrString(str.substring(1).trim()));
5123
+ }
5124
+ return makeCriteria("=", parseNumericOrString(str));
5125
+ }
5126
+ function parseNumericOrString(s) {
5127
+ const n = Number(s);
5128
+ if (!isNaN(n) && s !== "") return n;
5129
+ return s;
5130
+ }
5131
+ function matchesCriteria(cellValue, criteria) {
5132
+ const { op, value } = criteria;
5133
+ let comparableCell = cellValue;
5134
+ if (typeof value === "number" && typeof cellValue !== "number") {
5135
+ const n = toNumber(cellValue);
5136
+ if (n instanceof FormulaError) return false;
5137
+ comparableCell = n;
5138
+ }
5139
+ if (typeof comparableCell === "string" && criteria.valueLower !== null) {
5140
+ const a = comparableCell.toLowerCase();
5141
+ const b = criteria.valueLower;
5142
+ switch (op) {
5143
+ case "=":
5144
+ return a === b;
5145
+ case "<>":
5146
+ return a !== b;
5147
+ case ">":
5148
+ return a > b;
5149
+ case "<":
5150
+ return a < b;
5151
+ case ">=":
5152
+ return a >= b;
5153
+ case "<=":
5154
+ return a <= b;
5155
+ }
5156
+ }
5157
+ if (typeof comparableCell === "number" && typeof value === "number") {
4405
5158
  switch (op) {
4406
5159
  case "=":
4407
5160
  return comparableCell === value;
@@ -4652,52 +5405,1092 @@ function registerInfoFunctions(registry) {
4652
5405
  return val === null || val === void 0 || val === "";
4653
5406
  }
4654
5407
  });
4655
- registry.set("ISNUMBER", {
5408
+ registry.set("ISNUMBER", {
5409
+ minArgs: 1,
5410
+ maxArgs: 1,
5411
+ evaluate(args, context, evaluator) {
5412
+ const val = evaluator.evaluate(args[0], context);
5413
+ return typeof val === "number" && !isNaN(val);
5414
+ }
5415
+ });
5416
+ registry.set("ISTEXT", {
5417
+ minArgs: 1,
5418
+ maxArgs: 1,
5419
+ evaluate(args, context, evaluator) {
5420
+ const val = evaluator.evaluate(args[0], context);
5421
+ return typeof val === "string";
5422
+ }
5423
+ });
5424
+ registry.set("ISERROR", {
5425
+ minArgs: 1,
5426
+ maxArgs: 1,
5427
+ evaluate(args, context, evaluator) {
5428
+ const val = evaluator.evaluate(args[0], context);
5429
+ return val instanceof FormulaError;
5430
+ }
5431
+ });
5432
+ registry.set("ISNA", {
5433
+ minArgs: 1,
5434
+ maxArgs: 1,
5435
+ evaluate(args, context, evaluator) {
5436
+ const val = evaluator.evaluate(args[0], context);
5437
+ return val instanceof FormulaError && val.type === "#N/A";
5438
+ }
5439
+ });
5440
+ registry.set("TYPE", {
5441
+ minArgs: 1,
5442
+ maxArgs: 1,
5443
+ evaluate(args, context, evaluator) {
5444
+ const val = evaluator.evaluate(args[0], context);
5445
+ if (val instanceof FormulaError) return 16;
5446
+ if (typeof val === "number") return 1;
5447
+ if (typeof val === "string") return 2;
5448
+ if (typeof val === "boolean") return 4;
5449
+ if (val === null || val === void 0) return 1;
5450
+ return 1;
5451
+ }
5452
+ });
5453
+ registry.set("ISODD", {
5454
+ minArgs: 1,
5455
+ maxArgs: 1,
5456
+ evaluate(args, context, evaluator) {
5457
+ const val = evaluator.evaluate(args[0], context);
5458
+ if (val instanceof FormulaError) return val;
5459
+ if (typeof val === "boolean") return new FormulaError("#VALUE!", "ISODD requires a number");
5460
+ const n = toNumber(val);
5461
+ if (n instanceof FormulaError) return n;
5462
+ return Math.trunc(n) % 2 !== 0;
5463
+ }
5464
+ });
5465
+ registry.set("ISEVEN", {
5466
+ minArgs: 1,
5467
+ maxArgs: 1,
5468
+ evaluate(args, context, evaluator) {
5469
+ const val = evaluator.evaluate(args[0], context);
5470
+ if (val instanceof FormulaError) return val;
5471
+ if (typeof val === "boolean") return new FormulaError("#VALUE!", "ISEVEN requires a number");
5472
+ const n = toNumber(val);
5473
+ if (n instanceof FormulaError) return n;
5474
+ return Math.trunc(n) % 2 === 0;
5475
+ }
5476
+ });
5477
+ registry.set("ISFORMULA", {
5478
+ minArgs: 1,
5479
+ maxArgs: 1,
5480
+ evaluate(args, context, _evaluator) {
5481
+ const arg = args[0];
5482
+ if (arg.kind !== "cellRef") return false;
5483
+ if (!context.getCellFormula) return false;
5484
+ const formula = context.getCellFormula(arg.address);
5485
+ return formula !== void 0;
5486
+ }
5487
+ });
5488
+ registry.set("ISLOGICAL", {
5489
+ minArgs: 1,
5490
+ maxArgs: 1,
5491
+ evaluate(args, context, evaluator) {
5492
+ const val = evaluator.evaluate(args[0], context);
5493
+ if (val instanceof FormulaError) return false;
5494
+ return typeof val === "boolean";
5495
+ }
5496
+ });
5497
+ registry.set("ISNONTEXT", {
5498
+ minArgs: 1,
5499
+ maxArgs: 1,
5500
+ evaluate(args, context, evaluator) {
5501
+ const val = evaluator.evaluate(args[0], context);
5502
+ if (val instanceof FormulaError) return true;
5503
+ return typeof val !== "string";
5504
+ }
5505
+ });
5506
+ registry.set("ISREF", {
5507
+ minArgs: 1,
5508
+ maxArgs: 1,
5509
+ evaluate(args, _context, _evaluator) {
5510
+ const arg = args[0];
5511
+ return arg.kind === "cellRef" || arg.kind === "range";
5512
+ }
5513
+ });
5514
+ }
5515
+ function registerFinancialFunctions(registry) {
5516
+ registry.set("PMT", {
5517
+ minArgs: 3,
5518
+ maxArgs: 5,
5519
+ evaluate(args, context, evaluator) {
5520
+ const rawRate = evaluator.evaluate(args[0], context);
5521
+ if (rawRate instanceof FormulaError) return rawRate;
5522
+ const rate = toNumber(rawRate);
5523
+ if (rate instanceof FormulaError) return rate;
5524
+ const rawNper = evaluator.evaluate(args[1], context);
5525
+ if (rawNper instanceof FormulaError) return rawNper;
5526
+ const nper = toNumber(rawNper);
5527
+ if (nper instanceof FormulaError) return nper;
5528
+ const rawPv = evaluator.evaluate(args[2], context);
5529
+ if (rawPv instanceof FormulaError) return rawPv;
5530
+ const pv = toNumber(rawPv);
5531
+ if (pv instanceof FormulaError) return pv;
5532
+ let fv = 0;
5533
+ if (args.length >= 4) {
5534
+ const rawFv = evaluator.evaluate(args[3], context);
5535
+ if (rawFv instanceof FormulaError) return rawFv;
5536
+ const fvNum = toNumber(rawFv);
5537
+ if (fvNum instanceof FormulaError) return fvNum;
5538
+ fv = fvNum;
5539
+ }
5540
+ let type = 0;
5541
+ if (args.length >= 5) {
5542
+ const rawType = evaluator.evaluate(args[4], context);
5543
+ if (rawType instanceof FormulaError) return rawType;
5544
+ const typeNum = toNumber(rawType);
5545
+ if (typeNum instanceof FormulaError) return typeNum;
5546
+ type = typeNum;
5547
+ }
5548
+ if (nper === 0) return new FormulaError("#NUM!", "PMT: nper cannot be 0");
5549
+ if (rate === 0) {
5550
+ return -(pv + fv) / nper;
5551
+ }
5552
+ const factor = Math.pow(1 + rate, nper);
5553
+ const typeAdj = type !== 0 ? 1 + rate : 1;
5554
+ return -(pv * factor + fv) / ((factor - 1) / rate * typeAdj);
5555
+ }
5556
+ });
5557
+ registry.set("FV", {
5558
+ minArgs: 3,
5559
+ maxArgs: 5,
5560
+ evaluate(args, context, evaluator) {
5561
+ const rawRate = evaluator.evaluate(args[0], context);
5562
+ if (rawRate instanceof FormulaError) return rawRate;
5563
+ const rate = toNumber(rawRate);
5564
+ if (rate instanceof FormulaError) return rate;
5565
+ const rawNper = evaluator.evaluate(args[1], context);
5566
+ if (rawNper instanceof FormulaError) return rawNper;
5567
+ const nper = toNumber(rawNper);
5568
+ if (nper instanceof FormulaError) return nper;
5569
+ const rawPmt = evaluator.evaluate(args[2], context);
5570
+ if (rawPmt instanceof FormulaError) return rawPmt;
5571
+ const pmt = toNumber(rawPmt);
5572
+ if (pmt instanceof FormulaError) return pmt;
5573
+ let pv = 0;
5574
+ if (args.length >= 4) {
5575
+ const rawPv = evaluator.evaluate(args[3], context);
5576
+ if (rawPv instanceof FormulaError) return rawPv;
5577
+ const pvNum = toNumber(rawPv);
5578
+ if (pvNum instanceof FormulaError) return pvNum;
5579
+ pv = pvNum;
5580
+ }
5581
+ let type = 0;
5582
+ if (args.length >= 5) {
5583
+ const rawType = evaluator.evaluate(args[4], context);
5584
+ if (rawType instanceof FormulaError) return rawType;
5585
+ const typeNum = toNumber(rawType);
5586
+ if (typeNum instanceof FormulaError) return typeNum;
5587
+ type = typeNum;
5588
+ }
5589
+ if (rate === 0) {
5590
+ return -(pv + pmt * nper);
5591
+ }
5592
+ const factor = Math.pow(1 + rate, nper);
5593
+ const typeAdj = type !== 0 ? 1 + rate : 1;
5594
+ return -(pv * factor + pmt * typeAdj * (factor - 1) / rate);
5595
+ }
5596
+ });
5597
+ registry.set("PV", {
5598
+ minArgs: 3,
5599
+ maxArgs: 5,
5600
+ evaluate(args, context, evaluator) {
5601
+ const rawRate = evaluator.evaluate(args[0], context);
5602
+ if (rawRate instanceof FormulaError) return rawRate;
5603
+ const rate = toNumber(rawRate);
5604
+ if (rate instanceof FormulaError) return rate;
5605
+ const rawNper = evaluator.evaluate(args[1], context);
5606
+ if (rawNper instanceof FormulaError) return rawNper;
5607
+ const nper = toNumber(rawNper);
5608
+ if (nper instanceof FormulaError) return nper;
5609
+ const rawPmt = evaluator.evaluate(args[2], context);
5610
+ if (rawPmt instanceof FormulaError) return rawPmt;
5611
+ const pmt = toNumber(rawPmt);
5612
+ if (pmt instanceof FormulaError) return pmt;
5613
+ let fv = 0;
5614
+ if (args.length >= 4) {
5615
+ const rawFv = evaluator.evaluate(args[3], context);
5616
+ if (rawFv instanceof FormulaError) return rawFv;
5617
+ const fvNum = toNumber(rawFv);
5618
+ if (fvNum instanceof FormulaError) return fvNum;
5619
+ fv = fvNum;
5620
+ }
5621
+ let type = 0;
5622
+ if (args.length >= 5) {
5623
+ const rawType = evaluator.evaluate(args[4], context);
5624
+ if (rawType instanceof FormulaError) return rawType;
5625
+ const typeNum = toNumber(rawType);
5626
+ if (typeNum instanceof FormulaError) return typeNum;
5627
+ type = typeNum;
5628
+ }
5629
+ if (rate === 0) {
5630
+ return -pmt * nper - fv;
5631
+ }
5632
+ const factor = Math.pow(1 + rate, nper);
5633
+ const typeAdj = type !== 0 ? 1 + rate : 1;
5634
+ return -(fv + pmt * typeAdj * (factor - 1) / rate) / factor;
5635
+ }
5636
+ });
5637
+ registry.set("NPER", {
5638
+ minArgs: 3,
5639
+ maxArgs: 5,
5640
+ evaluate(args, context, evaluator) {
5641
+ const rawRate = evaluator.evaluate(args[0], context);
5642
+ if (rawRate instanceof FormulaError) return rawRate;
5643
+ const rate = toNumber(rawRate);
5644
+ if (rate instanceof FormulaError) return rate;
5645
+ const rawPmt = evaluator.evaluate(args[1], context);
5646
+ if (rawPmt instanceof FormulaError) return rawPmt;
5647
+ const pmt = toNumber(rawPmt);
5648
+ if (pmt instanceof FormulaError) return pmt;
5649
+ const rawPv = evaluator.evaluate(args[2], context);
5650
+ if (rawPv instanceof FormulaError) return rawPv;
5651
+ const pv = toNumber(rawPv);
5652
+ if (pv instanceof FormulaError) return pv;
5653
+ let fv = 0;
5654
+ if (args.length >= 4) {
5655
+ const rawFv = evaluator.evaluate(args[3], context);
5656
+ if (rawFv instanceof FormulaError) return rawFv;
5657
+ const fvNum = toNumber(rawFv);
5658
+ if (fvNum instanceof FormulaError) return fvNum;
5659
+ fv = fvNum;
5660
+ }
5661
+ let type = 0;
5662
+ if (args.length >= 5) {
5663
+ const rawType = evaluator.evaluate(args[4], context);
5664
+ if (rawType instanceof FormulaError) return rawType;
5665
+ const typeNum = toNumber(rawType);
5666
+ if (typeNum instanceof FormulaError) return typeNum;
5667
+ type = typeNum;
5668
+ }
5669
+ if (rate === 0) {
5670
+ if (pmt === 0) return new FormulaError("#NUM!", "NPER: pmt cannot be 0 when rate is 0");
5671
+ return -(pv + fv) / pmt;
5672
+ }
5673
+ const typeAdj = type !== 0 ? 1 + rate : 1;
5674
+ const pmtAdj = pmt * typeAdj;
5675
+ const num = pmtAdj - fv * rate;
5676
+ const den = pmtAdj + pv * rate;
5677
+ if (den === 0) return new FormulaError("#DIV/0!", "NPER: division by zero");
5678
+ if (num / den <= 0) return new FormulaError("#NUM!", "NPER: logarithm of non-positive number");
5679
+ return Math.log(num / den) / Math.log(1 + rate);
5680
+ }
5681
+ });
5682
+ registry.set("RATE", {
5683
+ minArgs: 3,
5684
+ maxArgs: 6,
5685
+ evaluate(args, context, evaluator) {
5686
+ const rawNper = evaluator.evaluate(args[0], context);
5687
+ if (rawNper instanceof FormulaError) return rawNper;
5688
+ const nper = toNumber(rawNper);
5689
+ if (nper instanceof FormulaError) return nper;
5690
+ const rawPmt = evaluator.evaluate(args[1], context);
5691
+ if (rawPmt instanceof FormulaError) return rawPmt;
5692
+ const pmt = toNumber(rawPmt);
5693
+ if (pmt instanceof FormulaError) return pmt;
5694
+ const rawPv = evaluator.evaluate(args[2], context);
5695
+ if (rawPv instanceof FormulaError) return rawPv;
5696
+ const pv = toNumber(rawPv);
5697
+ if (pv instanceof FormulaError) return pv;
5698
+ let fv = 0;
5699
+ if (args.length >= 4) {
5700
+ const rawFv = evaluator.evaluate(args[3], context);
5701
+ if (rawFv instanceof FormulaError) return rawFv;
5702
+ const fvNum = toNumber(rawFv);
5703
+ if (fvNum instanceof FormulaError) return fvNum;
5704
+ fv = fvNum;
5705
+ }
5706
+ let type = 0;
5707
+ if (args.length >= 5) {
5708
+ const rawType = evaluator.evaluate(args[4], context);
5709
+ if (rawType instanceof FormulaError) return rawType;
5710
+ const typeNum = toNumber(rawType);
5711
+ if (typeNum instanceof FormulaError) return typeNum;
5712
+ type = typeNum;
5713
+ }
5714
+ let guess = 0.1;
5715
+ if (args.length >= 6) {
5716
+ const rawGuess = evaluator.evaluate(args[5], context);
5717
+ if (rawGuess instanceof FormulaError) return rawGuess;
5718
+ const guessNum = toNumber(rawGuess);
5719
+ if (guessNum instanceof FormulaError) return guessNum;
5720
+ guess = guessNum;
5721
+ }
5722
+ const MAX_ITER = 20;
5723
+ const TOLERANCE = 1e-7;
5724
+ let r = guess;
5725
+ for (let i = 0; i < MAX_ITER; i++) {
5726
+ if (r <= -1) return new FormulaError("#NUM!", "RATE: rate converged to invalid value");
5727
+ const factor = Math.pow(1 + r, nper);
5728
+ const typeAdj = type !== 0 ? 1 + r : 1;
5729
+ let f;
5730
+ let df;
5731
+ if (Math.abs(r) < 1e-10) {
5732
+ f = pv + pmt * nper + fv;
5733
+ df = pv * nper + pmt * nper * (nper - 1) / 2;
5734
+ } else {
5735
+ f = pv * factor + pmt * typeAdj * (factor - 1) / r + fv;
5736
+ const dfactor = nper * Math.pow(1 + r, nper - 1);
5737
+ const dTypeAdj = type !== 0 ? 1 : 0;
5738
+ df = pv * dfactor + pmt * (dTypeAdj * (factor - 1) / r + typeAdj * (dfactor * r - (factor - 1)) / (r * r));
5739
+ }
5740
+ if (Math.abs(df) < 1e-15) return new FormulaError("#NUM!", "RATE: derivative too small, no convergence");
5741
+ const delta = f / df;
5742
+ r = r - delta;
5743
+ if (Math.abs(delta) < TOLERANCE) return r;
5744
+ }
5745
+ return new FormulaError("#NUM!", "RATE: did not converge");
5746
+ }
5747
+ });
5748
+ registry.set("NPV", {
5749
+ minArgs: 2,
5750
+ maxArgs: -1,
5751
+ evaluate(args, context, evaluator) {
5752
+ const rawRate = evaluator.evaluate(args[0], context);
5753
+ if (rawRate instanceof FormulaError) return rawRate;
5754
+ const rate = toNumber(rawRate);
5755
+ if (rate instanceof FormulaError) return rate;
5756
+ const values = flattenArgs(args.slice(1), context, evaluator);
5757
+ let npv = 0;
5758
+ let period = 1;
5759
+ for (const val of values) {
5760
+ if (val instanceof FormulaError) return val;
5761
+ if (typeof val === "number" || typeof val === "boolean") {
5762
+ const n = typeof val === "boolean" ? val ? 1 : 0 : val;
5763
+ npv += n / Math.pow(1 + rate, period);
5764
+ period++;
5765
+ }
5766
+ }
5767
+ return npv;
5768
+ }
5769
+ });
5770
+ registry.set("IRR", {
5771
+ minArgs: 1,
5772
+ maxArgs: 2,
5773
+ evaluate(args, context, evaluator) {
5774
+ const cashFlows = flattenArgs([args[0]], context, evaluator);
5775
+ const nums = [];
5776
+ for (const val of cashFlows) {
5777
+ if (val instanceof FormulaError) return val;
5778
+ if (typeof val === "number") nums.push(val);
5779
+ else if (typeof val === "boolean") nums.push(val ? 1 : 0);
5780
+ }
5781
+ if (nums.length === 0) return new FormulaError("#NUM!", "IRR: no values");
5782
+ let guess = 0.1;
5783
+ if (args.length >= 2) {
5784
+ const rawGuess = evaluator.evaluate(args[1], context);
5785
+ if (rawGuess instanceof FormulaError) return rawGuess;
5786
+ const guessNum = toNumber(rawGuess);
5787
+ if (guessNum instanceof FormulaError) return guessNum;
5788
+ guess = guessNum;
5789
+ }
5790
+ const MAX_ITER = 20;
5791
+ const TOLERANCE = 1e-7;
5792
+ let r = guess;
5793
+ for (let i = 0; i < MAX_ITER; i++) {
5794
+ if (r <= -1) return new FormulaError("#NUM!", "IRR: rate converged below -1");
5795
+ let f = 0;
5796
+ let df = 0;
5797
+ for (let j = 0; j < nums.length; j++) {
5798
+ const factor = Math.pow(1 + r, j);
5799
+ f += nums[j] / factor;
5800
+ if (j > 0) {
5801
+ df -= j * nums[j] / Math.pow(1 + r, j + 1);
5802
+ }
5803
+ }
5804
+ if (Math.abs(df) < 1e-15) return new FormulaError("#NUM!", "IRR: derivative too small");
5805
+ const delta = f / df;
5806
+ r = r - delta;
5807
+ if (Math.abs(delta) < TOLERANCE) return r;
5808
+ }
5809
+ return new FormulaError("#NUM!", "IRR: did not converge");
5810
+ }
5811
+ });
5812
+ registry.set("SLN", {
5813
+ minArgs: 3,
5814
+ maxArgs: 3,
5815
+ evaluate(args, context, evaluator) {
5816
+ const rawCost = evaluator.evaluate(args[0], context);
5817
+ if (rawCost instanceof FormulaError) return rawCost;
5818
+ const cost = toNumber(rawCost);
5819
+ if (cost instanceof FormulaError) return cost;
5820
+ const rawSalvage = evaluator.evaluate(args[1], context);
5821
+ if (rawSalvage instanceof FormulaError) return rawSalvage;
5822
+ const salvage = toNumber(rawSalvage);
5823
+ if (salvage instanceof FormulaError) return salvage;
5824
+ const rawLife = evaluator.evaluate(args[2], context);
5825
+ if (rawLife instanceof FormulaError) return rawLife;
5826
+ const life = toNumber(rawLife);
5827
+ if (life instanceof FormulaError) return life;
5828
+ if (life === 0) return new FormulaError("#DIV/0!", "SLN: life cannot be 0");
5829
+ return (cost - salvage) / life;
5830
+ }
5831
+ });
5832
+ }
5833
+ function extractNums(args, context, evaluator) {
5834
+ const values = flattenArgs(args, context, evaluator);
5835
+ const nums = [];
5836
+ for (const val of values) {
5837
+ if (val instanceof FormulaError) return val;
5838
+ if (typeof val === "number") nums.push(val);
5839
+ else if (typeof val === "boolean") nums.push(val ? 1 : 0);
5840
+ }
5841
+ return nums;
5842
+ }
5843
+ function mean(nums) {
5844
+ let sum = 0;
5845
+ for (const n of nums) sum += n;
5846
+ return sum / nums.length;
5847
+ }
5848
+ function registerStatisticalExtendedFunctions(registry) {
5849
+ const stdevImpl = {
5850
+ minArgs: 1,
5851
+ maxArgs: -1,
5852
+ evaluate(args, context, evaluator) {
5853
+ const nums = extractNums(args, context, evaluator);
5854
+ if (nums instanceof FormulaError) return nums;
5855
+ if (nums.length < 2) return new FormulaError("#DIV/0!", "STDEV requires at least 2 values");
5856
+ const m = mean(nums);
5857
+ let sum = 0;
5858
+ for (const n of nums) sum += (n - m) * (n - m);
5859
+ return Math.sqrt(sum / (nums.length - 1));
5860
+ }
5861
+ };
5862
+ registry.set("STDEV", stdevImpl);
5863
+ registry.set("STDEV.S", stdevImpl);
5864
+ const stdevpImpl = {
5865
+ minArgs: 1,
5866
+ maxArgs: -1,
5867
+ evaluate(args, context, evaluator) {
5868
+ const nums = extractNums(args, context, evaluator);
5869
+ if (nums instanceof FormulaError) return nums;
5870
+ if (nums.length === 0) return new FormulaError("#DIV/0!", "STDEVP requires at least 1 value");
5871
+ const m = mean(nums);
5872
+ let sum = 0;
5873
+ for (const n of nums) sum += (n - m) * (n - m);
5874
+ return Math.sqrt(sum / nums.length);
5875
+ }
5876
+ };
5877
+ registry.set("STDEVP", stdevpImpl);
5878
+ registry.set("STDEV.P", stdevpImpl);
5879
+ const varImpl = {
5880
+ minArgs: 1,
5881
+ maxArgs: -1,
5882
+ evaluate(args, context, evaluator) {
5883
+ const nums = extractNums(args, context, evaluator);
5884
+ if (nums instanceof FormulaError) return nums;
5885
+ if (nums.length < 2) return new FormulaError("#DIV/0!", "VAR requires at least 2 values");
5886
+ const m = mean(nums);
5887
+ let sum = 0;
5888
+ for (const n of nums) sum += (n - m) * (n - m);
5889
+ return sum / (nums.length - 1);
5890
+ }
5891
+ };
5892
+ registry.set("VAR", varImpl);
5893
+ registry.set("VAR.S", varImpl);
5894
+ const varpImpl = {
5895
+ minArgs: 1,
5896
+ maxArgs: -1,
5897
+ evaluate(args, context, evaluator) {
5898
+ const nums = extractNums(args, context, evaluator);
5899
+ if (nums instanceof FormulaError) return nums;
5900
+ if (nums.length === 0) return new FormulaError("#DIV/0!", "VARP requires at least 1 value");
5901
+ const m = mean(nums);
5902
+ let sum = 0;
5903
+ for (const n of nums) sum += (n - m) * (n - m);
5904
+ return sum / nums.length;
5905
+ }
5906
+ };
5907
+ registry.set("VARP", varpImpl);
5908
+ registry.set("VAR.P", varpImpl);
5909
+ registry.set("CORREL", {
5910
+ minArgs: 2,
5911
+ maxArgs: 2,
5912
+ evaluate(args, context, evaluator) {
5913
+ const nums1 = extractNums([args[0]], context, evaluator);
5914
+ if (nums1 instanceof FormulaError) return nums1;
5915
+ const nums2 = extractNums([args[1]], context, evaluator);
5916
+ if (nums2 instanceof FormulaError) return nums2;
5917
+ if (nums1.length !== nums2.length) {
5918
+ return new FormulaError("#N/A", "CORREL: arrays must have same number of values");
5919
+ }
5920
+ if (nums1.length < 2) {
5921
+ return new FormulaError("#DIV/0!", "CORREL requires at least 2 paired values");
5922
+ }
5923
+ const m1 = mean(nums1);
5924
+ const m2 = mean(nums2);
5925
+ let cov = 0;
5926
+ let var1 = 0;
5927
+ let var2 = 0;
5928
+ for (let i = 0; i < nums1.length; i++) {
5929
+ const d1 = nums1[i] - m1;
5930
+ const d2 = nums2[i] - m2;
5931
+ cov += d1 * d2;
5932
+ var1 += d1 * d1;
5933
+ var2 += d2 * d2;
5934
+ }
5935
+ const denom = Math.sqrt(var1 * var2);
5936
+ if (denom === 0) return new FormulaError("#DIV/0!", "CORREL: zero variance");
5937
+ return cov / denom;
5938
+ }
5939
+ });
5940
+ function percentileCalc(nums, k) {
5941
+ if (k < 0 || k > 1) return new FormulaError("#NUM!", "PERCENTILE: k must be between 0 and 1");
5942
+ if (nums.length === 0) return new FormulaError("#NUM!", "PERCENTILE: empty array");
5943
+ const sorted = [...nums].sort((a, b) => a - b);
5944
+ const idx = k * (sorted.length - 1);
5945
+ const low = Math.floor(idx);
5946
+ const high = Math.ceil(idx);
5947
+ if (low === high) return sorted[low];
5948
+ const frac = idx - low;
5949
+ return sorted[low] + frac * (sorted[high] - sorted[low]);
5950
+ }
5951
+ const percentileImpl = {
5952
+ minArgs: 2,
5953
+ maxArgs: 2,
5954
+ evaluate(args, context, evaluator) {
5955
+ const nums = extractNums([args[0]], context, evaluator);
5956
+ if (nums instanceof FormulaError) return nums;
5957
+ const rawK = evaluator.evaluate(args[1], context);
5958
+ if (rawK instanceof FormulaError) return rawK;
5959
+ const k = toNumber(rawK);
5960
+ if (k instanceof FormulaError) return k;
5961
+ return percentileCalc(nums, k);
5962
+ }
5963
+ };
5964
+ registry.set("PERCENTILE", percentileImpl);
5965
+ registry.set("PERCENTILE.INC", percentileImpl);
5966
+ const quartileImpl = {
5967
+ minArgs: 2,
5968
+ maxArgs: 2,
5969
+ evaluate(args, context, evaluator) {
5970
+ const nums = extractNums([args[0]], context, evaluator);
5971
+ if (nums instanceof FormulaError) return nums;
5972
+ const rawQuart = evaluator.evaluate(args[1], context);
5973
+ if (rawQuart instanceof FormulaError) return rawQuart;
5974
+ const quart = toNumber(rawQuart);
5975
+ if (quart instanceof FormulaError) return quart;
5976
+ const quartInt = Math.trunc(quart);
5977
+ if (quartInt < 0 || quartInt > 4) {
5978
+ return new FormulaError("#NUM!", "QUARTILE: quart must be 0, 1, 2, 3, or 4");
5979
+ }
5980
+ return percentileCalc(nums, quartInt * 0.25);
5981
+ }
5982
+ };
5983
+ registry.set("QUARTILE", quartileImpl);
5984
+ registry.set("QUARTILE.INC", quartileImpl);
5985
+ const modeImpl = {
5986
+ minArgs: 1,
5987
+ maxArgs: -1,
5988
+ evaluate(args, context, evaluator) {
5989
+ const nums = extractNums(args, context, evaluator);
5990
+ if (nums instanceof FormulaError) return nums;
5991
+ if (nums.length === 0) return new FormulaError("#N/A", "MODE: no numeric values");
5992
+ const freq = /* @__PURE__ */ new Map();
5993
+ for (const n of nums) {
5994
+ freq.set(n, (freq.get(n) ?? 0) + 1);
5995
+ }
5996
+ let maxFreq = 0;
5997
+ let modeVal = null;
5998
+ for (const n of nums) {
5999
+ const f = freq.get(n) ?? 0;
6000
+ if (f > maxFreq) {
6001
+ maxFreq = f;
6002
+ modeVal = n;
6003
+ }
6004
+ }
6005
+ if (maxFreq < 1 || modeVal === null) return new FormulaError("#N/A", "MODE: no values");
6006
+ return modeVal;
6007
+ }
6008
+ };
6009
+ registry.set("MODE", modeImpl);
6010
+ registry.set("MODE.SNGL", modeImpl);
6011
+ registry.set("GEOMEAN", {
6012
+ minArgs: 1,
6013
+ maxArgs: -1,
6014
+ evaluate(args, context, evaluator) {
6015
+ const nums = extractNums(args, context, evaluator);
6016
+ if (nums instanceof FormulaError) return nums;
6017
+ if (nums.length === 0) return new FormulaError("#NUM!", "GEOMEAN: no numeric values");
6018
+ let sumLn = 0;
6019
+ for (const n of nums) {
6020
+ if (n <= 0) return new FormulaError("#NUM!", "GEOMEAN: all values must be positive");
6021
+ sumLn += Math.log(n);
6022
+ }
6023
+ return Math.exp(sumLn / nums.length);
6024
+ }
6025
+ });
6026
+ registry.set("HARMEAN", {
6027
+ minArgs: 1,
6028
+ maxArgs: -1,
6029
+ evaluate(args, context, evaluator) {
6030
+ const nums = extractNums(args, context, evaluator);
6031
+ if (nums instanceof FormulaError) return nums;
6032
+ if (nums.length === 0) return new FormulaError("#NUM!", "HARMEAN: no numeric values");
6033
+ let sumRecip = 0;
6034
+ for (const n of nums) {
6035
+ if (n <= 0) return new FormulaError("#NUM!", "HARMEAN: all values must be positive");
6036
+ sumRecip += 1 / n;
6037
+ }
6038
+ if (sumRecip === 0) return new FormulaError("#DIV/0!", "HARMEAN: sum of reciprocals is zero");
6039
+ return nums.length / sumRecip;
6040
+ }
6041
+ });
6042
+ }
6043
+ function registerReferenceFunctions(registry) {
6044
+ registry.set("INDIRECT", {
6045
+ minArgs: 1,
6046
+ maxArgs: 2,
6047
+ evaluate(args, context, evaluator) {
6048
+ const rawRef = evaluator.evaluate(args[0], context);
6049
+ if (rawRef instanceof FormulaError) return rawRef;
6050
+ const refText = String(rawRef ?? "");
6051
+ const range = parseRange(refText);
6052
+ if (range) {
6053
+ const start = range.start;
6054
+ const end = range.end;
6055
+ if (start.row === end.row && start.col === end.col) {
6056
+ return context.getCellValue(start);
6057
+ }
6058
+ return context.getCellValue(start);
6059
+ }
6060
+ const addr = parseCellRef(refText);
6061
+ if (!addr) {
6062
+ return new FormulaError("#REF!", `INDIRECT: invalid reference "${refText}"`);
6063
+ }
6064
+ return context.getCellValue(addr);
6065
+ }
6066
+ });
6067
+ registry.set("OFFSET", {
6068
+ minArgs: 3,
6069
+ maxArgs: 5,
6070
+ evaluate(args, context, evaluator) {
6071
+ let baseCol;
6072
+ let baseRow;
6073
+ if (args[0].kind === "cellRef") {
6074
+ baseCol = args[0].address.col;
6075
+ baseRow = args[0].address.row;
6076
+ } else if (args[0].kind === "range") {
6077
+ baseCol = args[0].start.col;
6078
+ baseRow = args[0].start.row;
6079
+ } else {
6080
+ return new FormulaError("#VALUE!", "OFFSET: first argument must be a cell reference");
6081
+ }
6082
+ const rawRows = evaluator.evaluate(args[1], context);
6083
+ if (rawRows instanceof FormulaError) return rawRows;
6084
+ const rowOffset = toNumber(rawRows);
6085
+ if (rowOffset instanceof FormulaError) return rowOffset;
6086
+ const rawCols = evaluator.evaluate(args[2], context);
6087
+ if (rawCols instanceof FormulaError) return rawCols;
6088
+ const colOffset = toNumber(rawCols);
6089
+ if (colOffset instanceof FormulaError) return colOffset;
6090
+ const targetRow = baseRow + Math.trunc(rowOffset);
6091
+ const targetCol = baseCol + Math.trunc(colOffset);
6092
+ if (targetRow < 0 || targetCol < 0) {
6093
+ return new FormulaError("#REF!", "OFFSET: reference out of bounds");
6094
+ }
6095
+ let height = 1;
6096
+ if (args.length >= 4) {
6097
+ const rawH = evaluator.evaluate(args[3], context);
6098
+ if (rawH instanceof FormulaError) return rawH;
6099
+ const h = toNumber(rawH);
6100
+ if (h instanceof FormulaError) return h;
6101
+ height = Math.trunc(h);
6102
+ }
6103
+ let width = 1;
6104
+ if (args.length >= 5) {
6105
+ const rawW = evaluator.evaluate(args[4], context);
6106
+ if (rawW instanceof FormulaError) return rawW;
6107
+ const w = toNumber(rawW);
6108
+ if (w instanceof FormulaError) return w;
6109
+ width = Math.trunc(w);
6110
+ }
6111
+ if (height <= 0 || width <= 0) {
6112
+ return new FormulaError("#VALUE!", "OFFSET: height and width must be >= 1");
6113
+ }
6114
+ if (height === 1 && width === 1) {
6115
+ return context.getCellValue({ col: targetCol, row: targetRow, absCol: false, absRow: false });
6116
+ }
6117
+ return context.getCellValue({ col: targetCol, row: targetRow, absCol: false, absRow: false });
6118
+ }
6119
+ });
6120
+ registry.set("ADDRESS", {
6121
+ minArgs: 2,
6122
+ maxArgs: 5,
6123
+ evaluate(args, context, evaluator) {
6124
+ const rawRow = evaluator.evaluate(args[0], context);
6125
+ if (rawRow instanceof FormulaError) return rawRow;
6126
+ const rowNum = toNumber(rawRow);
6127
+ if (rowNum instanceof FormulaError) return rowNum;
6128
+ const rawCol = evaluator.evaluate(args[1], context);
6129
+ if (rawCol instanceof FormulaError) return rawCol;
6130
+ const colNum = toNumber(rawCol);
6131
+ if (colNum instanceof FormulaError) return colNum;
6132
+ const row = Math.trunc(rowNum);
6133
+ const col = Math.trunc(colNum);
6134
+ if (row < 1 || col < 1) {
6135
+ return new FormulaError("#VALUE!", "ADDRESS: row and column must be >= 1");
6136
+ }
6137
+ let absNum = 1;
6138
+ if (args.length >= 3) {
6139
+ const rawAbs = evaluator.evaluate(args[2], context);
6140
+ if (rawAbs instanceof FormulaError) return rawAbs;
6141
+ const a = toNumber(rawAbs);
6142
+ if (a instanceof FormulaError) return a;
6143
+ absNum = Math.trunc(a);
6144
+ }
6145
+ let sheetText = "";
6146
+ if (args.length >= 5) {
6147
+ const rawSheet = evaluator.evaluate(args[4], context);
6148
+ if (rawSheet instanceof FormulaError) return rawSheet;
6149
+ if (rawSheet !== null && rawSheet !== void 0 && rawSheet !== false) {
6150
+ sheetText = String(rawSheet);
6151
+ }
6152
+ }
6153
+ const colLetter = indexToColumnLetter(col - 1);
6154
+ let address;
6155
+ switch (absNum) {
6156
+ case 1:
6157
+ address = `$${colLetter}$${row}`;
6158
+ break;
6159
+ // $A$1
6160
+ case 2:
6161
+ address = `${colLetter}$${row}`;
6162
+ break;
6163
+ // A$1
6164
+ case 3:
6165
+ address = `$${colLetter}${row}`;
6166
+ break;
6167
+ // $A1
6168
+ case 4:
6169
+ address = `${colLetter}${row}`;
6170
+ break;
6171
+ // A1
6172
+ default:
6173
+ address = `$${colLetter}$${row}`;
6174
+ }
6175
+ if (sheetText) {
6176
+ const quoted = sheetText.includes(" ") ? `'${sheetText}'` : sheetText;
6177
+ return `${quoted}!${address}`;
6178
+ }
6179
+ return address;
6180
+ }
6181
+ });
6182
+ registry.set("ROW", {
6183
+ minArgs: 0,
6184
+ maxArgs: 1,
6185
+ evaluate(args, context, evaluator) {
6186
+ if (args.length === 0) {
6187
+ return 1;
6188
+ }
6189
+ const arg = args[0];
6190
+ if (arg.kind === "cellRef") {
6191
+ return arg.address.row + 1;
6192
+ }
6193
+ if (arg.kind === "range") {
6194
+ return arg.start.row + 1;
6195
+ }
6196
+ const rawRef = evaluator.evaluate(arg, context);
6197
+ if (rawRef instanceof FormulaError) return rawRef;
6198
+ const refText = String(rawRef ?? "");
6199
+ const addr = parseCellRef(refText);
6200
+ if (!addr) {
6201
+ const rng = parseRange(refText);
6202
+ if (rng) return rng.start.row + 1;
6203
+ return new FormulaError("#VALUE!", "ROW: invalid reference");
6204
+ }
6205
+ return addr.row + 1;
6206
+ }
6207
+ });
6208
+ registry.set("COLUMN", {
6209
+ minArgs: 0,
6210
+ maxArgs: 1,
6211
+ evaluate(args, context, evaluator) {
6212
+ if (args.length === 0) {
6213
+ return 1;
6214
+ }
6215
+ const arg = args[0];
6216
+ if (arg.kind === "cellRef") {
6217
+ return arg.address.col + 1;
6218
+ }
6219
+ if (arg.kind === "range") {
6220
+ return arg.start.col + 1;
6221
+ }
6222
+ const rawRef = evaluator.evaluate(arg, context);
6223
+ if (rawRef instanceof FormulaError) return rawRef;
6224
+ const refText = String(rawRef ?? "");
6225
+ const addr = parseCellRef(refText);
6226
+ if (!addr) {
6227
+ const rng = parseRange(refText);
6228
+ if (rng) return rng.start.col + 1;
6229
+ return new FormulaError("#VALUE!", "COLUMN: invalid reference");
6230
+ }
6231
+ return addr.col + 1;
6232
+ }
6233
+ });
6234
+ registry.set("ROWS", {
6235
+ minArgs: 1,
6236
+ maxArgs: 1,
6237
+ evaluate(args, _context, _evaluator) {
6238
+ const arg = args[0];
6239
+ if (arg.kind === "range") {
6240
+ return Math.abs(arg.end.row - arg.start.row) + 1;
6241
+ }
6242
+ if (arg.kind === "cellRef") {
6243
+ return 1;
6244
+ }
6245
+ return new FormulaError("#VALUE!", "ROWS: argument must be a range reference");
6246
+ }
6247
+ });
6248
+ registry.set("COLUMNS", {
4656
6249
  minArgs: 1,
4657
6250
  maxArgs: 1,
4658
- evaluate(args, context, evaluator) {
4659
- const val = evaluator.evaluate(args[0], context);
4660
- return typeof val === "number" && !isNaN(val);
6251
+ evaluate(args, _context, _evaluator) {
6252
+ const arg = args[0];
6253
+ if (arg.kind === "range") {
6254
+ return Math.abs(arg.end.col - arg.start.col) + 1;
6255
+ }
6256
+ if (arg.kind === "cellRef") {
6257
+ return 1;
6258
+ }
6259
+ return new FormulaError("#VALUE!", "COLUMNS: argument must be a range reference");
4661
6260
  }
4662
6261
  });
4663
- registry.set("ISTEXT", {
6262
+ registry.set("SEQUENCE", {
4664
6263
  minArgs: 1,
4665
- maxArgs: 1,
6264
+ maxArgs: 4,
4666
6265
  evaluate(args, context, evaluator) {
4667
- const val = evaluator.evaluate(args[0], context);
4668
- return typeof val === "string";
6266
+ const rawRows = evaluator.evaluate(args[0], context);
6267
+ if (rawRows instanceof FormulaError) return rawRows;
6268
+ const rows = toNumber(rawRows);
6269
+ if (rows instanceof FormulaError) return rows;
6270
+ let cols = 1;
6271
+ if (args.length >= 2) {
6272
+ const rawCols = evaluator.evaluate(args[1], context);
6273
+ if (rawCols instanceof FormulaError) return rawCols;
6274
+ const c = toNumber(rawCols);
6275
+ if (c instanceof FormulaError) return c;
6276
+ cols = Math.trunc(c);
6277
+ }
6278
+ let start = 1;
6279
+ if (args.length >= 3) {
6280
+ const rawStart = evaluator.evaluate(args[2], context);
6281
+ if (rawStart instanceof FormulaError) return rawStart;
6282
+ const s = toNumber(rawStart);
6283
+ if (s instanceof FormulaError) return s;
6284
+ start = s;
6285
+ }
6286
+ let step = 1;
6287
+ if (args.length >= 4) {
6288
+ const rawStep = evaluator.evaluate(args[3], context);
6289
+ if (rawStep instanceof FormulaError) return rawStep;
6290
+ const st = toNumber(rawStep);
6291
+ if (st instanceof FormulaError) return st;
6292
+ step = st;
6293
+ }
6294
+ const rowCount = Math.trunc(rows);
6295
+ const colCount = Math.max(1, cols);
6296
+ if (rowCount < 1) {
6297
+ return new FormulaError("#VALUE!", "SEQUENCE: rows must be >= 1");
6298
+ }
6299
+ const result = [];
6300
+ let current = start;
6301
+ for (let r = 0; r < rowCount; r++) {
6302
+ const row = [];
6303
+ for (let c = 0; c < colCount; c++) {
6304
+ row.push(current);
6305
+ current += step;
6306
+ }
6307
+ result.push(row);
6308
+ }
6309
+ if (rowCount === 1 && colCount === 1) {
6310
+ return result[0][0];
6311
+ }
6312
+ return result[0][0];
4669
6313
  }
4670
6314
  });
4671
- registry.set("ISERROR", {
6315
+ registry.set("TRANSPOSE", {
4672
6316
  minArgs: 1,
4673
6317
  maxArgs: 1,
4674
- evaluate(args, context, evaluator) {
4675
- const val = evaluator.evaluate(args[0], context);
4676
- return val instanceof FormulaError;
6318
+ evaluate(args, context, _evaluator) {
6319
+ if (args[0].kind !== "range") {
6320
+ return new FormulaError("#VALUE!", "TRANSPOSE: argument must be a range");
6321
+ }
6322
+ const data = context.getRangeValues({ start: args[0].start, end: args[0].end });
6323
+ if (data.length === 0) return null;
6324
+ const rows = data.length;
6325
+ const cols = data[0].length;
6326
+ const transposed = [];
6327
+ for (let c = 0; c < cols; c++) {
6328
+ const newRow = [];
6329
+ for (let r = 0; r < rows; r++) {
6330
+ newRow.push(data[r][c]);
6331
+ }
6332
+ transposed.push(newRow);
6333
+ }
6334
+ return transposed[0][0] ?? null;
4677
6335
  }
4678
6336
  });
4679
- registry.set("ISNA", {
6337
+ registry.set("MMULT", {
6338
+ minArgs: 2,
6339
+ maxArgs: 2,
6340
+ evaluate(args, context, _evaluator) {
6341
+ if (args[0].kind !== "range") {
6342
+ return new FormulaError("#VALUE!", "MMULT: array1 must be a range");
6343
+ }
6344
+ if (args[1].kind !== "range") {
6345
+ return new FormulaError("#VALUE!", "MMULT: array2 must be a range");
6346
+ }
6347
+ const a = context.getRangeValues({ start: args[0].start, end: args[0].end });
6348
+ const b = context.getRangeValues({ start: args[1].start, end: args[1].end });
6349
+ if (a.length === 0 || b.length === 0) {
6350
+ return new FormulaError("#VALUE!", "MMULT: empty array");
6351
+ }
6352
+ const aRows = a.length;
6353
+ const aCols = a[0].length;
6354
+ const bRows = b.length;
6355
+ const bCols = b[0].length;
6356
+ if (aCols !== bRows) {
6357
+ return new FormulaError("#VALUE!", `MMULT: columns of array1 (${aCols}) must equal rows of array2 (${bRows})`);
6358
+ }
6359
+ const result = [];
6360
+ for (let r = 0; r < aRows; r++) {
6361
+ const row = [];
6362
+ for (let c = 0; c < bCols; c++) {
6363
+ let sum = 0;
6364
+ for (let k = 0; k < aCols; k++) {
6365
+ const av = toNumber(a[r][k]);
6366
+ const bv = toNumber(b[k][c]);
6367
+ if (av instanceof FormulaError) return av;
6368
+ if (bv instanceof FormulaError) return bv;
6369
+ sum += av * bv;
6370
+ }
6371
+ row.push(sum);
6372
+ }
6373
+ result.push(row);
6374
+ }
6375
+ return result[0][0];
6376
+ }
6377
+ });
6378
+ registry.set("MDETERM", {
4680
6379
  minArgs: 1,
4681
6380
  maxArgs: 1,
4682
- evaluate(args, context, evaluator) {
4683
- const val = evaluator.evaluate(args[0], context);
4684
- return val instanceof FormulaError && val.type === "#N/A";
6381
+ evaluate(args, context, _evaluator) {
6382
+ if (args[0].kind !== "range") {
6383
+ return new FormulaError("#VALUE!", "MDETERM: argument must be a range");
6384
+ }
6385
+ const data = context.getRangeValues({ start: args[0].start, end: args[0].end });
6386
+ if (data.length === 0) return new FormulaError("#VALUE!", "MDETERM: empty array");
6387
+ const n = data.length;
6388
+ if (data.some((row) => row.length !== n)) {
6389
+ return new FormulaError("#VALUE!", "MDETERM: array must be square");
6390
+ }
6391
+ const matrix = [];
6392
+ for (let r = 0; r < n; r++) {
6393
+ const row = [];
6394
+ for (let c = 0; c < n; c++) {
6395
+ const v = toNumber(data[r][c]);
6396
+ if (v instanceof FormulaError) return v;
6397
+ row.push(v);
6398
+ }
6399
+ matrix.push(row);
6400
+ }
6401
+ return determinant(matrix);
4685
6402
  }
4686
6403
  });
4687
- registry.set("TYPE", {
6404
+ registry.set("MINVERSE", {
4688
6405
  minArgs: 1,
4689
6406
  maxArgs: 1,
4690
- evaluate(args, context, evaluator) {
4691
- const val = evaluator.evaluate(args[0], context);
4692
- if (val instanceof FormulaError) return 16;
4693
- if (typeof val === "number") return 1;
4694
- if (typeof val === "string") return 2;
4695
- if (typeof val === "boolean") return 4;
4696
- if (val === null || val === void 0) return 1;
4697
- return 1;
6407
+ evaluate(args, context, _evaluator) {
6408
+ if (args[0].kind !== "range") {
6409
+ return new FormulaError("#VALUE!", "MINVERSE: argument must be a range");
6410
+ }
6411
+ const data = context.getRangeValues({ start: args[0].start, end: args[0].end });
6412
+ if (data.length === 0) return new FormulaError("#VALUE!", "MINVERSE: empty array");
6413
+ const n = data.length;
6414
+ if (data.some((row) => row.length !== n)) {
6415
+ return new FormulaError("#VALUE!", "MINVERSE: array must be square");
6416
+ }
6417
+ const matrix = [];
6418
+ for (let r = 0; r < n; r++) {
6419
+ const row = [];
6420
+ for (let c = 0; c < n; c++) {
6421
+ const v = toNumber(data[r][c]);
6422
+ if (v instanceof FormulaError) return v;
6423
+ row.push(v);
6424
+ }
6425
+ matrix.push(row);
6426
+ }
6427
+ const inv = matrixInverse(matrix, n);
6428
+ if (inv instanceof FormulaError) return inv;
6429
+ return inv[0][0];
4698
6430
  }
4699
6431
  });
4700
6432
  }
6433
+ function determinant(m) {
6434
+ const n = m.length;
6435
+ if (n === 1) return m[0][0];
6436
+ if (n === 2) return m[0][0] * m[1][1] - m[0][1] * m[1][0];
6437
+ let det = 0;
6438
+ for (let c = 0; c < n; c++) {
6439
+ const minor = [];
6440
+ for (let r = 1; r < n; r++) {
6441
+ const row = [];
6442
+ for (let cc = 0; cc < n; cc++) {
6443
+ if (cc !== c) row.push(m[r][cc]);
6444
+ }
6445
+ minor.push(row);
6446
+ }
6447
+ det += (c % 2 === 0 ? 1 : -1) * m[0][c] * determinant(minor);
6448
+ }
6449
+ return det;
6450
+ }
6451
+ function matrixInverse(m, n) {
6452
+ const aug = [];
6453
+ for (let r = 0; r < n; r++) {
6454
+ const row = [...m[r]];
6455
+ for (let c = 0; c < n; c++) {
6456
+ row.push(c === r ? 1 : 0);
6457
+ }
6458
+ aug.push(row);
6459
+ }
6460
+ for (let col = 0; col < n; col++) {
6461
+ let pivotRow = -1;
6462
+ let pivotVal = 0;
6463
+ for (let r = col; r < n; r++) {
6464
+ if (Math.abs(aug[r][col]) > Math.abs(pivotVal)) {
6465
+ pivotVal = aug[r][col];
6466
+ pivotRow = r;
6467
+ }
6468
+ }
6469
+ if (pivotRow === -1 || Math.abs(pivotVal) < 1e-12) {
6470
+ return new FormulaError("#NUM!", "MINVERSE: matrix is singular");
6471
+ }
6472
+ if (pivotRow !== col) {
6473
+ [aug[col], aug[pivotRow]] = [aug[pivotRow], aug[col]];
6474
+ }
6475
+ const scale = aug[col][col];
6476
+ for (let c = 0; c < 2 * n; c++) {
6477
+ aug[col][c] /= scale;
6478
+ }
6479
+ for (let r = 0; r < n; r++) {
6480
+ if (r !== col) {
6481
+ const factor = aug[r][col];
6482
+ for (let c = 0; c < 2 * n; c++) {
6483
+ aug[r][c] -= factor * aug[col][c];
6484
+ }
6485
+ }
6486
+ }
6487
+ }
6488
+ const result = [];
6489
+ for (let r = 0; r < n; r++) {
6490
+ result.push(aug[r].slice(n));
6491
+ }
6492
+ return result;
6493
+ }
4701
6494
  function createBuiltInFunctions() {
4702
6495
  const registry = /* @__PURE__ */ new Map();
4703
6496
  registerMathFunctions(registry);
@@ -4707,6 +6500,9 @@ function createBuiltInFunctions() {
4707
6500
  registerDateFunctions(registry);
4708
6501
  registerStatsFunctions(registry);
4709
6502
  registerInfoFunctions(registry);
6503
+ registerFinancialFunctions(registry);
6504
+ registerStatisticalExtendedFunctions(registry);
6505
+ registerReferenceFunctions(registry);
4710
6506
  return registry;
4711
6507
  }
4712
6508
  function extractDependencies(node) {
@@ -5059,8 +6855,7 @@ var FormulaEngine = class {
5059
6855
  return {
5060
6856
  getCellValue: (addr) => {
5061
6857
  const key = toCellKey(addr.col, addr.row, addr.sheet);
5062
- const cached = this.values.get(key);
5063
- if (cached !== void 0) return cached;
6858
+ if (this.values.has(key)) return this.values.get(key);
5064
6859
  if (addr.sheet) {
5065
6860
  const sheetAccessor = this.sheetAccessors.get(addr.sheet);
5066
6861
  if (!sheetAccessor) return new FormulaError("#REF!", `Unknown sheet: ${addr.sheet}`);
@@ -5083,18 +6878,21 @@ var FormulaEngine = class {
5083
6878
  const row = [];
5084
6879
  for (let c = minCol; c <= maxCol; c++) {
5085
6880
  const key = toCellKey(c, r, sheet);
5086
- const cached = this.values.get(key);
5087
- if (cached !== void 0) {
5088
- row.push(cached);
6881
+ if (this.values.has(key)) {
6882
+ row.push(this.values.get(key));
5089
6883
  } else {
5090
- row.push(rangeAccessor.getCellValue(c, r));
6884
+ row.push(rangeAccessor?.getCellValue(c, r));
5091
6885
  }
5092
6886
  }
5093
6887
  result.push(row);
5094
6888
  }
5095
6889
  return result;
5096
6890
  },
5097
- now: () => contextNow
6891
+ now: () => contextNow,
6892
+ getCellFormula: (addr) => {
6893
+ const key = toCellKey(addr.col, addr.row, addr.sheet);
6894
+ return this.formulas.get(key);
6895
+ }
5098
6896
  };
5099
6897
  }
5100
6898
  recalcCells(order, accessor, updatedCells) {
@@ -5256,19 +7054,10 @@ function useFormulaEngine(params) {
5256
7054
  }
5257
7055
  };
5258
7056
  }, [sheets]);
5259
- const createAccessor = useCallback(() => {
5260
- const currentItems = itemsRef.current;
5261
- const currentCols = flatColumnsRef.current;
5262
- return {
5263
- getCellValue: (col, row) => {
5264
- if (row < 0 || row >= currentItems.length) return null;
5265
- if (col < 0 || col >= currentCols.length) return null;
5266
- return getCellValue(currentItems[row], currentCols[col]);
5267
- },
5268
- getRowCount: () => currentItems.length,
5269
- getColumnCount: () => currentCols.length
5270
- };
5271
- }, [itemsRef, flatColumnsRef]);
7057
+ const createAccessor = useCallback(
7058
+ () => createGridDataAccessor(itemsRef.current, flatColumnsRef.current),
7059
+ [itemsRef, flatColumnsRef]
7060
+ );
5272
7061
  const initialLoadedRef = useRef(false);
5273
7062
  useEffect(() => {
5274
7063
  if (formulas && engineRef.current && initialFormulas && !initialLoadedRef.current) {
@@ -5314,20 +7103,192 @@ function useFormulaEngine(params) {
5314
7103
  const getAuditTrail = useCallback((col, row) => {
5315
7104
  return engineRef.current?.getAuditTrail(col, row) ?? null;
5316
7105
  }, []);
5317
- return useMemo(() => {
5318
- if (!formulas) return NOOP_RESULT;
5319
- return {
5320
- getFormulaValue,
5321
- hasFormula,
5322
- getFormula,
5323
- setFormula,
5324
- onCellChanged,
5325
- getPrecedents,
5326
- getDependents,
5327
- getAuditTrail,
5328
- enabled: true
5329
- };
5330
- }, [formulas, getFormulaValue, hasFormula, getFormula, setFormula, onCellChanged, getPrecedents, getDependents, getAuditTrail]);
7106
+ if (!formulas) return NOOP_RESULT;
7107
+ return {
7108
+ getFormulaValue,
7109
+ hasFormula,
7110
+ getFormula,
7111
+ setFormula,
7112
+ onCellChanged,
7113
+ getPrecedents,
7114
+ getDependents,
7115
+ getAuditTrail,
7116
+ enabled: true
7117
+ };
7118
+ }
7119
+ function useFormulaBar(params) {
7120
+ const { activeCol, activeRow, activeCellRef, getFormula, getRawValue, setFormula, onCellValueChanged } = params;
7121
+ const [isEditing, setIsEditing] = useState(false);
7122
+ const [editText, setEditText] = useState("");
7123
+ const isFormulaBarEditing = useRef(false);
7124
+ const displayText = useMemo(
7125
+ () => deriveFormulaBarText(activeCol, activeRow, getFormula, getRawValue),
7126
+ [activeCol, activeRow, getFormula, getRawValue]
7127
+ );
7128
+ useEffect(() => {
7129
+ setIsEditing(false);
7130
+ isFormulaBarEditing.current = false;
7131
+ }, [activeCol, activeRow]);
7132
+ const startEditing = useCallback(() => {
7133
+ setEditText(displayText);
7134
+ setIsEditing(true);
7135
+ isFormulaBarEditing.current = true;
7136
+ }, [displayText]);
7137
+ const onInputChange = useCallback((text) => {
7138
+ setEditText(text);
7139
+ }, []);
7140
+ const onCommit = useCallback(() => {
7141
+ if (activeCol == null || activeRow == null || !setFormula) return;
7142
+ processFormulaBarCommit(editText, activeCol, activeRow, setFormula, onCellValueChanged);
7143
+ setIsEditing(false);
7144
+ isFormulaBarEditing.current = false;
7145
+ }, [activeCol, activeRow, editText, setFormula, onCellValueChanged]);
7146
+ const onCancel = useCallback(() => {
7147
+ setIsEditing(false);
7148
+ isFormulaBarEditing.current = false;
7149
+ setEditText("");
7150
+ }, []);
7151
+ const currentText = isEditing ? editText : displayText;
7152
+ const referencedCells = useMemo(
7153
+ () => extractFormulaReferences(currentText),
7154
+ [currentText]
7155
+ );
7156
+ return {
7157
+ cellRef: activeCellRef,
7158
+ formulaText: isEditing ? editText : displayText,
7159
+ isEditing,
7160
+ onInputChange,
7161
+ onCommit,
7162
+ onCancel,
7163
+ startEditing,
7164
+ referencedCells,
7165
+ isFormulaBarEditing
7166
+ };
7167
+ }
7168
+ function FormulaBar({
7169
+ cellRef,
7170
+ formulaText,
7171
+ isEditing,
7172
+ onInputChange,
7173
+ onCommit,
7174
+ onCancel,
7175
+ startEditing
7176
+ }) {
7177
+ const inputRef = useRef(null);
7178
+ useEffect(() => {
7179
+ if (isEditing && inputRef.current) {
7180
+ inputRef.current.focus();
7181
+ }
7182
+ }, [isEditing]);
7183
+ return /* @__PURE__ */ jsxs("div", { style: FORMULA_BAR_STYLES.bar, role: "toolbar", "aria-label": "Formula bar", children: [
7184
+ /* @__PURE__ */ jsx("div", { style: FORMULA_BAR_STYLES.nameBox, "aria-label": "Active cell reference", children: cellRef ?? "\u2014" }),
7185
+ /* @__PURE__ */ jsx("div", { style: FORMULA_BAR_STYLES.fxLabel, "aria-hidden": "true", children: "fx" }),
7186
+ /* @__PURE__ */ jsx(
7187
+ "input",
7188
+ {
7189
+ ref: inputRef,
7190
+ type: "text",
7191
+ style: FORMULA_BAR_STYLES.input,
7192
+ value: formulaText,
7193
+ readOnly: !isEditing,
7194
+ onChange: (e) => onInputChange(e.target.value),
7195
+ onKeyDown: (e) => handleFormulaBarKeyDown(e.key, () => e.preventDefault(), onCommit, onCancel),
7196
+ onClick: () => {
7197
+ if (!isEditing) startEditing();
7198
+ },
7199
+ onDoubleClick: () => {
7200
+ if (!isEditing) startEditing();
7201
+ },
7202
+ "aria-label": "Formula input",
7203
+ spellCheck: false,
7204
+ autoComplete: "off"
7205
+ }
7206
+ )
7207
+ ] });
7208
+ }
7209
+ var barStyle = {
7210
+ display: "flex",
7211
+ alignItems: "center",
7212
+ borderTop: "1px solid var(--ogrid-border, #e0e0e0)",
7213
+ background: "var(--ogrid-header-bg, #f5f5f5)",
7214
+ minHeight: 30,
7215
+ overflowX: "auto",
7216
+ overflowY: "hidden",
7217
+ gap: 0,
7218
+ fontSize: 12
7219
+ };
7220
+ var addBtnStyle = {
7221
+ background: "none",
7222
+ border: "none",
7223
+ cursor: "pointer",
7224
+ padding: "4px 10px",
7225
+ fontSize: 16,
7226
+ lineHeight: "22px",
7227
+ color: "var(--ogrid-fg-secondary, #666)",
7228
+ flexShrink: 0
7229
+ };
7230
+ var tabBaseStyle = {
7231
+ background: "none",
7232
+ border: "none",
7233
+ borderBottom: "2px solid transparent",
7234
+ cursor: "pointer",
7235
+ padding: "4px 16px",
7236
+ fontSize: 12,
7237
+ lineHeight: "22px",
7238
+ color: "var(--ogrid-fg, #242424)",
7239
+ whiteSpace: "nowrap",
7240
+ position: "relative"
7241
+ };
7242
+ var activeTabStyle = {
7243
+ ...tabBaseStyle,
7244
+ fontWeight: 600,
7245
+ borderBottomColor: "var(--ogrid-primary, #217346)",
7246
+ background: "var(--ogrid-bg, #fff)"
7247
+ };
7248
+ function SheetTabs({
7249
+ sheets,
7250
+ activeSheet,
7251
+ onSheetChange,
7252
+ onSheetAdd
7253
+ }) {
7254
+ const handleTabClick = useCallback(
7255
+ (e) => {
7256
+ const id = e.currentTarget.dataset.sheetId;
7257
+ if (id) onSheetChange(id);
7258
+ },
7259
+ [onSheetChange]
7260
+ );
7261
+ return /* @__PURE__ */ jsxs("div", { style: barStyle, role: "tablist", "aria-label": "Sheet tabs", children: [
7262
+ onSheetAdd && /* @__PURE__ */ jsx(
7263
+ "button",
7264
+ {
7265
+ type: "button",
7266
+ style: addBtnStyle,
7267
+ onClick: onSheetAdd,
7268
+ title: "Add sheet",
7269
+ "aria-label": "Add sheet",
7270
+ children: "+"
7271
+ }
7272
+ ),
7273
+ sheets.map((sheet) => {
7274
+ const isActive = sheet.id === activeSheet;
7275
+ const base = isActive ? activeTabStyle : tabBaseStyle;
7276
+ const style = sheet.color ? { ...base, borderBottomColor: sheet.color } : base;
7277
+ return /* @__PURE__ */ jsx(
7278
+ "button",
7279
+ {
7280
+ type: "button",
7281
+ role: "tab",
7282
+ "aria-selected": isActive,
7283
+ style,
7284
+ "data-sheet-id": sheet.id,
7285
+ onClick: handleTabClick,
7286
+ children: sheet.name
7287
+ },
7288
+ sheet.id
7289
+ );
7290
+ })
7291
+ ] });
5331
7292
  }
5332
7293
  function useOGridPagination(params) {
5333
7294
  const { controlledPage, controlledPageSize, defaultPageSize, onPageChange, onPageSizeChange } = params;
@@ -5658,6 +7619,10 @@ function useOGrid(props, ref) {
5658
7619
  formulaFunctions,
5659
7620
  namedRanges,
5660
7621
  sheets,
7622
+ sheetDefs,
7623
+ activeSheet,
7624
+ onSheetChange,
7625
+ onSheetAdd,
5661
7626
  "aria-label": ariaLabel,
5662
7627
  "aria-labelledby": ariaLabelledBy
5663
7628
  } = props;
@@ -5964,22 +7929,56 @@ function useOGrid(props, ref) {
5964
7929
  filtersState.handleFilterChange,
5965
7930
  filtersState.clientFilterOptions
5966
7931
  ]);
7932
+ const [formulaVersion, setFormulaVersion] = useState(0);
7933
+ const wrappedOnFormulaRecalc = useCallback((result) => {
7934
+ setFormulaVersion((v) => v + 1);
7935
+ onFormulaRecalc?.(result);
7936
+ }, [onFormulaRecalc]);
5967
7937
  const formulaEngine = useFormulaEngine({
5968
7938
  formulas,
5969
7939
  items: dataFetchingState.displayItems,
5970
7940
  flatColumns: columns,
5971
7941
  initialFormulas,
5972
- onFormulaRecalc,
7942
+ onFormulaRecalc: wrappedOnFormulaRecalc,
5973
7943
  formulaFunctions,
5974
7944
  namedRanges,
5975
7945
  sheets
5976
7946
  });
5977
7947
  const clearAllFilters = useCallback(() => filtersState.setFilters({}), [filtersState]);
5978
7948
  const isLoadingResolved = isServerSide && dataFetchingState.serverLoading || displayLoading;
7949
+ const showRowNumbersResolved = showRowNumbers || cellReferences || formulas;
7950
+ const showColumnLettersResolved = !!(cellReferences || formulas);
7951
+ const showNameBox = !!cellReferences && !formulas;
7952
+ const showActiveCellChange = !!(cellReferences || formulas);
5979
7953
  const [activeCellRef, setActiveCellRef] = useState(null);
7954
+ const [activeCellCoords, setActiveCellCoords] = useState(null);
5980
7955
  const onActiveCellChange = useCallback((ref2) => {
5981
7956
  setActiveCellRef(ref2);
7957
+ if (ref2) {
7958
+ const m = ref2.match(/^([A-Z]+)(\d+)$/);
7959
+ if (m) {
7960
+ setActiveCellCoords({ col: columnLetterToIndex(m[1]), row: parseInt(m[2], 10) - 1 });
7961
+ } else {
7962
+ setActiveCellCoords(null);
7963
+ }
7964
+ } else {
7965
+ setActiveCellCoords(null);
7966
+ }
5982
7967
  }, []);
7968
+ const getRawValue = useCallback((col, row) => {
7969
+ const items = displayItemsRef.current;
7970
+ const cols = columnsRef.current;
7971
+ if (row < 0 || row >= items.length || col < 0 || col >= cols.length) return void 0;
7972
+ return getCellValue(items[row], cols[col]);
7973
+ }, [displayItemsRef, columnsRef]);
7974
+ const formulaBarState = useFormulaBar({
7975
+ activeCol: activeCellCoords?.col ?? null,
7976
+ activeRow: activeCellCoords?.row ?? null,
7977
+ activeCellRef,
7978
+ getFormula: formulaEngine.enabled ? formulaEngine.getFormula : void 0,
7979
+ getRawValue,
7980
+ setFormula: formulaEngine.enabled ? formulaEngine.setFormula : void 0
7981
+ });
5983
7982
  const dataGridProps = useMemo(() => ({
5984
7983
  items: dataFetchingState.displayItems,
5985
7984
  columns: columnsProp,
@@ -6004,10 +8003,10 @@ function useOGrid(props, ref) {
6004
8003
  rowSelection,
6005
8004
  selectedRows: effectiveSelectedRows,
6006
8005
  onSelectionChange: handleSelectionChange,
6007
- showRowNumbers: showRowNumbers || cellReferences,
6008
- showColumnLetters: !!cellReferences,
6009
- showNameBox: !!cellReferences,
6010
- onActiveCellChange: cellReferences ? onActiveCellChange : void 0,
8006
+ showRowNumbers: showRowNumbersResolved,
8007
+ showColumnLetters: showColumnLettersResolved,
8008
+ showNameBox,
8009
+ onActiveCellChange: showActiveCellChange ? onActiveCellChange : void 0,
6011
8010
  currentPage: paginationState.page,
6012
8011
  pageSize: paginationState.pageSize,
6013
8012
  statusBar: statusBarConfig,
@@ -6041,7 +8040,9 @@ function useOGrid(props, ref) {
6041
8040
  onFormulaCellChanged: formulaEngine.enabled ? formulaEngine.onCellChanged : void 0,
6042
8041
  getPrecedents: formulaEngine.enabled ? formulaEngine.getPrecedents : void 0,
6043
8042
  getDependents: formulaEngine.enabled ? formulaEngine.getDependents : void 0,
6044
- getAuditTrail: formulaEngine.enabled ? formulaEngine.getAuditTrail : void 0
8043
+ getAuditTrail: formulaEngine.enabled ? formulaEngine.getAuditTrail : void 0,
8044
+ formulaVersion,
8045
+ formulaReferences: formulaBarState.referencedCells.length > 0 ? formulaBarState.referencedCells : void 0
6045
8046
  }), [
6046
8047
  dataFetchingState.displayItems,
6047
8048
  columnsProp,
@@ -6066,8 +8067,10 @@ function useOGrid(props, ref) {
6066
8067
  rowSelection,
6067
8068
  effectiveSelectedRows,
6068
8069
  handleSelectionChange,
6069
- showRowNumbers,
6070
- cellReferences,
8070
+ showRowNumbersResolved,
8071
+ showColumnLettersResolved,
8072
+ showNameBox,
8073
+ showActiveCellChange,
6071
8074
  onActiveCellChange,
6072
8075
  paginationState.page,
6073
8076
  paginationState.pageSize,
@@ -6091,7 +8094,9 @@ function useOGrid(props, ref) {
6091
8094
  clearAllFilters,
6092
8095
  emptyState,
6093
8096
  formulas,
6094
- formulaEngine
8097
+ formulaEngine,
8098
+ formulaVersion,
8099
+ formulaBarState.referencedCells
6095
8100
  ]);
6096
8101
  const pagination = useMemo(() => ({
6097
8102
  page: paginationState.page,
@@ -6109,20 +8114,42 @@ function useOGrid(props, ref) {
6109
8114
  onSetVisibleColumns: setVisibleColumns,
6110
8115
  placement: columnChooserPlacement
6111
8116
  }), [columnChooserColumns, visibleColumns, handleVisibilityChange, setVisibleColumns, columnChooserPlacement]);
6112
- const showNameBox = !!cellReferences;
6113
8117
  const nameBoxEl = useMemo(() => showNameBox ? React5.createElement("div", {
6114
8118
  style: NAME_BOX_STYLE,
6115
8119
  "aria-label": "Active cell reference"
6116
8120
  }, activeCellRef ?? "\u2014") : null, [showNameBox, activeCellRef]);
6117
8121
  const resolvedToolbar = useMemo(() => showNameBox ? React5.createElement(React5.Fragment, null, nameBoxEl, toolbar) : toolbar, [showNameBox, nameBoxEl, toolbar]);
8122
+ const formulaBarEl = useMemo(() => {
8123
+ if (!formulas) return void 0;
8124
+ return React5.createElement(FormulaBar, {
8125
+ cellRef: formulaBarState.cellRef,
8126
+ formulaText: formulaBarState.formulaText,
8127
+ isEditing: formulaBarState.isEditing,
8128
+ onInputChange: formulaBarState.onInputChange,
8129
+ onCommit: formulaBarState.onCommit,
8130
+ onCancel: formulaBarState.onCancel,
8131
+ startEditing: formulaBarState.startEditing
8132
+ });
8133
+ }, [formulas, formulaBarState.cellRef, formulaBarState.formulaText, formulaBarState.isEditing, formulaBarState.onInputChange, formulaBarState.onCommit, formulaBarState.onCancel, formulaBarState.startEditing]);
8134
+ const sheetTabsEl = useMemo(() => {
8135
+ if (!sheetDefs || sheetDefs.length === 0 || !activeSheet || !onSheetChange) return void 0;
8136
+ return React5.createElement(SheetTabs, {
8137
+ sheets: sheetDefs,
8138
+ activeSheet,
8139
+ onSheetChange,
8140
+ onSheetAdd
8141
+ });
8142
+ }, [sheetDefs, activeSheet, onSheetChange, onSheetAdd]);
6118
8143
  const layout = useMemo(() => ({
6119
8144
  toolbar: resolvedToolbar,
6120
8145
  toolbarBelow,
6121
8146
  className,
6122
8147
  emptyState,
6123
8148
  sideBarProps,
6124
- fullScreen
6125
- }), [resolvedToolbar, toolbarBelow, className, emptyState, sideBarProps, fullScreen]);
8149
+ fullScreen,
8150
+ formulaBar: formulaBarEl,
8151
+ sheetTabs: sheetTabsEl
8152
+ }), [resolvedToolbar, toolbarBelow, className, emptyState, sideBarProps, fullScreen, formulaBarEl, sheetTabsEl]);
6126
8153
  const filtersResult = useMemo(() => ({
6127
8154
  hasActiveFilters: filtersState.hasActiveFilters,
6128
8155
  setFilters: filtersState.setFilters
@@ -6779,6 +8806,7 @@ function useKeyboardNavigation(params) {
6779
8806
  case "ArrowUp":
6780
8807
  case "ArrowRight":
6781
8808
  case "ArrowLeft": {
8809
+ if (editingCell != null) break;
6782
8810
  e.preventDefault();
6783
8811
  const { newRowIndex, newColumnIndex, newRange } = computeArrowNavigation({
6784
8812
  direction: e.key,
@@ -7109,6 +9137,9 @@ function useFillHandle(params) {
7109
9137
  const rafRef = useRef(0);
7110
9138
  const liveFillRangeRef = useRef(null);
7111
9139
  const colOffsetRef = useLatestRef(colOffset);
9140
+ const itemsRef = useLatestRef(items);
9141
+ const visibleColsRef = useLatestRef(visibleCols);
9142
+ const formulaOptionsRef = useLatestRef(formulaOptions);
7112
9143
  useEffect(() => {
7113
9144
  if (!fillDrag || editable === false || !onCellValueChangedRef.current || !wrapperRef.current) return;
7114
9145
  fillDragEndRef.current = { endRow: fillDrag.startRow, endCol: fillDrag.startCol };
@@ -7207,7 +9238,7 @@ function useFillHandle(params) {
7207
9238
  });
7208
9239
  setSelectionRange(norm);
7209
9240
  setActiveCell({ rowIndex: fillDrag.startRow, columnIndex: fillDrag.startCol + colOffsetRef.current });
7210
- const fillEvents = applyFillValues(norm, fillDrag.startRow, fillDrag.startCol, items, visibleCols, formulaOptions);
9241
+ const fillEvents = applyFillValues(norm, fillDrag.startRow, fillDrag.startCol, items, visibleCols, formulaOptionsRef.current);
7211
9242
  if (fillEvents.length > 0) {
7212
9243
  beginBatch?.();
7213
9244
  for (const evt of fillEvents) onCellValueChangedRef.current?.(evt);
@@ -7234,7 +9265,8 @@ function useFillHandle(params) {
7234
9265
  endBatch,
7235
9266
  colOffsetRef,
7236
9267
  wrapperRef,
7237
- onCellValueChangedRef
9268
+ onCellValueChangedRef,
9269
+ formulaOptionsRef
7238
9270
  ]);
7239
9271
  const selectionRangeRef = useRef(selectionRange);
7240
9272
  selectionRangeRef.current = selectionRange;
@@ -7251,8 +9283,6 @@ function useFillHandle(params) {
7251
9283
  },
7252
9284
  []
7253
9285
  );
7254
- const itemsRef = useLatestRef(items);
7255
- const visibleColsRef = useLatestRef(visibleCols);
7256
9286
  const fillDown = useCallback(() => {
7257
9287
  const range = selectionRangeRef.current;
7258
9288
  if (!range || editable === false || !onCellValueChangedRef.current) return;
@@ -7263,14 +9293,14 @@ function useFillHandle(params) {
7263
9293
  norm.startCol,
7264
9294
  itemsRef.current,
7265
9295
  visibleColsRef.current,
7266
- formulaOptions
9296
+ formulaOptionsRef.current
7267
9297
  );
7268
9298
  if (fillEvents.length > 0) {
7269
9299
  beginBatch?.();
7270
9300
  for (const evt of fillEvents) onCellValueChangedRef.current(evt);
7271
9301
  endBatch?.();
7272
9302
  }
7273
- }, [editable, beginBatch, endBatch, onCellValueChangedRef, itemsRef, visibleColsRef]);
9303
+ }, [editable, beginBatch, endBatch, onCellValueChangedRef, itemsRef, visibleColsRef, formulaOptionsRef]);
7274
9304
  return { fillDrag, setFillDrag, handleFillHandleMouseDown, fillDown };
7275
9305
  }
7276
9306
  function useTableLayout(params) {
@@ -7518,6 +9548,27 @@ function useColumnHeaderMenuState(params) {
7518
9548
  isResizable
7519
9549
  };
7520
9550
  }
9551
+ function getColumnHeaderMenuProps(headerMenu) {
9552
+ return {
9553
+ isOpen: headerMenu.isOpen,
9554
+ anchorElement: headerMenu.anchorElement,
9555
+ onClose: headerMenu.close,
9556
+ onPinLeft: headerMenu.handlePinLeft,
9557
+ onPinRight: headerMenu.handlePinRight,
9558
+ onUnpin: headerMenu.handleUnpin,
9559
+ onSortAsc: headerMenu.handleSortAsc,
9560
+ onSortDesc: headerMenu.handleSortDesc,
9561
+ onClearSort: headerMenu.handleClearSort,
9562
+ onAutosizeThis: headerMenu.handleAutosizeThis,
9563
+ onAutosizeAll: headerMenu.handleAutosizeAll,
9564
+ canPinLeft: headerMenu.canPinLeft,
9565
+ canPinRight: headerMenu.canPinRight,
9566
+ canUnpin: headerMenu.canUnpin,
9567
+ currentSort: headerMenu.currentSort,
9568
+ isSortable: headerMenu.isSortable,
9569
+ isResizable: headerMenu.isResizable
9570
+ };
9571
+ }
7521
9572
 
7522
9573
  // src/hooks/useDataGridLayout.ts
7523
9574
  function useDataGridLayout(params) {
@@ -8176,7 +10227,10 @@ function useDataGridState(params) {
8176
10227
  getRowId,
8177
10228
  editable,
8178
10229
  onCellValueChanged,
8179
- isDragging: cellSelection ? isDragging : false
10230
+ isDragging: cellSelection ? isDragging : false,
10231
+ getFormulaValue: props.getFormulaValue,
10232
+ hasFormula: props.hasFormula,
10233
+ formulaVersion: props.formulaVersion
8180
10234
  }),
8181
10235
  [
8182
10236
  editingCell,
@@ -8190,7 +10244,10 @@ function useDataGridState(params) {
8190
10244
  editable,
8191
10245
  onCellValueChanged,
8192
10246
  cellSelection,
8193
- isDragging
10247
+ isDragging,
10248
+ props.getFormulaValue,
10249
+ props.hasFormula,
10250
+ props.formulaVersion
8194
10251
  ]
8195
10252
  );
8196
10253
  const aggregation = useMemo(
@@ -8270,8 +10327,9 @@ function useMultiSelectFilterState(params) {
8270
10327
  }
8271
10328
  }, [isFilterOpen, safeSelectedValues]);
8272
10329
  const filteredOptions = useMemo(() => {
8273
- if (!debouncedSearchText.trim()) return safeOptions;
8274
- const searchLower = debouncedSearchText.toLowerCase().trim();
10330
+ const trimmed = debouncedSearchText.trim();
10331
+ if (!trimmed) return safeOptions;
10332
+ const searchLower = trimmed.toLowerCase();
8275
10333
  return safeOptions.filter((opt) => opt.toLowerCase().includes(searchLower));
8276
10334
  }, [safeOptions, debouncedSearchText]);
8277
10335
  const handleCheckboxChange = useCallback((option, checked) => {
@@ -8283,7 +10341,11 @@ function useMultiSelectFilterState(params) {
8283
10341
  });
8284
10342
  }, []);
8285
10343
  const handleSelectAll = useCallback(() => {
8286
- setTempSelected((prev) => /* @__PURE__ */ new Set([...prev, ...filteredOptions]));
10344
+ setTempSelected((prev) => {
10345
+ const next = new Set(prev);
10346
+ for (const opt of filteredOptions) next.add(opt);
10347
+ return next;
10348
+ });
8287
10349
  }, [filteredOptions]);
8288
10350
  const handleClearSelection = useCallback(() => setTempSelected(/* @__PURE__ */ new Set()), []);
8289
10351
  const handleApplyMultiSelect = useCallback(() => {
@@ -8638,9 +10700,14 @@ function useColumnChooserState(params) {
8638
10700
  }
8639
10701
  function useInlineCellEditorState(params) {
8640
10702
  const { value, editorType, onCommit, onCancel } = params;
8641
- const [localValue, setLocalValue] = useState(
8642
- value !== null && value !== void 0 ? String(value) : ""
8643
- );
10703
+ const [localValue, setLocalValue] = useState(() => {
10704
+ if (value === null || value === void 0) return "";
10705
+ if (editorType === "date") {
10706
+ const str = String(value);
10707
+ return str.match(/^\d{4}-\d{2}-\d{2}/) ? str.substring(0, 10) : str;
10708
+ }
10709
+ return String(value);
10710
+ });
8644
10711
  const handleKeyDown = useCallback(
8645
10712
  (e) => {
8646
10713
  if (e.key === "Escape") {
@@ -8729,9 +10796,10 @@ function useColumnResize({
8729
10796
  [columnId]: { widthPx: latestWidth }
8730
10797
  }));
8731
10798
  };
10799
+ const effectiveMinWidth = columnId === ROW_NUMBER_COLUMN_ID ? ROW_NUMBER_COLUMN_MIN_WIDTH : minWidth;
8732
10800
  const onMove = (moveEvent) => {
8733
10801
  const deltaX = moveEvent.clientX - startX;
8734
- latestWidth = Math.max(minWidth, startWidth + deltaX);
10802
+ latestWidth = Math.max(effectiveMinWidth, startWidth + deltaX);
8735
10803
  if (!rafRef.current) {
8736
10804
  rafRef.current = requestAnimationFrame(() => {
8737
10805
  rafRef.current = 0;
@@ -8792,9 +10860,14 @@ function getSelectDisplayText(value, formatValue) {
8792
10860
  return value != null ? String(value) : "";
8793
10861
  }
8794
10862
  function useRichSelectState(params) {
8795
- const { values, formatValue, onCommit, onCancel } = params;
10863
+ const { values, formatValue, initialValue, onCommit, onCancel } = params;
8796
10864
  const [searchText, setSearchText] = useState("");
8797
- const [highlightedIndex, setHighlightedIndex] = useState(0);
10865
+ const initialIndex = values.findIndex((v) => String(v) === String(initialValue));
10866
+ const [highlightedIndex, setHighlightedIndex] = useState(Math.max(initialIndex, 0));
10867
+ useEffect(() => {
10868
+ const idx = values.findIndex((v) => String(v) === String(initialValue));
10869
+ setHighlightedIndex(Math.max(idx, 0));
10870
+ }, [initialValue, values]);
8798
10871
  const getDisplayText = useCallback(
8799
10872
  (value) => getSelectDisplayText(value, formatValue),
8800
10873
  [formatValue]
@@ -8856,6 +10929,10 @@ function useSelectState(params) {
8856
10929
  );
8857
10930
  const initialIndex = values.findIndex((v) => String(v) === String(initialValue));
8858
10931
  const [highlightedIndex, setHighlightedIndex] = useState(Math.max(initialIndex, 0));
10932
+ useEffect(() => {
10933
+ const idx = values.findIndex((v) => String(v) === String(initialValue));
10934
+ setHighlightedIndex(Math.max(idx, 0));
10935
+ }, [initialValue, values]);
8859
10936
  useEffect(() => {
8860
10937
  const dropdown = dropdownRef.current;
8861
10938
  if (!dropdown) return;
@@ -10169,11 +12246,11 @@ function useDataGridTableOrchestration(params) {
10169
12246
  if (!onActiveCellChangeRef.current) return;
10170
12247
  const ac = interaction.activeCell;
10171
12248
  if (ac) {
10172
- onActiveCellChangeRef.current(formatCellReference(ac.columnIndex, rowNumberOffset + ac.rowIndex + 1));
12249
+ onActiveCellChangeRef.current(formatCellReference(ac.columnIndex - colOffset, rowNumberOffset + ac.rowIndex + 1));
10173
12250
  } else {
10174
12251
  onActiveCellChangeRef.current(null);
10175
12252
  }
10176
- }, [interaction.activeCell, rowNumberOffset]);
12253
+ }, [interaction.activeCell, rowNumberOffset, colOffset]);
10177
12254
  const { handleResizeStart, handleResizeDoubleClick, getColumnWidth } = useColumnResize({
10178
12255
  columnSizingOverrides,
10179
12256
  setColumnSizingOverrides
@@ -10784,7 +12861,9 @@ function OGridLayout(props) {
10784
12861
  children,
10785
12862
  pagination,
10786
12863
  sideBar,
10787
- fullScreen
12864
+ fullScreen,
12865
+ formulaBar,
12866
+ sheetTabs
10788
12867
  } = props;
10789
12868
  const [isFullScreen, setIsFullScreen] = useState(false);
10790
12869
  const toggleFullScreen = useCallback(() => {
@@ -10827,11 +12906,13 @@ function OGridLayout(props) {
10827
12906
  ] })
10828
12907
  ] }),
10829
12908
  toolbarBelow && /* @__PURE__ */ jsx("div", { style: toolbarBelowStyle, children: toolbarBelow }),
12909
+ formulaBar,
10830
12910
  hasSideBar ? /* @__PURE__ */ jsxs("div", { style: gridAreaFlexStyle, children: [
10831
12911
  sideBarPosition === "left" && /* @__PURE__ */ jsx(SideBar, { ...sideBar }),
10832
12912
  /* @__PURE__ */ jsx("div", { style: gridChildStyle, children }),
10833
12913
  sideBarPosition !== "left" && /* @__PURE__ */ jsx(SideBar, { ...sideBar })
10834
12914
  ] }) : /* @__PURE__ */ jsx("div", { style: gridAreaSoloStyle, children }),
12915
+ sheetTabs,
10835
12916
  pagination && /* @__PURE__ */ jsx("div", { style: footerStripStyle, children: pagination })
10836
12917
  ] })
10837
12918
  }
@@ -10929,6 +13010,7 @@ function BaseInlineCellEditor(props) {
10929
13010
  const richSelect = useRichSelectState({
10930
13011
  values: editorValues,
10931
13012
  formatValue: editorFormatValue,
13013
+ initialValue: value,
10932
13014
  onCommit,
10933
13015
  onCancel
10934
13016
  });
@@ -11120,6 +13202,8 @@ function GridContextMenu(props) {
11120
13202
  );
11121
13203
  }
11122
13204
  var MARCHING_ANTS_ANIMATION = { animation: "ogrid-marching-ants 0.5s linear infinite" };
13205
+ var CRISP_EDGES = { shapeRendering: "crispEdges" };
13206
+ var MARCHING_ANTS_RECT_STYLE = { ...MARCHING_ANTS_ANIMATION, shapeRendering: "crispEdges" };
11123
13207
  function MarchingAntsOverlay({
11124
13208
  containerRef,
11125
13209
  selectionRange,
@@ -11202,7 +13286,7 @@ function MarchingAntsOverlay({
11202
13286
  fill: "none",
11203
13287
  stroke: "var(--ogrid-selection, #217346)",
11204
13288
  strokeWidth: "2",
11205
- style: { shapeRendering: "crispEdges" }
13289
+ style: CRISP_EDGES
11206
13290
  }
11207
13291
  )
11208
13292
  }
@@ -11232,13 +13316,97 @@ function MarchingAntsOverlay({
11232
13316
  stroke: "var(--ogrid-selection, #217346)",
11233
13317
  strokeWidth: "2",
11234
13318
  strokeDasharray: "4 4",
11235
- style: { ...MARCHING_ANTS_ANIMATION, shapeRendering: "crispEdges" }
13319
+ style: MARCHING_ANTS_RECT_STYLE
11236
13320
  }
11237
13321
  )
11238
13322
  }
11239
13323
  )
11240
13324
  ] });
11241
13325
  }
13326
+ var CRISP_EDGES2 = { shapeRendering: "crispEdges" };
13327
+ function measureRef(container, ref, colOffset) {
13328
+ const startCol = ref.col + colOffset;
13329
+ const endCol = (ref.endCol ?? ref.col) + colOffset;
13330
+ const endRow = ref.endRow ?? ref.row;
13331
+ const tl = container.querySelector(
13332
+ `[data-row-index="${ref.row}"][data-col-index="${startCol}"]`
13333
+ );
13334
+ const br = container.querySelector(
13335
+ `[data-row-index="${endRow}"][data-col-index="${endCol}"]`
13336
+ );
13337
+ if (!tl || !br) return null;
13338
+ const cRect = container.getBoundingClientRect();
13339
+ const tlRect = tl.getBoundingClientRect();
13340
+ const brRect = br.getBoundingClientRect();
13341
+ return {
13342
+ top: Math.round(tlRect.top - cRect.top),
13343
+ left: Math.round(tlRect.left - cRect.left),
13344
+ width: Math.round(brRect.right - tlRect.left),
13345
+ height: Math.round(brRect.bottom - tlRect.top),
13346
+ color: FORMULA_REF_COLORS[ref.colorIndex % FORMULA_REF_COLORS.length]
13347
+ };
13348
+ }
13349
+ function FormulaRefOverlayInner({
13350
+ containerRef,
13351
+ references,
13352
+ colOffset
13353
+ }) {
13354
+ const [rects, setRects] = useState([]);
13355
+ const rafRef = useRef(0);
13356
+ const measureAll = useCallback(() => {
13357
+ const container = containerRef.current;
13358
+ if (!container || references.length === 0) {
13359
+ setRects([]);
13360
+ return;
13361
+ }
13362
+ const measured = [];
13363
+ for (const ref of references) {
13364
+ const r = measureRef(container, ref, colOffset);
13365
+ if (r) measured.push(r);
13366
+ }
13367
+ setRects(measured);
13368
+ }, [references, containerRef, colOffset]);
13369
+ useEffect(() => {
13370
+ if (references.length === 0) {
13371
+ setRects([]);
13372
+ return;
13373
+ }
13374
+ rafRef.current = requestAnimationFrame(measureAll);
13375
+ return () => cancelAnimationFrame(rafRef.current);
13376
+ }, [references, measureAll]);
13377
+ if (rects.length === 0) return null;
13378
+ return /* @__PURE__ */ jsx(Fragment, { children: rects.map((r, i) => /* @__PURE__ */ jsx(
13379
+ "svg",
13380
+ {
13381
+ style: {
13382
+ position: "absolute",
13383
+ top: r.top,
13384
+ left: r.left,
13385
+ width: r.width,
13386
+ height: r.height,
13387
+ pointerEvents: "none",
13388
+ zIndex: 3,
13389
+ overflow: "visible"
13390
+ },
13391
+ "aria-hidden": "true",
13392
+ children: /* @__PURE__ */ jsx(
13393
+ "rect",
13394
+ {
13395
+ x: "1",
13396
+ y: "1",
13397
+ width: Math.max(0, r.width - 2),
13398
+ height: Math.max(0, r.height - 2),
13399
+ fill: "none",
13400
+ stroke: r.color,
13401
+ strokeWidth: "2",
13402
+ style: CRISP_EDGES2
13403
+ }
13404
+ )
13405
+ },
13406
+ i
13407
+ )) });
13408
+ }
13409
+ var FormulaRefOverlay = memo$1(FormulaRefOverlayInner);
11242
13410
  function BaseColumnHeaderMenu(props) {
11243
13411
  const {
11244
13412
  isOpen,
@@ -11351,6 +13519,155 @@ function BaseColumnHeaderMenu(props) {
11351
13519
  portalTarget
11352
13520
  );
11353
13521
  }
13522
+ var PaginationControlsBase = React5.memo((props) => {
13523
+ const {
13524
+ currentPage,
13525
+ pageSize,
13526
+ totalCount,
13527
+ onPageChange,
13528
+ onPageSizeChange,
13529
+ pageSizeOptions,
13530
+ entityLabelPlural,
13531
+ className,
13532
+ slots,
13533
+ classNames: cn = {}
13534
+ } = props;
13535
+ const { labelPlural, vm, handlePageSizeChange } = usePaginationControls({
13536
+ currentPage,
13537
+ pageSize,
13538
+ totalCount,
13539
+ onPageSizeChange,
13540
+ pageSizeOptions,
13541
+ entityLabelPlural
13542
+ });
13543
+ if (!vm) return null;
13544
+ const { pageNumbers, showStartEllipsis, showEndEllipsis, totalPages, startItem, endItem } = vm;
13545
+ const {
13546
+ NavButton,
13547
+ PageButton,
13548
+ PageSizeSelect,
13549
+ OuterContainer,
13550
+ InfoText,
13551
+ NavButtonsContainer,
13552
+ PageSizeContainer,
13553
+ PageSizeLabel,
13554
+ Ellipsis
13555
+ } = slots;
13556
+ const infoContent = `Showing ${startItem} to ${endItem} of ${totalCount.toLocaleString()} ${labelPlural}`;
13557
+ const infoNode = InfoText ? /* @__PURE__ */ jsx(InfoText, { children: infoContent }) : /* @__PURE__ */ jsx("div", { className: cn.paginationInfo, children: infoContent });
13558
+ const ellipsisNode = Ellipsis ? /* @__PURE__ */ jsx(Ellipsis, {}) : /* @__PURE__ */ jsx("span", { className: cn.ellipsis, "aria-hidden": true, children: "\u2026" });
13559
+ const pageSizeLabelNode = PageSizeLabel ? /* @__PURE__ */ jsx(PageSizeLabel, {}) : /* @__PURE__ */ jsx("span", { className: cn.pageSizeLabel, children: "Rows" });
13560
+ const pageNumbersNode = /* @__PURE__ */ jsxs("div", { className: cn.pageNumbers, children: [
13561
+ showStartEllipsis && /* @__PURE__ */ jsxs(Fragment, { children: [
13562
+ /* @__PURE__ */ jsx(PageButton, { onClick: () => onPageChange(1), active: false, "aria-label": "Page 1", className: cn.pageBtn, children: "1" }),
13563
+ ellipsisNode
13564
+ ] }),
13565
+ pageNumbers.map((pageNum) => /* @__PURE__ */ jsx(
13566
+ PageButton,
13567
+ {
13568
+ onClick: () => onPageChange(pageNum),
13569
+ active: currentPage === pageNum,
13570
+ "aria-label": `Page ${pageNum}`,
13571
+ "aria-current": currentPage === pageNum ? "page" : void 0,
13572
+ className: cn.pageBtn,
13573
+ children: pageNum
13574
+ },
13575
+ pageNum
13576
+ )),
13577
+ showEndEllipsis && /* @__PURE__ */ jsxs(Fragment, { children: [
13578
+ ellipsisNode,
13579
+ /* @__PURE__ */ jsx(PageButton, { onClick: () => onPageChange(totalPages), active: false, "aria-label": `Page ${totalPages}`, className: cn.pageBtn, children: totalPages })
13580
+ ] })
13581
+ ] });
13582
+ const navContent = /* @__PURE__ */ jsxs(Fragment, { children: [
13583
+ /* @__PURE__ */ jsx(NavButton, { variant: "first", onClick: () => onPageChange(1), disabled: currentPage === 1, "aria-label": "First page", className: cn.navBtn }),
13584
+ /* @__PURE__ */ jsx(NavButton, { variant: "prev", onClick: () => onPageChange(currentPage - 1), disabled: currentPage === 1, "aria-label": "Previous page", className: cn.navBtn }),
13585
+ pageNumbersNode,
13586
+ /* @__PURE__ */ jsx(NavButton, { variant: "next", onClick: () => onPageChange(currentPage + 1), disabled: currentPage >= totalPages, "aria-label": "Next page", className: cn.navBtn }),
13587
+ /* @__PURE__ */ jsx(NavButton, { variant: "last", onClick: () => onPageChange(totalPages), disabled: currentPage >= totalPages, "aria-label": "Last page", className: cn.navBtn })
13588
+ ] });
13589
+ const navNode = NavButtonsContainer ? /* @__PURE__ */ jsx(NavButtonsContainer, { className: cn.paginationControls, children: navContent }) : /* @__PURE__ */ jsx("div", { className: cn.paginationControls, children: navContent });
13590
+ const pageSizeContent = /* @__PURE__ */ jsxs(Fragment, { children: [
13591
+ pageSizeLabelNode,
13592
+ /* @__PURE__ */ jsx(
13593
+ PageSizeSelect,
13594
+ {
13595
+ value: pageSize,
13596
+ options: vm.pageSizeOptions,
13597
+ onChange: handlePageSizeChange,
13598
+ "aria-label": "Rows per page",
13599
+ className: cn.pageSizeSelect
13600
+ }
13601
+ )
13602
+ ] });
13603
+ const pageSizeNode = PageSizeContainer ? /* @__PURE__ */ jsx(PageSizeContainer, { className: cn.pageSizeSelector, children: pageSizeContent }) : /* @__PURE__ */ jsx("div", { className: cn.pageSizeSelector, children: pageSizeContent });
13604
+ const outerClass = `${cn.pagination ?? ""} ${className || ""}`.trim();
13605
+ const outerRole = "navigation";
13606
+ const outerLabel = "Pagination";
13607
+ if (OuterContainer) {
13608
+ return /* @__PURE__ */ jsxs(OuterContainer, { className: outerClass, role: outerRole, "aria-label": outerLabel, children: [
13609
+ infoNode,
13610
+ navNode,
13611
+ pageSizeNode
13612
+ ] });
13613
+ }
13614
+ return /* @__PURE__ */ jsxs("div", { className: outerClass, role: outerRole, "aria-label": outerLabel, children: [
13615
+ infoNode,
13616
+ navNode,
13617
+ pageSizeNode
13618
+ ] });
13619
+ });
13620
+ PaginationControlsBase.displayName = "PaginationControlsBase";
13621
+ var ColumnChooserContent = ({
13622
+ columns,
13623
+ visibleColumns,
13624
+ visibleCount,
13625
+ totalCount,
13626
+ handleSelectAll,
13627
+ handleClearAll,
13628
+ handleCheckboxChange,
13629
+ CheckboxItem,
13630
+ classNames: cn = {},
13631
+ Header,
13632
+ OptionsListContainer,
13633
+ OptionItemContainer,
13634
+ Actions
13635
+ }) => {
13636
+ const headerNode = Header ? /* @__PURE__ */ jsx(Header, { visibleCount, totalCount }) : /* @__PURE__ */ jsxs("div", { className: cn.header, children: [
13637
+ "Select Columns (",
13638
+ visibleCount,
13639
+ " of ",
13640
+ totalCount,
13641
+ ")"
13642
+ ] });
13643
+ const optionItems = columns.map((column) => {
13644
+ const checkboxItem = /* @__PURE__ */ jsx(
13645
+ CheckboxItem,
13646
+ {
13647
+ columnId: column.columnId,
13648
+ columnName: column.name,
13649
+ checked: visibleColumns.has(column.columnId),
13650
+ disabled: column.required === true,
13651
+ onChange: handleCheckboxChange(column.columnId)
13652
+ }
13653
+ );
13654
+ if (OptionItemContainer) {
13655
+ return /* @__PURE__ */ jsx(OptionItemContainer, { columnId: column.columnId, children: checkboxItem }, column.columnId);
13656
+ }
13657
+ return /* @__PURE__ */ jsx("div", { className: cn.optionItem, children: checkboxItem }, column.columnId);
13658
+ });
13659
+ const listNode = OptionsListContainer ? /* @__PURE__ */ jsx(OptionsListContainer, { children: optionItems }) : /* @__PURE__ */ jsx("div", { className: cn.optionsList, children: optionItems });
13660
+ const actionsNode = Actions ? /* @__PURE__ */ jsx(Actions, { onClearAll: handleClearAll, onSelectAll: handleSelectAll }) : /* @__PURE__ */ jsxs("div", { className: cn.actions, children: [
13661
+ /* @__PURE__ */ jsx("button", { type: "button", className: cn.clearButton, onClick: handleClearAll, children: "Clear All" }),
13662
+ /* @__PURE__ */ jsx("button", { type: "button", className: cn.selectAllButton, onClick: handleSelectAll, children: "Select All" })
13663
+ ] });
13664
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
13665
+ headerNode,
13666
+ listNode,
13667
+ actionsNode
13668
+ ] });
13669
+ };
13670
+ ColumnChooserContent.displayName = "ColumnChooserContent";
11354
13671
  function createOGrid(components) {
11355
13672
  const {
11356
13673
  DataGridTable,
@@ -11371,6 +13688,8 @@ function createOGrid(components) {
11371
13688
  toolbar: layout.toolbar,
11372
13689
  toolbarBelow: layout.toolbarBelow,
11373
13690
  fullScreen: layout.fullScreen,
13691
+ formulaBar: layout.formulaBar,
13692
+ sheetTabs: layout.sheetTabs,
11374
13693
  toolbarEnd: columnChooser.placement === "toolbar" ? /* @__PURE__ */ jsx(
11375
13694
  ColumnChooser,
11376
13695
  {
@@ -11477,6 +13796,9 @@ function BaseDropIndicator({ dropIndicatorX, wrapperLeft, className }) {
11477
13796
  }
11478
13797
  );
11479
13798
  }
13799
+ var dateContainerStyle2 = { padding: "8px 12px", display: "flex", flexDirection: "column", gap: 6 };
13800
+ var dateLabelStyle2 = { display: "flex", alignItems: "center", gap: 6, fontSize: 12 };
13801
+ var dateInputFlexStyle = { flex: 1 };
11480
13802
  var DateFilterContent = ({
11481
13803
  tempDateFrom,
11482
13804
  setTempDateFrom,
@@ -11486,14 +13808,14 @@ var DateFilterContent = ({
11486
13808
  onClear,
11487
13809
  classNames
11488
13810
  }) => /* @__PURE__ */ jsxs(Fragment, { children: [
11489
- /* @__PURE__ */ jsxs("div", { style: { padding: "8px 12px", display: "flex", flexDirection: "column", gap: 6 }, children: [
11490
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 12 }, children: [
13811
+ /* @__PURE__ */ jsxs("div", { style: dateContainerStyle2, children: [
13812
+ /* @__PURE__ */ jsxs("label", { style: dateLabelStyle2, children: [
11491
13813
  "From:",
11492
- /* @__PURE__ */ jsx("input", { type: "date", value: tempDateFrom, onChange: (e) => setTempDateFrom(e.target.value), style: { flex: 1 } })
13814
+ /* @__PURE__ */ jsx("input", { type: "date", value: tempDateFrom, onChange: (e) => setTempDateFrom(e.target.value), style: dateInputFlexStyle })
11493
13815
  ] }),
11494
- /* @__PURE__ */ jsxs("label", { style: { display: "flex", alignItems: "center", gap: 6, fontSize: 12 }, children: [
13816
+ /* @__PURE__ */ jsxs("label", { style: dateLabelStyle2, children: [
11495
13817
  "To:",
11496
- /* @__PURE__ */ jsx("input", { type: "date", value: tempDateTo, onChange: (e) => setTempDateTo(e.target.value), style: { flex: 1 } })
13818
+ /* @__PURE__ */ jsx("input", { type: "date", value: tempDateTo, onChange: (e) => setTempDateTo(e.target.value), style: dateInputFlexStyle })
11497
13819
  ] })
11498
13820
  ] }),
11499
13821
  /* @__PURE__ */ jsxs("div", { className: classNames?.popoverActions, children: [
@@ -11530,8 +13852,6 @@ function getDateFilterContentProps(state, classNames) {
11530
13852
  classNames
11531
13853
  };
11532
13854
  }
11533
-
11534
- // src/components/ColumnHeaderFilterRenderers.tsx
11535
13855
  function renderFilterContent(filterType, state, options, isLoadingOptions, selectedUser, renderers) {
11536
13856
  if (filterType === "multiSelect") {
11537
13857
  return renderers.renderMultiSelect({
@@ -11579,5 +13899,13 @@ function renderFilterContent(filterType, state, options, isLoadingOptions, selec
11579
13899
  }
11580
13900
  return null;
11581
13901
  }
13902
+ function createBaseFilterRenderers(components, dateClassNames) {
13903
+ return {
13904
+ renderMultiSelect: (p) => /* @__PURE__ */ jsx(components.MultiSelectFilterPopover, { ...p }),
13905
+ renderText: (p) => /* @__PURE__ */ jsx(components.TextFilterPopover, { ...p }),
13906
+ renderPeople: (p) => /* @__PURE__ */ jsx(components.PeopleFilterPopover, { ...p }),
13907
+ renderDate: (p) => /* @__PURE__ */ jsx(DateFilterContent, { ...p, classNames: dateClassNames })
13908
+ };
13909
+ }
11582
13910
 
11583
- export { BaseColumnHeaderMenu, BaseDropIndicator, BaseEmptyState, BaseInlineCellEditor, BaseLoadingOverlay, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CURSOR_CELL_STYLE, CellDescriptorCache, CellErrorBoundary, DEFAULT_MIN_COLUMN_WIDTH, DateFilterContent, EmptyState, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, GRID_ROOT_STYLE, GridContextMenu, MAX_PAGE_BUTTONS, MarchingAntsOverlay, NOOP3 as NOOP, OGridLayout, PAGE_SIZE_OPTIONS, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, ROW_NUMBER_COLUMN_WIDTH, STOP_PROPAGATION, SideBar, StatusBar, UndoRedoStack, areGridRowPropsEqual, booleanParser, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps2 as buildInlineEditorProps, buildPopoverEditorProps2 as buildPopoverEditorProps, clampSelectionToBounds, computeAggregations, computeAutoScrollSpeed, computeTabNavigation, createOGrid, currencyParser, dateParser, deriveFilterOptionsFromData, editorInputStyle, editorWrapperStyle, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellReference, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellInteractionProps, getCellRenderDescriptor, getCellValue, getColumnHeaderFilterStateParams, getColumnHeaderMenuItems, getContextMenuHandlers, getDataGridStatusBarConfig, getDateFilterContentProps, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getStatusBarParts, indexToColumnLetter, isInSelectionRange, isRowInRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, partitionColumnsForVirtualization, processClientSideData, rangesEqual, renderFilterContent, resolveCellDisplayContent2 as resolveCellDisplayContent, resolveCellStyle2 as resolveCellStyle, richSelectDropdownStyle, richSelectNoMatchesStyle, richSelectOptionHighlightedStyle, richSelectOptionStyle, richSelectWrapperStyle, selectChevronStyle, selectDisplayStyle, selectEditorStyle, toUserLike, triggerCsvDownload, useActiveCell, useCellEditing, useCellSelection, useClipboard, useColumnChooserState, useColumnHeaderFilterState, useColumnMeta, useColumnReorder, useColumnResize, useContextMenu, useDataGridState, useDataGridTableOrchestration, useDateFilterState, useDebounce, useFillHandle, useFilterOptions, useFormulaEngine, useInlineCellEditorState, useKeyboardNavigation, useLatestRef, useListVirtualizer, useMultiSelectFilterState, useOGrid, usePaginationControls, usePeopleFilterState, useRichSelectState, useRowSelection, useSelectState, useSideBarState, useTableLayout, useTextFilterState, useUndoRedo, useVirtualScroll };
13911
+ export { BaseColumnHeaderMenu, BaseDropIndicator, BaseEmptyState, BaseInlineCellEditor, BaseLoadingOverlay, CELL_PADDING, CHECKBOX_COLUMN_WIDTH, COLUMN_HEADER_MENU_ITEMS, CURSOR_CELL_STYLE, CellDescriptorCache, CellErrorBoundary, ColumnChooserContent, DEFAULT_MIN_COLUMN_WIDTH, DateFilterContent, EmptyState, FormulaBar, FormulaRefOverlay, GRID_BORDER_RADIUS, GRID_CONTEXT_MENU_ITEMS, GRID_ROOT_STYLE, GridContextMenu, MAX_PAGE_BUTTONS, MarchingAntsOverlay, NOOP3 as NOOP, OGridLayout, PAGE_SIZE_OPTIONS, POPOVER_ANCHOR_STYLE, PREVENT_DEFAULT, PaginationControlsBase, ROW_NUMBER_COLUMN_ID, ROW_NUMBER_COLUMN_MIN_WIDTH, ROW_NUMBER_COLUMN_WIDTH, STOP_PROPAGATION, SheetTabs, SideBar, StatusBar, UndoRedoStack, areGridRowPropsEqual, booleanParser, buildCsvHeader, buildCsvRows, buildHeaderRows, buildInlineEditorProps2 as buildInlineEditorProps, buildPopoverEditorProps2 as buildPopoverEditorProps, clampSelectionToBounds, computeAggregations, computeAutoScrollSpeed, computeTabNavigation, createBaseFilterRenderers, createOGrid, currencyParser, dateParser, deriveFilterOptionsFromData, editorInputStyle, editorWrapperStyle, emailParser, escapeCsvValue, exportToCsv, findCtrlArrowTarget, flattenColumns, formatCellReference, formatCellValueForTsv, formatSelectionAsTsv, formatShortcut, getCellInteractionProps, getCellRenderDescriptor, getCellValue, getColumnHeaderFilterStateParams, getColumnHeaderMenuItems, getColumnHeaderMenuProps, getContextMenuHandlers, getDataGridStatusBarConfig, getDateFilterContentProps, getFilterField, getHeaderFilterConfig, getMultiSelectFilterFields, getPaginationViewModel, getStatusBarParts, indexToColumnLetter, isInSelectionRange, isRowInRange, mergeFilter, normalizeSelectionRange, numberParser, parseTsvClipboard, parseValue, partitionColumnsForVirtualization, processClientSideData, rangesEqual, renderFilterContent, resolveCellDisplayContent2 as resolveCellDisplayContent, resolveCellStyle2 as resolveCellStyle, richSelectDropdownStyle, richSelectNoMatchesStyle, richSelectOptionHighlightedStyle, richSelectOptionStyle, richSelectWrapperStyle, selectChevronStyle, selectDisplayStyle, selectEditorStyle, toUserLike, triggerCsvDownload, useActiveCell, useCellEditing, useCellSelection, useClipboard, useColumnChooserState, useColumnHeaderFilterState, useColumnMeta, useColumnReorder, useColumnResize, useContextMenu, useDataGridState, useDataGridTableOrchestration, useDateFilterState, useDebounce, useFillHandle, useFilterOptions, useFormulaBar, useFormulaEngine, useInlineCellEditorState, useKeyboardNavigation, useLatestRef, useListVirtualizer, useMultiSelectFilterState, useOGrid, usePaginationControls, usePeopleFilterState, useRichSelectState, useRowSelection, useSelectState, useSideBarState, useTableLayout, useTextFilterState, useUndoRedo, useVirtualScroll };