Overlay - Stacking
OverlayEl sistema overlay soporta abrir overlays encima de otros overlays. El stack sigue un modelo LIFO gestionado por Alpine.
Demo en vivo
Prueba el stacking de overlays. Abre el wizard para ver como se apilan y navegan.
En el wizard, usa Siguiente para apilar, Anterior para volver al paso previo, y Cerrar todo para limpiar el stack completo.
Como funciona el stack
Modelo LIFO (last-in, first-out) para apilar overlays.
Al abrir un overlay mientras otro esta visible:
- El ID del overlay actual se agrega al array
stack. - El contenido actual hace transicion de salida (300ms).
- El nuevo overlay hace transicion de entrada y se convierte en el
currentactivo. - El backdrop permanece visible durante todo el proceso (sin parpadeo).
Al cerrar el overlay superior:
- Si el stack no esta vacio, el overlay previo se restaura como activo.
- Si el stack esta vacio, todo se cierra (backdrop desaparece, scroll del body se restaura).
Los overlays en el stack mantienen su estado de componente Livewire. Siguen montados en el servidor; solo la visibilidad Alpine cambia.
Abrir overlays anidados
Desde dentro de un overlay, despacha kore:open igual que desde una pagina.
{{-- Dentro de la vista Blade de un overlay --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.nested-detail',
arguments: { itemId: {{ $itemId }} }
})">
Ver detalles
</button>{{-- Dentro de la vista Blade de un overlay --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.nested-detail',
arguments: { itemId: {{ $itemId }} }
})">
Ver detalles
</button>public function openNested(): void
{
$this->dispatch('kore:open',
name: 'overlays.nested-detail',
arguments: ['itemId' => $this->itemId]
);
}public function openNested(): void
{
$this->dispatch('kore:open',
name: 'overlays.nested-detail',
arguments: ['itemId' => $this->itemId]
);
}closeWith y eventos
Despacha uno o mas eventos Livewire y luego cierra el overlay. Es la forma principal de comunicar resultados al padre.
public function confirm(): void
{
$this->closeWith(['order-confirmed']);
}public function confirm(): void
{
$this->closeWith(['order-confirmed']);
}// 1. Evento global (string)
$this->closeWith(['user-updated']);
// -> Livewire.dispatch('user-updated')
// 2. Evento global con parametros (array)
$this->closeWith([
['user-updated', ['userId' => $this->userId, 'name' => $this->name]],
]);
// -> Livewire.dispatch('user-updated', { userId: 5, name: 'John' })
// 3. Evento dirigido a componente (class => string)
use App\Livewire\UserList;
$this->closeWith([
UserList::class => 'refresh',
]);
// -> Livewire.dispatchTo('user-list', 'refresh')
// 4. Evento dirigido con parametros (class => array)
$this->closeWith([
UserList::class => ['user-saved', ['userId' => $this->userId]],
]);
// -> Livewire.dispatchTo('user-list', 'user-saved', { userId: 5 })// 1. Evento global (string)
$this->closeWith(['user-updated']);
// -> Livewire.dispatch('user-updated')
// 2. Evento global con parametros (array)
$this->closeWith([
['user-updated', ['userId' => $this->userId, 'name' => $this->name]],
]);
// -> Livewire.dispatch('user-updated', { userId: 5, name: 'John' })
// 3. Evento dirigido a componente (class => string)
use App\Livewire\UserList;
$this->closeWith([
UserList::class => 'refresh',
]);
// -> Livewire.dispatchTo('user-list', 'refresh')
// 4. Evento dirigido con parametros (class => array)
$this->closeWith([
UserList::class => ['user-saved', ['userId' => $this->userId]],
]);
// -> Livewire.dispatchTo('user-list', 'user-saved', { userId: 5 })$this->closeWith([
'global-notification',
UserList::class => 'refresh',
['audit-log', ['action' => 'updated']],
]);$this->closeWith([
'global-notification',
UserList::class => 'refresh',
['audit-log', ['action' => 'updated']],
]);Stacking de tipos mixtos
Overlays de diferentes tipos se pueden apilar libremente. La posicion, tamano y animacion se actualizan automaticamente.
{{-- Pagina: abrir un drawer --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.settings-drawer'
})">
Configuracion
</button>
{{-- Dentro de settings-drawer: abrir un confirm --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.reset-confirm'
})">
Restablecer valores
</button>
{{-- Dentro de reset-confirm: abrir fullscreen --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.preview-fullscreen'
})">
Vista previa
</button>{{-- Pagina: abrir un drawer --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.settings-drawer'
})">
Configuracion
</button>
{{-- Dentro de settings-drawer: abrir un confirm --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.reset-confirm'
})">
Restablecer valores
</button>
{{-- Dentro de reset-confirm: abrir fullscreen --}}
<button x-on:click="$dispatch('kore:open', {
name: 'overlays.preview-fullscreen'
})">
Vista previa
</button>Estado del stack:
[settings-drawer, reset-confirm] con preview-fullscreen como actual.
Cerrar fullscreen retorna al confirm; cerrar confirm retorna al drawer.
Ejemplo: wizard multi-paso
Flujo de pasos donde cada paso es un overlay que abre el siguiente.
class WizardStep1 extends OverlayComponent
{
public function next(): void
{
$this->dispatch('kore:open',
name: 'overlays.wizard-step2',
arguments: ['data' => $this->formData]
);
}
public function render()
{
return view('livewire.overlays.wizard-step1');
}
}
class WizardStep2 extends OverlayComponent
{
public function back(): void
{
// Cierra step 2, retorna a step 1
$this->close();
}
public function finish(): void
{
// Guarda y cierra todo
$this->closeAll();
}
public function render()
{
return view('livewire.overlays.wizard-step2');
}
}class WizardStep1 extends OverlayComponent
{
public function next(): void
{
$this->dispatch('kore:open',
name: 'overlays.wizard-step2',
arguments: ['data' => $this->formData]
);
}
public function render()
{
return view('livewire.overlays.wizard-step1');
}
}
class WizardStep2 extends OverlayComponent
{
public function back(): void
{
// Cierra step 2, retorna a step 1
$this->close();
}
public function finish(): void
{
// Guarda y cierra todo
$this->closeAll();
}
public function render()
{
return view('livewire.overlays.wizard-step2');
}
}Ejemplo: skipBack en stack profundo
Salta multiples overlays en el stack para ir directamente a uno anterior.
// Stack: [Step1, Step2, Step3] — Step3 es actual
class WizardStep3 extends OverlayComponent
{
public function backToStart(): void
{
// Salta Step2, va directamente a Step1
$this->skipBack(1);
}
public function backToStartAndCleanUp(): void
{
// Salta Step2 y destruye su estado, va a Step1
$this->skipBack(1, destroy: true);
}
public function render()
{
return view('livewire.overlays.wizard-step3');
}
}// Stack: [Step1, Step2, Step3] — Step3 es actual
class WizardStep3 extends OverlayComponent
{
public function backToStart(): void
{
// Salta Step2, va directamente a Step1
$this->skipBack(1);
}
public function backToStartAndCleanUp(): void
{
// Salta Step2 y destruye su estado, va a Step1
$this->skipBack(1, destroy: true);
}
public function render()
{
return view('livewire.overlays.wizard-step3');
}
}Con destroy: true, los overlays saltados tienen su estado de componente Livewire eliminado del servidor, liberando recursos.