Ação
Componentes de ação são o ponto onde o usuário dispara algo: clicar, escolher numa lista, confirmar. Eles carregam intenção — um clique muda dados, navega, ou inicia um fluxo. Por isso a categoria reúne tanto o gatilho direto (Button) quanto os elementos que cercam uma ação: dica contextual (Tooltip), conjunto de ações secundárias (DropdownMenu), painel ancorado (Popover) e a salvaguarda antes de algo destrutivo (ConfirmDialog).
Use esta página quando precisar que o usuário faça algo. Para entrada de dados (texto, seleção, datas) veja inputs; para apresentar coleções, veja data.
Button
Quando usar: a ação primária ou secundária de qualquer tela — submeter um form, abrir um modal, navegar. É o gatilho de ação por padrão.
Botão primário com variants, sizes, estado de loading.
import { Button } from "tempest-react-sdk";
import { Plus, Trash } from "lucide-react";
<Button>Salvar</Button>;
<Button variant="danger" leftIcon={<Trash size={16} />}>
Excluir
</Button>;
<Button variant="outline" loading>
Carregando…
</Button>;
<Button variant="link" rightIcon={<ArrowRight size={14} />}>
Ver mais
</Button>;
<Button iconOnly aria-label="Adicionar">
<Plus size={16} />
</Button>;
<Button fullWidth pill>
CTA
</Button>;
| Prop | Tipo | Default |
|---|---|---|
variant |
"primary" \| "secondary" \| "success" \| "danger" \| "soft" \| "outline" \| "ghost" \| "link" |
"primary" |
size |
"xs" \| "sm" \| "md" \| "lg" \| "xl" |
"md" |
loading |
boolean |
false |
fullWidth |
boolean |
false |
iconOnly |
boolean (square, requer aria-label) |
false |
pill |
boolean (border-radius pílula) |
false |
leftIcon |
ReactNode |
— |
rightIcon |
ReactNode |
— |
iconOnly precisa de rótulo acessível
iconOnly remove o texto visível, então leitores de tela não têm o que anunciar. Sempre passe aria-label descrevendo a ação (aria-label="Excluir"). Sem isso o botão é um ícone mudo para tecnologia assistiva.
loading bloqueia duplo clique
loading desabilita o botão e seta aria-busy="true" — é o padrão para submits assíncronos. Ative-o assim que disparar a request para evitar requisições duplicadas por cliques repetidos.
FloatingActionButton
Quando usar: a ação primária e persistente de uma tela (criar, compor, adicionar) que deve ficar sempre acessível, flutuando sobre o conteúdo. Redondo quando só tem ícone, ou estendido (pílula) quando tem
label.
Por padrão fica fixo no canto inferior direito; passe position="none" para posicioná-lo inline (ex.: dentro de um NavigationRail). Espalha todos os props nativos de <button> (onClick, disabled, etc.).
import { FloatingActionButton } from "tempest-react-sdk";
import { Plus } from "lucide-react";
<FloatingActionButton icon={<Plus />} aria-label="Novo" position="none" onClick={create} />;
<FloatingActionButton icon={<Plus />} label="Novo pedido" onClick={create} />;
| Prop | Tipo | Default |
|---|---|---|
icon |
ReactNode |
— |
label |
ReactNode (presente → FAB estendido) |
— |
position |
"bottom-right" \| "bottom-left" \| "none" |
"bottom-right" |
size |
"sm" \| "md" \| "lg" |
"md" |
variant |
"primary" \| "surface" |
"primary" |
| ... | Todos os atributos de HTMLButtonElement |
— |
FAB só de ícone precisa de aria-label
Sem label visível, o FAB redondo não tem nome acessível. Sempre passe aria-label descrevendo a ação (aria-label="Novo"); quando há label, ele já serve de nome.
Tooltip
Quando usar: dar contexto extra a um controle cujo significado não é óbvio — tipicamente botões
iconOnly. Nunca para informação crítica.
Hover tooltip portalado. Aparece no hover e no foco por teclado.
<Tooltip content="Excluir permanentemente" placement="bottom" openDelay={300}>
<Button variant="danger" iconOnly aria-label="Excluir">
<Trash />
</Button>
</Tooltip>
| Prop | Tipo | Default |
|---|---|---|
content |
ReactNode |
— |
placement |
"top" \| "right" \| "bottom" \| "left" |
"top" |
openDelay |
number (ms antes de aparecer) |
150 |
disabled |
boolean (desliga sem mexer no trigger) |
false |
Não esconda informação essencial num tooltip
Usuários de touch não têm hover — eles nunca verão o conteúdo. Tooltip é reforço, não a única fonte de uma informação necessária para concluir a tarefa.
DropdownMenu
Quando usar: agrupar ações secundárias atrás de um único gatilho ("Mais ações", menu de perfil) quando elas não cabem na barra principal.
Menu suspenso de ações. Navegação por teclado (↑↓ Home End Esc). Cada entrada precisa de um id estável (usado como key do React).
<DropdownMenu
trigger={<Button variant="ghost">Mais ações</Button>}
items={[
{ type: "label", id: "h", label: "Conta" },
{ type: "item", id: "edit", label: "Editar perfil", onSelect: () => navigate("/profile") },
{ type: "separator", id: "s1" },
{ type: "item", id: "logout", label: "Sair", onSelect: logout, danger: true },
]}
/>
| Entry type | Campos |
|---|---|
"item" |
id, label, icon?, onSelect, disabled?, danger? |
"label" |
id, label |
"separator" |
id |
Props do componente: trigger (ReactElement), items (DropdownMenuEntry[]), placement ("bottom-start" \| "bottom-end" \| "top-start" \| "top-end", default "bottom-start").
Fecha após selecionar
Selecionar um item dispara onSelect e fecha o menu. Para um painel que permanece aberto com múltiplas escolhas (checkboxes, filtros), use Popover em vez de DropdownMenu.
Popover
Quando usar: um painel flutuante com conteúdo arbitrário (filtros, mini-form, preview) ancorado a um gatilho — quando você precisa de mais que uma lista de ações.
Painel flutuante genérico (anchor + outside-click + Esc dismiss). Funciona controlado (open + onOpenChange) ou não-controlado (defaultOpen).
<Popover
open={open}
onOpenChange={setOpen}
placement="bottom"
trigger={<Button>Filtros</Button>}
>
<Stack gap={3}>
<Checkbox label="Apenas ativos" />
<Checkbox label="Pago" />
<Button onClick={() => setOpen(false)}>Aplicar</Button>
</Stack>
</Popover>
| Prop | Tipo | Default |
|---|---|---|
trigger |
ReactElement (clonado com handlers) |
— |
open |
boolean |
— (controlled) |
onOpenChange |
(open: boolean) => void |
— |
defaultOpen |
boolean (uso não-controlado) |
false |
placement |
"top" \| "bottom" \| "left" \| "right" |
"bottom" |
closeOnEsc |
boolean |
true |
closeOnOutsideClick |
boolean |
true |
Sem collision detection
O Popover não reposiciona automaticamente quando esbarra na borda da viewport. Se você precisa de flip/shift automático, prefira o DropdownMenu (lista simples) ou integre Floating UI no app.
ConfirmDialog
Quando usar: a última barreira antes de uma ação irreversível ou cara (excluir, sobrescrever, cancelar). Sempre com
variant="danger"quando destrutiva.
Prompt destrutivo pré-montado em cima do Modal (texto + 2 botões).
<ConfirmDialog
open={open}
title="Excluir usuário"
description={`Esta ação é permanente. Excluir ${user.name}?`}
confirmLabel="Sim, excluir"
cancelLabel="Cancelar"
variant="danger"
loading={deleting}
onConfirm={async () => {
await deleteUser(user.id);
setOpen(false);
}}
onCancel={() => setOpen(false)}
/>
| Prop | Tipo | Default |
|---|---|---|
open |
boolean |
— |
title |
ReactNode |
— |
description |
ReactNode |
— |
confirmLabel |
string |
"Confirmar" |
cancelLabel |
string |
"Cancelar" |
variant |
"primary" \| "danger" |
"primary" |
loading |
boolean (mostra spinner + desabilita ambos os botões) |
false |
onConfirm |
() => void \| Promise<void> |
— |
onCancel |
() => void |
— |
Controle o loading durante a request
onConfirm aceita uma promise, mas o ConfirmDialog não gerencia o estado de loading sozinho — passe loading={deleting} controlado pelo seu estado para travar ambos os botões enquanto a ação assíncrona corre.
InstallButton
Botão de instalação do PWA, ligado ao prompt beforeinstallprompt (useBeforeInstallPrompt). Renderiza null quando o app não pode ser instalado — prompt ainda não capturado, já instalado, ou rodando standalone — então você o solta na UI sem guardar visibilidade. Herda todas as props do Button.
import { InstallButton } from "tempest-react-sdk";
import { Download } from "lucide-react";
<InstallButton variant="primary" leftIcon={<Download size={18} />} />;
| Prop | Tipo | Default |
|---|---|---|
label |
ReactNode |
"Instalar app" |
onResult |
(o: "accepted" \| "dismissed" \| "unsupported") => void |
— |
| … | todas as props de Button (variant, size, leftIcon) |
— |
InstallBanner
Banner inferior dispensável que convida a instalar o PWA. Aparece só quando há prompt capturado e o app não está standalone; em plataformas que nunca disparam beforeinstallprompt (iOS Safari) fica oculto — surfa instruções manuais em outro lugar. storageKey lembra a dispensa entre recarregamentos.
<InstallBanner
title="Instale o app"
description="Acesso offline e atalho na tela inicial."
storageKey="meu-app:install-dismissed"
/>;
| Prop | Tipo | Default |
|---|---|---|
title |
ReactNode |
"Instale o app" |
description |
ReactNode |
— |
installLabel |
string |
"Instalar" |
dismissLabel |
string |
"Dispensar" |
icon |
ReactNode |
— |
storageKey |
string |
— (sessão) |
onResult |
(o) => void |
— |
Resumo
| Componente | Use para | Gatilho |
|---|---|---|
Button |
Disparar a ação primária/secundária | clique |
FloatingActionButton |
Ação primária flutuante e persistente | clique |
InstallButton |
Instalar o PWA (some quando não aplicável) | clique |
InstallBanner |
Convite dispensável pra instalar o PWA | clique |
Tooltip |
Contexto não-crítico num controle | hover/foco |
DropdownMenu |
Lista de ações secundárias (fecha ao escolher) | clique |
Popover |
Painel flutuante com conteúdo arbitrário | clique |
ConfirmDialog |
Confirmar ação destrutiva antes de executar | — |
Pontos-chave de acessibilidade:
- Ações destrutivas devem usar
variant="danger". Button.loadingé o padrão para submits async — bloqueia duplos cliques.- Tooltips não devem conter informação crítica (usuários de touch não veem hover).
iconOnlyexigearia-label.
Relacionados: overlay (ConfirmDialog é construído sobre Modal) · inputs (entrada de dados) · feedback (toasts/alerts após a ação).