Tema
BasePageState e BaseEmptyState
BaseEmptyState ocupa uma área sem conteúdo: diz o que vai aparecer ali e qual é o primeiro passo. BasePageState cobre os outros estados que toda tela prevê: carregando, erro, sem permissão e sessão expirada, com o texto padrão em português que a tela pode trocar.
Nenhuma medição lançada
As medições deste contrato aparecem aqui assim que o apoio lançar a primeira.
Uso
BaseEmptyState
- Use quando a área carregou e não tem nada: lista, tabela, painel.
- O título diz o que falta ("Nenhuma medição lançada"), não "Sem dados".
- O texto diz quando o conteúdo aparece ou o que fazer. Vazio convida a agir.
- Coloque no slot
actionuma ação só, a que preenche a área. Quem não pode agir (por exemplo, não tem permissão para lançar) vê o vazio sem botão. - Para busca sem resultado, diga o que foi buscado e ofereça limpar a busca:
Nenhum contrato com "Serra 2"
Confira a grafia ou busque pelo número do contrato.
BasePageState
- Envolva o conteúdo da área com
BasePageStatee mude só ostatus. Comready, ele mostra o slot padrão; nos outros estados, a mensagem toma o lugar do conteúdo. - loading: o esqueleto aparece depois de 300 ms (
delay), para uma resposta rápida não piscar na tela. Desenhe a forma do conteúdo real no slotloading; sem ele, aparecem um título, três linhas e um bloco. - error: diz que não carregou e oferece "Tentar de novo" (evento
retry). Passe ocodepara o usuário informar à STI; o texto padrão passa a apontar para ele. O código só aparece no erro. - forbidden: diz que a pessoa não tem acesso e a quem pedir. Não tem botão padrão; use o slot
actionpara um link de volta, se fizer sentido. - expired: diz que a sessão expirou e oferece "Entrar de novo" (evento
signIn). - Troque
titleetextquando o contexto permitir dizer mais, como "Não foi possível carregar os contratos". - Mostre o estado vazio dentro do
ready, comBaseEmptyState: vazio não é erro.
Escolha um estado para ver a troca; "Tentar de novo" carrega por 1,5 s e mostra o conteúdo.
title e text trocam o texto de qualquer estado de problema que estiver na tela. Por isso o exemplo só passa o title quando o estado é erro: sem permissão e sessão expirada ficam com o texto padrão. O code aparece só no erro, então pode ficar ligado o tempo todo.
Texto padrão
| Estado | Título | Texto | Ação |
|---|---|---|---|
loading | — (anuncia "Carregando") | — | — |
error | Não foi possível carregar as informações | Tente de novo. Se o erro continuar, avise a STI. Com code: Tente de novo. Se o erro continuar, informe o código abaixo à STI. | Tentar de novo |
forbidden | Você não tem acesso a esta página | Se precisa dela para o seu trabalho, peça o acesso ao gestor da sua unidade. | — |
expired | Sua sessão expirou | Por segurança, a sessão termina depois de um tempo sem uso. Entre de novo para continuar. | Entrar de novo |
Estilo
| Propriedade | Token |
|---|---|
| Espaço entre os itens | --tj-space-3 (12 px); a ação fica --tj-space-2 mais longe |
| Margem interna do vazio | --tj-space-12 em cima e embaixo, --tj-space-6 nos lados |
| Margem interna dos estados | --tj-space-10 em cima e embaixo, --tj-space-6 nos lados |
| Caixa do ícone | 56 px no vazio, 48 px nos estados; raio --tj-radius-lg; ícone --tj-icon-lg |
| Ícone do vazio | fundo --tj-layer-selected-01, cor --tj-icon-interactive, contorno --tj-border-selected-subtle |
| Ícone do erro | fundo --tj-support-error-subtle, cor --tj-support-error-text |
| Ícone de sem permissão | fundo --tj-layer-accent-01, cor --tj-text-secondary |
| Ícone de sessão expirada | fundo --tj-support-warning-subtle, cor --tj-support-warning-text |
| Título | estilo h3 do BaseHeading, --tj-text-primary |
| Texto | --tj-text-helper, até 380 px no vazio e 360 px nos estados |
| Código para a STI | --tj-font-mono, --tj-text-xs, --tj-text-secondary |
No tema escuro os tokens trocam sozinhos. No modo de alto contraste do sistema, a caixa do ícone ganha contorno. Diferenças em relação ao protótipo v3:
- raio da caixa do ícone: o protótipo usa 16 e 14 px; aqui fica
--tj-radius-lg(12 px), o maior raio da escala; - cor do ícone do vazio: o protótipo usa a cor de link (
--tj-link-primary); aqui fica o papel de ícone,--tj-icon-interactive; - tamanho do ícone dos estados: o protótipo usa 22 px; aqui fica
--tj-icon-lg(24 px), o mesmo do vazio, porque a escala de ícones não tem 22 px; - código para a STI: o protótipo o coloca depois do botão; aqui ele fica logo abaixo do texto, que aponta para ele.
Código
vue
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import type { PageStateStatus } from '@lab-inovacao/design-system'
const status = ref<PageStateStatus>('loading')
const errorCode = ref<string>()
const contracts = ref<Contract[]>([])
async function load(): Promise<void> {
status.value = 'loading'
try {
contracts.value = await fetchContracts()
status.value = 'ready'
} catch (error) {
errorCode.value = errorReference(error)
status.value = isForbidden(error) ? 'forbidden' : isExpired(error) ? 'expired' : 'error'
}
}
onMounted(load)
</script>
<template>
<BasePageState
:status="status"
:code="errorCode"
:title="status === 'error' ? 'Não foi possível carregar os contratos' : undefined"
loading-label="Carregando contratos"
ready-label="Contratos carregados"
@retry="load"
@sign-in="goToLogin"
>
<template #loading>
<BaseSkeleton size="lg" width="30%" />
<BaseSkeleton variant="rect" height="320px" />
</template>
<ContractTable v-if="contracts.length" :contracts="contracts" />
<BaseEmptyState
v-else
icon="file-earmark-text"
title="Nenhum contrato cadastrado"
text="Os contratos de obra aparecem aqui assim que o primeiro for cadastrado."
>
<template #action>
<BaseButton icon="plus-lg" @click="newContract">Novo contrato</BaseButton>
</template>
</BaseEmptyState>
</BasePageState>
</template>BaseEmptyState
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
title | string | — | Obrigatório. O que vai aparecer ali |
text | string | — | Quando o conteúdo aparece ou qual o primeiro passo |
icon | string | — | Nome do ícone do Bootstrap Icons, sem bi-; decorativo |
headingLevel | 1 a 6 | 2 | Nível do título no sumário da página; o visual não muda |
Slots: action (a ação que preenche a área) e o padrão (conteúdo extra abaixo do texto). Sem eventos.
BasePageState
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
status | 'loading' | 'ready' | 'error' | 'forbidden' | 'expired' | — | Obrigatório. Estado da área |
title | string | texto padrão do estado | Troca o título do estado de problema que estiver na tela (erro, sem permissão ou sessão expirada); passe só para o estado que ele descreve |
text | string | texto padrão do estado | Troca o texto do estado de problema que estiver na tela, como o title |
actionLabel | string | "Tentar de novo" ou "Entrar de novo" | Troca o texto do botão padrão |
code | string | — | Código para a STI, mostrado como "Código …" só no erro |
headingLevel | 1 a 6 | 2 | Nível do título da mensagem |
loadingLabel | string | 'Carregando' | Anunciado ao leitor de tela quando o esqueleto aparece |
readyLabel | string | 'Conteúdo carregado' | Anunciado quando a carga termina com o conteúdo |
delay | number | 300 | Milissegundos antes de mostrar o esqueleto; 0 mostra na hora |
Eventos: retry (botão do erro) e signIn (botão da sessão expirada; no template, @sign-in).
Slots: o padrão (conteúdo, com ready), loading (forma do esqueleto) e action (troca o botão padrão de qualquer estado de problema).
Texto em branco em title, text ou actionLabel volta ao texto padrão, para a tela nunca ficar sem mensagem.
Acessibilidade
- O título é um cabeçalho de verdade (
h2por padrão). AjusteheadingLevelao sumário da página e não pule nível. - O ícone é decorativo (
aria-hidden="true"): o título diz o que houve. A cor do ícone nunca é o único sinal. BasePageStatemantém uma regiãorole="status"sempre na página, fora da área comaria-busy. Ela entra vazia e só depois recebe o texto, porque uma região que já entra com texto costuma não ser anunciada:- ao carregar, anuncia o
loadingLabelquando o esqueleto aparece (uma resposta mais rápida que odelaynão anuncia nada); - ao terminar, anuncia o
readyLabel, só se houve carga antes; - nos estados de problema, anuncia o título e o texto, para a pessoa ouvir o que houve e como seguir;
- um estado de problema encerra a carga: voltar dele para o conteúdo não anuncia o
readyLabel; - o texto sai da região depois de 5 segundos, para não ser lido de novo na leitura corrida da página.
- ao carregar, anuncia o
- A região é educada (
role="status"), mesmo no erro depois de "Tentar de novo": o foco vai para o título do erro e o leitor de tela já o lê; umrole="alert"interromperia essa leitura. - A área do conteúdo recebe
aria-busy="true"só enquanto carrega; com o conteúdo ou com um erro, o atributo sai. - Os botões são
BaseButton: Enter e Espaço acionam, e o foco aparece com--tj-ring. - Quando o botão com foco sai da tela (por exemplo, "Tentar de novo" passa o estado para carregando), o foco não cai no começo da página: vai para a área (
tabindex="-1", sem anel) enquanto carrega ou com o conteúdo, e para o título da mensagem (tabindex="-1") nos estados de problema. Se o foco estava fora da área, o componente não mexe nele. - O esqueleto respeita
prefers-reduced-motion(vejaBaseSkeleton). - Testado com axe em cada estado: pronto, carregando antes e depois do atraso e com esqueleto próprio, erro com e sem código, sem permissão com e sem link no slot
actione sessão expirada; e o vazio com e sem ícone, texto, ação e conteúdo extra.