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

mad-tabs

Abas (default/underline/vertical).

Componente de abas com 3 variantes visuais. Requer o wrapper <mad-tabs-list> em volta dos botoes de aba.

Estrutura obrigatoria

<mad-tabs default="aba1" variant="underline">
    <mad-tabs-list>
        <mad-tab name="aba1" icon="info">Aba 1</mad-tab>
        <mad-tab name="aba2" icon="settings">Aba 2</mad-tab>
        <mad-tab name="aba3" icon="users">Aba 3</mad-tab>
    </mad-tabs-list>

    <mad-tab-panel name="aba1">
        Conteudo da aba 1
    </mad-tab-panel>
    <mad-tab-panel name="aba2">
        Conteudo da aba 2
    </mad-tab-panel>
    <mad-tab-panel name="aba3">
        Conteudo da aba 3
    </mad-tab-panel>
</mad-tabs>

CRITICO: Os <mad-tab> DEVEM estar dentro de <mad-tabs-list>. Sem esse wrapper, os botoes renderizam empilhados verticalmente (o CSS .mad-tabs-list e quem aplica display:flex horizontal + underline).

Props do <mad-tabs>

Prop Tipo Default Descricao
variant string 'default' default, underline, vertical
default string '' Nome da aba ativa inicial
class string '' Classes CSS extras
style string '' Estilos inline

Props do <mad-tab>

Prop Tipo Default Descricao
name string '' Identificador da aba (deve bater com <mad-tab-panel name="...">)
icon string '' Icone Lucide
title string '' Tooltip nativo (title) do botao
disabled bool false
class string ''

<mad-tab> e <mad-tab-panel> consultam o MadRenderContext pelo name no escopo tab (MadRenderContext::isHidden($name, 'tab')) — esconder a aba pelo servidor esconde tambem o painel correspondente.

Props do <mad-tab-panel>

Prop Tipo Default Descricao
name string '' Identificador (deve bater com <mad-tab name="...">)
class string ''
style string ''

Variantes

default — botoes com fundo ativo

<mad-tabs default="perfil">
    <mad-tabs-list>
        <mad-tab name="perfil">Perfil</mad-tab>
        <mad-tab name="senha">Senha</mad-tab>
    </mad-tabs-list>
    <mad-tab-panel name="perfil">...</mad-tab-panel>
    <mad-tab-panel name="senha">...</mad-tab-panel>
</mad-tabs>

underline — linha inferior abaixo da aba ativa

<mad-tabs default="geral" variant="underline">
    <mad-tabs-list>
        <mad-tab name="geral" icon="info">Geral</mad-tab>
        <mad-tab name="versoes" icon="git-branch">Versoes</mad-tab>
        <mad-tab name="atividade" icon="activity">Atividade</mad-tab>
    </mad-tabs-list>
    <mad-tab-panel name="geral">...</mad-tab-panel>
    <mad-tab-panel name="versoes">...</mad-tab-panel>
    <mad-tab-panel name="atividade">...</mad-tab-panel>
</mad-tabs>

vertical — abas laterais (sidebar)

<mad-tabs default="profile" variant="vertical">
    <mad-tabs-list>
        <mad-tab name="profile" icon="user">Perfil</mad-tab>
        <mad-tab name="security" icon="shield">Seguranca</mad-tab>
        <mad-tab name="notifications" icon="bell">Notificacoes</mad-tab>
    </mad-tabs-list>
    <mad-tab-panel name="profile">...</mad-tab-panel>
    <mad-tab-panel name="security">...</mad-tab-panel>
    <mad-tab-panel name="notifications">...</mad-tab-panel>
</mad-tabs>

Com contador dinamico no label

<mad-tab name="versoes" icon="git-branch">Versoes ({{ count($versoes) }})</mad-tab>

Aba desabilitada

<mad-tab name="premium" disabled>Premium (indisponivel)</mad-tab>

Erro de campo em aba inativa — auto-ativacao

Um erro de validacao apontando para um campo dentro de um <mad-tab-panel> inativo ficaria invisivel: o x-show="activeTab === '...'" esconde o painel via display:none e o usuario so veria o toast, sem saber onde corrigir.

O JS do framework (Mad._revealFieldError) resolve isso sozinho: ao aplicar o erro no slot [data-field-error], ele sobe ate o [data-mad-tab]; se o painel estiver escondido, pega o .mad-tabs mais proximo e escreve activeTab no estado Alpine, ativando a aba que contem o campo com erro.

Regras do comportamento:

  • A primeira aba com erro vence. Um lock curto (100ms) no root das tabs impede que erros seguintes da mesma resposta fiquem trocando de aba.
  • Nao faz nada quando o painel ja esta visivel, quando o campo nao esta dentro de nenhuma aba, ou quando o .mad-tabs nao tem Alpine inicializado.
  • Nao exige configuracao: basta o campo estar dentro de um <mad-tab-panel> com name.

Preservar a aba ativa apos forceFullRender()

A aba ativa vive so no Alpine (x-data="{ activeTab: '...' }"). Quando uma action do MadComponent chama $this->forceFullRender(), o HTML inteiro volta do server e o Alpine re-inicializa com o valor do atributo default — se ele for estatico, a aba salta de volta pra esse valor e o usuario perde onde estava.

Solucao: transformar default em estado server-side e atualizar a prop em toda action que chama forceFullRender().

class MeuForm extends MadComponent
{
    public string $activeTab = 'profile'; // aba inicial

    public function onAlgumaAcao2FA(): MadResponse
    {
        // ... logica que exige re-render completo ...
        $this->activeTab = 'account'; // garante que o Alpine vai re-inicializar na aba certa
        $this->forceFullRender();
        return (new MadResponse())->toast('OK', 'success');
    }

    protected function view(): string|array
    {
        return ['meu-form', ['activeTab' => $this->activeTab, /* ... */]];
    }
}
<mad-tabs :default="$activeTab" variant="vertical">
    <mad-tabs-list>
        <mad-tab name="profile" icon="user">Profile</mad-tab>
        <mad-tab name="account" icon="shield">Account</mad-tab>
    </mad-tabs-list>
    <mad-tab-panel name="profile">...</mad-tab-panel>
    <mad-tab-panel name="account">...</mad-tab-panel>
</mad-tabs>

Regras praticas

  • O <mad-tab> nao dispara evento no clique — so escreve activeTab no Alpine. Por isso o server nao sabe quando o usuario troca de aba manualmente.
  • Se o re-render nunca acontece a partir de outras abas, nao precisa rastrear: basta setar $this->activeTab = '<aba da acao>' em cada action que chama forceFullRender().
  • Quando uma mesma view tem actions em varias abas que forcam re-render, voce precisa de tracking real da aba. Duas opcoes:
    1. Em cada data-mad-click dentro da aba X, chamar um $set({activeTab: 'X'}) junto ou seguir a convencao de setar a prop no inicio de cada action da aba X.
    2. Custom: emitir evento no clique do tab via attrs do <mad-tabs> (wrapper Alpine com $watch('activeTab', v => MadWire.call($el, 'onTabChange', [v]))) e registrar um onTabChange(string $tab): void { $this->activeTab = $tab; }.

NUNCA fazer

{{-- ERRADO: sem <mad-tabs-list> — botoes empilham verticalmente --}}
<mad-tabs default="a">
    <mad-tab name="a">A</mad-tab>
    <mad-tab name="b">B</mad-tab>
    <mad-tab-panel name="a">...</mad-tab-panel>
    <mad-tab-panel name="b">...</mad-tab-panel>
</mad-tabs>

{{-- CERTO: com o wrapper --}}
<mad-tabs default="a">
    <mad-tabs-list>
        <mad-tab name="a">A</mad-tab>
        <mad-tab name="b">B</mad-tab>
    </mad-tabs-list>
    <mad-tab-panel name="a">...</mad-tab-panel>
    <mad-tab-panel name="b">...</mad-tab-panel>
</mad-tabs>

{{-- ERRADO: name do <mad-tab> nao bate com <mad-tab-panel> --}}
<mad-tab name="geral">Geral</mad-tab>
<mad-tab-panel name="overview">...</mad-tab-panel>

{{-- CERTO: names devem ser iguais --}}
<mad-tab name="geral">Geral</mad-tab>
<mad-tab-panel name="geral">...</mad-tab-panel>

{{-- ERRADO: montar tabs manualmente com HTML --}}
<div class="tabs">
    <button @click="tab=1">A</button>
    <button @click="tab=2">B</button>
    <div x-show="tab===1">...</div>
</div>

{{-- CERTO: usar o componente --}}
<mad-tabs>...</mad-tabs>

Dica: padrao comum em drawers/forms

<mad-form submit="onSave">
    <mad-tabs default="dados" variant="underline">
        <mad-tabs-list>
            <mad-tab name="dados" icon="user">Dados</mad-tab>
            <mad-tab name="endereco" icon="map-pin">Endereco</mad-tab>
            <mad-tab name="contato" icon="phone">Contato</mad-tab>
        </mad-tabs-list>

        <mad-tab-panel name="dados">
            <mad-form-grid :cols="2">
                <mad-input-field name="nome" label="Nome" required />
                <mad-input-field name="cpf" label="CPF" />
            </mad-form-grid>
        </mad-tab-panel>

        <mad-tab-panel name="endereco">
            <mad-input-field name="cep" label="CEP" />
            <mad-input-field name="logradouro" label="Logradouro" />
        </mad-tab-panel>

        <mad-tab-panel name="contato">
            <mad-input-field name="email" label="Email" />
            <mad-input-field name="telefone" label="Telefone" />
        </mad-tab-panel>
    </mad-tabs>

    <mad-form-actions>
        <mad-btn type="submit" variant="primary" icon="save">Salvar</mad-btn>
    </mad-form-actions>
</mad-form>