Wrappers (MODAL/DRAWER/INTERNAL/SPA)
Como o framework embrulha o componente conforme contexto.
Todo MadComponent declara um wrapper — define COMO ele é
apresentado ao chamar show(), sem que a view precise saber nada sobre modal,
drawer ou chrome de página. A classe base MadComponent expõe exatamente três
constantes para isso: INTERNAL, MODAL e DRAWER.
Os 3 wrappers
| Const | Chrome | Uso típico |
|---|---|---|
self::INTERNAL |
Nenhum — renderiza direto no main da página | Listagens, dashboards, MadSitePage (este portal de docs usa INTERNAL). |
self::MODAL |
Modal centralizado, com overlay e título | Confirmações, formulários curtos, pickers. |
self::DRAWER |
Painel lateral overlay, com título e lado configurável | Formulários longos, edição detalhada. |
DRAWER — exemplo
class ProdutoForm extends MadComponent
{
protected static string $wrapper = self::DRAWER;
protected static string $title = 'Produto';
protected static string $size = '900px'; // preset (sm/md/lg/xl) ou CSS direto
protected static string $side = 'right'; // right | left
public MadForm $form;
public function mount(): void
{
$this->form = new MadForm('form');
}
public function onSave(): MadResponse
{
// ...
return (new MadResponse())->toast('Salvo!', 'success')->closeDrawer();
}
protected function view(): string|array
{
return 'admin.produto-form';
}
}
MODAL — exemplo
class ConfirmarExclusao extends MadComponent
{
protected static string $wrapper = self::MODAL;
protected static string $title = 'Confirmar exclusão';
protected static string $size = 'sm';
public int $registroId = 0;
public function mount(int $id): void
{
$this->registroId = $id;
}
public function onConfirm(): MadResponse
{
Produto::findOrFail($this->registroId)->delete();
return (new MadResponse())->toast('Excluído!', 'success')->closeModal();
}
protected function view(): string|array
{
return 'admin.confirmar-exclusao';
}
}
INTERNAL — exemplo
Listagens, dashboards e páginas full-content usam INTERNAL: nenhum chrome além do que a própria view declarar.
class ProdutoListagem extends MadDataGrid
{
// MadDataGrid ja' usa INTERNAL por padrao — sem chrome extra alem do que
// VOCE colocar na view (<mad-page-container>, <mad-page-header>, etc).
protected static string $wrapper = self::INTERNAL;
protected string $model = Produto::class;
}
Páginas públicas reativas (MadSitePage, base de todo conteúdo deste
portal) forçam INTERNAL — o casco visual vem do layout público
(public.docs-fw.layout), não do MadComponentWrapper.
Tamanhos do modal/drawer
Os presets sm/md/lg/xl mapeiam para larguras diferentes em cada componente — modal e drawer têm escalas próprias:
| Preset | Modal | Drawer |
|---|---|---|
sm | 380px | 320px |
md | 520px (default do componente mad-modal) | 420px |
lg | 680px | 560px (default de MadComponent::$size) |
xl | 900px | 720px |
full | — | 100vw (só drawer) |
| CSS direto | "900px", "60vw", "min(900px, 90vw)" — qualquer valor fora da lista de presets vira max-width literal. | |
Como o framework decide o chrome
Mad\Component\MadComponentWrapper::wrap() é chamado dentro de
MadComponent::_wrapRenderedHtml(), depois de dehydrate() e da
criptografia do estado — um simples match sobre getWrapper():
// Mad\Component\MadComponentWrapper::wrap() — logica real
public static function wrap(MadComponent $component, string $html): string
{
return match ($component::getWrapper()) {
MadComponent::MODAL => self::buildModal($component, $html),
MadComponent::DRAWER => self::buildDrawer($component, $html),
default => $html, // INTERNAL (e qualquer outro valor) => passthrough
};
}
MODAL renderiza _wrapper-modal.blade.php (envolve o conteúdo num
<mad-modal :name :title :size>); DRAWER renderiza
_wrapper-drawer.blade.php (<mad-drawer :name :title :size :side>).
Ambas as views fazem o mesmo housekeeping no JS: removem instâncias anteriores do mesmo
overlay no DOM, fazem teleport do wrapper para o <body> (escapa
de containing blocks de ancestrais com transform/filter),
empilham z-index por profundidade de overlay, e disparam os eventos Alpine
madmodal/maddrawer que abrem a animação de entrada.
Fechar overlays via MadResponse
return (new MadResponse())
->toast('Salvo!', 'success')
->closeDrawer() // fecha o drawer atual
->closeModal(); // fecha o modal atual
// Especificando um wrapper nomeado
return (new MadResponse())->closeDrawer('produto-form');
Quando a tela foi aberta pelo bloco "Sem resultados → Cadastrar novo" de um
<mad-dbcombo-field> (e afins), não chame
closeDrawer()/closeModal() na mão:
$this->returnToCombo($id) devolve a option nova já selecionada no
combo de origem, avisa o usuário e fecha o overlay certo para o
$wrapper da classe. Retorna null quando a tela
não veio de combo — inclusive em INTERNAL, onde a tela de origem foi
substituída e não existe <select> para onde voltar. Padrão de
uso: if (($r = $this->returnToCombo($this->recordId)) !== null) return $r;.
Companheiros: comboOrigin(): ?MadComboOrigin e
openedFromCombo(): bool.
Cobertura completa de todas as ops do MadResponse (toast, html, redirect,
openModal...) em
MadResponse — todas as ops.
Quando usar cada um
| Cenário | Wrapper recomendado |
|---|---|
| Listagem ou dashboard | INTERNAL |
| CRUD form (5+ campos) | DRAWER size="lg" |
| Confirmação destrutiva | MODAL size="sm" |
| Picker / dialog rápido | MODAL size="md" |
| Página pública (site) | MadSitePage (INTERNAL automático) |
| Detalhe rápido (não-edita) | MODAL size="lg" |