@rendra-ui/web 2.4.0 → 2.6.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 (73) hide show
  1. package/README.md +33 -1
  2. package/dist/base.css +33 -1
  3. package/dist/{button-C6rjowQi.js → button-B6-644xP.js} +86 -21
  4. package/dist/button-B6-644xP.js.map +1 -0
  5. package/dist/chart.js +63 -15
  6. package/dist/chart.js.map +1 -1
  7. package/dist/cli/auditar.js +1 -1
  8. package/dist/cli/codigos.js +2 -2
  9. package/dist/cli/executar-auditar.js +1 -1
  10. package/dist/cli/help.js +1 -1
  11. package/dist/cli/trocar.js +2 -2
  12. package/dist/cli/typescript-loader.js +1 -1
  13. package/dist/{cn-DXDpTGsu.js → cn-TBlI6-gS.js} +2 -2
  14. package/dist/{cn-DXDpTGsu.js.map → cn-TBlI6-gS.js.map} +1 -1
  15. package/dist/{components-hp7iWioq.js → components-Btk1cUg2.js} +2 -2
  16. package/dist/{components-hp7iWioq.js.map → components-Btk1cUg2.js.map} +1 -1
  17. package/dist/components.css +169 -33
  18. package/dist/document-viewer.js +6 -6
  19. package/dist/document-viewer.js.map +1 -1
  20. package/dist/{empty-state-CdaDLcsB.js → empty-state-DrQjqxvp.js} +36 -12
  21. package/dist/{empty-state-CdaDLcsB.js.map → empty-state-DrQjqxvp.js.map} +1 -1
  22. package/dist/features-max-CC6CorTg.js +3 -0
  23. package/dist/index.js +1075 -339
  24. package/dist/index.js.map +1 -1
  25. package/dist/motion-context-BL545cb6.js +222 -0
  26. package/dist/motion-context-BL545cb6.js.map +1 -0
  27. package/dist/motion-on-DMoTsWrG.js +29 -0
  28. package/dist/motion-on-DMoTsWrG.js.map +1 -0
  29. package/dist/motion.js +3 -0
  30. package/dist/{rendra-provider-7jUgUHzR.js → rendra-provider-CeCkPafT.js} +9 -5
  31. package/dist/rendra-provider-CeCkPafT.js.map +1 -0
  32. package/dist/rich-text-editor.js +4 -4
  33. package/dist/router-bridge.js +5 -4
  34. package/dist/router-bridge.js.map +1 -1
  35. package/dist/tokens.css +63 -15
  36. package/dist/tooltip-kgsorOg6.js +224 -0
  37. package/dist/tooltip-kgsorOg6.js.map +1 -0
  38. package/dist/types/components/rendra-provider.d.ts +12 -1
  39. package/dist/types/components/rendra-router-bridge.d.ts +4 -1
  40. package/dist/types/components/ui/accordion.d.ts +1 -1
  41. package/dist/types/components/ui/badge.d.ts +1 -1
  42. package/dist/types/components/ui/button.d.ts +1 -1
  43. package/dist/types/components/ui/dropdown-menu.d.ts +5 -5
  44. package/dist/types/components/ui/picker-panel.d.ts +1 -1
  45. package/dist/types/components/ui/popover.d.ts +3 -3
  46. package/dist/types/components/ui/radio-group.d.ts +1 -1
  47. package/dist/types/components/ui/skeleton.d.ts +5 -1
  48. package/dist/types/components/ui/stat-card.d.ts +1 -1
  49. package/dist/types/components/ui/table.d.ts +0 -6
  50. package/dist/types/components/ui/tooltip.d.ts +1 -1
  51. package/dist/types/hooks/use-reduced-motion.d.ts +3 -0
  52. package/dist/types/index.d.ts +2 -0
  53. package/dist/types/lib/motion/count.d.ts +17 -0
  54. package/dist/types/lib/motion/features-max.d.ts +1 -0
  55. package/dist/types/lib/motion/index.d.ts +5 -0
  56. package/dist/types/lib/motion/motion-context.d.ts +58 -0
  57. package/dist/types/lib/motion/motion-on.d.ts +12 -0
  58. package/dist/types/lib/motion/native-events.d.ts +16 -0
  59. package/dist/types/lib/motion/presets.d.ts +92 -0
  60. package/dist/types/lib/motion/reorder.d.ts +7 -0
  61. package/dist/types/lib/motion/tokens.d.ts +66 -0
  62. package/dist/types/lib/motion/use-motion-presets.d.ts +8 -0
  63. package/dist/types/motion.d.ts +1 -0
  64. package/dist/{use-breakpoint-Dyp-0E_c.js → use-breakpoint-CNxDwEpc.js} +2 -2
  65. package/dist/{use-breakpoint-Dyp-0E_c.js.map → use-breakpoint-CNxDwEpc.js.map} +1 -1
  66. package/dist/use-motion-presets-DCJPBfNJ.js +401 -0
  67. package/dist/use-motion-presets-DCJPBfNJ.js.map +1 -0
  68. package/dist/widget-grid.js +3 -3
  69. package/package.json +10 -1
  70. package/dist/button-C6rjowQi.js.map +0 -1
  71. package/dist/rendra-provider-7jUgUHzR.js.map +0 -1
  72. package/dist/tooltip-bdmbX6vC.js +0 -105
  73. package/dist/tooltip-bdmbX6vC.js.map +0 -1
package/README.md CHANGED
@@ -200,13 +200,38 @@ Tudo configurável por props, com o padrão em `src/config/layout.ts`:
200
200
 
201
201
  Login, esqueci a senha, verificação em duas etapas (2FA), nova senha, cadastro, painel com indicadores e gráficos, listagem com barra de ferramentas e filtros, detalhe com abas, formulário longo em seções, cadastro em wizard, configurações com navegação interna, criação e edição de registro em drawer (com busca de CNPJ e CEP), tarefas, atendimento omnichannel, agenda, funil de vendas em kanban, painel em widgets, galeria e página 404.
202
202
 
203
+ ### Movimento (opcional, desligado por padrão)
204
+
205
+ O `RendraProvider` (e o `RendraRouterBridge`) aceita a prop `motion`: `'off'` (o padrão), `'on'` ou o objeto `rendraMotion` (de `@rendra-ui/web/motion`). Ligado, os componentes animam com o Motion (`motion` 14, já em `dependencies`). Em `'off'` o Motion nem entra no bundle inicial do app: ele só custa bytes a quem liga o `on`. A linguagem é uma só: o que se repete dezenas de vezes por dia (tabela, abas, teclado) não anima a entrada; o toque responde em 120 ms; overlays entram em mola sem quique e saem mais rápido que entram, a partir do gatilho; nada parte de `scale(0)`; movimento reduzido vira só opacidade.
206
+
207
+ ```tsx
208
+ // Carrega o Motion sob demanda: o primeiro quadro sai em `off` e liga quando o chunk chega.
209
+ <RendraProvider motion="on">{/* ... */}</RendraProvider>
210
+ ```
211
+
212
+ Com `motion="on"` (texto), a tela começa em `off` (nunca fica vazia) e liga assim que o chunk do Motion chega, sem remontar nada: estado, foco, campos e overlays abertos ficam como estavam, e o que já estava na tela não repete a animação de entrada (o número de um indicador não volta a 0). Em servidor (SSR) o HTML sai em `off`, e a hidratação bate. Para ter o `on` desde o primeiro quadro e no servidor, importe o objeto do subcaminho e passe-o no lugar do texto; o Motion então vai no bundle de quem importa o objeto:
213
+
214
+ ```tsx
215
+ import { rendraMotion } from '@rendra-ui/web/motion'
216
+
217
+ ;<RendraProvider motion={rendraMotion}>{/* ... */}</RendraProvider>
218
+ ```
219
+
220
+ Com React Router, ponha a prop na ponte, que já entrega ao provider o link, o caminho atual e a navegação: `<RendraRouterBridge motion="on">` ou `<RendraRouterBridge motion={rendraMotion}>`. Montando o `RendraProvider` direto, ele também exige `linkComponent`, `useCurrentPath`, `navigate` e `goBack`. Quem testa a interface em `on` e afirma o resultado logo depois do `render` usa `motion={rendraMotion}` (síncrono); com o texto é preciso esperar o chunk. No clone deste repositório, `rendraMotion` vem de `@/lib/motion/motion-on`.
221
+
222
+ - **Tokens** (variáveis CSS que o host sobrescreve): `--rendra-duration-press|hover|fast|base|move|slow|draw|pulse`, `--rendra-reveal-enter|stagger|count|draw` (a chegada do painel, mais lenta, de uso único) e as curvas `--rendra-ease-out|in-out|spring|move|drawer`, com os aliases do Tailwind `ease-move` e `ease-drawer`.
223
+ - **Presets** em `src/lib/motion` (`presets.ts`, `tokens.ts`; no registry do shadcn vão todos os arquivos da pasta): `press`, `open`, `sheet`, `tooltip`, `swap`, `indicator`, `move`, `settle`, `reveal`, `stagger`. Os nomes antigos (`fade`, `scale`, `pop`, `slide`, `layout`, `tap`, `bump`) continuam.
224
+ - **Telas de comparação**: `/componentes-motion` (as mesmas seções de `/componentes`, com a chave Ligado/Desligado) e `/dashboard-motion` (painel que chega com contagem, gráficos desenhando e cascata). Ficam fora das suítes de captura.
225
+ - **O `off` também mudou nesta versão** (2.5.0), só em tempo, curva, origem e estado intermediário, nunca em quadro parado: o toque do botão é de 120 ms a 0,97; popover, menu, tooltip e painel do Select nascem do gatilho; toda saída de overlay dura 120 ms; o tooltip em `instant-open` não anima; a bolinha do radio e o check do checkbox partem de 0,9, não de zero; com movimento reduzido os overlays só esmaecem. As 152 capturas de referência não mudam.
226
+ - `npm run test:motion` mede tempo, escala, origem e gesto no navegador (`BASE_URL` para o seu servidor).
227
+
203
228
  ### Vitrine, Storybook e testes
204
229
 
205
230
  - **Vitrine** na rota `/componentes`: cada componente com variantes, tamanhos e estados (padrão, hover, foco, ativo, desabilitado, carregando, erro) e as regras de composição.
206
231
  - **Storybook 10** com mais de 100 stories: viewport de celular por padrão, com tablet e desktop, e alternância de template, paleta e modo claro/escuro na barra.
207
232
  - **Testes de layout com Playwright** (`npm run test:layout`): 16 rotas × 5 larguras (360, 390, 768, 1280 e 1920) × 3 templates no modo claro, mais 360 e 1280 no escuro, somando 336 testes. Falham se houver rolagem horizontal, elemento maior que a tela, toque menor que 44px no celular ou erro de JavaScript, e geram capturas de todas as telas.
208
233
  - **Testes de acessibilidade com axe-core** (`npm run test:a11y`): 144 testes, todas as rotas nos três templates, claro e escuro, mais a paleta Ardósia e o celular. Falham em qualquer violação WCAG 2.1 A ou AA, inclusive contraste.
209
- - **Regressão visual** (`npm run test:visual`): 128 capturas comparadas com a referência aprovada. Pegam cor que mudou, espaçamento que quebrou e ícone que sumiu. As referências são geradas no Linux do CI; depois de uma mudança visual intencional, aplique o rótulo `atualizar-visual` no Pull Request.
234
+ - **Regressão visual** (`npm run test:visual`): 152 capturas comparadas com a referência aprovada. Pegam cor que mudou, espaçamento que quebrou e ícone que sumiu. As referências são geradas no Linux do CI; depois de uma mudança visual intencional, aplique o rótulo `atualizar-visual` no Pull Request.
210
235
  - **Testes unitários e de componente com Vitest e Testing Library** (`npm test`; `npm run test:coverage` com o piso por arquivo): comportamento de Table (ordenação, busca, paginação, seleção, ações em massa, modo remoto e cartões no celular), Select, Input (máscaras, centavos e busca de CEP e CNPJ), Modal, Drawer, campos de formulário e navegação, além de validadores, máscaras, buscas e o gerador de paletas (AA garantido em todas). Cada componente tem o teste ao lado, e ele vai junto quando você copia o componente para outro projeto.
211
236
  - **CI no GitHub Actions**: todo Pull Request passa por tipos, lint, formatação, regras, unitários, build, Storybook e os três testes no navegador. A `main` só aceita merge com tudo verde. Os alertas de segurança do GitHub avisam de dependência vulnerável; as atualizações de versão são feitas à mão (veja o `CONTRIBUTING.md`).
212
237
  - **Verificador de regras** (`npm run check:rules`): barra cor fixa, valor arbitrário, degrau fora da escala, estilo inline, fonte fixa, `100vh` e arquivos duplicados ou mobile. A mesma varredura, com as 17 regras, roda em qualquer projeto por `npx @rendra-ui/web auditar --all`.
@@ -459,3 +484,10 @@ Contribuições são bem-vindas pelo fluxo descrito em [CONTRIBUTING.md](CONTRIB
459
484
  ## Créditos
460
485
 
461
486
  Construído sobre Tailwind CSS, shadcn/ui, Radix UI, Lucide, TanStack Table, cmdk, Sonner, React Hook Form, Zod, IMask, React Day Picker, Recharts, Storybook e Playwright. As fontes Poppins, Inter e DM Sans são distribuídas sob a SIL Open Font License. Os símbolos dos templates são ilustrativos. O logotipo é montado pelo componente `BrandLogo` com as cores da paleta ativa.
487
+
488
+ ### Ferramentas de movimento
489
+
490
+ A linguagem de movimento do Rendra (tempo, curva, origem, mola e o que não anima) foi desenhada e auditada com duas skills de agente de código. Elas ficam só na máquina de quem desenvolve; o Rendra não as redistribui.
491
+
492
+ - **Emil Kowalski**: as skills `emil-design-eng`, `animate`, `review-animations`, `animation-performance`, `find-animation-opportunities` e `improve-animations`, sob a licença **MIT**. Repositório de origem: [github.com/emilkowalski/skills](https://github.com/emilkowalski/skills). As regras de frequência, de assimetria entre entrada e saída, de origem do gatilho e do orçamento de tempo que o motion deste projeto segue vêm do trabalho dele.
493
+ - **Impeccable**: a skill `impeccable` (modo Operar: `audit`, `polish`, `harden`), sob a licença **Apache 2.0**. Repositório de origem: [github.com/pbakaus/impeccable](https://github.com/pbakaus/impeccable).
package/dist/base.css CHANGED
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.4.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.6.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
 
3
3
  @layer base {
4
4
  *,
@@ -241,6 +241,38 @@
241
241
  animation-iteration-count: 1 !important;
242
242
  transition-duration: 0.01ms !important;
243
243
  scroll-behavior: auto !important;
244
+ --tw-enter-scale: 1 !important;
245
+ --tw-enter-translate-x: 0 !important;
246
+ --tw-enter-translate-y: 0 !important;
247
+ --tw-enter-rotate: 0 !important;
248
+ --tw-enter-blur: 0 !important;
249
+ --tw-exit-scale: 1 !important;
250
+ --tw-exit-translate-x: 0 !important;
251
+ --tw-exit-translate-y: 0 !important;
252
+ --tw-exit-rotate: 0 !important;
253
+ --tw-exit-blur: 0 !important;
254
+ }
255
+ [data-state='open'],
256
+ [data-state='closed'],
257
+ [data-state='delayed-open'],
258
+ [data-state='instant-open'] {
259
+ animation-duration: var(--rendra-duration-base) !important;
260
+ }
261
+ @keyframes rd-reduced-in {
262
+ from {
263
+ opacity: 0;
264
+ }
265
+ }
266
+ @keyframes rd-reduced-out {
267
+ to {
268
+ opacity: 0;
269
+ }
270
+ }
271
+ [data-state='open'].overflow-hidden {
272
+ animation-name: rd-reduced-in !important;
273
+ }
274
+ [data-state='closed'].overflow-hidden {
275
+ animation-name: rd-reduced-out !important;
244
276
  }
245
277
  }
246
278
  }
@@ -1,24 +1,13 @@
1
- /*! Rendra Design System v2.4.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-DXDpTGsu.js";
3
- import { p as resolveCatalogCode } from "./components-hp7iWioq.js";
1
+ /*! Rendra Design System v2.6.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as cn } from "./cn-TBlI6-gS.js";
3
+ import { a as useMotionMode, o as useMotionOn, u as usePrefersReducedMotion } from "./motion-context-BL545cb6.js";
4
+ import { t as useMotionPresets } from "./use-motion-presets-DCJPBfNJ.js";
5
+ import { p as resolveCatalogCode } from "./components-Btk1cUg2.js";
4
6
  import { Loader2 } from "lucide-react";
5
7
  import { Slot } from "radix-ui";
8
+ import { forwardRef } from "react";
6
9
  import { jsx, jsxs } from "react/jsx-runtime";
7
- import { forwardRef, useSyncExternalStore } from "react";
8
10
  import { cva } from "class-variance-authority";
9
- //#region src/hooks/use-reduced-motion.ts
10
- function subscribe(cb) {
11
- const mq = matchMedia("(prefers-reduced-motion: reduce)");
12
- mq.addEventListener("change", cb);
13
- return () => mq.removeEventListener("change", cb);
14
- }
15
- function getCurrent() {
16
- return matchMedia("(prefers-reduced-motion: reduce)").matches;
17
- }
18
- function usePrefersReducedMotion() {
19
- return useSyncExternalStore(subscribe, getCurrent, () => false);
20
- }
21
- //#endregion
22
11
  //#region src/components/ui/spinner.tsx
23
12
  var sizeClass = {
24
13
  sm: "size-icon-sm",
@@ -48,13 +37,44 @@ function Spinner({ size = "md", label, className }) {
48
37
  });
49
38
  }
50
39
  //#endregion
40
+ //#region src/lib/motion/native-events.ts
41
+ var NATIVE_EVENTS = {
42
+ onDrag: "drag",
43
+ onDragStart: "dragstart",
44
+ onDragEnd: "dragend",
45
+ onAnimationStart: "animationstart"
46
+ };
47
+ /** Separa as quatro chaves em conflito; o resto segue para o elemento do Motion. */
48
+ function splitNativeEvents(props) {
49
+ const native = {};
50
+ const rest = {};
51
+ for (const [key, value] of Object.entries(props)) if (key in NATIVE_EVENTS) native[key] = value;
52
+ else rest[key] = value;
53
+ return {
54
+ native,
55
+ rest
56
+ };
57
+ }
58
+ /** Liga os manipuladores como ouvintes nativos. Devolve a função que os remove. */
59
+ function bindNativeEvents(el, native) {
60
+ const bound = [];
61
+ for (const [key, type] of Object.entries(NATIVE_EVENTS)) {
62
+ const handler = native[key];
63
+ if (!handler) continue;
64
+ const listener = ((event) => handler(event));
65
+ el.addEventListener(type, listener);
66
+ bound.push([type, listener]);
67
+ }
68
+ return () => bound.forEach(([type, listener]) => el.removeEventListener(type, listener));
69
+ }
70
+ //#endregion
51
71
  //#region src/components/ui/button.tsx
52
72
  /**
53
73
  * Botão único do sistema. Variantes, tamanho, ícone, só ícone, carregamento e
54
74
  * largura total são props: nunca crie outro arquivo de botão.
55
75
  * Altura vem de h-control-*: 44px ou mais no mobile, compacta a partir de md.
56
76
  */
57
- var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform] duration-150 ease-out select-none active:scale-98 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
77
+ var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform] duration-(--rendra-duration-press) ease-out select-none active:scale-97 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:shrink-0", {
58
78
  variants: {
59
79
  variant: {
60
80
  primary: "bg-primary text-primary-foreground shadow-sm hover:bg-primary-hover hover:text-primary-hover-foreground hover:shadow-md active:bg-primary-hover active:text-primary-hover-foreground active:shadow-sm",
@@ -106,13 +126,44 @@ var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justi
106
126
  fullWidth: false
107
127
  }
108
128
  });
129
+ /**
130
+ * O botão de `on`: `m.button` com toque discreto (whileTap). Os eventos que o Motion toma
131
+ * (`onDrag*` e `onAnimationStart`) saem das props dele e viram ouvintes nativos no elemento,
132
+ * então o consumidor continua recebendo o evento. Só existe em `on`; por isso os hooks de motion
133
+ * moram aqui e o `off` não paga nada.
134
+ */
135
+ var MotionButton = forwardRef(function MotionButton(props, ref) {
136
+ const { m } = useMotionOn();
137
+ const presets = useMotionPresets();
138
+ const { native, rest } = splitNativeEvents(props);
139
+ const attach = (el) => {
140
+ assignRef(ref, el);
141
+ if (!el) return;
142
+ const unbind = bindNativeEvents(el, native);
143
+ return () => {
144
+ unbind();
145
+ assignRef(ref, null);
146
+ };
147
+ };
148
+ return /* @__PURE__ */ jsx(m.button, {
149
+ ref: attach,
150
+ whileTap: presets.press.whileTap,
151
+ transition: presets.press.transition,
152
+ ...rest
153
+ });
154
+ });
155
+ function assignRef(ref, value) {
156
+ if (typeof ref === "function") ref(value);
157
+ else if (ref) ref.current = value;
158
+ }
109
159
  var Button = forwardRef(function Button({ className, variant, size, icon, iconRight, iconOnly = false, loading = false, fullWidth = false, asChild = false, disabled, type = "button", children, ...props }, ref) {
160
+ const on = useMotionMode() === "on";
110
161
  const classes = cn(buttonVariants({
111
162
  variant,
112
163
  size,
113
164
  iconOnly,
114
165
  fullWidth
115
- }), className);
166
+ }), on && !asChild && "transition-[color,background-color,border-color,box-shadow] active:scale-100", className);
116
167
  const code = resolveCatalogCode("Button", { variant: variant ?? "primary" });
117
168
  if (asChild) return /* @__PURE__ */ jsx(Slot.Root, {
118
169
  ref,
@@ -122,6 +173,20 @@ var Button = forwardRef(function Button({ className, variant, size, icon, iconRi
122
173
  children
123
174
  });
124
175
  const leading = loading ? /* @__PURE__ */ jsx(Spinner, { size: size === "lg" ? "md" : "sm" }) : icon;
176
+ if (on) return /* @__PURE__ */ jsxs(MotionButton, {
177
+ ref,
178
+ type,
179
+ "data-rendra": code,
180
+ className: classes,
181
+ disabled: disabled || loading,
182
+ "aria-busy": loading || void 0,
183
+ ...props,
184
+ children: [
185
+ leading,
186
+ iconOnly ? loading ? null : children : children,
187
+ !iconOnly && iconRight
188
+ ]
189
+ });
125
190
  return /* @__PURE__ */ jsxs("button", {
126
191
  ref,
127
192
  type,
@@ -138,6 +203,6 @@ var Button = forwardRef(function Button({ className, variant, size, icon, iconRi
138
203
  });
139
204
  });
140
205
  //#endregion
141
- export { usePrefersReducedMotion as i, buttonVariants as n, Spinner as r, Button as t };
206
+ export { buttonVariants as n, Spinner as r, Button as t };
142
207
 
143
- //# sourceMappingURL=button-C6rjowQi.js.map
208
+ //# sourceMappingURL=button-B6-644xP.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-B6-644xP.js","names":[],"sources":["../src/components/ui/spinner.tsx","../src/lib/motion/native-events.ts","../src/components/ui/button.tsx"],"sourcesContent":["import { Loader2 } from 'lucide-react'\nimport { usePrefersReducedMotion } from '@/hooks/use-reduced-motion'\nimport { cn } from '@/lib/cn'\n\n/*\n * Indicador de carregamento único do sistema (A17 do plano da v2). Substitui qualquer\n * `Loader2 animate-spin` solto em Button, Input e Select. Sem label é decorativo\n * (aria-hidden); com label, anuncia o carregamento a quem usa leitor de tela. Para de girar\n * quando o sistema pede menos movimento.\n */\n\nexport interface SpinnerProps {\n size?: 'sm' | 'md' | 'lg'\n /** Nome do que está carregando. Vazio (padrão): decorativo, sem anúncio de leitor de tela. */\n label?: string\n className?: string\n}\n\nconst sizeClass: Record<NonNullable<SpinnerProps['size']>, string> = {\n sm: 'size-icon-sm',\n md: 'size-icon-md',\n lg: 'size-icon-lg',\n}\n\nexport function Spinner({ size = 'md', label, className }: SpinnerProps) {\n const reduced = usePrefersReducedMotion()\n const icon = (\n <Loader2 aria-hidden className={cn(sizeClass[size], reduced ? '' : 'animate-spin')} />\n )\n\n if (!label) {\n return (\n <span data-rendra=\"SPIN-001\" aria-hidden className={cn('inline-flex', className)}>\n {icon}\n </span>\n )\n }\n\n return (\n <span\n data-rendra=\"SPIN-001\"\n role=\"status\"\n className={cn('inline-flex items-center gap-2', className)}\n >\n {icon}\n <span className=\"sr-only\">{label}</span>\n </span>\n )\n}\n","/*\n * O Motion toma `onDrag`, `onDragStart`, `onDragEnd` e `onAnimationStart` para os gestos e\n * animações dele: tira essas props do DOM e as tipa com outra assinatura. Em `m.button` e\n * `m.li`, o consumidor que passa o evento nativo (HTML5 drag, animationstart do CSS) o perderia.\n * Estes ajudantes separam essas chaves das props e as ligam como ouvintes nativos no elemento.\n */\n\nconst NATIVE_EVENTS = {\n onDrag: 'drag',\n onDragStart: 'dragstart',\n onDragEnd: 'dragend',\n onAnimationStart: 'animationstart',\n} as const\n\ntype NativeKey = keyof typeof NATIVE_EVENTS\ntype Handler = ((event: never) => void) | undefined\n\n/** Separa as quatro chaves em conflito; o resto segue para o elemento do Motion. */\nexport function splitNativeEvents<P extends object>(props: P) {\n const native: Partial<Record<NativeKey, Handler>> = {}\n const rest: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(props)) {\n if (key in NATIVE_EVENTS) native[key as NativeKey] = value as Handler\n else rest[key] = value\n }\n return { native, rest: rest as Omit<P, NativeKey> }\n}\n\n/** Liga os manipuladores como ouvintes nativos. Devolve a função que os remove. */\nexport function bindNativeEvents(el: HTMLElement, native: Partial<Record<NativeKey, Handler>>) {\n const bound: [string, EventListener][] = []\n for (const [key, type] of Object.entries(NATIVE_EVENTS)) {\n const handler = native[key as NativeKey]\n if (!handler) continue\n const listener = ((event: Event) => handler(event as never)) as EventListener\n el.addEventListener(type, listener)\n bound.push([type, listener])\n }\n return () => bound.forEach(([type, listener]) => el.removeEventListener(type, listener))\n}\n","import { cva, type VariantProps } from 'class-variance-authority'\nimport { Slot } from 'radix-ui'\nimport { forwardRef, type ButtonHTMLAttributes, type ReactNode, type Ref } from 'react'\nimport { resolveCatalogCode } from '@/catalog/components'\nimport { Spinner } from '@/components/ui/spinner'\nimport { cn } from '@/lib/cn'\nimport { useMotionMode, useMotionOn, useMotionPresets } from '@/lib/motion'\nimport { bindNativeEvents, splitNativeEvents } from '@/lib/motion/native-events'\n\n/**\n * Botão único do sistema. Variantes, tamanho, ícone, só ícone, carregamento e\n * largura total são props: nunca crie outro arquivo de botão.\n * Altura vem de h-control-*: 44px ou mais no mobile, compacta a partir de md.\n */\nexport const buttonVariants = cva(\n 'inline-flex shrink-0 cursor-pointer items-center justify-center gap-2 rounded-control font-medium whitespace-nowrap transition-[color,background-color,border-color,box-shadow,transform] duration-(--rendra-duration-press) ease-out select-none active:scale-97 disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 motion-reduce:active:scale-100 [&_svg]:pointer-events-none [&_svg]:shrink-0',\n {\n variants: {\n variant: {\n // Hover troca para a cor de hover da marca e eleva um nível; pressionar comprime.\n primary:\n 'bg-primary text-primary-foreground shadow-sm hover:bg-primary-hover hover:text-primary-hover-foreground hover:shadow-md active:bg-primary-hover active:text-primary-hover-foreground active:shadow-sm',\n secondary:\n 'bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary-hover hover:text-secondary-hover-foreground hover:shadow-md active:bg-secondary-hover active:text-secondary-hover-foreground active:shadow-sm',\n outline:\n 'border border-input bg-card text-foreground hover:border-primary hover:bg-primary-soft hover:text-primary-soft-foreground active:bg-primary-soft',\n // Transparentes: no hover, assumem a cor primária do template.\n ghost:\n 'text-foreground hover:bg-primary-soft hover:text-primary-soft-foreground active:bg-primary-soft',\n destructive:\n 'bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive-hover hover:shadow-md active:bg-destructive-hover active:shadow-sm',\n link: 'min-h-touch min-w-touch text-primary-text underline-offset-4 hover:text-primary-hover hover:underline md:min-h-0 md:min-w-0',\n },\n size: {\n sm: 'h-control-sm px-3 text-sm [&_svg]:size-icon-sm',\n md: 'h-control-md px-4 text-sm [&_svg]:size-icon-sm',\n lg: 'h-control-lg px-6 text-base [&_svg]:size-icon-md',\n },\n iconOnly: { true: 'px-0', false: '' },\n fullWidth: { true: 'w-full', false: '' },\n },\n compoundVariants: [\n { iconOnly: true, size: 'sm', className: 'w-control-sm' },\n { iconOnly: true, size: 'md', className: 'w-control-md' },\n { iconOnly: true, size: 'lg', className: 'w-control-lg' },\n { variant: 'link', className: 'h-auto px-0' },\n ],\n defaultVariants: { variant: 'primary', size: 'md', iconOnly: false, fullWidth: false },\n },\n)\n\ntype Variants = VariantProps<typeof buttonVariants>\n\nexport interface ButtonProps\n extends ButtonHTMLAttributes<HTMLButtonElement>, Omit<Variants, 'iconOnly' | 'fullWidth'> {\n /** Ícone à esquerda do texto (ou o próprio conteúdo, com iconOnly). */\n icon?: ReactNode\n /** Ícone à direita do texto. */\n iconRight?: ReactNode\n /** Botão só com ícone: quadrado, e aria-label passa a ser obrigatório. */\n iconOnly?: boolean\n /** Mostra indicador, bloqueia cliques e marca aria-busy. */\n loading?: boolean\n /** Ocupa toda a largura do contêiner. */\n fullWidth?: boolean\n /** Renderiza o filho (ex.: <Link>) com a aparência de botão. */\n asChild?: boolean\n}\n\ntype MotionButtonProps = Omit<ButtonHTMLAttributes<HTMLButtonElement>, 'children'> & {\n children: ReactNode\n}\n\n/**\n * O botão de `on`: `m.button` com toque discreto (whileTap). Os eventos que o Motion toma\n * (`onDrag*` e `onAnimationStart`) saem das props dele e viram ouvintes nativos no elemento,\n * então o consumidor continua recebendo o evento. Só existe em `on`; por isso os hooks de motion\n * moram aqui e o `off` não paga nada.\n */\nconst MotionButton = forwardRef<HTMLButtonElement, MotionButtonProps>(\n function MotionButton(props, ref) {\n const { m } = useMotionOn()\n const presets = useMotionPresets()\n const { native, rest } = splitNativeEvents(props)\n const attach = (el: HTMLButtonElement | null) => {\n assignRef(ref, el)\n if (!el) return\n const unbind = bindNativeEvents(el, native)\n return () => {\n unbind()\n assignRef(ref, null)\n }\n }\n return (\n <m.button\n ref={attach}\n whileTap={presets.press.whileTap}\n transition={presets.press.transition}\n {...rest}\n />\n )\n },\n)\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === 'function') ref(value)\n else if (ref) ref.current = value\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n className,\n variant,\n size,\n icon,\n iconRight,\n iconOnly = false,\n loading = false,\n fullWidth = false,\n asChild = false,\n disabled,\n type = 'button',\n children,\n ...props\n },\n ref,\n) {\n if (import.meta.env.DEV && iconOnly && !props['aria-label']) {\n console.warn('Button com iconOnly precisa de aria-label.')\n }\n const on = useMotionMode() === 'on'\n // Em `on` o toque é do whileTap: a classe `active:scale-97` sairia somando com ele, e o\n // `transform` sai da lista de transition (senão o CSS transiciona cada quadro que o Motion grava\n // e o toque chega atrasado). Com asChild nada disso: é só CSS, como em `off`.\n const classes = cn(\n buttonVariants({ variant, size, iconOnly, fullWidth }),\n on &&\n !asChild &&\n 'transition-[color,background-color,border-color,box-shadow] active:scale-100',\n className,\n )\n const code = resolveCatalogCode('Button', { variant: variant ?? 'primary' })\n\n if (asChild) {\n return (\n <Slot.Root ref={ref} data-rendra={code} className={classes} {...props}>\n {children}\n </Slot.Root>\n )\n }\n\n // Ícone dentro do botão segue o tamanho do próprio botão (sm/md -> sm, lg -> md, como o\n // [&_svg]:size-icon-* de buttonVariants já fazia com o Loader2 solto).\n const leading = loading ? <Spinner size={size === 'lg' ? 'md' : 'sm'} /> : icon\n if (on) {\n return (\n <MotionButton\n ref={ref}\n type={type}\n data-rendra={code}\n className={classes}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...props}\n >\n {leading}\n {iconOnly ? (loading ? null : children) : children}\n {!iconOnly && iconRight}\n </MotionButton>\n )\n }\n return (\n <button\n ref={ref}\n type={type}\n data-rendra={code}\n className={classes}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...props}\n >\n {leading}\n {iconOnly ? (loading ? null : children) : children}\n {!iconOnly && iconRight}\n </button>\n )\n})\n"],"mappings":"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"}
package/dist/chart.js CHANGED
@@ -1,11 +1,13 @@
1
- /*! Rendra Design System v2.4.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-DXDpTGsu.js";
3
- import { p as resolveCatalogCode } from "./components-hp7iWioq.js";
4
- import { t as Button } from "./button-C6rjowQi.js";
5
- import { n as useBreakpoint } from "./use-breakpoint-Dyp-0E_c.js";
1
+ /*! Rendra Design System v2.6.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as cn } from "./cn-TBlI6-gS.js";
3
+ import { a as useMotionMode, i as useMotionLib, s as useRevealScope } from "./motion-context-BL545cb6.js";
4
+ import { n as toCubicBezier, t as useMotionPresets } from "./use-motion-presets-DCJPBfNJ.js";
5
+ import { p as resolveCatalogCode } from "./components-Btk1cUg2.js";
6
+ import { t as Button } from "./button-B6-644xP.js";
7
+ import { n as useBreakpoint } from "./use-breakpoint-CNxDwEpc.js";
6
8
  import { ArrowDown, BarChart3, List } from "lucide-react";
7
- import { jsx, jsxs } from "react/jsx-runtime";
8
9
  import { useId, useRef, useState } from "react";
10
+ import { jsx, jsxs } from "react/jsx-runtime";
9
11
  import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, ComposedChart, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
10
12
  //#region src/components/ui/chart.tsx
11
13
  var palette = [
@@ -75,6 +77,11 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
75
77
  const tone = (zones.find((z) => frac <= z.to) ?? zones[zones.length - 1])?.tone ?? "primary";
76
78
  const gradientId = useId();
77
79
  const [ex, ey] = point(frac, 74);
80
+ const lib = useMotionLib();
81
+ const on = lib !== null;
82
+ const presets = useMotionPresets();
83
+ const reveal = useRevealScope();
84
+ const [sx, sy] = point(0, 74);
78
85
  return /* @__PURE__ */ jsxs("figure", {
79
86
  "data-rendra": resolveCatalogCode("Chart", { type: "gauge" }),
80
87
  className: cn("flex min-w-0 flex-col items-center gap-2", className),
@@ -125,15 +132,32 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
125
132
  strokeWidth: 20,
126
133
  strokeLinecap: "round"
127
134
  }),
128
- /* @__PURE__ */ jsx("line", {
135
+ on ? /* @__PURE__ */ jsx(lib.m.line, {
136
+ x1: CX,
137
+ y1: CY,
138
+ initial: {
139
+ x2: sx,
140
+ y2: sy
141
+ },
142
+ animate: {
143
+ x2: ex,
144
+ y2: ey
145
+ },
146
+ transition: {
147
+ duration: reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw,
148
+ ease: presets.tokens.ease.out
149
+ },
150
+ stroke: "var(--rendra-foreground)",
151
+ strokeWidth: 4,
152
+ strokeLinecap: "round"
153
+ }) : /* @__PURE__ */ jsx("line", {
129
154
  x1: CX,
130
155
  y1: CY,
131
156
  x2: ex,
132
157
  y2: ey,
133
158
  stroke: "var(--rendra-foreground)",
134
159
  strokeWidth: 4,
135
- strokeLinecap: "round",
136
- className: "transition-all duration-700"
160
+ strokeLinecap: "round"
137
161
  }),
138
162
  /* @__PURE__ */ jsx("circle", {
139
163
  cx: CX,
@@ -195,6 +219,10 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
195
219
  });
196
220
  }
197
221
  function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
222
+ const lib = useMotionLib();
223
+ const on = lib !== null;
224
+ const presets = useMotionPresets();
225
+ const Root = on ? lib.m.figure : "figure";
198
226
  const n = stages.length;
199
227
  const top = stages[0]?.value || 1;
200
228
  const last = stages[n - 1];
@@ -207,10 +235,11 @@ function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
207
235
  return next && s.value > 0 ? next.value / s.value * 100 : null;
208
236
  });
209
237
  const worst = rates.reduce((w, r, i) => r !== null && (w === null || r < (rates[w] ?? Infinity)) ? i : w, null);
210
- return /* @__PURE__ */ jsxs("figure", {
238
+ return /* @__PURE__ */ jsxs(Root, {
211
239
  "data-rendra": resolveCatalogCode("Chart", { type: "funnel" }),
212
240
  className: cn("flex min-w-0 flex-col items-center", className),
213
241
  "aria-label": aria["aria-label"],
242
+ ...on ? presets.fade : {},
214
243
  children: [
215
244
  /* @__PURE__ */ jsx("ol", {
216
245
  className: "flex w-full flex-col items-center",
@@ -276,6 +305,20 @@ function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
276
305
  }
277
306
  function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFormatter = fmtDefault, height = "md", listThreshold = 8, className, ...aria }) {
278
307
  const { isMobile } = useBreakpoint();
308
+ const motion = useMotionMode();
309
+ const presets = useMotionPresets();
310
+ const reveal = useRevealScope();
311
+ const [drawn, setDrawn] = useState(false);
312
+ const ended = useRef(0);
313
+ const animated = type === "pie" ? 1 : series.length;
314
+ const [mountedOn] = useState(motion === "on");
315
+ const draw = motion === "on" && mountedOn ? {
316
+ animationDuration: Math.round((reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw) * 1e3),
317
+ animationEasing: toCubicBezier(presets.tokens.ease.out),
318
+ onAnimationEnd: () => {
319
+ if (++ended.current >= animated) setDrawn(true);
320
+ }
321
+ } : {};
279
322
  const [asList, setAsList] = useState(false);
280
323
  const [animate, setAnimate] = useState(() => !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches);
281
324
  const firstWidth = useRef(null);
@@ -360,7 +403,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
360
403
  0
361
404
  ],
362
405
  maxBarSize: 40,
363
- isAnimationActive: animate,
406
+ isAnimationActive: animate && !drawn,
407
+ ...draw,
364
408
  children: colorByCategory && data.map((_, j) => /* @__PURE__ */ jsx(Cell, { fill: colorVar(j + 1) }, j))
365
409
  }, s.key);
366
410
  let chart;
@@ -375,7 +419,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
375
419
  outerRadius: "85%",
376
420
  paddingAngle: 2,
377
421
  stroke: "var(--rendra-card)",
378
- isAnimationActive: animate,
422
+ isAnimationActive: animate && !drawn,
423
+ ...draw,
379
424
  children: data.map((_, i) => /* @__PURE__ */ jsx(Cell, { fill: colorVar(i + 1) }, i))
380
425
  }),
381
426
  tooltip,
@@ -414,7 +459,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
414
459
  strokeWidth: 0
415
460
  },
416
461
  activeDot: { r: 5 },
417
- isAnimationActive: animate
462
+ isAnimationActive: animate && !drawn,
463
+ ...draw
418
464
  }, s.key))
419
465
  ]
420
466
  });
@@ -449,7 +495,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
449
495
  stroke: colors[i],
450
496
  strokeWidth: 2,
451
497
  fill: `url(#area-${s.key})`,
452
- isAnimationActive: animate
498
+ isAnimationActive: animate && !drawn,
499
+ ...draw
453
500
  }, s.key))
454
501
  ]
455
502
  });
@@ -469,7 +516,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
469
516
  strokeWidth: 2,
470
517
  dot: false,
471
518
  activeDot: { r: 4 },
472
- isAnimationActive: animate
519
+ isAnimationActive: animate && !drawn,
520
+ ...draw
473
521
  }, s.key))
474
522
  ]
475
523
  });
package/dist/chart.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"chart.js","names":[],"sources":["../src/components/ui/chart.tsx"],"sourcesContent":["import { ArrowDown, BarChart3, List as ListIcon } from 'lucide-react'\nimport { useId, useRef, useState, type CSSProperties } from 'react'\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts'\nimport { resolveCatalogCode } from '@/catalog/components'\nimport { Button } from '@/components/ui/button'\nimport { useBreakpoint } from '@/hooks/use-breakpoint'\nimport { cn } from '@/lib/cn'\n\n/*\n * Gráfico único do sistema. O tipo é a prop type, nunca um componente novo:\n * line, area, bar, pie séries sobre categorias (Recharts)\n * combo barras e linhas juntas (ex.: receita em barras e meta em linha)\n * gauge velocímetro de meta: valor, máximo, meta e faixas de cor\n * funnel funil de etapas com a taxa de conversão entre elas\n * Cores sempre do template (--chart-1 a --chart-5). Mobile: legenda abaixo, eixos\n * simplificados, lista de valores quando há muitos pontos; o funil empilha rótulo e barra.\n */\n\nexport interface ChartSeries {\n /** Chave no objeto de dados. */\n key: string\n label: string\n /** Índice da cor do template (1 a 5). Padrão: na ordem das séries. */\n color?: 1 | 2 | 3 | 4 | 5\n /** No type=\"combo\": desenha a série como barra (padrão) ou linha. */\n kind?: 'bar' | 'line'\n}\n\ninterface BaseProps {\n valueFormatter?: (n: number) => string\n /** Altura: sm (12rem), md (16rem) ou fill (a altura do contêiner, como num widget). */\n height?: 'sm' | 'md' | 'fill'\n 'aria-label': string\n className?: string\n}\n\nexport interface CartesianChartProps extends BaseProps {\n type: 'line' | 'bar' | 'area' | 'pie' | 'combo'\n data: Record<string, string | number>[]\n /** Chave da categoria (eixo X ou fatias da pizza). */\n xKey: string\n series: ChartSeries[]\n /** Barras empilhadas (bar e combo). */\n stacked?: boolean\n /** Barra de uma série só com uma cor por categoria. */\n colorByCategory?: boolean\n /** Acima deste número de pontos, o mobile oferece ver como lista. */\n listThreshold?: number\n}\n\nexport interface GaugeZone {\n /** Fim da faixa, de 0 a 1 do máximo. */\n to: number\n tone: 'error' | 'warning' | 'success' | 'primary' | 'neutral'\n}\n\nexport interface GaugeChartProps extends BaseProps {\n type: 'gauge'\n value: number\n max: number\n min?: number\n /** Meta: marcada no arco e usada no percentual atingido. */\n target?: number\n /** Faixas de cor do arco. Padrão: vermelho até 60%, amarelo até 90%, verde até o fim. */\n zones?: GaugeZone[]\n /** Texto abaixo do valor (ex.: \"Receita do mês\"). */\n label?: string\n}\n\nexport interface FunnelStage {\n label: string\n value: number\n}\n\nexport interface FunnelChartProps extends BaseProps {\n type: 'funnel'\n stages: FunnelStage[]\n}\n\nexport type ChartProps = CartesianChartProps | GaugeChartProps | FunnelChartProps\n\n// Lê as variáveis do tema direto (as --color-* do Tailwind são inline e não existem no CSS).\nconst palette = [\n '--rendra-chart-1',\n '--rendra-chart-2',\n '--rendra-chart-3',\n '--rendra-chart-4',\n '--rendra-chart-5',\n '--rendra-ring',\n]\nconst colorVar = (i: number) => `var(${palette[(i - 1) % palette.length]})`\nconst fmtDefault = (n: number) => n.toLocaleString('pt-BR')\nconst pct = (n: number) =>\n `${n.toLocaleString('pt-BR', { minimumFractionDigits: 1, maximumFractionDigits: 1 })}%`\n\nexport function Chart(props: ChartProps) {\n if (props.type === 'gauge') return <Gauge {...props} />\n if (props.type === 'funnel') return <Funnel {...props} />\n return <Cartesian {...props} />\n}\n\n/* ================================================================ velocímetro */\n\nconst defaultZones: GaugeZone[] = [\n { to: 0.6, tone: 'error' },\n { to: 0.9, tone: 'warning' },\n { to: 1, tone: 'success' },\n]\n\nconst zoneBadge: Record<GaugeZone['tone'], string> = {\n error: 'bg-destructive-soft text-destructive-soft-foreground',\n warning: 'bg-warning-soft text-warning-soft-foreground',\n success: 'bg-success-soft text-success-soft-foreground',\n primary: 'bg-primary-soft text-primary-soft-foreground',\n neutral: 'bg-muted text-foreground',\n}\nconst zoneText: Record<GaugeZone['tone'], string> = {\n error: 'Abaixo da meta',\n warning: 'Atenção',\n success: 'No verde',\n primary: 'Em andamento',\n neutral: 'Em andamento',\n}\n\n// Meio círculo (180°), da esquerda para a direita por cima, centro em (120, 120), raio 100.\nconst CX = 120\nconst CY = 120\nconst R = 100\nconst point = (frac: number, r = R) => {\n const a = Math.PI * (1 - frac)\n return [CX + r * Math.cos(a), CY - r * Math.sin(a)] as const\n}\nconst arc = (from: number, to: number, r = R) => {\n const [x1, y1] = point(from, r)\n const [x2, y2] = point(to, r)\n return `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${r} ${r} 0 0 1 ${x2.toFixed(2)} ${y2.toFixed(2)}`\n}\n\nfunction Gauge({\n value,\n max,\n min = 0,\n target,\n zones = defaultZones,\n label,\n valueFormatter = fmtDefault,\n className,\n ...aria\n}: GaugeChartProps) {\n const range = Math.max(1, max - min)\n const clamp = (n: number) => Math.min(1, Math.max(0, n))\n // Com meta, o ponteiro mostra o percentual da meta (100% = meta batida); sem meta, do máximo.\n const reached = target ? (value / target) * 100 : null\n const frac = reached !== null ? clamp(reached / 100) : clamp((value - min) / range)\n const zone = zones.find((z) => frac <= z.to) ?? zones[zones.length - 1]\n const tone = zone?.tone ?? 'primary'\n const gradientId = useId()\n const [ex, ey] = point(frac, R - 26)\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type: 'gauge' })}\n className={cn('flex min-w-0 flex-col items-center gap-2', className)}\n aria-label={aria['aria-label']}\n >\n <div\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(frac * 100)}\n aria-valuetext={`${reached !== null ? `${pct(reached)} da meta` : valueFormatter(value)}, ${zoneText[tone]}`}\n aria-label={aria['aria-label']}\n className=\"w-full max-w-xs\"\n >\n <svg viewBox=\"0 0 240 140\" className=\"w-full\" aria-hidden>\n <defs>\n {/* Degradê contínuo: vermelho, laranja, amarelo e verde */}\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">\n <stop offset=\"0%\" stopColor=\"var(--rendra-meter-low)\" />\n <stop\n offset=\"38%\"\n stopColor=\"color-mix(in oklab, var(--rendra-meter-low), var(--rendra-meter-mid))\"\n />\n <stop offset=\"62%\" stopColor=\"var(--rendra-meter-mid)\" />\n <stop offset=\"100%\" stopColor=\"var(--rendra-meter-high)\" />\n </linearGradient>\n </defs>\n <path\n d={arc(0, 1)}\n fill=\"none\"\n stroke={`url(#${gradientId})`}\n strokeWidth={20}\n strokeLinecap=\"round\"\n />\n {/* Ponteiro fino, do centro até perto do arco */}\n <line\n x1={CX}\n y1={CY}\n x2={ex}\n y2={ey}\n stroke=\"var(--rendra-foreground)\"\n strokeWidth={4}\n strokeLinecap=\"round\"\n className=\"transition-all duration-700\"\n />\n <circle cx={CX} cy={CY} r={9} fill=\"var(--rendra-foreground)\" />\n <circle cx={CX} cy={CY} r={4} fill=\"var(--rendra-card)\" />\n <text\n x={point(0)[0]}\n y={CY + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--rendra-muted-foreground)\"\n >\n 0%\n </text>\n <text\n x={point(1)[0]}\n y={CY + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--rendra-muted-foreground)\"\n >\n 100%\n </text>\n </svg>\n </div>\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <span className=\"text-3xl font-semibold tabular-nums\">\n {reached !== null ? `${Math.round(reached)}%` : valueFormatter(value)}\n </span>\n {label && <span className=\"text-sm font-medium\">{label}</span>}\n {target !== undefined && (\n <span className=\"flex flex-col text-sm text-muted-foreground tabular-nums\">\n <span>Meta: {valueFormatter(target)}</span>\n <span>Realizado: {valueFormatter(value)}</span>\n </span>\n )}\n <span className={cn('mt-1 rounded-full px-3 py-1 text-xs font-semibold', zoneBadge[tone])}>\n {zoneText[tone]}\n </span>\n </div>\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n\n/* ================================================================ funil */\n\nfunction Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }: FunnelChartProps) {\n const n = stages.length\n const top = stages[0]?.value || 1\n const last = stages[n - 1]\n // Afunila por igual, de 100% a 46% da largura, como um funil de verdade.\n const width = (i: number) => 1 - (i / Math.max(1, n)) * 0.54\n // Da primária ao sucesso, escurecido com o tom da sidebar: o texto claro passa AA.\n const fill = (i: number) => {\n const p = n > 1 ? Math.round(100 - (i / (n - 1)) * 100) : 100\n return `color-mix(in oklab, color-mix(in oklab, var(--rendra-primary) ${p}%, var(--rendra-success)) 58%, var(--rendra-sidebar))`\n }\n const rates = stages.map((s, i) => {\n const next = stages[i + 1]\n return next && s.value > 0 ? (next.value / s.value) * 100 : null\n })\n const worst = rates.reduce<number | null>(\n (w, r, i) => (r !== null && (w === null || r < (rates[w] ?? Infinity)) ? i : w),\n null,\n )\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type: 'funnel' })}\n className={cn('flex min-w-0 flex-col items-center', className)}\n aria-label={aria['aria-label']}\n >\n <ol className=\"flex w-full flex-col items-center\">\n {stages.map((s, i) => {\n const rate = rates[i]\n return (\n <li key={s.label} className=\"flex w-full flex-col items-center\">\n <div\n className=\"flex h-16 w-full flex-col items-center justify-center bg-fill text-sidebar-foreground clip-trapezoid\"\n style={\n {\n '--top': width(i).toFixed(4),\n '--bottom': width(i + 1).toFixed(4),\n '--fill': fill(i),\n } as CSSProperties\n }\n >\n <span className=\"text-lg leading-tight font-semibold tabular-nums\">\n {valueFormatter(s.value)}\n </span>\n <span className=\"text-xs\">{s.label}</span>\n </div>\n {rate != null && (\n <p className=\"flex items-center gap-1 py-1 text-xs text-muted-foreground\">\n <ArrowDown className=\"size-3\" aria-hidden />\n <span className=\"font-semibold text-foreground tabular-nums\">{pct(rate)}</span>\n <span>para a próxima etapa</span>\n {i === worst && n > 2 && (\n <span className=\"font-semibold text-destructive-soft-foreground\">\n · Maior queda\n </span>\n )}\n </p>\n )}\n </li>\n )\n })}\n </ol>\n {n > 1 && last && (\n <p className=\"flex items-center gap-1 pt-2 text-sm text-muted-foreground\">\n <ArrowDown className=\"size-icon-sm\" aria-hidden />\n <span className=\"font-semibold text-foreground tabular-nums\">\n {pct((last.value / top) * 100)}\n </span>\n de conversão total do funil\n </p>\n )}\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n\n/* ================================================================ séries (Recharts) */\n\nfunction Cartesian({\n type,\n data,\n xKey,\n series,\n stacked,\n colorByCategory,\n valueFormatter = fmtDefault,\n height = 'md',\n listThreshold = 8,\n className,\n ...aria\n}: CartesianChartProps) {\n const { isMobile } = useBreakpoint()\n const [asList, setAsList] = useState(false)\n // Anima só a entrada. Se a largura muda depois (barra de rolagem que aparece, janela\n // redimensionada), o gráfico vai direto à nova forma, sem desenhar fora do card no meio do\n // caminho. Com \"reduzir movimento\" no sistema, não anima.\n const [animate, setAnimate] = useState(\n () => !window.matchMedia?.('(prefers-reduced-motion: reduce)').matches,\n )\n const firstWidth = useRef<number | null>(null)\n const onResize = (width: number) => {\n if (firstWidth.current === null) firstWidth.current = width\n else if (Math.abs(width - firstWidth.current) > 1) setAnimate(false)\n }\n const canList = isMobile && data.length > listThreshold\n const showList = canList && asList\n const colors = series.map((s, i) => colorVar(s.color ?? i + 1))\n\n const axis = {\n stroke: 'var(--rendra-muted-foreground)',\n fontSize: 12,\n tickLine: false,\n axisLine: false,\n }\n const tooltip = (\n <Tooltip\n cursor={{ fill: 'var(--rendra-muted)', stroke: 'var(--rendra-border)' }}\n formatter={(v) => valueFormatter(Number(v))}\n contentStyle={{\n background: 'var(--rendra-popover)',\n border: '1px solid var(--rendra-border)',\n borderRadius: 'var(--rendra-shape-control)',\n color: 'var(--rendra-popover-foreground)',\n fontSize: 12,\n }}\n />\n )\n const legend = (\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12, paddingTop: 12 }}\n />\n )\n const common = { data, margin: { top: 8, right: 8, left: isMobile ? -16 : 0, bottom: 0 } }\n const xAxis = (\n <XAxis dataKey={xKey} {...axis} interval=\"preserveStartEnd\" minTickGap={isMobile ? 24 : 12} />\n )\n const yAxis = (\n <YAxis\n {...axis}\n width={isMobile ? 72 : 80}\n tickCount={isMobile ? 3 : 5}\n // Mostra todas as marcas calculadas (o padrão do Recharts some com algumas).\n interval={0}\n allowDecimals={false}\n tickFormatter={(v) => valueFormatter(Number(v))}\n />\n )\n const grid = (\n <CartesianGrid vertical={false} stroke=\"var(--rendra-border)\" strokeDasharray=\"3 3\" />\n )\n const bar = (s: ChartSeries, i: number, lastBar: boolean) => (\n <Bar\n key={s.key}\n dataKey={s.key}\n name={s.label}\n fill={colors[i]}\n stackId={stacked ? 'pilha' : undefined}\n // Empilhadas: só a barra de cima tem os cantos arredondados.\n radius={!stacked || lastBar ? [4, 4, 0, 0] : [0, 0, 0, 0]}\n maxBarSize={40}\n isAnimationActive={animate}\n >\n {colorByCategory && data.map((_, j) => <Cell key={j} fill={colorVar(j + 1)} />)}\n </Bar>\n )\n\n let chart\n if (type === 'pie') {\n const s = series[0]\n chart = (\n <PieChart>\n <Pie\n data={data}\n dataKey={s?.key ?? ''}\n nameKey={xKey}\n innerRadius=\"55%\"\n outerRadius=\"85%\"\n paddingAngle={2}\n stroke=\"var(--rendra-card)\"\n isAnimationActive={animate}\n >\n {data.map((_, i) => (\n <Cell key={i} fill={colorVar(i + 1)} />\n ))}\n </Pie>\n {tooltip}\n {legend}\n </PieChart>\n )\n } else if (type === 'bar') {\n chart = (\n <BarChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => bar(s, i, i === series.length - 1))}\n </BarChart>\n )\n } else if (type === 'combo') {\n const bars = series.filter((s) => (s.kind ?? 'bar') === 'bar')\n chart = (\n <ComposedChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {legend}\n {series.map((s, i) =>\n (s.kind ?? 'bar') === 'bar' ? (\n bar(s, i, s.key === bars[bars.length - 1]?.key)\n ) : (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n dot={{ r: 3, fill: colors[i], strokeWidth: 0 }}\n activeDot={{ r: 5 }}\n isAnimationActive={animate}\n />\n ),\n )}\n </ComposedChart>\n )\n } else if (type === 'area') {\n chart = (\n <AreaChart {...common}>\n <defs>\n {series.map((s, i) => (\n <linearGradient key={s.key} id={`area-${s.key}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={colors[i]} stopOpacity={0.3} />\n <stop offset=\"100%\" stopColor={colors[i]} stopOpacity={0} />\n </linearGradient>\n ))}\n </defs>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => (\n <Area\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n fill={`url(#area-${s.key})`}\n isAnimationActive={animate}\n />\n ))}\n </AreaChart>\n )\n } else {\n chart = (\n <LineChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 4 }}\n isAnimationActive={animate}\n />\n ))}\n </LineChart>\n )\n }\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type })}\n className={cn('flex min-w-0 flex-col gap-3', height === 'fill' && 'h-full', className)}\n aria-label={aria['aria-label']}\n >\n {canList && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-end\"\n icon={asList ? <BarChart3 /> : <ListIcon />}\n onClick={() => setAsList((v) => !v)}\n >\n {asList ? 'Ver gráfico' : 'Ver como lista'}\n </Button>\n )}\n {showList ? (\n <ul className=\"flex flex-col divide-y\">\n {data.map((d) => (\n <li\n key={String(d[xKey])}\n className=\"flex items-center justify-between gap-3 py-2 text-sm\"\n >\n <span className=\"text-muted-foreground\">{String(d[xKey])}</span>\n <span className=\"flex gap-3 font-medium tabular-nums\">\n {series.map((s) => (\n <span key={s.key}>{valueFormatter(Number(d[s.key]))}</span>\n ))}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div\n className={cn(\n 'w-full',\n height === 'fill'\n ? 'min-h-chart-sm flex-1'\n : height === 'sm'\n ? 'h-chart-sm'\n : 'h-chart-md',\n )}\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\" onResize={onResize}>\n {chart}\n </ResponsiveContainer>\n </div>\n )}\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chart.js","names":[],"sources":["../src/components/ui/chart.tsx"],"sourcesContent":["import { ArrowDown, BarChart3, List as ListIcon } from 'lucide-react'\nimport { useId, useRef, useState, type CSSProperties } from 'react'\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts'\nimport { resolveCatalogCode } from '@/catalog/components'\nimport { Button } from '@/components/ui/button'\nimport { useBreakpoint } from '@/hooks/use-breakpoint'\nimport { cn } from '@/lib/cn'\nimport { useMotionLib, useMotionMode, useMotionPresets, useRevealScope } from '@/lib/motion'\nimport { toCubicBezier } from '@/lib/motion/presets'\n\n/*\n * Gráfico único do sistema. O tipo é a prop type, nunca um componente novo:\n * line, area, bar, pie séries sobre categorias (Recharts)\n * combo barras e linhas juntas (ex.: receita em barras e meta em linha)\n * gauge velocímetro de meta: valor, máximo, meta e faixas de cor\n * funnel funil de etapas com a taxa de conversão entre elas\n * Cores sempre do template (--chart-1 a --chart-5). Mobile: legenda abaixo, eixos\n * simplificados, lista de valores quando há muitos pontos; o funil empilha rótulo e barra.\n */\n\nexport interface ChartSeries {\n /** Chave no objeto de dados. */\n key: string\n label: string\n /** Índice da cor do template (1 a 5). Padrão: na ordem das séries. */\n color?: 1 | 2 | 3 | 4 | 5\n /** No type=\"combo\": desenha a série como barra (padrão) ou linha. */\n kind?: 'bar' | 'line'\n}\n\ninterface BaseProps {\n valueFormatter?: (n: number) => string\n /** Altura: sm (12rem), md (16rem) ou fill (a altura do contêiner, como num widget). */\n height?: 'sm' | 'md' | 'fill'\n 'aria-label': string\n className?: string\n}\n\nexport interface CartesianChartProps extends BaseProps {\n type: 'line' | 'bar' | 'area' | 'pie' | 'combo'\n data: Record<string, string | number>[]\n /** Chave da categoria (eixo X ou fatias da pizza). */\n xKey: string\n series: ChartSeries[]\n /** Barras empilhadas (bar e combo). */\n stacked?: boolean\n /** Barra de uma série só com uma cor por categoria. */\n colorByCategory?: boolean\n /** Acima deste número de pontos, o mobile oferece ver como lista. */\n listThreshold?: number\n}\n\nexport interface GaugeZone {\n /** Fim da faixa, de 0 a 1 do máximo. */\n to: number\n tone: 'error' | 'warning' | 'success' | 'primary' | 'neutral'\n}\n\nexport interface GaugeChartProps extends BaseProps {\n type: 'gauge'\n value: number\n max: number\n min?: number\n /** Meta: marcada no arco e usada no percentual atingido. */\n target?: number\n /** Faixas de cor do arco. Padrão: vermelho até 60%, amarelo até 90%, verde até o fim. */\n zones?: GaugeZone[]\n /** Texto abaixo do valor (ex.: \"Receita do mês\"). */\n label?: string\n}\n\nexport interface FunnelStage {\n label: string\n value: number\n}\n\nexport interface FunnelChartProps extends BaseProps {\n type: 'funnel'\n stages: FunnelStage[]\n}\n\nexport type ChartProps = CartesianChartProps | GaugeChartProps | FunnelChartProps\n\n// Lê as variáveis do tema direto (as --color-* do Tailwind são inline e não existem no CSS).\nconst palette = [\n '--rendra-chart-1',\n '--rendra-chart-2',\n '--rendra-chart-3',\n '--rendra-chart-4',\n '--rendra-chart-5',\n '--rendra-ring',\n]\nconst colorVar = (i: number) => `var(${palette[(i - 1) % palette.length]})`\nconst fmtDefault = (n: number) => n.toLocaleString('pt-BR')\nconst pct = (n: number) =>\n `${n.toLocaleString('pt-BR', { minimumFractionDigits: 1, maximumFractionDigits: 1 })}%`\n\nexport function Chart(props: ChartProps) {\n if (props.type === 'gauge') return <Gauge {...props} />\n if (props.type === 'funnel') return <Funnel {...props} />\n return <Cartesian {...props} />\n}\n\n/* ================================================================ velocímetro */\n\nconst defaultZones: GaugeZone[] = [\n { to: 0.6, tone: 'error' },\n { to: 0.9, tone: 'warning' },\n { to: 1, tone: 'success' },\n]\n\nconst zoneBadge: Record<GaugeZone['tone'], string> = {\n error: 'bg-destructive-soft text-destructive-soft-foreground',\n warning: 'bg-warning-soft text-warning-soft-foreground',\n success: 'bg-success-soft text-success-soft-foreground',\n primary: 'bg-primary-soft text-primary-soft-foreground',\n neutral: 'bg-muted text-foreground',\n}\nconst zoneText: Record<GaugeZone['tone'], string> = {\n error: 'Abaixo da meta',\n warning: 'Atenção',\n success: 'No verde',\n primary: 'Em andamento',\n neutral: 'Em andamento',\n}\n\n// Meio círculo (180°), da esquerda para a direita por cima, centro em (120, 120), raio 100.\nconst CX = 120\nconst CY = 120\nconst R = 100\nconst point = (frac: number, r = R) => {\n const a = Math.PI * (1 - frac)\n return [CX + r * Math.cos(a), CY - r * Math.sin(a)] as const\n}\nconst arc = (from: number, to: number, r = R) => {\n const [x1, y1] = point(from, r)\n const [x2, y2] = point(to, r)\n return `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${r} ${r} 0 0 1 ${x2.toFixed(2)} ${y2.toFixed(2)}`\n}\n\nfunction Gauge({\n value,\n max,\n min = 0,\n target,\n zones = defaultZones,\n label,\n valueFormatter = fmtDefault,\n className,\n ...aria\n}: GaugeChartProps) {\n const range = Math.max(1, max - min)\n const clamp = (n: number) => Math.min(1, Math.max(0, n))\n // Com meta, o ponteiro mostra o percentual da meta (100% = meta batida); sem meta, do máximo.\n const reached = target ? (value / target) * 100 : null\n const frac = reached !== null ? clamp(reached / 100) : clamp((value - min) / range)\n const zone = zones.find((z) => frac <= z.to) ?? zones[zones.length - 1]\n const tone = zone?.tone ?? 'primary'\n const gradientId = useId()\n const [ex, ey] = point(frac, R - 26)\n const lib = useMotionLib()\n const on = lib !== null\n const presets = useMotionPresets()\n const reveal = useRevealScope()\n const [sx, sy] = point(0, R - 26)\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type: 'gauge' })}\n className={cn('flex min-w-0 flex-col items-center gap-2', className)}\n aria-label={aria['aria-label']}\n >\n <div\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(frac * 100)}\n aria-valuetext={`${reached !== null ? `${pct(reached)} da meta` : valueFormatter(value)}, ${zoneText[tone]}`}\n aria-label={aria['aria-label']}\n className=\"w-full max-w-xs\"\n >\n <svg viewBox=\"0 0 240 140\" className=\"w-full\" aria-hidden>\n <defs>\n {/* Degradê contínuo: vermelho, laranja, amarelo e verde */}\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">\n <stop offset=\"0%\" stopColor=\"var(--rendra-meter-low)\" />\n <stop\n offset=\"38%\"\n stopColor=\"color-mix(in oklab, var(--rendra-meter-low), var(--rendra-meter-mid))\"\n />\n <stop offset=\"62%\" stopColor=\"var(--rendra-meter-mid)\" />\n <stop offset=\"100%\" stopColor=\"var(--rendra-meter-high)\" />\n </linearGradient>\n </defs>\n <path\n d={arc(0, 1)}\n fill=\"none\"\n stroke={`url(#${gradientId})`}\n strokeWidth={20}\n strokeLinecap=\"round\"\n />\n {/* Ponteiro fino, do centro até perto do arco */}\n {on ? (\n // `on`: a agulha vai do 0% até o valor com a duração e a curva dos presets.\n <lib.m.line\n x1={CX}\n y1={CY}\n initial={{ x2: sx, y2: sy }}\n animate={{ x2: ex, y2: ey }}\n transition={{\n duration: reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw,\n ease: presets.tokens.ease.out,\n }}\n stroke=\"var(--rendra-foreground)\"\n strokeWidth={4}\n strokeLinecap=\"round\"\n />\n ) : (\n // `off`: a agulha vai direto ao valor. Antes havia aqui uma transição de CSS em \"all\"\n // com 700 ms, que era letra morta: x2 e y2 do <line> não são propriedades de CSS (o\n // Chromium não cria transição nenhuma; provado em tests/motion.spec.ts), e \"all\" é\n // proibido pela linguagem de movimento.\n <line\n x1={CX}\n y1={CY}\n x2={ex}\n y2={ey}\n stroke=\"var(--rendra-foreground)\"\n strokeWidth={4}\n strokeLinecap=\"round\"\n />\n )}\n <circle cx={CX} cy={CY} r={9} fill=\"var(--rendra-foreground)\" />\n <circle cx={CX} cy={CY} r={4} fill=\"var(--rendra-card)\" />\n <text\n x={point(0)[0]}\n y={CY + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--rendra-muted-foreground)\"\n >\n 0%\n </text>\n <text\n x={point(1)[0]}\n y={CY + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--rendra-muted-foreground)\"\n >\n 100%\n </text>\n </svg>\n </div>\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <span className=\"text-3xl font-semibold tabular-nums\">\n {reached !== null ? `${Math.round(reached)}%` : valueFormatter(value)}\n </span>\n {label && <span className=\"text-sm font-medium\">{label}</span>}\n {target !== undefined && (\n <span className=\"flex flex-col text-sm text-muted-foreground tabular-nums\">\n <span>Meta: {valueFormatter(target)}</span>\n <span>Realizado: {valueFormatter(value)}</span>\n </span>\n )}\n <span className={cn('mt-1 rounded-full px-3 py-1 text-xs font-semibold', zoneBadge[tone])}>\n {zoneText[tone]}\n </span>\n </div>\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n\n/* ================================================================ funil */\n\nfunction Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }: FunnelChartProps) {\n const lib = useMotionLib()\n const on = lib !== null\n const presets = useMotionPresets()\n // Em `on` o contêiner do funil entra com fade.\n const Root = on ? lib.m.figure : 'figure'\n const n = stages.length\n const top = stages[0]?.value || 1\n const last = stages[n - 1]\n // Afunila por igual, de 100% a 46% da largura, como um funil de verdade.\n const width = (i: number) => 1 - (i / Math.max(1, n)) * 0.54\n // Da primária ao sucesso, escurecido com o tom da sidebar: o texto claro passa AA.\n const fill = (i: number) => {\n const p = n > 1 ? Math.round(100 - (i / (n - 1)) * 100) : 100\n return `color-mix(in oklab, color-mix(in oklab, var(--rendra-primary) ${p}%, var(--rendra-success)) 58%, var(--rendra-sidebar))`\n }\n const rates = stages.map((s, i) => {\n const next = stages[i + 1]\n return next && s.value > 0 ? (next.value / s.value) * 100 : null\n })\n const worst = rates.reduce<number | null>(\n (w, r, i) => (r !== null && (w === null || r < (rates[w] ?? Infinity)) ? i : w),\n null,\n )\n\n return (\n <Root\n data-rendra={resolveCatalogCode('Chart', { type: 'funnel' })}\n className={cn('flex min-w-0 flex-col items-center', className)}\n aria-label={aria['aria-label']}\n {...(on ? presets.fade : {})}\n >\n <ol className=\"flex w-full flex-col items-center\">\n {stages.map((s, i) => {\n const rate = rates[i]\n return (\n <li key={s.label} className=\"flex w-full flex-col items-center\">\n <div\n className=\"flex h-16 w-full flex-col items-center justify-center bg-fill text-sidebar-foreground clip-trapezoid\"\n style={\n {\n '--top': width(i).toFixed(4),\n '--bottom': width(i + 1).toFixed(4),\n '--fill': fill(i),\n } as CSSProperties\n }\n >\n <span className=\"text-lg leading-tight font-semibold tabular-nums\">\n {valueFormatter(s.value)}\n </span>\n <span className=\"text-xs\">{s.label}</span>\n </div>\n {rate != null && (\n <p className=\"flex items-center gap-1 py-1 text-xs text-muted-foreground\">\n <ArrowDown className=\"size-3\" aria-hidden />\n <span className=\"font-semibold text-foreground tabular-nums\">{pct(rate)}</span>\n <span>para a próxima etapa</span>\n {i === worst && n > 2 && (\n <span className=\"font-semibold text-destructive-soft-foreground\">\n · Maior queda\n </span>\n )}\n </p>\n )}\n </li>\n )\n })}\n </ol>\n {n > 1 && last && (\n <p className=\"flex items-center gap-1 pt-2 text-sm text-muted-foreground\">\n <ArrowDown className=\"size-icon-sm\" aria-hidden />\n <span className=\"font-semibold text-foreground tabular-nums\">\n {pct((last.value / top) * 100)}\n </span>\n de conversão total do funil\n </p>\n )}\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </Root>\n )\n}\n\n/* ================================================================ séries (Recharts) */\n\nfunction Cartesian({\n type,\n data,\n xKey,\n series,\n stacked,\n colorByCategory,\n valueFormatter = fmtDefault,\n height = 'md',\n listThreshold = 8,\n className,\n ...aria\n}: CartesianChartProps) {\n const { isMobile } = useBreakpoint()\n const motion = useMotionMode()\n const presets = useMotionPresets()\n const reveal = useRevealScope()\n // Gráfico é dado que o usuário lê: desenha uma vez, na primeira montagem, e rápido (`draw`,\n // 420 ms). Trocar período ou filtro não o redesenha (W11). Só dentro de um RevealScope (a\n // chegada do dashboard) o desenho dura o tempo do reveal (900 ms). O recharts aceita a curva só\n // no formato de toCubicBezier (quatro números, sem espaços). Em `off` não recebe prop nenhuma,\n // como antes.\n const [drawn, setDrawn] = useState(false)\n const ended = useRef(0)\n const animated = type === 'pie' ? 1 : series.length\n // Um gráfico que já estava na tela em `off` quando o `on` chegou (troca tardia) já desenhou, e\n // com as props antigas: manter `draw` vazio evita que o recharts o redesenhe.\n const [mountedOn] = useState(motion === 'on')\n const draw =\n motion === 'on' && mountedOn\n ? {\n animationDuration: Math.round(\n (reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw) * 1000,\n ),\n animationEasing: toCubicBezier(presets.tokens.ease.out),\n // Só marca \"desenhado\" quando todas as séries terminaram, senão uma série pararia a\n // outra no meio do desenho.\n onAnimationEnd: () => {\n if (++ended.current >= animated) setDrawn(true)\n },\n }\n : {}\n const [asList, setAsList] = useState(false)\n // Anima só a entrada. Se a largura muda depois (barra de rolagem que aparece, janela\n // redimensionada), o gráfico vai direto à nova forma, sem desenhar fora do card no meio do\n // caminho. Com \"reduzir movimento\" no sistema, não anima.\n const [animate, setAnimate] = useState(\n () => !window.matchMedia?.('(prefers-reduced-motion: reduce)').matches,\n )\n const firstWidth = useRef<number | null>(null)\n const onResize = (width: number) => {\n if (firstWidth.current === null) firstWidth.current = width\n else if (Math.abs(width - firstWidth.current) > 1) setAnimate(false)\n }\n const canList = isMobile && data.length > listThreshold\n const showList = canList && asList\n const colors = series.map((s, i) => colorVar(s.color ?? i + 1))\n\n const axis = {\n stroke: 'var(--rendra-muted-foreground)',\n fontSize: 12,\n tickLine: false,\n axisLine: false,\n }\n const tooltip = (\n <Tooltip\n cursor={{ fill: 'var(--rendra-muted)', stroke: 'var(--rendra-border)' }}\n formatter={(v) => valueFormatter(Number(v))}\n contentStyle={{\n background: 'var(--rendra-popover)',\n border: '1px solid var(--rendra-border)',\n borderRadius: 'var(--rendra-shape-control)',\n color: 'var(--rendra-popover-foreground)',\n fontSize: 12,\n }}\n />\n )\n const legend = (\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12, paddingTop: 12 }}\n />\n )\n const common = { data, margin: { top: 8, right: 8, left: isMobile ? -16 : 0, bottom: 0 } }\n const xAxis = (\n <XAxis dataKey={xKey} {...axis} interval=\"preserveStartEnd\" minTickGap={isMobile ? 24 : 12} />\n )\n const yAxis = (\n <YAxis\n {...axis}\n width={isMobile ? 72 : 80}\n tickCount={isMobile ? 3 : 5}\n // Mostra todas as marcas calculadas (o padrão do Recharts some com algumas).\n interval={0}\n allowDecimals={false}\n tickFormatter={(v) => valueFormatter(Number(v))}\n />\n )\n const grid = (\n <CartesianGrid vertical={false} stroke=\"var(--rendra-border)\" strokeDasharray=\"3 3\" />\n )\n const bar = (s: ChartSeries, i: number, lastBar: boolean) => (\n <Bar\n key={s.key}\n dataKey={s.key}\n name={s.label}\n fill={colors[i]}\n stackId={stacked ? 'pilha' : undefined}\n // Empilhadas: só a barra de cima tem os cantos arredondados.\n radius={!stacked || lastBar ? [4, 4, 0, 0] : [0, 0, 0, 0]}\n maxBarSize={40}\n isAnimationActive={animate && !drawn}\n {...draw}\n >\n {colorByCategory && data.map((_, j) => <Cell key={j} fill={colorVar(j + 1)} />)}\n </Bar>\n )\n\n let chart\n if (type === 'pie') {\n const s = series[0]\n chart = (\n <PieChart>\n <Pie\n data={data}\n dataKey={s?.key ?? ''}\n nameKey={xKey}\n innerRadius=\"55%\"\n outerRadius=\"85%\"\n paddingAngle={2}\n stroke=\"var(--rendra-card)\"\n isAnimationActive={animate && !drawn}\n {...draw}\n >\n {data.map((_, i) => (\n <Cell key={i} fill={colorVar(i + 1)} />\n ))}\n </Pie>\n {tooltip}\n {legend}\n </PieChart>\n )\n } else if (type === 'bar') {\n chart = (\n <BarChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => bar(s, i, i === series.length - 1))}\n </BarChart>\n )\n } else if (type === 'combo') {\n const bars = series.filter((s) => (s.kind ?? 'bar') === 'bar')\n chart = (\n <ComposedChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {legend}\n {series.map((s, i) =>\n (s.kind ?? 'bar') === 'bar' ? (\n bar(s, i, s.key === bars[bars.length - 1]?.key)\n ) : (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n dot={{ r: 3, fill: colors[i], strokeWidth: 0 }}\n activeDot={{ r: 5 }}\n isAnimationActive={animate && !drawn}\n {...draw}\n />\n ),\n )}\n </ComposedChart>\n )\n } else if (type === 'area') {\n chart = (\n <AreaChart {...common}>\n <defs>\n {series.map((s, i) => (\n <linearGradient key={s.key} id={`area-${s.key}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={colors[i]} stopOpacity={0.3} />\n <stop offset=\"100%\" stopColor={colors[i]} stopOpacity={0} />\n </linearGradient>\n ))}\n </defs>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => (\n <Area\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n fill={`url(#area-${s.key})`}\n isAnimationActive={animate && !drawn}\n {...draw}\n />\n ))}\n </AreaChart>\n )\n } else {\n chart = (\n <LineChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 4 }}\n isAnimationActive={animate && !drawn}\n {...draw}\n />\n ))}\n </LineChart>\n )\n }\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type })}\n className={cn('flex min-w-0 flex-col gap-3', height === 'fill' && 'h-full', className)}\n aria-label={aria['aria-label']}\n >\n {canList && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-end\"\n icon={asList ? <BarChart3 /> : <ListIcon />}\n onClick={() => setAsList((v) => !v)}\n >\n {asList ? 'Ver gráfico' : 'Ver como lista'}\n </Button>\n )}\n {showList ? (\n <ul className=\"flex flex-col divide-y\">\n {data.map((d) => (\n <li\n key={String(d[xKey])}\n className=\"flex items-center justify-between gap-3 py-2 text-sm\"\n >\n <span className=\"text-muted-foreground\">{String(d[xKey])}</span>\n <span className=\"flex gap-3 font-medium tabular-nums\">\n {series.map((s) => (\n <span key={s.key}>{valueFormatter(Number(d[s.key]))}</span>\n ))}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div\n className={cn(\n 'w-full',\n height === 'fill'\n ? 'min-h-chart-sm flex-1'\n : height === 'sm'\n ? 'h-chart-sm'\n : 'h-chart-md',\n )}\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\" onResize={onResize}>\n {chart}\n </ResponsiveContainer>\n </div>\n )}\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.4.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.6.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
  import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
3
3
  import { join, relative, sep } from "node:path";
4
4
  //#region src/cli/auditar.ts
@@ -1,5 +1,5 @@
1
- /*! Rendra Design System v2.4.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as CATALOG } from "../components-hp7iWioq.js";
1
+ /*! Rendra Design System v2.6.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as CATALOG } from "../components-Btk1cUg2.js";
3
3
  //#region src/cli/codigos.ts
4
4
  /** Todo o catálogo, na ordem cadastrada. Sem duplicata: garantido por assertCatalogIntegrity. */
5
5
  function codigos() {