Dynamic Table Workbench
Dynamic Table Workbench
A spreadsheet-inspired data workbench where you define typed columns, edit rows inline, configure safe operations instead of formulas, and watch summaries, schema, and exports update live.
Read the full notesThis program is best experienced on a larger display. Rotate your device, or scroll sideways at your own risk.
Builder
Define typed columns, computed rules, validation expectations, and summary operations.
Columns render left-to-right in the sheet. “Move left / right” is the keyboard-friendly reorder path - no drag required.
Sheet
Edit rows against the current schema. Raw input is preserved while validation and summaries derive from it.
| Task (required) | Status (required) | Owner | Effort points | Due date | Done? | Row actions |
|---|---|---|---|---|---|---|
| Filled 4 / 4 | Groups 4 | Filled 4 / 4 | Sum 14 | Filled 4 / 4 | Checked 1 / 4 | No summary |
Summary
Live derived values from the current rows, computed cells, and column operations.
Add summary operations to Number, Money, Select, Checkbox, or Computed columns to make this panel adapt to the schema.
Grouped totals
Status totals - Effort points
- Planned 2
- Done 3
- Review 4
- Active 5
Column summaries
- Task
- Filled 4 / 4
- Status
- Groups 4
- Owner
- Filled 4 / 4
- Effort points
- Sum 14
- Due date
- Filled 4 / 4
- Done?
- Checked 1 / 4
Output
Inspect the generated schema, row data, derived summary object, and CSV export preview.
schema.json
{
"columns": [
{
"id": "col-task",
"label": "Task",
"type": "text",
"required": true,
"operation": "count-filled"
},
{
"id": "col-status",
"label": "Status",
"type": "select",
"required": true,
"options": [
{
"id": "opt-planned",
"label": "Planned"
},
{
"id": "opt-active",
"label": "Active"
},
{
"id": "opt-review",
"label": "Review"
},
{
"id": "opt-done",
"label": "Done"
}
],
"operation": "group-totals",
"groupBy": {
"sourceColumnId": "col-effort"
}
},
{
"id": "col-owner",
"label": "Owner",
"type": "text",
"operation": "count-filled"
},
{
"id": "col-effort",
"label": "Effort points",
"type": "number",
"operation": "sum"
},
{
"id": "col-due",
"label": "Due date",
"type": "date",
"operation": "count-filled"
},
{
"id": "col-done",
"label": "Done?",
"type": "checkbox",
"operation": "count-checked"
}
]
} rows.json (4)
[
{
"id": "row-1",
"values": {
"Task": "Audit current layout",
"Status": "Done",
"Owner": "Kjetil",
"Effort points": 3,
"Due date": "2026-06-10",
"Done?": true
}
},
{
"id": "row-2",
"values": {
"Task": "Refactor output panels",
"Status": "Active",
"Owner": "Kjetil",
"Effort points": 5,
"Due date": "2026-06-12",
"Done?": false
}
},
{
"id": "row-3",
"values": {
"Task": "Add summary polish",
"Status": "Review",
"Owner": "Future me",
"Effort points": 4,
"Due date": "2026-06-13",
"Done?": false
}
},
{
"id": "row-4",
"values": {
"Task": "Try another schema",
"Status": "Planned",
"Owner": "Visitor",
"Effort points": 2,
"Due date": "2026-06-15",
"Done?": false
}
}
] summary.json
{
"rowCount": 4,
"signedTotals": null,
"groups": [
{
"title": "Status totals - Effort points",
"totals": {
"Planned": 2,
"Done": 3,
"Review": 4,
"Active": 5
}
}
],
"columns": [
{
"column": "Task",
"values": {
"Filled": "4 / 4"
}
},
{
"column": "Status",
"values": {
"Groups": "4"
}
},
{
"column": "Owner",
"values": {
"Filled": "4 / 4"
}
},
{
"column": "Effort points",
"values": {
"Sum": "14"
}
},
{
"column": "Due date",
"values": {
"Filled": "4 / 4"
}
},
{
"column": "Done?",
"values": {
"Checked": "1 / 4"
}
}
]
} export.csv (4 rows)
"Task","Status","Owner","Effort points","Due date","Done?"
"Audit current layout","Done","Kjetil","3","2026-06-10","Yes"
"Refactor output panels","Active","Kjetil","5","2026-06-12","No"
"Add summary polish","Review","Future me","4","2026-06-13","No"
"Try another schema","Planned","Visitor","2","2026-06-15","No" Overview
A constrained, schema-driven table builder - deliberately more than a static table and deliberately less than a spreadsheet clone. You define columns with real types (text, number, money, date, time, select, checkbox, computed), enter rows in an editable sheet, and pick safe column-level operations: sums, averages, min/max, counts, income/expense handling, and grouped totals by category.
It loads with a working budget tracker: editable transactions, a category select, a money column, an income/expense type, and a computed signed amount that feeds a live summary with income, expenses, balance, spending by category, largest expense, and row count. Every edit updates everything downstream immediately.
There are no freeform formulas, no cell references, and no spreadsheet engine. Computed columns are picked from a small set of safe rules - signed amounts, differences, durations between times - configured through selects and explained in plain language next to the column.
Why it matters
Most internal tools are, at heart, a typed table with derived values: expense trackers, inventory lists, time logs, CRM-ish records, admin panels. The hard part is never rendering a grid - it's modeling user-defined schemas, keeping row data intact while the schema changes underneath it, validating without destroying input, and deriving aggregates from a single source of truth.
The product constraint is the point. Saying no to formulas and cell references is what keeps a tool like this safe to put in front of non-technical users, and what keeps the implementation maintainable instead of becoming a tenth-rate Excel.
Technical notes
Two arrays are the entire source of truth: column definitions and row records. Validation, computed cells, footer aggregates, the budget summary, the JSON schema, the row data, and the CSV preview are all pure derivations - there is no second copy of any of it.
Identity is stable everywhere: columns, rows, and select options have ids; cells are keyed by column id; select cells store option ids. Renaming labels, reordering columns, or re-labeling options can never corrupt data. Changing a column's type keeps existing values and re-validates them under the new type rather than wiping them; incompatible operations auto-clear.
Numeric cells store raw strings and parse at compute time, so invalid input is allowed to exist: it's flagged in the cell, announced to assistive tech, excluded from every aggregate, and counted as 'skipped' next to the affected summary. Computed columns may only reference non-computed columns, which structurally rules out dependency cycles.
Rendering goes through the Chameleon table primitives (sticky header, compact density, semantic caption, tfoot aggregates); on small screens the sheet becomes per-row editing cards instead of a crushed grid. Every visual property is a design token, so the whole workbench restyles itself across Modern, Retro, Rainbow, and Terminal.
What this proves
- Schema-driven UI where user-defined structure drives rendering, editing, validation, and aggregation
- Complex state without a state library: stable identities, derived-only computations, one source of truth
- Data modeling that survives schema edits - type changes, reorders, renames, deletions - without data loss
- Typed cell editing with per-type validation that flags problems instead of silently swallowing them
- Safe, picked operations as a deliberate product constraint instead of a formula engine
- Design-system discipline: table primitives, tokens only, and a mobile interaction model that adapts rather than shrinks