# USAGE — How to use LombokTableSheet
This is the detailed how-to guide. For a quick start, see `README.md`. For internals,
see `ARCHITECTURE.md`. For an exhaustive, per-function API reference across all three
languages (parameter tables, error codes, full locale list, worked examples), see
[DETAILED_USAGE.md](./DETAILED_USAGE.md) — this document covers every public API
surface with runnable examples, but that one is the line-by-line reference.
## Table of contents
- [Installation](#installation)
- [TypeScript/JS: core concepts](#typescriptjs-core-concepts)
- [TypeScript/JS: import & export](#typescriptjs-import--export)
- [TypeScript/JS: the Table (read-only) renderer](#typescriptjs-the-table-read-only-renderer)
- [TypeScript/JS: the Spreadsheet (editable) renderer](#typescriptjs-the-spreadsheet-editable-renderer)
- [TypeScript/JS: formulas](#typescriptjs-formulas)
- [TypeScript/JS: split & merge](#typescriptjs-split--merge)
- [TypeScript/JS: templates](#typescriptjs-templates)
- [TypeScript/JS: internationalization](#typescriptjs-internationalization)
- [TypeScript/JS: React adapter](#typescriptjs-react-adapter)
- [TypeScript/JS: Vue adapter](#typescriptjs-vue-adapter)
- [PHP: installation & concepts](#php-installation--concepts)
- [PHP: import & export](#php-import--export)
- [PHP: formulas](#php-formulas)
- [PHP: split & merge](#php-split--merge)
- [Go: installation & concepts](#go-installation--concepts)
- [Go: import & export](#go-import--export)
- [Go: formulas](#go-formulas)
- [Go: split & merge](#go-split--merge)
- [Security-relevant options](#security-relevant-options)
- [Troubleshooting](#troubleshooting)
---
## Installation
**TypeScript/JS (once published to npm):**
```bash
npm install lomboktablesheet
```
**PHP (once published to Packagist):**
```bash
composer require codinglombok/lomboktablesheet
```
**Working from this repository directly (not yet published):**
```bash
# TS/JS
cd LombokTableSheet && npm install && npm run build
# then import from ./dist/index.js
# PHP
cd LombokTableSheet/ports/php
# composer install (once Packagist is reachable), or use the bundled
# classmap-style bootstrap for local testing:
php -r 'require "autoload.php"; /* your code here */'
```
---
## TypeScript/JS: core concepts
Everything revolves around a `Workbook`, which holds one or more `Sheet`s, each a grid
of `Cell`s. You rarely construct `Cell` objects directly — use `Sheet.setValue()` or
`Workbook.fromRows()`.
```ts
import { Workbook, Sheet } from 'lomboktablesheet';
// From a 2D array (first row often used as a header by convention, not enforced)
const workbook = Workbook.fromRows([
['name', 'age'],
['Alice', 30],
['Bob', 25],
], 'People', 'en-US');
// Or build a sheet manually
const sheet = new Sheet('Custom');
sheet.setValue(0, 0, 'Hello');
sheet.setValue(0, 1, 42);
const wb2 = new Workbook();
wb2.addSheet(sheet);
// Reading data back
console.log(workbook.sheets[0].toRows());
// [['name','age'], ['Alice',30], ['Bob',25]]
console.log(workbook.sheet('People')?.getCell(1, 0).value); // 'Alice'
```
`Sheet` only ever grows in dimension as you write cells further out — this is
deliberate (see ARCHITECTURE.md §2.2) and is why `undo()` in the Spreadsheet adapter
needs to explicitly restore prior dimensions, not just cell values (see SECURITY.md
for the bug this caused and how it was fixed).
---
## TypeScript/JS: import & export
All decoders return `{ workbook, warnings }` and **never throw** on malformed input —
check `warnings` instead of wrapping in try/catch for expected parse issues.
```ts
import {
decodeCsv, encodeCsv,
decodeJson, encodeJson, encodeMarkdown,
decodeXlsx, encodeXlsx,
decodeHtml, encodeHtml,
} from 'lomboktablesheet';
// CSV
const { workbook, warnings } = decodeCsv('name,age\nAlice,30\n');
if (warnings.length) console.warn(warnings);
const csvText = encodeCsv(workbook!);
// JSON (array-of-records shape)
const { workbook: fromJson } = decodeJson('[{"name":"Alice","age":30}]');
const jsonText = encodeJson(fromJson!, { pretty: true });
// Markdown — export only (import is unsupported; Markdown tables are a lossy,
// ambiguous source format, so decoding one isn't offered)
const mdText = encodeMarkdown(workbook!);
// XLSX — Buffer in, Buffer out. No external xlsx/zip library involved.
const xlsxBuffer = encodeXlsx(workbook!);
import { writeFileSync } from 'node:fs';
writeFileSync('out.xlsx', xlsxBuffer);
const { workbook: fromXlsx } = decodeXlsx(xlsxBuffer);
// HTML
const html = encodeHtml(workbook!, { className: 'my-table' });
const { workbook: fromHtml } = decodeHtml('
');
```
### Handling multiple sheets
```ts
const workbook = new Workbook('en-US');
workbook.addSheet(Sheet1);
workbook.addSheet(Sheet2);
// Most codecs default to the first sheet; pass sheetName to target another:
const csvOfSecondSheet = encodeCsv(workbook, { sheetName: 'Sheet2' });
```
---
## TypeScript/JS: the Table (read-only) renderer
```ts
import { LombokTable } from 'lomboktablesheet';
const container = document.getElementById('app')!;
const table = new LombokTable(container, {
workbook, // OR: data: [[...]] / data: [{...}, {...}]
template: 'report', // 'plain' | 'report' | 'invoice' | 'financial-statement'
locale: 'en-US',
});
// Update data later without recreating the instance
table.setData([['x', 'y'], [1, 2]]);
// Get the underlying workbook (e.g. to export it)
const wb = table.getWorkbook();
```
`LombokTable` renders every cell as a text node — never `innerHTML` — so it's safe
against malicious cell content by construction (see SECURITY.md).
---
## TypeScript/JS: the Spreadsheet (editable) renderer
```ts
import { LombokSheet, Workbook } from 'lomboktablesheet';
const workbook = new Workbook('en-US');
const sheet = new LombokSheet(document.getElementById('app')!, { workbook });
sheet.on('cellChange', (row, col) => {
console.log(`Cell (${row}, ${col}) changed`);
});
// Programmatic undo/redo (also bound to Ctrl+Z / Ctrl+Y in the rendered grid)
sheet.undo();
sheet.redo();
sheet.canUndo(); // boolean
sheet.canRedo(); // boolean
```
**Interaction model:** double-click a cell to edit; type a value or a formula
(anything starting with `=`); press Enter to commit, Escape to cancel, or click away
(blur) to commit.
---
## TypeScript/JS: formulas
You don't usually call the formula engine directly — `LombokSheet` does it for you when
a cell's raw input starts with `=`. But it's a public API if you need it standalone
(e.g. to validate a formula string before accepting it, or to build your own UI):
```ts
import { Sheet, parseFormula, evaluate, makeSheetResolver, FormulaError } from 'lomboktablesheet';
const sheet = new Sheet('S1');
sheet.setValue(0, 0, 10); // A1
sheet.setValue(0, 1, 20); // B1
const ast = parseFormula('=SUM(A1:B1)*2');
const result = evaluate(ast, makeSheetResolver(sheet));
if (result instanceof FormulaError) {
console.error('Formula error:', result.code); // e.g. '#DIV/0!'
} else {
console.log(result); // 60
}
```
**Supported syntax:** `+ - * / ^`, comparisons (`= <> < <= > >=`), cell refs (`A1`),
ranges (`A1:B3`), parentheses, and functions `SUM`, `AVG`, `MIN`, `MAX`, `COUNT`, `IF`,
`ROUND`, `CONCAT`. Circular references resolve to `#CIRC!` rather than hanging. Formulas
nested more than 200 levels deep (parens or unary chains) are rejected with a clear error
rather than risking a stack overflow — see SECURITY.md.
### Using the transaction layer directly (undo/redo without a DOM)
```ts
import { Sheet, TransactionalSheet } from 'lomboktablesheet';
const sheet = new Sheet('S1');
const tx = new TransactionalSheet(sheet);
tx.setCellInput(0, 0, 10);
tx.setCellInput(0, 1, '=A1*2'); // recalculates automatically
tx.undo(); // reverts the last commit, including sheet dimensions
tx.redo();
tx.dependentsOf('A1'); // -> cell refs of formulas that reference A1
```
---
## TypeScript/JS: split & merge
```ts
import { splitByRows, splitByColumns, splitBySheet, merge } from 'lomboktablesheet';
const [top, bottom] = splitByRows(workbook, 'Sheet1', 100); // split at row 100
const [left, right] = splitByColumns(workbook, 'Sheet1', 3); // split at column 3
const perSheet = splitBySheet(workbook); // one workbook per sheet
const combined = merge([top, bottom], { onConflict: 'left-wins' });
// onConflict: 'left-wins' (default) | 'right-wins' | 'error'
```
---
## TypeScript/JS: templates
Templates are purely presentational — they never touch data, so exporting to
CSV/JSON strips them entirely, as it should.
```ts
import { defaultTemplates } from 'lomboktablesheet';
defaultTemplates.list(); // ['plain', 'report', 'invoice', 'financial-statement']
defaultTemplates.register({
name: 'dashboard',
description: 'Compact dashboard style',
header: { bold: true, sticky: true },
zebraRows: true,
borders: 'horizontal',
numberAlign: 'right',
cssHooks: ['lts-dashboard'], // class names LombokCSS (if present) can theme
});
```
---
## TypeScript/JS: internationalization
```ts
import { I18n, t } from 'lomboktablesheet';
const i18n = new I18n('ar-EG');
i18n.isRtl(); // true
i18n.formatNumber(1234.5); // locale-formatted
i18n.formatCurrency(1500, 'USD');
i18n.formatDate(new Date());
t('id', 'export'); // 'Ekspor' — UI-string catalog lookup (11 built-in locales)
```
Locale is also accepted directly by `LombokTable`/`LombokSheet`/decoders via a
`locale` option, and drives both `Intl` cell formatting and RTL layout automatically.
---
## TypeScript/JS: React adapter
```tsx
import { LombokTableReact, LombokSheetReact } from 'lomboktablesheet/react';
function ReportView({ rows }: { rows: unknown[][] }) {
return ;
}
function EditableView({ workbook }: { workbook: Workbook }) {
return (
console.log('changed', row, col)}
/>
);
}
```
This is an opt-in sub-path — importing it does not add React to projects that don't
use it (the core bundle has zero React code, verified in CI).
---
## TypeScript/JS: Vue adapter
```vue
console.log(r, c)" />
```
---
## PHP: installation & concepts
Same `Workbook`/`Sheet`/`Cell` model, structurally translated:
```php
use Lombok\TableSheet\Core\Workbook;
use Lombok\TableSheet\Core\Sheet;
$workbook = Workbook::fromRows([
['name', 'age'],
['Alice', 30],
['Bob', 25],
], 'People', 'en-US');
$sheet = new Sheet('Custom');
$sheet->setValue(0, 0, 'Hello');
$sheet->setValue(0, 1, 42);
echo json_encode($workbook->sheets[0]->toRows());
```
**Scope note:** the PHP port is a data/formula layer, not a UI library — PHP has no
DOM to render into. There is no `LombokTable`/`LombokSheet` equivalent in PHP; instead,
use it inside your own framework (Laravel, Symfony, plain PHP) to build/transform data
that you then export as CSV/JSON, or render however your framework does tables.
---
## PHP: import & export
```php
use Lombok\TableSheet\Formats\CsvCodec;
use Lombok\TableSheet\Formats\JsonCodec;
use Lombok\TableSheet\Formats\MarkdownCodec;
$result = CsvCodec::decode("name,age\nAlice,30\n");
if (!empty($result->warnings)) {
foreach ($result->warnings as $w) { error_log($w->message); }
}
$csvOut = CsvCodec::encode($result->workbook);
$jsonResult = JsonCodec::decode('[{"name":"Alice","age":30}]');
$jsonOut = JsonCodec::encode($jsonResult->workbook, pretty: true);
$mdOut = MarkdownCodec::encode($result->workbook); // export only, same rationale as TS
```
---
## PHP: formulas
```php
use Lombok\TableSheet\Core\Sheet;
use Lombok\TableSheet\Core\FormulaEngine;
use Lombok\TableSheet\Core\FormulaError;
$sheet = new Sheet('S1');
$sheet->setValue(0, 0, 10);
$sheet->setValue(0, 1, 20);
$ast = FormulaEngine::parse('=SUM(A1:B1)*2');
$result = FormulaEngine::evaluate($ast, FormulaEngine::sheetResolver($sheet));
if ($result instanceof FormulaError) {
echo "Error: {$result->code}\n";
} else {
echo $result . "\n"; // 60
}
```
Same syntax support and same 200-level nesting-depth guard as the TS engine — both
were built to the same spec and verified to produce identical results on the same
inputs (see ARCHITECTURE.md §8 and SECURITY.md).
---
## PHP: split & merge
```php
use Lombok\TableSheet\Core\SplitMerge;
[$top, $bottom] = SplitMerge::splitByRows($workbook, 'Sheet1', 100);
[$left, $right] = SplitMerge::splitByColumns($workbook, 'Sheet1', 3);
$perSheet = SplitMerge::splitBySheet($workbook);
$combined = SplitMerge::merge([$top, $bottom], 'left-wins'); // or 'right-wins' | 'error'
```
---
## Go: installation & concepts
```bash
go get github.com/codinglombok/lomboktablesheet-go
```
Same `Workbook`/`Sheet`/`Cell` model as TS and PHP, structurally translated:
```go
import lombok "github.com/codinglombok/lomboktablesheet-go/lombok"
workbook := lombok.WorkbookFromRows([][]interface{}{
{"name", "age"},
{"Alice", 30.0},
{"Bob", 25.0},
}, "People", "en-US")
sheet := lombok.NewSheet("Custom")
sheet.SetValue(0, 0, "Hello")
sheet.SetValue(0, 1, 42.0)
```
**Scope note:** same as PHP — Go has no DOM, so this is a data/formula layer, not a UI
library. No `LombokTable`/`LombokSheet` equivalent exists in Go.
## Go: import & export
```go
result := lombok.DecodeCsv("name,age\nAlice,30\n", lombok.CsvDecodeOptions{})
if len(result.Warnings) > 0 {
for _, w := range result.Warnings { log.Println(w.Message) }
}
csvOut := lombok.EncodeCsv(result.Workbook, "", 0) // delimiter 0 = default ','
jsonResult := lombok.DecodeJson(`[{"name":"Alice","age":30}]`, lombok.JsonDecodeOptions{})
jsonOut, _ := lombok.EncodeJson(jsonResult.Workbook, "", true) // pretty=true
mdOut := lombok.EncodeMarkdown(result.Workbook, "") // export only, same rationale as TS/PHP
```
## Go: formulas
```go
sheet := lombok.NewSheet("S1")
sheet.SetValue(0, 0, 10.0)
sheet.SetValue(0, 1, 20.0)
ast, err := lombok.ParseFormula("=SUM(A1:B1)*2")
if err != nil { /* syntax or nesting-depth error */ }
result := lombok.Evaluate(ast, lombok.SheetResolver(sheet))
if fe, ok := result.(lombok.FormulaError); ok {
fmt.Println("Error:", fe.Code)
} else {
fmt.Println(result) // 60
}
```
Same syntax support and the same 200-level nesting-depth guard as the TS/PHP engines —
`Evaluate` additionally wraps execution in a `recover()`, so an unexpected internal
failure degrades to a `#ERROR!` value rather than panicking your program.
## Go: split & merge
```go
top, bottom, err := lombok.SplitByRows(workbook, "Sheet1", 100)
left, right, err := lombok.SplitByColumns(workbook, "Sheet1", 3)
perSheet := lombok.SplitBySheet(workbook)
combined, err := lombok.Merge([]*lombok.Workbook{top, bottom}, lombok.LeftWins)
```
---
## Security-relevant options
Every decoder that touches untrusted input has configurable resource limits — the
defaults are generous but not infinite. Lower them if you're accepting arbitrary
user uploads:
```ts
// TS
decodeCsv(text, { maxInputBytes: 10 * 1024 * 1024, maxRows: 100_000 });
decodeJson(text, { maxInputBytes: 10 * 1024 * 1024 });
decodeHtml(html, { maxInputBytes: 10 * 1024 * 1024 });
decodeXlsx(buf, { maxEntrySize: 50 * 1024 * 1024, maxTotalSize: 100 * 1024 * 1024, maxEntries: 100 });
```
```php
// PHP
CsvCodec::decode($text, maxInputBytes: 10 * 1024 * 1024);
JsonCodec::decode($text, maxInputBytes: 10 * 1024 * 1024);
```
See `SECURITY.md` for what these guard against and why the defaults are what they are.
---
## Troubleshooting
**"Cannot find module 'lomboktablesheet'"** — the package isn't published yet (see
PROJECT_SUMMARY.md's honest scope section). Build from source: `npm install && npm run
build` in the repo root, then import from `./dist/index.js`.
**Decoders return `workbook: null`** — check `warnings`; decoders never throw on bad
input by design, they return warnings instead. A `null` workbook with a warning like
"exceeds the configured size limit" means a resource guard rejected the input — see
Security-relevant options above to adjust the limit if the input is legitimately large.
**Formula returns a string like `#DIV/0!` instead of throwing** — that's by design.
Formula errors are typed values (`FormulaError` in TS, an instance with a `->code`
property in PHP), not exceptions, matching how spreadsheet software has always
surfaced these to users. Check `result instanceof FormulaError`.
**PHP: "Class not found" errors when not using Composer** — if you're working from
this repo directly without `composer install` (e.g. because Packagist isn't reachable
in your environment), use the bundled `ports/php/autoload.php`, which eagerly requires
every source file rather than relying on PSR-4 autoloading.