mad-drawer
Painel lateral overlay.
Painel lateral overlay. Usar para formularios de edicao, filtros avancados, detalhes e visualizadores dinamicos. Escuta o evento maddrawer (window) com { name, action: 'open'|'close' }.
Props
| Prop | Tipo | Default | Descricao |
|---|---|---|---|
| name | string | '' | Identificador unico (obrigatorio para abrir/fechar por nome) |
| title | string | '' | Titulo do header |
| subtitle | string | '' | Subtitulo (acima do titulo) |
| icon | string | '' | Icone Lucide no header |
| side | string | 'right' | right ou left |
| size | string | 'lg' | sm (320px), md (420px), lg (560px), xl (720px), full (100vw) ou valor CSS direto ('800px') |
| dismissible | bool | true | Permite fechar clicando no overlay e botao X |
| class | string | '' | Classes CSS extras |
Teleport para o body
O overlay do drawer é renderizado dentro de <template x-teleport="body">:
ele escapa de qualquer containing block de ancestral (transform, filter,
contain), que de outro modo prenderia o position:fixed dentro do
<mad-page-content> e cortaria o painel.
Consequência prática: o conteúdo do drawer sai do lugar onde você o
declarou no DOM. Código que procura elementos do drawer por
wrapper.querySelector(...) a partir do container original não acha nada —
por isso o df_field_error de <mad-detail-form mode="drawer"> tem fallback
global por [data-df-fields][data-df-name="<nome>"] (ver detail-form.md).
Ao escrever seletores para conteúdo de drawer, use busca global.
Abrir/fechar — sempre via API MAD
Pelo template (botao)
<mad-btn open-drawer="filtro-avancado" icon="sliders-horizontal">Filtro</mad-btn>
<mad-btn close-drawer="filtro-avancado">Fechar</mad-btn>
Pelo PHP (MadResponse)
return (new MadResponse())
->openDrawer('filtro-avancado');
return (new MadResponse())
->toast('Salvo!', 'success')
->closeDrawer(); // sem nome = fecha o drawer atual
MadComponent como drawer (wrapper=DRAWER)
Quando o MadComponent inteiro e um drawer (formulario navegavel via navigate="Classe"), declarar $wrapper = self::DRAWER na propria classe — sem precisar de <mad-drawer> no Blade. O framework injeta o wrapper automaticamente.
class PedidoVendaForm extends MadComponent
{
protected static string $wrapper = self::DRAWER;
protected static string $title = 'Pedido';
protected static string $size = '900px';
protected static string $side = 'right';
}
{{-- Em outra pagina/listagem: navigate detecta o wrapper e abre como drawer --}}
<mad-btn navigate="PedidoVendaForm" method="onEdit" params="{id: 42}" icon="pencil">Editar</mad-btn>
Exemplo — filtro avancado de listagem
<mad-btn variant="outline" icon="sliders-horizontal" open-drawer="filtros">Filtro Avancado</mad-btn>
<mad-drawer name="filtros" title="Filtros" icon="sliders-horizontal" size="md">
<mad-form submit="onReload">
<mad-form-stack>
<mad-daterange-field name_start="dtIni" name_end="dtFim" label="Periodo" presets />
<mad-dbcombo-field name="statusFiltro" label="Status"
model="Estado" display="nome" placeholder="Todos" />
</mad-form-stack>
<mad-form-actions>
<mad-btn type="submit" variant="primary" icon="search">Aplicar</mad-btn>
<mad-btn variant="ghost" icon="x-circle" mad:click="onLimpar">Limpar</mad-btn>
</mad-form-actions>
</mad-form>
</mad-drawer>
Exemplo — formulario de edicao em drawer (declarativo)
<mad-drawer name="edit-cliente" title="Editar Cliente" icon="user" size="lg">
<mad-form submit="onSave">
<mad-form-section title="Dados" icon="user">
<mad-form-grid :cols="2">
<mad-input-field name="nome" label="Nome" required />
<mad-input-field name="email" label="Email" />
</mad-form-grid>
</mad-form-section>
<mad-form-actions>
<mad-btn type="submit" variant="primary" icon="save">Salvar</mad-btn>
</mad-form-actions>
</mad-form>
</mad-drawer>
public function onSave(): MadResponse
{
// ... salvar ...
return (new MadResponse())
->toast('Cliente salvo!', 'success')
->closeDrawer();
}
Exemplo — drawer dinamico (conteudo injetado por acao)
Quando o conteudo depende de qual linha o usuario clicou (trace, JSON, detalhe), declare um placeholder vazio e injete via MadResponse::html() antes de abrir.
<mad-drawer name="detalhe-viewer" title="Detalhes" icon="eye" size="lg">
<div id="detalhe-viewer-content"></div>
</mad-drawer>
public function onVerDetalhe(int $id): MadResponse
{
$log = SystemSqlLog::findOrFail($id); // leitura não precisa de transação
$html = '<pre>' . htmlspecialchars($log->log_trace) . '</pre>';
return (new MadResponse())
->html('#detalhe-viewer-content', $html) // injeta ANTES
->openDrawer('detalhe-viewer'); // depois abre
}
A ordem importa: html() antes de openDrawer() — o conteudo precisa estar no DOM quando o drawer animar.
Resumo de decisao
| Cenario | Abordagem |
|---|---|
Form/pagina inteira que abre como drawer via navigate |
MadComponent com $wrapper = self::DRAWER |
| Drawer com conteudo fixo (filtro, form curto) | <mad-drawer name="x"> no Blade + open-drawer="x" |
| Drawer com conteudo que muda por linha (trace, JSON) | Placeholder + MadResponse->html('#id', $html)->openDrawer('x') |
| Fechar drawer apos salvar | MadResponse->closeDrawer() (sem nome fecha o atual) |
| Abrir/fechar pelo botao | <mad-btn open-drawer="nome"> / <mad-btn close-drawer="nome"> |
NUNCA fazer
{{-- ERRADO: dispatch manual de evento --}}
<mad-btn :attrs="'onclick=window.dispatchEvent(new CustomEvent(...))'">Abrir</mad-btn>
{{-- CERTO --}}
<mad-btn open-drawer="meu-drawer">Abrir</mad-btn>
{{-- ERRADO: renderizar conteudo dinamico via prop do componente — prop esta vazia no render inicial --}}
<mad-drawer name="trace" title="Trace" size="lg">
{!! $__component->traceHtml !!}
</mad-drawer>
{{-- CERTO: placeholder + MadResponse->html() --}}
<mad-drawer name="trace" title="Trace" size="lg">
<div id="trace-content"></div>
</mad-drawer>
// ERRADO: script() manual para abrir drawer
return (new MadResponse())->script("window.dispatchEvent(new CustomEvent('maddrawer',...))");
// CERTO
return (new MadResponse())->openDrawer('meu-drawer');
// ERRADO: openDrawer ANTES de injetar conteudo — drawer abre vazio
return (new MadResponse())
->openDrawer('viewer')
->html('#viewer-content', $html);
// CERTO: html primeiro, openDrawer depois
return (new MadResponse())
->html('#viewer-content', $html)
->openDrawer('viewer');
// ERRADO: montar drawer manualmente em controller que ja e MadComponent navegavel
class PedidoForm extends MadComponent {
// sem $wrapper, e o Blade tem <mad-drawer> envolvendo tudo
}
// CERTO: declarar $wrapper = self::DRAWER e deixar o framework wrappar
class PedidoForm extends MadComponent {
protected static string $wrapper = self::DRAWER;
}