You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(islands): add board.kanban with browser update actions (#46)
Add a full-bleed kanban island that groups rows by a column field and
persists card moves through a manifest update action. Extend /api/action
to support match+set updates (insert remains for form.entry). Dogfood on
the operations example Incident board.
Co-authored-by: Luka Isailovic <23192278+lukaisailovic@users.noreply.github.com>
Copy file name to clipboardExpand all lines: apps/docs/content/docs/islands/overview.mdx
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -80,6 +80,7 @@ Each type links to its section with a live preview and full configuration.
80
80
|[`content.editor`](/islands/content-and-layout#contenteditor)| (none) | A full-page workspace to browse and edit markdown files (`file` or `dir`); no data. |
81
81
|[`layout.row`](/islands/content-and-layout#layoutrow)|`islands`| A full-width structural row holding other islands. |
82
82
|[`form.entry`](/islands/content-and-layout#formentry)|`action`| A data-entry form bound to an action; one typed input per field inserts a row. No data binding of its own. |
83
+
|[`board.kanban`](/islands/content-and-layout#boardkanban)|`dataset`, `key`, `column`, `titleField`| A full-bleed kanban board; drag/select moves persist via an update action. |
83
84
84
85
One layout construct isn't in the table because it isn't an island: a page can split into tabbed
85
86
[`groups`](/islands/content-and-layout#tabs-groups). Groups are page structure, not a tile, so
@@ -730,6 +730,26 @@ A data-entry form card bound to a manifest `action` — renders one typed input
730
730
|`fields`|`array of string`| no | the action's columns to render as inputs, in this order; omit to show every insertable column. Each must be a column of the action's dataset |
731
731
|`submitLabel`|`string`| no | text on the submit button; defaults to "Add" |
732
732
733
+
### `board.kanban`
734
+
735
+
A full-bleed kanban board grouping rows into columns by a status/stage field — use for work queues, incidents, or pipeline stages. Pick this over `table.grid` / `status.grid` when cards should move between columns, and over `form.entry` when the interaction is reassigning an existing row rather than inserting one. Moving a card POSTs match+set through the bound update action; without `action` or with `readOnly`, the board is view-only.
736
+
737
+
**Span:** min 6, recommended 12, max 12 (of 12). Omit `span` to render at the recommended width; below min or above max is a named error.
738
+
739
+
| Field | Type | Required | Description |
740
+
| --- | --- | --- | --- |
741
+
|`id`|`string`| no ||
742
+
|`title`|`string`| no ||
743
+
|`span`|`number`| no ||
744
+
|`dataset`|`string`| yes ||
745
+
|`key`|`string`| yes | field uniquely identifying each card — used as the match key when moving a card via the bound update action |
746
+
|`column`|`string`| yes | field whose value places each row into a board column (e.g. status, stage) |
747
+
|`titleField`|`string`| yes | field shown as the card title |
748
+
|`columns`|`array of string`| no | ordered column values to always show (including empty ones); omit to derive columns from distinct values in the data, in first-seen order. Values present in the data but missing here still appear after the listed ones so nothing is hidden |
749
+
|`details`|`array of string`| no | optional extra fields shown on each card under the title; each must be a column of the dataset |
750
+
|`action`|`string`| no | name of a manifest `actions` entry with mode `update` that persists a column move (match on `key`, set `column`); omit or set `readOnly` to disable moves |
751
+
|`readOnly`|`boolean`| no | disable drag/keyboard moves — a viewer only; default false when `action` is set |
752
+
733
753
## Custom islands
734
754
735
755
When no built-in fits, register a renderer in the user's project under
Copy file name to clipboardExpand all lines: docs/data-app-model.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -135,6 +135,7 @@ or read `packages/schema/src/index.ts`.
135
135
|`content.editor`| — | a full-page, data-free content workspace — browse and edit a tree of markdown files (and view CSVs) Obsidian-style; exactly one of `file` (one doc under `data/` or `docs/`) or `dir` (a directory, recursed) — both or neither is a named error; with `dir`: `include?` globs (default markdown), `csv: true` also surfaces `.csv` files as an editable table (cells + row add/delete; read-only under `readOnly`), `groups: [{id, label?, icon?, match}]` are virtual folders (globs relative to `dir`; unmatched files fall into Ungrouped); `readOnly?` makes it a viewer; edits save to the file, each save records a restorable version under `.openislands/editor.sqlite`; renders full-bleed and binds no dataset |
136
136
|`layout.row`|`islands`| a full-width structural row holding other islands — children render on their own 12-column grid row; no `span`/`title`, no nesting, no data binding |
137
137
|`form.entry`|`action`| a data-entry form bound to a manifest action; one typed input per field + a submit button that inserts a row (types/enums/defaults come from the action); optional `fields` subset/order and `submitLabel`; binds no dataset |
138
+
|`board.kanban`|`dataset`, `key`, `column`, `titleField`| a full-bleed kanban board grouping rows by `column`; cards show `titleField` (+ optional `details`); `columns?` ordered values (empty columns still render); `action?` an update action (match on `key`, set `column`) so drag/select moves persist; `readOnly?` or no action disables moves |
errors.push({kind: "island",page: page.id, index, type,field: "action",message: `unknown action '${actionName}' — declare it in manifest.actions`});
1127
+
}elseif(action.mode!=="update"){
1128
+
errors.push({kind: "island",page: page.id, index, type,field: "action",message: `action '${actionName}' must use mode 'update' (got '${action.mode}') so card moves can patch the column field`});
1129
+
}elseif(action.dataset!==config.dataset){
1130
+
errors.push({kind: "island",page: page.id, index, type,field: "action",message: `action '${actionName}' targets dataset '${action.dataset}' but the board binds '${config.datasetasstring}'`});
1131
+
}
1132
+
// fall through to binding checks on dataset columns
0 commit comments