From 3da83885b5604f90f9a36b6965954aeba63d0c06 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 24 Aug 2026 13:33:00 +0200 Subject: [PATCH] feat(model): add sheet and cell model --- lib/src/sheet.dart | 90 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 lib/src/sheet.dart diff --git a/lib/src/sheet.dart b/lib/src/sheet.dart new file mode 100644 index 0000000..d6e03b5 --- /dev/null +++ b/lib/src/sheet.dart @@ -0,0 +1,90 @@ +import 'address.dart'; +import 'ast.dart'; +import 'format.dart'; +import 'values.dart'; +import 'workbook.dart'; + +typedef GlobalCell = (Sheet sheet, CellAddress address); + +class Cell { + String raw; + Expr? formula; + CellValue value; + NumberFormat? format; + Set precedents; + Set dependents; + + Cell() + : raw = '', + formula = null, + value = const BlankValue(), + format = null, + precedents = {}, + dependents = {}; + + bool get isFormula => formula != null; + + bool get isOccupied => raw.isNotEmpty || formula != null; +} + +class Sheet { + final String name; + final Workbook workbook; + final Map cells = {}; + + Sheet.internal(this.name, this.workbook); + + factory Sheet([String name = 'Sheet1']) { + final book = Workbook.empty(); + return book.addSheet(name); + } + + Iterable> get occupiedCells => + cells.entries.where((entry) => entry.value.isOccupied); + + Cell? cellAt(CellAddress address) => cells[address]; + + CellValue valueAt(CellAddress address) => cells[address]?.value ?? const BlankValue(); + + CellValue valueOf(String reference) => valueAt(CellAddress.parse(reference)); + + String rawAt(CellAddress address) => cells[address]?.raw ?? ''; + + String displayAt(CellAddress address) { + final cell = cells[address]; + if (cell == null) return ''; + final format = cell.format; + if (format != null) return format.apply(cell.value); + return cell.value.display(); + } + + NumberFormat? formatAt(CellAddress address) => cells[address]?.format; + + void setFormat(String reference, NumberFormat? format) => + setFormatAt(CellAddress.parse(reference), format); + + void setFormatAt(CellAddress address, NumberFormat? format) { + final cell = cells.putIfAbsent(address, Cell.new); + cell.format = format; + } + + void setValue(String reference, String input) => setCell(CellAddress.parse(reference), input); + + void setCell(CellAddress address, String input, {bool recalculate = true}) { + workbook.setCell(this, address, input, recalculate: recalculate); + } + + void clear(CellAddress address) => workbook.clearCell(this, address); + + void recalculate() => workbook.recalculate(); + + ({int cols, int rows}) dimensions() { + var maxCol = -1; + var maxRow = -1; + for (final entry in occupiedCells) { + if (entry.key.col > maxCol) maxCol = entry.key.col; + if (entry.key.row > maxRow) maxRow = entry.key.row; + } + return (cols: maxCol + 1, rows: maxRow + 1); + } +}