# Tracker

A faixa de quadradinhos por período: as últimas 90 emissões, a disponibilidade
do mês, a fila dos últimos dias.

Ela responde uma pergunta que o número sozinho não responde ("esteve sempre
assim, ou piorou ontem?") e por isso cabe dentro de um `Stat`, embaixo do
valor.

Cada quadrado carrega o próprio texto. Uma faixa de cor sem texto não existe
para quem usa leitor de tela, e "verde, verde, vermelho" também não diz nada
para quem enxerga: o que importa é qual dia foi o vermelho.

A dica é uma só. A faixa inteira é o alvo: o ponteiro corre por ela, uma marca
fina acompanha o período lido e um único painel flutuante anda junto. Antes
cada quadrado montava a própria dica, e um ano de emissões montava 365 delas
para que no máximo uma aparecesse.

O teclado chega ao mesmo período. A faixa é uma parada de tabulação (uma só,
e não uma por quadrado): ao receber foco ela abre no período mais recente, as
setas caminham pelos períodos, `Home` e `End` vão às pontas e `Esc` fecha o
painel sem tirar o foco dali. O período lido pelo teclado é dito também numa
região viva, porque desenho não chega a quem ouve; o ponteiro fica calado ali,
para não encher a fila do leitor de tela com cada quadrado varrido.

## O nome é dito uma vez

O texto de `label` sai num `<p>` que a fatia `label` pode mostrar, e o grupo o
toma por `aria-labelledby` em vez de repeti-lo num `aria-label`. Antes eram duas
frases idênticas em seguida na árvore de acessibilidade: o parágrafo escondido e
o nome do grupo, um atrás do outro. O `<p>` fica `aria-hidden`, porque quem
carrega a frase para quem ouve é o nome do grupo; tirá-lo da leitura não tira
nada de ninguém, e é o que faz a frase ser dita uma vez em vez de duas.

Guardar o `<p>` em vez de apagá-lo é o que preserva as duas intenções: a fatia
`label` existe para quem quer o texto na tela, e o nome do grupo continua saindo
do mesmo lugar que essa pessoa vê.

Um `aria-label` escrito por quem chama vence o `label` e cai no mesmo grupo.
Antes ele parava na `div` externa, sem papel, que o Chrome guarda como um nó
`generic` nomeado e nenhum leitor de tela expõe.

## Sentido da escrita

Em `dir="rtl"` a faixa vira junto. O período mais recente passa a ser o da
esquerda, o ponteiro lê a partir da borda onde a leitura começa e as setas
andam para o lado que a pessoa vê: `←` avança no tempo, `→` volta. `Home` e
`End` continuam lógicos: o primeiro e o último período, e não a esquerda e a
direita.

A direção vem do `RivoProvider`, e não de um `dir` escrito à mão num elemento
acima da faixa. É o mesmo `dir` que o resto do catálogo lê, e sem ele a faixa
espelharia o desenho sem espelhar a conta: o dedo na primeira coluna leria o
último período.

## O balão anda um quadro atrás

Enquanto o dedo varre a faixa, a marca fina troca de período no mesmo quadro do
evento e o balão chega no quadro seguinte. A Base UI acompanha âncora que se
move por `IntersectionObserver`, que só avisa no fim do quadro. Não há como
fazê-lo chegar junto sem dar à dica um posicionador próprio.

O atraso é de exatamente um quadro, e não cresce: a distância entre a marca e o
balão é a que o ponteiro percorre nesse quadro (a 800px/s, cerca de 13px em
60Hz) e no quadro em que o dedo para os dois voltam a coincidir. Como a dica
só se lê com o ponteiro parado, isto está declarado como limite e não como
defeito a corrigir.

## Movimento

A faixa aparece da esquerda para a direita na montagem, por recorte (`animate-reveal`, `--rc-duration-slow`): é uma linha do tempo, e se revela no sentido dela. Com "reduzir movimento", aparece inteira.

## Quando não usar

Quando a grandeza é contínua e a forma da curva importa, use `Sparkline`: o
tracker conta ocorrências discretas, uma por período, e não desenha tendência.

## No React Native

Traduz, e os dois lados chegaram ao mesmo desenho: **a faixa inteira é um alvo só**. O nativo chegou primeiro por necessidade, e o web o seguiu. Lá cada ponto montava um `Tooltip`, e tooltip é portal: 365 dias eram 365 portais montados para que no máximo um aparecesse. Aqui nem essa saída existia, porque dica se abre ao pousar o ponteiro, e trocar cada quadrado por um `Pressable` também não resolveria: 90 períodos em 358px dão 4px por quadrado, seis vezes menos que o alvo de toque mínimo.

**O que não atravessa é o balão.** No web a leitura sai num `Tooltip` único que segue ponteiro e teclado; aqui ela mora numa linha fixa embaixo da faixa. O dedo pousa e arrasta, uma marca fina acompanha, e o período lido aparece nessa linha, que existe desde o primeiro quadro, mostrando o período mais recente: o espaço fica reservado, a tela não pula no primeiro toque, e o mais recente é o que a pergunta "piorou ontem?" quer ler primeiro.

A leitura de tela também muda de forma. A lista escondida com os 365 textos, que no web é barata, aqui seriam 365 paradas de VoiceOver dentro de um cartão; a faixa é uma parada só, do tipo ajustável (o mesmo contrato do `Slider`), e cada passo anuncia o texto de um período. Nenhum dado fica inalcançável e nenhum vira obstáculo. Por isso o `label` de cada ponto é `string`, e não `ReactNode`: ele vai inteiro para o valor acessível da faixa, e de um `ReactNode` não há como ler o texto de volta.

## Importação

```tsx
import { Tracker } from '@rivocode/ui'
```

## Exemplos

### Últimos 30 dias

```tsx
import { Stat, Tracker } from '@rivocode/ui'

const EMISSOES = Array.from({ length: 30 }, (_, index) => {
  const dia = 30 - index
  if (dia === 12) return { tone: 'danger' as const, label: `Dia ${dia}: 3 rejeitadas` }
  if (dia === 11) return { tone: 'warning' as const, label: `Dia ${dia}: fila acima do normal` }
  if (dia === 4) return { tone: 'neutral' as const, label: `Dia ${dia}: sem emissão` }
  return { tone: 'success' as const, label: `Dia ${dia}: todas autorizadas` }
})

export function LastThirtyDays() {
  return (
    <div className="w-96">
      <Stat
        label="Emissões autorizadas"
        value="1.284"
        delta={4}
        deltaLabel="sobre o mês passado"
        deltaVariant="pill"
        footer={<Tracker label="Emissões dos últimos 30 dias" data={EMISSOES} />}
      />
    </div>
  )
}
```

## Props

| Prop | Tipo | Obrigatória | Desde | O que faz |
| --- | --- | --- | --- | --- |
| `data` | `TrackerPoint[]` | sim | 0.5.0 |  |
| `label` | `string` | sim | 0.5.0 | O que a faixa mede, dito por extenso para o leitor de tela. |
| `classNames` | `Partial<Record<"cell" \| "label" \| "track", string>>` |  | 0.5.0 | Classe por parte: `label`, `track`, `cell`. |

Além dessas: repassa `className`, `style`, `id` e os demais atributos do elemento raiz.

## Ver também

- [Code](/componentes/code.md)
- [EventCalendar](/componentes/event-calendar.md)
- [RelativeTime](/componentes/relative-time.md)
- [Timeline](/componentes/timeline.md)
- [Convenções da biblioteca](/convencoes.md): Provider, tokens e as regras que valem para toda peça
- [Índice completo](/llms.txt)
