Skip to content

ui-kanban

Purpose
Renders a collection as state-grouped kanban columns: each column represents one state, each card is a factory_data row. Dragging a card between columns writes the row's new state.

When to use / when NOT
Use when you need to visualize and manually manage rows by state (workflows, task boards, pipelines). NOT for read-only state visualization; NOT when state changes must be guarded or restricted.

YAML shape

component: kanban
data:
  collection: <name>        # ALL rows; NO state filter
config:
  columns:                  # Ordered list; order = display order
    - { state: <name>, label: <text>, color: <hex|token>?, empty_text: <text>? }
children:
  - component: card         # exactly one, per-row template

Config keys - columns (required): List of column definitions, each mapping to one state; display order matches list order. - state (required): State value for this column. - label (required): Column header text. - color (optional): Hex color or token for column header top border. - empty_text (optional): Message shown when column has no cards. - children (required): Exactly one child, component: card. Card leaves read fields flat (field: title, not field: row.title). Row metadata exposed as _key, _state, _updated_at.

Data & events - Fetches ALL rows in collection, groups by state; no state filter in data:. - Cards within a column ordered by updated_at, most recent first. - Rows with unmatched states are hidden. - Drop dispatches save_data_item with target state, no data payload — state-only PUT fires {factory}.{collection}.{state} NOTIFY. - No transition guards; any card can move to any column. - Optimistic UI: card appears in target column immediately; server mismatch rolls back after timeout; concurrent server-side state wins. - Drags on interactive leaves (button, a, input, textarea, select) suppressed — clicks not swallowed by drag.

Example

component: kanban
data:
  collection: tasks
config:
  columns:
    - { state: todo, label: To do }
    - { state: in_progress, label: In progress, color: "#f59e0b" }
    - { state: done, label: Done, empty_text: Nothing done yet }
children:
  - component: card
    children:
      - component: markdown
        config: { field: title }

Gotchas - Must provide at least one column. - children must be exactly one card component. - No state filter in data: — kanban fetches and groups all rows. - Rows whose state doesn't match any declared column are silently hidden. - No state transition hooks or guards; moves are optimistic and fire-and-forget.