@iclips/ui 2.0.2 → 3.0.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 (122) hide show
  1. package/MIGRATION-v3.md +244 -0
  2. package/README.md +1 -1
  3. package/dist/alert-theme.css +20 -79
  4. package/dist/components/ui/charts/donut-chart.cjs +1 -1
  5. package/dist/components/ui/charts/donut-chart.cjs.map +1 -1
  6. package/dist/components/ui/charts/donut-chart.js +39 -60
  7. package/dist/components/ui/charts/donut-chart.js.map +1 -1
  8. package/dist/components/ui/charts/kpi-card.cjs +1 -1
  9. package/dist/components/ui/charts/kpi-card.cjs.map +1 -1
  10. package/dist/components/ui/charts/kpi-card.js +19 -19
  11. package/dist/components/ui/charts/kpi-card.js.map +1 -1
  12. package/dist/components/ui/data/comment-system.cjs +1 -1
  13. package/dist/components/ui/data/comment-system.cjs.map +1 -1
  14. package/dist/components/ui/data/comment-system.js +67 -67
  15. package/dist/components/ui/data/comment-system.js.map +1 -1
  16. package/dist/components/ui/data/data-table.cjs +1 -1
  17. package/dist/components/ui/data/data-table.cjs.map +1 -1
  18. package/dist/components/ui/data/data-table.js +112 -101
  19. package/dist/components/ui/data/data-table.js.map +1 -1
  20. package/dist/components/ui/data/editorial-calendar.cjs +1 -1
  21. package/dist/components/ui/data/editorial-calendar.cjs.map +1 -1
  22. package/dist/components/ui/data/editorial-calendar.js +291 -291
  23. package/dist/components/ui/data/editorial-calendar.js.map +1 -1
  24. package/dist/components/ui/data/filter-builder.cjs +1 -1
  25. package/dist/components/ui/data/filter-builder.cjs.map +1 -1
  26. package/dist/components/ui/data/filter-builder.js +1 -1
  27. package/dist/components/ui/data/filter-builder.js.map +1 -1
  28. package/dist/components/ui/feedback/empty-state.cjs +1 -1
  29. package/dist/components/ui/feedback/empty-state.cjs.map +1 -1
  30. package/dist/components/ui/feedback/empty-state.js +14 -14
  31. package/dist/components/ui/feedback/empty-state.js.map +1 -1
  32. package/dist/components/ui/feedback/toast.cjs +1 -1
  33. package/dist/components/ui/feedback/toast.cjs.map +1 -1
  34. package/dist/components/ui/feedback/toast.js +53 -22
  35. package/dist/components/ui/feedback/toast.js.map +1 -1
  36. package/dist/components/ui/forms/calendar.cjs +1 -1
  37. package/dist/components/ui/forms/calendar.cjs.map +1 -1
  38. package/dist/components/ui/forms/calendar.js +16 -16
  39. package/dist/components/ui/forms/calendar.js.map +1 -1
  40. package/dist/components/ui/forms/combobox.cjs +1 -1
  41. package/dist/components/ui/forms/combobox.cjs.map +1 -1
  42. package/dist/components/ui/forms/combobox.js +78 -76
  43. package/dist/components/ui/forms/combobox.js.map +1 -1
  44. package/dist/components/ui/forms/file-attachment-list.cjs +1 -1
  45. package/dist/components/ui/forms/file-attachment-list.cjs.map +1 -1
  46. package/dist/components/ui/forms/file-attachment-list.js +4 -4
  47. package/dist/components/ui/forms/file-attachment-list.js.map +1 -1
  48. package/dist/components/ui/forms/file-upload-zone.cjs +2 -2
  49. package/dist/components/ui/forms/file-upload-zone.cjs.map +1 -1
  50. package/dist/components/ui/forms/file-upload-zone.js +55 -49
  51. package/dist/components/ui/forms/file-upload-zone.js.map +1 -1
  52. package/dist/components/ui/forms/form.cjs +1 -1
  53. package/dist/components/ui/forms/form.cjs.map +1 -1
  54. package/dist/components/ui/forms/form.js +11 -11
  55. package/dist/components/ui/forms/form.js.map +1 -1
  56. package/dist/components/ui/forms/input-validation.cjs +1 -1
  57. package/dist/components/ui/forms/input-validation.cjs.map +1 -1
  58. package/dist/components/ui/forms/input-validation.js +20 -20
  59. package/dist/components/ui/forms/input-validation.js.map +1 -1
  60. package/dist/components/ui/forms/multi-select.cjs +1 -1
  61. package/dist/components/ui/forms/multi-select.cjs.map +1 -1
  62. package/dist/components/ui/forms/multi-select.js +69 -67
  63. package/dist/components/ui/forms/multi-select.js.map +1 -1
  64. package/dist/components/ui/forms/radio-group.cjs +1 -1
  65. package/dist/components/ui/forms/radio-group.cjs.map +1 -1
  66. package/dist/components/ui/forms/radio-group.js +19 -19
  67. package/dist/components/ui/forms/radio-group.js.map +1 -1
  68. package/dist/components/ui/forms/social-button.cjs +1 -1
  69. package/dist/components/ui/forms/social-button.cjs.map +1 -1
  70. package/dist/components/ui/forms/social-button.js +6 -6
  71. package/dist/components/ui/forms/social-button.js.map +1 -1
  72. package/dist/components/ui/forms/toggle.cjs +1 -1
  73. package/dist/components/ui/forms/toggle.cjs.map +1 -1
  74. package/dist/components/ui/forms/toggle.js +7 -7
  75. package/dist/components/ui/forms/toggle.js.map +1 -1
  76. package/dist/components/ui/navigation/navigation-menu.cjs +1 -1
  77. package/dist/components/ui/navigation/navigation-menu.cjs.map +1 -1
  78. package/dist/components/ui/navigation/navigation-menu.js +14 -14
  79. package/dist/components/ui/navigation/navigation-menu.js.map +1 -1
  80. package/dist/components/ui/navigation/sidebar.cjs +1 -1
  81. package/dist/components/ui/navigation/sidebar.cjs.map +1 -1
  82. package/dist/components/ui/navigation/sidebar.js +44 -44
  83. package/dist/components/ui/navigation/sidebar.js.map +1 -1
  84. package/dist/components/ui/overlays/context-menu.cjs +1 -1
  85. package/dist/components/ui/overlays/context-menu.cjs.map +1 -1
  86. package/dist/components/ui/overlays/context-menu.js +1 -1
  87. package/dist/components/ui/overlays/context-menu.js.map +1 -1
  88. package/dist/components/ui/overlays/dropdown-menu.cjs +1 -1
  89. package/dist/components/ui/overlays/dropdown-menu.cjs.map +1 -1
  90. package/dist/components/ui/overlays/dropdown-menu.js +5 -5
  91. package/dist/components/ui/overlays/dropdown-menu.js.map +1 -1
  92. package/dist/components/ui/overlays/menubar.cjs +1 -1
  93. package/dist/components/ui/overlays/menubar.cjs.map +1 -1
  94. package/dist/components/ui/overlays/menubar.js +29 -29
  95. package/dist/components/ui/overlays/menubar.js.map +1 -1
  96. package/dist/components/ui/overlays/tooltip.cjs +1 -1
  97. package/dist/components/ui/overlays/tooltip.cjs.map +1 -1
  98. package/dist/components/ui/overlays/tooltip.js +12 -12
  99. package/dist/components/ui/overlays/tooltip.js.map +1 -1
  100. package/dist/components/ui/primitives/avatar.cjs +1 -1
  101. package/dist/components/ui/primitives/avatar.cjs.map +1 -1
  102. package/dist/components/ui/primitives/avatar.js +20 -27
  103. package/dist/components/ui/primitives/avatar.js.map +1 -1
  104. package/dist/components/ui/primitives/badge.cjs +1 -1
  105. package/dist/components/ui/primitives/badge.cjs.map +1 -1
  106. package/dist/components/ui/primitives/badge.js +13 -13
  107. package/dist/components/ui/primitives/badge.js.map +1 -1
  108. package/dist/components/ui/primitives/button.cjs +1 -1
  109. package/dist/components/ui/primitives/button.cjs.map +1 -1
  110. package/dist/components/ui/primitives/button.js +11 -8
  111. package/dist/components/ui/primitives/button.js.map +1 -1
  112. package/dist/index.cjs +1 -1
  113. package/dist/index.css +1 -1
  114. package/dist/index.d.cts +3 -3
  115. package/dist/index.d.ts +3 -3
  116. package/dist/index.js +34 -35
  117. package/dist/index.js.map +1 -1
  118. package/dist/preset.css +1 -2
  119. package/dist/toast-theme.css +100 -64
  120. package/dist/tokens.css +59 -159
  121. package/package.json +5 -3
  122. package/scripts/check-colors.mjs +208 -0
@@ -0,0 +1,244 @@
1
+ # Migração para @iclips/ui v3
2
+
3
+ `3.0.0` reduz a paleta à **paleta iClips** — "um roxo, muito cinza, e cor só onde
4
+ ela significa algo" — e junta as mudanças visuais que iam sair na 2.1. Nenhum
5
+ import nem componente foi renomeado; o que quebra é **cor crua** no seu código e
6
+ dois valores de prop (seção 1).
7
+
8
+ **Comece por aqui**, na raiz do app, depois de instalar a 3.0.0:
9
+
10
+ ```bash
11
+ node node_modules/@iclips/ui/scripts/check-colors.mjs src
12
+ ```
13
+
14
+ Ele lista cada cor crua (`arquivo:linha classe → sugestão`) e não altera nada.
15
+ Exit 0 = nada a migrar.
16
+
17
+ ## 1. Paleta iClips enxuta (BREAKING)
18
+
19
+ ### O que existe agora
20
+
21
+ Só tokens semânticos viram classe: `bg-background`, `bg-card`, `bg-muted`,
22
+ `text-foreground`, `text-muted-foreground`, `border-border`, `border-input`,
23
+ `bg-primary`, `bg-secondary`, `bg-accent`, `bg-destructive`/`success`/`warning`/`info`,
24
+ `text-<status>-emphasis`, `bg-chart-1..8`, `bg-sidebar-*` e afins — mais
25
+ `white`, `black`, `transparent` e `current`.
26
+
27
+ Os primitivos que sobraram (gray 50–900; purple 50–500 e 700, com 300 e 400
28
+ novos; red/green/cyan 600; yellow 500/600) existem só como custom property
29
+ (`var(--gray-100)`), para alimentar os semânticos. **Não geram classe.**
30
+
31
+ ### O que saiu
32
+
33
+ - As famílias `pink`, `blue`, `teal` e `orange` inteiras, e os degraus sem
34
+ consumidor das demais (`purple-600`, `red-50`, `green-200`, `cyan-500`…).
35
+ - Toda classe de cor crua: `bg-gray-100`, `text-purple-600`, `border-red-200`…
36
+ - A **paleta padrão do Tailwind** para quem usa o preset (modo B): o `@theme`
37
+ da lib começa com `--color-*: initial`, então `bg-zinc-900`, `text-sky-500`
38
+ etc. deixam de gerar CSS. Classe que não gera CSS não dá erro — ela só não
39
+ pinta. Por isso rode o `check-colors.mjs`.
40
+ - `var(--pink-*)`, `var(--blue-*)`, `var(--teal-*)`, `var(--orange-*)` e os
41
+ degraus removidos.
42
+
43
+ > **Tem cores próprias no `@theme` do app?** Declare-as **depois** do
44
+ > `@import "@iclips/ui/preset.css"` — o `--color-*: initial` da lib zera o que
45
+ > vier antes.
46
+
47
+ ### Tabela de equivalência
48
+
49
+ | Antes | Depois |
50
+ | --- | --- |
51
+ | `bg-gray-50` | `bg-background` ou `bg-muted/40` |
52
+ | `bg-gray-100`, `bg-gray-200` | `bg-muted` |
53
+ | `bg-gray-700`…`bg-zinc-900` (superfície escura) | `bg-card` |
54
+ | `border-gray-100`, `border-gray-200` | `border-border` |
55
+ | `border-gray-300` | `border-input` |
56
+ | `text-gray-400`…`text-gray-600` | `text-muted-foreground` |
57
+ | `text-gray-700`…`text-gray-900` | `text-foreground` |
58
+ | `bg-purple-500`, `bg-purple-600` | `bg-primary` |
59
+ | `bg-purple-50`, `bg-purple-100` | `bg-secondary` (seleção) |
60
+ | `text-purple-500`, `text-purple-600` | `text-primary` |
61
+ | `text-purple-700` | `text-secondary-foreground` |
62
+ | `border-purple-*`, `ring-purple-*` | `border-primary`, `ring-ring` |
63
+ | `text-red-600`, `text-green-600`, `text-yellow-700`, `text-blue-600` | `text-destructive-emphasis`, `text-success-emphasis`, `text-warning-emphasis`, `text-info-emphasis` |
64
+ | `bg-red-600` / `bg-red-50` (idem green, yellow, blue) | `bg-destructive` / `bg-destructive/10` |
65
+ | `border-red-200` (idem) | `border-destructive/20` |
66
+ | `teal`, `orange`, `pink`, `indigo`, `violet`… | sem equivalente: a paleta não tem essas cores. Em dados, `var(--chart-N)`; fora disso, um token semântico |
67
+
68
+ ### Componentes
69
+
70
+ - **`Avatar`** — `colorVariant` aceita só `"gray"` e `"purple"` (roxo =
71
+ `bg-secondary text-secondary-foreground`). `pink`, `blue`, `cyan`, `teal`,
72
+ `green`, `yellow`, `orange` e `red` não compilam mais no TypeScript: troque por
73
+ `purple` ou `gray`. `getColorFromString` passa a devolver um dos dois.
74
+ - **`DonutChart`** — fatia sem `color` usa `var(--chart-N)`, não mais 10 hex
75
+ da paleta padrão do Tailwind.
76
+ - **`<Button variant="cta">`** e `--cta` — não existem (anunciados para a 2.1,
77
+ que não chegou a sair). Use `variant="default"` para a ação principal.
78
+
79
+ ### Gráficos
80
+
81
+ `--chart-1..8` passam a roxo e cinza alternando claro e escuro, começando pelo
82
+ roxo. Quem usa `var(--chart-N)` vê o gráfico mudar de cor. Categoria é só para
83
+ dados — nunca botão, link ou fundo de seção.
84
+
85
+ ## 2. Hover deixa de ser lavanda; lavanda passa a significar "selecionado"
86
+
87
+ `--accent` e `--secondary` tinham o mesmo valor. Como `--accent` é a cor de
88
+ hover e de item realçado em todo o sistema, e `--secondary` é a de item
89
+ selecionado, **selecionado e sob o cursor pintavam o mesmo pixel**: numa lista
90
+ não havia como distinguir a linha escolhida da linha sob o mouse.
91
+
92
+ Hover foi quem cedeu — dura 200ms, não existe em tela de toque e não diz nada
93
+ sobre o dado.
94
+
95
+ | Token | Claro, antes → depois | Escuro, antes → depois |
96
+ | --------------------------- | ----------------------------------- | --------------------------------- |
97
+ | `--accent` | `purple.50` → `gray.100` | `purple.700` → `gray.600` |
98
+ | `--accent-foreground` | `purple.600` → `--foreground` | `purple.100` → `gray.50` |
99
+ | `--secondary` | sem mudança (`purple.50`) | `gray.700` → `purple.700` |
100
+ | `--secondary-foreground` | sem mudança (`purple.700`) | `gray.50` → `purple.100` |
101
+ | `--sidebar-accent` | `purple.50` → `gray.100` | sem mudança (`gray.700`) |
102
+ | `--sidebar-accent-foreground` | `purple.700` → `--sidebar-foreground` | `purple.100` → `gray.50` |
103
+
104
+ No escuro o defeito era o inverso: `--secondary` tinha a mesma cor de `--card`
105
+ e `--popover`, então item selecionado dentro de popover era invisível.
106
+
107
+ ### O que você vai ver mudar
108
+
109
+ | Onde | Antes | Depois |
110
+ | ------------------------------------------------------- | ------- | --------------------------- |
111
+ | `Button` `outline` e `ghost`, sob o cursor | lavanda | cinza |
112
+ | Item realçado em menus, `Select` e `Command` | lavanda | cinza |
113
+ | Dia de hoje e dia sob o cursor no `Calendar` | lavanda | cinza |
114
+ | Item de sidebar sob o cursor | lavanda | cinza |
115
+ | Item **selecionado** em `Combobox`, `MultiSelect`, faixa do `Calendar` | lavanda (ou só ✓) | lavanda + texto roxo + peso 500 |
116
+ | Item **ativo** na sidebar e no `NavigationMenu` | lavanda | lavanda, sem mudança |
117
+ | `Toggle` ligado, status "aprovação", reação ativa, menção | lavanda | lavanda, sem mudança |
118
+ | `Button`/`Badge` `secondary` **no tema escuro** | cinza | roxo escuro |
119
+
120
+ Foco por teclado nas listas passou a ser um anel, não um fundo, para
121
+ sobreviver sobre qualquer um dos dois.
122
+
123
+ Efeito colateral bem-vindo: passar o mouse por uma barra de ferramentas deixa
124
+ de pintar a tela de roxo da marca.
125
+
126
+ ### Se você usou `bg-accent` para decoração
127
+
128
+ `bg-accent` agora renderiza cinza. Se a intenção era o tom da marca, troque por
129
+ `bg-secondary` — **só em tela sem lista, tabela, menu ou calendário**, onde
130
+ lavanda passou a significar "isto está selecionado".
131
+
132
+ ### Se você sobrescreveu `--accent`
133
+
134
+ Nada muda para você. Vale reavaliar: o papel do token mudou de "realce da
135
+ marca" para "hover neutro".
136
+
137
+ ## 3. Novo token: `--sidebar-selected`
138
+
139
+ - `--sidebar-selected` / `--sidebar-selected-foreground` — item ativo da
140
+ sidebar (os valores que `--sidebar-accent` tinha).
141
+
142
+ Adição; nada existente mudou por causa dela.
143
+
144
+ ## 4. Botões um degrau mais pesados
145
+
146
+ `Button` e `SocialButton` passaram de `font-medium` (500) para `font-semibold`
147
+ (600). Peso 600 agora significa uma coisa só no sistema: ação.
148
+
149
+ Botão com rótulo longo em espaço apertado: confira — 600 ocupa um pouco mais de
150
+ largura.
151
+
152
+ ## 5. Tooltip neutro
153
+
154
+ `Tooltip` era roxo da marca; agora é cinza-escuro no claro e claro no escuro.
155
+ Sobreposição informativa não compete mais com os botões primários.
156
+
157
+ ## 6. Contraste de status (sem ação necessária)
158
+
159
+ - Texto e ícone de status passaram de `text-<status>` para
160
+ `text-<status>-emphasis` (`Form`, `EmptyState`, menus, anexos, validação).
161
+ - No escuro, `--*-emphasis` passou a misturar 45% do status (era 80%, que
162
+ ficava entre 2,4 e 4,2:1 sobre `card`/`popover`) e é redeclarado em `.dark`.
163
+ Se o seu app aplica `.dark` numa seção em vez do `<html>`, o `-emphasis`
164
+ agora acompanha o tema — antes herdava a mistura do claro.
165
+ - As iniciais do `Avatar` e os chips do `EditorialCalendar` misturam o token
166
+ com `--foreground` para passar AA. Se você usa `text-chart-N` em texto,
167
+ faça o mesmo: os tokens de gráfico são cor de preenchimento.
168
+
169
+ ## 6b. Roxo de ação mais claro no escuro
170
+
171
+ No tema escuro, `--primary` passou de purple-200 (`#BB6DF2`) para purple-100
172
+ (`#DAA6FF`). O antigo dava 3,8:1 como texto sobre `card`/`popover` (gray-700),
173
+ abaixo de AA; o novo dá ~6,4:1. Botões `default`, checkbox marcado e slider
174
+ ficam lavanda-claro no escuro, com o mesmo texto quase preto. `ring` e
175
+ `chart-1` continuam em purple-200. Se você fixou `#BB6DF2` como "roxo da marca
176
+ no escuro" no seu app, troque por `var(--primary)`.
177
+
178
+ ## 6c. Estado suave: nada de vermelho, verde, amarelo ou azul cheio
179
+
180
+ Cor de estado não é mais fundo sólido em lugar nenhum. `Button`
181
+ `variant="destructive"` e `"success"`, `Badge variant="destructive"` e os
182
+ `Alert` de estado usam a receita suave: fundo `bg-<status>/10`, texto e ícone
183
+ `text-<status>-emphasis`, borda `border-<status>/20` (Alert: tinta de 10%, era
184
+ 22%). Os nomes de variante não mudam.
185
+
186
+ - Se você passou `className="text-white"` (ou similar) para um Button/Badge de
187
+ estado presumindo fundo cheio, remova: o texto agora é escuro no claro e
188
+ claro no escuro.
189
+ - Não escreva `bg-destructive`/`bg-success`/`bg-warning`/`bg-info` sem
190
+ opacidade no seu app; use a receita acima.
191
+
192
+ ## 6d. Tons de estado mais calmos (sem ação necessária)
193
+
194
+ Os primitivos de estado perderam cerca de 40% da saturação, com a mesma
195
+ luminosidade (o contraste não muda):
196
+
197
+ | Primitivo | Antes | Agora |
198
+ | --- | --- | --- |
199
+ | `red-600` (`destructive`) | `#CC0A1A` | `#AB4A43` |
200
+ | `green-600` (`success`) | `#007F2A` | `#427748` |
201
+ | `cyan-600` (`info`) | `#0053A6` | `#315687` |
202
+ | `yellow-500` (`warning` claro) | `#FAB325` | `#E4BB78` |
203
+ | `yellow-600` (`warning` escuro) | `#E6960B` | `#CFA066` |
204
+
205
+ Quem usa os tokens acompanha sozinho. Se o seu app fixou algum desses hex,
206
+ troque pelo token.
207
+
208
+ ## 7. Alvos de toque (sem ação necessária)
209
+
210
+ Quatro botões de ícone de 24px (`CommentSystem` reagir/excluir,
211
+ `EditorialCalendar` agendar/recolher backlog) mantêm o tamanho visual e ganham
212
+ área de toque de 32–36px.
213
+
214
+ ## 8. Toast: superfície neutra e durações por tipo
215
+
216
+ O visual muda sem mudar a API: `toast`, `toast.success/error/warning/info`,
217
+ `toast.promise` e `<Toaster />` continuam iguais.
218
+
219
+ - **Visual:** o toast não é mais um retângulo colorido. Fundo `card`, título
220
+ `foreground`, descrição `muted-foreground`; a cor de estado fica só no ícone,
221
+ dentro de um disco. O toast de processando (`toast.loading` /
222
+ `toast.promise`) é o único roxo. `toast(msg, { action })` **sem tipo** vem em
223
+ superfície invertida (escura no tema claro): é o padrão para "Desfazer".
224
+ - **Durações padrão** (quando você não passa `duration`): sucesso 4s, aviso 6s,
225
+ informação 5s, sem tipo com `action` 8s. **`toast.error` não some mais
226
+ sozinho**: fica até o usuário fechar, com botão de fechar. Se algum fluxo
227
+ depende do erro sumir, passe `duration` explicitamente.
228
+ - Se você estilizava `.toast-variant-*`, essas classes saíram; o estilo agora
229
+ vive em `[data-sonner-toast].toast[data-type=…]`.
230
+
231
+ ---
232
+
233
+ ## Resumo do que conferir
234
+
235
+ 0. **`check-colors.mjs` sem achados** — seção 1. É o único passo que quebra
236
+ em silêncio.
237
+ 1. Telas com **lista, tabela ou calendário selecionável** — seção 2, para
238
+ melhor.
239
+ 2. Uso de `bg-accent` **decorativo** no seu código.
240
+ 3. `Button`/`Badge` `secondary` no tema escuro.
241
+ 4. Botões com rótulo longo (seção 4).
242
+ 5. Fluxos que contam com `toast.error` sumindo sozinho (seção 8).
243
+
244
+ O resto é repintura sem risco.
package/README.md CHANGED
@@ -94,7 +94,7 @@ Before pushing, run the checks that CI also runs so issues are caught locally fi
94
94
  A few things to keep in mind while working in this repo:
95
95
 
96
96
  - Hardcoded hex colors are blocked by lint in new code — use the semantic design tokens instead (see `src/styles/tokens.css`).
97
- - Run `npx changeset` to add a changeset describing your change. This drives the automated version bump and CHANGELOG entry — a PR without one will not produce a release.
97
+ - Releases are automatic: a push to `master` that changes the published package gets a patch version after CI passes. Run `npx changeset` only to ship a `minor` or `major`, or to write a custom CHANGELOG entry.
98
98
 
99
99
  ### Size variant naming
100
100
 
@@ -1,94 +1,35 @@
1
1
  /*
2
- * Alert Variant Theme Styles
3
- * Uses CSS variables for automatic dark/light mode adaptation.
4
- * These classes are referenced by alert.tsx CVA variants.
5
- *
6
- * Variables used (defined in globals.css, change between modes):
7
- * --info → cyan-500 (light) / cyan-600 (dark)
8
- * --success → green-500 (light) / green-600 (dark)
9
- * --warning → yellow-500 (light) / yellow-600 (dark)
10
- * --destructive → red-500 (light) / red-600 (dark)
11
- * --foreground → gray-800 (light) / gray-50 (dark)
12
- * --card → white (light) / gray-700 (dark)
13
- * --border → gray-200 (light) / gray-600 (dark)
2
+ * Alert de estado — mesma receita suave do resto da lib: tinta de 10% da cor
3
+ * de estado sobre o card, borda levemente tingida, texto e ícone na camada
4
+ * -emphasis (que passa AA nos dois temas). Estado nunca é fundo sólido nem
5
+ * tinta forte: a cor identifica, a superfície não grita.
14
6
  */
15
7
 
16
- /* ── Info ─────────────────────────────────────────────────── */
17
8
  [data-slot="alert"].alert-variant-info {
18
- color: color-mix(in oklab, var(--info) 80%, var(--foreground));
19
- background-color: color-mix(in oklab, var(--info) 22%, var(--card));
20
- border-color: color-mix(in oklab, var(--info) 50%, var(--border));
9
+ --alert-state: var(--info);
10
+ --alert-text: var(--info-emphasis);
21
11
  }
22
-
23
- [data-slot="alert"].alert-variant-info > svg {
24
- color: var(--info);
25
- }
26
-
27
- [data-slot="alert"].alert-variant-info *[data-slot="alert-description"] {
28
- color: color-mix(in oklab, var(--info) 65%, var(--foreground));
29
- }
30
-
31
- [data-slot="alert"].alert-variant-info *[data-slot="alert-title"] {
32
- color: color-mix(in oklab, var(--info) 80%, var(--foreground));
33
- }
34
-
35
- /* ── Success ──────────────────────────────────────────────── */
36
12
  [data-slot="alert"].alert-variant-success {
37
- color: color-mix(in oklab, var(--success) 80%, var(--foreground));
38
- background-color: color-mix(in oklab, var(--success) 22%, var(--card));
39
- border-color: color-mix(in oklab, var(--success) 50%, var(--border));
40
- }
41
-
42
- [data-slot="alert"].alert-variant-success > svg {
43
- color: var(--success);
13
+ --alert-state: var(--success);
14
+ --alert-text: var(--success-emphasis);
44
15
  }
45
-
46
- [data-slot="alert"].alert-variant-success *[data-slot="alert-description"] {
47
- color: color-mix(in oklab, var(--success) 65%, var(--foreground));
48
- }
49
-
50
- [data-slot="alert"].alert-variant-success *[data-slot="alert-title"] {
51
- color: color-mix(in oklab, var(--success) 80%, var(--foreground));
52
- }
53
-
54
- /* ── Warning ──────────────────────────────────────────────── */
55
- /* --warning é o único token de status "claro" (yellow-500). Derivar texto
56
- com o mesmo 80/65% dos outros deixaria ~2,4:1 sobre o fundo pálido — abaixo
57
- de AA. O texto do warning mistura muito mais --foreground (fica âmbar
58
- escuro); o ícone continua no amarelo cheio. */
59
16
  [data-slot="alert"].alert-variant-warning {
60
- color: color-mix(in oklab, var(--warning) 32%, var(--foreground));
61
- background-color: color-mix(in oklab, var(--warning) 22%, var(--card));
62
- border-color: color-mix(in oklab, var(--warning) 50%, var(--border));
63
- }
64
-
65
- [data-slot="alert"].alert-variant-warning > svg {
66
- color: color-mix(in oklab, var(--warning) 80%, var(--foreground));
67
- }
68
-
69
- [data-slot="alert"].alert-variant-warning *[data-slot="alert-description"] {
70
- color: color-mix(in oklab, var(--warning) 26%, var(--foreground));
17
+ --alert-state: var(--warning);
18
+ --alert-text: var(--warning-emphasis);
71
19
  }
72
-
73
- [data-slot="alert"].alert-variant-warning *[data-slot="alert-title"] {
74
- color: color-mix(in oklab, var(--warning) 32%, var(--foreground));
75
- }
76
-
77
- /* ── Destructive ──────────────────────────────────────────── */
78
20
  [data-slot="alert"].alert-variant-destructive {
79
- color: color-mix(in oklab, var(--destructive) 80%, var(--foreground));
80
- background-color: color-mix(in oklab, var(--destructive) 22%, var(--card));
81
- border-color: color-mix(in oklab, var(--destructive) 50%, var(--border));
82
- }
83
-
84
- [data-slot="alert"].alert-variant-destructive > svg {
85
- color: var(--destructive);
21
+ --alert-state: var(--destructive);
22
+ --alert-text: var(--destructive-emphasis);
86
23
  }
87
24
 
88
- [data-slot="alert"].alert-variant-destructive *[data-slot="alert-description"] {
89
- color: color-mix(in oklab, var(--destructive) 65%, var(--foreground));
25
+ [data-slot="alert"][class*="alert-variant-"] {
26
+ color: var(--alert-text);
27
+ background-color: color-mix(in oklab, var(--alert-state) 10%, var(--card));
28
+ border-color: color-mix(in oklab, var(--alert-state) 20%, var(--border));
90
29
  }
91
30
 
92
- [data-slot="alert"].alert-variant-destructive *[data-slot="alert-title"] {
93
- color: color-mix(in oklab, var(--destructive) 80%, var(--foreground));
31
+ [data-slot="alert"][class*="alert-variant-"] > svg,
32
+ [data-slot="alert"][class*="alert-variant-"] *[data-slot="alert-title"],
33
+ [data-slot="alert"][class*="alert-variant-"] *[data-slot="alert-description"] {
34
+ color: var(--alert-text);
94
35
  }
@@ -1,2 +1,2 @@
1
- "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),w=require("react"),h=require("recharts"),p=require("../utils.cjs");function z(n){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const s in n)if(s!=="default"){const l=Object.getOwnPropertyDescriptor(n,s);Object.defineProperty(t,s,l.get?l:{enumerable:!0,get:()=>n[s]})}}return t.default=n,Object.freeze(t)}const b=z(w),v=["#3b82f6","#10b981","#f59e0b","#ef4444","#8b5cf6","#06b6d4","#f97316","#14b8a6","#ec4899","#6366f1"];function D(n){return new Intl.NumberFormat("pt-BR",{style:"currency",currency:"BRL"}).format(n)}function M({layout:n,chartSize:t}){const s=[1,2,3].map(l=>e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-muted shrink-0"}),e.jsx("div",{className:"h-3 bg-muted rounded flex-1"})]},l));return n==="horizontal"?e.jsxs("div",{className:"flex items-center gap-6 w-full animate-pulse",children:[e.jsx("div",{className:"rounded-full bg-muted shrink-0",style:{width:t,height:t}}),e.jsx("div",{className:"flex flex-col gap-2 flex-1",children:s})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center w-full animate-pulse gap-4",children:[e.jsx("div",{className:"rounded-full bg-muted",style:{width:140,height:140}}),e.jsx("div",{className:"flex flex-col gap-2 w-full max-w-50",children:s})]})}function R(){return e.jsxs("div",{className:"flex flex-col items-center justify-center w-full h-full text-muted-foreground gap-2",children:[e.jsx("div",{className:"rounded-full border-4 border-dashed border-muted",style:{width:100,height:100}}),e.jsx("span",{className:"text-sm",children:"Sem dados para exibir"})]})}function k({active:n,payload:t,formatValue:s,total:l}){if(!n||!(t!=null&&t.length))return null;const c=t[0],r=l>0?(c.value/l*100).toFixed(1):"0.0";return e.jsxs("div",{className:"bg-popover border border-border rounded shadow-lg p-2 text-xs min-w-35",children:[e.jsx("div",{className:"font-medium mb-1",children:c.name}),e.jsxs("div",{className:"flex justify-between gap-4",children:[e.jsx("span",{className:"text-muted-foreground",children:"Valor"}),e.jsx("span",{className:"font-mono",children:s(c.value)})]}),e.jsxs("div",{className:"flex justify-between gap-4",children:[e.jsx("span",{className:"text-muted-foreground",children:"%"}),e.jsxs("span",{className:"font-mono",children:[r,"%"]})]})]})}function A({viewBox:n,activeIndex:t,chartData:s,formatValue:l,total:c}){const r=(n==null?void 0:n.cx)??0,i=(n==null?void 0:n.cy)??0,o=t!==null&&t>=0?s[t]:null;if(o){const a=c>0?(o.value/c*100).toFixed(1):"0.0",d=13,j=o.name.length>d?o.name.substring(0,d-1)+"…":o.name;return e.jsxs("g",{children:[e.jsx("text",{x:r,y:i-14,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:10,fill:"var(--muted-foreground)"},children:j}),e.jsx("text",{x:r,y:i+2,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:11,fontWeight:700,fill:o.color},children:l(o.value)}),e.jsxs("text",{x:r,y:i+18,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:11,fill:"var(--muted-foreground)"},children:[a,"%"]})]})}return e.jsxs("g",{children:[e.jsx("text",{x:r,y:i-8,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:10,fill:"var(--muted-foreground)"},children:"Total"}),e.jsx("text",{x:r,y:i+8,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:13,fontWeight:700,fill:"currentColor"},children:l(c)})]})}function C({chartData:n,total:t,formatValue:s,activeIndex:l,onHover:c}){return e.jsxs("div",{className:"flex flex-col gap-1.5",children:[n.map((r,i)=>{const o=t>0?(r.value/t*100).toFixed(1):"0.0",a=l===i,d=l!==null&&!a;return e.jsxs("div",{className:"flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity",style:{opacity:d?.4:1},onMouseEnter:()=>c(i),onMouseLeave:()=>c(null),children:[e.jsx("span",{className:"inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform",style:{background:r.color,transform:a?"scale(1.3)":"scale(1)"}}),e.jsx("span",{className:"flex-1 truncate text-muted-foreground",children:r.name}),e.jsx("span",{className:"font-mono text-foreground",children:s(r.value)}),e.jsxs("span",{className:"text-muted-foreground w-10.5 text-right",children:[o,"%"]})]},i)}),e.jsxs("div",{className:"flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5",children:[e.jsx("span",{className:"flex-1 font-medium",children:"Total"}),e.jsx("span",{className:"font-mono font-semibold",children:s(t)})]})]})}function L({data:n,title:t,formatValue:s=D,className:l,isLoading:c=!1,layout:r="vertical",activeLabel:i=!1,chartSize:o}){const[a,d]=b.useState(null),j=b.useMemo(()=>n.reduce((u,m)=>u+m.value,0),[n]),g=b.useMemo(()=>n.map((u,m)=>({name:u.label,value:u.value,color:u.color??v[m%v.length]})),[n]),x=r==="horizontal",f=o??(x?140:220),y=x?Math.round(f*.34):55,S=x?Math.round(f*.51):85;return e.jsxs("div",{className:p.cn("flex flex-col gap-4 w-full",l),children:[t&&e.jsx("div",{className:"text-sm font-semibold text-foreground",children:t}),c?e.jsx(M,{layout:r,chartSize:f}):n.length===0?e.jsx(R,{}):e.jsxs("div",{className:p.cn(x?"flex items-center gap-4":"flex flex-col gap-4"),children:[e.jsx("div",{className:"shrink-0",style:x?{width:f,height:f}:{width:"100%",height:f},children:e.jsx(h.ResponsiveContainer,{width:"100%",height:"100%",children:e.jsxs(h.PieChart,{children:[e.jsx(h.Pie,{data:g,cx:"50%",cy:"50%",innerRadius:y,outerRadius:S,paddingAngle:2,dataKey:"value",nameKey:"name",onMouseEnter:(u,m)=>d(m),onMouseLeave:()=>d(null),shape:(u,m)=>{var N;return e.jsx(h.Sector,{...u,fill:(N=g[m])==null?void 0:N.color,stroke:"transparent",opacity:a!==null&&a!==m?.4:1})},children:i&&e.jsx(h.Label,{content:e.jsx(A,{activeIndex:a,chartData:g,formatValue:s,total:j})})}),!i&&e.jsx(h.Tooltip,{content:e.jsx(k,{formatValue:s,total:j})})]})})}),e.jsx("div",{className:p.cn(x?"flex-1 min-w-0":""),children:e.jsx(C,{chartData:g,total:j,formatValue:s,activeIndex:a,onHover:d})})]})]})}exports.DonutChart=L;
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const e=require("react/jsx-runtime"),w=require("react"),h=require("recharts"),p=require("../utils.cjs");function z(n){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(n){for(const s in n)if(s!=="default"){const l=Object.getOwnPropertyDescriptor(n,s);Object.defineProperty(t,s,l.get?l:{enumerable:!0,get:()=>n[s]})}}return t.default=n,Object.freeze(t)}const N=z(w),b=Array.from({length:8},(n,t)=>`var(--chart-${t+1})`);function A(n){return new Intl.NumberFormat("pt-BR",{style:"currency",currency:"BRL"}).format(n)}function D({layout:n,chartSize:t}){const s=[1,2,3].map(l=>e.jsxs("div",{className:"flex items-center gap-2",children:[e.jsx("div",{className:"w-2.5 h-2.5 rounded-full bg-muted shrink-0"}),e.jsx("div",{className:"h-3 bg-muted rounded flex-1"})]},l));return n==="horizontal"?e.jsxs("div",{className:"flex items-center gap-6 w-full animate-pulse",children:[e.jsx("div",{className:"rounded-full bg-muted shrink-0",style:{width:t,height:t}}),e.jsx("div",{className:"flex flex-col gap-2 flex-1",children:s})]}):e.jsxs("div",{className:"flex flex-col items-center justify-center w-full animate-pulse gap-4",children:[e.jsx("div",{className:"rounded-full bg-muted",style:{width:140,height:140}}),e.jsx("div",{className:"flex flex-col gap-2 w-full max-w-50",children:s})]})}function M(){return e.jsxs("div",{className:"flex flex-col items-center justify-center w-full h-full text-muted-foreground gap-2",children:[e.jsx("div",{className:"rounded-full border-4 border-dashed border-muted",style:{width:100,height:100}}),e.jsx("span",{className:"text-sm",children:"Sem dados para exibir"})]})}function R({active:n,payload:t,formatValue:s,total:l}){if(!n||!(t!=null&&t.length))return null;const c=t[0],r=l>0?(c.value/l*100).toFixed(1):"0.0";return e.jsxs("div",{className:"bg-popover border border-border rounded shadow-lg p-2 text-xs min-w-35",children:[e.jsx("div",{className:"font-medium mb-1",children:c.name}),e.jsxs("div",{className:"flex justify-between gap-4",children:[e.jsx("span",{className:"text-muted-foreground",children:"Valor"}),e.jsx("span",{className:"font-mono",children:s(c.value)})]}),e.jsxs("div",{className:"flex justify-between gap-4",children:[e.jsx("span",{className:"text-muted-foreground",children:"%"}),e.jsxs("span",{className:"font-mono",children:[r,"%"]})]})]})}function k({viewBox:n,activeIndex:t,chartData:s,formatValue:l,total:c}){const r=(n==null?void 0:n.cx)??0,i=(n==null?void 0:n.cy)??0,o=t!==null&&t>=0?s[t]:null;if(o){const a=c>0?(o.value/c*100).toFixed(1):"0.0",d=13,j=o.name.length>d?o.name.substring(0,d-1)+"…":o.name;return e.jsxs("g",{children:[e.jsx("text",{x:r,y:i-14,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:10,fill:"var(--muted-foreground)"},children:j}),e.jsx("text",{x:r,y:i+2,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:11,fontWeight:700,fill:o.color},children:l(o.value)}),e.jsxs("text",{x:r,y:i+18,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:11,fill:"var(--muted-foreground)"},children:[a,"%"]})]})}return e.jsxs("g",{children:[e.jsx("text",{x:r,y:i-8,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:10,fill:"var(--muted-foreground)"},children:"Total"}),e.jsx("text",{x:r,y:i+8,textAnchor:"middle",dominantBaseline:"middle",style:{fontSize:13,fontWeight:700,fill:"currentColor"},children:l(c)})]})}function C({chartData:n,total:t,formatValue:s,activeIndex:l,onHover:c}){return e.jsxs("div",{className:"flex flex-col gap-1.5",children:[n.map((r,i)=>{const o=t>0?(r.value/t*100).toFixed(1):"0.0",a=l===i,d=l!==null&&!a;return e.jsxs("div",{className:"flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity",style:{opacity:d?.4:1},onMouseEnter:()=>c(i),onMouseLeave:()=>c(null),children:[e.jsx("span",{className:"inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform",style:{background:r.color,transform:a?"scale(1.3)":"scale(1)"}}),e.jsx("span",{className:"flex-1 truncate text-muted-foreground",children:r.name}),e.jsx("span",{className:"font-mono text-foreground",children:s(r.value)}),e.jsxs("span",{className:"text-muted-foreground w-10.5 text-right",children:[o,"%"]})]},i)}),e.jsxs("div",{className:"flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5",children:[e.jsx("span",{className:"flex-1 font-medium",children:"Total"}),e.jsx("span",{className:"font-mono font-semibold",children:s(t)})]})]})}function L({data:n,title:t,formatValue:s=A,className:l,isLoading:c=!1,layout:r="vertical",activeLabel:i=!1,chartSize:o}){const[a,d]=N.useState(null),j=N.useMemo(()=>n.reduce((u,m)=>u+m.value,0),[n]),g=N.useMemo(()=>n.map((u,m)=>({name:u.label,value:u.value,color:u.color??b[m%b.length]})),[n]),x=r==="horizontal",f=o??(x?140:220),y=x?Math.round(f*.34):55,S=x?Math.round(f*.51):85;return e.jsxs("div",{className:p.cn("flex flex-col gap-4 w-full",l),children:[t&&e.jsx("div",{className:"text-sm font-semibold text-foreground",children:t}),c?e.jsx(D,{layout:r,chartSize:f}):n.length===0?e.jsx(M,{}):e.jsxs("div",{className:p.cn(x?"flex items-center gap-4":"flex flex-col gap-4"),children:[e.jsx("div",{className:"shrink-0",style:x?{width:f,height:f}:{width:"100%",height:f},children:e.jsx(h.ResponsiveContainer,{width:"100%",height:"100%",children:e.jsxs(h.PieChart,{children:[e.jsx(h.Pie,{data:g,cx:"50%",cy:"50%",innerRadius:y,outerRadius:S,paddingAngle:2,dataKey:"value",nameKey:"name",onMouseEnter:(u,m)=>d(m),onMouseLeave:()=>d(null),shape:(u,m)=>{var v;return e.jsx(h.Sector,{...u,fill:(v=g[m])==null?void 0:v.color,stroke:"transparent",opacity:a!==null&&a!==m?.4:1})},children:i&&e.jsx(h.Label,{content:e.jsx(k,{activeIndex:a,chartData:g,formatValue:s,total:j})})}),!i&&e.jsx(h.Tooltip,{content:e.jsx(R,{formatValue:s,total:j})})]})})}),e.jsx("div",{className:p.cn(x?"flex-1 min-w-0":""),children:e.jsx(C,{chartData:g,total:j,formatValue:s,activeIndex:a,onHover:d})})]})]})}exports.DonutChart=L;
2
2
  //# sourceMappingURL=donut-chart.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"donut-chart.cjs","sources":["../../../../src/components/ui/charts/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Label, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from \"recharts\";\nimport { cn } from \"../utils\";\n\n// ── Paleta padrão ─────────────────────────────────────────────────────────────\nconst DEFAULT_COLORS = [\n \"#3b82f6\", // blue-500\n \"#10b981\", // emerald-500\n \"#f59e0b\", // amber-500\n \"#ef4444\", // red-500\n \"#8b5cf6\", // violet-500\n \"#06b6d4\", // cyan-500\n \"#f97316\", // orange-500\n \"#14b8a6\", // teal-500\n \"#ec4899\", // pink-500\n \"#6366f1\", // indigo-500\n];\n\n// ── Tipos ─────────────────────────────────────────────────────────────────────\n\nexport interface DonutChartItem {\n label: string;\n value: number;\n color?: string;\n}\n\nexport interface DonutChartProps {\n data: DonutChartItem[];\n title?: string;\n formatValue?: (v: number) => string;\n className?: string;\n isLoading?: boolean;\n /**\n * Orientação da legenda em relação ao gráfico.\n * - \"vertical\": legenda abaixo do donut (padrão)\n * - \"horizontal\": legenda ao lado direito do donut\n */\n layout?: \"vertical\" | \"horizontal\";\n /**\n * Quando true, exibe o nome, valor e % da categoria no centro do\n * donut ao passar o mouse — desabilita o tooltip flutuante padrão.\n */\n activeLabel?: boolean;\n /**\n * Tamanho (px) do container do gráfico no eixo fixo.\n * - horizontal: largura e altura do donut (padrão: 140)\n * - vertical: altura do donut (padrão: 220)\n */\n chartSize?: number;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction defaultFormat(v: number): string {\n return new Intl.NumberFormat(\"pt-BR\", { style: \"currency\", currency: \"BRL\" }).format(v);\n}\n\n// ── Skeleton ──────────────────────────────────────────────────────────────────\n\nfunction DonutSkeleton({ layout, chartSize }: { layout: \"vertical\" | \"horizontal\"; chartSize: number }) {\n const legendItems = [1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"w-2.5 h-2.5 rounded-full bg-muted shrink-0\" />\n <div className=\"h-3 bg-muted rounded flex-1\" />\n </div>\n ));\n\n if (layout === \"horizontal\") {\n return (\n <div className=\"flex items-center gap-6 w-full animate-pulse\">\n <div className=\"rounded-full bg-muted shrink-0\" style={{ width: chartSize, height: chartSize }} />\n <div className=\"flex flex-col gap-2 flex-1\">{legendItems}</div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col items-center justify-center w-full animate-pulse gap-4\">\n <div className=\"rounded-full bg-muted\" style={{ width: 140, height: 140 }} />\n <div className=\"flex flex-col gap-2 w-full max-w-50\">{legendItems}</div>\n </div>\n );\n}\n\n// ── Empty state ───────────────────────────────────────────────────────────────\n\nfunction DonutEmpty() {\n return (\n <div className=\"flex flex-col items-center justify-center w-full h-full text-muted-foreground gap-2\">\n <div\n className=\"rounded-full border-4 border-dashed border-muted\"\n style={{ width: 100, height: 100 }}\n />\n <span className=\"text-sm\">Sem dados para exibir</span>\n </div>\n );\n}\n\n// ── Tooltip flutuante (usado quando activeLabel=false) ────────────────────────\n\nfunction CustomTooltip({\n active,\n payload,\n formatValue,\n total,\n}: {\n active?: boolean;\n payload?: any[];\n formatValue: (v: number) => string;\n total: number;\n}) {\n if (!active || !payload?.length) return null;\n const item = payload[0];\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n return (\n <div className=\"bg-popover border border-border rounded shadow-lg p-2 text-xs min-w-35\">\n <div className=\"font-medium mb-1\">{item.name}</div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">Valor</span>\n <span className=\"font-mono\">{formatValue(item.value)}</span>\n </div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">%</span>\n <span className=\"font-mono\">{pct}%</span>\n </div>\n </div>\n );\n}\n\n// ── Label central (usado quando activeLabel=true) ─────────────────────────────\n\ninterface CenterLabelProps {\n viewBox?: { cx: number; cy: number; innerRadius?: number; outerRadius?: number };\n activeIndex: number | null;\n chartData: Array<{ name: string; value: number; color: string }>;\n formatValue: (v: number) => string;\n total: number;\n}\n\nfunction CenterLabel({ viewBox, activeIndex, chartData, formatValue, total }: CenterLabelProps) {\n const cx = viewBox?.cx ?? 0;\n const cy = viewBox?.cy ?? 0;\n\n const item = activeIndex !== null && activeIndex >= 0 ? chartData[activeIndex] : null;\n\n if (item) {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const maxLen = 13;\n const name = item.name.length > maxLen ? item.name.substring(0, maxLen - 1) + \"…\" : item.name;\n return (\n <g>\n <text\n x={cx}\n y={cy - 14}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n {name}\n </text>\n <text\n x={cx}\n y={cy + 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fontWeight: 700, fill: item.color }}\n >\n {formatValue(item.value)}\n </text>\n <text\n x={cx}\n y={cy + 18}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fill: \"var(--muted-foreground)\" }}\n >\n {pct}%\n </text>\n </g>\n );\n }\n\n return (\n <g>\n <text\n x={cx}\n y={cy - 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n Total\n </text>\n <text\n x={cx}\n y={cy + 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 13, fontWeight: 700, fill: \"currentColor\" }}\n >\n {formatValue(total)}\n </text>\n </g>\n );\n}\n\n// ── Legenda ───────────────────────────────────────────────────────────────────\n\nfunction DonutLegend({\n chartData,\n total,\n formatValue,\n activeIndex,\n onHover,\n}: {\n chartData: Array<{ name: string; value: number; color: string }>;\n total: number;\n formatValue: (v: number) => string;\n activeIndex: number | null;\n onHover: (index: number | null) => void;\n}) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chartData.map((item, i) => {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const isActive = activeIndex === i;\n const isDimmed = activeIndex !== null && !isActive;\n return (\n <div\n key={i}\n className=\"flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity\"\n style={{ opacity: isDimmed ? 0.4 : 1 }}\n onMouseEnter={() => onHover(i)}\n onMouseLeave={() => onHover(null)}\n >\n <span\n className=\"inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform\"\n style={{\n background: item.color,\n transform: isActive ? \"scale(1.3)\" : \"scale(1)\",\n }}\n />\n <span className=\"flex-1 truncate text-muted-foreground\">{item.name}</span>\n <span className=\"font-mono text-foreground\">{formatValue(item.value)}</span>\n <span className=\"text-muted-foreground w-10.5 text-right\">{pct}%</span>\n </div>\n );\n })}\n <div className=\"flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5\">\n <span className=\"flex-1 font-medium\">Total</span>\n <span className=\"font-mono font-semibold\">{formatValue(total)}</span>\n </div>\n </div>\n );\n}\n\n// ── Componente principal ──────────────────────────────────────────────────────\n\nexport function DonutChart({\n data,\n title,\n formatValue = defaultFormat,\n className,\n isLoading = false,\n layout = \"vertical\",\n activeLabel = false,\n chartSize: chartSizeProp,\n}: DonutChartProps) {\n const [activeIndex, setActiveIndex] = React.useState<number | null>(null);\n\n const total = React.useMemo(() => data.reduce((acc, d) => acc + d.value, 0), [data]);\n\n const chartData = React.useMemo(\n () =>\n data.map((item, i) => ({\n name: item.label,\n value: item.value,\n color: item.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length],\n })),\n [data],\n );\n\n const isHorizontal = layout === \"horizontal\";\n const chartSize = chartSizeProp ?? (isHorizontal ? 140 : 220);\n const innerRadius = isHorizontal ? Math.round(chartSize * 0.34) : 55;\n const outerRadius = isHorizontal ? Math.round(chartSize * 0.51) : 85;\n\n return (\n <div className={cn(\"flex flex-col gap-4 w-full\", className)}>\n {title && <div className=\"text-sm font-semibold text-foreground\">{title}</div>}\n\n {isLoading ? (\n <DonutSkeleton layout={layout} chartSize={chartSize} />\n ) : data.length === 0 ? (\n <DonutEmpty />\n ) : (\n <div className={cn(isHorizontal ? \"flex items-center gap-4\" : \"flex flex-col gap-4\")}>\n {/* Gráfico */}\n <div\n className=\"shrink-0\"\n style={\n isHorizontal\n ? { width: chartSize, height: chartSize }\n : { width: \"100%\", height: chartSize }\n }\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={chartData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={2}\n dataKey=\"value\"\n nameKey=\"name\"\n onMouseEnter={(_, index) => setActiveIndex(index)}\n onMouseLeave={() => setActiveIndex(null)}\n shape={(props, index) => (\n <Sector\n {...props}\n fill={chartData[index]?.color}\n stroke=\"transparent\"\n opacity={activeIndex !== null && activeIndex !== index ? 0.4 : 1}\n />\n )}\n >\n {activeLabel && (\n <Label\n content={\n <CenterLabel\n activeIndex={activeIndex}\n chartData={chartData}\n formatValue={formatValue}\n total={total}\n />\n }\n />\n )}\n </Pie>\n {!activeLabel && (\n <Tooltip\n content={<CustomTooltip formatValue={formatValue} total={total} />}\n />\n )}\n </PieChart>\n </ResponsiveContainer>\n </div>\n\n {/* Legenda */}\n <div className={cn(isHorizontal ? \"flex-1 min-w-0\" : \"\")}>\n <DonutLegend\n chartData={chartData}\n total={total}\n formatValue={formatValue}\n activeIndex={activeIndex}\n onHover={setActiveIndex}\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":["DEFAULT_COLORS","defaultFormat","v","DonutSkeleton","layout","chartSize","legendItems","i","jsxs","jsx","DonutEmpty","CustomTooltip","active","payload","formatValue","total","item","pct","CenterLabel","viewBox","activeIndex","chartData","cx","cy","maxLen","name","DonutLegend","onHover","isActive","isDimmed","DonutChart","data","title","className","isLoading","activeLabel","chartSizeProp","setActiveIndex","React","acc","d","isHorizontal","innerRadius","outerRadius","cn","ResponsiveContainer","PieChart","Pie","_","index","props","Sector","_a","Label","Tooltip"],"mappings":"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"}
1
+ {"version":3,"file":"donut-chart.cjs","sources":["../../../../src/components/ui/charts/donut-chart.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport { Label, Pie, PieChart, ResponsiveContainer, Sector, Tooltip } from \"recharts\";\nimport { cn } from \"../utils\";\n\n// ── Paleta padrão ─────────────────────────────────────────────────────────────\n// Séries de gráfico da paleta iClips (roxo + cinza, vizinhas distintas em luminância).\nconst DEFAULT_COLORS = Array.from({ length: 8 }, (_, i) => `var(--chart-${i + 1})`);\n\n// ── Tipos ─────────────────────────────────────────────────────────────────────\n\nexport interface DonutChartItem {\n label: string;\n value: number;\n color?: string;\n}\n\nexport interface DonutChartProps {\n data: DonutChartItem[];\n title?: string;\n formatValue?: (v: number) => string;\n className?: string;\n isLoading?: boolean;\n /**\n * Orientação da legenda em relação ao gráfico.\n * - \"vertical\": legenda abaixo do donut (padrão)\n * - \"horizontal\": legenda ao lado direito do donut\n */\n layout?: \"vertical\" | \"horizontal\";\n /**\n * Quando true, exibe o nome, valor e % da categoria no centro do\n * donut ao passar o mouse — desabilita o tooltip flutuante padrão.\n */\n activeLabel?: boolean;\n /**\n * Tamanho (px) do container do gráfico no eixo fixo.\n * - horizontal: largura e altura do donut (padrão: 140)\n * - vertical: altura do donut (padrão: 220)\n */\n chartSize?: number;\n}\n\n// ── Helpers ───────────────────────────────────────────────────────────────────\n\nfunction defaultFormat(v: number): string {\n return new Intl.NumberFormat(\"pt-BR\", { style: \"currency\", currency: \"BRL\" }).format(v);\n}\n\n// ── Skeleton ──────────────────────────────────────────────────────────────────\n\nfunction DonutSkeleton({ layout, chartSize }: { layout: \"vertical\" | \"horizontal\"; chartSize: number }) {\n const legendItems = [1, 2, 3].map((i) => (\n <div key={i} className=\"flex items-center gap-2\">\n <div className=\"w-2.5 h-2.5 rounded-full bg-muted shrink-0\" />\n <div className=\"h-3 bg-muted rounded flex-1\" />\n </div>\n ));\n\n if (layout === \"horizontal\") {\n return (\n <div className=\"flex items-center gap-6 w-full animate-pulse\">\n <div className=\"rounded-full bg-muted shrink-0\" style={{ width: chartSize, height: chartSize }} />\n <div className=\"flex flex-col gap-2 flex-1\">{legendItems}</div>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col items-center justify-center w-full animate-pulse gap-4\">\n <div className=\"rounded-full bg-muted\" style={{ width: 140, height: 140 }} />\n <div className=\"flex flex-col gap-2 w-full max-w-50\">{legendItems}</div>\n </div>\n );\n}\n\n// ── Empty state ───────────────────────────────────────────────────────────────\n\nfunction DonutEmpty() {\n return (\n <div className=\"flex flex-col items-center justify-center w-full h-full text-muted-foreground gap-2\">\n <div\n className=\"rounded-full border-4 border-dashed border-muted\"\n style={{ width: 100, height: 100 }}\n />\n <span className=\"text-sm\">Sem dados para exibir</span>\n </div>\n );\n}\n\n// ── Tooltip flutuante (usado quando activeLabel=false) ────────────────────────\n\nfunction CustomTooltip({\n active,\n payload,\n formatValue,\n total,\n}: {\n active?: boolean;\n payload?: any[];\n formatValue: (v: number) => string;\n total: number;\n}) {\n if (!active || !payload?.length) return null;\n const item = payload[0];\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n return (\n <div className=\"bg-popover border border-border rounded shadow-lg p-2 text-xs min-w-35\">\n <div className=\"font-medium mb-1\">{item.name}</div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">Valor</span>\n <span className=\"font-mono\">{formatValue(item.value)}</span>\n </div>\n <div className=\"flex justify-between gap-4\">\n <span className=\"text-muted-foreground\">%</span>\n <span className=\"font-mono\">{pct}%</span>\n </div>\n </div>\n );\n}\n\n// ── Label central (usado quando activeLabel=true) ─────────────────────────────\n\ninterface CenterLabelProps {\n viewBox?: { cx: number; cy: number; innerRadius?: number; outerRadius?: number };\n activeIndex: number | null;\n chartData: Array<{ name: string; value: number; color: string }>;\n formatValue: (v: number) => string;\n total: number;\n}\n\nfunction CenterLabel({ viewBox, activeIndex, chartData, formatValue, total }: CenterLabelProps) {\n const cx = viewBox?.cx ?? 0;\n const cy = viewBox?.cy ?? 0;\n\n const item = activeIndex !== null && activeIndex >= 0 ? chartData[activeIndex] : null;\n\n if (item) {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const maxLen = 13;\n const name = item.name.length > maxLen ? item.name.substring(0, maxLen - 1) + \"…\" : item.name;\n return (\n <g>\n <text\n x={cx}\n y={cy - 14}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n {name}\n </text>\n <text\n x={cx}\n y={cy + 2}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fontWeight: 700, fill: item.color }}\n >\n {formatValue(item.value)}\n </text>\n <text\n x={cx}\n y={cy + 18}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 11, fill: \"var(--muted-foreground)\" }}\n >\n {pct}%\n </text>\n </g>\n );\n }\n\n return (\n <g>\n <text\n x={cx}\n y={cy - 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 10, fill: \"var(--muted-foreground)\" }}\n >\n Total\n </text>\n <text\n x={cx}\n y={cy + 8}\n textAnchor=\"middle\"\n dominantBaseline=\"middle\"\n style={{ fontSize: 13, fontWeight: 700, fill: \"currentColor\" }}\n >\n {formatValue(total)}\n </text>\n </g>\n );\n}\n\n// ── Legenda ───────────────────────────────────────────────────────────────────\n\nfunction DonutLegend({\n chartData,\n total,\n formatValue,\n activeIndex,\n onHover,\n}: {\n chartData: Array<{ name: string; value: number; color: string }>;\n total: number;\n formatValue: (v: number) => string;\n activeIndex: number | null;\n onHover: (index: number | null) => void;\n}) {\n return (\n <div className=\"flex flex-col gap-1.5\">\n {chartData.map((item, i) => {\n const pct = total > 0 ? ((item.value / total) * 100).toFixed(1) : \"0.0\";\n const isActive = activeIndex === i;\n const isDimmed = activeIndex !== null && !isActive;\n return (\n <div\n key={i}\n className=\"flex items-center gap-2 text-xs cursor-default rounded px-1 py-0.5 transition-opacity\"\n style={{ opacity: isDimmed ? 0.4 : 1 }}\n onMouseEnter={() => onHover(i)}\n onMouseLeave={() => onHover(null)}\n >\n <span\n className=\"inline-block w-2.5 h-2.5 rounded-full shrink-0 transition-transform\"\n style={{\n background: item.color,\n transform: isActive ? \"scale(1.3)\" : \"scale(1)\",\n }}\n />\n <span className=\"flex-1 truncate text-muted-foreground\">{item.name}</span>\n <span className=\"font-mono text-foreground\">{formatValue(item.value)}</span>\n <span className=\"text-muted-foreground w-10.5 text-right\">{pct}%</span>\n </div>\n );\n })}\n <div className=\"flex items-center gap-2 text-xs border-t border-border pt-1.5 mt-0.5\">\n <span className=\"flex-1 font-medium\">Total</span>\n <span className=\"font-mono font-semibold\">{formatValue(total)}</span>\n </div>\n </div>\n );\n}\n\n// ── Componente principal ──────────────────────────────────────────────────────\n\nexport function DonutChart({\n data,\n title,\n formatValue = defaultFormat,\n className,\n isLoading = false,\n layout = \"vertical\",\n activeLabel = false,\n chartSize: chartSizeProp,\n}: DonutChartProps) {\n const [activeIndex, setActiveIndex] = React.useState<number | null>(null);\n\n const total = React.useMemo(() => data.reduce((acc, d) => acc + d.value, 0), [data]);\n\n const chartData = React.useMemo(\n () =>\n data.map((item, i) => ({\n name: item.label,\n value: item.value,\n color: item.color ?? DEFAULT_COLORS[i % DEFAULT_COLORS.length],\n })),\n [data],\n );\n\n const isHorizontal = layout === \"horizontal\";\n const chartSize = chartSizeProp ?? (isHorizontal ? 140 : 220);\n const innerRadius = isHorizontal ? Math.round(chartSize * 0.34) : 55;\n const outerRadius = isHorizontal ? Math.round(chartSize * 0.51) : 85;\n\n return (\n <div className={cn(\"flex flex-col gap-4 w-full\", className)}>\n {title && <div className=\"text-sm font-semibold text-foreground\">{title}</div>}\n\n {isLoading ? (\n <DonutSkeleton layout={layout} chartSize={chartSize} />\n ) : data.length === 0 ? (\n <DonutEmpty />\n ) : (\n <div className={cn(isHorizontal ? \"flex items-center gap-4\" : \"flex flex-col gap-4\")}>\n {/* Gráfico */}\n <div\n className=\"shrink-0\"\n style={\n isHorizontal\n ? { width: chartSize, height: chartSize }\n : { width: \"100%\", height: chartSize }\n }\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\">\n <PieChart>\n <Pie\n data={chartData}\n cx=\"50%\"\n cy=\"50%\"\n innerRadius={innerRadius}\n outerRadius={outerRadius}\n paddingAngle={2}\n dataKey=\"value\"\n nameKey=\"name\"\n onMouseEnter={(_, index) => setActiveIndex(index)}\n onMouseLeave={() => setActiveIndex(null)}\n shape={(props, index) => (\n <Sector\n {...props}\n fill={chartData[index]?.color}\n stroke=\"transparent\"\n opacity={activeIndex !== null && activeIndex !== index ? 0.4 : 1}\n />\n )}\n >\n {activeLabel && (\n <Label\n content={\n <CenterLabel\n activeIndex={activeIndex}\n chartData={chartData}\n formatValue={formatValue}\n total={total}\n />\n }\n />\n )}\n </Pie>\n {!activeLabel && (\n <Tooltip\n content={<CustomTooltip formatValue={formatValue} total={total} />}\n />\n )}\n </PieChart>\n </ResponsiveContainer>\n </div>\n\n {/* Legenda */}\n <div className={cn(isHorizontal ? \"flex-1 min-w-0\" : \"\")}>\n <DonutLegend\n chartData={chartData}\n total={total}\n formatValue={formatValue}\n activeIndex={activeIndex}\n onHover={setActiveIndex}\n />\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"names":["DEFAULT_COLORS","_","i","defaultFormat","v","DonutSkeleton","layout","chartSize","legendItems","jsxs","jsx","DonutEmpty","CustomTooltip","active","payload","formatValue","total","item","pct","CenterLabel","viewBox","activeIndex","chartData","cx","cy","maxLen","name","DonutLegend","onHover","isActive","isDimmed","DonutChart","data","title","className","isLoading","activeLabel","chartSizeProp","setActiveIndex","React","acc","d","isHorizontal","innerRadius","outerRadius","cn","ResponsiveContainer","PieChart","Pie","index","props","Sector","_a","Label","Tooltip"],"mappings":"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"}