# ToastViewport

A área onde os avisos aparecem. Ela já vem montada dentro do `RivoProvider`,
então na prática o aplicativo nunca a escreve: chama `useToast()` e pronto.

```tsx
const toast = useToast()

toast.add({ title: 'Nota 4816 emitida', description: 'O PDF foi para o e-mail.' })
```

## O gancho

`useToast()` devolve quatro funções, e nenhuma delas precisa de estado seu:

| Função | O que faz |
|---|---|
| `add(options)` | Cria o aviso e devolve o `id` dele |
| `update(id, options)` | Reescreve um aviso que ainda está na tela |
| `close(id)` | Tira o aviso antes da hora |
| `promise(promessa, estados)` | Um aviso só para as três fases de uma espera |

O `options` do `add` tem `title`, `description`, `type` e `timeout`. O `type`
escolhe o tom no mesmo vocabulário do `Alert`, `info`, `success`, `warning` e
`danger`; sem ele o aviso sai neutro, que é o padrão e o que serve para a maior
parte das confirmações. `timeout: 0` deixa o aviso na tela até alguém fechar.

```tsx
toast.promise(emitirNota(), {
  loading: { title: 'Emitindo a nota…' },
  success: (numero) => ({ title: `Nota ${numero} emitida` }),
  error: { title: 'A emissão falhou' },
})
```

O `promise` existe para a espera não virar três avisos empilhados. É um aviso
só, que troca de texto e de tom conforme a promessa resolve; a alternativa,
`add` na saída e outro `add` na volta, deixa o "enviando" na tela ao lado do
"enviado".

O objeto que o gancho devolve tem identidade estável entre renderizações, então
ele pode entrar na lista de dependências de um efeito sem laço. O gerenciador
da Base UI por baixo não tem essa garantia, e absorver isso é trabalho da
biblioteca, não de quem a usa.

## Onde o aviso aparece

O padrão é `bottom-right`, que é o canto que menos disputa com o conteúdo:
cabeçalho, título e ação principal moram em cima. Para mudar, escolha no
provider, e não com CSS por cima:

```tsx
<RivoProvider toastPosition="top-center">
  <App />
</RivoProvider>
```

Os seis cantos são `top-left`, `top-center`, `top-right`, `bottom-left`,
`bottom-center` e `bottom-right`; o tipo `ToastPosition` é essa união, para
quando o canto vem de uma configuração e não de uma constante.

O aviso entra sempre pela borda mais próxima do canto escolhido. Um aviso
ancorado à esquerda que deslizasse da direita atravessaria a tela inteira para
chegar ao lugar, e o olho seguiria o movimento errado até perceber que o texto
já estava lá.

Vale sair do padrão quando o aviso responde a uma ação que acontece longe dali,
ou quando aquele canto já está ocupado por outra coisa fixa, como um botão
flutuante.

## Quando não usar

Aviso é para o que já aconteceu, e o que já aconteceu não precisa de resposta.
Se a pessoa tem de decidir algo, use `AlertDialog`. Se a informação precisa
ficar na tela enquanto ela trabalha, use `Alert`, que mora no fluxo da página e
não some sozinho.

Erro de formulário também não é aviso: ele pertence ao campo que errou, via
`FieldError`, onde a pessoa está olhando e pode corrigir.

## No React Native

No React Native esta peça é `useToast` - não se monta nada: o `RivoProvider` já traz a fiação, e o hook é o mesmo. O aviso sobe e desce com as durações do web, e aparece parado quando o sistema pede para reduzir movimento. A [tabela de paridade](/react-native) tem o resto do catálogo.

## Importação

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

## Exemplos

### Aviso de sucesso

```tsx
import { Button, useToast } from '@rivocode/ui'

export function SuccessNotice() {
  const toast = useToast()

  return (
    <div className="flex min-h-40 flex-col items-start gap-3">
      <p className="text-base text-fg-muted">
        A área de avisos já vem montada pelo RivoProvider. A aplicação só chama o
        gancho.
      </p>
      <Button
        onClick={() =>
          toast.add({
            type: 'success',
            title: 'Nota 4816 emitida',
            description: 'O PDF foi enviado para o e-mail do cliente.',
          })
        }
      >
        Emitir nota
      </Button>
    </div>
  )
}
```

### Aviso de uma espera

```tsx
import { Button, useToast } from '@rivocode/ui'

export function PromiseNotice() {
  const toast = useToast()

  const emitir = () =>
    new Promise<string>((resolve) => setTimeout(() => resolve('4817'), 1500))

  return (
    <div className="flex min-h-40 flex-col items-start gap-3">
      <p className="text-base text-fg-muted">
        Com promise, o mesmo aviso atravessa a espera e vira o resultado.
      </p>
      <Button
        variant="secondary"
        onClick={() =>
          toast.promise(emitir(), {
            loading: { title: 'Emitindo a nota…' },
            success: (numero) => ({ title: `Nota ${numero} emitida` }),
            error: { title: 'A emissão falhou', description: 'Tente de novo em instantes.' },
          })
        }
      >
        Emitir com espera
      </Button>
    </div>
  )
}
```

## Props

| Prop | Tipo | Obrigatória | Desde | O que faz |
| --- | --- | --- | --- | --- |
| `container` | `HTMLElement \| null` | sim | 0.4.0 | Onde o portal ancora. |
| `position` | `ToastPosition` |  | 0.4.0 | Onde os avisos aparecem. |
| `render` | `ComponentRenderFn<HTMLProps, ToastViewportState> \| ReactElement<unknown, string \| JSXElementConstructor<any>>` |  | - | Allows you to replace the component's HTML element with a different tag, or compose it with another component. |

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

## Ver também

- [Alert](/componentes/alert.md)
- [Badge](/componentes/badge.md)
- [EmptyState](/componentes/empty-state.md)
- [Indicator](/componentes/indicator.md)
- [Kbd](/componentes/kbd.md)
- [Meter](/componentes/meter.md)
- [Convenções da biblioteca](/convencoes.md): Provider, tokens e as regras que valem para toda peça
- [Índice completo](/llms.txt)
