K
KoreUI

Kanban

Kanban

Tablero de columnas con tarjetas arrastrables, dentro y entre columnas: pipelines de CRM, gestión de tareas, flujos de aprobación. El arrastre usa x-sort, el plugin de Alpine que Livewire 4 trae embebido, sin dependencias nuevas.

Requiere Livewire. El arrastre llama a $wire.moveCard(...) en cada drop, así que la vista necesita un componente Livewire con el método handler —no funciona en un contexto Alpine puro sin Livewire cargado—.

Dos formas de usarlo

Igual que el DataTable: una clase base con estado, o un componente anónimo data-driven.

Esta página documenta el componente anónimo kore::kanban: el board no guarda estado propio, tú le pasas columnas y tarjetas y le dices qué método llamar en cada drop. Es la opción flexible: el board puede vivir dentro de otro componente Livewire (un dashboard, un modal), no necesita ser toda la página.

Si prefieres extender una clase y que la librería resuelva el render() por ti —el mismo patrón que KoreDataTable—, ve a KoreKanban, la clase base.

En vivo

Arrastra las tarjetas dentro de una columna para reordenarlas, o entre columnas para moverlas. Es un componente Livewire real: seis tareas en memoria, sin base de datos detrás.

Backlog
2

Investigar competidores

Analizar 3 productos del mercado

Research

Definir métricas

KPIs del trimestre

Por hacer
2

Diseñar el esquema de datos

Backend

Wireframes del panel

En curso
1

Migrar autenticación

A Livewire 4

En curso
Hecho
1

Configurar CI

Listo
<x-kore::kanban
    handler="moveCard"
    :columns="$this->columns()"
    :cards="$tasks"
/>
<x-kore::kanban
    handler="moveCard"
    :columns="$this->columns()"
    :cards="$tasks"
/>
El handler, en tu componente Livewire
public array $tasks = [
    ['id' => 1, 'column' => 'backlog', 'title' => 'Investigar competidores'],
    ['id' => 2, 'column' => 'todo', 'title' => 'Diseñar el esquema de datos'],
    // ...
];

public function moveCard($cardId, $position, $toColumn): void
{
    // $cardId = tarjeta movida · $position = índice destino dentro de $toColumn
    // $toColumn = columna destino (se conoce al renderizar)
    Task::whereKey($cardId)->update(['status' => $toColumn, 'position' => $position]);
}
public array $tasks = [
    ['id' => 1, 'column' => 'backlog', 'title' => 'Investigar competidores'],
    ['id' => 2, 'column' => 'todo', 'title' => 'Diseñar el esquema de datos'],
    // ...
];

public function moveCard($cardId, $position, $toColumn): void
{
    // $cardId = tarjeta movida · $position = índice destino dentro de $toColumn
    // $toColumn = columna destino (se conoce al renderizar)
    Task::whereKey($cardId)->update(['status' => $toColumn, 'position' => $position]);
}

Cómo funciona

El board no tiene estado: solo pinta lo que le pasas y delega el drop en tu handler.

  • columns es la lista de cabeceras: cada una con id, label y un color semántico opcional para el punto de la cabecera.
  • cards es la lista plana de tarjetas: cada una lleva column (el id de la columna a la que pertenece hoy), y el board las agrupa por columna al renderizar. Acepta array o Collection.
  • group es el grupo compartido de x-sort entre columnas: por eso una tarjeta se puede soltar en otra columna y no solo reordenarse dentro de la suya. Por defecto usa config('kore-ui.kanban.group'), así que dos boards en la misma página no se mezclan mientras no compartan grupo a propósito.
  • animation son los milisegundos de la animación de arrastre (default 150).

Los colores de columna aceptan los tokens semánticos habituales: primary, success, warning, destructive, info, secondary.

La tarjeta

Por defecto pinta title, description y un badge opcional. Con el slot, el contenido es tuyo.

Cada tarjeta se renderiza con kore::kanban.card. Sin slot, muestra title, description (si existe) y un badge con su badgeColor (si existe) —así son las seis tarjetas de la demo de arriba—. Para contenido totalmente personalizado, pásale el slot: el card conserva el wire:key y el hook de arrastre, solo cambia lo que se pinta dentro.

Tarjeta con slot propio
<x-kore::kanban.card :card="$card">
    <div class="flex items-center gap-2">
        <x-kore::avatar :name="$card['assignee']" size="sm" />
        <span class="text-sm">{{ $card['title'] }}</span>
    </div>
</x-kore::kanban.card>
<x-kore::kanban.card :card="$card">
    <div class="flex items-center gap-2">
        <x-kore::avatar :name="$card['assignee']" size="sm" />
        <span class="text-sm">{{ $card['title'] }}</span>
    </div>
</x-kore::kanban.card>

Este card individual solo se usa así dentro de un board propio hecho a mano (montando kore::kanban.column tú mismo); con el componente kore::kanban normal, dale el dato extra en cada elemento de cards y decide dentro del slot qué pintar según lo que traiga.

El handler

Un único método, invocado en cada drop, dentro o entre columnas.

moveCard($cardId, $position, $toColumn) es el nombre por defecto —cámbialo con la prop handler si ya tienes un método con ese nombre—. Se dispara tanto al reordenar dentro de una columna como al mover una tarjeta a otra: $toColumn siempre trae la columna destino, y $position el índice donde quedó dentro de ella. Persiste el cambio y en el siguiente render vuelves a exponer las tarjetas actualizadas.

wire:key por tarjeta. El board pone wire:key="kore-card-{id}" en cada tarjeta automáticamente, para que el morphing de Livewire reconcilie bien tras un drop. No hace falta que lo agregues tú.

Props

Prop Tipo Default Descripción
columns array [] Columnas: [[id, label, color?], ...]
cards array|Collection [] Tarjetas: [[id, column, title, ...], ...]
handler string moveCard Método Livewire invocado en el drop: (cardId, position, toColumn)
group string config Grupo x-sort compartido (permite arrastrar entre columnas)
animation int 150 Animación de arrastre, en ms
Cargando