Componentes
Os quarenta componentes da Vitrine: texto, entrada, dados e retorno — e o que cada um devolve.
Todo componente faz duas coisas: põe um nó na árvore e devolve o que quem escreve precisa. É essa devolução que dispensa callback.
Texto#
V.titulo("Vendas", icone := "📊")
V.subtitulo("Primeiro semestre")
V.cabecalho("Por região", nivel := 3)
V.texto("Um parágrafo.")
V.markdown("**negrito**, `código`, [link](/docs) e tabelas.")
V.codigo("x := 10", linguagem := "dataforge")
V.divisor()
V.espaco(24)V.texto aceita vários argumentos e junta com espaço, como o out.
Entrada#
| Componente | Devolve | Para |
|---|---|---|
V.botao(rótulo) | yes no ciclo do clique | uma ação |
V.entrada(rótulo) | o texto digitado | uma linha de texto |
V.area_de_texto(rótulo) | o texto digitado | várias linhas |
V.numero(rótulo) | o número | quantidade, com mínimo e máximo |
V.deslizante(rótulo, min, max) | o número | escolher numa faixa |
V.caixa(rótulo) | yes/no | uma opção ligada ou desligada |
V.interruptor(rótulo) | yes/no | o mesmo, com cara de chave |
V.opcao(rótulo, opções) | a escolhida | uma de poucas, em rádio |
V.escolha(rótulo, opções) | a escolhida | uma de muitas, em lista |
V.escolhas(rótulo, opções) | um cluster | várias de muitas |
V.data(rótulo) | "2026-03-14" | uma data |
V.cor(rótulo) | "#FED403" | uma cor |
V.arquivo(rótulo) | void ou o vault do arquivo | enviar arquivo |
nome := V.entrada("Nome", "", dica := "como no documento")
senha := V.entrada("Senha", tipo := "senha")
idade := V.numero("Idade", 18, minimo := 0, maximo := 120)
fatia := V.deslizante("Desconto", 0, 100, valor := 10, passo := 5)
tags := V.escolhas("Tags", ["novo", "urgente", "revisar"])O botão vale por uma execução#
V.botao devolve yes só no ciclo em que foi clicado. Se fosse permanente, a ação dispararia de novo no próximo carregamento da página — e duplicar um pagamento é o tipo de bug que ninguém perdoa.
Formulário: quando cada tecla custa caro#
Sem formulário, cada tecla digitada roda o programa inteiro. Num campo ligado a uma consulta pesada, isso é a diferença entre um app usável e um que trava a cada letra.
forma := V.formulario("cadastro")
nome := forma.entrada("Nome")
email := forma.entrada("E-mail", tipo := "email")
given forma.enviar("Cadastrar"):
criar_usuario(nome, email)
V.sucesso("Usuário cadastrado.")Dentro do formulário, os valores só chegam ao programa quando alguém aperta o botão de envio. Com limpar := yes, os campos são esvaziados depois — e só os deste formulário, não os da página inteira.
Arquivos#
arq := V.arquivo("Planilha", tipos := [".csv"], varios := no)
given arq is not void:
V.texto($"{arq["nome"]} — {arq["tamanho"]} bytes")
linhas := arq["texto"].lines()
V.tabela(linhas)O vault tem nome, tamanho, tipo, conteudo (bytes) e texto. Com varios := yes, devolve um cluster deles. O teto padrão é 8 MB, ajustável em V.configurar("limite_upload", …).
Validação#
O erro aparece sob o campo, e não num alerta no topo. Num formulário de doze campos, um alerta dizendo "há erros" obriga a pessoa a caçar qual deles — e é a diferença entre corrigir na hora e desistir.
adopt Arcane.Regex as Regex
email := V.entrada("E-mail")
V.validar(email, Regex.is_email, "Digite um e-mail válido.")
// ou com a regra junto, devolvendo (valor, esta_bom)
senha, ok := V.campo_validado("Senha", lambda s: len(s) bigger 7,
"Mínimo de 8 caracteres.", tipo := "senha")A regra é uma ação que recebe o valor e devolve yes/no — e aí a mensagem é a que você passou — ou um texto, que vira a mensagem (vazio significa que passou).
V.validar(senha, lambda s: "" given len(s) bigger 7
otherwise $"faltam {8 - len(s)} caracteres")| Comportamento | Por quê |
|---|---|
| campo vazio e nunca tocado não é acusado | reclamar antes de a pessoa digitar é ruído, não ajuda |
| uma regra que dispara vira "a regra de validação falhou" | dizer "E-mail inválido" ali esconderia o bug real |
o campo ganha aria-invalid e aponta para a mensagem | senão quem não vê a tela descobre o erro só ao voltar nele, se voltar |
Dados#
V.tabela(linhas) // estática
V.frame(linhas) // com busca e ordenação
V.metrica("Receita", "R$ 850 mil", variacao := 18.0, ajuda := "vs. meta")
V.json(vault)
V.vault(vault) // lista de chave e valorV.tabela e V.frame aceitam três formas, porque são as três que o resto da linguagem devolve:
| Forma | Exemplo | Vem de |
|---|---|---|
| cluster de vaults | [{"a": 1}, {"a": 2}] | IO.read_csv, Banco.consultar |
| vault de colunas | {"a": [1, 2]} | um group_by |
| matriz | [[1, 2], [3, 4]] | cálculo direto |
Um frame do Arcane.Analytics também entra direto. A ordem das colunas é a de aparição, e não a alfabética: quem montou o vault escolheu uma ordem, e ela costuma ser a certa.
A variação da métrica é um número e não um texto, porque a Vitrine precisa saber o sinal: ela sobe em verde e desce em vermelho.
Retorno ao usuário#
V.sucesso("Salvo.")
V.erro("Não foi possível salvar.")
V.aviso("Isto não pode ser desfeito.")
V.informacao("Os dados são de ontem.")
V.progresso(0.62, "62% processado")
V.carregando("Consultando o banco…")
V.imagem("/static/grafico.png", legenda := "Vendas")
V.audio("/static/podcast.mp3")
V.video("/static/tour.mp4")
V.link("Documentação", "/docs", nova_aba := yes)
V.baixar("Baixar relatório", texto, "relatorio.txt")Para exportar dados já formatados, V.exportar_csv(linhas) e V.exportar_json(dados) desenham o botão e cuidam do escape.
Componentes próprios#
Uma ação já é um componente. Chamá-la desenha o que ela desenha, e nada além disso é necessário:
action cartao_de_usuario(nome, email):
caixa := V.cartao(nome)
caixa.texto(email)
cartao_de_usuario("Ana", "ana@exemplo.br")O registro por nome existe para o caso em que o nome precisa atravessar módulos — um plugin que acrescenta componentes, ou um tema que substitui um deles sem que quem chama saiba:
V.componente("usuario", cartao_de_usuario)
V.usar("usuario", "Ana", "ana@exemplo.br")
// também serve de decorador
mark @V.componente("usuario")
action cartao_de_usuario(nome, email):
…O registro vive na aplicação, e não no módulo: dois apps no mesmo processo — o que os testes fazem o tempo todo — não podem ver os componentes um do outro. E um nome errado sugere o parecido, em vez de falhar em silêncio.