@rendra-ui/web 2.3.0 → 2.5.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 (85) hide show
  1. package/README.md +107 -61
  2. package/bin/rendra.mjs +19 -7
  3. package/dist/base.css +33 -1
  4. package/dist/{button-DWhICtIG.js → button-BzJgR2gH.js} +86 -21
  5. package/dist/button-BzJgR2gH.js.map +1 -0
  6. package/dist/chart.js +61 -15
  7. package/dist/chart.js.map +1 -1
  8. package/dist/cli/auditar.js +66 -16
  9. package/dist/cli/auditar.js.map +1 -1
  10. package/dist/cli/codigos.js +2 -2
  11. package/dist/cli/executar-auditar.js +634 -0
  12. package/dist/cli/executar-auditar.js.map +1 -0
  13. package/dist/cli/help.js +7 -2
  14. package/dist/cli/help.js.map +1 -1
  15. package/dist/cli/trocar.js +2 -2
  16. package/dist/cli/typescript-loader.js +1 -1
  17. package/dist/{cn-DDZQKUVV.js → cn-Cw7M5kEP.js} +2 -2
  18. package/dist/{cn-DDZQKUVV.js.map → cn-Cw7M5kEP.js.map} +1 -1
  19. package/dist/{components-Cq_PmN4y.js → components-DkpspH9h.js} +21 -4
  20. package/dist/components-DkpspH9h.js.map +1 -0
  21. package/dist/components.css +274 -32
  22. package/dist/document-viewer.js +6 -6
  23. package/dist/document-viewer.js.map +1 -1
  24. package/dist/{empty-state-C491qfzf.js → empty-state-Cdb1vkMT.js} +81 -37
  25. package/dist/empty-state-Cdb1vkMT.js.map +1 -0
  26. package/dist/features-max--5VEt6M7.js +3 -0
  27. package/dist/index.js +1170 -390
  28. package/dist/index.js.map +1 -1
  29. package/dist/motion-context-5BRIQAJm.js +107 -0
  30. package/dist/motion-context-5BRIQAJm.js.map +1 -0
  31. package/dist/{rendra-provider-BRtk5Qa1.js → rendra-provider-CRe0XdOU.js} +9 -5
  32. package/dist/rendra-provider-CRe0XdOU.js.map +1 -0
  33. package/dist/rich-text-editor.js +4 -4
  34. package/dist/router-bridge.js +5 -4
  35. package/dist/router-bridge.js.map +1 -1
  36. package/dist/tokens.css +73 -15
  37. package/dist/tooltip-BAMSaGbS.js +224 -0
  38. package/dist/tooltip-BAMSaGbS.js.map +1 -0
  39. package/dist/types/components/app-shell/app-shell.d.ts +22 -2
  40. package/dist/types/components/app-shell/shell-context.d.ts +7 -1
  41. package/dist/types/components/app-shell/types.d.ts +23 -0
  42. package/dist/types/components/layout/page-header.d.ts +17 -1
  43. package/dist/types/components/rendra-provider.d.ts +7 -1
  44. package/dist/types/components/rendra-router-bridge.d.ts +3 -1
  45. package/dist/types/components/ui/accordion.d.ts +1 -1
  46. package/dist/types/components/ui/badge.d.ts +1 -1
  47. package/dist/types/components/ui/brand-feedback-icon.d.ts +20 -3
  48. package/dist/types/components/ui/button-group.d.ts +8 -1
  49. package/dist/types/components/ui/button.d.ts +1 -1
  50. package/dist/types/components/ui/dropdown-menu.d.ts +5 -5
  51. package/dist/types/components/ui/empty-state.d.ts +4 -0
  52. package/dist/types/components/ui/error-page.d.ts +6 -1
  53. package/dist/types/components/ui/modal.d.ts +4 -0
  54. package/dist/types/components/ui/picker-panel.d.ts +1 -1
  55. package/dist/types/components/ui/popover.d.ts +3 -3
  56. package/dist/types/components/ui/radio-group.d.ts +1 -1
  57. package/dist/types/components/ui/skeleton.d.ts +5 -1
  58. package/dist/types/components/ui/stat-card.d.ts +1 -1
  59. package/dist/types/components/ui/table.d.ts +0 -6
  60. package/dist/types/components/ui/tooltip.d.ts +1 -1
  61. package/dist/types/components/ui/wizard.d.ts +14 -1
  62. package/dist/types/hooks/use-reduced-motion.d.ts +3 -0
  63. package/dist/types/index.d.ts +1 -0
  64. package/dist/types/lib/initials.d.ts +2 -0
  65. package/dist/types/lib/motion/count.d.ts +17 -0
  66. package/dist/types/lib/motion/features-max.d.ts +1 -0
  67. package/dist/types/lib/motion/index.d.ts +5 -0
  68. package/dist/types/lib/motion/motion-context.d.ts +43 -0
  69. package/dist/types/lib/motion/native-events.d.ts +16 -0
  70. package/dist/types/lib/motion/presets.d.ts +92 -0
  71. package/dist/types/lib/motion/reorder.d.ts +7 -0
  72. package/dist/types/lib/motion/tokens.d.ts +66 -0
  73. package/dist/types/lib/motion/use-motion-presets.d.ts +8 -0
  74. package/dist/{use-breakpoint-C3ivDt2R.js → use-breakpoint-O9eqFmsF.js} +2 -2
  75. package/dist/{use-breakpoint-C3ivDt2R.js.map → use-breakpoint-O9eqFmsF.js.map} +1 -1
  76. package/dist/use-motion-presets-BrkgL8lG.js +401 -0
  77. package/dist/use-motion-presets-BrkgL8lG.js.map +1 -0
  78. package/dist/widget-grid.js +3 -3
  79. package/package.json +4 -1
  80. package/dist/button-DWhICtIG.js.map +0 -1
  81. package/dist/components-Cq_PmN4y.js.map +0 -1
  82. package/dist/empty-state-C491qfzf.js.map +0 -1
  83. package/dist/rendra-provider-BRtk5Qa1.js.map +0 -1
  84. package/dist/tooltip-DsNlnBSY.js +0 -105
  85. package/dist/tooltip-DsNlnBSY.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"button-BzJgR2gH.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 { m } from 'motion/react'\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, 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 presets = useMotionPresets()\n const { native, rest } = splitNativeEvents(props)\n const attach = (el: HTMLButtonElement | null) => {\n assignRef(ref, el)\n if (!el) return\n const unbind = bindNativeEvents(el, native)\n return () => {\n unbind()\n assignRef(ref, null)\n }\n }\n return (\n <m.button\n ref={attach}\n whileTap={presets.press.whileTap}\n transition={presets.press.transition}\n {...rest}\n />\n )\n },\n)\n\nfunction assignRef<T>(ref: Ref<T> | undefined, value: T | null) {\n if (typeof ref === 'function') ref(value)\n else if (ref) ref.current = value\n}\n\nexport const Button = forwardRef<HTMLButtonElement, ButtonProps>(function Button(\n {\n className,\n variant,\n size,\n icon,\n iconRight,\n iconOnly = false,\n loading = false,\n fullWidth = false,\n asChild = false,\n disabled,\n type = 'button',\n children,\n ...props\n },\n ref,\n) {\n if (import.meta.env.DEV && iconOnly && !props['aria-label']) {\n console.warn('Button com iconOnly precisa de aria-label.')\n }\n const on = useMotionMode() === 'on'\n // Em `on` o toque é do whileTap: a classe `active:scale-97` sairia somando com ele, e o\n // `transform` sai da lista de transition (senão o CSS transiciona cada quadro que o Motion grava\n // e o toque chega atrasado). Com asChild nada disso: é só CSS, como em `off`.\n const classes = cn(\n buttonVariants({ variant, size, iconOnly, fullWidth }),\n on &&\n !asChild &&\n 'transition-[color,background-color,border-color,box-shadow] active:scale-100',\n className,\n )\n const code = resolveCatalogCode('Button', { variant: variant ?? 'primary' })\n\n if (asChild) {\n return (\n <Slot.Root ref={ref} data-rendra={code} className={classes} {...props}>\n {children}\n </Slot.Root>\n )\n }\n\n // Ícone dentro do botão segue o tamanho do próprio botão (sm/md -> sm, lg -> md, como o\n // [&_svg]:size-icon-* de buttonVariants já fazia com o Loader2 solto).\n const leading = loading ? <Spinner size={size === 'lg' ? 'md' : 'sm'} /> : icon\n if (on) {\n return (\n <MotionButton\n ref={ref}\n type={type}\n data-rendra={code}\n className={classes}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...props}\n >\n {leading}\n {iconOnly ? (loading ? null : children) : children}\n {!iconOnly && iconRight}\n </MotionButton>\n )\n }\n return (\n <button\n ref={ref}\n type={type}\n data-rendra={code}\n className={classes}\n disabled={disabled || loading}\n aria-busy={loading || undefined}\n {...props}\n >\n {leading}\n {iconOnly ? (loading ? null : children) : children}\n {!iconOnly && iconRight}\n </button>\n )\n})\n"],"mappings":"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"}
package/dist/chart.js CHANGED
@@ -1,11 +1,14 @@
1
- /*! Rendra Design System v2.3.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as cn } from "./cn-DDZQKUVV.js";
3
- import { p as resolveCatalogCode } from "./components-Cq_PmN4y.js";
4
- import { t as Button } from "./button-DWhICtIG.js";
5
- import { n as useBreakpoint } from "./use-breakpoint-C3ivDt2R.js";
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";
6
8
  import { ArrowDown, BarChart3, List } from "lucide-react";
7
- import { jsx, jsxs } from "react/jsx-runtime";
9
+ import { m } from "motion/react";
8
10
  import { useId, useRef, useState } from "react";
11
+ import { jsx, jsxs } from "react/jsx-runtime";
9
12
  import { Area, AreaChart, Bar, BarChart, CartesianGrid, Cell, ComposedChart, Legend, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts";
10
13
  //#region src/components/ui/chart.tsx
11
14
  var palette = [
@@ -75,6 +78,10 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
75
78
  const tone = (zones.find((z) => frac <= z.to) ?? zones[zones.length - 1])?.tone ?? "primary";
76
79
  const gradientId = useId();
77
80
  const [ex, ey] = point(frac, 74);
81
+ const on = useMotionMode() === "on";
82
+ const presets = useMotionPresets();
83
+ const reveal = useRevealScope();
84
+ const [sx, sy] = point(0, 74);
78
85
  return /* @__PURE__ */ jsxs("figure", {
79
86
  "data-rendra": resolveCatalogCode("Chart", { type: "gauge" }),
80
87
  className: cn("flex min-w-0 flex-col items-center gap-2", className),
@@ -125,15 +132,32 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
125
132
  strokeWidth: 20,
126
133
  strokeLinecap: "round"
127
134
  }),
128
- /* @__PURE__ */ jsx("line", {
135
+ on ? /* @__PURE__ */ jsx(m.line, {
136
+ x1: CX,
137
+ y1: CY,
138
+ initial: {
139
+ x2: sx,
140
+ y2: sy
141
+ },
142
+ animate: {
143
+ x2: ex,
144
+ y2: ey
145
+ },
146
+ transition: {
147
+ duration: reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw,
148
+ ease: presets.tokens.ease.out
149
+ },
150
+ stroke: "var(--rendra-foreground)",
151
+ strokeWidth: 4,
152
+ strokeLinecap: "round"
153
+ }) : /* @__PURE__ */ jsx("line", {
129
154
  x1: CX,
130
155
  y1: CY,
131
156
  x2: ex,
132
157
  y2: ey,
133
158
  stroke: "var(--rendra-foreground)",
134
159
  strokeWidth: 4,
135
- strokeLinecap: "round",
136
- className: "transition-all duration-700"
160
+ strokeLinecap: "round"
137
161
  }),
138
162
  /* @__PURE__ */ jsx("circle", {
139
163
  cx: CX,
@@ -195,6 +219,9 @@ function Gauge({ value, max, min = 0, target, zones = defaultZones, label, value
195
219
  });
196
220
  }
197
221
  function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
222
+ const on = useMotionMode() === "on";
223
+ const presets = useMotionPresets();
224
+ const Root = on ? m.figure : "figure";
198
225
  const n = stages.length;
199
226
  const top = stages[0]?.value || 1;
200
227
  const last = stages[n - 1];
@@ -207,10 +234,11 @@ function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
207
234
  return next && s.value > 0 ? next.value / s.value * 100 : null;
208
235
  });
209
236
  const worst = rates.reduce((w, r, i) => r !== null && (w === null || r < (rates[w] ?? Infinity)) ? i : w, null);
210
- return /* @__PURE__ */ jsxs("figure", {
237
+ return /* @__PURE__ */ jsxs(Root, {
211
238
  "data-rendra": resolveCatalogCode("Chart", { type: "funnel" }),
212
239
  className: cn("flex min-w-0 flex-col items-center", className),
213
240
  "aria-label": aria["aria-label"],
241
+ ...on ? presets.fade : {},
214
242
  children: [
215
243
  /* @__PURE__ */ jsx("ol", {
216
244
  className: "flex w-full flex-col items-center",
@@ -276,6 +304,19 @@ function Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }) {
276
304
  }
277
305
  function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFormatter = fmtDefault, height = "md", listThreshold = 8, className, ...aria }) {
278
306
  const { isMobile } = useBreakpoint();
307
+ const motion = useMotionMode();
308
+ const presets = useMotionPresets();
309
+ const reveal = useRevealScope();
310
+ const [drawn, setDrawn] = useState(false);
311
+ const ended = useRef(0);
312
+ const animated = type === "pie" ? 1 : series.length;
313
+ const draw = motion === "on" ? {
314
+ animationDuration: Math.round((reveal ? presets.tokens.reveal.draw : presets.tokens.duration.draw) * 1e3),
315
+ animationEasing: toCubicBezier(presets.tokens.ease.out),
316
+ onAnimationEnd: () => {
317
+ if (++ended.current >= animated) setDrawn(true);
318
+ }
319
+ } : {};
279
320
  const [asList, setAsList] = useState(false);
280
321
  const [animate, setAnimate] = useState(() => !window.matchMedia?.("(prefers-reduced-motion: reduce)").matches);
281
322
  const firstWidth = useRef(null);
@@ -360,7 +401,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
360
401
  0
361
402
  ],
362
403
  maxBarSize: 40,
363
- isAnimationActive: animate,
404
+ isAnimationActive: animate && !drawn,
405
+ ...draw,
364
406
  children: colorByCategory && data.map((_, j) => /* @__PURE__ */ jsx(Cell, { fill: colorVar(j + 1) }, j))
365
407
  }, s.key);
366
408
  let chart;
@@ -375,7 +417,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
375
417
  outerRadius: "85%",
376
418
  paddingAngle: 2,
377
419
  stroke: "var(--rendra-card)",
378
- isAnimationActive: animate,
420
+ isAnimationActive: animate && !drawn,
421
+ ...draw,
379
422
  children: data.map((_, i) => /* @__PURE__ */ jsx(Cell, { fill: colorVar(i + 1) }, i))
380
423
  }),
381
424
  tooltip,
@@ -414,7 +457,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
414
457
  strokeWidth: 0
415
458
  },
416
459
  activeDot: { r: 5 },
417
- isAnimationActive: animate
460
+ isAnimationActive: animate && !drawn,
461
+ ...draw
418
462
  }, s.key))
419
463
  ]
420
464
  });
@@ -449,7 +493,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
449
493
  stroke: colors[i],
450
494
  strokeWidth: 2,
451
495
  fill: `url(#area-${s.key})`,
452
- isAnimationActive: animate
496
+ isAnimationActive: animate && !drawn,
497
+ ...draw
453
498
  }, s.key))
454
499
  ]
455
500
  });
@@ -469,7 +514,8 @@ function Cartesian({ type, data, xKey, series, stacked, colorByCategory, valueFo
469
514
  strokeWidth: 2,
470
515
  dot: false,
471
516
  activeDot: { r: 4 },
472
- isAnimationActive: animate
517
+ isAnimationActive: animate && !drawn,
518
+ ...draw
473
519
  }, s.key))
474
520
  ]
475
521
  });
package/dist/chart.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"chart.js","names":[],"sources":["../src/components/ui/chart.tsx"],"sourcesContent":["import { ArrowDown, BarChart3, List as ListIcon } from 'lucide-react'\nimport { useId, useRef, useState, type CSSProperties } from 'react'\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n ComposedChart,\n Legend,\n Line,\n LineChart,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip,\n XAxis,\n YAxis,\n} from 'recharts'\nimport { resolveCatalogCode } from '@/catalog/components'\nimport { Button } from '@/components/ui/button'\nimport { useBreakpoint } from '@/hooks/use-breakpoint'\nimport { cn } from '@/lib/cn'\n\n/*\n * Gráfico único do sistema. O tipo é a prop type, nunca um componente novo:\n * line, area, bar, pie séries sobre categorias (Recharts)\n * combo barras e linhas juntas (ex.: receita em barras e meta em linha)\n * gauge velocímetro de meta: valor, máximo, meta e faixas de cor\n * funnel funil de etapas com a taxa de conversão entre elas\n * Cores sempre do template (--chart-1 a --chart-5). Mobile: legenda abaixo, eixos\n * simplificados, lista de valores quando há muitos pontos; o funil empilha rótulo e barra.\n */\n\nexport interface ChartSeries {\n /** Chave no objeto de dados. */\n key: string\n label: string\n /** Índice da cor do template (1 a 5). Padrão: na ordem das séries. */\n color?: 1 | 2 | 3 | 4 | 5\n /** No type=\"combo\": desenha a série como barra (padrão) ou linha. */\n kind?: 'bar' | 'line'\n}\n\ninterface BaseProps {\n valueFormatter?: (n: number) => string\n /** Altura: sm (12rem), md (16rem) ou fill (a altura do contêiner, como num widget). */\n height?: 'sm' | 'md' | 'fill'\n 'aria-label': string\n className?: string\n}\n\nexport interface CartesianChartProps extends BaseProps {\n type: 'line' | 'bar' | 'area' | 'pie' | 'combo'\n data: Record<string, string | number>[]\n /** Chave da categoria (eixo X ou fatias da pizza). */\n xKey: string\n series: ChartSeries[]\n /** Barras empilhadas (bar e combo). */\n stacked?: boolean\n /** Barra de uma série só com uma cor por categoria. */\n colorByCategory?: boolean\n /** Acima deste número de pontos, o mobile oferece ver como lista. */\n listThreshold?: number\n}\n\nexport interface GaugeZone {\n /** Fim da faixa, de 0 a 1 do máximo. */\n to: number\n tone: 'error' | 'warning' | 'success' | 'primary' | 'neutral'\n}\n\nexport interface GaugeChartProps extends BaseProps {\n type: 'gauge'\n value: number\n max: number\n min?: number\n /** Meta: marcada no arco e usada no percentual atingido. */\n target?: number\n /** Faixas de cor do arco. Padrão: vermelho até 60%, amarelo até 90%, verde até o fim. */\n zones?: GaugeZone[]\n /** Texto abaixo do valor (ex.: \"Receita do mês\"). */\n label?: string\n}\n\nexport interface FunnelStage {\n label: string\n value: number\n}\n\nexport interface FunnelChartProps extends BaseProps {\n type: 'funnel'\n stages: FunnelStage[]\n}\n\nexport type ChartProps = CartesianChartProps | GaugeChartProps | FunnelChartProps\n\n// Lê as variáveis do tema direto (as --color-* do Tailwind são inline e não existem no CSS).\nconst palette = [\n '--rendra-chart-1',\n '--rendra-chart-2',\n '--rendra-chart-3',\n '--rendra-chart-4',\n '--rendra-chart-5',\n '--rendra-ring',\n]\nconst colorVar = (i: number) => `var(${palette[(i - 1) % palette.length]})`\nconst fmtDefault = (n: number) => n.toLocaleString('pt-BR')\nconst pct = (n: number) =>\n `${n.toLocaleString('pt-BR', { minimumFractionDigits: 1, maximumFractionDigits: 1 })}%`\n\nexport function Chart(props: ChartProps) {\n if (props.type === 'gauge') return <Gauge {...props} />\n if (props.type === 'funnel') return <Funnel {...props} />\n return <Cartesian {...props} />\n}\n\n/* ================================================================ velocímetro */\n\nconst defaultZones: GaugeZone[] = [\n { to: 0.6, tone: 'error' },\n { to: 0.9, tone: 'warning' },\n { to: 1, tone: 'success' },\n]\n\nconst zoneBadge: Record<GaugeZone['tone'], string> = {\n error: 'bg-destructive-soft text-destructive-soft-foreground',\n warning: 'bg-warning-soft text-warning-soft-foreground',\n success: 'bg-success-soft text-success-soft-foreground',\n primary: 'bg-primary-soft text-primary-soft-foreground',\n neutral: 'bg-muted text-foreground',\n}\nconst zoneText: Record<GaugeZone['tone'], string> = {\n error: 'Abaixo da meta',\n warning: 'Atenção',\n success: 'No verde',\n primary: 'Em andamento',\n neutral: 'Em andamento',\n}\n\n// Meio círculo (180°), da esquerda para a direita por cima, centro em (120, 120), raio 100.\nconst CX = 120\nconst CY = 120\nconst R = 100\nconst point = (frac: number, r = R) => {\n const a = Math.PI * (1 - frac)\n return [CX + r * Math.cos(a), CY - r * Math.sin(a)] as const\n}\nconst arc = (from: number, to: number, r = R) => {\n const [x1, y1] = point(from, r)\n const [x2, y2] = point(to, r)\n return `M ${x1.toFixed(2)} ${y1.toFixed(2)} A ${r} ${r} 0 0 1 ${x2.toFixed(2)} ${y2.toFixed(2)}`\n}\n\nfunction Gauge({\n value,\n max,\n min = 0,\n target,\n zones = defaultZones,\n label,\n valueFormatter = fmtDefault,\n className,\n ...aria\n}: GaugeChartProps) {\n const range = Math.max(1, max - min)\n const clamp = (n: number) => Math.min(1, Math.max(0, n))\n // Com meta, o ponteiro mostra o percentual da meta (100% = meta batida); sem meta, do máximo.\n const reached = target ? (value / target) * 100 : null\n const frac = reached !== null ? clamp(reached / 100) : clamp((value - min) / range)\n const zone = zones.find((z) => frac <= z.to) ?? zones[zones.length - 1]\n const tone = zone?.tone ?? 'primary'\n const gradientId = useId()\n const [ex, ey] = point(frac, R - 26)\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type: 'gauge' })}\n className={cn('flex min-w-0 flex-col items-center gap-2', className)}\n aria-label={aria['aria-label']}\n >\n <div\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={100}\n aria-valuenow={Math.round(frac * 100)}\n aria-valuetext={`${reached !== null ? `${pct(reached)} da meta` : valueFormatter(value)}, ${zoneText[tone]}`}\n aria-label={aria['aria-label']}\n className=\"w-full max-w-xs\"\n >\n <svg viewBox=\"0 0 240 140\" className=\"w-full\" aria-hidden>\n <defs>\n {/* Degradê contínuo: vermelho, laranja, amarelo e verde */}\n <linearGradient id={gradientId} x1=\"0\" y1=\"0\" x2=\"1\" y2=\"0\">\n <stop offset=\"0%\" stopColor=\"var(--rendra-meter-low)\" />\n <stop\n offset=\"38%\"\n stopColor=\"color-mix(in oklab, var(--rendra-meter-low), var(--rendra-meter-mid))\"\n />\n <stop offset=\"62%\" stopColor=\"var(--rendra-meter-mid)\" />\n <stop offset=\"100%\" stopColor=\"var(--rendra-meter-high)\" />\n </linearGradient>\n </defs>\n <path\n d={arc(0, 1)}\n fill=\"none\"\n stroke={`url(#${gradientId})`}\n strokeWidth={20}\n strokeLinecap=\"round\"\n />\n {/* Ponteiro fino, do centro até perto do arco */}\n <line\n x1={CX}\n y1={CY}\n x2={ex}\n y2={ey}\n stroke=\"var(--rendra-foreground)\"\n strokeWidth={4}\n strokeLinecap=\"round\"\n className=\"transition-all duration-700\"\n />\n <circle cx={CX} cy={CY} r={9} fill=\"var(--rendra-foreground)\" />\n <circle cx={CX} cy={CY} r={4} fill=\"var(--rendra-card)\" />\n <text\n x={point(0)[0]}\n y={CY + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--rendra-muted-foreground)\"\n >\n 0%\n </text>\n <text\n x={point(1)[0]}\n y={CY + 18}\n textAnchor=\"middle\"\n fontSize={11}\n fill=\"var(--rendra-muted-foreground)\"\n >\n 100%\n </text>\n </svg>\n </div>\n <div className=\"flex flex-col items-center gap-1 text-center\">\n <span className=\"text-3xl font-semibold tabular-nums\">\n {reached !== null ? `${Math.round(reached)}%` : valueFormatter(value)}\n </span>\n {label && <span className=\"text-sm font-medium\">{label}</span>}\n {target !== undefined && (\n <span className=\"flex flex-col text-sm text-muted-foreground tabular-nums\">\n <span>Meta: {valueFormatter(target)}</span>\n <span>Realizado: {valueFormatter(value)}</span>\n </span>\n )}\n <span className={cn('mt-1 rounded-full px-3 py-1 text-xs font-semibold', zoneBadge[tone])}>\n {zoneText[tone]}\n </span>\n </div>\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n\n/* ================================================================ funil */\n\nfunction Funnel({ stages, valueFormatter = fmtDefault, className, ...aria }: FunnelChartProps) {\n const n = stages.length\n const top = stages[0]?.value || 1\n const last = stages[n - 1]\n // Afunila por igual, de 100% a 46% da largura, como um funil de verdade.\n const width = (i: number) => 1 - (i / Math.max(1, n)) * 0.54\n // Da primária ao sucesso, escurecido com o tom da sidebar: o texto claro passa AA.\n const fill = (i: number) => {\n const p = n > 1 ? Math.round(100 - (i / (n - 1)) * 100) : 100\n return `color-mix(in oklab, color-mix(in oklab, var(--rendra-primary) ${p}%, var(--rendra-success)) 58%, var(--rendra-sidebar))`\n }\n const rates = stages.map((s, i) => {\n const next = stages[i + 1]\n return next && s.value > 0 ? (next.value / s.value) * 100 : null\n })\n const worst = rates.reduce<number | null>(\n (w, r, i) => (r !== null && (w === null || r < (rates[w] ?? Infinity)) ? i : w),\n null,\n )\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type: 'funnel' })}\n className={cn('flex min-w-0 flex-col items-center', className)}\n aria-label={aria['aria-label']}\n >\n <ol className=\"flex w-full flex-col items-center\">\n {stages.map((s, i) => {\n const rate = rates[i]\n return (\n <li key={s.label} className=\"flex w-full flex-col items-center\">\n <div\n className=\"flex h-16 w-full flex-col items-center justify-center bg-fill text-sidebar-foreground clip-trapezoid\"\n style={\n {\n '--top': width(i).toFixed(4),\n '--bottom': width(i + 1).toFixed(4),\n '--fill': fill(i),\n } as CSSProperties\n }\n >\n <span className=\"text-lg leading-tight font-semibold tabular-nums\">\n {valueFormatter(s.value)}\n </span>\n <span className=\"text-xs\">{s.label}</span>\n </div>\n {rate != null && (\n <p className=\"flex items-center gap-1 py-1 text-xs text-muted-foreground\">\n <ArrowDown className=\"size-3\" aria-hidden />\n <span className=\"font-semibold text-foreground tabular-nums\">{pct(rate)}</span>\n <span>para a próxima etapa</span>\n {i === worst && n > 2 && (\n <span className=\"font-semibold text-destructive-soft-foreground\">\n · Maior queda\n </span>\n )}\n </p>\n )}\n </li>\n )\n })}\n </ol>\n {n > 1 && last && (\n <p className=\"flex items-center gap-1 pt-2 text-sm text-muted-foreground\">\n <ArrowDown className=\"size-icon-sm\" aria-hidden />\n <span className=\"font-semibold text-foreground tabular-nums\">\n {pct((last.value / top) * 100)}\n </span>\n de conversão total do funil\n </p>\n )}\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n\n/* ================================================================ séries (Recharts) */\n\nfunction Cartesian({\n type,\n data,\n xKey,\n series,\n stacked,\n colorByCategory,\n valueFormatter = fmtDefault,\n height = 'md',\n listThreshold = 8,\n className,\n ...aria\n}: CartesianChartProps) {\n const { isMobile } = useBreakpoint()\n const [asList, setAsList] = useState(false)\n // Anima só a entrada. Se a largura muda depois (barra de rolagem que aparece, janela\n // redimensionada), o gráfico vai direto à nova forma, sem desenhar fora do card no meio do\n // caminho. Com \"reduzir movimento\" no sistema, não anima.\n const [animate, setAnimate] = useState(\n () => !window.matchMedia?.('(prefers-reduced-motion: reduce)').matches,\n )\n const firstWidth = useRef<number | null>(null)\n const onResize = (width: number) => {\n if (firstWidth.current === null) firstWidth.current = width\n else if (Math.abs(width - firstWidth.current) > 1) setAnimate(false)\n }\n const canList = isMobile && data.length > listThreshold\n const showList = canList && asList\n const colors = series.map((s, i) => colorVar(s.color ?? i + 1))\n\n const axis = {\n stroke: 'var(--rendra-muted-foreground)',\n fontSize: 12,\n tickLine: false,\n axisLine: false,\n }\n const tooltip = (\n <Tooltip\n cursor={{ fill: 'var(--rendra-muted)', stroke: 'var(--rendra-border)' }}\n formatter={(v) => valueFormatter(Number(v))}\n contentStyle={{\n background: 'var(--rendra-popover)',\n border: '1px solid var(--rendra-border)',\n borderRadius: 'var(--rendra-shape-control)',\n color: 'var(--rendra-popover-foreground)',\n fontSize: 12,\n }}\n />\n )\n const legend = (\n <Legend\n verticalAlign=\"bottom\"\n iconType=\"circle\"\n iconSize={8}\n wrapperStyle={{ fontSize: 12, paddingTop: 12 }}\n />\n )\n const common = { data, margin: { top: 8, right: 8, left: isMobile ? -16 : 0, bottom: 0 } }\n const xAxis = (\n <XAxis dataKey={xKey} {...axis} interval=\"preserveStartEnd\" minTickGap={isMobile ? 24 : 12} />\n )\n const yAxis = (\n <YAxis\n {...axis}\n width={isMobile ? 72 : 80}\n tickCount={isMobile ? 3 : 5}\n // Mostra todas as marcas calculadas (o padrão do Recharts some com algumas).\n interval={0}\n allowDecimals={false}\n tickFormatter={(v) => valueFormatter(Number(v))}\n />\n )\n const grid = (\n <CartesianGrid vertical={false} stroke=\"var(--rendra-border)\" strokeDasharray=\"3 3\" />\n )\n const bar = (s: ChartSeries, i: number, lastBar: boolean) => (\n <Bar\n key={s.key}\n dataKey={s.key}\n name={s.label}\n fill={colors[i]}\n stackId={stacked ? 'pilha' : undefined}\n // Empilhadas: só a barra de cima tem os cantos arredondados.\n radius={!stacked || lastBar ? [4, 4, 0, 0] : [0, 0, 0, 0]}\n maxBarSize={40}\n isAnimationActive={animate}\n >\n {colorByCategory && data.map((_, j) => <Cell key={j} fill={colorVar(j + 1)} />)}\n </Bar>\n )\n\n let chart\n if (type === 'pie') {\n const s = series[0]\n chart = (\n <PieChart>\n <Pie\n data={data}\n dataKey={s?.key ?? ''}\n nameKey={xKey}\n innerRadius=\"55%\"\n outerRadius=\"85%\"\n paddingAngle={2}\n stroke=\"var(--rendra-card)\"\n isAnimationActive={animate}\n >\n {data.map((_, i) => (\n <Cell key={i} fill={colorVar(i + 1)} />\n ))}\n </Pie>\n {tooltip}\n {legend}\n </PieChart>\n )\n } else if (type === 'bar') {\n chart = (\n <BarChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => bar(s, i, i === series.length - 1))}\n </BarChart>\n )\n } else if (type === 'combo') {\n const bars = series.filter((s) => (s.kind ?? 'bar') === 'bar')\n chart = (\n <ComposedChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {legend}\n {series.map((s, i) =>\n (s.kind ?? 'bar') === 'bar' ? (\n bar(s, i, s.key === bars[bars.length - 1]?.key)\n ) : (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n dot={{ r: 3, fill: colors[i], strokeWidth: 0 }}\n activeDot={{ r: 5 }}\n isAnimationActive={animate}\n />\n ),\n )}\n </ComposedChart>\n )\n } else if (type === 'area') {\n chart = (\n <AreaChart {...common}>\n <defs>\n {series.map((s, i) => (\n <linearGradient key={s.key} id={`area-${s.key}`} x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop offset=\"0%\" stopColor={colors[i]} stopOpacity={0.3} />\n <stop offset=\"100%\" stopColor={colors[i]} stopOpacity={0} />\n </linearGradient>\n ))}\n </defs>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => (\n <Area\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n fill={`url(#area-${s.key})`}\n isAnimationActive={animate}\n />\n ))}\n </AreaChart>\n )\n } else {\n chart = (\n <LineChart {...common}>\n {grid}\n {xAxis}\n {yAxis}\n {tooltip}\n {series.length > 1 && legend}\n {series.map((s, i) => (\n <Line\n key={s.key}\n type=\"monotone\"\n dataKey={s.key}\n name={s.label}\n stroke={colors[i]}\n strokeWidth={2}\n dot={false}\n activeDot={{ r: 4 }}\n isAnimationActive={animate}\n />\n ))}\n </LineChart>\n )\n }\n\n return (\n <figure\n data-rendra={resolveCatalogCode('Chart', { type })}\n className={cn('flex min-w-0 flex-col gap-3', height === 'fill' && 'h-full', className)}\n aria-label={aria['aria-label']}\n >\n {canList && (\n <Button\n variant=\"ghost\"\n size=\"sm\"\n className=\"self-end\"\n icon={asList ? <BarChart3 /> : <ListIcon />}\n onClick={() => setAsList((v) => !v)}\n >\n {asList ? 'Ver gráfico' : 'Ver como lista'}\n </Button>\n )}\n {showList ? (\n <ul className=\"flex flex-col divide-y\">\n {data.map((d) => (\n <li\n key={String(d[xKey])}\n className=\"flex items-center justify-between gap-3 py-2 text-sm\"\n >\n <span className=\"text-muted-foreground\">{String(d[xKey])}</span>\n <span className=\"flex gap-3 font-medium tabular-nums\">\n {series.map((s) => (\n <span key={s.key}>{valueFormatter(Number(d[s.key]))}</span>\n ))}\n </span>\n </li>\n ))}\n </ul>\n ) : (\n <div\n className={cn(\n 'w-full',\n height === 'fill'\n ? 'min-h-chart-sm flex-1'\n : height === 'sm'\n ? 'h-chart-sm'\n : 'h-chart-md',\n )}\n >\n <ResponsiveContainer width=\"100%\" height=\"100%\" onResize={onResize}>\n {chart}\n </ResponsiveContainer>\n </div>\n )}\n <figcaption className=\"sr-only\">{aria['aria-label']}</figcaption>\n </figure>\n )\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"chart.js","names":[],"sources":["../src/components/ui/chart.tsx"],"sourcesContent":["import { ArrowDown, BarChart3, List as ListIcon } from 'lucide-react'\nimport { 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":"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"}
@@ -1,4 +1,4 @@
1
- /*! Rendra Design System v2.3.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
1
+ /*! Rendra Design System v2.5.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
@@ -39,6 +39,8 @@ var LINE_RULES = [
39
39
  skipLine: isCommentLine
40
40
  }
41
41
  ];
42
+ /** Identificadores das sete regras genéricas (as seis de linha e `fora-da-escala`). */
43
+ var IDS_REGRAS_GENERICAS = [...LINE_RULES.map((r) => r.id), "fora-da-escala"];
42
44
  var ALLOWED_STEPS = /* @__PURE__ */ new Set([
43
45
  "0",
44
46
  "1",
@@ -84,42 +86,90 @@ function auditLines(text) {
84
86
  });
85
87
  return found;
86
88
  }
87
- var SKIP_DIR_NAMES = /* @__PURE__ */ new Set([
89
+ var NOMES_PULADOS = /* @__PURE__ */ new Set([
88
90
  "node_modules",
89
91
  "dist",
90
92
  "build",
91
93
  ".git",
92
94
  ".storybook-static"
93
95
  ]);
94
- function walk(dir) {
96
+ /** `./src/brand/` e `src\\brand` viram `src/brand`; o que sobrar vazio (`.`, `./`) é descartado. */
97
+ function normalizarPrefixo(prefixo) {
98
+ let p = prefixo.replace(/\\/g, "/");
99
+ while (p.startsWith("./")) p = p.slice(2);
100
+ p = p.replace(/\/+$/, "");
101
+ return p === "." ? "" : p;
102
+ }
103
+ /**
104
+ * `file` (relativo a `cwd`, com `/`) está numa das pastas de `prefixos`? Prefixo de pasta:
105
+ * `src/brand` casa `src/brand` e `src/brand/x.ts`, nunca `src/brand-novo/x.ts` nem, com
106
+ * `src/br`, `src/brand`.
107
+ */
108
+ function casaPrefixo(file, prefixos) {
109
+ return prefixos.some((bruto) => {
110
+ const p = normalizarPrefixo(bruto);
111
+ return p !== "" && (file === p || file.startsWith(p + "/"));
112
+ });
113
+ }
114
+ /**
115
+ * Lista os arquivos sob `dir`. `pular(nome, caminho)` decide por pasta ou arquivo se a entrada
116
+ * fica de fora (a pasta pulada não é aberta). Ordem: a do sistema de arquivos, como sempre.
117
+ */
118
+ function percorrer(dir, pular) {
95
119
  return readdirSync(dir).flatMap((name) => {
96
- if (SKIP_DIR_NAMES.has(name)) return [];
97
120
  const full = join(dir, name);
98
- return statSync(full).isDirectory() ? walk(full) : [full];
121
+ if (pular(name, full)) return [];
122
+ return statSync(full).isDirectory() ? percorrer(full, pular) : [full];
99
123
  });
100
124
  }
125
+ var EXTENSAO_VARRIDA = /\.(tsx?|jsx?|css)$/;
126
+ /**
127
+ * Arquivos que `auditar` (e `auditarTudo`, no modo `cli`) examinam: `.ts`/`.tsx`/`.js`/`.jsx`/
128
+ * `.css`, sem teste nem story, sem `NOMES_PULADOS` e sem as pastas de `ignorar`. Caminhos
129
+ * absolutos, na ordem do sistema de arquivos, sem repetição. Sem `pastas`, a raiz é
130
+ * `cwd/src` (quando existe) ou `cwd`; com `pastas`, são exatamente essas, relativas a `cwd`.
131
+ */
132
+ function coletarArquivos(cwd, opcoes = {}) {
133
+ const ignorar = opcoes.ignorar ?? [];
134
+ const raizes = opcoes.pastas && opcoes.pastas.length > 0 ? opcoes.pastas.map((p) => join(cwd, p)) : [existsSync(join(cwd, "src")) ? join(cwd, "src") : cwd];
135
+ const pular = (nome, caminho) => NOMES_PULADOS.has(nome) || casaPrefixo(relativoA(cwd, caminho), ignorar);
136
+ const arquivos = /* @__PURE__ */ new Set();
137
+ for (const raiz of raizes) {
138
+ if (!existsSync(raiz)) continue;
139
+ for (const f of percorrer(raiz, pular)) arquivos.add(f);
140
+ }
141
+ return [...arquivos].filter((f) => EXTENSAO_VARRIDA.test(f) && !f.includes(".test.") && !f.includes(".stories."));
142
+ }
143
+ /** Caminho relativo a `base`, com `/` em qualquer sistema. */
144
+ function relativoA(base, caminho) {
145
+ return relative(base, caminho).split(sep).join("/");
146
+ }
101
147
  /**
102
148
  * Varre `cwd` (ou `cwd/src`, quando existir: a convenção deste boilerplate e da maioria dos
103
149
  * projetos React) por arquivos `.ts`/`.tsx`/`.js`/`.jsx`/`.css`, ignorando teste e story, e
104
150
  * devolve toda violação das sete regras genéricas. É o que `rendra auditar [cwd]` chama; não
105
151
  * imprime nada e não lança (a CLI decide o formato de saída e o código de saída).
152
+ *
153
+ * Com `opcoes.pastas`, varre exatamente essas pastas (relativas a `cwd`, sem o atalho para
154
+ * `src`); com `opcoes.ignorar`, pula as pastas dadas; `opcoes.regras` desliga regras por id.
155
+ * `file` sai sempre relativo a `cwd`, com `/`, ordenado por arquivo e linha.
106
156
  */
107
- function auditar(cwd) {
108
- const root = existsSync(join(cwd, "src")) ? join(cwd, "src") : cwd;
109
- if (!existsSync(root)) return [];
110
- const files = walk(root).filter((f) => /\.(tsx?|jsx?|css)$/.test(f) && !f.includes(".test.") && !f.includes(".stories."));
157
+ function auditar(cwd, opcoes = {}) {
111
158
  const problems = [];
112
- for (const file of files) {
113
- const rel = relative(cwd, file).split(sep).join("/");
159
+ for (const file of coletarArquivos(cwd, opcoes)) {
160
+ const rel = relativoA(cwd, file);
114
161
  const text = readFileSync(file, "utf8");
115
- for (const violation of auditLines(text)) problems.push({
116
- file: rel,
117
- ...violation
118
- });
162
+ for (const violation of auditLines(text)) {
163
+ if (opcoes.regras?.[violation.rule] === false) continue;
164
+ problems.push({
165
+ file: rel,
166
+ ...violation
167
+ });
168
+ }
119
169
  }
120
170
  return problems.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line);
121
171
  }
122
172
  //#endregion
123
- export { LINE_RULES, auditLines, auditar };
173
+ export { EXTENSAO_VARRIDA, IDS_REGRAS_GENERICAS, LINE_RULES, NOMES_PULADOS, auditLines, auditar, casaPrefixo, coletarArquivos, percorrer, relativoA };
124
174
 
125
175
  //# sourceMappingURL=auditar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"auditar.js","names":[],"sources":["../../src/cli/auditar.ts"],"sourcesContent":["/*\n * Regras genéricas de design (DESIGN_RULES.md) que não são específicas deste repositório:\n * qualquer projeto que use o Rendra pode ter cor fixa, valor arbitrário do Tailwind, estilo\n * inline, fonte fixa, 100vh ou raio fixo. Módulo único: `scripts/check-design-rules.mjs` (as\n * regras deste repositório, mais as específicas: var-inline, svg-marca, classe-dinamica,\n * texto-estreito, primaria-como-texto, componente-duplicado, css-fora-do-lugar, botao-solto)\n * e `rendra auditar` (a CLI publicada, para qualquer projeto de destino) importam daqui, para\n * nunca divergir.\n *\n * Duas camadas:\n * - `auditLines(text)`: pura, sem acesso a arquivo. Recebe o texto de UM arquivo e devolve\n * as violações das sete regras (linha, regra, mensagem, trecho). `check-design-rules.mjs`\n * chama isso por arquivo, porque ele já tem sua própria varredura de diretório com a\n * exceção `src/brand`/`src/styles` (regras próprias do tema deste repositório, que não\n * fazem sentido num projeto de destino qualquer).\n * - `auditar(cwd)`: varre `cwd` (ou `cwd/src`, quando existir) sozinha, sem exceção de\n * diretório, e devolve a lista completa. É o que `rendra auditar` chama.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { join, relative, sep } from 'node:path'\n\nexport interface AuditViolation {\n line: number\n rule: string\n message: string\n source: string\n}\n\nexport interface FileAuditViolation extends AuditViolation {\n file: string\n}\n\ninterface LineRule {\n id: string\n test: RegExp\n message: string\n skipLine?: (line: string) => boolean\n}\n\nconst isCommentLine = (line: string) => /^\\s*(\\/\\/|\\*|\\/\\*)/.test(line)\n\n/** As seis regras genéricas mais simples: um regex por linha. */\nexport const LINE_RULES: LineRule[] = [\n {\n id: 'cor-fixa',\n test: /#[0-9a-fA-F]{3,8}\\b|\\b(?:rgba?|hsla?|oklch|oklab)\\(/g,\n message: 'Cor fixa em componente. Use um token semântico (bg-primary, text-muted-foreground).',\n skipLine: (line) => isCommentLine(line) || /&#\\d+;/.test(line),\n },\n {\n id: 'valor-arbitrario',\n // Valor arbitrário (p-[13px]). Variantes de estado (data-[state=open]:) terminam em ':' e são\n // permitidas; transition-[...] só lista propriedades, também permitido.\n test: /(?<![\\w-])(?:[a-z]+:)*-?(?!transition-)[a-z][a-z-]*-\\[[^\\]\\s]+\\](?!(?:\\/[\\w-]+)?:)/g,\n message: 'Valor arbitrário do Tailwind. Use a escala de tokens ou crie um token nomeado.',\n skipLine: isCommentLine,\n },\n {\n id: 'estilo-inline',\n // Qualquer style= que não seja só variável CSS (--x) é estilo inline.\n test: /style=\\{(?![^}]*['\"]--)(?!\\s*$)/g,\n message: 'Estilo inline. Use classes; para valor dinâmico use variável CSS (--x).',\n },\n {\n id: 'fonte-fixa',\n // Só em contexto de fonte (declaração ou nome entre aspas numa pilha de fontes): a\n // palavra \"Inter\" num texto da interface não é violação.\n test: /font-family|fontFamily|['\"](?:Poppins|Inter|Roboto|Arial|Helvetica)(?:['\",]| sans| serif)/g,\n message:\n 'Nome de fonte fixo. A fonte vem da variável de tema da marca (ex.: --rendra-brand-font).',\n },\n {\n id: '100vh',\n test: /100vh|\\bh-screen\\b|\\bmin-h-screen\\b/g,\n message: 'Use 100dvh (h-dvh / min-h-dvh), nunca 100vh.',\n },\n {\n id: 'raio-fixo',\n // border-radius fixo em CSS ou em estilo inline (borderRadius): nunca em px/rem/% direto,\n // sempre por um token de raio por papel (rounded-control, rounded-surface, rounded-item...)\n // ou uma variável de tema (--rendra-shape-*, --rendra-radius).\n test: /border-radius\\s*:\\s*(?!var\\()\\d|borderRadius\\s*:\\s*(?!['\"`]?var\\()['\"`]?\\d/g,\n message:\n 'Raio fixo. Use um token de raio por papel (rounded-control, rounded-surface, rounded-item...) ou uma variável de tema.',\n skipLine: isCommentLine,\n },\n]\n\n// Degraus fora da escala não geram CSS (o Tailwind ignora em silêncio e o layout quebra).\nconst ALLOWED_STEPS = new Set(['0', '1', '2', '3', '4', '6', '8', '12', '16', '24'])\nconst STEP_CLASS =\n /(?<![\\w-])-?(?:p[xytrbl]?|m[xytrbl]?|gap(?:-[xy])?|w|h|size|min-[wh]|max-[wh]|inset(?:-[xy])?|top|left|right|bottom|space-[xy]|translate-[xy]|scroll-[mp][xytrbl]?)-(\\d+(?:\\.\\d+)?)(?![\\d/.\\w-])/g\n\n/** Sétima regra (\"fora-da-escala\"): olha todos os degraus da linha, não só o primeiro. */\nfunction checkForaDaEscala(line: string): boolean {\n for (const m of line.matchAll(STEP_CLASS)) if (!ALLOWED_STEPS.has(m[1]!)) return true\n return false\n}\n\n/**\n * Varre o texto de um arquivo (linha a linha) contra as sete regras genéricas de\n * DESIGN_RULES.md. Pura: não lê arquivo, não decide o que é escopo do projeto (isso é de quem\n * chama, como `auditar` abaixo ou o `walk`/`ALLOWED_DIRS` de check-design-rules.mjs).\n */\nexport function auditLines(text: string): AuditViolation[] {\n const found: AuditViolation[] = []\n const lines = text.split('\\n')\n lines.forEach((line, i) => {\n for (const rule of LINE_RULES) {\n if (rule.skipLine?.(line)) continue\n rule.test.lastIndex = 0\n if (rule.test.test(line)) {\n found.push({ line: i + 1, rule: rule.id, message: rule.message, source: line.trim() })\n }\n }\n if (!isCommentLine(line) && checkForaDaEscala(line)) {\n found.push({\n line: i + 1,\n rule: 'fora-da-escala',\n message:\n 'Degrau fora da escala (permitidos: 0, 1, 2, 3, 4, 6, 8, 12, 16, 24). Use a escala ou um token nomeado.',\n source: line.trim(),\n })\n }\n })\n return found\n}\n\nconst SKIP_DIR_NAMES = new Set(['node_modules', 'dist', 'build', '.git', '.storybook-static'])\n\nfunction walk(dir: string): string[] {\n return readdirSync(dir).flatMap((name) => {\n if (SKIP_DIR_NAMES.has(name)) return []\n const full = join(dir, name)\n return statSync(full).isDirectory() ? walk(full) : [full]\n })\n}\n\n/**\n * Varre `cwd` (ou `cwd/src`, quando existir: a convenção deste boilerplate e da maioria dos\n * projetos React) por arquivos `.ts`/`.tsx`/`.js`/`.jsx`/`.css`, ignorando teste e story, e\n * devolve toda violação das sete regras genéricas. É o que `rendra auditar [cwd]` chama; não\n * imprime nada e não lança (a CLI decide o formato de saída e o código de saída).\n */\nexport function auditar(cwd: string): FileAuditViolation[] {\n const root = existsSync(join(cwd, 'src')) ? join(cwd, 'src') : cwd\n if (!existsSync(root)) return []\n const files = walk(root).filter(\n (f) => /\\.(tsx?|jsx?|css)$/.test(f) && !f.includes('.test.') && !f.includes('.stories.'),\n )\n const problems: FileAuditViolation[] = []\n for (const file of files) {\n const rel = relative(cwd, file).split(sep).join('/')\n const text = readFileSync(file, 'utf8')\n for (const violation of auditLines(text)) problems.push({ file: rel, ...violation })\n }\n return problems.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line)\n}\n"],"mappings":";;;;AAuCA,IAAM,iBAAiB,SAAiB,qBAAqB,KAAK,IAAI;;AAGtE,IAAa,aAAyB;CACpC;EACE,IAAI;EACJ,MAAM;EACN,SAAS;EACT,WAAW,SAAS,cAAc,IAAI,KAAK,SAAS,KAAK,IAAI;CAC/D;CACA;EACE,IAAI;EAGJ,MAAM;EACN,SAAS;EACT,UAAU;CACZ;CACA;EACE,IAAI;EAEJ,MAAM;EACN,SAAS;CACX;CACA;EACE,IAAI;EAGJ,MAAM;EACN,SACE;CACJ;CACA;EACE,IAAI;EACJ,MAAM;EACN,SAAS;CACX;CACA;EACE,IAAI;EAIJ,MAAM;EACN,SACE;EACF,UAAU;CACZ;AACF;AAGA,IAAM,gCAAgB,IAAI,IAAI;CAAC;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAK;CAAM;CAAM;AAAI,CAAC;AACnF,IAAM,aACJ;;AAGF,SAAS,kBAAkB,MAAuB;CAChD,KAAK,MAAM,KAAK,KAAK,SAAS,UAAU,GAAG,IAAI,CAAC,cAAc,IAAI,EAAE,EAAG,GAAG,OAAO;CACjF,OAAO;AACT;;;;;;AAOA,SAAgB,WAAW,MAAgC;CACzD,MAAM,QAA0B,CAAC;CAEjC,KADmB,MAAM,IACzB,CAAA,CAAM,SAAS,MAAM,MAAM;EACzB,KAAK,MAAM,QAAQ,YAAY;GAC7B,IAAI,KAAK,WAAW,IAAI,GAAG;GAC3B,KAAK,KAAK,YAAY;GACtB,IAAI,KAAK,KAAK,KAAK,IAAI,GACrB,MAAM,KAAK;IAAE,MAAM,IAAI;IAAG,MAAM,KAAK;IAAI,SAAS,KAAK;IAAS,QAAQ,KAAK,KAAK;GAAE,CAAC;EAEzF;EACA,IAAI,CAAC,cAAc,IAAI,KAAK,kBAAkB,IAAI,GAChD,MAAM,KAAK;GACT,MAAM,IAAI;GACV,MAAM;GACN,SACE;GACF,QAAQ,KAAK,KAAK;EACpB,CAAC;CAEL,CAAC;CACD,OAAO;AACT;AAEA,IAAM,iCAAiB,IAAI,IAAI;CAAC;CAAgB;CAAQ;CAAS;CAAQ;AAAmB,CAAC;AAE7F,SAAS,KAAK,KAAuB;CACnC,OAAO,YAAY,GAAG,CAAC,CAAC,SAAS,SAAS;EACxC,IAAI,eAAe,IAAI,IAAI,GAAG,OAAO,CAAC;EACtC,MAAM,OAAO,KAAK,KAAK,IAAI;EAC3B,OAAO,SAAS,IAAI,CAAC,CAAC,YAAY,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI;CAC1D,CAAC;AACH;;;;;;;AAQA,SAAgB,QAAQ,KAAmC;CACzD,MAAM,OAAO,WAAW,KAAK,KAAK,KAAK,CAAC,IAAI,KAAK,KAAK,KAAK,IAAI;CAC/D,IAAI,CAAC,WAAW,IAAI,GAAG,OAAO,CAAC;CAC/B,MAAM,QAAQ,KAAK,IAAI,CAAC,CAAC,QACtB,MAAM,qBAAqB,KAAK,CAAC,KAAK,CAAC,EAAE,SAAS,QAAQ,KAAK,CAAC,EAAE,SAAS,WAAW,CACzF;CACA,MAAM,WAAiC,CAAC;CACxC,KAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,MAAM,SAAS,KAAK,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,KAAK,GAAG;EACnD,MAAM,OAAO,aAAa,MAAM,MAAM;EACtC,KAAK,MAAM,aAAa,WAAW,IAAI,GAAG,SAAS,KAAK;GAAE,MAAM;GAAK,GAAG;EAAU,CAAC;CACrF;CACA,OAAO,SAAS,MAAM,GAAG,MAAM,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,OAAO,EAAE,IAAI;AAChF"}
1
+ {"version":3,"file":"auditar.js","names":[],"sources":["../../src/cli/auditar.ts"],"sourcesContent":["/*\n * Regras genéricas de design (DESIGN_RULES.md) que não são específicas deste repositório:\n * qualquer projeto que use o Rendra pode ter cor fixa, valor arbitrário do Tailwind, estilo\n * inline, fonte fixa, 100vh ou raio fixo. Módulo único: `scripts/check-design-rules.mjs` (as\n * regras deste repositório, mais as específicas: var-inline, svg-marca, classe-dinamica,\n * texto-estreito, primaria-como-texto, componente-duplicado, css-fora-do-lugar, botao-solto)\n * e `rendra auditar` (a CLI publicada, para qualquer projeto de destino) importam daqui, para\n * nunca divergir.\n *\n * Duas camadas:\n * - `auditLines(text)`: pura, sem acesso a arquivo. Recebe o texto de UM arquivo e devolve\n * as violações das sete regras (linha, regra, mensagem, trecho). `check-design-rules.mjs`\n * chama isso por arquivo, porque ele já tem sua própria varredura de diretório com a\n * exceção `src/brand`/`src/styles` (regras próprias do tema deste repositório, que não\n * fazem sentido num projeto de destino qualquer).\n * - `auditar(cwd, opcoes?)`: varre `cwd` (ou `cwd/src`, quando existir) sozinha e devolve a\n * lista completa. Por padrão, sem exceção de diretório; `opcoes` (`pastas`, `ignorar`,\n * `regras`) são opt-in e, sem elas, o resultado é o de sempre. É o que `rendra auditar`\n * chama. As regras do vocabulário do Rendra (as dez extras) ficam em `auditar-tudo.ts`.\n */\nimport { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'\nimport { join, relative, sep } from 'node:path'\n\nexport interface AuditViolation {\n line: number\n rule: string\n message: string\n source: string\n}\n\nexport interface FileAuditViolation extends AuditViolation {\n file: string\n}\n\ninterface LineRule {\n id: string\n test: RegExp\n message: string\n skipLine?: (line: string) => boolean\n}\n\nconst isCommentLine = (line: string) => /^\\s*(\\/\\/|\\*|\\/\\*)/.test(line)\n\n/** As seis regras genéricas mais simples: um regex por linha. */\nexport const LINE_RULES: LineRule[] = [\n {\n id: 'cor-fixa',\n test: /#[0-9a-fA-F]{3,8}\\b|\\b(?:rgba?|hsla?|oklch|oklab)\\(/g,\n message: 'Cor fixa em componente. Use um token semântico (bg-primary, text-muted-foreground).',\n skipLine: (line) => isCommentLine(line) || /&#\\d+;/.test(line),\n },\n {\n id: 'valor-arbitrario',\n // Valor arbitrário (p-[13px]). Variantes de estado (data-[state=open]:) terminam em ':' e são\n // permitidas; transition-[...] só lista propriedades, também permitido.\n test: /(?<![\\w-])(?:[a-z]+:)*-?(?!transition-)[a-z][a-z-]*-\\[[^\\]\\s]+\\](?!(?:\\/[\\w-]+)?:)/g,\n message: 'Valor arbitrário do Tailwind. Use a escala de tokens ou crie um token nomeado.',\n skipLine: isCommentLine,\n },\n {\n id: 'estilo-inline',\n // Qualquer style= que não seja só variável CSS (--x) é estilo inline.\n test: /style=\\{(?![^}]*['\"]--)(?!\\s*$)/g,\n message: 'Estilo inline. Use classes; para valor dinâmico use variável CSS (--x).',\n },\n {\n id: 'fonte-fixa',\n // Só em contexto de fonte (declaração ou nome entre aspas numa pilha de fontes): a\n // palavra \"Inter\" num texto da interface não é violação.\n test: /font-family|fontFamily|['\"](?:Poppins|Inter|Roboto|Arial|Helvetica)(?:['\",]| sans| serif)/g,\n message:\n 'Nome de fonte fixo. A fonte vem da variável de tema da marca (ex.: --rendra-brand-font).',\n },\n {\n id: '100vh',\n test: /100vh|\\bh-screen\\b|\\bmin-h-screen\\b/g,\n message: 'Use 100dvh (h-dvh / min-h-dvh), nunca 100vh.',\n },\n {\n id: 'raio-fixo',\n // border-radius fixo em CSS ou em estilo inline (borderRadius): nunca em px/rem/% direto,\n // sempre por um token de raio por papel (rounded-control, rounded-surface, rounded-item...)\n // ou uma variável de tema (--rendra-shape-*, --rendra-radius).\n test: /border-radius\\s*:\\s*(?!var\\()\\d|borderRadius\\s*:\\s*(?!['\"`]?var\\()['\"`]?\\d/g,\n message:\n 'Raio fixo. Use um token de raio por papel (rounded-control, rounded-surface, rounded-item...) ou uma variável de tema.',\n skipLine: isCommentLine,\n },\n]\n\n/** Identificadores das sete regras genéricas (as seis de linha e `fora-da-escala`). */\nexport const IDS_REGRAS_GENERICAS = [...LINE_RULES.map((r) => r.id), 'fora-da-escala']\n\n// Degraus fora da escala não geram CSS (o Tailwind ignora em silêncio e o layout quebra).\nconst ALLOWED_STEPS = new Set(['0', '1', '2', '3', '4', '6', '8', '12', '16', '24'])\nconst STEP_CLASS =\n /(?<![\\w-])-?(?:p[xytrbl]?|m[xytrbl]?|gap(?:-[xy])?|w|h|size|min-[wh]|max-[wh]|inset(?:-[xy])?|top|left|right|bottom|space-[xy]|translate-[xy]|scroll-[mp][xytrbl]?)-(\\d+(?:\\.\\d+)?)(?![\\d/.\\w-])/g\n\n/** Sétima regra (\"fora-da-escala\"): olha todos os degraus da linha, não só o primeiro. */\nfunction checkForaDaEscala(line: string): boolean {\n for (const m of line.matchAll(STEP_CLASS)) if (!ALLOWED_STEPS.has(m[1]!)) return true\n return false\n}\n\n/**\n * Varre o texto de um arquivo (linha a linha) contra as sete regras genéricas de\n * DESIGN_RULES.md. Pura: não lê arquivo, não decide o que é escopo do projeto (isso é de quem\n * chama, como `auditar` abaixo ou o `walk`/`ALLOWED_DIRS` de check-design-rules.mjs).\n */\nexport function auditLines(text: string): AuditViolation[] {\n const found: AuditViolation[] = []\n const lines = text.split('\\n')\n lines.forEach((line, i) => {\n for (const rule of LINE_RULES) {\n if (rule.skipLine?.(line)) continue\n rule.test.lastIndex = 0\n if (rule.test.test(line)) {\n found.push({ line: i + 1, rule: rule.id, message: rule.message, source: line.trim() })\n }\n }\n if (!isCommentLine(line) && checkForaDaEscala(line)) {\n found.push({\n line: i + 1,\n rule: 'fora-da-escala',\n message:\n 'Degrau fora da escala (permitidos: 0, 1, 2, 3, 4, 6, 8, 12, 16, 24). Use a escala ou um token nomeado.',\n source: line.trim(),\n })\n }\n })\n return found\n}\n\nexport const NOMES_PULADOS = new Set(['node_modules', 'dist', 'build', '.git', '.storybook-static'])\n\n/** Opções de `auditar`. Todas opcionais: sem elas, o comportamento é o de sempre. */\nexport interface OpcoesAuditar {\n /** Pastas a varrer, relativas a `cwd`. Valem como estão (sem o atalho para `src`). */\n pastas?: string[]\n /** Pastas a pular, relativas a `cwd`: prefixo de pasta, nunca glob nem prefixo de texto. */\n ignorar?: string[]\n /** `{ \"<id>\": false }` desliga a regra de identificador `<id>`. */\n regras?: Record<string, boolean>\n}\n\n/** `./src/brand/` e `src\\\\brand` viram `src/brand`; o que sobrar vazio (`.`, `./`) é descartado. */\nfunction normalizarPrefixo(prefixo: string): string {\n let p = prefixo.replace(/\\\\/g, '/')\n while (p.startsWith('./')) p = p.slice(2)\n p = p.replace(/\\/+$/, '')\n return p === '.' ? '' : p\n}\n\n/**\n * `file` (relativo a `cwd`, com `/`) está numa das pastas de `prefixos`? Prefixo de pasta:\n * `src/brand` casa `src/brand` e `src/brand/x.ts`, nunca `src/brand-novo/x.ts` nem, com\n * `src/br`, `src/brand`.\n */\nexport function casaPrefixo(file: string, prefixos: string[]): boolean {\n return prefixos.some((bruto) => {\n const p = normalizarPrefixo(bruto)\n return p !== '' && (file === p || file.startsWith(p + '/'))\n })\n}\n\n/**\n * Lista os arquivos sob `dir`. `pular(nome, caminho)` decide por pasta ou arquivo se a entrada\n * fica de fora (a pasta pulada não é aberta). Ordem: a do sistema de arquivos, como sempre.\n */\nexport function percorrer(\n dir: string,\n pular: (nome: string, caminho: string) => boolean,\n): string[] {\n return readdirSync(dir).flatMap((name) => {\n const full = join(dir, name)\n if (pular(name, full)) return []\n return statSync(full).isDirectory() ? percorrer(full, pular) : [full]\n })\n}\n\nexport const EXTENSAO_VARRIDA = /\\.(tsx?|jsx?|css)$/\n\n/**\n * Arquivos que `auditar` (e `auditarTudo`, no modo `cli`) examinam: `.ts`/`.tsx`/`.js`/`.jsx`/\n * `.css`, sem teste nem story, sem `NOMES_PULADOS` e sem as pastas de `ignorar`. Caminhos\n * absolutos, na ordem do sistema de arquivos, sem repetição. Sem `pastas`, a raiz é\n * `cwd/src` (quando existe) ou `cwd`; com `pastas`, são exatamente essas, relativas a `cwd`.\n */\nexport function coletarArquivos(\n cwd: string,\n opcoes: Pick<OpcoesAuditar, 'pastas' | 'ignorar'> = {},\n): string[] {\n const ignorar = opcoes.ignorar ?? []\n const raizes =\n opcoes.pastas && opcoes.pastas.length > 0\n ? opcoes.pastas.map((p) => join(cwd, p))\n : [existsSync(join(cwd, 'src')) ? join(cwd, 'src') : cwd]\n const pular = (nome: string, caminho: string) =>\n NOMES_PULADOS.has(nome) || casaPrefixo(relativoA(cwd, caminho), ignorar)\n const arquivos = new Set<string>()\n for (const raiz of raizes) {\n if (!existsSync(raiz)) continue\n for (const f of percorrer(raiz, pular)) arquivos.add(f)\n }\n return [...arquivos].filter(\n (f) => EXTENSAO_VARRIDA.test(f) && !f.includes('.test.') && !f.includes('.stories.'),\n )\n}\n\n/** Caminho relativo a `base`, com `/` em qualquer sistema. */\nexport function relativoA(base: string, caminho: string): string {\n return relative(base, caminho).split(sep).join('/')\n}\n\n/**\n * Varre `cwd` (ou `cwd/src`, quando existir: a convenção deste boilerplate e da maioria dos\n * projetos React) por arquivos `.ts`/`.tsx`/`.js`/`.jsx`/`.css`, ignorando teste e story, e\n * devolve toda violação das sete regras genéricas. É o que `rendra auditar [cwd]` chama; não\n * imprime nada e não lança (a CLI decide o formato de saída e o código de saída).\n *\n * Com `opcoes.pastas`, varre exatamente essas pastas (relativas a `cwd`, sem o atalho para\n * `src`); com `opcoes.ignorar`, pula as pastas dadas; `opcoes.regras` desliga regras por id.\n * `file` sai sempre relativo a `cwd`, com `/`, ordenado por arquivo e linha.\n */\nexport function auditar(cwd: string, opcoes: OpcoesAuditar = {}): FileAuditViolation[] {\n const problems: FileAuditViolation[] = []\n for (const file of coletarArquivos(cwd, opcoes)) {\n const rel = relativoA(cwd, file)\n const text = readFileSync(file, 'utf8')\n for (const violation of auditLines(text)) {\n if (opcoes.regras?.[violation.rule] === false) continue\n problems.push({ file: rel, ...violation })\n }\n }\n return problems.sort((a, b) => a.file.localeCompare(b.file) || a.line - b.line)\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- /*! Rendra Design System v2.3.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
- import { t as CATALOG } from "../components-Cq_PmN4y.js";
1
+ /*! Rendra Design System v2.5.0 | MIT | © 2026 Bruno Magalhaes | brunomagalhaes.me */
2
+ import { t as CATALOG } from "../components-DkpspH9h.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() {