@bimetal/kanban-headless
The canonical, framework-agnostic kanban controller — instance #2 of the
BIMETAL component family. Built on @bimetal/headless-core exactly like the
calendar controller, so it shares the identity-stable getSnapshot/subscribe
contract and errors-as-state surface; framework bindings are pure bindings.
createKanbanController({ store, boardId, config }) owns every board
interaction decision:
- Dialog —
openCreateCard/openEditCard/saveCard/deleteCard. - Drag/drop —
dropCard(cardId, toColumnId, toIndex)/reorderCard, withstartDrag/updateDragPreview/endDragfor live previews. - Pointer drag —
pointerDownCard/pointerMoveCard/pointerUpCard/pointerCancelCard. A mouse drags after 4 px. Atouchorpenpointer (passe.pointerType) picks the card up only afterconfig.touchHoldMs(default 450 ms) without movement. A swipe before that stays the browser's scroll (the card hastouch-action: pan-x pan-y). Once a card is picked up,touchMoveCard(e)stops the scroll; register it ontouchmovewith{ passive: false }.contextMenuCard(e)keeps the long-press menu away (bimetal-363).touchHoldMs: 0picks the card up on touchdown; a swipe that starts on a card then drags it and does not scroll (bimetal-386). - WIP-error-as-state — a rejected create/move (WIP limit) surfaces as
snapshot.lastAction(a toast) and never throws into the binding. - Undo —
undo()reverts the whole board one step;Ctrl/Cmd+ZviahandleKeydown. - Filter / selection —
setFilter,selectCard/clearSelection. - Activation — a card click (not the click trailing a drag) and
Entergo throughactivateCard: it selects the card (onSelectionChangefires), then calls the optionalonCardActivate(cardId)hook. With the hook set, or withconfig.cardDialog: false, the built-in edit dialog does not open — for a host that shows the card in its own detail view (master-detail). - Locks —
config.readOnlylocks everything, moving included.config.lockStructure(columns: add, rename, reorder, delete, WIP) andconfig.lockCardContent(cards: create, edit, delete) lock finer and keep moving cards by pointer and keyboard; undo/redo are off under any lock. The snapshot carriescanEditStructure/canEditCards/undoRedoAvailable, and the bindings hide their affordances by these.
The cut line is identical to the calendar controller: DOM measurement and
listener registration belong to the framework; every if belongs here.