@rendra-ui/web 2.5.0 → 2.7.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 (68) hide show
  1. package/README.md +13 -3
  2. package/dist/base.css +1 -1
  3. package/dist/{empty-state-Cdb1vkMT.js → brand-feedback-icon-BYNcqBel.js} +4 -64
  4. package/dist/brand-feedback-icon-BYNcqBel.js.map +1 -0
  5. package/dist/{button-BzJgR2gH.js → button-DlzWwpZk.js} +7 -7
  6. package/dist/button-DlzWwpZk.js.map +1 -0
  7. package/dist/chart.js +15 -13
  8. package/dist/chart.js.map +1 -1
  9. package/dist/cli/auditar.js +1 -1
  10. package/dist/cli/codigos.js +2 -2
  11. package/dist/cli/executar-auditar.js +1 -1
  12. package/dist/cli/help.js +1 -1
  13. package/dist/cli/trocar.js +2 -2
  14. package/dist/cli/typescript-loader.js +1 -1
  15. package/dist/{cn-Cw7M5kEP.js → cn-B0N3Pek0.js} +2 -2
  16. package/dist/{cn-Cw7M5kEP.js.map → cn-B0N3Pek0.js.map} +1 -1
  17. package/dist/{components-DkpspH9h.js → components-CpQPMGfT.js} +18 -2
  18. package/dist/{components-DkpspH9h.js.map → components-CpQPMGfT.js.map} +1 -1
  19. package/dist/components.css +90 -1
  20. package/dist/document-viewer.js +4 -4
  21. package/dist/{tooltip-BAMSaGbS.js → dropdown-menu-M8vs0byD.js} +15 -81
  22. package/dist/dropdown-menu-M8vs0byD.js.map +1 -0
  23. package/dist/empty-state-a47Kd_Ci.js +67 -0
  24. package/dist/empty-state-a47Kd_Ci.js.map +1 -0
  25. package/dist/{features-max--5VEt6M7.js → features-max-ScxV8gb3.js} +1 -1
  26. package/dist/flow.js +1485 -0
  27. package/dist/flow.js.map +1 -0
  28. package/dist/index.js +130 -1899
  29. package/dist/index.js.map +1 -1
  30. package/dist/motion-context-BUMPf0e9.js +222 -0
  31. package/dist/motion-context-BUMPf0e9.js.map +1 -0
  32. package/dist/motion-on-CNN6y8g-.js +29 -0
  33. package/dist/motion-on-CNN6y8g-.js.map +1 -0
  34. package/dist/motion.js +3 -0
  35. package/dist/{rendra-provider-CRe0XdOU.js → rendra-provider-DEURIIL3.js} +3 -3
  36. package/dist/rendra-provider-DEURIIL3.js.map +1 -0
  37. package/dist/rich-text-editor.js +4 -3
  38. package/dist/rich-text-editor.js.map +1 -1
  39. package/dist/router-bridge.js +2 -2
  40. package/dist/router-bridge.js.map +1 -1
  41. package/dist/timeline-BOh7gUdA.js +1803 -0
  42. package/dist/timeline-BOh7gUdA.js.map +1 -0
  43. package/dist/tokens.css +1 -1
  44. package/dist/tooltip-Ym9S4h9_.js +76 -0
  45. package/dist/tooltip-Ym9S4h9_.js.map +1 -0
  46. package/dist/types/components/rendra-provider.d.ts +7 -2
  47. package/dist/types/components/rendra-router-bridge.d.ts +2 -1
  48. package/dist/types/components/ui/flow-editor.d.ts +11 -0
  49. package/dist/types/components/ui/flow-viewer.d.ts +58 -0
  50. package/dist/types/flow.d.ts +3 -0
  51. package/dist/types/index.d.ts +1 -0
  52. package/dist/types/lib/flow.d.ts +121 -0
  53. package/dist/types/lib/motion/index.d.ts +1 -1
  54. package/dist/types/lib/motion/motion-context.d.ts +21 -6
  55. package/dist/types/lib/motion/motion-on.d.ts +12 -0
  56. package/dist/types/motion.d.ts +1 -0
  57. package/dist/{use-breakpoint-O9eqFmsF.js → use-breakpoint-En17oXKY.js} +2 -2
  58. package/dist/{use-breakpoint-O9eqFmsF.js.map → use-breakpoint-En17oXKY.js.map} +1 -1
  59. package/dist/{use-motion-presets-BrkgL8lG.js → use-motion-presets-D3ww1oJX.js} +3 -3
  60. package/dist/{use-motion-presets-BrkgL8lG.js.map → use-motion-presets-D3ww1oJX.js.map} +1 -1
  61. package/dist/widget-grid.js +2 -2
  62. package/package.json +14 -1
  63. package/dist/button-BzJgR2gH.js.map +0 -1
  64. package/dist/empty-state-Cdb1vkMT.js.map +0 -1
  65. package/dist/motion-context-5BRIQAJm.js +0 -107
  66. package/dist/motion-context-5BRIQAJm.js.map +0 -1
  67. package/dist/rendra-provider-CRe0XdOU.js.map +0 -1
  68. package/dist/tooltip-BAMSaGbS.js.map +0 -1
package/README.md CHANGED
@@ -180,6 +180,7 @@ Todos em `src/components/ui`, um por finalidade, com o ícone de feedback da mar
180
180
  | **Atendimento** | Chat omnichannel: ConversationList (foto com o logotipo do canal: WhatsApp, WhatsApp Web, Instagram, Facebook, TikTok, Google Meu Negócio e Reclame Aqui), ChatThread (cliente, atendente, robô e avisos; botão direito ou "…" para responder, reagir, editar e excluir; citação que leva à mensagem original; áudio em 1x, 1,5x e 2x; baixar anexos) e ChatComposer (textarea que cresce, anexos, emoji, mensagens rápidas, gravação de áudio, arrastar e soltar ou colar prints; no celular, campo, enviar e um botão de ações) |
181
181
  | **Conteúdo** | RichTextEditor (Tiptap): formatação completa, listas, alinhamento, links, tabela, imagem com 4 alças para redimensionar, colar print, arrastar imagens e modo HTML |
182
182
  | **Painel** | WidgetGrid: widgets que se arrastam e redimensionam no modo "Ajustar dashboard", com a arrumação salva; Chart com velocímetro de meta, funil com conversão, barras com linha, barras coloridas e empilhadas |
183
+ | **Fluxo** | FlowEditor (monta o fluxo de automação com gatilho, ação, condição, espera e fim: paleta, arrastar, ligar, apagar, validar com mensagens e testar o caminho passo a passo; o JSON do fluxo vai e volta por `serializeFlow` e `parseFlow`; no celular só vê e navega, e a gaveta do bloco edita, liga ao próximo e exclui) e FlowViewer (só leitura), em `@rendra-ui/web/flow` com o React Flow |
183
184
  | **Planejamento** | Calendar (mês, semana, dia e agenda em lista, no mesmo componente; no celular vira mês compacto com a lista do dia), Kanban (altura da tela, 5 etapas visíveis e rolagem lateral também pelo touchpad, rolagem infinita por etapa, (+) no título da etapa, totais em R$ de P&S e MRR por etapa e por cartão, arrastar no computador, menu "Mover para" no toque e no teclado, uma coluna por vez no celular, limite por coluna) e ImageViewer (imagens em popup, com setas, Esc, arrastar e pinça) |
184
185
  | **Layout** | Container (respiro igual em todos os lados), Stack, Inline, Grid (por tela ou por container query), Section e PageHeader (título no corpo do celular por `bodyTitle`, sem reservar o bloco do título por `titleSpace`) |
185
186
 
@@ -202,13 +203,22 @@ Login, esqueci a senha, verificação em duas etapas (2FA), nova senha, cadastro
202
203
 
203
204
  ### Movimento (opcional, desligado por padrão)
204
205
 
205
- O `RendraProvider` (e o `RendraRouterBridge`) aceita a prop `motion`: `'off'` (o padrão) ou `'on'`. Em `'on'` os componentes animam com o Motion (`motion` 14, já em `dependencies`); em `'off'` nada do Motion é montado. 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
+ 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
 
207
208
  ```tsx
209
+ // Carrega o Motion sob demanda: o primeiro quadro sai em `off` e liga quando o chunk chega.
208
210
  <RendraProvider motion="on">{/* ... */}</RendraProvider>
209
211
  ```
210
212
 
211
- 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">`. Montando o `RendraProvider` direto, ele também exige `linkComponent`, `useCurrentPath`, `navigate` e `goBack`.
213
+ 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:
214
+
215
+ ```tsx
216
+ import { rendraMotion } from '@rendra-ui/web/motion'
217
+
218
+ ;<RendraProvider motion={rendraMotion}>{/* ... */}</RendraProvider>
219
+ ```
220
+
221
+ 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`.
212
222
 
213
223
  - **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`.
214
224
  - **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.
@@ -376,7 +386,7 @@ import '@rendra-ui/web/base.css'
376
386
  import '@rendra-ui/web/components.css'
377
387
  ```
378
388
 
379
- O CSS já sai compilado (estratégia A: o host recebe `tokens.css`, `base.css` e `components.css` prontos, sem precisar ter o Tailwind instalado, e sem nenhuma variável do namespace do Tailwind vazar para o seu tema). `react-router` fica fora das dependências: quem usa React Router importa a ponte, `@rendra-ui/web/router-bridge`; quem não usa, não precisa dela. Os componentes pesados (`document-viewer`, `rich-text-editor`, `chart`, `widget-grid`) são subcaminhos próprios, para não engordar quem não usa.
389
+ O CSS já sai compilado (estratégia A: o host recebe `tokens.css`, `base.css` e `components.css` prontos, sem precisar ter o Tailwind instalado, e sem nenhuma variável do namespace do Tailwind vazar para o seu tema). `react-router` fica fora das dependências: quem usa React Router importa a ponte, `@rendra-ui/web/router-bridge`; quem não usa, não precisa dela. Os componentes pesados (`document-viewer`, `rich-text-editor`, `chart`, `widget-grid`, `flow`) são subcaminhos próprios, para não engordar quem não usa. O `flow` (FlowEditor e FlowViewer) usa o React Flow (`@xyflow/react`, MIT): ele vem como dependência do pacote e você só importa `@rendra-ui/web/flow`, que já traz o CSS dele. No clone deste repositório, `FlowEditor` e `FlowViewer` vêm de `@/components/ui/flow-editor` e `@/components/ui/flow-viewer`, e `validateFlow`, `serializeFlow` e `parseFlow` de `@/lib/flow`.
380
390
 
381
391
  **O que o `components.css` traz, e o que não traz.** Ele contém só as classes que os componentes do pacote usam por dentro e as `@utility` do Rendra. Classes de composição das suas próprias telas (`p-12`, `max-w-sm`, `flex-col`, `mb-0`) não estão garantidas: num host com Tailwind v4, deixe o seu Tailwind gerá-las e reproduza no `@theme` a escala do Rendra (espaço 0, px, 1, 2, 3, 4, 6, 8, 12, 16 e 24, raios por papel, cores `--rendra-*`); num host sem Tailwind, escreva o CSS de composição à mão com as variáveis `--rendra-*`. A receita completa e a lista de todas as `@utility` estão em [`docs/COMO_APLICAR.md`](docs/COMO_APLICAR.md), na seção "O CSS do pacote e as classes das suas telas".
382
392
 
package/dist/base.css CHANGED
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
 
3
3
  @layer base {
4
4
  *,
@@ -1,9 +1,6 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-Cw7M5kEP.js";
3
- import { r as useMotionMode } from "./motion-context-5BRIQAJm.js";
4
- import { t as useMotionPresets } from "./use-motion-presets-BrkgL8lG.js";
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as cn } from "./cn-B0N3Pek0.js";
5
3
  import { CircleCheck, CircleX, Info, TriangleAlert } from "lucide-react";
6
- import { m } from "motion/react";
7
4
  import { createContext, useContext } from "react";
8
5
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
9
6
  import { cva } from "class-variance-authority";
@@ -150,63 +147,6 @@ function BrandFeedbackIcon({ type, size, label, animated = false, symbolTone = "
150
147
  });
151
148
  }
152
149
  //#endregion
153
- //#region src/components/ui/empty-state.tsx
154
- /** Estado vazio (e de erro) com o ícone de feedback da marca. */
155
- function EmptyState({ title, description, type = "info", actions, size = "default", className }) {
156
- const on = useMotionMode() === "on";
157
- const presets = useMotionPresets();
158
- const Icon = on ? m.span : "span";
159
- const Title = on ? m.p : "p";
160
- const Description = on ? m.p : "p";
161
- const Actions = on ? m.div : "div";
162
- const part = (index) => {
163
- if (!on) return {};
164
- const enter = presets.slide("bottom", "short");
165
- return {
166
- ...enter,
167
- transition: {
168
- ...enter.transition,
169
- ...presets.stagger(index)
170
- }
171
- };
172
- };
173
- return /* @__PURE__ */ jsxs("div", {
174
- "data-rendra": "VAZ-001",
175
- className: cn("flex flex-col items-center justify-center gap-4 text-center", size === "compact" ? "px-4 py-8" : "px-4 py-12 md:py-16", className),
176
- children: [
177
- /* @__PURE__ */ jsxs(Icon, {
178
- className: "relative flex items-center justify-center",
179
- ...part(0),
180
- children: [/* @__PURE__ */ jsx("span", {
181
- "aria-hidden": true,
182
- className: "absolute size-16 rounded-full bg-gradient-soft"
183
- }), /* @__PURE__ */ jsx(BrandFeedbackIcon, {
184
- type,
185
- size: size === "compact" ? "xl" : "2xl",
186
- className: "relative"
187
- })]
188
- }),
189
- /* @__PURE__ */ jsxs("div", {
190
- className: "flex w-full flex-col gap-1",
191
- children: [/* @__PURE__ */ jsx(Title, {
192
- className: "text-base font-semibold wrap-break-word",
193
- ...part(1),
194
- children: title
195
- }), description && /* @__PURE__ */ jsx(Description, {
196
- className: "text-sm wrap-break-word text-muted-foreground",
197
- ...part(2),
198
- children: description
199
- })]
200
- }),
201
- actions && /* @__PURE__ */ jsx(Actions, {
202
- className: "flex flex-wrap justify-center gap-3",
203
- ...part(3),
204
- children: actions
205
- })
206
- ]
207
- });
208
- }
209
- //#endregion
210
- export { BrandContext as a, useBrand as i, BrandFeedbackIcon as n, feedbackLabels as r, EmptyState as t };
150
+ export { BrandContext as i, feedbackLabels as n, useBrand as r, BrandFeedbackIcon as t };
211
151
 
212
- //# sourceMappingURL=empty-state-Cdb1vkMT.js.map
152
+ //# sourceMappingURL=brand-feedback-icon-BYNcqBel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-feedback-icon-BYNcqBel.js","names":[],"sources":["../src/brand/brand-context.ts","../src/brand/use-brand.ts","../src/components/ui/brand-feedback-icon.tsx"],"sourcesContent":["import { createContext } from 'react'\nimport type { Shape } from '@/lib/shape'\nimport type { BrandConfig, PaletteConfig } from './types'\n\nexport type ColorMode = 'light' | 'dark' | 'system'\n\nexport interface BrandContextValue {\n brand: BrandConfig\n brands: BrandConfig[]\n setBrandId: (id: string) => void\n mode: ColorMode\n resolvedMode: 'light' | 'dark'\n setMode: (mode: ColorMode) => void\n /** Formato do template ativo (fixo por template). */\n shape: Shape\n /** Paleta de cores em uso (por padrão, a do próprio modelo). */\n palette: PaletteConfig\n palettes: PaletteConfig[]\n /** Usa a paleta de outro template; null volta às cores do próprio modelo. */\n setPaletteId: (id: string | null) => void\n /** Logotipo adequado para a sidebar com a paleta atual. */\n sidebarLogoVariant: 'light' | 'dark'\n}\n\nexport const BrandContext = createContext<BrandContextValue | null>(null)\n","import { useContext } from 'react'\nimport { BrandContext } from './brand-context'\n\nexport function useBrand() {\n const ctx = useContext(BrandContext)\n if (!ctx) throw new Error('useBrand precisa estar dentro de <BrandProvider>.')\n return ctx\n}\n","import { cva, type VariantProps } from 'class-variance-authority'\nimport { CircleCheck, CircleX, Info, TriangleAlert, type LucideIcon } from 'lucide-react'\nimport type { SVGProps } from 'react'\nimport { useBrand } from '@/brand/use-brand'\nimport type { FeedbackType } from '@/brand/types'\nimport { cn } from '@/lib/cn'\n\nconst sizes = {\n sm: 'size-icon-sm',\n md: 'size-icon-md',\n lg: 'size-icon-lg',\n xl: 'size-12',\n '2xl': 'size-16',\n} as const\n\nconst iconVariants = cva('relative inline-flex shrink-0', {\n variants: { size: sizes },\n defaultVariants: { size: 'md' },\n})\n\n/** Cor do estado (tinge o símbolo e, em statusBadge, o ícone ao lado). */\nconst stateText: Record<FeedbackType, string> = {\n success: 'text-success',\n error: 'text-destructive',\n warning: 'text-warning',\n info: 'text-info',\n}\n\n/** Ícone Lucide do selo ao lado do símbolo (statusBadge). */\nconst badgeIcons: Record<FeedbackType, LucideIcon> = {\n success: CircleCheck,\n error: CircleX,\n warning: TriangleAlert,\n info: Info,\n}\n\n/** Tamanho do ícone ao lado, proporcional ao do símbolo. */\nconst badgeIconSize: Record<keyof typeof sizes, string> = {\n sm: 'size-3',\n md: 'size-icon-sm',\n lg: 'size-icon-md',\n xl: 'size-icon-lg',\n '2xl': 'size-icon-lg',\n}\n\nconst badgeTone: Record<FeedbackType, string> = {\n success: 'bg-success text-success-foreground',\n error: 'bg-destructive text-destructive-foreground',\n warning: 'bg-warning text-warning-foreground',\n info: 'bg-info text-info-foreground',\n}\n\nexport const feedbackLabels: Record<FeedbackType, string> = {\n success: 'Sucesso',\n error: 'Erro',\n warning: 'Atenção',\n info: 'Informação',\n}\n\n/** Glifos universais de status desenhados em traço, herdando currentColor. */\nfunction StatusGlyph({\n type,\n animated,\n ...props\n}: { type: FeedbackType; animated?: boolean } & SVGProps<SVGSVGElement>) {\n // pathLength=1 normaliza o traço: o desenho anima de 1 a 0 em qualquer glifo.\n const draw = animated\n ? ({ pathLength: 1, strokeDasharray: 1, className: 'animate-draw' } as const)\n : {}\n const common = {\n viewBox: '0 0 12 12',\n fill: 'none',\n stroke: 'currentColor',\n strokeWidth: 2,\n strokeLinecap: 'round' as const,\n strokeLinejoin: 'round' as const,\n 'aria-hidden': true,\n ...props,\n }\n switch (type) {\n case 'success':\n return (\n <svg {...common}>\n <path d=\"M3 6.2 5.1 8.2 9 4\" {...draw} />\n </svg>\n )\n case 'error':\n return (\n <svg {...common}>\n <path d=\"M4 4l4 4M8 4 4 8\" {...draw} />\n </svg>\n )\n case 'warning':\n return (\n <svg {...common}>\n <path d=\"M6 3.2v3.3M6 8.8v0\" {...draw} />\n </svg>\n )\n case 'info':\n return (\n <svg {...common}>\n <path d=\"M6 3.2v0M6 5.5v3.3\" {...draw} />\n </svg>\n )\n }\n}\n\nexport interface BrandFeedbackIconProps extends VariantProps<typeof iconVariants> {\n type: FeedbackType\n /** Texto para leitores de tela. Sem ele o ícone é decorativo (o texto ao lado informa). */\n label?: string\n /**\n * Anima a entrada: o símbolo entra com mola e o glifo se desenha (check se formando,\n * X se riscando). O erro treme ao final. Respeita reduzir movimento.\n * Troque a key do componente para repetir a animação.\n */\n animated?: boolean\n /**\n * Cor do símbolo. `state` (padrão): a cor do estado (verde, vermelho, amarelo, azul).\n * `brand`: a cor primária da marca (`text-primary-text`); o selo de estado mantém a cor do\n * estado, para o significado não depender só da cor.\n */\n symbolTone?: 'state' | 'brand'\n /**\n * `false` (padrão): selo de estado sobreposto ao símbolo. `true`: o ícone do estado\n * (Lucide, na cor do estado) fica ao lado do símbolo, no lugar do selo sobreposto. Com um\n * SVG próprio em `brand.feedbackIcons[type]`, o SVG vence e nenhum selo é desenhado.\n */\n statusBadge?: boolean\n /**\n * Só vale com `animated`. `pop` (padrão): entra com mola, o glifo se desenha e o erro\n * treme. `encaixe`: entra em 240 ms, curva de saída suave, sem quique, sem desenho e sem\n * tremor. Com \"reduzir movimento\" do sistema, nenhuma animação roda.\n */\n animation?: 'pop' | 'encaixe'\n className?: string\n}\n\n/**\n * Ícone de feedback da marca: o símbolo da marca em currentColor, tingido pela cor\n * semântica, com um selo de status para que o significado não dependa só da cor.\n * Cada tipo pode ser substituído por um SVG próprio em brand.config.ts (feedbackIcons).\n */\nexport function BrandFeedbackIcon({\n type,\n size,\n label,\n animated = false,\n symbolTone = 'state',\n statusBadge = false,\n animation = 'pop',\n className,\n}: BrandFeedbackIconProps) {\n const { brand } = useBrand()\n const Custom = brand.feedbackIcons?.[type]\n const Symbol = brand.symbol\n const a11y = label\n ? ({ role: 'img', 'aria-label': label } as const)\n : ({ 'aria-hidden': true } as const)\n const encaixe = animation === 'encaixe'\n // Com `symbolTone=\"state\"` o texto do estado fica na raiz (como sempre); com `brand` sai da raiz.\n const rootTone = symbolTone === 'state' ? stateText[type] : undefined\n const symbolTonal = symbolTone === 'brand' && 'text-primary-text'\n const symbolAnim = animated && (encaixe ? 'animate-encaixe' : 'animate-pop')\n const badgeAnim = animated && (encaixe ? 'animate-encaixe' : 'animate-pop anim-delay-1')\n const BadgeIcon = badgeIcons[type]\n const sideBadge = statusBadge && !Custom\n\n return (\n <span\n data-slot=\"feedback-icon\"\n data-rendra=\"BFI-001\"\n className={cn(\n sideBadge ? 'relative inline-flex shrink-0 items-center gap-1' : iconVariants({ size }),\n rootTone,\n animated && !encaixe && type === 'error' && 'animate-shake',\n className,\n )}\n {...a11y}\n >\n {Custom ? (\n <Custom className={cn('size-full', symbolTonal, symbolAnim)} />\n ) : sideBadge ? (\n <>\n <Symbol className={cn(sizes[size ?? 'md'], symbolTonal, symbolAnim)} />\n <BadgeIcon\n aria-hidden\n className={cn('shrink-0', badgeIconSize[size ?? 'md'], stateText[type], badgeAnim)}\n />\n </>\n ) : (\n <>\n <Symbol className={cn('size-full mask-status-cutout', symbolTonal, symbolAnim)} />\n <span\n className={cn(\n 'absolute right-0 bottom-0 flex size-1/2 items-center justify-center rounded-full',\n badgeTone[type],\n badgeAnim,\n )}\n >\n <StatusGlyph type={type} animated={animated && !encaixe} className=\"size-full\" />\n </span>\n </>\n )}\n </span>\n )\n}\n"],"mappings":"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"}
@@ -1,10 +1,9 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-Cw7M5kEP.js";
3
- import { r as useMotionMode, s as usePrefersReducedMotion } from "./motion-context-5BRIQAJm.js";
4
- import { t as useMotionPresets } from "./use-motion-presets-BrkgL8lG.js";
5
- import { p as resolveCatalogCode } from "./components-DkpspH9h.js";
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as cn } from "./cn-B0N3Pek0.js";
3
+ import { a as useMotionMode, o as useMotionOn, u as usePrefersReducedMotion } from "./motion-context-BUMPf0e9.js";
4
+ import { t as useMotionPresets } from "./use-motion-presets-D3ww1oJX.js";
5
+ import { p as resolveCatalogCode } from "./components-CpQPMGfT.js";
6
6
  import { Loader2 } from "lucide-react";
7
- import { m } from "motion/react";
8
7
  import { Slot } from "radix-ui";
9
8
  import { forwardRef } from "react";
10
9
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -134,6 +133,7 @@ var buttonVariants = cva("inline-flex shrink-0 cursor-pointer items-center justi
134
133
  * moram aqui e o `off` não paga nada.
135
134
  */
136
135
  var MotionButton = forwardRef(function MotionButton(props, ref) {
136
+ const { m } = useMotionOn();
137
137
  const presets = useMotionPresets();
138
138
  const { native, rest } = splitNativeEvents(props);
139
139
  const attach = (el) => {
@@ -205,4 +205,4 @@ var Button = forwardRef(function Button({ className, variant, size, icon, iconRi
205
205
  //#endregion
206
206
  export { buttonVariants as n, Spinner as r, Button as t };
207
207
 
208
- //# sourceMappingURL=button-BzJgR2gH.js.map
208
+ //# sourceMappingURL=button-DlzWwpZk.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-DlzWwpZk.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,12 +1,11 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-Cw7M5kEP.js";
3
- import { i as useRevealScope, r as useMotionMode } from "./motion-context-5BRIQAJm.js";
4
- import { n as toCubicBezier, t as useMotionPresets } from "./use-motion-presets-BrkgL8lG.js";
5
- import { p as resolveCatalogCode } from "./components-DkpspH9h.js";
6
- import { t as Button } from "./button-BzJgR2gH.js";
7
- import { n as useBreakpoint } from "./use-breakpoint-O9eqFmsF.js";
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as cn } from "./cn-B0N3Pek0.js";
3
+ import { a as useMotionMode, i as useMotionLib, s as useRevealScope } from "./motion-context-BUMPf0e9.js";
4
+ import { n as toCubicBezier, t as useMotionPresets } from "./use-motion-presets-D3ww1oJX.js";
5
+ import { p as resolveCatalogCode } from "./components-CpQPMGfT.js";
6
+ import { t as Button } from "./button-DlzWwpZk.js";
7
+ import { n as useBreakpoint } from "./use-breakpoint-En17oXKY.js";
8
8
  import { ArrowDown, BarChart3, List } from "lucide-react";
9
- import { m } from "motion/react";
10
9
  import { useId, useRef, useState } from "react";
11
10
  import { jsx, jsxs } from "react/jsx-runtime";
12
11
  import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, ComposedChart, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
@@ -78,7 +77,8 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
78
77
  const tone = (zones.find((z) => frac <= z.to) ?? zones[zones.length - 1])?.tone ?? "primary";
79
78
  const gradientId = useId();
80
79
  const [ex, ey] = point(frac, 74);
81
- const on = useMotionMode() === "on";
80
+ const lib = useMotionLib();
81
+ const on = lib !== null;
82
82
  const presets = useMotionPresets();
83
83
  const reveal = useRevealScope();
84
84
  const [sx, sy] = point(0, 74);
@@ -132,7 +132,7 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
132
132
  strokeWidth: 20,
133
133
  strokeLinecap: "round"
134
134
  }),
135
- on ? /* @__PURE__ */ jsx(m.line, {
135
+ on ? /* @__PURE__ */ jsx(lib.m.line, {
136
136
  x1: CX,
137
137
  y1: CY,
138
138
  initial: {
@@ -219,9 +219,10 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
219
219
  });
220
220
  }
221
221
  function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
222
- const on = useMotionMode() === "on";
222
+ const lib = useMotionLib();
223
+ const on = lib !== null;
223
224
  const presets = useMotionPresets();
224
- const Root = on ? m.figure : "figure";
225
+ const Root = on ? lib.m.figure : "figure";
225
226
  const n = stages.length;
226
227
  const top = stages[0]?.value || 1;
227
228
  const last = stages[n - 1];
@@ -310,7 +311,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
310
311
  const [drawn, setDrawn] = useState(false);
311
312
  const ended = useRef(0);
312
313
  const animated = type === "pie" ? 1 : series.length;
313
- const draw = motion === "on" ? {
314
+ const [mountedOn] = useState(motion === "on");
315
+ const draw = motion === "on" && mountedOn ? {
314
316
  animationDuration: Math.round((reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw) * 1e3),
315
317
  animationEasing: toCubicBezier(presets.tokens.ease.out),
316
318
  onAnimationEnd: () => {
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 { m } from 'motion/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 { 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 on = useMotionMode() === 'on'\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 <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 on = useMotionMode() === 'on'\n const presets = useMotionPresets()\n // Em `on` o contêiner do funil entra com fade.\n const Root = on ? 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 const draw =\n motion === 'on'\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":";;;;;;;;;;;;;AAsGA,IAAM,UAAU;CACd;CACA;CACA;CACA;CACA;CACA;AACF;AACA,IAAM,YAAY,MAAc,OAAO,SAAS,IAAI,KAAK,QAAQ,QAAQ;AACzE,IAAM,cAAc,MAAc,EAAE,eAAe,OAAO;AAC1D,IAAM,OAAO,MACX,GAAG,EAAE,eAAe,SAAS;CAAE,uBAAuB;CAAG,uBAAuB;AAAE,CAAC,EAAE;AAEvF,SAAgB,MAAM,OAAmB;CACvC,IAAI,MAAM,SAAS,SAAS,OAAO,oBAAC,OAAD,EAAO,GAAI,MAAQ,CAAA;CACtD,IAAI,MAAM,SAAS,UAAU,OAAO,oBAAC,QAAD,EAAQ,GAAI,MAAQ,CAAA;CACxD,OAAO,oBAAC,WAAD,EAAW,GAAI,MAAQ,CAAA;AAChC;AAIA,IAAM,eAA4B;CAChC;EAAE,IAAI;EAAK,MAAM;CAAQ;CACzB;EAAE,IAAI;EAAK,MAAM;CAAU;CAC3B;EAAE,IAAI;EAAG,MAAM;CAAU;AAC3B;AAEA,IAAM,YAA+C;CACnD,OAAO;CACP,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;AACX;AACA,IAAM,WAA8C;CAClD,OAAO;CACP,SAAS;CACT,SAAS;CACT,SAAS;CACT,SAAS;AACX;AAGA,IAAM,KAAK;AACX,IAAM,KAAK;AACX,IAAM,IAAI;AACV,IAAM,SAAS,MAAc,IAAI,MAAM;CACrC,MAAM,IAAI,KAAK,MAAM,IAAI;CACzB,OAAO,CAAC,KAAK,IAAI,KAAK,IAAI,CAAC,GAAG,KAAK,IAAI,KAAK,IAAI,CAAC,CAAC;AACpD;AACA,IAAM,OAAO,MAAc,IAAY,IAAI,MAAM;CAC/C,MAAM,CAAC,IAAI,MAAM,MAAM,MAAM,CAAC;CAC9B,MAAM,CAAC,IAAI,MAAM,MAAM,IAAI,CAAC;CAC5B,OAAO,KAAK,GAAG,QAAQ,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,SAAS,GAAG,QAAQ,CAAC,EAAE,GAAG,GAAG,QAAQ,CAAC;AAC/F;AAEA,SAAS,MAAM,EACb,OACA,KACA,MAAM,GACN,QACA,QAAQ,cACR,OACA,iBAAiB,YACjB,WACA,GAAG,QACe;CAClB,MAAM,QAAQ,KAAK,IAAI,GAAG,MAAM,GAAG;CACnC,MAAM,SAAS,MAAc,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,CAAC,CAAC;CAEvD,MAAM,UAAU,SAAU,QAAQ,SAAU,MAAM;CAClD,MAAM,OAAO,YAAY,OAAO,MAAM,UAAU,GAAG,IAAI,OAAO,QAAQ,OAAO,KAAK;CAElF,MAAM,QADO,MAAM,MAAM,MAAM,QAAQ,EAAE,EAAE,KAAK,MAAM,MAAM,SAAS,GAAA,EAClD,QAAQ;CAC3B,MAAM,aAAa,MAAM;CACzB,MAAM,CAAC,IAAI,MAAM,MAAM,MAAM,EAAM;CACnC,MAAM,KAAK,cAAc,MAAM;CAC/B,MAAM,UAAU,iBAAiB;CACjC,MAAM,SAAS,eAAe;CAC9B,MAAM,CAAC,IAAI,MAAM,MAAM,GAAG,EAAM;CAEhC,OACE,qBAAC,UAAD;EACE,eAAa,mBAAmB,SAAS,EAAE,MAAM,QAAQ,CAAC;EAC1D,WAAW,GAAG,4CAA4C,SAAS;EACnE,cAAY,KAAK;EAHnB,UAAA;GAKE,oBAAC,OAAD;IACE,MAAK;IACL,iBAAe;IACf,iBAAe;IACf,iBAAe,KAAK,MAAM,OAAO,GAAG;IACpC,kBAAgB,GAAG,YAAY,OAAO,GAAG,IAAI,OAAO,EAAE,YAAY,eAAe,KAAK,EAAE,IAAI,SAAS;IACrG,cAAY,KAAK;IACjB,WAAU;IAEV,UAAA,qBAAC,OAAD;KAAK,SAAQ;KAAc,WAAU;KAAS,eAAA;KAA9C,UAAA;MACE,oBAAC,QAAD,EAAA,UAEE,qBAAC,kBAAD;OAAgB,IAAI;OAAY,IAAG;OAAI,IAAG;OAAI,IAAG;OAAI,IAAG;OAAxD,UAAA;QACE,oBAAC,QAAD;SAAM,QAAO;SAAK,WAAU;QAA2B,CAAA;QACvD,oBAAC,QAAD;SACE,QAAO;SACP,WAAU;QACX,CAAA;QACD,oBAAC,QAAD;SAAM,QAAO;SAAM,WAAU;QAA2B,CAAA;QACxD,oBAAC,QAAD;SAAM,QAAO;SAAO,WAAU;QAA4B,CAAA;OAC5C;MACZ,CAAA,EAAA,CAAA;MACN,oBAAC,QAAD;OACE,GAAG,IAAI,GAAG,CAAC;OACX,MAAK;OACL,QAAQ,QAAQ,WAAW;OAC3B,aAAa;OACb,eAAc;MACf,CAAA;MAEA,KAEC,oBAAC,EAAE,MAAH;OACE,IAAI;OACJ,IAAI;OACJ,SAAS;QAAE,IAAI;QAAI,IAAI;OAAG;OAC1B,SAAS;QAAE,IAAI;QAAI,IAAI;OAAG;OAC1B,YAAY;QACV,UAAU,SAAS,QAAQ,OAAO,OAAO,OAAO,QAAQ,OAAO,SAAS;QACxE,MAAM,QAAQ,OAAO,KAAK;OAC5B;OACA,QAAO;OACP,aAAa;OACb,eAAc;MACf,CAAA,IAMD,oBAAC,QAAD;OACE,IAAI;OACJ,IAAI;OACJ,IAAI;OACJ,IAAI;OACJ,QAAO;OACP,aAAa;OACb,eAAc;MACf,CAAA;MAEH,oBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAG,MAAK;MAA4B,CAAA;MAC/D,oBAAC,UAAD;OAAQ,IAAI;OAAI,IAAI;OAAI,GAAG;OAAG,MAAK;MAAsB,CAAA;MACzD,oBAAC,QAAD;OACE,GAAG,MAAM,CAAC,CAAC,CAAC;OACZ,GAAG;OACH,YAAW;OACX,UAAU;OACV,MAAK;OACN,UAAA;MAEK,CAAA;MACN,oBAAC,QAAD;OACE,GAAG,MAAM,CAAC,CAAC,CAAC;OACZ,GAAG;OACH,YAAW;OACX,UAAU;OACV,MAAK;OACN,UAAA;MAEK,CAAA;KACH;;GACF,CAAA;GACL,qBAAC,OAAD;IAAK,WAAU;IAAf,UAAA;KACE,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,YAAY,OAAO,GAAG,KAAK,MAAM,OAAO,EAAE,KAAK,eAAe,KAAK;KAChE,CAAA;KACL,SAAS,oBAAC,QAAD;MAAM,WAAU;MAAuB,UAAA;KAAY,CAAA;KAC5D,WAAW,KAAA,KACV,qBAAC,QAAD;MAAM,WAAU;MAAhB,UAAA,CACE,qBAAC,QAAD,EAAA,UAAA,CAAM,UAAO,eAAe,MAAM,CAAQ,EAAA,CAAA,GAC1C,qBAAC,QAAD,EAAA,UAAA,CAAM,eAAY,eAAe,KAAK,CAAQ,EAAA,CAAA,CAC1C;;KAER,oBAAC,QAAD;MAAM,WAAW,GAAG,qDAAqD,UAAU,KAAK;MACrF,UAAA,SAAS;KACN,CAAA;IACH;;GACL,oBAAC,cAAD;IAAY,WAAU;IAAW,UAAA,KAAK;GAA0B,CAAA;EAC1D;;AAEZ;AAIA,SAAS,OAAO,EAAE,QAAQ,iBAAiB,YAAY,WAAW,GAAG,QAA0B;CAC7F,MAAM,KAAK,cAAc,MAAM;CAC/B,MAAM,UAAU,iBAAiB;CAEjC,MAAM,OAAO,KAAK,EAAE,SAAS;CAC7B,MAAM,IAAI,OAAO;CACjB,MAAM,MAAM,OAAO,EAAE,EAAE,SAAS;CAChC,MAAM,OAAO,OAAO,IAAI;CAExB,MAAM,SAAS,MAAc,IAAK,IAAI,KAAK,IAAI,GAAG,CAAC,IAAK;CAExD,MAAM,QAAQ,MAAc;EAE1B,OAAO,iEADG,IAAI,IAAI,KAAK,MAAM,MAAO,KAAK,IAAI,KAAM,GAAG,IAAI,IACgB;CAC5E;CACA,MAAM,QAAQ,OAAO,KAAK,GAAG,MAAM;EACjC,MAAM,OAAO,OAAO,IAAI;EACxB,OAAO,QAAQ,EAAE,QAAQ,IAAK,KAAK,QAAQ,EAAE,QAAS,MAAM;CAC9D,CAAC;CACD,MAAM,QAAQ,MAAM,QACjB,GAAG,GAAG,MAAO,MAAM,SAAS,MAAM,QAAQ,KAAK,MAAM,MAAM,aAAa,IAAI,GAC7E,IACF;CAEA,OACE,qBAAC,MAAD;EACE,eAAa,mBAAmB,SAAS,EAAE,MAAM,SAAS,CAAC;EAC3D,WAAW,GAAG,sCAAsC,SAAS;EAC7D,cAAY,KAAK;EACjB,GAAK,KAAK,QAAQ,OAAO,CAAC;EAJ5B,UAAA;GAME,oBAAC,MAAD;IAAI,WAAU;IACX,UAAA,OAAO,KAAK,GAAG,MAAM;KACpB,MAAM,OAAO,MAAM;KACnB,OACE,qBAAC,MAAD;MAAkB,WAAU;MAA5B,UAAA,CACE,qBAAC,OAAD;OACE,WAAU;OACV,OACE;QACE,SAAS,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC;QAC3B,YAAY,MAAM,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC;QAClC,UAAU,KAAK,CAAC;OAClB;OAPJ,UAAA,CAUE,oBAAC,QAAD;QAAM,WAAU;QACb,UAAA,eAAe,EAAE,KAAK;OACnB,CAAA,GACN,oBAAC,QAAD;QAAM,WAAU;QAAW,UAAA,EAAE;OAAY,CAAA,CACtC;MACJ,CAAA,GAAA,QAAQ,QACP,qBAAC,KAAD;OAAG,WAAU;OAAb,UAAA;QACE,oBAAC,WAAD;SAAW,WAAU;SAAS,eAAA;QAAa,CAAA;QAC3C,oBAAC,QAAD;SAAM,WAAU;SAA8C,UAAA,IAAI,IAAI;QAAQ,CAAA;QAC9E,oBAAC,QAAD,EAAA,UAAM,uBAA0B,CAAA;QAC/B,MAAM,SAAS,IAAI,KAClB,oBAAC,QAAD;SAAM,WAAU;SAAiD,UAAA;QAE3D,CAAA;OAEP;MAEH,CAAA,CAAA;KA5BK,GAAA,EAAE,KA4BP;IAER,CAAC;GACC,CAAA;GACH,IAAI,KAAK,QACR,qBAAC,KAAD;IAAG,WAAU;IAAb,UAAA;KACE,oBAAC,WAAD;MAAW,WAAU;MAAe,eAAA;KAAa,CAAA;KACjD,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,IAAK,KAAK,QAAQ,MAAO,GAAG;KACzB,CAAA;KAAC;IAEN;;GAEL,oBAAC,cAAD;IAAY,WAAU;IAAW,UAAA,KAAK;GAA0B,CAAA;EAC5D;;AAEV;AAIA,SAAS,UAAU,EACjB,MACA,MACA,MACA,QACA,SACA,iBACA,iBAAiB,YACjB,SAAS,MACT,gBAAgB,GAChB,WACA,GAAG,QACmB;CACtB,MAAM,EAAE,aAAa,cAAc;CACnC,MAAM,SAAS,cAAc;CAC7B,MAAM,UAAU,iBAAiB;CACjC,MAAM,SAAS,eAAe;CAM9B,MAAM,CAAC,OAAO,YAAY,SAAS,KAAK;CACxC,MAAM,QAAQ,OAAO,CAAC;CACtB,MAAM,WAAW,SAAS,QAAQ,IAAI,OAAO;CAC7C,MAAM,OACJ,WAAW,OACP;EACE,mBAAmB,KAAK,OACrB,SAAS,QAAQ,OAAO,OAAO,OAAO,QAAQ,OAAO,SAAS,QAAQ,GACzE;EACA,iBAAiB,cAAc,QAAQ,OAAO,KAAK,GAAG;EAGtD,sBAAsB;GACpB,IAAI,EAAE,MAAM,WAAW,UAAU,SAAS,IAAI;EAChD;CACF,IACA,CAAC;CACP,MAAM,CAAC,QAAQ,aAAa,SAAS,KAAK;CAI1C,MAAM,CAAC,SAAS,cAAc,eACtB,CAAC,OAAO,aAAa,kCAAkC,CAAC,CAAC,OACjE;CACA,MAAM,aAAa,OAAsB,IAAI;CAC7C,MAAM,YAAY,UAAkB;EAClC,IAAI,WAAW,YAAY,MAAM,WAAW,UAAU;OACjD,IAAI,KAAK,IAAI,QAAQ,WAAW,OAAO,IAAI,GAAG,WAAW,KAAK;CACrE;CACA,MAAM,UAAU,YAAY,KAAK,SAAS;CAC1C,MAAM,WAAW,WAAW;CAC5B,MAAM,SAAS,OAAO,KAAK,GAAG,MAAM,SAAS,EAAE,SAAS,IAAI,CAAC,CAAC;CAE9D,MAAM,OAAO;EACX,QAAQ;EACR,UAAU;EACV,UAAU;EACV,UAAU;CACZ;CACA,MAAM,UACJ,oBAAC,SAAD;EACE,QAAQ;GAAE,MAAM;GAAuB,QAAQ;EAAuB;EACtE,YAAY,MAAM,eAAe,OAAO,CAAC,CAAC;EAC1C,cAAc;GACZ,YAAY;GACZ,QAAQ;GACR,cAAc;GACd,OAAO;GACP,UAAU;EACZ;CACD,CAAA;CAEH,MAAM,SACJ,oBAAC,QAAD;EACE,eAAc;EACd,UAAS;EACT,UAAU;EACV,cAAc;GAAE,UAAU;GAAI,YAAY;EAAG;CAC9C,CAAA;CAEH,MAAM,SAAS;EAAE;EAAM,QAAQ;GAAE,KAAK;GAAG,OAAO;GAAG,MAAM,WAAW,MAAM;GAAG,QAAQ;EAAE;CAAE;CACzF,MAAM,QACJ,oBAAC,OAAD;EAAO,SAAS;EAAM,GAAI;EAAM,UAAS;EAAmB,YAAY,WAAW,KAAK;CAAK,CAAA;CAE/F,MAAM,QACJ,oBAAC,OAAD;EACE,GAAI;EACJ,OAAO,WAAW,KAAK;EACvB,WAAW,WAAW,IAAI;EAE1B,UAAU;EACV,eAAe;EACf,gBAAgB,MAAM,eAAe,OAAO,CAAC,CAAC;CAC/C,CAAA;CAEH,MAAM,OACJ,oBAAC,eAAD;EAAe,UAAU;EAAO,QAAO;EAAuB,iBAAgB;CAAO,CAAA;CAEvF,MAAM,OAAO,GAAgB,GAAW,YACtC,oBAAC,KAAD;EAEE,SAAS,EAAE;EACX,MAAM,EAAE;EACR,MAAM,OAAO;EACb,SAAS,UAAU,UAAU,KAAA;EAE7B,QAAQ,CAAC,WAAW,UAAU;GAAC;GAAG;GAAG;GAAG;EAAC,IAAI;GAAC;GAAG;GAAG;GAAG;EAAC;EACxD,YAAY;EACZ,mBAAmB,WAAW,CAAC;EAC/B,GAAI;EAEH,UAAA,mBAAmB,KAAK,KAAK,GAAG,MAAM,oBAAC,MAAD,EAAc,MAAM,SAAS,IAAI,CAAC,EAAI,GAA3B,CAA2B,CAAC;CAC3E,GAZE,EAAE,GAYJ;CAGP,IAAI;CACJ,IAAI,SAAS,OAAO;EAClB,MAAM,IAAI,OAAO;EACjB,QACE,qBAAC,UAAD,EAAA,UAAA;GACE,oBAAC,KAAD;IACQ;IACN,SAAS,GAAG,OAAO;IACnB,SAAS;IACT,aAAY;IACZ,aAAY;IACZ,cAAc;IACd,QAAO;IACP,mBAAmB,WAAW,CAAC;IAC/B,GAAI;IAEH,UAAA,KAAK,KAAK,GAAG,MACZ,oBAAC,MAAD,EAAc,MAAM,SAAS,IAAI,CAAC,EAAI,GAA3B,CAA2B,CACvC;GACE,CAAA;GACJ;GACA;EACO,EAAA,CAAA;CAEd,OAAO,IAAI,SAAS,OAClB,QACE,qBAAC,UAAD;EAAU,GAAI;EAAd,UAAA;GACG;GACA;GACA;GACA;GACA,OAAO,SAAS,KAAK;GACrB,OAAO,KAAK,GAAG,MAAM,IAAI,GAAG,GAAG,MAAM,OAAO,SAAS,CAAC,CAAC;EAChD;;MAEP,IAAI,SAAS,SAAS;EAC3B,MAAM,OAAO,OAAO,QAAQ,OAAO,EAAE,QAAQ,WAAW,KAAK;EAC7D,QACE,qBAAC,eAAD;GAAe,GAAI;GAAnB,UAAA;IACG;IACA;IACA;IACA;IACA;IACA,OAAO,KAAK,GAAG,OACb,EAAE,QAAQ,WAAW,QACpB,IAAI,GAAG,GAAG,EAAE,QAAQ,KAAK,KAAK,SAAS,EAAE,EAAE,GAAG,IAE9C,oBAAC,MAAD;KAEE,MAAK;KACL,SAAS,EAAE;KACX,MAAM,EAAE;KACR,QAAQ,OAAO;KACf,aAAa;KACb,KAAK;MAAE,GAAG;MAAG,MAAM,OAAO;MAAI,aAAa;KAAE;KAC7C,WAAW,EAAE,GAAG,EAAE;KAClB,mBAAmB,WAAW,CAAC;KAC/B,GAAI;IACL,GAVM,EAAE,GAUR,CAEL;GACa;;CAEnB,OAAO,IAAI,SAAS,QAClB,QACE,qBAAC,WAAD;EAAW,GAAI;EAAf,UAAA;GACE,oBAAC,QAAD,EAAA,UACG,OAAO,KAAK,GAAG,MACd,qBAAC,kBAAD;IAA4B,IAAI,QAAQ,EAAE;IAAO,IAAG;IAAI,IAAG;IAAI,IAAG;IAAI,IAAG;IAAzE,UAAA,CACE,oBAAC,QAAD;KAAM,QAAO;KAAK,WAAW,OAAO;KAAI,aAAa;IAAM,CAAA,GAC3D,oBAAC,QAAD;KAAM,QAAO;KAAO,WAAW,OAAO;KAAI,aAAa;IAAI,CAAA,CAC7C;GAHK,GAAA,EAAE,GAGP,CACjB,EACG,CAAA;GACL;GACA;GACA;GACA;GACA,OAAO,SAAS,KAAK;GACrB,OAAO,KAAK,GAAG,MACd,oBAAC,MAAD;IAEE,MAAK;IACL,SAAS,EAAE;IACX,MAAM,EAAE;IACR,QAAQ,OAAO;IACf,aAAa;IACb,MAAM,aAAa,EAAE,IAAI;IACzB,mBAAmB,WAAW,CAAC;IAC/B,GAAI;GACL,GATM,EAAE,GASR,CACF;EACQ;;MAGb,QACE,qBAAC,WAAD;EAAW,GAAI;EAAf,UAAA;GACG;GACA;GACA;GACA;GACA,OAAO,SAAS,KAAK;GACrB,OAAO,KAAK,GAAG,MACd,oBAAC,MAAD;IAEE,MAAK;IACL,SAAS,EAAE;IACX,MAAM,EAAE;IACR,QAAQ,OAAO;IACf,aAAa;IACb,KAAK;IACL,WAAW,EAAE,GAAG,EAAE;IAClB,mBAAmB,WAAW,CAAC;IAC/B,GAAI;GACL,GAVM,EAAE,GAUR,CACF;EACQ;;CAIf,OACE,qBAAC,UAAD;EACE,eAAa,mBAAmB,SAAS,EAAE,KAAK,CAAC;EACjD,WAAW,GAAG,+BAA+B,WAAW,UAAU,UAAU,SAAS;EACrF,cAAY,KAAK;EAHnB,UAAA;GAKG,WACC,oBAAC,QAAD;IACE,SAAQ;IACR,MAAK;IACL,WAAU;IACV,MAAM,SAAS,oBAAC,WAAD,CAAY,CAAA,IAAI,oBAAC,MAAD,CAAW,CAAA;IAC1C,eAAe,WAAW,MAAM,CAAC,CAAC;IAEjC,UAAA,SAAS,gBAAgB;GACpB,CAAA;GAET,WACC,oBAAC,MAAD;IAAI,WAAU;IACX,UAAA,KAAK,KAAK,MACT,qBAAC,MAAD;KAEE,WAAU;KAFZ,UAAA,CAIE,oBAAC,QAAD;MAAM,WAAU;MAAyB,UAAA,OAAO,EAAE,KAAK;KAAQ,CAAA,GAC/D,oBAAC,QAAD;MAAM,WAAU;MACb,UAAA,OAAO,KAAK,MACX,oBAAC,QAAD,EAAA,UAAmB,eAAe,OAAO,EAAE,EAAE,IAAI,CAAC,EAAQ,GAA/C,EAAE,GAA6C,CAC3D;KACG,CAAA,CACJ;IATG,GAAA,OAAO,EAAE,KAAK,CASjB,CACL;GACC,CAAA,IAEJ,oBAAC,OAAD;IACE,WAAW,GACT,UACA,WAAW,SACP,0BACA,WAAW,OACT,eACA,YACR;IAEA,UAAA,oBAAC,qBAAD;KAAqB,OAAM;KAAO,QAAO;KAAiB;KACvD,UAAA;IACkB,CAAA;GAClB,CAAA;GAEP,oBAAC,cAAD;IAAY,WAAU;IAAW,UAAA,KAAK;GAA0B,CAAA;EAC1D;;AAEZ"}
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.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.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.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as CATALOG } from "../components-DkpspH9h.js";
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as CATALOG } from "../components-CpQPMGfT.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() {
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
  import { EXTENSAO_VARRIDA, IDS_REGRAS_GENERICAS, auditLines, auditar, casaPrefixo, coletarArquivos, percorrer, relativoA } from "./auditar.js";
3
3
  import { formatHelp } from "./help.js";
4
4
  import { existsSync, readFileSync } from "node:fs";
package/dist/cli/help.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
  //#region src/cli/help.ts
3
3
  /** Linha final de autoria, igual em toda saída publicável (CLI, banner do pacote). */
4
4
  var AUTHOR_LINE = "Rendra Design System, por Bruno Magalhaes: brunomagalhaes.me · instagram.com/brunomagalhaes.me";
@@ -1,5 +1,5 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { f as getCatalogEntry } from "../components-DkpspH9h.js";
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { f as getCatalogEntry } from "../components-CpQPMGfT.js";
3
3
  import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
4
  import { join, relative, sep } from "node:path";
5
5
  //#region src/cli/trocar.ts
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
  import { join } from "node:path";
3
3
  import { createRequire } from "node:module";
4
4
  import { pathToFileURL } from "node:url";
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
  import { clsx } from "clsx";
3
3
  import { extendTailwindMerge } from "tailwind-merge";
4
4
  //#region src/lib/cn.ts
@@ -19,4 +19,4 @@ function cn(...inputs) {
19
19
  //#endregion
20
20
  export { cn as t };
21
21
 
22
- //# sourceMappingURL=cn-Cw7M5kEP.js.map
22
+ //# sourceMappingURL=cn-B0N3Pek0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cn-Cw7M5kEP.js","names":[],"sources":["../src/lib/cn.ts"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx'\nimport { extendTailwindMerge } from 'tailwind-merge'\n\n// Ensina o tailwind-merge sobre os tokens próprios, para que text-xs e text-primary-text\n// não sejam tratados como conflito, e h-control-md substitua h-12 corretamente. text-label e\n// text-help são tamanho (rótulo e orientação do campo); text-label-foreground é cor.\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', 'label', 'help'] }],\n },\n },\n})\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":";;;;AAMA,IAAM,UAAU,oBAAoB,EAClC,QAAQ,EACN,aAAa,EACX,aAAa,CAAC,EAAE,MAAM;CAAC;CAAM;CAAM;CAAQ;CAAM;CAAM;CAAO;CAAO;CAAS;AAAM,EAAE,CAAC,EACzF,EACF,EACF,CAAC;AAED,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}
1
+ {"version":3,"file":"cn-B0N3Pek0.js","names":[],"sources":["../src/lib/cn.ts"],"sourcesContent":["import { clsx, type ClassValue } from 'clsx'\nimport { extendTailwindMerge } from 'tailwind-merge'\n\n// Ensina o tailwind-merge sobre os tokens próprios, para que text-xs e text-primary-text\n// não sejam tratados como conflito, e h-control-md substitua h-12 corretamente. text-label e\n// text-help são tamanho (rótulo e orientação do campo); text-label-foreground é cor.\nconst twMerge = extendTailwindMerge({\n extend: {\n classGroups: {\n 'font-size': [{ text: ['xs', 'sm', 'base', 'lg', 'xl', '2xl', '3xl', 'label', 'help'] }],\n },\n },\n})\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n"],"mappings":";;;;AAMA,IAAM,UAAU,oBAAoB,EAClC,QAAQ,EACN,aAAa,EACX,aAAa,CAAC,EAAE,MAAM;CAAC;CAAM;CAAM;CAAQ;CAAM;CAAM;CAAO;CAAO;CAAS;AAAM,EAAE,CAAC,EACzF,EACF,EACF,CAAC;AAED,SAAgB,GAAG,GAAG,QAAsB;CAC1C,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B"}
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.7.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
2
  //#region src/catalog/components.ts
3
3
  /** Formato do código: de 3 a 4 letras maiúsculas, hífen, três dígitos. */
4
4
  var COMPONENT_CODE_PATTERN = /^[A-Z]{3,4}-\d{3}$/;
@@ -703,6 +703,22 @@ var CATALOG = [
703
703
  variantProps: { layout: "gallery" },
704
704
  whenToUse: "Para upload de fotos e vídeos, com miniatura em vez de linha de lista."
705
705
  },
706
+ {
707
+ code: "FLX-001",
708
+ name: "Editor de fluxo",
709
+ component: "FlowEditor",
710
+ file: "components/ui/flow-editor.tsx",
711
+ variantProps: {},
712
+ whenToUse: "Para montar, ligar, validar e testar um fluxo de automação (gatilho, ação, condição, espera e fim)."
713
+ },
714
+ {
715
+ code: "FLX-002",
716
+ name: "Visualizador de fluxo",
717
+ component: "FlowViewer",
718
+ file: "components/ui/flow-viewer.tsx",
719
+ variantProps: {},
720
+ whenToUse: "Para mostrar um fluxo de automação só para ler, sem editar."
721
+ },
706
722
  {
707
723
  code: "WDG-001",
708
724
  name: "Grade de widgets",
@@ -814,4 +830,4 @@ function assertCatalogIntegrity() {
814
830
  //#endregion
815
831
  export { catalogByComponent as a, findComponentsWithoutDefault as c, findPresetCollisions as d, getCatalogEntry as f, assertCatalogIntegrity as i, findDuplicateCodes as l, CATALOG_EXCLUDED_FILES as n, filesMissingCatalogEntry as o, resolveCatalogCode as p, COMPONENT_CODE_PATTERN as r, findComponentsWithMultipleDefaults as s, CATALOG as t, findInvalidFormatCodes as u };
816
832
 
817
- //# sourceMappingURL=components-DkpspH9h.js.map
833
+ //# sourceMappingURL=components-CpQPMGfT.js.map