@alaarab/ogrid-react 2.3.0 → 2.4.0

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