Docs›Componentes (Admin)›mad-drawer
Componentes (Admin)

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;
}