-
Notifications
You must be signed in to change notification settings - Fork 0
USAGE
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 — this document covers every public API
surface with runnable examples, but that one is the line-by-line reference.
- Installation
- TypeScript/JS: core concepts
- TypeScript/JS: import & export
- TypeScript/JS: the Table (read-only) renderer
- TypeScript/JS: the Spreadsheet (editable) renderer
- TypeScript/JS: formulas
- TypeScript/JS: split & merge
- TypeScript/JS: templates
- TypeScript/JS: internationalization
- TypeScript/JS: React adapter
- TypeScript/JS: Vue adapter
- PHP: installation & concepts
- PHP: import & export
- PHP: formulas
- PHP: split & merge
- Go: installation & concepts
- Go: import & export
- Go: formulas
- Go: split & merge
- Security-relevant options
- Troubleshooting
TypeScript/JS (once published to npm):
npm install lomboktablesheetPHP (once published to Packagist):
composer require codinglombok/lomboktablesheetWorking from this repository directly (not yet published):
# 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 */'Everything revolves around a Workbook, which holds one or more Sheets, each a grid
of Cells. You rarely construct Cell objects directly — use Sheet.setValue() or
Workbook.fromRows().
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).
All decoders return { workbook, warnings } and never throw on malformed input —
check warnings instead of wrapping in try/catch for expected parse issues.
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('<table><tr><td>x</td></tr></table>');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' });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).
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(); // booleanInteraction 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.
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):
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.
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 A1import { 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'Templates are purely presentational — they never touch data, so exporting to CSV/JSON strips them entirely, as it should.
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
});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.
import { LombokTableReact, LombokSheetReact } from 'lomboktablesheet/react';
function ReportView({ rows }: { rows: unknown[][] }) {
return <LombokTableReact data={rows} template="report" locale="en-US" />;
}
function EditableView({ workbook }: { workbook: Workbook }) {
return (
<LombokSheetReact
workbook={workbook}
onCellChange={(row, col) => 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).
<script setup lang="ts">
import { LombokTableVue, LombokSheetVue } from 'lomboktablesheet/vue';
import { ref } from 'vue';
const workbook = ref(/* ... */);
</script>
<template>
<LombokTableVue :workbook="workbook" template="invoice" />
<LombokSheetVue :workbook="workbook" @cell-change="(r, c) => console.log(r, c)" />
</template>Same Workbook/Sheet/Cell model, structurally translated:
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.
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 TSuse 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).
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 get github.com/codinglombok/lomboktablesheet-goSame Workbook/Sheet/Cell model as TS and PHP, structurally translated:
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.
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/PHPsheet := 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.
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)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
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
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.
"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.