Navegação e overlays
mad-drawer
Form em drawer (ação wire)
mad-drawer com mad-form submit real — fecha via closeDrawer() e mostra toast.
resources/views/tutor/examples/navegacao-drawer/basico.blade.php
<mad-btn variant="primary" icon="user-plus" open-drawer="tutor-drawer-contato">Novo contato</mad-btn>
<mad-drawer name="tutor-drawer-contato" title="Novo contato" icon="user-plus" size="md">
<mad-form submit="onSalvarContatoDrawer">
<mad-form-grid :cols="2">
<mad-input-field name="nomeContatoDrawer" label="Nome" required icon="user" />
<mad-input-field name="emailContatoDrawer" label="E-mail" type="email" icon="mail" />
</mad-form-grid>
<mad-form-actions align="right">
<mad-btn type="submit" variant="primary" icon="save">Salvar</mad-btn>
</mad-form-actions>
</mad-form>
</mad-drawer>
app/control/Tutor/TutorNavegacaoDrawer.php
/** Exemplo "Form em drawer" — submit real fechando o drawer. */
public function onSalvarContatoDrawer(): MadResponse
{
$data = $this->form->getData();
$nome = trim((string) ($data->nomeContatoDrawer ?? ''));
return (new MadResponse())
->toast($nome !== '' ? "Contato \"{$nome}\" salvo!" : 'Contato salvo!', 'success')
->closeDrawer('tutor-drawer-contato');
}
Lados e tamanhos
side=left|right e size=sm|lg|full.
resources/views/tutor/examples/navegacao-drawer/lados-tamanhos.blade.php
<div style="display:flex;gap:.5rem;flex-wrap:wrap;">
<mad-btn variant="outline" icon="arrow-right" open-drawer="tutor-drawer-direita">Abrir à direita (lg)</mad-btn>
<mad-btn variant="outline" icon="arrow-left" open-drawer="tutor-drawer-esquerda">Abrir à esquerda (sm)</mad-btn>
<mad-btn variant="outline" icon="maximize" open-drawer="tutor-drawer-full">Abrir tela cheia</mad-btn>
</div>
<mad-drawer name="tutor-drawer-direita" title="Lado direito" side="right" size="lg">
<p>side="right" (padrão) — size="lg" (560px).</p>
</mad-drawer>
<mad-drawer name="tutor-drawer-esquerda" title="Lado esquerdo" side="left" size="sm">
<p>side="left" — size="sm" (320px).</p>
</mad-drawer>
<mad-drawer name="tutor-drawer-full" title="Tela cheia" size="full">
<p>size="full" — ocupa 100vw, útil para editores/visualizadores densos.</p>
</mad-drawer>
Não-dismissível (ação obrigatória)
dismissible=false + mad:click real fechando o drawer.
resources/views/tutor/examples/navegacao-drawer/nao-dismissivel.blade.php
<mad-btn variant="outline" icon="shield-alert" open-drawer="tutor-drawer-obrigatorio">Iniciar processo obrigatório</mad-btn>
<mad-drawer name="tutor-drawer-obrigatorio" title="Processo em andamento" icon="loader" size="sm" :dismissible="false">
<p>Este processo não pode ser cancelado pelo overlay (clique fora nem X) — confirme para concluir.</p>
<div style="display:flex;justify-content:flex-end;margin-top:1rem;">
<mad-btn mad:click="onConfirmarSaidaDrawer" variant="primary" icon="check">Concluir</mad-btn>
</div>
</mad-drawer>
app/control/Tutor/TutorNavegacaoDrawer.php
/** Exemplo "Não-dismissível" — só fecha depois da ação obrigatória. */
public function onConfirmarSaidaDrawer(): MadResponse
{
return (new MadResponse())
->toast('Processo concluído.', 'success')
->closeDrawer('tutor-drawer-obrigatorio');
}
Filtro avançado
padrão comum: drawer com formulário de filtro de listagem.
resources/views/tutor/examples/navegacao-drawer/filtro-avancado.blade.php
<mad-btn variant="outline" icon="sliders-horizontal" open-drawer="tutor-drawer-filtro">Filtro avançado</mad-btn>
<mad-drawer name="tutor-drawer-filtro" title="Filtros" icon="sliders-horizontal" size="md">
<mad-form-stack>
<mad-select-field name="statusFiltroDrawer" label="Status"
:items="['novo' => 'Novo', 'enviado' => 'Enviado', 'entregue' => 'Entregue']"
placeholder="Todos" />
<mad-daterange-field name_start="dtIniFiltroDrawer" name_end="dtFimFiltroDrawer" label="Período" presets />
</mad-form-stack>
<div style="display:flex;justify-content:flex-end;gap:.5rem;margin-top:1rem;">
<mad-btn variant="ghost" close-drawer="tutor-drawer-filtro">Cancelar</mad-btn>
<mad-btn variant="primary" icon="search" close-drawer="tutor-drawer-filtro">Aplicar</mad-btn>
</div>
</mad-drawer>
Drawer dinâmico (ação wire)
conteúdo por linha injetado via MadResponse->html() antes de openDrawer().
resources/views/tutor/examples/navegacao-drawer/dinamico.blade.php
<div style="display:flex;gap:.5rem;flex-wrap:wrap;">
<mad-btn variant="outline" icon="terminal" mad:click="onVerLogDrawer(1)">Log #1</mad-btn>
<mad-btn variant="outline" icon="terminal" mad:click="onVerLogDrawer(2)">Log #2</mad-btn>
<mad-btn variant="outline" icon="terminal" mad:click="onVerLogDrawer(3)">Log #3</mad-btn>
</div>
<mad-drawer name="tutor-drawer-log" title="Trace da query" icon="terminal" size="lg">
<div id="tutor-drawer-log-content">Selecione um log acima.</div>
</mad-drawer>
app/control/Tutor/TutorNavegacaoDrawer.php
/**
* Exemplo "Drawer dinâmico" — conteúdo depende de QUAL log foi clicado,
* então injeta o HTML no placeholder antes de abrir o drawer.
*/
public function onVerLogDrawer(int $id): MadResponse
{
$log = self::LOGS_DEMO[$id] ?? null;
if ($log === null) {
return (new MadResponse())->toast('Log não encontrado.', 'danger');
}
$html = '<pre style="white-space:pre-wrap;">' . e($log) . '</pre>';
return (new MadResponse())
->html('#tutor-drawer-log-content', $html)
->openDrawer('tutor-drawer-log');
}
Código-fonte completo da página
app/control/Tutor/TutorNavegacaoDrawer.php
<?php
namespace App\Control\Tutor;
use App\Support\Tutor\TutorPage;
use Mad\Form\MadForm;
use Mad\Http\MadResponse;
/**
* TutorNavegacaoDrawer — página "Drawer" do MAD Lab.
*
* Cobre mad-drawer: form declarativo com submit real (fecha via
* closeDrawer()), side (left/right) + tamanhos (sm/lg/full), drawer
* não-dismissível (ação obrigatória) e drawer dinâmico — conteúdo por LINHA
* (trace de log) injetado por MadResponse->html() antes de openDrawer().
*
* closeDrawer()/closeModal() SEM nome fecham "o overlay do componente atual"
* (op close_overlay) — pensado pro MadComponent com $wrapper=self::DRAWER
* (a classe INTEIRA é o drawer). Aqui os <mad-drawer name="..."> são tags
* declarativas soltas na página (várias na mesma tela), então toda ação
* fecha pelo NOME explícito — sem depender de qual foi "a última aberta".
*/
class TutorNavegacaoDrawer extends TutorPage
{
protected string $catalogKey = 'navegacao_drawer';
public MadForm $form;
/** Fixture local (sem banco) do exemplo "Drawer dinâmico". */
private const LOGS_DEMO = [
1 => "SELECT * FROM mad_iam_user WHERE id = 1\n12ms — 1 linha",
2 => "UPDATE mad_iam_user SET last_login = NOW() WHERE id = 7\n4ms — 1 linha",
3 => "DELETE FROM mad_tmp_session WHERE expires_at < NOW()\n38ms — 214 linhas",
];
public function mount(array $params = []): void
{
$this->form = new MadForm('tutor_navegacao_drawer');
}
/** Exemplo "Form em drawer" — submit real fechando o drawer. */
public function onSalvarContatoDrawer(): MadResponse
{
$data = $this->form->getData();
$nome = trim((string) ($data->nomeContatoDrawer ?? ''));
return (new MadResponse())
->toast($nome !== '' ? "Contato \"{$nome}\" salvo!" : 'Contato salvo!', 'success')
->closeDrawer('tutor-drawer-contato');
}
/** Exemplo "Não-dismissível" — só fecha depois da ação obrigatória. */
public function onConfirmarSaidaDrawer(): MadResponse
{
return (new MadResponse())
->toast('Processo concluído.', 'success')
->closeDrawer('tutor-drawer-obrigatorio');
}
/**
* Exemplo "Drawer dinâmico" — conteúdo depende de QUAL log foi clicado,
* então injeta o HTML no placeholder antes de abrir o drawer.
*/
public function onVerLogDrawer(int $id): MadResponse
{
$log = self::LOGS_DEMO[$id] ?? null;
if ($log === null) {
return (new MadResponse())->toast('Log não encontrado.', 'danger');
}
$html = '<pre style="white-space:pre-wrap;">' . e($log) . '</pre>';
return (new MadResponse())
->html('#tutor-drawer-log-content', $html)
->openDrawer('tutor-drawer-log');
}
}
resources/views/tutor/navegacao/drawer.blade.php
<x-tutor-page page="navegacao_drawer">
<x-tutor-demo demo="tutor.examples.navegacao-drawer.basico"
title="Form em drawer (ação wire)" desc="mad-drawer com mad-form submit real — fecha via closeDrawer() e mostra toast."
php="TutorNavegacaoDrawer::onSalvarContatoDrawer" />
<x-tutor-demo demo="tutor.examples.navegacao-drawer.lados-tamanhos"
title="Lados e tamanhos" desc="side=left|right e size=sm|lg|full." />
<x-tutor-demo demo="tutor.examples.navegacao-drawer.nao-dismissivel"
title="Não-dismissível (ação obrigatória)" desc="dismissible=false + mad:click real fechando o drawer."
php="TutorNavegacaoDrawer::onConfirmarSaidaDrawer" />
<x-tutor-demo demo="tutor.examples.navegacao-drawer.filtro-avancado"
title="Filtro avançado" desc="padrão comum: drawer com formulário de filtro de listagem." />
<x-tutor-demo demo="tutor.examples.navegacao-drawer.dinamico"
title="Drawer dinâmico (ação wire)" desc="conteúdo por linha injetado via MadResponse->html() antes de openDrawer()."
php="TutorNavegacaoDrawer::onVerLogDrawer" />
</x-tutor-page>