Skip to content
oldmangan edited this page Oct 7, 2026 · 1 revision

A board of columns whose cards drag between them. Every column is a Listbox, so cards support the same templates, live widgets, actions and keyboard moves. Added in #46.

from wapyt import KanbanColumn, KanbanConfig, ListAction, ProgressBar, ProgressBarConfig

board = self.add_kanban("main", KanbanConfig(columns=[
    KanbanColumn("todo", "To do", items=[{"id": "c1", "title": "Write spec", "badge": 3}]),
    KanbanColumn("doing", "Doing", wip_limit=3, color="#f59e0b"),
    KanbanColumn("done", "Done", color="#10b981"),
], actions=[ListAction("edit", "Edit", "mdi-pencil")], wip_strict=True, filterable=True))

ProgressBar(ProgressBarConfig(value=60, compact=True), container=board.card_body("c1"))

def moved(e):
    if not api.move_card(e["id"], e["to_column"], e["to_index"]):
        board.move_card(e["id"], e["from_column"], e["from_index"])   # put it back

board.on_move(moved)
board.on_add_card(lambda e: board.add_card(e["column"], new_card_from_server(e["column"])))

Mount it with Layout.add_kanban(id, config), or Kanban(config, container=...) / root=....

Columns

KanbanColumn(id, title, items, wip_limit, color, collapsed, width)

Field
id Reported as from_column / to_column
title Header text (set as text)
items The column's cards; see Listbox items
wip_limit Shown as "3 / 5": amber when full, red when over
color The column's top edge
collapsed Start as a narrow strip
width px; default KanbanConfig.column_width (280)
  • The header shows the title, the card count and an add button that fires on_add_card (add_card=False hides it). Collapse it with the arrow button.
  • Reorder columns by dragging a header, or focus a header and press Alt+← / Alt+→. on_column_move reports the new order; move_column restores a saved one without firing.

Cards

Cards are Listbox items: title, subtitle, meta, icon, badge, or a template with escaped {key} values (a key ending in Html is inserted as markup). card_body(id) returns an element to mount widgets into; they move with the card. Card ids are unique across the board.

  • Drag cards within and between columns by mouse or touch.
  • Keyboard: focus a card, Space picks it up, ↑ / ↓ move it, ← / → move it to the next column, Space drops, Escape puts it back.
  • WIP: with wip_strict=True a full column takes no more cards (it can still reorder its own). add_card from code can still exceed the limit, which then shows red.
  • The app has the final say: a move happens at once and fires on_move; if your server refuses, move_card puts it back without firing.
  • One card is selected across the whole board. With filterable=True, one filter box searches every column.

Options

KanbanConfig
columns KanbanColumns, left to right
id_field, template, actions As in Listbox
add_card, add_card_label The header's add button
wip_strict Full columns refuse cards
filterable, filter_placeholder One filter for the board
column_width, reorderable_columns Default width; header dragging (on by default)
empty_text, label A column's empty state; the board's accessible name

Events and methods

Event Payload
on_move {id, card, from_column, to_column, from_index, to_index}
on_select {id, card, column} (all None when cleared)
on_activate {id, card, column} on double-click or Enter
on_action {action, id, card, column}
on_add_card {column}
on_column_move {id, from_index, to_index, columns}
on_column_toggle {id, collapsed}

Methods: get_board() ({column: [cards]}), get_cards(column), get_card(id), column_of(id), add_card(column, card, index), update_card(id, fields), remove_card(id), card_body(id), move_card(id, column, index), select(id), get_selected(), get_column_ids(), move_column(id, index), set_wip_limit(column, limit), set_collapsed(column, collapsed), is_collapsed, set_filter, destroy.

Not yet

Swimlanes (rows across the columns).

Clone this wiki locally