Element: #431 Board (foundation/drag-and-drop) What it shows: A three-column board: cards reorder inside a column and move between columns by mouse, touch or keyboard. When to use: Kanban boards, status pipelines and any set of lists whose items move from one to another. Spec: - Every column is `{@attach dnd.sortable(() => ({ id: col, group: 'board',`; lists with the same `group` accept each other's cards - `onDrop` receives the moved id with its `from` and `to` positions and applies it with `applyMove(lists, e, (c) => c.id)` - Keyboard: Space lifts the card, ArrowUp and ArrowDown move it inside the column, ArrowLeft and ArrowRight jump to the next column, Enter drops, Escape cancels - Each card is `
  • import { applyMove, DndLayer, useDnd, type DndDropEvent } from '@codeatlasdev/ui'; type Card = { id: string; title: string }; const names: Record = { todo: 'To do', doing: 'In progress', done: 'Done' }; let lists = $state>({ todo: [{ id: 'c1', title: 'Design the empty state' }, { id: 'c2', title: 'Audit contrast' }], doing: [{ id: 'c3', title: 'Wire the export menu' }], done: [{ id: 'c4', title: 'Ship dark mode' }], }); const dnd = useDnd(); function onDrop(e: DndDropEvent) { lists = applyMove(lists, e, (c) => c.id); } const title = (id: string) => Object.values(lists).flat().find((c) => c.id === id)?.title ?? id;
    {#each Object.keys(lists) as col (col)}

    {names[col]} {lists[col].length}

      ({ id: col, group: 'board', label: names[col], getItems: () => lists[col].map((c) => c.id), getLabel: title, onDrop }))} > {#each lists[col] as card (card.id)}
    • {/each}
    {/each}