Estrutura de formulário
mad-wizard
mad-wizard-step
mad-steps
Wizard básico com validação por etapa
mad-wizard + mad-wizard-step; Avançar valida só os campos da etapa atual e Concluir devolve toast com o payload.
resources/views/tutor/examples/estrutura-wizard/basico.blade.php
@php
// $that (como $__component) e uma variavel LEXICAL setada por
// MadComponent::render() no template de TOPO da pagina. Este partial e
// incluido de dentro do componente Blade nativo tutor-demo (escopo
// isolado), entao $that nao chega ate aqui sozinho — precisa ser
// reobtido via o stack estatico de MadRenderContext (push/pop em
// MadComponent::render(), atravessa qualquer aninhamento de componentes).
$that = \Mad\Component\MadRenderContext::getComponent();
@endphp
<mad-form submit="onWizardFinish">
<mad-wizard :current="$that->wizStep" variant="numbers">
<mad-wizard-step key="dados" title="Dados" icon="user" description="Nome e e-mail">
<mad-form-section title="Dados pessoais" icon="user">
<mad-form-grid :cols="2">
<mad-input-field name="nome_wiz" label="Nome completo" required icon="user" />
<mad-input-field name="email_wiz" label="E-mail" type="email" required icon="mail" />
</mad-form-grid>
</mad-form-section>
</mad-wizard-step>
<mad-wizard-step key="endereco" title="Endereço" icon="map-pin" description="Cidade">
<mad-form-section title="Endereço" icon="map-pin">
<mad-form-grid :cols="2">
<mad-input-field name="cidade_wiz" label="Cidade" required />
<mad-input-field name="uf_wiz" label="UF" maxlength="2" force-case="upper" />
</mad-form-grid>
</mad-form-section>
</mad-wizard-step>
<mad-wizard-step key="confirmacao" title="Confirmação" icon="check" description="Revisar e concluir">
<mad-form-section title="Confirmação" icon="check-circle">
<mad-callout type="info" title="Revise os dados" icon="info">
Ao concluir, os dados das 3 etapas são lidos e devolvidos num toast — nenhum registro é salvo em banco.
</mad-callout>
</mad-form-section>
</mad-wizard-step>
</mad-wizard>
<mad-separator />
<mad-form-actions align="right">
@if($that->wizStep > 1)
<mad-btn variant="ghost" icon="arrow-left" mad:click="onWizardPrev">Voltar</mad-btn>
@endif
@if($that->wizStep < 3)
<mad-btn variant="primary" icon-end="arrow-right" mad:click="onWizardNext">Avançar</mad-btn>
@else
<mad-btn variant="primary" type="submit" icon="check">Concluir</mad-btn>
@endif
</mad-form-actions>
</mad-form>
app/control/Tutor/TutorEstruturaWizard.php
/**
* Conclusão do exemplo "básico" — valida a última etapa e devolve um
* toast com o payload das 3 etapas (getDataRaw: sem schema/token, seguro
* mesmo havendo só este <mad-form> na página). Não persiste em banco.
*/
public function onWizardFinish(): MadResponse
{
try {
$this->form->validate($this->rulesForWizStep(3));
} catch (MadValidationException $e) {
return $e->asInline();
}
$data = $this->form->getDataRaw();
$resumo = trim(($data->nome_wiz ?? '') . ' | ' . ($data->email_wiz ?? '') . ' | ' . ($data->cidade_wiz ?? ''), ' |');
return (new MadResponse())
->toast("Wizard concluído: {$resumo}", 'success', 'onWizardFinish');
}
Navegação clicável no indicador
mad-wizard clickable + mad:click no indicador — só permite ir a uma etapa já alcançada.
Categoria
Escolha a categoria
2
Detalhes
Preencha os detalhes
3
Revisão
Revise e conclua
Etapa 1
Clique num círculo do indicador ACIMA já visitado pra voltar sem usar os botões — indo além da etapa alcançada mostra um aviso.
Etapa 2
Segunda etapa alcançada — o indicador já deixa clicar de volta na etapa 1.
Etapa 3
Última etapa — agora dá pra clicar em qualquer círculo do indicador.
resources/views/tutor/examples/estrutura-wizard/clicavel.blade.php
@php
// $that (como $__component) e uma variavel LEXICAL setada por
// MadComponent::render() no template de TOPO da pagina. Este partial e
// incluido de dentro do componente Blade nativo tutor-demo (escopo
// isolado), entao $that nao chega ate aqui sozinho — precisa ser
// reobtido via o stack estatico de MadRenderContext (push/pop em
// MadComponent::render(), atravessa qualquer aninhamento de componentes).
$that = \Mad\Component\MadRenderContext::getComponent();
@endphp
<mad-wizard :current="$that->wizGotoStep" variant="dots" clickable mad:click="onWizardGotoClick">
<mad-wizard-step key="1" title="Categoria" icon="tag" description="Escolha a categoria">
<mad-callout type="info" title="Etapa 1" icon="tag">
Clique num círculo do indicador ACIMA já visitado pra voltar sem usar os botões — indo além da etapa alcançada mostra um aviso.
</mad-callout>
</mad-wizard-step>
<mad-wizard-step key="2" title="Detalhes" icon="file-text" description="Preencha os detalhes">
<mad-callout type="info" title="Etapa 2" icon="file-text">
Segunda etapa alcançada — o indicador já deixa clicar de volta na etapa 1.
</mad-callout>
</mad-wizard-step>
<mad-wizard-step key="3" title="Revisão" icon="check" description="Revise e conclua">
<mad-callout type="success" title="Etapa 3" icon="check">
Última etapa — agora dá pra clicar em qualquer círculo do indicador.
</mad-callout>
</mad-wizard-step>
</mad-wizard>
<mad-separator />
<mad-form-actions align="right">
@if($that->wizGotoStep > 1)
<mad-btn variant="ghost" icon="arrow-left" mad:click="onWizardGotoPrev">Voltar</mad-btn>
@endif
@if($that->wizGotoStep < 3)
<mad-btn variant="primary" icon-end="arrow-right" mad:click="onWizardGotoNext">Avançar</mad-btn>
@endif
</mad-form-actions>
app/control/Tutor/TutorEstruturaWizard.php
/**
* Clique no indicador do exemplo "clicável" (mad-wizard clickable) —
* recebe a KEY da etapa clicada ("1".."3", posicional) e só permite ir
* pra uma etapa já alcançada (evita pular pra frente sem passar pelas
* anteriores).
*/
public function onWizardGotoClick(string $key): MadResponse
{
$step = (int) $key;
if ($step < 1 || $step > 3) {
return new MadResponse();
}
if ($step > $this->wizGotoMaxReached) {
return (new MadResponse())->toast('Complete a etapa atual antes de avançar.', 'warning');
}
$this->wizGotoStep = $step;
return new MadResponse();
}
mad-steps isolado
indicador de progresso sozinho (sem mad-wizard em volta), nas 4 variantes: numbers, dots, arrows e progress.
variant="numbers"
Dados
Pagamento
3
Concluído
variant="dots" (com description)
Dados
Nome e e-mail
Pagamento
Cartão ou Pix
3
Concluído
Pedido confirmado
variant="arrows"
1
Dados
2
Pagamento
3
Concluído
variant="progress"
✓
Dados
✓
Pagamento
Concluído
resources/views/tutor/examples/estrutura-wizard/steps-standalone.blade.php
<div style="display:flex; flex-direction:column; gap:28px;">
<div>
<p style="margin:0 0 8px; font-weight:600;">variant="numbers"</p>
<mad-steps variant="numbers" :current="2">
<mad-step icon="user" label="Dados" />
<mad-step icon="credit-card" label="Pagamento" />
<mad-step icon="check" label="Concluído" />
</mad-steps>
</div>
<div>
<p style="margin:0 0 8px; font-weight:600;">variant="dots" (com description)</p>
<mad-steps variant="dots" :current="2">
<mad-step icon="user" label="Dados" description="Nome e e-mail" />
<mad-step icon="credit-card" label="Pagamento" description="Cartão ou Pix" />
<mad-step icon="check" label="Concluído" description="Pedido confirmado" />
</mad-steps>
</div>
<div>
<p style="margin:0 0 8px; font-weight:600;">variant="arrows"</p>
<mad-steps variant="arrows" :current="1">
<mad-step label="Dados" />
<mad-step label="Pagamento" />
<mad-step label="Concluído" />
</mad-steps>
</div>
<div>
<p style="margin:0 0 8px; font-weight:600;">variant="progress"</p>
<mad-steps variant="progress" :current="3">
<mad-step label="Dados" />
<mad-step label="Pagamento" />
<mad-step label="Concluído" />
</mad-steps>
</div>
</div>
Código-fonte completo da página
app/control/Tutor/TutorEstruturaWizard.php
<?php
namespace App\Control\Tutor;
use App\Support\Tutor\TutorPage;
use Mad\Form\MadForm;
use Mad\Form\MadValidationException;
use Mad\Http\MadResponse;
/**
* TutorEstruturaWizard — cobre <mad-wizard>, <mad-wizard-step> e <mad-steps>.
*
* Demo 1 ("básico") valida por etapa e finaliza com toast do payload lido via
* getDataRaw() — nada é persistido. Demo 2 ("clicável") navega pelo clique no
* indicador (clickable), travado à maior etapa já alcançada. Demo 3 é
* <mad-steps> isolado (sem <mad-wizard> em volta), só pra ilustrar as 4
* variantes visuais do indicador.
*/
class TutorEstruturaWizard extends TutorPage
{
protected string $catalogKey = 'estrutura_wizard';
public MadForm $form;
/** Demo 1 — wizard básico com validação por etapa. */
public int $wizStep = 1;
/** Demo 2 — navegação clicável no indicador. */
public int $wizGotoStep = 1;
public int $wizGotoMaxReached = 1;
public function mount(array $params = []): void
{
$this->form = new MadForm('tutor_estrutura_wizard');
}
/**
* Avançar do exemplo "básico" — só valida os campos da ETAPA ATUAL antes
* de liberar a próxima (padrão de wizard: nunca validar o form inteiro
* de uma vez).
*/
public function onWizardNext(): MadResponse
{
try {
$this->form->validate($this->rulesForWizStep($this->wizStep));
} catch (MadValidationException $e) {
return $e->asInline();
}
if ($this->wizStep < 3) {
$this->wizStep++;
}
return (new MadResponse())->toast("Etapa {$this->wizStep}/3", 'info');
}
public function onWizardPrev(): void
{
if ($this->wizStep > 1) {
$this->wizStep--;
}
}
/**
* Conclusão do exemplo "básico" — valida a última etapa e devolve um
* toast com o payload das 3 etapas (getDataRaw: sem schema/token, seguro
* mesmo havendo só este <mad-form> na página). Não persiste em banco.
*/
public function onWizardFinish(): MadResponse
{
try {
$this->form->validate($this->rulesForWizStep(3));
} catch (MadValidationException $e) {
return $e->asInline();
}
$data = $this->form->getDataRaw();
$resumo = trim(($data->nome_wiz ?? '') . ' | ' . ($data->email_wiz ?? '') . ' | ' . ($data->cidade_wiz ?? ''), ' |');
return (new MadResponse())
->toast("Wizard concluído: {$resumo}", 'success', 'onWizardFinish');
}
private function rulesForWizStep(int $step): array
{
return match ($step) {
1 => ['nome_wiz|Nome' => 'required', 'email_wiz|E-mail' => 'required|email'],
2 => ['cidade_wiz|Cidade' => 'required'],
default => [],
};
}
/**
* Clique no indicador do exemplo "clicável" (mad-wizard clickable) —
* recebe a KEY da etapa clicada ("1".."3", posicional) e só permite ir
* pra uma etapa já alcançada (evita pular pra frente sem passar pelas
* anteriores).
*/
public function onWizardGotoClick(string $key): MadResponse
{
$step = (int) $key;
if ($step < 1 || $step > 3) {
return new MadResponse();
}
if ($step > $this->wizGotoMaxReached) {
return (new MadResponse())->toast('Complete a etapa atual antes de avançar.', 'warning');
}
$this->wizGotoStep = $step;
return new MadResponse();
}
public function onWizardGotoNext(): void
{
if ($this->wizGotoStep < 3) {
$this->wizGotoStep++;
$this->wizGotoMaxReached = max($this->wizGotoMaxReached, $this->wizGotoStep);
}
}
public function onWizardGotoPrev(): void
{
if ($this->wizGotoStep > 1) {
$this->wizGotoStep--;
}
}
}
resources/views/tutor/estrutura/wizard.blade.php
<x-tutor-page page="estrutura_wizard">
<x-tutor-demo demo="tutor.examples.estrutura-wizard.basico"
title="Wizard básico com validação por etapa" desc="mad-wizard + mad-wizard-step; Avançar valida só os campos da etapa atual e Concluir devolve toast com o payload."
php="TutorEstruturaWizard::onWizardFinish" />
<x-tutor-demo demo="tutor.examples.estrutura-wizard.clicavel"
title="Navegação clicável no indicador" desc="mad-wizard clickable + mad:click no indicador — só permite ir a uma etapa já alcançada."
php="TutorEstruturaWizard::onWizardGotoClick" />
<x-tutor-demo demo="tutor.examples.estrutura-wizard.steps-standalone"
title="mad-steps isolado" desc="indicador de progresso sozinho (sem mad-wizard em volta), nas 4 variantes: numbers, dots, arrows e progress." />
</x-tutor-page>