@fabio.caffarello/react-design-system 3.6.0 → 3.8.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.
- package/README.md +3 -2
- package/dist/hooks/index.cjs +2 -0
- package/dist/hooks/index.cjs.map +1 -0
- package/dist/hooks/index.js +23 -0
- package/dist/hooks/index.js.map +1 -0
- package/dist/index.cjs +46 -72
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2025 -1920
- package/dist/index.js.map +1 -1
- package/dist/react-design-system.css +1 -1
- package/dist/server/index.cjs +23 -23
- package/dist/server/index.cjs.map +1 -1
- package/dist/server/index.js +798 -691
- package/dist/server/index.js.map +1 -1
- package/dist/ui/components/Accordion/Accordion.d.ts +9 -1
- package/dist/ui/components/Stat/Stat.d.ts +105 -0
- package/dist/ui/components/Stat/StatGroup.d.ts +69 -0
- package/dist/ui/components/Stat/index.d.ts +4 -0
- package/dist/ui/components/index.d.ts +2 -0
- package/dist/ui/hooks-entry.d.ts +2 -0
- package/dist/ui/server.d.ts +4 -0
- package/dist/ui/utils/tailwind-safelist.d.ts +3 -1
- package/package.json +17 -13
|
@@ -5,6 +5,12 @@ export interface AccordionItem {
|
|
|
5
5
|
title: string;
|
|
6
6
|
content: ReactNode;
|
|
7
7
|
disabled?: boolean;
|
|
8
|
+
/** Extra classes for the item wrapper (the bordered card). Merged after the defaults, so conflicting Tailwind classes win. */
|
|
9
|
+
className?: string;
|
|
10
|
+
/** Extra classes for the header button — overrides the default `label` typography (e.g. `font-semibold text-base`). */
|
|
11
|
+
triggerClassName?: string;
|
|
12
|
+
/** Extra classes for the content padding wrapper inside the panel. */
|
|
13
|
+
contentClassName?: string;
|
|
8
14
|
}
|
|
9
15
|
export interface AccordionProps {
|
|
10
16
|
items: AccordionItem[];
|
|
@@ -18,7 +24,9 @@ export interface AccordionProps {
|
|
|
18
24
|
*
|
|
19
25
|
* A collapsible content component that can display multiple items.
|
|
20
26
|
* Supports single and multiple selection modes.
|
|
21
|
-
*
|
|
27
|
+
*
|
|
28
|
+
* Open panels animate via `grid-template-rows: 0fr → 1fr`, so content of
|
|
29
|
+
* any height expands fully — there is no max-height clamp (issue #202).
|
|
22
30
|
*
|
|
23
31
|
* @example
|
|
24
32
|
* ```tsx
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type StatTone = "neutral" | "success" | "warning" | "error";
|
|
3
|
+
export type StatAlign = "start" | "center";
|
|
4
|
+
export interface StatProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* The metric value to display. Strings are rendered verbatim — formatting
|
|
7
|
+
* (number locale, currency, units, relative time, etc.) is the consumer's
|
|
8
|
+
* responsibility, not the design system's. Pass `null` or `undefined` to
|
|
9
|
+
* render the empty-state placeholder (see "Empty state" below).
|
|
10
|
+
*/
|
|
11
|
+
value: ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Short metric label (e.g. "Votos", "Alinhamento"). Required for screen
|
|
14
|
+
* reader context — the label describes what the value means.
|
|
15
|
+
*/
|
|
16
|
+
label: string;
|
|
17
|
+
/**
|
|
18
|
+
* Optional third line of context below the value (e.g. "no banco",
|
|
19
|
+
* "últimos 12 m", "+3% no mês"). The `tone` prop styles THIS line — see
|
|
20
|
+
* `tone` for the contract.
|
|
21
|
+
*/
|
|
22
|
+
hint?: ReactNode;
|
|
23
|
+
/**
|
|
24
|
+
* Optional icon rendered above the value (home-style stats use icons;
|
|
25
|
+
* detail-page stats typically don't).
|
|
26
|
+
*/
|
|
27
|
+
icon?: ReactNode;
|
|
28
|
+
/**
|
|
29
|
+
* Block alignment. `start` left-aligns label/value/hint (detail-page
|
|
30
|
+
* style); `center` centers them (home-hero style).
|
|
31
|
+
* @default 'start'
|
|
32
|
+
*/
|
|
33
|
+
align?: StatAlign;
|
|
34
|
+
/**
|
|
35
|
+
* Semantic tone for the metric — `neutral` for plain stats, the others
|
|
36
|
+
* for classified states (good/warning/bad).
|
|
37
|
+
*
|
|
38
|
+
* **Scope (contract).** Tone affects ONLY the `hint`, not the `value`,
|
|
39
|
+
* `label`, or `icon`. The `value` always renders in `text-fg-primary`
|
|
40
|
+
* regardless of tone; the `label` in `text-fg-secondary`; the `icon` in
|
|
41
|
+
* `text-icon-default`. This is deliberate — a colored value would
|
|
42
|
+
* compete with the label for attention and bias the reader's
|
|
43
|
+
* interpretation of the metric. If a future requirement needs the
|
|
44
|
+
* `value` (or icon) to inherit tone, that becomes a new prop or a
|
|
45
|
+
* semver-bound default change, not a surprise expansion of `tone`.
|
|
46
|
+
*
|
|
47
|
+
* Tone maps directly to the semantic foreground tokens (no new
|
|
48
|
+
* vocabulary): `neutral` → `text-fg-tertiary`, `success` →
|
|
49
|
+
* `text-fg-success`, `warning` → `text-fg-warning`, `error` →
|
|
50
|
+
* `text-fg-error`. See `.claude/rules/colors.md`.
|
|
51
|
+
*
|
|
52
|
+
* @default 'neutral'
|
|
53
|
+
*/
|
|
54
|
+
tone?: StatTone;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* `Stat` — a single statistic block (icon? + value + label + hint?).
|
|
58
|
+
*
|
|
59
|
+
* Composes with `StatGroup` (1-px-divider strip or grid) but is also
|
|
60
|
+
* valid standalone — a single `Stat` outside a group is a legitimate use
|
|
61
|
+
* case for a hero metric.
|
|
62
|
+
*
|
|
63
|
+
* ### Empty state contract
|
|
64
|
+
*
|
|
65
|
+
* When `value` is `null` OR `undefined`, the component renders the
|
|
66
|
+
* em-dash placeholder `—` (U+2014) with `aria-label="No data"` on its
|
|
67
|
+
* wrapper. The label is intentionally hard-coded in English in this
|
|
68
|
+
* version; a screen reader announcing "No data" in an otherwise
|
|
69
|
+
* Portuguese app is a known inconsistency accepted for now — if i18n
|
|
70
|
+
* becomes a requirement, an `emptyLabel?: string` prop will be added in
|
|
71
|
+
* a follow-up PR (semver-safe addition).
|
|
72
|
+
*
|
|
73
|
+
* Other falsy values — `0`, `""`, `false`, an empty fragment — are
|
|
74
|
+
* **legitimate values** and render as-is. The empty trigger is only
|
|
75
|
+
* `null`/`undefined`, because `0` (count = zero) is meaningful data that
|
|
76
|
+
* the consumer would not want masked.
|
|
77
|
+
*
|
|
78
|
+
* ### Server-safe
|
|
79
|
+
*
|
|
80
|
+
* Pure presentation — no hooks, no event handlers on the DOM. Ships in
|
|
81
|
+
* the `./server` entry alongside `StatGroup`. Consumer-supplied `icon`
|
|
82
|
+
* may itself be a client component; React's RSC boundary handles that
|
|
83
|
+
* normally.
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```tsx
|
|
87
|
+
* // Home-style (centered, with icon)
|
|
88
|
+
* <Stat
|
|
89
|
+
* icon={<Users size={20} aria-hidden="true" />}
|
|
90
|
+
* value="9,4 mil"
|
|
91
|
+
* label="Parlamentares"
|
|
92
|
+
* align="center"
|
|
93
|
+
* />
|
|
94
|
+
*
|
|
95
|
+
* // Detail-page-style (start-aligned, with hint)
|
|
96
|
+
* <Stat
|
|
97
|
+
* value="87%"
|
|
98
|
+
* label="Alinhamento"
|
|
99
|
+
* hint="últimos 12 meses"
|
|
100
|
+
* tone="success"
|
|
101
|
+
* />
|
|
102
|
+
* ```
|
|
103
|
+
*/
|
|
104
|
+
export declare function Stat({ value, label, hint, icon, align, tone, className, ...props }: StatProps): import("react").JSX.Element;
|
|
105
|
+
export default Stat;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type HTMLAttributes, type ReactNode } from "react";
|
|
2
|
+
export type StatGroupLayout = "strip" | "grid";
|
|
3
|
+
export type StatGroupCols = 2 | 3 | 4;
|
|
4
|
+
export interface StatGroupProps extends HTMLAttributes<HTMLDivElement> {
|
|
5
|
+
/**
|
|
6
|
+
* `strip` — single horizontal row, no wrap. Each `Stat` shares the row
|
|
7
|
+
* width via `flex-1`. Use when you guarantee the horizontal space
|
|
8
|
+
* (hero areas, wide dashboards). On narrow viewports the row does NOT
|
|
9
|
+
* reflow — choose `grid` if you need responsive collapse.
|
|
10
|
+
*
|
|
11
|
+
* `grid` — multi-column grid that reflows. Always 2-up on mobile,
|
|
12
|
+
* expands to `cols` columns at the `md` breakpoint (768 px) and up.
|
|
13
|
+
* Five or more children spill to a second row with the divider lines
|
|
14
|
+
* preserved.
|
|
15
|
+
*
|
|
16
|
+
* @default 'grid'
|
|
17
|
+
*/
|
|
18
|
+
layout?: StatGroupLayout;
|
|
19
|
+
/**
|
|
20
|
+
* Desktop column count (≥ 768 px). Only effective in `layout="grid"`;
|
|
21
|
+
* ignored in `layout="strip"`. Mobile is always 2 columns regardless.
|
|
22
|
+
*
|
|
23
|
+
* @default 4
|
|
24
|
+
*/
|
|
25
|
+
cols?: StatGroupCols;
|
|
26
|
+
children: ReactNode;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* `StatGroup` — container for one or more `Stat` blocks with 1-px
|
|
30
|
+
* dividers between them.
|
|
31
|
+
*
|
|
32
|
+
* ### Divider technique
|
|
33
|
+
*
|
|
34
|
+
* The container has `bg-line-default` and `gap-px` (1 px); each child
|
|
35
|
+
* `Stat` carries its own `bg-surface-base`. The 1 px of gap exposes the
|
|
36
|
+
* container's background as the divider line, while each cell masks its
|
|
37
|
+
* own area with `bg-surface-base`. Works identically for the strip
|
|
38
|
+
* layout (vertical dividers) and the grid layout (vertical AND
|
|
39
|
+
* horizontal dividers, automatically, as the grid reflows). No separate
|
|
40
|
+
* `Divider` component, no per-cell border logic.
|
|
41
|
+
*
|
|
42
|
+
* The outer ring is `border border-line-default` matching the gap color,
|
|
43
|
+
* with `rounded-lg` and `overflow-hidden` clipping the inner cells to
|
|
44
|
+
* the radius.
|
|
45
|
+
*
|
|
46
|
+
* ### Server-safe
|
|
47
|
+
*
|
|
48
|
+
* Pure presentation — no hooks, no event handlers on the DOM. Ships in
|
|
49
|
+
* `./server` alongside `Stat`.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```tsx
|
|
53
|
+
* <StatGroup layout="strip">
|
|
54
|
+
* <Stat icon={<Users />} value="9,4 mil" label="Parlamentares" align="center" />
|
|
55
|
+
* <Stat icon={<FileText />} value="3,2 mil" label="Proposições" align="center" />
|
|
56
|
+
* <Stat icon={<Vote />} value="1,1 mil" label="Votações" align="center" />
|
|
57
|
+
* <Stat icon={<Clock />} value="há 18 dias" label="Última atualização" align="center" />
|
|
58
|
+
* </StatGroup>
|
|
59
|
+
*
|
|
60
|
+
* <StatGroup layout="grid" cols={4}>
|
|
61
|
+
* <Stat value="87%" label="Alinhamento" hint="últimos 12 m" tone="success" />
|
|
62
|
+
* <Stat value={null} label="Sem orientação" hint="no período" />
|
|
63
|
+
* <Stat value="R$ 187.472,95" label="Gastos" hint="no mandato" />
|
|
64
|
+
* <Stat value="42" label="Votações" hint="no banco" />
|
|
65
|
+
* </StatGroup>
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare function StatGroup({ layout, cols, className, children, ...props }: StatGroupProps): import("react").JSX.Element;
|
|
69
|
+
export default StatGroup;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
export { default as Card } from "./Card";
|
|
2
|
+
export { Stat, StatGroup } from "./Stat";
|
|
3
|
+
export type { StatProps, StatTone, StatAlign, StatGroupProps, StatGroupLayout, StatGroupCols, } from "./Stat";
|
|
2
4
|
export * from "./Form";
|
|
3
5
|
export { default as Breadcrumb } from "./Breadcrumb";
|
|
4
6
|
export type { BreadcrumbItem } from "./Breadcrumb";
|
package/dist/ui/server.d.ts
CHANGED
|
@@ -53,6 +53,10 @@ export { default as NavbarSeparator } from "./components/SideNavbar/components/N
|
|
|
53
53
|
export type { NavbarSeparatorProps } from "./components/SideNavbar/types";
|
|
54
54
|
export { default as PageHeader } from "./components/PageHeader/PageHeader";
|
|
55
55
|
export type { PageHeaderProps, PageHeaderVariant, } from "./components/PageHeader/types";
|
|
56
|
+
export { default as Stat } from "./components/Stat/Stat";
|
|
57
|
+
export type { StatProps, StatTone, StatAlign } from "./components/Stat/Stat";
|
|
58
|
+
export { StatGroup } from "./components/Stat/StatGroup";
|
|
59
|
+
export type { StatGroupProps, StatGroupLayout, StatGroupCols, } from "./components/Stat/StatGroup";
|
|
56
60
|
export { default as TableCell } from "./components/Table/TableCell";
|
|
57
61
|
export type { TableCellProps } from "./components/Table/TableCell";
|
|
58
62
|
export { default as Timeline } from "./components/Timeline/Timeline";
|
|
@@ -32,7 +32,9 @@ export declare const TAILWIND_SAFELIST: readonly [RegExp, RegExp, RegExp, RegExp
|
|
|
32
32
|
* Documentation for Tailwind v4 configuration
|
|
33
33
|
*
|
|
34
34
|
* In Tailwind v4, safelist is configured via CSS using @theme or @source directives.
|
|
35
|
-
* Since we're using @tailwindcss/
|
|
35
|
+
* Since we're using the @tailwindcss/vite plugin (see vite.config.ts and
|
|
36
|
+
* .claude/rules/tokens.md — the PostCSS pipeline is banned), we can add
|
|
37
|
+
* safelist in the CSS file.
|
|
36
38
|
*
|
|
37
39
|
* For dynamic classes, we should ensure they're:
|
|
38
40
|
* 1. Used in component code (so Tailwind detects them)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fabio.caffarello/react-design-system",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.8.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
"import": "./dist/server/index.js",
|
|
20
20
|
"require": "./dist/server/index.cjs"
|
|
21
21
|
},
|
|
22
|
+
"./hooks": {
|
|
23
|
+
"types": "./dist/ui/hooks-entry.d.ts",
|
|
24
|
+
"import": "./dist/hooks/index.js",
|
|
25
|
+
"require": "./dist/hooks/index.cjs"
|
|
26
|
+
},
|
|
22
27
|
"./styles": "./dist/react-design-system.css",
|
|
23
28
|
"./styles.css": "./dist/react-design-system.css"
|
|
24
29
|
},
|
|
@@ -31,7 +36,7 @@
|
|
|
31
36
|
"access": "public"
|
|
32
37
|
},
|
|
33
38
|
"scripts": {
|
|
34
|
-
"build": "npx tsc --project tsconfig.app.json --declaration --emitDeclarationOnly --outDir dist && vite build && vite build --config vite.config.server.ts && npm run build:validate",
|
|
39
|
+
"build": "npx tsc --project tsconfig.app.json --declaration --emitDeclarationOnly --outDir dist && vite build && vite build --config vite.config.server.ts && vite build --config vite.config.hooks.ts && npm run build:validate",
|
|
35
40
|
"build:validate": "tsx scripts/validate-build-exports.ts && node scripts/validate-use-client-in-dist.mjs && node scripts/validate-server-entry.mjs",
|
|
36
41
|
"lint": "eslint .",
|
|
37
42
|
"typecheck": "tsc --build --force tsconfig.json",
|
|
@@ -50,7 +55,7 @@
|
|
|
50
55
|
"lucide-react": "^0.552.0 || ^1.0.0",
|
|
51
56
|
"react": ">=19",
|
|
52
57
|
"react-dom": ">=19",
|
|
53
|
-
"react-hook-form": "^7.
|
|
58
|
+
"react-hook-form": "^7.78.0"
|
|
54
59
|
},
|
|
55
60
|
"overrides": {
|
|
56
61
|
"handlebars": "^4.7.9",
|
|
@@ -77,8 +82,7 @@
|
|
|
77
82
|
}
|
|
78
83
|
},
|
|
79
84
|
"dependencies": {
|
|
80
|
-
"@radix-ui/react-slot": "^1.2.
|
|
81
|
-
"@tailwindcss/postcss": "^4.1.16",
|
|
85
|
+
"@radix-ui/react-slot": "^1.2.5",
|
|
82
86
|
"class-variance-authority": "^0.7.1",
|
|
83
87
|
"clsx": "^2.1.1",
|
|
84
88
|
"react": ">=19",
|
|
@@ -86,8 +90,8 @@
|
|
|
86
90
|
"tailwind-merge": "^3.6.0"
|
|
87
91
|
},
|
|
88
92
|
"devDependencies": {
|
|
89
|
-
"@commitlint/cli": "^
|
|
90
|
-
"@commitlint/config-conventional": "^
|
|
93
|
+
"@commitlint/cli": "^21.0.2",
|
|
94
|
+
"@commitlint/config-conventional": "^21.0.2",
|
|
91
95
|
"@eslint/js": "^10.0.1",
|
|
92
96
|
"@playwright/test": "^1.60.0",
|
|
93
97
|
"@storybook/addon-a11y": "^10.4.2",
|
|
@@ -99,19 +103,19 @@
|
|
|
99
103
|
"@storybook/addon-vitest": "^10.4.2",
|
|
100
104
|
"@storybook/react-vite": "^10.4.2",
|
|
101
105
|
"@storybook/test": "^8.6.15",
|
|
102
|
-
"@tailwindcss/vite": "^4.
|
|
106
|
+
"@tailwindcss/vite": "^4.3.0",
|
|
103
107
|
"@testing-library/dom": "^10.4.1",
|
|
104
108
|
"@testing-library/jest-dom": "^6.9.1",
|
|
105
109
|
"@testing-library/react": "^16.3.2",
|
|
106
110
|
"@types/bun": "^1.3.7",
|
|
107
|
-
"@types/node": "^
|
|
108
|
-
"@types/react": "^19.2.
|
|
111
|
+
"@types/node": "^25.9.2",
|
|
112
|
+
"@types/react": "^19.2.17",
|
|
109
113
|
"@types/react-dom": "^19.2.3",
|
|
110
114
|
"@vitejs/plugin-react": "^5.0.4",
|
|
111
115
|
"@vitest/browser-playwright": "^4.1.8",
|
|
112
116
|
"@vitest/coverage-v8": "^4.1.8",
|
|
113
|
-
"autoprefixer": "^10.
|
|
114
|
-
"baseline-browser-mapping": "^2.10.
|
|
117
|
+
"autoprefixer": "^10.5.0",
|
|
118
|
+
"baseline-browser-mapping": "^2.10.34",
|
|
115
119
|
"culori": "^4.0.2",
|
|
116
120
|
"dependency-cruiser": "^17.3.6",
|
|
117
121
|
"eslint": "^10.4.1",
|
|
@@ -127,7 +131,7 @@
|
|
|
127
131
|
"plop": "^4.0.4",
|
|
128
132
|
"postcss": "^8.5.6",
|
|
129
133
|
"prettier": "^3.8.3",
|
|
130
|
-
"react-hook-form": "^7.
|
|
134
|
+
"react-hook-form": "^7.78.0",
|
|
131
135
|
"remark-gfm": "^4.0.1",
|
|
132
136
|
"storybook": "^10.4.2",
|
|
133
137
|
"storybook-addon-performance": "^0.17.3",
|