@snksergio/design-system 0.67.0 → 0.68.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist-lib/ai/componentes/AppShell.md +1 -1
- package/dist-lib/ai/componentes/Chip.md +11 -0
- package/dist-lib/ai/componentes/Combobox.md +23 -0
- package/dist-lib/ai/componentes/DatePicker.md +15 -0
- package/dist-lib/ai/componentes/FileUploadField.md +23 -0
- package/dist-lib/ai/componentes/Header.md +9 -0
- package/dist-lib/ai/componentes/Kpi.md +14 -1
- package/dist-lib/ai/componentes/MenuSidebar.md +3 -3
- package/dist-lib/ai/componentes/PageHeader.md +6 -0
- package/dist-lib/ai/componentes/_primitivos.md +9 -2
- package/dist-lib/ai/global/componentes.md +20 -2
- package/dist-lib/ai/manifest.json +2 -2
- package/dist-lib/shadcn.cjs +1 -0
- package/dist-lib/shadcn.cjs.map +1 -1
- package/dist-lib/shadcn.mjs +2 -1
- package/dist-lib/src/components/shadcn/alert-dialog.cjs +13 -2
- package/dist-lib/src/components/shadcn/alert-dialog.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/alert-dialog.d.ts +21 -1
- package/dist-lib/src/components/shadcn/alert-dialog.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/alert-dialog.mjs +13 -2
- package/dist-lib/src/components/shadcn/alert-dialog.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/card.cjs +22 -2
- package/dist-lib/src/components/shadcn/card.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/card.d.ts +10 -1
- package/dist-lib/src/components/shadcn/card.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/card.mjs +22 -2
- package/dist-lib/src/components/shadcn/card.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/clickable-surface.cjs +28 -4
- package/dist-lib/src/components/shadcn/clickable-surface.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/clickable-surface.d.ts +26 -2
- package/dist-lib/src/components/shadcn/clickable-surface.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/clickable-surface.mjs +28 -4
- package/dist-lib/src/components/shadcn/clickable-surface.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/command.cjs +26 -5
- package/dist-lib/src/components/shadcn/command.cjs.map +1 -1
- package/dist-lib/src/components/shadcn/command.d.ts +49 -2
- package/dist-lib/src/components/shadcn/command.d.ts.map +1 -1
- package/dist-lib/src/components/shadcn/command.mjs +26 -5
- package/dist-lib/src/components/shadcn/command.mjs.map +1 -1
- package/dist-lib/src/components/shadcn/input-group.d.ts +1 -1
- package/dist-lib/src/components/shadcn/input-otp.d.ts +1 -1
- package/dist-lib/src/components/shadcn/input.d.ts +1 -1
- package/dist-lib/src/components/shadcn/sheet.d.ts +1 -1
- package/dist-lib/src/components/ui/AppShell/app-shell.types.d.ts +1 -1
- package/dist-lib/src/components/ui/Chip/chip.cjs +7 -0
- package/dist-lib/src/components/ui/Chip/chip.cjs.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.mjs +7 -0
- package/dist-lib/src/components/ui/Chip/chip.mjs.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.styles.cjs.map +1 -1
- package/dist-lib/src/components/ui/Chip/chip.styles.mjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.cjs +60 -22
- package/dist-lib/src/components/ui/Combobox/combobox.cjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.d.ts +2 -1
- package/dist-lib/src/components/ui/Combobox/combobox.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.mjs +44 -22
- package/dist-lib/src/components/ui/Combobox/combobox.mjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs +5 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts +18 -0
- package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs +5 -1
- package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs.map +1 -1
- package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts +21 -0
- package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/DatePicker/datepicker.cjs +21 -3
- package/dist-lib/src/components/ui/DatePicker/datepicker.cjs.map +1 -1
- package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts +10 -0
- package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts.map +1 -1
- package/dist-lib/src/components/ui/DatePicker/datepicker.mjs +21 -3
- package/dist-lib/src/components/ui/DatePicker/datepicker.mjs.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs +1 -4
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.d.ts.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs +1 -4
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs.map +1 -1
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts +6 -0
- package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Header/header-search.cjs +6 -4
- package/dist-lib/src/components/ui/Header/header-search.cjs.map +1 -1
- package/dist-lib/src/components/ui/Header/header-search.d.ts +14 -3
- package/dist-lib/src/components/ui/Header/header-search.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Header/header-search.mjs +6 -4
- package/dist-lib/src/components/ui/Header/header-search.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs +5 -0
- package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-delta.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs +5 -0
- package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-group.cjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi-group.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.cjs +8 -3
- package/dist-lib/src/components/ui/Kpi/kpi.cjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.d.ts +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.mjs +8 -3
- package/dist-lib/src/components/ui/Kpi/kpi.mjs.map +1 -1
- package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts +9 -0
- package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/MenuSidebar/sidebar.types.d.ts +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs +4 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs.map +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts +9 -0
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts.map +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs +4 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs.map +1 -1
- package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts +11 -4
- package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts.map +1 -1
- package/dist-lib/src/components/ui/Table/table-span-row.cjs.map +1 -1
- package/dist-lib/src/components/ui/Table/table-span-row.mjs.map +1 -1
- package/dist-lib/src/lib/utils.cjs.map +1 -1
- package/dist-lib/src/lib/utils.mjs.map +1 -1
- package/dist-lib/src/utils/tv.cjs.map +1 -1
- package/dist-lib/src/utils/tv.mjs.map +1 -1
- package/dist-lib/src/utils/tw-merge-config.cjs.map +1 -1
- package/dist-lib/src/utils/tw-merge-config.mjs.map +1 -1
- package/dist-lib/src/utils/use-title-se-truncado.cjs +34 -0
- package/dist-lib/src/utils/use-title-se-truncado.cjs.map +1 -0
- package/dist-lib/src/utils/use-title-se-truncado.d.ts +30 -0
- package/dist-lib/src/utils/use-title-se-truncado.d.ts.map +1 -0
- package/dist-lib/src/utils/use-title-se-truncado.mjs +34 -0
- package/dist-lib/src/utils/use-title-se-truncado.mjs.map +1 -0
- package/package.json +1 -1
|
@@ -129,4 +129,4 @@ sem erro nenhum.
|
|
|
129
129
|
- User menu (layout + tema + settings + logout) só renderiza quando `user` é passado; sem ele o rail mantém o avatar default
|
|
130
130
|
- `layout` é controlled-only: sem `onLayoutChange` o switcher Fluido/Compacto do user menu não tem efeito — guarde o valor em state e devolva via `layout`
|
|
131
131
|
- Pra navegação real, use `activeItemHref` + `onItemClick` (controlled) ligados ao router — os `default*` servem só pro modo uncontrolled/preview
|
|
132
|
-
- ⚠️ **Com react-router (ou qualquer router de history), passe `renderLink`**: `renderLink={(p) => <Link {...p} to={
|
|
132
|
+
- ⚠️ **Com react-router (ou qualquer router de history), passe `renderLink`**: `renderLink={({ href, ...p }) => <Link {...p} to={href} />}`. Sem isso, `href` de path fazia o browser recarregar a página inteira a cada clique de menu — bug real reportado em 2026-08-08, corrigido na v0.38.0. Detalhe e as 5 exceções em `MenuSidebar/USAGE.md` §Integração com router
|
|
@@ -86,3 +86,14 @@ duas ações — "editar este filtro" e "tirar este filtro" — no mesmo chip.
|
|
|
86
86
|
- **`removeLabel`**: o nome acessível do × sai do texto do chip ("Remover Status: Ativo").
|
|
87
87
|
Quando `children` não é texto simples, o default vira só "Remover" — e cinco botões
|
|
88
88
|
"Remover" numa barra de filtros são indistinguíveis no leitor de tela. Passe o nome.
|
|
89
|
+
|
|
90
|
+
## `disabled` alcança os botões (2026-09-24)
|
|
91
|
+
|
|
92
|
+
Com `onRemove` a pílula é um `<span>`, e `disabled` num `<span>` **não é atributo
|
|
93
|
+
válido** — até a v0.67.0 ele caía ali e não desabilitava nada, nem a label nem o ×.
|
|
94
|
+
Agora é extraído e vai pros botões internos.
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// durante o salvamento: nem o clique da label nem o × respondem
|
|
98
|
+
<Chip onClick={editar} onRemove={remover} disabled={salvando}>Status: Ativo</Chip>
|
|
99
|
+
```
|
|
@@ -78,3 +78,26 @@ lista longa):
|
|
|
78
78
|
ali dentro seria botão aninhado: HTML inválido, o navegador desaninha e o × passa a
|
|
79
79
|
abrir o dropdown. Pra dar remoção com ×, renderize `<Chip onRemove>` **abaixo** do
|
|
80
80
|
campo, a partir do mesmo estado.
|
|
81
|
+
|
|
82
|
+
## Grupos, hint e closeOnSelect (2026-09-24)
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
<Combobox
|
|
86
|
+
options={[
|
|
87
|
+
{ value: "todas", label: "Todas as colunas" }, // sem grupo: vem antes
|
|
88
|
+
{ value: "nome", label: "Nome", group: "Cliente" },
|
|
89
|
+
{ value: "doc", label: "CNPJ", hint: "14 dígitos", group: "Cliente" },
|
|
90
|
+
{ value: "sla", label: "SLA", group: "Contrato" },
|
|
91
|
+
]}
|
|
92
|
+
/>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
- **`group`** é chave na OPÇÃO, não uma mudança na forma de `options`: quem já passa
|
|
96
|
+
lista plana não muda nada. Os grupos aparecem na ordem da primeira opção de cada um —
|
|
97
|
+
ordenar por nome seria decidir pelo consumidor.
|
|
98
|
+
- **`hint`** é a linha secundária (código, e-mail, unidade). Entra na busca.
|
|
99
|
+
- **`closeOnSelect={false}`** (só no modo simples) mantém o dropdown aberto.
|
|
100
|
+
|
|
101
|
+
⚠️ **O `value` do `CommandItem` é o `option.value`, não o label.** O cmdk indexa por
|
|
102
|
+
`value`; com o label, rótulos repetidos (duas "Matriz", em unidades diferentes)
|
|
103
|
+
compartilham a mesma chave. O label segue pesquisável porque vai em `keywords`.
|
|
@@ -107,3 +107,18 @@ const [dates, setDates] = useState<Date[]>();
|
|
|
107
107
|
clearLabel="Limpar período"
|
|
108
108
|
/>
|
|
109
109
|
```
|
|
110
|
+
|
|
111
|
+
## Locale e período de um dia (2026-09-24)
|
|
112
|
+
|
|
113
|
+
- **`locale`** — default **`ptBR`**. O calendário renderizava meses e dias em inglês
|
|
114
|
+
enquanto o trigger já formatava pt-BR ("12 de março de 2026"); era o único texto em
|
|
115
|
+
inglês do DS, e não era escolha — o `Calendar` nunca recebeu `locale`. Passe outro
|
|
116
|
+
locale do `date-fns` se precisar.
|
|
117
|
+
- **Período de UM DIA agora é possível** no `mode="range"`: clique duas vezes no mesmo
|
|
118
|
+
dia. Antes havia `min={1}` no Calendar e isso limpava a seleção.
|
|
119
|
+
|
|
120
|
+
⚠️ **O popover do range fecha no SEGUNDO clique, e quem controla isso é o componente.**
|
|
121
|
+
O `min={1}` existia por um motivo real — sem ele o react-day-picker completa o range já
|
|
122
|
+
no primeiro clique e o popover fechava antes de escolher o fim. A troca foi mover o
|
|
123
|
+
controle do fechamento pra um estado nosso. Se for mexer na seleção de range, os dois
|
|
124
|
+
caminhos (dia único e período normal) estão em `datepicker-range-locale.test.tsx`.
|
|
@@ -116,3 +116,26 @@ que aceitar o nosso texto ou refazer o campo.
|
|
|
116
116
|
Só as chaves passadas mudam; o resto segue o default. `texts.remove` é o nome
|
|
117
117
|
acessível do botão — numa tela com vários uploads, três botões "Remover arquivo" são
|
|
118
118
|
indistinguíveis no leitor de tela.
|
|
119
|
+
|
|
120
|
+
### `texts.hint` — a dica, quando `accept` não serve de dica (2026-09-24)
|
|
121
|
+
|
|
122
|
+
A linha menor abaixo da chamada é derivada de `accept` + `maxSizeMB`. Isso funciona pra
|
|
123
|
+
`accept=".pdf"` e **não** funciona pra importação em lote, onde `accept` é a lista de MIME
|
|
124
|
+
crua:
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
text/csv,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument... · máx. 10MB
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
<FileUploadField
|
|
132
|
+
accept="text/csv,application/vnd.ms-excel"
|
|
133
|
+
maxSizeMB={10}
|
|
134
|
+
texts={{ hint: "CSV ou XLS · até 10MB · uma linha por cliente" }}
|
|
135
|
+
/>
|
|
136
|
+
|
|
137
|
+
<FileUploadField texts={{ hint: "" }} /> {/* esconde a linha */}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
⚠️ `texts.hint` substitui a dica **inteira**, incluindo o `máx. NMB`. Se o limite importa
|
|
141
|
+
pro usuário, repita-o no texto — o componente não concatena.
|
|
@@ -125,3 +125,12 @@ import {
|
|
|
125
125
|
```
|
|
126
126
|
|
|
127
127
|
Sem elas o campo é reconstruído na unha e diverge no primeiro ajuste de token.
|
|
128
|
+
|
|
129
|
+
### `ariaLabel` e `hotkey={false}` (2026-09-24)
|
|
130
|
+
|
|
131
|
+
- **`ariaLabel`** — nome acessível do campo e do botão-ícone. Default `"Abrir busca"`,
|
|
132
|
+
que era **fixo**: não dava pra traduzir nem pra dizer o que a busca busca
|
|
133
|
+
(`ariaLabel="Buscar tickets"`).
|
|
134
|
+
- **`hotkey={false}`** desliga o listener global de ⌘K. Use quando o app já registra o
|
|
135
|
+
próprio — dois listeners no mesmo atalho brigam, e antes não havia como desligar o
|
|
136
|
+
nosso. Com `false` o `title` do botão também para de anunciar o atalho.
|
|
@@ -134,7 +134,20 @@ import { Phone } from "lucide-react";
|
|
|
134
134
|
value="1.284"
|
|
135
135
|
helperText="Conta tickets sem resolução no fim do dia."
|
|
136
136
|
href="/tickets?status=open"
|
|
137
|
-
renderLink={(p) => <Link {...p} to={
|
|
137
|
+
renderLink={({ href, ...p }) => <Link {...p} to={href} />}
|
|
138
138
|
/>
|
|
139
139
|
</KpiGroup>
|
|
140
140
|
```
|
|
141
|
+
|
|
142
|
+
## Texto cortado e tooltip (2026-09-24)
|
|
143
|
+
|
|
144
|
+
- **`title` automático** no rótulo e no valor quando são texto. Eles têm `line-clamp-2`
|
|
145
|
+
e `truncate`; cortados, não havia NENHUMA forma de ler o resto.
|
|
146
|
+
- **`helperSide`** (default `top`) e **`helperMaxWidth`** no tooltip do `helperText` —
|
|
147
|
+
texto de ajuda longo num tooltip de 320px vira coluna alta e ilegível.
|
|
148
|
+
- **`KpiDelta` anuncia a direção**: a seta é `aria-hidden` (decorativa), então sem isso
|
|
149
|
+
"subiu" ou "caiu" existia só em cor e ícone. Agora há um `sr-only` antes do valor —
|
|
150
|
+
"aumento de 18%", "queda de 12s".
|
|
151
|
+
|
|
152
|
+
⚠️ O `label` continua sendo `string`, e não `ReactNode`: ele é o nome acessível do alvo
|
|
153
|
+
de drill-down (`onClick`/`href`) e a fonte do `title`. Nó arbitrário quebra os dois.
|
|
@@ -59,15 +59,15 @@ import { Link } from "react-router-dom";
|
|
|
59
59
|
|
|
60
60
|
<MenuSidebar
|
|
61
61
|
contexts={contexts}
|
|
62
|
-
renderLink={(p) => <Link {...p} to={
|
|
62
|
+
renderLink={({ href, ...p }) => <Link {...p} to={href} />}
|
|
63
63
|
/>
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
| Router | `renderLink` |
|
|
67
67
|
|---|---|
|
|
68
|
-
| **react-router** | `(p) => <Link {...p} to={
|
|
68
|
+
| **react-router** | `({ href, ...p }) => <Link {...p} to={href} />` |
|
|
69
69
|
| **Next.js** | `(p) => <Link {...p} />` (já usa `href`) |
|
|
70
|
-
| **TanStack Router** | `(p) => <Link {...p} to={
|
|
70
|
+
| **TanStack Router** | `({ href, ...p }) => <Link {...p} to={href} />` |
|
|
71
71
|
|
|
72
72
|
Com `renderLink`, o sidebar **não** mexe em `preventDefault` — quem decide é o `<Link>`.
|
|
73
73
|
Você ganha navegação client-side **e** mantém ctrl/cmd+clique pra abrir em nova aba.
|
|
@@ -153,3 +153,9 @@ Duas props que destravam o header sem mudar o default.
|
|
|
153
153
|
`line-clamp-1` equivalente: o line-clamp troca o `display` para `-webkit-box`, e
|
|
154
154
|
trocar o display de um filho de flex por uma equivalência "visualmente igual" é o
|
|
155
155
|
tipo de detalhe que só aparece numa tela específica.
|
|
156
|
+
|
|
157
|
+
- **`description` aceita `ReactNode`** (2026-09-24), não só texto: a linha secundária
|
|
158
|
+
costuma ter link, `Chip` de status ou seletor de período. Com `string` apenas, o
|
|
159
|
+
consumidor recriava o header por causa de um `<a>`.
|
|
160
|
+
- **`descriptionLines="none"`** solta a descrição sem corte — use quando ela for
|
|
161
|
+
composta.
|
|
@@ -18,12 +18,12 @@ tabela = API shadcn padrão, sem gotcha → use direto e veja o showcase.
|
|
|
18
18
|
| `select` | **Nunca `<SelectItem value="">`.** `""` é a sentinela de "nada selecionado" do Radix (`shouldShowPlaceholder`): o rótulo da opção nunca aparece, e o item **lança** no 2.2.6 ("must have a value prop that is not an empty string") — erro que o 2.3.1 **removeu**, trocando quebra alta por silêncio. **E o valor controlado é ECOADO de volta**: dentro de um `<form>` o `SelectBubbleInput` faz `select.value = <valor>` e dispara `change`; sem `<option>` correspondente o DOM recusa e o eco volta `onValueChange("")` — apagando um valor que ninguém tocou. Morde sempre que as opções chegam DEPOIS do valor (lista assíncrona, ou opções derivadas do próprio formulário). **O `ui/FormField/FormFieldSelect` já trata os dois** (sentinela interna + guarda do eco, com testes em `valor-orfao.test.tsx`) — prefira-o a montar o primitivo na unha. |
|
|
19
19
|
| `hover-card` | `openDelay` default = 200ms. Segue a receita flutuante (L-040). |
|
|
20
20
|
| `context-menu`, `menubar`, `navigation-menu` | Superfície flutuante segue a **receita única** (L-040) — não reverter pros defaults shadcn. |
|
|
21
|
-
| `card` | **`CardHeader` usa `gap-gp-2xs` (2px), não os 6px do shadcn upstream** — título e subtítulo de card são uma unidade de leitura, não dois blocos. Divergência **deliberada** (2026-08-19): ao re-sincronizar o primitivo, não reverter (mesma classe de cuidado da L-040). **`size`** (`sm`/`md`/`lg` = 16/20/24px) escala o padding de TODAS as partes — declare **só no `<Card>`**, chega em Header/Content/Footer por contexto; repetir nas partes é o erro que deixa uma seção com densidade diferente. **`md` (20px) é o default e antes era 24** — card que dependia dos 24 passa `size="lg"`. Não existe 18px na escala de `pad`, então `sm` é 16. Tokens: `p-pad-card-sm|md|lg` — ⚠️ `p-pad-card-base` continua existindo como **alias de `md`** (era 24 até 2026-08-19, virou 20): nada quebra, mas quem já consumia `base` recebe o novo valor. Não usar em código novo. **Subtítulo** (`CardDescription`) é `text-caption-md` (12/400), não `body-md` (14) — entre os dois presets de 12px, `caption-md` mantém o peso 400 e o `body-xs` (12/500) exigiria `font-normal` por cima. **`CardHeader variant="banded"`** = faixa `bg-bg-subtle` + `border-b border-border-subtle`, encostada nas bordas (cancela o `py` do Card com `-mt-*` e usa `rounded-t-*`, não `overflow-hidden` no Card — clipar quebraria o que precisa vazar); ⚠️ **Ação à direita na faixa exige `className="flex …"`, não `flex-row`**: o `CardHeader` é `grid` por padrão e `flex-row` **não troca o display** — os filhos continuam empilhando (o botão cai embaixo do título). Com `flex`, o tailwind-merge substitui o `grid` porque estão no mesmo grupo. ⚠️ No **dark** o `bg-subtle` é 1% de branco sobre surface 0.225: faixa muito discreta **de propósito** — se precisar de presença, `bg-muted` é 3× e no light é idêntico. O `Card` carrega o padding **vertical** e cada parte o **horizontal**, então parte full-bleed (tabela encostando nas bordas) é só zerar o `px` dela. **Card clicável: `onClick`/`href` + `surfaceLabel` (obrigatório)** — não ponha `onClick` na div. O DS monta um `<button>`/`<a>` ESTICADO por cima (`ClickableSurface`) em vez de trocar a raiz: conteúdo de `<button>` é phrasing content, e um card tem `<div>`/parágrafos dentro. O alvo é vazio, daí o `surfaceLabel` — sem ele o leitor anuncia só "botão". Controle DENTRO do card (menu, botão secundário) precisa de `relative z-10`, senão o overlay o cobre. Com router, `renderLink` (L-068). |
|
|
21
|
+
| `card` | **`CardHeader` usa `gap-gp-2xs` (2px), não os 6px do shadcn upstream** — título e subtítulo de card são uma unidade de leitura, não dois blocos. Divergência **deliberada** (2026-08-19): ao re-sincronizar o primitivo, não reverter (mesma classe de cuidado da L-040). **`size`** (`sm`/`md`/`lg` = 16/20/24px) escala o padding de TODAS as partes — declare **só no `<Card>`**, chega em Header/Content/Footer por contexto; repetir nas partes é o erro que deixa uma seção com densidade diferente. **`md` (20px) é o default e antes era 24** — card que dependia dos 24 passa `size="lg"`. Não existe 18px na escala de `pad`, então `sm` é 16. Tokens: `p-pad-card-sm|md|lg` — ⚠️ `p-pad-card-base` continua existindo como **alias de `md`** (era 24 até 2026-08-19, virou 20): nada quebra, mas quem já consumia `base` recebe o novo valor. Não usar em código novo. **Subtítulo** (`CardDescription`) é `text-caption-md` (12/400), não `body-md` (14) — entre os dois presets de 12px, `caption-md` mantém o peso 400 e o `body-xs` (12/500) exigiria `font-normal` por cima. **`CardHeader variant="banded"`** = faixa `bg-bg-subtle` + `border-b border-border-subtle`, encostada nas bordas (cancela o `py` do Card com `-mt-*` e usa `rounded-t-*`, não `overflow-hidden` no Card — clipar quebraria o que precisa vazar); ⚠️ **Ação à direita na faixa exige `className="flex …"`, não `flex-row`**: o `CardHeader` é `grid` por padrão e `flex-row` **não troca o display** — os filhos continuam empilhando (o botão cai embaixo do título). Com `flex`, o tailwind-merge substitui o `grid` porque estão no mesmo grupo. ⚠️ No **dark** o `bg-subtle` é 1% de branco sobre surface 0.225: faixa muito discreta **de propósito** — se precisar de presença, `bg-muted` é 3× e no light é idêntico. O `Card` carrega o padding **vertical** e cada parte o **horizontal**, então parte full-bleed (tabela encostando nas bordas) é só zerar o `px` dela. **Card clicável: `onClick`/`href` + `surfaceLabel` (obrigatório)** — não ponha `onClick` na div. O DS monta um `<button>`/`<a>` ESTICADO por cima (`ClickableSurface`) em vez de trocar a raiz: conteúdo de `<button>` é phrasing content, e um card tem `<div>`/parágrafos dentro. O alvo é vazio, daí o `surfaceLabel` — sem ele o leitor anuncia só "botão". Controle DENTRO do card (menu, botão secundário) precisa de `relative z-10`, senão o overlay o cobre. Com router, `renderLink` (L-068). **`surfaceClassName`** (2026-09-24) aponta pro ALVO esticado, não pro card: o anel de foco é `ring` externo e **some** quando o card está dentro de container com `overflow-hidden` ou `content-visibility` — nesse caso passe `surfaceClassName="ring-inset"`. O Card também repassa `aria-pressed` / `aria-current` / `aria-expanded` / `disabled` pro alvo, que é o que faz card de SELEÇÃO ser anunciado como selecionado em vez de só pintado. |
|
|
22
22
|
| `carousel` | Usa o **DS `Button`** interno (não o button shadcn). Dep extra: `embla-carousel-react`. `<CarouselDots />` = indicador de posição clicável: um ponto por **parada** (`scrollSnapList`), não por slide — com vários slides visíveis ou `slidesToScroll` os números divergem. Some sozinho quando há 1 parada. |
|
|
23
23
|
| `input-otp` | Dep extra: `input-otp`. **Irmão do `Input`**: mesmos `size` (`xxs`/`xs`/`sm`/`md` → form-xs/sm/md/lg) e `state` (default/error/warning/success), mesma superfície, foco por **borda verde + `shadow-sh-ring`** (não `ring-4`). `variant`: `connected` (default) · `outlined` · `filled` · `underline`. Declare no `<InputOTP>` — os slots herdam por contexto; repetir a prop em cada slot é o erro que deixa um slot de tamanho diferente no meio da fileira. |
|
|
24
24
|
| `drawer` | Dep extra: `vaul`. Mobile dentro de overlay z-50 → wrapper a **z-60** (L-030). |
|
|
25
25
|
| `tabs` | **Duas props no `<Tabs>`, propagadas por contexto — NÃO em List/Trigger.** `variant`: `"segmented"` (default, pill) = trocar conteúdo DENTRO de uma superfície (card, bloco, `Panel`, `FloatingPanel`, `Modal`, drawer) · `"line"` (underline) = navegar seções de uma PÁGINA, ou 2º nível abaixo de um segmented; nunca empilhe a mesma variante em dois níveis. **`fullWidth`**: abas ocupam a largura e se distribuem — use em superfície compacta (`Panel` 560px, `FloatingPanel` 320–720 e redimensionável, `Modal` até `lg`, card, bloco); **não** use em toolbar (o `DataTable` usa hug de propósito), página livre ou `Modal` `xl` (1100px). ⛔ Não escreva `w-full` no List nem `flex-1` nos triggers na mão: `w-full` só no List estica o container e agrupa as abas na esquerda — era o defeito de 6 dos 7 usos manuais do próprio DS. |
|
|
26
|
-
| `dialog` | **O card inteiro rola** — `DialogContent` tem teto `max-h-[calc(100dvh-2rem)]` + `overflow-y-auto`, então header e footer rolam junto com o corpo. Header/footer fixos com só o corpo rolando → use o composto **`ui/Modal`**; não ponha `max-h` + `overflow` num filho "por garantia" (vira scroll duplo quando o conteúdo passa da tela). **Clique dentro de overlay Radix portalado** (`[data-radix-popper-content-wrapper]`: Popover, Select, DropdownMenu) **não fecha** o dialog, e o seu `onPointerDownOutside` **não é chamado** nesse caso (o clique não conta como "fora"); nos demais ele é chamado e o `preventDefault()` continua bloqueando. `DialogHeader` reserva
|
|
26
|
+
| `dialog` | **O card inteiro rola** — `DialogContent` tem teto `max-h-[calc(100dvh-2rem)]` + `overflow-y-auto`, então header e footer rolam junto com o corpo. Header/footer fixos com só o corpo rolando → use o composto **`ui/Modal`**; não ponha `max-h` + `overflow` num filho "por garantia" (vira scroll duplo quando o conteúdo passa da tela). **Clique dentro de overlay Radix portalado** (`[data-radix-popper-content-wrapper]`: Popover, Select, DropdownMenu) **não fecha** o dialog, e o seu `onPointerDownOutside` **não é chamado** nesse caso (o clique não conta como "fora"); nos demais ele é chamado e o `preventDefault()` continua bloqueando. `DialogHeader` reserva **Largura: use `size`, não `className`.** `size="sm"` 480 · `"md"` 640 · `"lg"` **768 (default)** — a escala `modal-*` do DS. Um `max-w-*` sem variante NÃO vence o `sm:max-w-modal-*` da base (breakpoints diferentes, o merge não funde, a media query ganha acima de 640px) — era isso que obrigava a escrever `sm:max-w-…`. A largura NÃO mudou: a base era `sm:max-w-md`, que com a escala de container sobrescrita resolvia pra 768px — um valor que ninguém tinha escolhido e que não estava em token nenhum. Agora são os mesmos 768, ditos em voz alta. **Geometria do header (2026-09-23):** X em `right-4 top-4` (16px), título com `leading-none` (16px) e `DialogHeader` SEM reserva à direita — a geometria de sempre. A #333 tinha mudado as três (X em 24px com caixa de 24, entrelinha do preset em 24px, `pr-32px`), o que somava **+8px de header em todo diálogo de título curto**; revertido, mantendo as correções de rolagem e do popover portalado. ⚠️ Em troca: título de 2 linhas fica com as linhas encostadas (use `className="leading-tight"` no `DialogTitle`) e título de 1 linha muito longo passa sob o X (use `className="pr-pad-6xl"` no `DialogHeader`). Gate: `dialog-header-geometry.test.tsx`. |
|
|
27
27
|
| `sheet` | As mesmas duas regras do `dialog`: o **conteúdo rola dentro do painel** (`overflow-y-auto`; `top`/`bottom` com teto `max-h-dvh`, as laterais já têm `h-full`) e clique dentro de overlay Radix portalado **não fecha** o sheet nem chama o seu `onPointerDownOutside`. Painel lateral com header/footer fixos → composto **`ui/Panel`**. **Largura: `size`** — `"sm"` 320 · `"md"` 480 · `"lg"` **640 (default)**, escala `drawer-*`. Só vale em `side` left/right: em `top`/`bottom` o painel é full-width e um `max-w` o estreitaria em vez de mudar a altura. Mesma largura de antes (`sm:max-w-sm` resolvia 640) — mudou o nome, não o pixel. |
|
|
28
28
|
| `alert-dialog` | **ESC FECHA**, ao contrário do que o comentário do componente afirmava (medido no browser). Só o clique fora é bloqueado. Decisão inescapável exige `onEscapeKeyDown={(e) => e.preventDefault()}` — é o que o `ui/AlertModal` faz enquanto `loading`, porque o ESC é o único caminho de dismiss que não passa por um botão e escapava dos `disabled`. Header é **centralizado** (o do Dialog é à esquerda) e o Footer é `col-reverse` no mobile — ação em cima. No `sm+` o Footer **estica os filhos** (`sm:[&>*]:flex-1`): os botões dividem a largura toda, e você **não** precisa de `fullWidth` neles. Antes não esticava, e botão sem `fullWidth` ficava na largura do texto encostado à esquerda — o oposto do desenho; o `AlertModal` compensava passando `fullWidth` em cada Button. Pra confirmação comum use o composto **`ui/AlertModal`**, que já monta tom + ícone + botões. O card tem o mesmo teto e rolagem do `dialog` (`max-h-[calc(100dvh-2rem)]` + `overflow-y-auto`). Largura base é **`modal-xs` (420px)** — o MESMO valor de antes, que era o hardcode `sm:max-w-[420px]`. O degrau nasceu pra ele. |
|
|
29
29
|
| `input` | **`type="file"`** tem 2 desvios tratados por variante de atributo (`[&[type=file]]:…`): o UA aplica `align-items: baseline` e o conteúdo sobe na caixa de 40px → forçado `items-center`; e o texto do input vinha em `fg-default`, destoando do `placeholder:text-fg-muted` de todo input do DS → forçado `text-fg-muted`. **Efeito colateral aceito:** o CSS não distingue "vazio" de "arquivo escolhido" num file input, então o **nome do arquivo também fica muted**. Pra UX de upload de verdade (nome em destaque, remover, drag-and-drop) use o composto **`ui/FileUploadField`**, não o `<Input type="file">` cru. |
|
|
@@ -76,3 +76,10 @@ a célula do `tabs` tem 816 chars e não cabe num aviso. Primitivo sem bloco = s
|
|
|
76
76
|
- `<Toaster/>` **1× no root**, senão o toast não aparece e não há erro
|
|
77
77
|
- card ergonômico (title/description/action) → use o composto `ui/Toast`
|
|
78
78
|
-->
|
|
79
|
+
|
|
80
|
+
> **`command`** — `CommandDialog` aceita `size` (sm 384 default · md 480 · lg 640),
|
|
81
|
+
> `contentClassName`, `commandProps` (repassadas ao cmdk: `shouldFilter`, `filter`,
|
|
82
|
+
> `loop`, `value`) e `title`. O `commandProps` é o que destrava **busca no servidor**:
|
|
83
|
+
> sem `shouldFilter={false}` o cmdk filtra do lado do cliente e esconde o que veio da
|
|
84
|
+
> API. `CommandLoading` agora é exportado pelo DS — não importe do `cmdk` direto, isso
|
|
85
|
+
> prende a versão dele no seu projeto.
|
|
@@ -155,7 +155,7 @@ sem passar pela entrevista guiada** (é justamente aí que o erro aparece):
|
|
|
155
155
|
- Nas duas: item com destino declara `href` (vira `<a>`, então ctrl+clique e nova aba funcionam). Router próprio → `renderLink`.
|
|
156
156
|
|
|
157
157
|
> 🧭 **O app tem router (react-router, Next, TanStack)? Passe `renderLink`.**
|
|
158
|
-
> `<AppShell renderLink={(p) => <Link {...p} to={
|
|
158
|
+
> `<AppShell renderLink={({ href, ...p }) => <Link {...p} to={href} />} … />`
|
|
159
159
|
>
|
|
160
160
|
> O menu renderiza `<a href={item.href}>`. Sem `renderLink`, um `item.href` de **path**
|
|
161
161
|
> (`/clientes`) fazia o browser **recarregar a página inteira** a cada clique — bug
|
|
@@ -238,7 +238,7 @@ você estava contornando algum destes na unha, pare de contornar.
|
|
|
238
238
|
| `className="font-normal"` num `<Label>` | **`weight="regular"`** — para rótulo de OPÇÃO em grupo (checkbox, radio, switch) |
|
|
239
239
|
| anular o **min-h** do `<Textarea>` | ****rows**** — agora encolhe de verdade |
|
|
240
240
|
| recriar o header porque o título trunca | **`titleWrap`** (default **true**) e **`descriptionLines`** no `PageHeader` |
|
|
241
|
-
| aceitar os textos em PT do upload | ****texts**** no `FileUploadField` |
|
|
241
|
+
| aceitar os textos em PT do upload | ****texts**** no `FileUploadField` — inclui ****texts.hint****, a dica menor, que sem ela é a lista de MIME crua do accept |
|
|
242
242
|
| reimplementar a paleta de busca | **`onOpen`** no `HeaderSearch` — o clique e o atalho chamam a SUA paleta |
|
|
243
243
|
| `sm:max-w-modal-lg` para vencer a base do overlay | ****size**** no `Dialog`/`Sheet`/`AlertDialog` |
|
|
244
244
|
|
|
@@ -249,3 +249,21 @@ dobro ou a metade do esperado, é isto. Overlay usa `max-w-modal-*`/`max-w-drawe
|
|
|
249
249
|
⚠️ **Importe `cn` e `tv` do DS**, não do seu projeto: a config de merge conhece os
|
|
250
250
|
tokens (escala de container, presets tipográficos, **pad-**/**gp-**/**sh-**…). Um `cn` de
|
|
251
251
|
fora resolve conflito errado e a classe perde em silêncio.
|
|
252
|
+
|
|
253
|
+
## Acessibilidade e saídas (v0.68.0)
|
|
254
|
+
|
|
255
|
+
| Quando você ia escrever | Use |
|
|
256
|
+
|---|---|
|
|
257
|
+
| card de SELEÇÃO pintando só a borda | ****aria-pressed**** (alternância) ou ****aria-current**** (item de um conjunto) no `Card`/`ClickableSurface` — sem isso a escolha existe só na cor |
|
|
258
|
+
| **ring-inset** na mão porque o anel some | **`surfaceClassName`** no `Card`: o alvo é esticado e o anel é externo; em container com **overflow-hidden** ele é cortado |
|
|
259
|
+
| proteger o handler porque o × do chip seguia clicável | ****disabled**** no `Chip` — agora alcança a label e o × (antes caía no `<span>`, onde não é atributo válido) |
|
|
260
|
+
| `Dialog` + `Command` na mão pra buscar no SERVIDOR | **`CommandDialog commandProps={{ shouldFilter: false }}`** — e **`CommandLoading`** sai do DS, não importe do **cmdk** |
|
|
261
|
+
| recriar o header por causa de um link no subtítulo | ****description**** do `PageHeader` aceita `ReactNode`; **`descriptionLines="none"`** solta a altura |
|
|
262
|
+
| date-range-picker próprio por causa do idioma | **`DatePicker`** já vem **pt-BR**; período de **um dia** é clicar duas vezes no mesmo |
|
|
263
|
+
| **react-select** por causa de grupo/descrição | **`Combobox`** com ****group**** e ****hint**** na opção; **`closeOnSelect={false}`** mantém aberto |
|
|
264
|
+
| **aria-label** fixo em português na busca do header | **`ariaLabel`** no `HeaderSearch`; **`hotkey={false}`** quando o app já tem o próprio ⌘K |
|
|
265
|
+
| tooltip nativo pra ler rótulo de KPI cortado | o `Kpi` já põe ****title** sozinho, e só quando corta**; ajuste o tooltip da ajuda com **`helperSide`** / **`helperMaxWidth`** |
|
|
266
|
+
| `className` pra largura de overlay | ****size**** — agora nos **três** (`Dialog`, `Sheet`, `AlertDialog`) |
|
|
267
|
+
|
|
268
|
+
⚠️ ****disabled** em `<a>` não existe.** O `ClickableSurface` traduz pra **aria-disabled** +
|
|
269
|
+
**pointer-events-none** + `tabIndex={-1}`. Não escreva **disabled** num link na mão.
|
package/dist-lib/shadcn.cjs
CHANGED
|
@@ -98,6 +98,7 @@ exports.CommandGroup = command.CommandGroup;
|
|
|
98
98
|
exports.CommandInput = command.CommandInput;
|
|
99
99
|
exports.CommandItem = command.CommandItem;
|
|
100
100
|
exports.CommandList = command.CommandList;
|
|
101
|
+
exports.CommandLoading = command.CommandLoading;
|
|
101
102
|
exports.CommandSeparator = command.CommandSeparator;
|
|
102
103
|
exports.CommandShortcut = command.CommandShortcut;
|
|
103
104
|
exports.ContextMenu = contextMenu.ContextMenu;
|
package/dist-lib/shadcn.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shadcn.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shadcn.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
package/dist-lib/shadcn.mjs
CHANGED
|
@@ -10,7 +10,7 @@ import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle }
|
|
|
10
10
|
import { Carousel, CarouselContent, CarouselDots, CarouselItem, CarouselNext, CarouselPrevious } from "./src/components/shadcn/carousel.mjs";
|
|
11
11
|
import { Checkbox } from "./src/components/shadcn/checkbox.mjs";
|
|
12
12
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./src/components/shadcn/collapsible.mjs";
|
|
13
|
-
import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, CommandShortcut } from "./src/components/shadcn/command.mjs";
|
|
13
|
+
import { Command, CommandDialog, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandLoading, CommandSeparator, CommandShortcut } from "./src/components/shadcn/command.mjs";
|
|
14
14
|
import { ContextMenu, ContextMenuCheckboxItem, ContextMenuContent, ContextMenuGroup, ContextMenuItem, ContextMenuLabel, ContextMenuPortal, ContextMenuRadioGroup, ContextMenuRadioItem, ContextMenuSeparator, ContextMenuShortcut, ContextMenuSub, ContextMenuSubContent, ContextMenuSubTrigger, ContextMenuTrigger } from "./src/components/shadcn/context-menu.mjs";
|
|
15
15
|
import { CLICKABLE_SURFACE_CLASS, ClickableSurface } from "./src/components/shadcn/clickable-surface.mjs";
|
|
16
16
|
import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "./src/components/shadcn/dialog.mjs";
|
|
@@ -98,6 +98,7 @@ export {
|
|
|
98
98
|
CommandInput,
|
|
99
99
|
CommandItem,
|
|
100
100
|
CommandList,
|
|
101
|
+
CommandLoading,
|
|
101
102
|
CommandSeparator,
|
|
102
103
|
CommandShortcut,
|
|
103
104
|
ContextMenu,
|
|
@@ -39,7 +39,17 @@ const AlertDialogOverlay = React__namespace.forwardRef(({ className, ...props },
|
|
|
39
39
|
}
|
|
40
40
|
));
|
|
41
41
|
AlertDialogOverlay.displayName = AlertDialogPrimitive__namespace.Overlay.displayName;
|
|
42
|
-
const
|
|
42
|
+
const ALERT_DIALOG_SIZE = {
|
|
43
|
+
xs: "sm:max-w-modal-xs",
|
|
44
|
+
// 420px — default
|
|
45
|
+
sm: "sm:max-w-modal-sm",
|
|
46
|
+
// 480px
|
|
47
|
+
md: "sm:max-w-modal-md",
|
|
48
|
+
// 640px
|
|
49
|
+
lg: "sm:max-w-modal-lg"
|
|
50
|
+
// 768px
|
|
51
|
+
};
|
|
52
|
+
const AlertDialogContent = React__namespace.forwardRef(({ className, size = "xs", ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { children: [
|
|
43
53
|
/* @__PURE__ */ jsxRuntime.jsx(AlertDialogOverlay, {}),
|
|
44
54
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
45
55
|
AlertDialogPrimitive__namespace.Content,
|
|
@@ -49,7 +59,8 @@ const AlertDialogContent = React__namespace.forwardRef(({ className, ...props },
|
|
|
49
59
|
// `modal-xs` = 420px: o MESMO valor de antes, que era `sm:max-w-[420px]` — um
|
|
50
60
|
// hardcode que a primeira regra crítica do CLAUDE.md proíbe. O degrau nasceu
|
|
51
61
|
// pra ele; tokenizar não muda pixel.
|
|
52
|
-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]
|
|
62
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]",
|
|
63
|
+
ALERT_DIALOG_SIZE[size],
|
|
53
64
|
"translate-x-[-50%] translate-y-[-50%]",
|
|
54
65
|
// Teto de altura + rolagem interna — mesma regra e margem do DialogContent.
|
|
55
66
|
"max-h-[calc(100dvh-2rem)] overflow-y-auto",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert-dialog.cjs","sources":["../../../../src/components/shadcn/alert-dialog.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n/**\r\n * AlertDialog — variante do Dialog pra confirmação destrutiva/crítica.\r\n *\r\n * Diferenças vs. Dialog, TODAS medidas no browser (2026-08-13) e não deduzidas:\r\n * - `role=\"alertdialog\"` (assistive tech anuncia diferente) ✔ confirmado\r\n * - Foco inicial no botão `Cancel`, que é o default seguro ✔ confirmado\r\n * - **Não fecha ao clicar fora** ✔ confirmado — e o Dialog comum, no MESMO teste,\r\n * fecha. A diferença é real.\r\n *\r\n * ⚠️ **ESC FECHA.** Este bloco afirmava \"não fecha ao clicar fora ou apertar ESC\"; a\r\n * segunda metade era falsa. O Radix trata as duas coisas em canais separados: o\r\n * pointer-down externo o AlertDialog bloqueia, o ESC não. Quem precisasse de uma decisão\r\n * inescapável (o caso que a frase prometia cobrir) sairia daqui acreditando estar\r\n * protegido. Pra travar de verdade, no consumidor:\r\n *\r\n * <AlertDialogContent onEscapeKeyDown={(e) => e.preventDefault()}>\r\n *\r\n * Estilo alinhado com `.tbl-confirm-dialog` do sandbox: 420px, body 32/28/24,\r\n * radius-2xl, shadow-2xl, outline halo, ícone 56x56 com bg-tone-muted.\r\n *\r\n * Pra um wrapper \"one-shot\" com tone + ícone + botões → use <AlertModal>. Doc e a\r\n * comparação dos três (AlertModal / AlertDialog / Dialog): página `#/alert-dialog`.\r\n */\r\n\r\nconst AlertDialog = AlertDialogPrimitive.Root;\r\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\r\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\r\n\r\nconst AlertDialogOverlay = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Overlay\r\n ref={ref}\r\n className={cn(\r\n \"fixed inset-0 z-50 bg-black/30 supports-[backdrop-filter]:backdrop-blur-sm\",\r\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\r\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\r\n\r\nconst AlertDialogContent = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content
|
|
1
|
+
{"version":3,"file":"alert-dialog.cjs","sources":["../../../../src/components/shadcn/alert-dialog.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n/**\r\n * AlertDialog — variante do Dialog pra confirmação destrutiva/crítica.\r\n *\r\n * Diferenças vs. Dialog, TODAS medidas no browser (2026-08-13) e não deduzidas:\r\n * - `role=\"alertdialog\"` (assistive tech anuncia diferente) ✔ confirmado\r\n * - Foco inicial no botão `Cancel`, que é o default seguro ✔ confirmado\r\n * - **Não fecha ao clicar fora** ✔ confirmado — e o Dialog comum, no MESMO teste,\r\n * fecha. A diferença é real.\r\n *\r\n * ⚠️ **ESC FECHA.** Este bloco afirmava \"não fecha ao clicar fora ou apertar ESC\"; a\r\n * segunda metade era falsa. O Radix trata as duas coisas em canais separados: o\r\n * pointer-down externo o AlertDialog bloqueia, o ESC não. Quem precisasse de uma decisão\r\n * inescapável (o caso que a frase prometia cobrir) sairia daqui acreditando estar\r\n * protegido. Pra travar de verdade, no consumidor:\r\n *\r\n * <AlertDialogContent onEscapeKeyDown={(e) => e.preventDefault()}>\r\n *\r\n * Estilo alinhado com `.tbl-confirm-dialog` do sandbox: 420px, body 32/28/24,\r\n * radius-2xl, shadow-2xl, outline halo, ícone 56x56 com bg-tone-muted.\r\n *\r\n * Pra um wrapper \"one-shot\" com tone + ícone + botões → use <AlertModal>. Doc e a\r\n * comparação dos três (AlertModal / AlertDialog / Dialog): página `#/alert-dialog`.\r\n */\r\n\r\nconst AlertDialog = AlertDialogPrimitive.Root;\r\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\r\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\r\n\r\nconst AlertDialogOverlay = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Overlay\r\n ref={ref}\r\n className={cn(\r\n \"fixed inset-0 z-50 bg-black/30 supports-[backdrop-filter]:backdrop-blur-sm\",\r\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\r\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\r\n\r\n/**\r\n * Largura do conteúdo, na mesma escala `modal-*` do `Dialog`.\r\n *\r\n * O default `xs` são os 420px que o AlertDialog sempre teve. Os degraus existem pra o\r\n * caso de confirmação com corpo maior (lista do que será apagado, diff, preview).\r\n *\r\n * ⚠️ A escala começa em `xs` aqui e em `lg` no `Dialog` — não é descuido: o default de\r\n * cada um é a largura que ELE já tinha, e alert é estreito de propósito. Os NOMES são os\r\n * mesmos, então `size=\"md\"` dá 640 nos dois.\r\n */\r\nconst ALERT_DIALOG_SIZE = {\r\n xs: \"sm:max-w-modal-xs\", // 420px — default\r\n sm: \"sm:max-w-modal-sm\", // 480px\r\n md: \"sm:max-w-modal-md\", // 640px\r\n lg: \"sm:max-w-modal-lg\", // 768px\r\n} as const;\r\n\r\nexport type AlertDialogSize = keyof typeof ALERT_DIALOG_SIZE;\r\n\r\nconst AlertDialogContent = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> & {\r\n /** Largura na escala de modal do DS: xs 420 (default) · sm 480 · md 640 · lg 768. */\r\n size?: AlertDialogSize;\r\n }\r\n>(({ className, size = \"xs\", ...props }, ref) => (\r\n <AlertDialogPortal>\r\n <AlertDialogOverlay />\r\n <AlertDialogPrimitive.Content\r\n ref={ref}\r\n className={cn(\r\n // `modal-xs` = 420px: o MESMO valor de antes, que era `sm:max-w-[420px]` — um\r\n // hardcode que a primeira regra crítica do CLAUDE.md proíbe. O degrau nasceu\r\n // pra ele; tokenizar não muda pixel.\r\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]\",\r\n ALERT_DIALOG_SIZE[size],\r\n \"translate-x-[-50%] translate-y-[-50%]\",\r\n // Teto de altura + rolagem interna — mesma regra e margem do DialogContent.\r\n \"max-h-[calc(100dvh-2rem)] overflow-y-auto\",\r\n \"rounded-radius-2xl bg-bg-surface text-fg-default\",\r\n \"border border-border-default shadow-sh-2xl outline-float\",\r\n \"duration-200\",\r\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\r\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\r\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n </AlertDialogPortal>\r\n));\r\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\r\n\r\nconst AlertDialogHeader = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n <div\r\n className={cn(\r\n \"flex flex-col items-center text-center gap-gp-lg\",\r\n \"px-pad-4xl pt-[32px] pb-pad-3xl\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n);\r\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\r\n\r\nconst AlertDialogFooter = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n // Mobile: empilha vertical, confirm em cima (flex-col-reverse põe Cancel embaixo)\r\n // sm+: lado a lado horizontal, dividindo a largura do footer em partes iguais.\r\n //\r\n // O `sm:[&>*]:flex-1` é o que faz os botões OCUPAREM o footer, e ele não é opcional:\r\n // `items-stretch` age no eixo cruzado, então em `flex-row` ele não estica nada no eixo\r\n // principal. Sem o flex-1, botão fica na largura do próprio texto e o par encosta à\r\n // esquerda — que é o oposto do desenho do DS (compare com o `AlertModal`, que\r\n // compensava passando `fullWidth` em cada Button). A opinião passou pra cá: montar um\r\n // alert dialog na mão agora sai certo sem ninguém lembrar do `fullWidth`.\r\n <div\r\n className={cn(\r\n \"flex flex-col-reverse items-stretch gap-gp-md sm:flex-row sm:[&>*]:flex-1\",\r\n \"px-pad-3xl pb-pad-3xl\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n);\r\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\r\n\r\nconst AlertDialogTitle = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Title\r\n ref={ref}\r\n className={cn(\r\n \"text-body-xl font-bold tracking-[-0.01em] text-fg-default leading-tight\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\r\n\r\nconst AlertDialogDescription = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Description\r\n ref={ref}\r\n className={cn(\r\n \"text-body-sm font-normal leading-[1.5] text-fg-muted max-w-[340px]\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogDescription.displayName =\r\n AlertDialogPrimitive.Description.displayName;\r\n\r\nconst AlertDialogAction = AlertDialogPrimitive.Action;\r\nconst AlertDialogCancel = AlertDialogPrimitive.Cancel;\r\n\r\nexport {\r\n AlertDialog,\r\n AlertDialogTrigger,\r\n AlertDialogPortal,\r\n AlertDialogOverlay,\r\n AlertDialogContent,\r\n AlertDialogHeader,\r\n AlertDialogFooter,\r\n AlertDialogTitle,\r\n AlertDialogDescription,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n};\r\n"],"names":["AlertDialogPrimitive","React","jsx","cn"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,cAAcA,gCAAqB;AACzC,MAAM,qBAAqBA,gCAAqB;AAChD,MAAM,oBAAoBA,gCAAqB;AAE/C,MAAM,qBAAqBC,iBAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACF,gCAAqB;AAAA,EAArB;AAAA,IACC;AAAA,IACA,WAAWG,MAAAA;AAAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,mBAAmB,cAAcH,gCAAqB,QAAQ;AAY9D,MAAM,oBAAoB;AAAA,EACxB,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AACN;AAIA,MAAM,qBAAqBC,iBAAM,WAM/B,CAAC,EAAE,WAAW,OAAO,MAAM,GAAG,MAAA,GAAS,wCACtC,mBAAA,EACC,UAAA;AAAA,EAAAC,2BAAAA,IAAC,oBAAA,EAAmB;AAAA,EACpBA,2BAAAA;AAAAA,IAACF,gCAAqB;AAAA,IAArB;AAAA,MACC;AAAA,MACA,WAAWG,MAAAA;AAAAA;AAAAA;AAAAA;AAAAA,QAIT;AAAA,QACA,kBAAkB,IAAI;AAAA,QACtB;AAAA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,IAAA;AAAA,EAAA;AACN,GACF,CACD;AACD,mBAAmB,cAAcH,gCAAqB,QAAQ;AAE9D,MAAM,oBAAoB,CAAC;AAAA,EACzB;AAAA,EACA,GAAG;AACL,MACEE,2BAAAA;AAAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAWC,MAAAA;AAAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN;AAEF,kBAAkB,cAAc;AAEhC,MAAM,oBAAoB,CAAC;AAAA,EACzB;AAAA,EACA,GAAG;AACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUED,2BAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAWC,MAAAA;AAAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,IAAA;AAAA,EAAA;AAAA;AAGR,kBAAkB,cAAc;AAEhC,MAAM,mBAAmBF,iBAAM,WAG7B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACF,gCAAqB;AAAA,EAArB;AAAA,IACC;AAAA,IACA,WAAWG,MAAAA;AAAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,iBAAiB,cAAcH,gCAAqB,MAAM;AAE1D,MAAM,yBAAyBC,iBAAM,WAGnC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BC,2BAAAA;AAAAA,EAACF,gCAAqB;AAAA,EAArB;AAAA,IACC;AAAA,IACA,WAAWG,MAAAA;AAAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,uBAAuB,cACrBH,gCAAqB,YAAY;AAEnC,MAAM,oBAAoBA,gCAAqB;AAC/C,MAAM,oBAAoBA,gCAAqB;;;;;;;;;;;;"}
|
|
@@ -27,7 +27,27 @@ declare const AlertDialog: React.FC<AlertDialogPrimitive.AlertDialogProps>;
|
|
|
27
27
|
declare const AlertDialogTrigger: React.ForwardRefExoticComponent<AlertDialogPrimitive.AlertDialogTriggerProps & React.RefAttributes<HTMLButtonElement>>;
|
|
28
28
|
declare const AlertDialogPortal: React.FC<AlertDialogPrimitive.AlertDialogPortalProps>;
|
|
29
29
|
declare const AlertDialogOverlay: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogOverlayProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
|
|
30
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Largura do conteúdo, na mesma escala `modal-*` do `Dialog`.
|
|
32
|
+
*
|
|
33
|
+
* O default `xs` são os 420px que o AlertDialog sempre teve. Os degraus existem pra o
|
|
34
|
+
* caso de confirmação com corpo maior (lista do que será apagado, diff, preview).
|
|
35
|
+
*
|
|
36
|
+
* ⚠️ A escala começa em `xs` aqui e em `lg` no `Dialog` — não é descuido: o default de
|
|
37
|
+
* cada um é a largura que ELE já tinha, e alert é estreito de propósito. Os NOMES são os
|
|
38
|
+
* mesmos, então `size="md"` dá 640 nos dois.
|
|
39
|
+
*/
|
|
40
|
+
declare const ALERT_DIALOG_SIZE: {
|
|
41
|
+
readonly xs: "sm:max-w-modal-xs";
|
|
42
|
+
readonly sm: "sm:max-w-modal-sm";
|
|
43
|
+
readonly md: "sm:max-w-modal-md";
|
|
44
|
+
readonly lg: "sm:max-w-modal-lg";
|
|
45
|
+
};
|
|
46
|
+
export type AlertDialogSize = keyof typeof ALERT_DIALOG_SIZE;
|
|
47
|
+
declare const AlertDialogContent: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & {
|
|
48
|
+
/** Largura na escala de modal do DS: xs 420 (default) · sm 480 · md 640 · lg 768. */
|
|
49
|
+
size?: AlertDialogSize;
|
|
50
|
+
} & React.RefAttributes<HTMLDivElement>>;
|
|
31
51
|
declare const AlertDialogHeader: {
|
|
32
52
|
({ className, ...props }: React.HTMLAttributes<HTMLDivElement>): import("react/jsx-runtime").JSX.Element;
|
|
33
53
|
displayName: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/shadcn/alert-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,oBAAoB,MAAM,8BAA8B,CAAC;AAIrE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,QAAA,MAAM,WAAW,iDAA4B,CAAC;AAC9C,QAAA,MAAM,kBAAkB,wHAA+B,CAAC;AACxD,QAAA,MAAM,iBAAiB,uDAA8B,CAAC;AAEtD,QAAA,MAAM,kBAAkB,wKActB,CAAC;AAGH,QAAA,MAAM,kBAAkB,
|
|
1
|
+
{"version":3,"file":"alert-dialog.d.ts","sourceRoot":"","sources":["../../../../src/components/shadcn/alert-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAC/B,OAAO,KAAK,oBAAoB,MAAM,8BAA8B,CAAC;AAIrE;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,QAAA,MAAM,WAAW,iDAA4B,CAAC;AAC9C,QAAA,MAAM,kBAAkB,wHAA+B,CAAC;AACxD,QAAA,MAAM,iBAAiB,uDAA8B,CAAC;AAEtD,QAAA,MAAM,kBAAkB,wKActB,CAAC;AAGH;;;;;;;;;GASG;AACH,QAAA,MAAM,iBAAiB;;;;;CAKb,CAAC;AAEX,MAAM,MAAM,eAAe,GAAG,MAAM,OAAO,iBAAiB,CAAC;AAE7D,QAAA,MAAM,kBAAkB;IAGpB,qFAAqF;WAC9E,eAAe;wCA2BxB,CAAC;AAGH,QAAA,MAAM,iBAAiB;8BAGpB,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAStC,CAAC;AAGF,QAAA,MAAM,iBAAiB;8BAGpB,KAAK,CAAC,cAAc,CAAC,cAAc,CAAC;;CAkBtC,CAAC;AAGF,QAAA,MAAM,gBAAgB,8KAYpB,CAAC;AAGH,QAAA,MAAM,sBAAsB,wLAY1B,CAAC;AAIH,QAAA,MAAM,iBAAiB,uHAA8B,CAAC;AACtD,QAAA,MAAM,iBAAiB,uHAA8B,CAAC;AAEtD,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,sBAAsB,EACtB,iBAAiB,EACjB,iBAAiB,GAClB,CAAC"}
|
|
@@ -19,7 +19,17 @@ const AlertDialogOverlay = React.forwardRef(({ className, ...props }, ref) => /*
|
|
|
19
19
|
}
|
|
20
20
|
));
|
|
21
21
|
AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
|
|
22
|
-
const
|
|
22
|
+
const ALERT_DIALOG_SIZE = {
|
|
23
|
+
xs: "sm:max-w-modal-xs",
|
|
24
|
+
// 420px — default
|
|
25
|
+
sm: "sm:max-w-modal-sm",
|
|
26
|
+
// 480px
|
|
27
|
+
md: "sm:max-w-modal-md",
|
|
28
|
+
// 640px
|
|
29
|
+
lg: "sm:max-w-modal-lg"
|
|
30
|
+
// 768px
|
|
31
|
+
};
|
|
32
|
+
const AlertDialogContent = React.forwardRef(({ className, size = "xs", ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
|
|
23
33
|
/* @__PURE__ */ jsx(AlertDialogOverlay, {}),
|
|
24
34
|
/* @__PURE__ */ jsx(
|
|
25
35
|
AlertDialogPrimitive.Content,
|
|
@@ -29,7 +39,8 @@ const AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => /*
|
|
|
29
39
|
// `modal-xs` = 420px: o MESMO valor de antes, que era `sm:max-w-[420px]` — um
|
|
30
40
|
// hardcode que a primeira regra crítica do CLAUDE.md proíbe. O degrau nasceu
|
|
31
41
|
// pra ele; tokenizar não muda pixel.
|
|
32
|
-
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]
|
|
42
|
+
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]",
|
|
43
|
+
ALERT_DIALOG_SIZE[size],
|
|
33
44
|
"translate-x-[-50%] translate-y-[-50%]",
|
|
34
45
|
// Teto de altura + rolagem interna — mesma regra e margem do DialogContent.
|
|
35
46
|
"max-h-[calc(100dvh-2rem)] overflow-y-auto",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"alert-dialog.mjs","sources":["../../../../src/components/shadcn/alert-dialog.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n/**\r\n * AlertDialog — variante do Dialog pra confirmação destrutiva/crítica.\r\n *\r\n * Diferenças vs. Dialog, TODAS medidas no browser (2026-08-13) e não deduzidas:\r\n * - `role=\"alertdialog\"` (assistive tech anuncia diferente) ✔ confirmado\r\n * - Foco inicial no botão `Cancel`, que é o default seguro ✔ confirmado\r\n * - **Não fecha ao clicar fora** ✔ confirmado — e o Dialog comum, no MESMO teste,\r\n * fecha. A diferença é real.\r\n *\r\n * ⚠️ **ESC FECHA.** Este bloco afirmava \"não fecha ao clicar fora ou apertar ESC\"; a\r\n * segunda metade era falsa. O Radix trata as duas coisas em canais separados: o\r\n * pointer-down externo o AlertDialog bloqueia, o ESC não. Quem precisasse de uma decisão\r\n * inescapável (o caso que a frase prometia cobrir) sairia daqui acreditando estar\r\n * protegido. Pra travar de verdade, no consumidor:\r\n *\r\n * <AlertDialogContent onEscapeKeyDown={(e) => e.preventDefault()}>\r\n *\r\n * Estilo alinhado com `.tbl-confirm-dialog` do sandbox: 420px, body 32/28/24,\r\n * radius-2xl, shadow-2xl, outline halo, ícone 56x56 com bg-tone-muted.\r\n *\r\n * Pra um wrapper \"one-shot\" com tone + ícone + botões → use <AlertModal>. Doc e a\r\n * comparação dos três (AlertModal / AlertDialog / Dialog): página `#/alert-dialog`.\r\n */\r\n\r\nconst AlertDialog = AlertDialogPrimitive.Root;\r\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\r\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\r\n\r\nconst AlertDialogOverlay = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Overlay\r\n ref={ref}\r\n className={cn(\r\n \"fixed inset-0 z-50 bg-black/30 supports-[backdrop-filter]:backdrop-blur-sm\",\r\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\r\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\r\n\r\nconst AlertDialogContent = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content
|
|
1
|
+
{"version":3,"file":"alert-dialog.mjs","sources":["../../../../src/components/shadcn/alert-dialog.tsx"],"sourcesContent":["import * as React from \"react\";\r\nimport * as AlertDialogPrimitive from \"@radix-ui/react-alert-dialog\";\r\n\r\nimport { cn } from \"@/lib/utils\";\r\n\r\n/**\r\n * AlertDialog — variante do Dialog pra confirmação destrutiva/crítica.\r\n *\r\n * Diferenças vs. Dialog, TODAS medidas no browser (2026-08-13) e não deduzidas:\r\n * - `role=\"alertdialog\"` (assistive tech anuncia diferente) ✔ confirmado\r\n * - Foco inicial no botão `Cancel`, que é o default seguro ✔ confirmado\r\n * - **Não fecha ao clicar fora** ✔ confirmado — e o Dialog comum, no MESMO teste,\r\n * fecha. A diferença é real.\r\n *\r\n * ⚠️ **ESC FECHA.** Este bloco afirmava \"não fecha ao clicar fora ou apertar ESC\"; a\r\n * segunda metade era falsa. O Radix trata as duas coisas em canais separados: o\r\n * pointer-down externo o AlertDialog bloqueia, o ESC não. Quem precisasse de uma decisão\r\n * inescapável (o caso que a frase prometia cobrir) sairia daqui acreditando estar\r\n * protegido. Pra travar de verdade, no consumidor:\r\n *\r\n * <AlertDialogContent onEscapeKeyDown={(e) => e.preventDefault()}>\r\n *\r\n * Estilo alinhado com `.tbl-confirm-dialog` do sandbox: 420px, body 32/28/24,\r\n * radius-2xl, shadow-2xl, outline halo, ícone 56x56 com bg-tone-muted.\r\n *\r\n * Pra um wrapper \"one-shot\" com tone + ícone + botões → use <AlertModal>. Doc e a\r\n * comparação dos três (AlertModal / AlertDialog / Dialog): página `#/alert-dialog`.\r\n */\r\n\r\nconst AlertDialog = AlertDialogPrimitive.Root;\r\nconst AlertDialogTrigger = AlertDialogPrimitive.Trigger;\r\nconst AlertDialogPortal = AlertDialogPrimitive.Portal;\r\n\r\nconst AlertDialogOverlay = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Overlay>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Overlay>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Overlay\r\n ref={ref}\r\n className={cn(\r\n \"fixed inset-0 z-50 bg-black/30 supports-[backdrop-filter]:backdrop-blur-sm\",\r\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\r\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;\r\n\r\n/**\r\n * Largura do conteúdo, na mesma escala `modal-*` do `Dialog`.\r\n *\r\n * O default `xs` são os 420px que o AlertDialog sempre teve. Os degraus existem pra o\r\n * caso de confirmação com corpo maior (lista do que será apagado, diff, preview).\r\n *\r\n * ⚠️ A escala começa em `xs` aqui e em `lg` no `Dialog` — não é descuido: o default de\r\n * cada um é a largura que ELE já tinha, e alert é estreito de propósito. Os NOMES são os\r\n * mesmos, então `size=\"md\"` dá 640 nos dois.\r\n */\r\nconst ALERT_DIALOG_SIZE = {\r\n xs: \"sm:max-w-modal-xs\", // 420px — default\r\n sm: \"sm:max-w-modal-sm\", // 480px\r\n md: \"sm:max-w-modal-md\", // 640px\r\n lg: \"sm:max-w-modal-lg\", // 768px\r\n} as const;\r\n\r\nexport type AlertDialogSize = keyof typeof ALERT_DIALOG_SIZE;\r\n\r\nconst AlertDialogContent = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Content>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Content> & {\r\n /** Largura na escala de modal do DS: xs 420 (default) · sm 480 · md 640 · lg 768. */\r\n size?: AlertDialogSize;\r\n }\r\n>(({ className, size = \"xs\", ...props }, ref) => (\r\n <AlertDialogPortal>\r\n <AlertDialogOverlay />\r\n <AlertDialogPrimitive.Content\r\n ref={ref}\r\n className={cn(\r\n // `modal-xs` = 420px: o MESMO valor de antes, que era `sm:max-w-[420px]` — um\r\n // hardcode que a primeira regra crítica do CLAUDE.md proíbe. O degrau nasceu\r\n // pra ele; tokenizar não muda pixel.\r\n \"fixed left-[50%] top-[50%] z-50 grid w-full max-w-[calc(100%-2rem)]\",\r\n ALERT_DIALOG_SIZE[size],\r\n \"translate-x-[-50%] translate-y-[-50%]\",\r\n // Teto de altura + rolagem interna — mesma regra e margem do DialogContent.\r\n \"max-h-[calc(100dvh-2rem)] overflow-y-auto\",\r\n \"rounded-radius-2xl bg-bg-surface text-fg-default\",\r\n \"border border-border-default shadow-sh-2xl outline-float\",\r\n \"duration-200\",\r\n \"data-[state=open]:animate-in data-[state=closed]:animate-out\",\r\n \"data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0\",\r\n \"data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n </AlertDialogPortal>\r\n));\r\nAlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;\r\n\r\nconst AlertDialogHeader = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n <div\r\n className={cn(\r\n \"flex flex-col items-center text-center gap-gp-lg\",\r\n \"px-pad-4xl pt-[32px] pb-pad-3xl\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n);\r\nAlertDialogHeader.displayName = \"AlertDialogHeader\";\r\n\r\nconst AlertDialogFooter = ({\r\n className,\r\n ...props\r\n}: React.HTMLAttributes<HTMLDivElement>) => (\r\n // Mobile: empilha vertical, confirm em cima (flex-col-reverse põe Cancel embaixo)\r\n // sm+: lado a lado horizontal, dividindo a largura do footer em partes iguais.\r\n //\r\n // O `sm:[&>*]:flex-1` é o que faz os botões OCUPAREM o footer, e ele não é opcional:\r\n // `items-stretch` age no eixo cruzado, então em `flex-row` ele não estica nada no eixo\r\n // principal. Sem o flex-1, botão fica na largura do próprio texto e o par encosta à\r\n // esquerda — que é o oposto do desenho do DS (compare com o `AlertModal`, que\r\n // compensava passando `fullWidth` em cada Button). A opinião passou pra cá: montar um\r\n // alert dialog na mão agora sai certo sem ninguém lembrar do `fullWidth`.\r\n <div\r\n className={cn(\r\n \"flex flex-col-reverse items-stretch gap-gp-md sm:flex-row sm:[&>*]:flex-1\",\r\n \"px-pad-3xl pb-pad-3xl\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n);\r\nAlertDialogFooter.displayName = \"AlertDialogFooter\";\r\n\r\nconst AlertDialogTitle = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Title>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Title>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Title\r\n ref={ref}\r\n className={cn(\r\n \"text-body-xl font-bold tracking-[-0.01em] text-fg-default leading-tight\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;\r\n\r\nconst AlertDialogDescription = React.forwardRef<\r\n React.ElementRef<typeof AlertDialogPrimitive.Description>,\r\n React.ComponentPropsWithoutRef<typeof AlertDialogPrimitive.Description>\r\n>(({ className, ...props }, ref) => (\r\n <AlertDialogPrimitive.Description\r\n ref={ref}\r\n className={cn(\r\n \"text-body-sm font-normal leading-[1.5] text-fg-muted max-w-[340px]\",\r\n className,\r\n )}\r\n {...props}\r\n />\r\n));\r\nAlertDialogDescription.displayName =\r\n AlertDialogPrimitive.Description.displayName;\r\n\r\nconst AlertDialogAction = AlertDialogPrimitive.Action;\r\nconst AlertDialogCancel = AlertDialogPrimitive.Cancel;\r\n\r\nexport {\r\n AlertDialog,\r\n AlertDialogTrigger,\r\n AlertDialogPortal,\r\n AlertDialogOverlay,\r\n AlertDialogContent,\r\n AlertDialogHeader,\r\n AlertDialogFooter,\r\n AlertDialogTitle,\r\n AlertDialogDescription,\r\n AlertDialogAction,\r\n AlertDialogCancel,\r\n};\r\n"],"names":[],"mappings":";;;;AA6BA,MAAM,cAAc,qBAAqB;AACzC,MAAM,qBAAqB,qBAAqB;AAChD,MAAM,oBAAoB,qBAAqB;AAE/C,MAAM,qBAAqB,MAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,qBAAqB;AAAA,EAArB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,mBAAmB,cAAc,qBAAqB,QAAQ;AAY9D,MAAM,oBAAoB;AAAA,EACxB,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AAAA,EACJ,IAAI;AAAA;AACN;AAIA,MAAM,qBAAqB,MAAM,WAM/B,CAAC,EAAE,WAAW,OAAO,MAAM,GAAG,MAAA,GAAS,6BACtC,mBAAA,EACC,UAAA;AAAA,EAAA,oBAAC,oBAAA,EAAmB;AAAA,EACpB;AAAA,IAAC,qBAAqB;AAAA,IAArB;AAAA,MACC;AAAA,MACA,WAAW;AAAA;AAAA;AAAA;AAAA,QAIT;AAAA,QACA,kBAAkB,IAAI;AAAA,QACtB;AAAA;AAAA,QAEA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,IAAA;AAAA,EAAA;AACN,GACF,CACD;AACD,mBAAmB,cAAc,qBAAqB,QAAQ;AAE9D,MAAM,oBAAoB,CAAC;AAAA,EACzB;AAAA,EACA,GAAG;AACL,MACE;AAAA,EAAC;AAAA,EAAA;AAAA,IACC,WAAW;AAAA,MACT;AAAA,MACA;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN;AAEF,kBAAkB,cAAc;AAEhC,MAAM,oBAAoB,CAAC;AAAA,EACzB;AAAA,EACA,GAAG;AACL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAUE;AAAA,IAAC;AAAA,IAAA;AAAA,MACC,WAAW;AAAA,QACT;AAAA,QACA;AAAA,QACA;AAAA,MAAA;AAAA,MAED,GAAG;AAAA,IAAA;AAAA,EAAA;AAAA;AAGR,kBAAkB,cAAc;AAEhC,MAAM,mBAAmB,MAAM,WAG7B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,qBAAqB;AAAA,EAArB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,iBAAiB,cAAc,qBAAqB,MAAM;AAE1D,MAAM,yBAAyB,MAAM,WAGnC,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B;AAAA,EAAC,qBAAqB;AAAA,EAArB;AAAA,IACC;AAAA,IACA,WAAW;AAAA,MACT;AAAA,MACA;AAAA,IAAA;AAAA,IAED,GAAG;AAAA,EAAA;AACN,CACD;AACD,uBAAuB,cACrB,qBAAqB,YAAY;AAEnC,MAAM,oBAAoB,qBAAqB;AAC/C,MAAM,oBAAoB,qBAAqB;"}
|
|
@@ -42,7 +42,22 @@ const PULL_TOP = {
|
|
|
42
42
|
md: "-mt-pad-card-md",
|
|
43
43
|
lg: "-mt-pad-card-lg"
|
|
44
44
|
};
|
|
45
|
-
const Card = React__namespace.forwardRef(({
|
|
45
|
+
const Card = React__namespace.forwardRef(({
|
|
46
|
+
className,
|
|
47
|
+
size = "md",
|
|
48
|
+
children,
|
|
49
|
+
onClick,
|
|
50
|
+
href,
|
|
51
|
+
surfaceLabel,
|
|
52
|
+
surfaceClassName,
|
|
53
|
+
target,
|
|
54
|
+
renderLink,
|
|
55
|
+
"aria-pressed": ariaPressed,
|
|
56
|
+
"aria-current": ariaCurrent,
|
|
57
|
+
"aria-expanded": ariaExpanded,
|
|
58
|
+
disabled,
|
|
59
|
+
...props
|
|
60
|
+
}, ref) => {
|
|
46
61
|
const clicavel = Boolean(onClick || href);
|
|
47
62
|
return /* @__PURE__ */ jsxRuntime.jsx(CardSizeContext.Provider, { value: size, children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
48
63
|
"div",
|
|
@@ -64,7 +79,12 @@ const Card = React__namespace.forwardRef(({ className, size = "md", children, on
|
|
|
64
79
|
onClick,
|
|
65
80
|
href,
|
|
66
81
|
target,
|
|
67
|
-
renderLink
|
|
82
|
+
renderLink,
|
|
83
|
+
className: surfaceClassName,
|
|
84
|
+
"aria-pressed": ariaPressed,
|
|
85
|
+
"aria-current": ariaCurrent,
|
|
86
|
+
"aria-expanded": ariaExpanded,
|
|
87
|
+
disabled
|
|
68
88
|
}
|
|
69
89
|
)
|
|
70
90
|
]
|