@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 +2489 -176
- package/dist/types/components/ColumnChooserContent.d.ts +53 -0
- package/dist/types/components/ColumnHeaderFilterRenderers.d.ts +15 -1
- package/dist/types/components/FormulaBar.d.ts +25 -0
- package/dist/types/components/FormulaRefOverlay.d.ts +19 -0
- package/dist/types/components/OGridLayout.d.ts +4 -0
- package/dist/types/components/PaginationControlsBase.d.ts +71 -0
- package/dist/types/components/SheetTabs.d.ts +14 -0
- package/dist/types/hooks/index.d.ts +3 -1
- package/dist/types/hooks/useColumnHeaderMenuState.d.ts +23 -0
- package/dist/types/hooks/useFormulaBar.d.ts +43 -0
- package/dist/types/hooks/useOGrid.d.ts +4 -0
- package/dist/types/index.d.ts +14 -4
- package/dist/types/types/dataGridTypes.d.ts +14 -2
- package/dist/types/types/index.d.ts +1 -1
- package/package.json +2 -2
package/dist/esm/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as React5 from 'react';
|
|
2
|
-
import {
|
|
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,
|
|
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
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
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
|
-
|
|
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,
|
|
4651
|
-
const
|
|
4652
|
-
|
|
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("
|
|
6245
|
+
registry.set("COLUMNS", {
|
|
4656
6246
|
minArgs: 1,
|
|
4657
6247
|
maxArgs: 1,
|
|
4658
|
-
evaluate(args,
|
|
4659
|
-
const
|
|
4660
|
-
|
|
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("
|
|
6259
|
+
registry.set("SEQUENCE", {
|
|
4664
6260
|
minArgs: 1,
|
|
4665
|
-
maxArgs:
|
|
6261
|
+
maxArgs: 4,
|
|
4666
6262
|
evaluate(args, context, evaluator) {
|
|
4667
|
-
const
|
|
4668
|
-
|
|
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("
|
|
6312
|
+
registry.set("TRANSPOSE", {
|
|
4672
6313
|
minArgs: 1,
|
|
4673
6314
|
maxArgs: 1,
|
|
4674
|
-
evaluate(args, context,
|
|
4675
|
-
|
|
4676
|
-
|
|
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("
|
|
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,
|
|
4683
|
-
|
|
4684
|
-
|
|
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("
|
|
6401
|
+
registry.set("MINVERSE", {
|
|
4688
6402
|
minArgs: 1,
|
|
4689
6403
|
maxArgs: 1,
|
|
4690
|
-
evaluate(args, context,
|
|
4691
|
-
|
|
4692
|
-
|
|
4693
|
-
|
|
4694
|
-
|
|
4695
|
-
if (
|
|
4696
|
-
|
|
4697
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5087
|
-
|
|
5088
|
-
row.push(cached);
|
|
6878
|
+
if (this.values.has(key)) {
|
|
6879
|
+
row.push(this.values.get(key));
|
|
5089
6880
|
} else {
|
|
5090
|
-
row.push(rangeAccessor
|
|
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
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
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
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
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:
|
|
6008
|
-
showColumnLetters:
|
|
6009
|
-
showNameBox
|
|
6010
|
-
onActiveCellChange:
|
|
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
|
-
|
|
6070
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
8274
|
-
|
|
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) =>
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
11490
|
-
/* @__PURE__ */ jsxs("label", { style:
|
|
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:
|
|
13799
|
+
/* @__PURE__ */ jsx("input", { type: "date", value: tempDateFrom, onChange: (e) => setTempDateFrom(e.target.value), style: dateInputFlexStyle })
|
|
11493
13800
|
] }),
|
|
11494
|
-
/* @__PURE__ */ jsxs("label", { style:
|
|
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:
|
|
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 };
|