@particle-academy/fancy-sheets 0.7.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/index.cjs +35 -28
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +90 -1
- package/dist/index.d.ts +90 -1
- package/dist/index.js +31 -29
- package/dist/index.js.map +1 -1
- package/docs/recipes/csv-roundtrip.md +53 -0
- package/docs/recipes/custom-functions.md +56 -0
- package/docs/recipes/external-state-sync.md +71 -0
- package/docs/recipes/headless-recalc.md +67 -0
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -308,6 +308,52 @@ declare namespace SheetWorkbook {
|
|
|
308
308
|
var displayName: string;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
type FormulaTokenType = "number" | "string" | "cellRef" | "rangeRef" | "sheetCellRef" | "sheetRangeRef" | "function" | "operator" | "paren" | "comma" | "boolean";
|
|
312
|
+
interface FormulaToken {
|
|
313
|
+
type: FormulaTokenType;
|
|
314
|
+
value: string;
|
|
315
|
+
position: number;
|
|
316
|
+
}
|
|
317
|
+
type FormulaASTNode = {
|
|
318
|
+
type: "number";
|
|
319
|
+
value: number;
|
|
320
|
+
} | {
|
|
321
|
+
type: "string";
|
|
322
|
+
value: string;
|
|
323
|
+
} | {
|
|
324
|
+
type: "boolean";
|
|
325
|
+
value: boolean;
|
|
326
|
+
} | {
|
|
327
|
+
type: "cellRef";
|
|
328
|
+
address: string;
|
|
329
|
+
} | {
|
|
330
|
+
type: "rangeRef";
|
|
331
|
+
start: string;
|
|
332
|
+
end: string;
|
|
333
|
+
} | {
|
|
334
|
+
type: "sheetCellRef";
|
|
335
|
+
sheet: string;
|
|
336
|
+
address: string;
|
|
337
|
+
} | {
|
|
338
|
+
type: "sheetRangeRef";
|
|
339
|
+
sheet: string;
|
|
340
|
+
start: string;
|
|
341
|
+
end: string;
|
|
342
|
+
} | {
|
|
343
|
+
type: "functionCall";
|
|
344
|
+
name: string;
|
|
345
|
+
args: FormulaASTNode[];
|
|
346
|
+
} | {
|
|
347
|
+
type: "binaryOp";
|
|
348
|
+
operator: string;
|
|
349
|
+
left: FormulaASTNode;
|
|
350
|
+
right: FormulaASTNode;
|
|
351
|
+
} | {
|
|
352
|
+
type: "unaryOp";
|
|
353
|
+
operator: string;
|
|
354
|
+
operand: FormulaASTNode;
|
|
355
|
+
};
|
|
356
|
+
|
|
311
357
|
/** Convert 0-based column index to letter(s): 0="A", 25="Z", 26="AA" */
|
|
312
358
|
declare function columnToLetter(col: number): string;
|
|
313
359
|
/** Convert column letter(s) to 0-based index: "A"=0, "Z"=25, "AA"=26 */
|
|
@@ -332,4 +378,47 @@ declare function workbookToCSV(workbook: WorkbookData, sheetId?: string): string
|
|
|
332
378
|
type FormulaRangeFunction = (args: CellValue[][]) => CellValue;
|
|
333
379
|
declare function registerFunction(name: string, fn: FormulaRangeFunction): void;
|
|
334
380
|
|
|
335
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Recalculate every formula cell in a workbook, resolving cross-sheet
|
|
383
|
+
* references. Pure — no React, no DOM — so it runs headless in Node for SSR,
|
|
384
|
+
* snapshots, export pipelines, and tests. Returns a new workbook with each
|
|
385
|
+
* formula cell's `computedValue` populated.
|
|
386
|
+
*/
|
|
387
|
+
declare function recalculateWorkbook(workbook: WorkbookData): WorkbookData;
|
|
388
|
+
/**
|
|
389
|
+
* Recalculate all formula cells in a single sheet, with optional cross-sheet
|
|
390
|
+
* reference support when `allSheets` is supplied. Pure.
|
|
391
|
+
*/
|
|
392
|
+
declare function recalculateSheet(sheet: SheetData, allSheets?: SheetData[]): SheetData;
|
|
393
|
+
|
|
394
|
+
declare function lexFormula(input: string): FormulaToken[];
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Recursive descent parser for spreadsheet formulas.
|
|
398
|
+
* Operator precedence (low to high):
|
|
399
|
+
* 1. Comparison: =, <>, <, >, <=, >=
|
|
400
|
+
* 2. String concatenation: &
|
|
401
|
+
* 3. Addition/subtraction: +, -
|
|
402
|
+
* 4. Multiplication/division: *, /
|
|
403
|
+
* 5. Exponentiation: ^
|
|
404
|
+
* 6. Unary: -, +
|
|
405
|
+
* 7. Atoms: number, string, boolean, cellRef, rangeRef, function call, (expr)
|
|
406
|
+
*/
|
|
407
|
+
declare function parseFormula(tokens: FormulaToken[]): FormulaASTNode;
|
|
408
|
+
|
|
409
|
+
type CellValueGetter = (address: string) => CellValue;
|
|
410
|
+
type RangeValueGetter = (start: string, end: string) => CellValue[];
|
|
411
|
+
type SheetCellValueGetter = (sheetName: string, address: string) => CellValue;
|
|
412
|
+
type SheetRangeValueGetter = (sheetName: string, start: string, end: string) => CellValue[];
|
|
413
|
+
interface EvaluatorContext {
|
|
414
|
+
getCellValue: CellValueGetter;
|
|
415
|
+
getRangeValues: RangeValueGetter;
|
|
416
|
+
getSheetCellValue?: SheetCellValueGetter;
|
|
417
|
+
getSheetRangeValues?: SheetRangeValueGetter;
|
|
418
|
+
}
|
|
419
|
+
declare function evaluateAST(node: FormulaASTNode, getCellValue: CellValueGetter, getRangeValues: RangeValueGetter, ctx?: {
|
|
420
|
+
getSheetCellValue?: SheetCellValueGetter;
|
|
421
|
+
getSheetRangeValues?: SheetRangeValueGetter;
|
|
422
|
+
}): CellValue;
|
|
423
|
+
|
|
424
|
+
export { type CellAddress, type CellComment, type CellData, type CellFormat, type CellHighlight, type CellHighlightMap, type CellMap, type CellRange, type CellValue, type CellValueGetter, type ColumnWidths, type EvaluatorContext, type FormulaASTNode, type FormulaRangeFunction, type FormulaToken, type FormulaTokenType, type MergedRegion, type RangeValueGetter, type SelectionState, Sheet, type SheetCellValueGetter, type SheetData, type SheetProps, type SheetRangeValueGetter, SheetWorkbook, type SheetWorkbookProps, Spreadsheet, type SpreadsheetContextMenuItem, type SpreadsheetContextValue, type SpreadsheetProps, type TextAlign, type ToolbarButton, type WorkbookData, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, evaluateAST, letterToColumn, lexFormula, parseAddress, parseCSV, parseFormula, recalculateSheet, recalculateWorkbook, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
|
package/dist/index.d.ts
CHANGED
|
@@ -308,6 +308,52 @@ declare namespace SheetWorkbook {
|
|
|
308
308
|
var displayName: string;
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
+
type FormulaTokenType = "number" | "string" | "cellRef" | "rangeRef" | "sheetCellRef" | "sheetRangeRef" | "function" | "operator" | "paren" | "comma" | "boolean";
|
|
312
|
+
interface FormulaToken {
|
|
313
|
+
type: FormulaTokenType;
|
|
314
|
+
value: string;
|
|
315
|
+
position: number;
|
|
316
|
+
}
|
|
317
|
+
type FormulaASTNode = {
|
|
318
|
+
type: "number";
|
|
319
|
+
value: number;
|
|
320
|
+
} | {
|
|
321
|
+
type: "string";
|
|
322
|
+
value: string;
|
|
323
|
+
} | {
|
|
324
|
+
type: "boolean";
|
|
325
|
+
value: boolean;
|
|
326
|
+
} | {
|
|
327
|
+
type: "cellRef";
|
|
328
|
+
address: string;
|
|
329
|
+
} | {
|
|
330
|
+
type: "rangeRef";
|
|
331
|
+
start: string;
|
|
332
|
+
end: string;
|
|
333
|
+
} | {
|
|
334
|
+
type: "sheetCellRef";
|
|
335
|
+
sheet: string;
|
|
336
|
+
address: string;
|
|
337
|
+
} | {
|
|
338
|
+
type: "sheetRangeRef";
|
|
339
|
+
sheet: string;
|
|
340
|
+
start: string;
|
|
341
|
+
end: string;
|
|
342
|
+
} | {
|
|
343
|
+
type: "functionCall";
|
|
344
|
+
name: string;
|
|
345
|
+
args: FormulaASTNode[];
|
|
346
|
+
} | {
|
|
347
|
+
type: "binaryOp";
|
|
348
|
+
operator: string;
|
|
349
|
+
left: FormulaASTNode;
|
|
350
|
+
right: FormulaASTNode;
|
|
351
|
+
} | {
|
|
352
|
+
type: "unaryOp";
|
|
353
|
+
operator: string;
|
|
354
|
+
operand: FormulaASTNode;
|
|
355
|
+
};
|
|
356
|
+
|
|
311
357
|
/** Convert 0-based column index to letter(s): 0="A", 25="Z", 26="AA" */
|
|
312
358
|
declare function columnToLetter(col: number): string;
|
|
313
359
|
/** Convert column letter(s) to 0-based index: "A"=0, "Z"=25, "AA"=26 */
|
|
@@ -332,4 +378,47 @@ declare function workbookToCSV(workbook: WorkbookData, sheetId?: string): string
|
|
|
332
378
|
type FormulaRangeFunction = (args: CellValue[][]) => CellValue;
|
|
333
379
|
declare function registerFunction(name: string, fn: FormulaRangeFunction): void;
|
|
334
380
|
|
|
335
|
-
|
|
381
|
+
/**
|
|
382
|
+
* Recalculate every formula cell in a workbook, resolving cross-sheet
|
|
383
|
+
* references. Pure — no React, no DOM — so it runs headless in Node for SSR,
|
|
384
|
+
* snapshots, export pipelines, and tests. Returns a new workbook with each
|
|
385
|
+
* formula cell's `computedValue` populated.
|
|
386
|
+
*/
|
|
387
|
+
declare function recalculateWorkbook(workbook: WorkbookData): WorkbookData;
|
|
388
|
+
/**
|
|
389
|
+
* Recalculate all formula cells in a single sheet, with optional cross-sheet
|
|
390
|
+
* reference support when `allSheets` is supplied. Pure.
|
|
391
|
+
*/
|
|
392
|
+
declare function recalculateSheet(sheet: SheetData, allSheets?: SheetData[]): SheetData;
|
|
393
|
+
|
|
394
|
+
declare function lexFormula(input: string): FormulaToken[];
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Recursive descent parser for spreadsheet formulas.
|
|
398
|
+
* Operator precedence (low to high):
|
|
399
|
+
* 1. Comparison: =, <>, <, >, <=, >=
|
|
400
|
+
* 2. String concatenation: &
|
|
401
|
+
* 3. Addition/subtraction: +, -
|
|
402
|
+
* 4. Multiplication/division: *, /
|
|
403
|
+
* 5. Exponentiation: ^
|
|
404
|
+
* 6. Unary: -, +
|
|
405
|
+
* 7. Atoms: number, string, boolean, cellRef, rangeRef, function call, (expr)
|
|
406
|
+
*/
|
|
407
|
+
declare function parseFormula(tokens: FormulaToken[]): FormulaASTNode;
|
|
408
|
+
|
|
409
|
+
type CellValueGetter = (address: string) => CellValue;
|
|
410
|
+
type RangeValueGetter = (start: string, end: string) => CellValue[];
|
|
411
|
+
type SheetCellValueGetter = (sheetName: string, address: string) => CellValue;
|
|
412
|
+
type SheetRangeValueGetter = (sheetName: string, start: string, end: string) => CellValue[];
|
|
413
|
+
interface EvaluatorContext {
|
|
414
|
+
getCellValue: CellValueGetter;
|
|
415
|
+
getRangeValues: RangeValueGetter;
|
|
416
|
+
getSheetCellValue?: SheetCellValueGetter;
|
|
417
|
+
getSheetRangeValues?: SheetRangeValueGetter;
|
|
418
|
+
}
|
|
419
|
+
declare function evaluateAST(node: FormulaASTNode, getCellValue: CellValueGetter, getRangeValues: RangeValueGetter, ctx?: {
|
|
420
|
+
getSheetCellValue?: SheetCellValueGetter;
|
|
421
|
+
getSheetRangeValues?: SheetRangeValueGetter;
|
|
422
|
+
}): CellValue;
|
|
423
|
+
|
|
424
|
+
export { type CellAddress, type CellComment, type CellData, type CellFormat, type CellHighlight, type CellHighlightMap, type CellMap, type CellRange, type CellValue, type CellValueGetter, type ColumnWidths, type EvaluatorContext, type FormulaASTNode, type FormulaRangeFunction, type FormulaToken, type FormulaTokenType, type MergedRegion, type RangeValueGetter, type SelectionState, Sheet, type SheetCellValueGetter, type SheetData, type SheetProps, type SheetRangeValueGetter, SheetWorkbook, type SheetWorkbookProps, Spreadsheet, type SpreadsheetContextMenuItem, type SpreadsheetContextValue, type SpreadsheetProps, type TextAlign, type ToolbarButton, type WorkbookData, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, evaluateAST, letterToColumn, lexFormula, parseAddress, parseCSV, parseFormula, recalculateSheet, recalculateWorkbook, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
|
package/dist/index.js
CHANGED
|
@@ -1249,7 +1249,7 @@ function getRecalculationOrder(graph) {
|
|
|
1249
1249
|
return order;
|
|
1250
1250
|
}
|
|
1251
1251
|
|
|
1252
|
-
// src/
|
|
1252
|
+
// src/engine/recalc.ts
|
|
1253
1253
|
function recalculateWorkbook(workbook) {
|
|
1254
1254
|
const recalculated = [];
|
|
1255
1255
|
for (const sheet of workbook.sheets) {
|
|
@@ -1261,33 +1261,6 @@ function recalculateWorkbook(workbook) {
|
|
|
1261
1261
|
}
|
|
1262
1262
|
return { ...workbook, sheets: finalSheets };
|
|
1263
1263
|
}
|
|
1264
|
-
function createInitialState(data) {
|
|
1265
|
-
const workbook = data ?? createEmptyWorkbook();
|
|
1266
|
-
return {
|
|
1267
|
-
workbook: recalculateWorkbook(workbook),
|
|
1268
|
-
selection: { activeCell: "A1", ranges: [{ start: "A1", end: "A1" }] },
|
|
1269
|
-
editingCell: null,
|
|
1270
|
-
editValue: "",
|
|
1271
|
-
undoStack: [],
|
|
1272
|
-
redoStack: []
|
|
1273
|
-
};
|
|
1274
|
-
}
|
|
1275
|
-
function getActiveSheet(state) {
|
|
1276
|
-
return state.workbook.sheets.find((s) => s.id === state.workbook.activeSheetId);
|
|
1277
|
-
}
|
|
1278
|
-
function updateActiveSheet(state, updater) {
|
|
1279
|
-
return {
|
|
1280
|
-
...state.workbook,
|
|
1281
|
-
sheets: state.workbook.sheets.map(
|
|
1282
|
-
(s) => s.id === state.workbook.activeSheetId ? updater(s) : s
|
|
1283
|
-
)
|
|
1284
|
-
};
|
|
1285
|
-
}
|
|
1286
|
-
function pushUndo(state) {
|
|
1287
|
-
const stack = [...state.undoStack, state.workbook];
|
|
1288
|
-
if (stack.length > 50) stack.shift();
|
|
1289
|
-
return { undoStack: stack, redoStack: [] };
|
|
1290
|
-
}
|
|
1291
1264
|
function recalculateSheet(sheet, allSheets) {
|
|
1292
1265
|
const graph = buildDependencyGraph(sheet.cells);
|
|
1293
1266
|
if (graph.size === 0) return sheet;
|
|
@@ -1344,6 +1317,35 @@ function recalculateSheet(sheet, allSheets) {
|
|
|
1344
1317
|
}
|
|
1345
1318
|
return { ...sheet, cells };
|
|
1346
1319
|
}
|
|
1320
|
+
|
|
1321
|
+
// src/hooks/use-spreadsheet-store.ts
|
|
1322
|
+
function createInitialState(data) {
|
|
1323
|
+
const workbook = data ?? createEmptyWorkbook();
|
|
1324
|
+
return {
|
|
1325
|
+
workbook: recalculateWorkbook(workbook),
|
|
1326
|
+
selection: { activeCell: "A1", ranges: [{ start: "A1", end: "A1" }] },
|
|
1327
|
+
editingCell: null,
|
|
1328
|
+
editValue: "",
|
|
1329
|
+
undoStack: [],
|
|
1330
|
+
redoStack: []
|
|
1331
|
+
};
|
|
1332
|
+
}
|
|
1333
|
+
function getActiveSheet(state) {
|
|
1334
|
+
return state.workbook.sheets.find((s) => s.id === state.workbook.activeSheetId);
|
|
1335
|
+
}
|
|
1336
|
+
function updateActiveSheet(state, updater) {
|
|
1337
|
+
return {
|
|
1338
|
+
...state.workbook,
|
|
1339
|
+
sheets: state.workbook.sheets.map(
|
|
1340
|
+
(s) => s.id === state.workbook.activeSheetId ? updater(s) : s
|
|
1341
|
+
)
|
|
1342
|
+
};
|
|
1343
|
+
}
|
|
1344
|
+
function pushUndo(state) {
|
|
1345
|
+
const stack = [...state.undoStack, state.workbook];
|
|
1346
|
+
if (stack.length > 50) stack.shift();
|
|
1347
|
+
return { undoStack: stack, redoStack: [] };
|
|
1348
|
+
}
|
|
1347
1349
|
function getCellDisplayValue(cell) {
|
|
1348
1350
|
if (!cell) return "";
|
|
1349
1351
|
if (cell.computedValue !== void 0) return String(cell.computedValue);
|
|
@@ -2859,6 +2861,6 @@ function workbookToCSV(workbook, sheetId) {
|
|
|
2859
2861
|
return stringifyCSV(data);
|
|
2860
2862
|
}
|
|
2861
2863
|
|
|
2862
|
-
export { Sheet, SheetWorkbook, Spreadsheet, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, letterToColumn, parseAddress, parseCSV, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
|
|
2864
|
+
export { Sheet, SheetWorkbook, Spreadsheet, columnToLetter, createEmptySheet, createEmptyWorkbook, csvToWorkbook, evaluateAST, letterToColumn, lexFormula, parseAddress, parseCSV, parseFormula, recalculateSheet, recalculateWorkbook, registerFunction, stringifyCSV, toAddress, useSpreadsheet, workbookToCSV };
|
|
2863
2865
|
//# sourceMappingURL=index.js.map
|
|
2864
2866
|
//# sourceMappingURL=index.js.map
|