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