# Data grid

A table you edit in place, the way a spreadsheet is edited. One cell is in focus at a time and the arrow keys move between them; Enter or F2 opens a cell for editing, Escape puts it back, and Tab keeps the edit and moves on. Paste a block of cells from a spreadsheet and it fills a block of the grid. Nothing is saved by typing: a row with changes keeps them as a draft, marks the cells that changed, lights a lamp, says “Edited” and carries its own Save and Discard keys.

Kit part · With the Kit · https://tactileui.dev/parts/data-grid

> The code and styling for this part come with the Tactile UI Kit, which is in the Founder Pass. Sign in if you have it. Sign in: https://tactileui.dev/auth/sign-in The price: https://tactileui.dev/pricing

## Anatomy

The kit’s table, field and keys. The grid is the table you already have, given roles and a last cell; an edit is the kit’s field laid into the cell, a changed cell is a corner tick and a heavier weight, and the lamp is the status lamp. It has no colour of its own: the only colour on it is the amber lamp, beside a word.

## Variants

- At rest · nothing edited
- One row with changes · lamp, Save and Discard
- A cell open for editing
- An invalid edit says why
- Narrow · scrolls inside itself, the last cell stays

## States

- Rest
- Cell in focus
- Edited
- Open
- Read only

## Props and options

| Name | Kind | Description |
| - | - | - |
| th[data-key][data-type][data-required][data-readonly] | attribute |  |
| th[data-min][data-max][data-integer][data-prefix][data-suffix] | attribute | A value outside them is refused with a sentence. |
| tr[data-id][data-flags] | attribute | A row. data-id names it in events; data-flags (“unpaid today”) is what a toolbar’s filter keys match. |
| rows · dirty · columns · counts | property |  |
| commit(id) · fail(id, message) · discard(id) · filter(query) · setFlags(flags) · setColumns(hidden) | method | Make a draft the row; say a save did not work; put a row back; and what a table toolbar drives. |
| gridPaste() · gridParse() · gridFormat() · gridValidate() · gridMove() · gridPasteSays() · dataGridHTML() | function | Tab-separated text as rows, a cell’s text as a value, a value as printed, whether a text is valid for a column (and why not), where a key leads, what is said after a paste, and the markup. |

## Usage notes

Tab into the grid, then use the arrow keys. Press Enter or F2 (or just type) to edit a cell, Enter to keep it and go down, Tab to keep it and go right, Escape to put it back. Change a cell and its row lights a lamp with Save and Discard. Press Paste a sample block to fill several cells at once; a number that is not a number is skipped and said so.

## Accessibility

- It is a WAI-ARIA grid with row and column counts. The grid is one tab stop; the arrow keys, Home and End, Ctrl+Home and Ctrl+End and PageUp and PageDown move the cell in focus, which is drawn with the focus ring laid inside its edges. Enter or F2 edits, any letter starts an edit that replaces the cell, Escape puts it back, and Tab inside an edit keeps it and moves to the next cell.
- Nothing is saved by typing. A row with changes is a draft: each changed cell is marked with a tick at its corner and printed heavier, and says “edited, was 3” to a screen reader. The row’s last cell lights an amber lamp and says Edited in words, with Save and Discard keys that name the row (“Save Gull’s Rest”). The saved, discarded and failed outcomes are each said once, politely.
- A value that is not allowed is refused in a sentence beside the open cell (“Nights must be a whole number”), set as the field’s description and said aloud; the cell stays open until it is fixed or Escape puts it back.
- A paste of tab-separated cells fills a block from the cell in focus. Read-only cells and invalid values are skipped, and the result says how many cells changed and why any were skipped. A single cell can be copied with the usual copy keys.
- The table scrolls inside itself and keeps the row’s last cell at the right edge, so Save and Discard are never scrolled away, and the page never scrolls sideways.

## Related parts

- [Table](https://tactileui.dev/parts/table): Rows of data with printed column labels, figures right-aligned in columns, and status in lamps with words.
- [Data table](https://tactileui.dev/parts/data-table): The kit’s table with the tools a working one needs: headers that sort, a filter well, a check key on every row with one in the header that selects them all and sinks half way when only some are chosen, and a header that stays at the top while the rows scroll under it. While rows are chosen a plate slides out of a slot under the tools, its lamp lit, carrying what can be done to them. Without the script it is the plain table.
- [Table toolbar](https://tactileui.dev/parts/table-toolbar): The strip over a table: a search well, filter keys that latch, a menu to choose which columns show, and the count of rows in view (“8 of 8 bookings”). It drives a data table or a data grid through the methods and events they share, so the table and its toolbar stay two parts. It tells the page about every change too, for a table that asks a server.
- [Inline edit](https://tactileui.dev/parts/inline-edit): A printed label that becomes a field when you press it. At rest it is plain printing, so a page of them reads as text; under the pointer an outline and a pencil say it can be changed. Pressed, it is let into the plate as the field, in the same type, so the words stay where they were. Enter prints the new words back and Escape puts the old ones back.
