Navegação e overlays
mad-modal
Básico
mad-modal size=sm com abrir/fechar via mad-btn (open-modal / close-modal).
resources/views/tutor/examples/navegacao-modal/basico.blade.php
<mad-modal name="tutor-modal-basico" title="Confirmar exclusão" size="sm">
<p>Tem certeza que deseja excluir este registro? Esta ação não pode ser desfeita.</p>
<div style="display:flex;justify-content:flex-end;gap:.5rem;margin-top:1rem;">
<mad-btn close-modal="tutor-modal-basico" variant="ghost">Cancelar</mad-btn>
<mad-btn close-modal="tutor-modal-basico" variant="danger" icon="trash-2">Confirmar exclusão</mad-btn>
</div>
</mad-modal>
<mad-btn open-modal="tutor-modal-basico" variant="danger" icon="trash-2">Excluir registro</mad-btn>
Footer (prop string)
footer NÃO é slot nomeado — monte o HTML em @php e passe via :footer.
resources/views/tutor/examples/navegacao-modal/footer.blade.php
@php
// footer NAO e slot nomeado do <mad-modal> — e prop STRING: monta-se o
// HTML em PHP e passa via :footer. Mad.closeModal(nome) e a mesma chamada
// que o compilador gera pro atalho close-modal="nome" (ver
// MadBladeCompiler::parseParams "Overlay shortcuts").
$footer = '<button type="button" class="mad-btn mad-btn-ghost" '
. 'onclick="Mad.closeModal(\'tutor-modal-footer\')">Cancelar</button>'
. '<button type="button" class="mad-btn mad-btn-primary" '
. 'onclick="Mad.closeModal(\'tutor-modal-footer\')">OK, entendi</button>';
@endphp
<mad-modal name="tutor-modal-footer" title="Aviso de manutenção" size="md" :footer="$footer">
O sistema ficará indisponível das 02h às 04h para manutenção programada.
</mad-modal>
<mad-btn open-modal="tutor-modal-footer" variant="outline" icon="info">Ver aviso</mad-btn>
Não-dismissível (ação obrigatória)
dismissible=false + mad:click real fechando o modal.
resources/views/tutor/examples/navegacao-modal/nao-dismissivel.blade.php
<mad-modal name="tutor-modal-termos" title="Aceite obrigatório" size="md" :dismissible="false">
<p>Você precisa aceitar os termos de uso antes de continuar usando o sistema.</p>
<div style="display:flex;justify-content:flex-end;margin-top:1rem;">
<mad-btn mad:click="onAceitarTermosModal" variant="primary" icon="check">Aceitar termos</mad-btn>
</div>
</mad-modal>
<mad-btn open-modal="tutor-modal-termos" variant="outline" icon="shield-alert">Abrir aceite obrigatório</mad-btn>
app/control/Tutor/TutorNavegacaoModal.php
/** Exemplo "Não-dismissível" — só fecha depois da ação obrigatória. */
public function onAceitarTermosModal(): MadResponse
{
return (new MadResponse())
->toast('Termos aceitos — obrigado!', 'success')
->closeModal('tutor-modal-termos');
}
Tamanhos
sm (380px), md (520px, padrão), lg (680px) e xl (900px).
resources/views/tutor/examples/navegacao-modal/tamanhos.blade.php
<mad-modal name="tutor-modal-sm" title="Tamanho sm (380px)" size="sm">
Modal pequeno — confirmações rápidas.
</mad-modal>
<mad-modal name="tutor-modal-md" title="Tamanho md (520px)" size="md">
Modal médio — formulários curtos (tamanho padrão).
</mad-modal>
<mad-modal name="tutor-modal-lg" title="Tamanho lg (680px)" size="lg">
Modal grande — visualização rápida de um detalhe.
</mad-modal>
<mad-modal name="tutor-modal-xl" title="Tamanho xl (900px)" size="xl">
Modal extra grande — conteúdo mais denso (tabelas, resumos).
</mad-modal>
<div style="display:flex;gap:.5rem;flex-wrap:wrap;">
<mad-btn open-modal="tutor-modal-sm" variant="outline">sm</mad-btn>
<mad-btn open-modal="tutor-modal-md" variant="outline">md</mad-btn>
<mad-btn open-modal="tutor-modal-lg" variant="outline">lg</mad-btn>
<mad-btn open-modal="tutor-modal-xl" variant="outline">xl</mad-btn>
</div>
Modal dinâmico (ação wire)
conteúdo injetado por MadResponse->html() ANTES de openModal() — a ordem importa.
resources/views/tutor/examples/navegacao-modal/dinamico.blade.php
<div style="display:flex;gap:.5rem;flex-wrap:wrap;">
<mad-btn variant="outline" icon="eye" mad:click="onVerPedidoModal(1)">Ver pedido #1</mad-btn>
<mad-btn variant="outline" icon="eye" mad:click="onVerPedidoModal(2)">Ver pedido #2</mad-btn>
<mad-btn variant="outline" icon="eye" mad:click="onVerPedidoModal(3)">Ver pedido #3</mad-btn>
</div>
<mad-modal name="tutor-modal-pedido" title="Detalhes do pedido" size="md">
<div id="tutor-modal-pedido-content">Selecione um pedido acima para ver os detalhes.</div>
</mad-modal>
app/control/Tutor/TutorNavegacaoModal.php
/**
* Exemplo "Modal dinâmico" — o conteúdo depende de QUAL pedido foi
* clicado, então injeta o HTML no placeholder antes de abrir o modal.
*/
public function onVerPedidoModal(int $id): MadResponse
{
$pedido = self::PEDIDOS_DEMO[$id] ?? null;
if (!$pedido) {
return (new MadResponse())->toast('Pedido não encontrado.', 'danger');
}
$html = '<p><strong>Pedido:</strong> #' . $id . '</p>'
. '<p><strong>Cliente:</strong> ' . e($pedido['cliente']) . '</p>'
. '<p><strong>Valor:</strong> R$ ' . number_format($pedido['valor'], 2, ',', '.') . '</p>'
. '<p><strong>Status:</strong> ' . e($pedido['status']) . '</p>';
return (new MadResponse())
->html('#tutor-modal-pedido-content', $html)
->openModal('tutor-modal-pedido');
}
Código-fonte completo da página
app/control/Tutor/TutorNavegacaoModal.php
<?php
namespace App\Control\Tutor;
use App\Support\Tutor\TutorPage;
use Mad\Http\MadResponse;
/**
* TutorNavegacaoModal — página "Modal" do MAD Lab.
*
* Cobre mad-modal: abrir/fechar por botão, footer como PROP STRING (não é
* slot nomeado), modal não-dismissível (ação obrigatória), os 4 tamanhos e
* um modal dinâmico — conteúdo injetado por MadResponse->html() ANTES de
* openModal() (ordem importa: o conteúdo precisa existir no DOM quando o
* modal abre).
*/
class TutorNavegacaoModal extends TutorPage
{
protected string $catalogKey = 'navegacao_modal';
/** Fixture local (sem banco) do exemplo "Modal dinâmico". */
private const PEDIDOS_DEMO = [
1 => ['cliente' => 'Ana Souza', 'valor' => 1240.50, 'status' => 'Enviado'],
2 => ['cliente' => 'Bruno Lima', 'valor' => 389.90, 'status' => 'Entregue'],
3 => ['cliente' => 'Carla Dias', 'valor' => 2110.00, 'status' => 'Novo'],
];
/** Exemplo "Não-dismissível" — só fecha depois da ação obrigatória. */
public function onAceitarTermosModal(): MadResponse
{
return (new MadResponse())
->toast('Termos aceitos — obrigado!', 'success')
->closeModal('tutor-modal-termos');
}
/**
* Exemplo "Modal dinâmico" — o conteúdo depende de QUAL pedido foi
* clicado, então injeta o HTML no placeholder antes de abrir o modal.
*/
public function onVerPedidoModal(int $id): MadResponse
{
$pedido = self::PEDIDOS_DEMO[$id] ?? null;
if (!$pedido) {
return (new MadResponse())->toast('Pedido não encontrado.', 'danger');
}
$html = '<p><strong>Pedido:</strong> #' . $id . '</p>'
. '<p><strong>Cliente:</strong> ' . e($pedido['cliente']) . '</p>'
. '<p><strong>Valor:</strong> R$ ' . number_format($pedido['valor'], 2, ',', '.') . '</p>'
. '<p><strong>Status:</strong> ' . e($pedido['status']) . '</p>';
return (new MadResponse())
->html('#tutor-modal-pedido-content', $html)
->openModal('tutor-modal-pedido');
}
}
resources/views/tutor/navegacao/modal.blade.php
<x-tutor-page page="navegacao_modal">
<x-tutor-demo demo="tutor.examples.navegacao-modal.basico"
title="Básico" desc="mad-modal size=sm com abrir/fechar via mad-btn (open-modal / close-modal)." />
<x-tutor-demo demo="tutor.examples.navegacao-modal.footer"
title="Footer (prop string)" desc="footer NÃO é slot nomeado — monte o HTML em @php e passe via :footer." />
<x-tutor-demo demo="tutor.examples.navegacao-modal.nao-dismissivel"
title="Não-dismissível (ação obrigatória)" desc="dismissible=false + mad:click real fechando o modal."
php="TutorNavegacaoModal::onAceitarTermosModal" />
<x-tutor-demo demo="tutor.examples.navegacao-modal.tamanhos"
title="Tamanhos" desc="sm (380px), md (520px, padrão), lg (680px) e xl (900px)." />
<x-tutor-demo demo="tutor.examples.navegacao-modal.dinamico"
title="Modal dinâmico (ação wire)" desc="conteúdo injetado por MadResponse->html() ANTES de openModal() — a ordem importa."
php="TutorNavegacaoModal::onVerPedidoModal" />
</x-tutor-page>