Navegação e overlays
mad-tabs (variant default) com mad-tabs-list + mad-tab-panel.
Dados pessoais do usuário: nome, e-mail e telefone.
Formulário de troca de senha.
Preferências de e-mail e push.
<mad-tabs default="perfil">
<mad-tabs-list>
<mad-tab name="perfil" icon="user">Perfil</mad-tab>
<mad-tab name="senha" icon="lock">Senha</mad-tab>
<mad-tab name="notificacoes" icon="bell">Notificações</mad-tab>
</mad-tabs-list>
<mad-tab-panel name="perfil">
<p>Dados pessoais do usuário: nome, e-mail e telefone.</p>
</mad-tab-panel>
<mad-tab-panel name="senha">
<p>Formulário de troca de senha.</p>
</mad-tab-panel>
<mad-tab-panel name="notificacoes">
<p>Preferências de e-mail e push.</p>
</mad-tab-panel>
</mad-tabs>
variant=underline, ícones Lucide no mad-tab e disabled.
Configurações gerais do módulo.
Histórico de versões: v1.2, v1.1, v1.0.
Recurso disponível apenas no plano Premium.
<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">Versões (3)</mad-tab>
<mad-tab name="premium" icon="lock" disabled>Premium</mad-tab>
</mad-tabs-list>
<mad-tab-panel name="geral">
<p>Configurações gerais do módulo.</p>
</mad-tab-panel>
<mad-tab-panel name="versoes">
<p>Histórico de versões: v1.2, v1.1, v1.0.</p>
</mad-tab-panel>
<mad-tab-panel name="premium">
<p>Recurso disponível apenas no plano Premium.</p>
</mad-tab-panel>
</mad-tabs>
variant=vertical — abas como sidebar de configurações.
Nome, foto e biografia.
Autenticação em dois fatores e sessões ativas.
Plano atual e histórico de faturas.
<mad-tabs default="profile" variant="vertical">
<mad-tabs-list>
<mad-tab name="profile" icon="user">Perfil</mad-tab>
<mad-tab name="security" icon="shield">Segurança</mad-tab>
<mad-tab name="billing" icon="credit-card">Cobrança</mad-tab>
</mad-tabs-list>
<mad-tab-panel name="profile">
<p>Nome, foto e biografia.</p>
</mad-tab-panel>
<mad-tab-panel name="security">
<p>Autenticação em dois fatores e sessões ativas.</p>
</mad-tab-panel>
<mad-tab-panel name="billing">
<p>Plano atual e histórico de faturas.</p>
</mad-tab-panel>
</mad-tabs>
mad-segmented + mad-segmented-item com mad:click real — troca o plano ativo no servidor.
Plano selecionado: Mensal
<mad-segmented>
<div id="tutor-seg-items" style="display:contents;">
<mad-segmented-item active mad:click="onSelecionarPlano('mensal')">Mensal</mad-segmented-item>
<mad-segmented-item mad:click="onSelecionarPlano('anual')">Anual</mad-segmented-item>
<mad-segmented-item mad:click="onSelecionarPlano('vitalicio')">Vitalício</mad-segmented-item>
</div>
</mad-segmented>
<div id="tutor-seg-resumo" style="margin-top:.75rem;">
<p>Plano selecionado: <strong>Mensal</strong></p>
</div>
/**
* Alterna o plano ativo do segmented control — prova o ciclo real de
* mad:click num mad-segmented-item: reescreve os itens (classe active +
* aria-selected) e o resumo via MadResponse->html(), sem full re-render.
*/
public function onSelecionarPlano(string $plano): MadResponse
{
if (!isset(self::PLANOS[$plano])) {
return (new MadResponse())->toast('Plano inválido.', 'danger');
}
$this->planoTutor = $plano;
$label = self::PLANOS[$plano];
$itens = '';
foreach (self::PLANOS as $key => $itemLabel) {
$active = $key === $plano;
$itens .= '<button type="button" class="mad-seg-item' . ($active ? ' active' : '') . '" '
. 'role="tab" aria-selected="' . ($active ? 'true' : 'false') . '" '
. 'data-mad-click="onSelecionarPlano(\'' . $key . '\')">' . e($itemLabel) . '</button>';
}
$resumo = '<p style="margin-top:.75rem;">Plano selecionado: <strong>' . e($label) . '</strong></p>';
return (new MadResponse())
->html('#tutor-seg-items', $itens)
->html('#tutor-seg-resumo', $resumo)
->toast("Plano \"{$label}\" selecionado.", 'success');
}
mad-tab-bar não é um componente de conteúdo como os outros desta página —
é o tab-strip do SHELL do app (abas internas tipo navegador, opção
"usar abas" nas preferências do app), renderizado UMA única vez pelo
layout ao redor de #mad_main
(ShellViewModel::tabBar() →
components.tab-bar).
Embutir outro <mad-tab-bar> aqui duplicaria esse id e o
singleton global window.MadTabs (além do hook de
window.__mad_load_page), quebrando a navegação do app
inteiro — por isso esta página só documenta a tag, sem executá-la. Fonte:
packages/mad-framework/src/mad/views/components/tab-bar.blade.php.
Código-fonte completo da página
<?php
namespace App\Control\Tutor;
use App\Support\Tutor\TutorPage;
use Mad\Http\MadResponse;
/**
* TutorNavegacaoTabs — página "Abas (tabs)" do MAD Lab.
*
* Cobre mad-tabs/mad-tab (+ mad-tabs-list/mad-tab-panel, filhos obrigatórios)
* nas 3 variantes (default/underline/vertical) e mad-segmented/
* mad-segmented-item com uma ação wire real (troca de plano no servidor).
*
* mad-tab-bar (tag do catálogo) NÃO tem demo ao vivo nesta página: não é um
* componente de CONTEÚDO como os outros — é o tab-strip do SHELL do app
* (browser-like tabs internas, liga/desliga em Preferências > "usar abas"),
* renderizado UMA única vez pelo layout ao redor de #mad_main
* (ShellViewModel::tabBar() -> components.tab-bar). Embutir outro
* <mad-tab-bar> aqui duplicaria esse id + o singleton global window.MadTabs
* e o hook de window.__mad_load_page, quebrando a navegação do app inteiro —
* por isso a página só documenta essa tag (callout no fim), sem executá-la.
*/
class TutorNavegacaoTabs extends TutorPage
{
protected string $catalogKey = 'navegacao_tabs';
/** Estado do exemplo "Segmented control" — plano ativo. */
public string $planoTutor = 'mensal';
private const PLANOS = [
'mensal' => 'Mensal',
'anual' => 'Anual',
'vitalicio' => 'Vitalício',
];
/**
* Alterna o plano ativo do segmented control — prova o ciclo real de
* mad:click num mad-segmented-item: reescreve os itens (classe active +
* aria-selected) e o resumo via MadResponse->html(), sem full re-render.
*/
public function onSelecionarPlano(string $plano): MadResponse
{
if (!isset(self::PLANOS[$plano])) {
return (new MadResponse())->toast('Plano inválido.', 'danger');
}
$this->planoTutor = $plano;
$label = self::PLANOS[$plano];
$itens = '';
foreach (self::PLANOS as $key => $itemLabel) {
$active = $key === $plano;
$itens .= '<button type="button" class="mad-seg-item' . ($active ? ' active' : '') . '" '
. 'role="tab" aria-selected="' . ($active ? 'true' : 'false') . '" '
. 'data-mad-click="onSelecionarPlano(\'' . $key . '\')">' . e($itemLabel) . '</button>';
}
$resumo = '<p style="margin-top:.75rem;">Plano selecionado: <strong>' . e($label) . '</strong></p>';
return (new MadResponse())
->html('#tutor-seg-items', $itens)
->html('#tutor-seg-resumo', $resumo)
->toast("Plano \"{$label}\" selecionado.", 'success');
}
}
<x-tutor-page page="navegacao_tabs">
<x-tutor-demo demo="tutor.examples.navegacao-tabs.basico"
title="Básico" desc="mad-tabs (variant default) com mad-tabs-list + mad-tab-panel." />
<x-tutor-demo demo="tutor.examples.navegacao-tabs.underline-icones"
title="Underline + ícones + aba desabilitada" desc="variant=underline, ícones Lucide no mad-tab e disabled." />
<x-tutor-demo demo="tutor.examples.navegacao-tabs.vertical"
title="Vertical" desc="variant=vertical — abas como sidebar de configurações." />
<x-tutor-demo demo="tutor.examples.navegacao-tabs.segmented"
title="Segmented control (ação wire)" desc="mad-segmented + mad-segmented-item com mad:click real — troca o plano ativo no servidor."
php="TutorNavegacaoTabs::onSelecionarPlano" />
<mad-callout type="info" title="mad-tab-bar (documentado, sem demo ao vivo)" icon="info">
<p>
mad-tab-bar não é um componente de conteúdo como os outros desta página —
é o tab-strip do SHELL do app (abas internas tipo navegador, opção
"usar abas" nas preferências do app), renderizado UMA única vez pelo
layout ao redor de <code>#mad_main</code>
(<code>ShellViewModel::tabBar()</code> →
<code>components.tab-bar</code>).
</p>
<p>
Embutir outro <code><mad-tab-bar></code> aqui duplicaria esse id e o
singleton global <code>window.MadTabs</code> (além do hook de
<code>window.__mad_load_page</code>), quebrando a navegação do app
inteiro — por isso esta página só documenta a tag, sem executá-la. Fonte:
<code>packages/mad-framework/src/mad/views/components/tab-bar.blade.php</code>.
</p>
</mad-callout>
</x-tutor-page>