Sortable
UILista reordenable por arrastre. Wrapper fino sobre wire:sort —o x-sort en modo cliente—, la directiva que Livewire 4 trae de fábrica con SortableJS embebido: no añade ninguna dependencia de JavaScript propia.
El modo server necesita un componente Livewire real. Cada drop dispara wire:sort, que llama a tu método handler con el id movido y la posición destino; tu handler reordena el array y el nuevo estado vuelve al DOM. Sin componente Livewire alrededor, usa mode="client" para reordenar solo en el navegador, sin round-trip.
En vivo
Arrastra cualquier tarea para reordenarla. El drop llama al handler en el servidor y el array de abajo refleja el orden que Livewire guarda de verdad, no un efecto visual del navegador.
<x-kore::sortable handler="reorderSort">
@foreach($sortItems as $task)
<x-kore::sortable.item :id="$task['id']" wire:key="task-{{ $task['id'] }}">
{{ $task['name'] }}
</x-kore::sortable.item>
@endforeach
</x-kore::sortable><x-kore::sortable handler="reorderSort">
@foreach($sortItems as $task)
<x-kore::sortable.item :id="$task['id']" wire:key="task-{{ $task['id'] }}">
{{ $task['name'] }}
</x-kore::sortable.item>
@endforeach
</x-kore::sortable>El handler
El contenedor solo pide un método con esta forma: recibe el id de la fila movida y el índice destino, y decide cómo queda el array.
public array $sortItems = [
['id' => 1, 'name' => 'Diseñar wireframes'],
['id' => 2, 'name' => 'Revisión de código'],
['id' => 3, 'name' => 'Escribir tests'],
['id' => 4, 'name' => 'Deploy a staging'],
];
public function reorderSort($item, $position): void
{
$items = collect($this->sortItems);
$moved = $items->firstWhere('id', (int) $item);
if (! $moved) {
return;
}
$rest = $items->reject(fn ($i) => $i['id'] == (int) $item)->values();
$this->sortItems = $rest->slice(0, $position)->push($moved)->concat($rest->slice($position))->values()->all();
}public array $sortItems = [
['id' => 1, 'name' => 'Diseñar wireframes'],
['id' => 2, 'name' => 'Revisión de código'],
['id' => 3, 'name' => 'Escribir tests'],
['id' => 4, 'name' => 'Deploy a staging'],
];
public function reorderSort($item, $position): void
{
$items = collect($this->sortItems);
$moved = $items->firstWhere('id', (int) $item);
if (! $moved) {
return;
}
$rest = $items->reject(fn ($i) => $i['id'] == (int) $item)->values();
$this->sortItems = $rest->slice(0, $position)->push($moved)->concat($rest->slice($position))->values()->all();
}El id estable es obligatorio en modo server. Usa el id real del registro, nunca un valor generado en el render: ese mismo valor alimenta a la vez wire:sort:item —lo que el drop le manda al handler— y el wire:key que el morphing de Livewire necesita para no confundir una fila con otra al reconciliar el DOM.
Modo cliente
Sin handler ni componente Livewire alrededor: el modo cliente usa x-sort de Alpine y reordena solo en el DOM. Útil para listas que no necesitan persistirse, o como paso intermedio antes de guardar con un botón aparte.
<x-kore::sortable mode="client">
<x-kore::sortable.item id="1">Uno</x-kore::sortable.item>
<x-kore::sortable.item id="2">Dos</x-kore::sortable.item>
<x-kore::sortable.item id="3">Tres</x-kore::sortable.item>
</x-kore::sortable><x-kore::sortable mode="client">
<x-kore::sortable.item id="1">Uno</x-kore::sortable.item>
<x-kore::sortable.item id="2">Dos</x-kore::sortable.item>
<x-kore::sortable.item id="3">Tres</x-kore::sortable.item>
</x-kore::sortable>Con tirador
Con handle activado, solo se arrastra desde el icono de la izquierda: el resto de la fila deja de ser zona de arrastre, así que puede llevar botones, enlaces o cualquier control clicable sin que el drag los tape. Funciona igual en modo server: añade handle a un sortable con handler.
<x-kore::sortable mode="client" :handle="true">
<x-kore::sortable.item id="1">Uno</x-kore::sortable.item>
<x-kore::sortable.item id="2">Dos</x-kore::sortable.item>
<x-kore::sortable.item id="3">Tres</x-kore::sortable.item>
</x-kore::sortable><x-kore::sortable mode="client" :handle="true">
<x-kore::sortable.item id="1">Uno</x-kore::sortable.item>
<x-kore::sortable.item id="2">Dos</x-kore::sortable.item>
<x-kore::sortable.item id="3">Tres</x-kore::sortable.item>
</x-kore::sortable>Entre listas (grupos)
Dos contenedores con el mismo group permiten arrastrar ítems de uno a otro. Es exactamente lo que arma kore::kanban por columnas y kore::transfer entre disponibles/seleccionados, por debajo.
Por hacer
Hecho
En modo server, cada lista lleva su propio handler y comparten group: <x-kore::sortable handler="moveTodo" group="tasks"> junto a <x-kore::sortable handler="moveDone" group="tasks">. El handler que se dispara ya te dice a qué lista fue a parar el ítem —es la base sobre la que están construidos kore::kanban y kore::transfer—.
<x-kore::sortable mode="client" group="demo-grupo">
<x-kore::sortable.item id="1">Por hacer A</x-kore::sortable.item>
<x-kore::sortable.item id="2">Por hacer B</x-kore::sortable.item>
</x-kore::sortable>
<x-kore::sortable mode="client" group="demo-grupo">
<x-kore::sortable.item id="3">Hecho C</x-kore::sortable.item>
</x-kore::sortable><x-kore::sortable mode="client" group="demo-grupo">
<x-kore::sortable.item id="1">Por hacer A</x-kore::sortable.item>
<x-kore::sortable.item id="2">Por hacer B</x-kore::sortable.item>
</x-kore::sortable>
<x-kore::sortable mode="client" group="demo-grupo">
<x-kore::sortable.item id="3">Hecho C</x-kore::sortable.item>
</x-kore::sortable>Props del contenedor
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
handler
|
string|null | null | Método Livewire que recibe (item, position) en el drop |
mode
|
string | server | server (wire:sort, round-trip) o client (x-sort, solo DOM) |
group
|
string|null | null | Nombre de grupo para arrastrar entre listas (Kanban/Transfer) |
handle
|
bool | false | Muestra un tirador por fila: solo se arrastra desde ahí |
tag
|
string | div | Etiqueta HTML del contenedor: div, ul, etc. |
animation
|
int | 150 | Duración de la animación de SortableJS, en ms |
Props de item
| Prop | Tipo | Default | Descripción |
|---|---|---|---|
id
|
string | requerido en modo server | Clave estable, alimenta wire:sort:item y wire:key |