@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.
Files changed (123) hide show
  1. package/dist-lib/ai/componentes/AppShell.md +1 -1
  2. package/dist-lib/ai/componentes/Chip.md +11 -0
  3. package/dist-lib/ai/componentes/Combobox.md +23 -0
  4. package/dist-lib/ai/componentes/DatePicker.md +15 -0
  5. package/dist-lib/ai/componentes/FileUploadField.md +23 -0
  6. package/dist-lib/ai/componentes/Header.md +9 -0
  7. package/dist-lib/ai/componentes/Kpi.md +14 -1
  8. package/dist-lib/ai/componentes/MenuSidebar.md +3 -3
  9. package/dist-lib/ai/componentes/PageHeader.md +6 -0
  10. package/dist-lib/ai/componentes/_primitivos.md +9 -2
  11. package/dist-lib/ai/global/componentes.md +20 -2
  12. package/dist-lib/ai/manifest.json +2 -2
  13. package/dist-lib/shadcn.cjs +1 -0
  14. package/dist-lib/shadcn.cjs.map +1 -1
  15. package/dist-lib/shadcn.mjs +2 -1
  16. package/dist-lib/src/components/shadcn/alert-dialog.cjs +13 -2
  17. package/dist-lib/src/components/shadcn/alert-dialog.cjs.map +1 -1
  18. package/dist-lib/src/components/shadcn/alert-dialog.d.ts +21 -1
  19. package/dist-lib/src/components/shadcn/alert-dialog.d.ts.map +1 -1
  20. package/dist-lib/src/components/shadcn/alert-dialog.mjs +13 -2
  21. package/dist-lib/src/components/shadcn/alert-dialog.mjs.map +1 -1
  22. package/dist-lib/src/components/shadcn/card.cjs +22 -2
  23. package/dist-lib/src/components/shadcn/card.cjs.map +1 -1
  24. package/dist-lib/src/components/shadcn/card.d.ts +10 -1
  25. package/dist-lib/src/components/shadcn/card.d.ts.map +1 -1
  26. package/dist-lib/src/components/shadcn/card.mjs +22 -2
  27. package/dist-lib/src/components/shadcn/card.mjs.map +1 -1
  28. package/dist-lib/src/components/shadcn/clickable-surface.cjs +28 -4
  29. package/dist-lib/src/components/shadcn/clickable-surface.cjs.map +1 -1
  30. package/dist-lib/src/components/shadcn/clickable-surface.d.ts +26 -2
  31. package/dist-lib/src/components/shadcn/clickable-surface.d.ts.map +1 -1
  32. package/dist-lib/src/components/shadcn/clickable-surface.mjs +28 -4
  33. package/dist-lib/src/components/shadcn/clickable-surface.mjs.map +1 -1
  34. package/dist-lib/src/components/shadcn/command.cjs +26 -5
  35. package/dist-lib/src/components/shadcn/command.cjs.map +1 -1
  36. package/dist-lib/src/components/shadcn/command.d.ts +49 -2
  37. package/dist-lib/src/components/shadcn/command.d.ts.map +1 -1
  38. package/dist-lib/src/components/shadcn/command.mjs +26 -5
  39. package/dist-lib/src/components/shadcn/command.mjs.map +1 -1
  40. package/dist-lib/src/components/shadcn/input-group.d.ts +1 -1
  41. package/dist-lib/src/components/shadcn/input-otp.d.ts +1 -1
  42. package/dist-lib/src/components/shadcn/input.d.ts +1 -1
  43. package/dist-lib/src/components/shadcn/sheet.d.ts +1 -1
  44. package/dist-lib/src/components/ui/AppShell/app-shell.types.d.ts +1 -1
  45. package/dist-lib/src/components/ui/Chip/chip.cjs +7 -0
  46. package/dist-lib/src/components/ui/Chip/chip.cjs.map +1 -1
  47. package/dist-lib/src/components/ui/Chip/chip.d.ts.map +1 -1
  48. package/dist-lib/src/components/ui/Chip/chip.mjs +7 -0
  49. package/dist-lib/src/components/ui/Chip/chip.mjs.map +1 -1
  50. package/dist-lib/src/components/ui/Chip/chip.styles.cjs.map +1 -1
  51. package/dist-lib/src/components/ui/Chip/chip.styles.mjs.map +1 -1
  52. package/dist-lib/src/components/ui/Combobox/combobox.cjs +60 -22
  53. package/dist-lib/src/components/ui/Combobox/combobox.cjs.map +1 -1
  54. package/dist-lib/src/components/ui/Combobox/combobox.d.ts +2 -1
  55. package/dist-lib/src/components/ui/Combobox/combobox.d.ts.map +1 -1
  56. package/dist-lib/src/components/ui/Combobox/combobox.mjs +44 -22
  57. package/dist-lib/src/components/ui/Combobox/combobox.mjs.map +1 -1
  58. package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs +5 -1
  59. package/dist-lib/src/components/ui/Combobox/combobox.styles.cjs.map +1 -1
  60. package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts +18 -0
  61. package/dist-lib/src/components/ui/Combobox/combobox.styles.d.ts.map +1 -1
  62. package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs +5 -1
  63. package/dist-lib/src/components/ui/Combobox/combobox.styles.mjs.map +1 -1
  64. package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts +21 -0
  65. package/dist-lib/src/components/ui/Combobox/combobox.types.d.ts.map +1 -1
  66. package/dist-lib/src/components/ui/DatePicker/datepicker.cjs +21 -3
  67. package/dist-lib/src/components/ui/DatePicker/datepicker.cjs.map +1 -1
  68. package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts +10 -0
  69. package/dist-lib/src/components/ui/DatePicker/datepicker.d.ts.map +1 -1
  70. package/dist-lib/src/components/ui/DatePicker/datepicker.mjs +21 -3
  71. package/dist-lib/src/components/ui/DatePicker/datepicker.mjs.map +1 -1
  72. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs +1 -4
  73. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.cjs.map +1 -1
  74. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.d.ts.map +1 -1
  75. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs +1 -4
  76. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.mjs.map +1 -1
  77. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts +6 -0
  78. package/dist-lib/src/components/ui/FileUploadField/file-upload-field.types.d.ts.map +1 -1
  79. package/dist-lib/src/components/ui/Header/header-search.cjs +6 -4
  80. package/dist-lib/src/components/ui/Header/header-search.cjs.map +1 -1
  81. package/dist-lib/src/components/ui/Header/header-search.d.ts +14 -3
  82. package/dist-lib/src/components/ui/Header/header-search.d.ts.map +1 -1
  83. package/dist-lib/src/components/ui/Header/header-search.mjs +6 -4
  84. package/dist-lib/src/components/ui/Header/header-search.mjs.map +1 -1
  85. package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs +5 -0
  86. package/dist-lib/src/components/ui/Kpi/kpi-delta.cjs.map +1 -1
  87. package/dist-lib/src/components/ui/Kpi/kpi-delta.d.ts.map +1 -1
  88. package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs +5 -0
  89. package/dist-lib/src/components/ui/Kpi/kpi-delta.mjs.map +1 -1
  90. package/dist-lib/src/components/ui/Kpi/kpi-group.cjs.map +1 -1
  91. package/dist-lib/src/components/ui/Kpi/kpi-group.mjs.map +1 -1
  92. package/dist-lib/src/components/ui/Kpi/kpi.cjs +8 -3
  93. package/dist-lib/src/components/ui/Kpi/kpi.cjs.map +1 -1
  94. package/dist-lib/src/components/ui/Kpi/kpi.d.ts +1 -1
  95. package/dist-lib/src/components/ui/Kpi/kpi.d.ts.map +1 -1
  96. package/dist-lib/src/components/ui/Kpi/kpi.mjs +8 -3
  97. package/dist-lib/src/components/ui/Kpi/kpi.mjs.map +1 -1
  98. package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts +9 -0
  99. package/dist-lib/src/components/ui/Kpi/kpi.types.d.ts.map +1 -1
  100. package/dist-lib/src/components/ui/MenuSidebar/sidebar.types.d.ts +1 -1
  101. package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs +4 -1
  102. package/dist-lib/src/components/ui/PageHeader/page-header.styles.cjs.map +1 -1
  103. package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts +9 -0
  104. package/dist-lib/src/components/ui/PageHeader/page-header.styles.d.ts.map +1 -1
  105. package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs +4 -1
  106. package/dist-lib/src/components/ui/PageHeader/page-header.styles.mjs.map +1 -1
  107. package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts +11 -4
  108. package/dist-lib/src/components/ui/PageHeader/page-header.types.d.ts.map +1 -1
  109. package/dist-lib/src/components/ui/Table/table-span-row.cjs.map +1 -1
  110. package/dist-lib/src/components/ui/Table/table-span-row.mjs.map +1 -1
  111. package/dist-lib/src/lib/utils.cjs.map +1 -1
  112. package/dist-lib/src/lib/utils.mjs.map +1 -1
  113. package/dist-lib/src/utils/tv.cjs.map +1 -1
  114. package/dist-lib/src/utils/tv.mjs.map +1 -1
  115. package/dist-lib/src/utils/tw-merge-config.cjs.map +1 -1
  116. package/dist-lib/src/utils/tw-merge-config.mjs.map +1 -1
  117. package/dist-lib/src/utils/use-title-se-truncado.cjs +34 -0
  118. package/dist-lib/src/utils/use-title-se-truncado.cjs.map +1 -0
  119. package/dist-lib/src/utils/use-title-se-truncado.d.ts +30 -0
  120. package/dist-lib/src/utils/use-title-se-truncado.d.ts.map +1 -0
  121. package/dist-lib/src/utils/use-title-se-truncado.mjs +34 -0
  122. package/dist-lib/src/utils/use-title-se-truncado.mjs.map +1 -0
  123. 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={p.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
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={p.href} />}
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={p.href} />}
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={p.href} />` |
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={p.href} />` |
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 `pr-pad-6xl` pra faixa do X — com `hideClose`, passe `pr-0` se quiser a largura toda. **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`. |
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={p.href} />} … />`
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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "pacote": "@snksergio/design-system",
3
- "versao": "0.67.0",
4
- "commit": "38c510a",
3
+ "versao": "0.68.0",
4
+ "commit": "bb6e88e",
5
5
  "gerado": "2026-09-24",
6
6
  "conteudo": {
7
7
  "roteiros": [
@@ -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;
@@ -1 +1 @@
1
- {"version":3,"file":"shadcn.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"shadcn.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -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 AlertDialogContent = React__namespace.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxRuntime.jsxs(AlertDialogPortal, { children: [
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)] sm:max-w-modal-xs",
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>\r\n>(({ className, ...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)] sm:max-w-modal-xs\",\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","jsxs"],"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;AAE9D,MAAM,qBAAqBC,iBAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1BG,2BAAAA,KAAC,mBAAA,EACC,UAAA;AAAA,EAAAF,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;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;;;;;;;;;;;;"}
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
- declare const AlertDialogContent: React.ForwardRefExoticComponent<Omit<AlertDialogPrimitive.AlertDialogContentProps & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
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,wKA2BtB,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"}
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 AlertDialogContent = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { children: [
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)] sm:max-w-modal-xs",
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>\r\n>(({ className, ...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)] sm:max-w-modal-xs\",\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;AAE9D,MAAM,qBAAqB,MAAM,WAG/B,CAAC,EAAE,WAAW,GAAG,MAAA,GAAS,QAC1B,qBAAC,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;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;"}
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(({ className, size = "md", children, onClick, href, surfaceLabel, target, renderLink, ...props }, ref) => {
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
  ]