@godxjp/ui 20.0.0 → 20.1.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 (250) hide show
  1. package/README.md +8 -2
  2. package/dist/app/theme-axes.d.ts +2 -0
  3. package/dist/app/theme-axes.js +45 -0
  4. package/dist/components/charts/chart-cartesian.d.ts +2 -1
  5. package/dist/components/charts/chart-cartesian.js +52 -3
  6. package/dist/components/charts/chart-category-axis.d.ts +55 -0
  7. package/dist/components/charts/chart-category-axis.js +93 -0
  8. package/dist/components/charts/chart-frame.d.ts +10 -1
  9. package/dist/components/charts/chart-frame.js +19 -3
  10. package/dist/components/charts/compact-bar-trend.d.ts +1 -1
  11. package/dist/components/charts/compact-bar-trend.js +2 -0
  12. package/dist/components/charts/pie-chart.d.ts +1 -1
  13. package/dist/components/charts/pie-chart.js +12 -1
  14. package/dist/components/charts/recharts-peer.d.ts +49 -0
  15. package/dist/components/charts/recharts-peer.js +45 -0
  16. package/dist/components/data-display/card.d.ts +12 -1
  17. package/dist/components/data-display/card.js +22 -4
  18. package/dist/components/data-display/code-block.js +6 -2
  19. package/dist/components/data-display/data-table.d.ts +39 -4
  20. package/dist/components/data-display/data-table.js +802 -270
  21. package/dist/components/data-display/descriptions.d.ts +27 -6
  22. package/dist/components/data-display/descriptions.js +58 -17
  23. package/dist/components/data-display/index.d.ts +2 -0
  24. package/dist/components/data-display/index.js +2 -0
  25. package/dist/components/data-display/list-row.d.ts +10 -1
  26. package/dist/components/data-display/list-row.js +7 -1
  27. package/dist/components/data-display/popover.d.ts +2 -2
  28. package/dist/components/data-display/popover.js +61 -11
  29. package/dist/components/data-display/range-timeline.d.ts +38 -0
  30. package/dist/components/data-display/range-timeline.js +161 -0
  31. package/dist/components/data-display/scroll-area.js +13 -2
  32. package/dist/components/data-display/service-launcher-card.js +10 -10
  33. package/dist/components/data-display/table.d.ts +11 -3
  34. package/dist/components/data-display/table.js +48 -18
  35. package/dist/components/data-display/tree-list.js +4 -9
  36. package/dist/components/data-entry/calendar.d.ts +2 -2
  37. package/dist/components/data-entry/calendar.js +12 -1
  38. package/dist/components/data-entry/cascader.d.ts +1 -1
  39. package/dist/components/data-entry/cascader.js +188 -24
  40. package/dist/components/data-entry/color-picker.d.ts +1 -1
  41. package/dist/components/data-entry/color-picker.js +17 -5
  42. package/dist/components/data-entry/control-appearance.d.ts +64 -0
  43. package/dist/components/data-entry/control-appearance.js +39 -0
  44. package/dist/components/data-entry/control-surface.d.ts +61 -0
  45. package/dist/components/data-entry/control-surface.js +39 -0
  46. package/dist/components/data-entry/date-picker.d.ts +1 -1
  47. package/dist/components/data-entry/date-picker.js +344 -113
  48. package/dist/components/data-entry/date-range-picker.d.ts +1 -1
  49. package/dist/components/data-entry/date-range-picker.js +278 -140
  50. package/dist/components/data-entry/form-field.d.ts +1 -1
  51. package/dist/components/data-entry/form-field.js +39 -4
  52. package/dist/components/data-entry/form.d.ts +4 -0
  53. package/dist/components/data-entry/form.js +4 -2
  54. package/dist/components/data-entry/index.d.ts +7 -3
  55. package/dist/components/data-entry/index.js +10 -1
  56. package/dist/components/data-entry/input-otp.d.ts +1 -1
  57. package/dist/components/data-entry/input.d.ts +12 -34
  58. package/dist/components/data-entry/input.js +92 -24
  59. package/dist/components/data-entry/month-picker.d.ts +1 -1
  60. package/dist/components/data-entry/month-picker.js +47 -10
  61. package/dist/components/data-entry/month-range-picker.d.ts +1 -1
  62. package/dist/components/data-entry/month-range-picker.js +51 -11
  63. package/dist/components/data-entry/number-input.d.ts +7 -0
  64. package/dist/components/data-entry/number-input.js +147 -98
  65. package/dist/components/data-entry/password-input.d.ts +1 -1
  66. package/dist/components/data-entry/radio.d.ts +1 -1
  67. package/dist/components/data-entry/radio.js +61 -13
  68. package/dist/components/data-entry/search-input.d.ts +1 -1
  69. package/dist/components/data-entry/search-input.js +16 -2
  70. package/dist/components/data-entry/search-select.d.ts +2 -2
  71. package/dist/components/data-entry/search-select.js +209 -78
  72. package/dist/components/data-entry/select.d.ts +22 -4
  73. package/dist/components/data-entry/select.js +215 -163
  74. package/dist/components/data-entry/slider.d.ts +9 -1
  75. package/dist/components/data-entry/slider.js +87 -9
  76. package/dist/components/data-entry/switch.d.ts +3 -0
  77. package/dist/components/data-entry/switch.js +29 -3
  78. package/dist/components/data-entry/textarea.d.ts +14 -56
  79. package/dist/components/data-entry/textarea.js +80 -33
  80. package/dist/components/data-entry/time-picker.d.ts +2 -2
  81. package/dist/components/data-entry/time-picker.js +333 -109
  82. package/dist/components/data-entry/time-range-picker.d.ts +5 -0
  83. package/dist/components/data-entry/time-range-picker.js +89 -0
  84. package/dist/components/data-entry/transfer.d.ts +1 -1
  85. package/dist/components/data-entry/transfer.js +86 -28
  86. package/dist/components/data-entry/tree-select.d.ts +1 -1
  87. package/dist/components/data-entry/tree-select.js +201 -113
  88. package/dist/components/data-entry/tree-utils.js +7 -14
  89. package/dist/components/data-entry/upload-files.d.ts +2 -0
  90. package/dist/components/data-entry/upload-files.js +31 -0
  91. package/dist/components/data-entry/upload-request.d.ts +4 -0
  92. package/dist/components/data-entry/upload-request.js +53 -0
  93. package/dist/components/data-entry/upload-types.d.ts +28 -0
  94. package/dist/components/data-entry/upload-types.js +2 -0
  95. package/dist/components/data-entry/upload.d.ts +2 -2
  96. package/dist/components/data-entry/upload.js +475 -115
  97. package/dist/components/feedback/dialog.js +16 -19
  98. package/dist/components/general/button.d.ts +1 -1
  99. package/dist/components/general/button.js +5 -1
  100. package/dist/components/general/index.d.ts +1 -0
  101. package/dist/components/general/index.js +2 -0
  102. package/dist/components/general/typography.d.ts +3 -0
  103. package/dist/components/general/typography.js +15 -1
  104. package/dist/components/general/visually-hidden.d.ts +3 -0
  105. package/dist/components/general/visually-hidden.js +9 -0
  106. package/dist/components/layout/app-launcher.d.ts +34 -0
  107. package/dist/components/layout/app-launcher.js +228 -0
  108. package/dist/components/layout/app-shell.d.ts +2 -0
  109. package/dist/components/layout/app-shell.js +20 -7
  110. package/dist/components/layout/breadcrumb.d.ts +14 -2
  111. package/dist/components/layout/breadcrumb.js +46 -4
  112. package/dist/components/layout/flex.d.ts +1 -1
  113. package/dist/components/layout/flex.js +23 -2
  114. package/dist/components/layout/index.d.ts +3 -0
  115. package/dist/components/layout/index.js +5 -1
  116. package/dist/components/layout/org-switcher.js +20 -1
  117. package/dist/components/layout/page-container.d.ts +1 -1
  118. package/dist/components/layout/page-container.js +5 -3
  119. package/dist/components/layout/responsive-grid.d.ts +16 -2
  120. package/dist/components/layout/responsive-grid.js +29 -2
  121. package/dist/components/layout/topbar-item.d.ts +3 -0
  122. package/dist/components/layout/topbar-item.js +18 -3
  123. package/dist/components/layout/topbar.d.ts +1 -1
  124. package/dist/components/layout/topbar.js +27 -6
  125. package/dist/components/navigation/dropdown-menu.d.ts +30 -2
  126. package/dist/components/navigation/dropdown-menu.js +68 -11
  127. package/dist/components/navigation/pagination.d.ts +2 -2
  128. package/dist/components/navigation/pagination.js +155 -83
  129. package/dist/components/navigation/steps.d.ts +2 -2
  130. package/dist/components/navigation/steps.js +29 -4
  131. package/dist/components/navigation/tabs.d.ts +5 -33
  132. package/dist/components/navigation/tabs.js +232 -64
  133. package/dist/components/ui/input-otp.d.ts +26 -28
  134. package/dist/components/ui/input-otp.js +50 -7
  135. package/dist/components/ui/password-input.d.ts +36 -2
  136. package/dist/components/ui/password-input.js +27 -7
  137. package/dist/components/ui/rating.d.ts +25 -0
  138. package/dist/components/ui/rating.js +67 -27
  139. package/dist/components/ui/segmented.d.ts +9 -0
  140. package/dist/components/ui/segmented.js +17 -2
  141. package/dist/components/ui/separator.d.ts +4 -0
  142. package/dist/components/ui/separator.js +13 -0
  143. package/dist/components/ui/tag-input.d.ts +45 -0
  144. package/dist/components/ui/tag-input.js +109 -27
  145. package/dist/form/form-context.d.ts +29 -0
  146. package/dist/form/form-context.js +44 -4
  147. package/dist/form/form-field-array.d.ts +22 -0
  148. package/dist/form/form-field-array.js +50 -0
  149. package/dist/form/form-field-control.d.ts +2 -1
  150. package/dist/form/form-field-control.js +116 -40
  151. package/dist/form/form-root.d.ts +2 -1
  152. package/dist/form/form-root.js +142 -23
  153. package/dist/form/index.d.ts +3 -1
  154. package/dist/form/index.js +12 -1
  155. package/dist/i18n/messages/en.json +56 -5
  156. package/dist/i18n/messages/ja.json +56 -5
  157. package/dist/i18n/messages/vi.json +56 -5
  158. package/dist/inertia/index.d.ts +20 -0
  159. package/dist/inertia/index.js +60 -1
  160. package/dist/lib/control-styles.d.ts +25 -3
  161. package/dist/lib/control-styles.js +9 -3
  162. package/dist/lib/datetime/picker-format.d.ts +8 -0
  163. package/dist/lib/datetime/picker-format.js +54 -0
  164. package/dist/props/components/charts.prop.d.ts +21 -0
  165. package/dist/props/components/data-display.prop.d.ts +55 -4
  166. package/dist/props/components/data-entry.prop.d.ts +687 -43
  167. package/dist/props/components/form.prop.d.ts +142 -4
  168. package/dist/props/components/general.prop.d.ts +17 -1
  169. package/dist/props/components/index.d.ts +2 -2
  170. package/dist/props/components/layout.prop.d.ts +163 -6
  171. package/dist/props/components/navigation.prop.d.ts +113 -3
  172. package/dist/props/registry.d.ts +376 -10
  173. package/dist/props/registry.js +493 -9
  174. package/dist/props/vocabulary/data.prop.d.ts +179 -1
  175. package/dist/props/vocabulary/index.d.ts +4 -4
  176. package/dist/props/vocabulary/interaction.prop.d.ts +51 -2
  177. package/dist/props/vocabulary/navigation.prop.d.ts +40 -0
  178. package/dist/props/vocabulary/shared.prop.d.ts +33 -0
  179. package/dist/styles/badge-layout.css +2 -1
  180. package/dist/styles/card-layout.css +26 -4
  181. package/dist/styles/chart-layout.css +15 -0
  182. package/dist/styles/control.css +515 -14
  183. package/dist/styles/data-display-layout.css +241 -6
  184. package/dist/styles/data-entry-layout.css +13 -0
  185. package/dist/styles/layout.css +221 -4
  186. package/dist/styles/navigation-layout.css +227 -0
  187. package/dist/styles/shell-layout.css +229 -5
  188. package/dist/styles/table-layout.css +95 -6
  189. package/dist/styles/text-layout.css +5 -0
  190. package/dist/tokens/base.css +1 -0
  191. package/dist/tokens/components/badge.css +1 -0
  192. package/dist/tokens/components/chart.css +6 -0
  193. package/dist/tokens/components/control.css +79 -2
  194. package/dist/tokens/components/data-display.css +9 -0
  195. package/dist/tokens/components/descriptions.css +11 -0
  196. package/dist/tokens/components/flex.css +8 -0
  197. package/dist/tokens/components/navigation.css +55 -0
  198. package/dist/tokens/components/shell.css +50 -2
  199. package/dist/tokens/components/table.css +14 -0
  200. package/dist/tokens/semantic/layout.css +9 -0
  201. package/docs/CONSUMER-RULES.md +13 -0
  202. package/docs/DESIGN-AUTHORITY.md +43 -6
  203. package/docs/DEVELOPMENT.md +4 -3
  204. package/docs/FORMS.md +151 -6
  205. package/docs/FRAME-COVERAGE-REPORT.md +29 -16
  206. package/docs/README.md +1 -1
  207. package/docs/STANDARDS-vocabulary-tokens.md +1 -1
  208. package/docs/TESTING.md +15 -6
  209. package/docs/charts/cjk-category-axis.tsx +81 -0
  210. package/docs/data-display/card/index.tsx +22 -0
  211. package/docs/data-display/data-table/examples/antd-parity.tsx +257 -0
  212. package/docs/data-display/data-table/index.tsx +23 -0
  213. package/docs/data-display/descriptions.tsx +41 -0
  214. package/docs/data-display/scroll-area.tsx +31 -25
  215. package/docs/data-display/table.tsx +104 -45
  216. package/docs/data-display/timeline.tsx +41 -0
  217. package/docs/data-entry/date-picker.tsx +85 -0
  218. package/docs/data-entry/date-range-picker.tsx +26 -0
  219. package/docs/data-entry/form-dynamic-fields.tsx +199 -0
  220. package/docs/data-entry/form.tsx +74 -4
  221. package/docs/data-entry/input-otp.tsx +24 -0
  222. package/docs/data-entry/input.tsx +46 -1
  223. package/docs/data-entry/month-range-picker.tsx +1 -1
  224. package/docs/data-entry/select.tsx +27 -0
  225. package/docs/data-entry/switch.tsx +41 -0
  226. package/docs/data-entry/textarea.tsx +38 -0
  227. package/docs/data-entry/time-picker.tsx +85 -1
  228. package/docs/data-entry/time-range-picker.tsx +55 -0
  229. package/docs/data-entry/transfer.tsx +17 -0
  230. package/docs/data-entry/upload.tsx +56 -12
  231. package/docs/feedback/tooltip.tsx +1 -1
  232. package/docs/general/activity.tsx +2 -2
  233. package/docs/general/button/index.tsx +14 -1
  234. package/docs/general/typography.tsx +14 -1
  235. package/docs/layout/app-launcher.tsx +151 -0
  236. package/docs/layout/app-shell.tsx +11 -0
  237. package/docs/layout/flex.tsx +50 -0
  238. package/docs/layout/responsive-grid.tsx +21 -1
  239. package/docs/layout/topbar.tsx +5 -9
  240. package/docs/navigation/app-setting-picker.tsx +11 -0
  241. package/docs/navigation/breadcrumb.tsx +48 -0
  242. package/docs/navigation/dropdown-menu.tsx +21 -0
  243. package/docs/navigation/pagination.tsx +52 -0
  244. package/docs/navigation/steps.tsx +37 -0
  245. package/docs/navigation/tabs.tsx +97 -1
  246. package/docs/query/button-refetch.tsx +1 -0
  247. package/package.json +19 -2
  248. package/scripts/_agent-setup.mjs +19 -2
  249. package/scripts/init-guinea-pig.mjs +26 -4
  250. package/scripts/ui-audit.mjs +243 -30
package/README.md CHANGED
@@ -133,8 +133,15 @@ One token `--phi-unit` drives page/section/card spacing; micro control gaps use
133
133
 
134
134
  ```bash
135
135
  pnpm preview # preview app → http://localhost:6008 (fixed port, kills stale)
136
+ pnpm typecheck && pnpm lint && pnpm run audit # the cheap gates — run these freely
137
+ pnpm vitest run src/components/<group>/__tests__ --maxWorkers=2 # ONLY what you touched
138
+
139
+ # Heavy — CI's job. Run locally AT MOST ONCE, right before opening a PR, and never
140
+ # with other agents working on the same machine: `verify` builds the library AND the
141
+ # preview site AND runs all 506 test files. Measured with agents in parallel: 70 vitest
142
+ # workers, load average 90, an overheating laptop.
136
143
  pnpm preview:build # static build — also what deploys to GitHub Pages
137
- pnpm verify # typecheck · lint · format · the 5 guards · test
144
+ pnpm verify # typecheck · lint · format · the 5 guards · FULL suite
138
145
  pnpm release --ui <patch|minor|major> --mcp <…|skip> # publish lib + MCP in lockstep
139
146
  ```
140
147
 
@@ -174,5 +181,4 @@ This repo ships two packages — `@godxjp/ui` (this dir) and `@godxjp/ui-mcp` (`
174
181
  - [docs/COMPONENTS.md](./docs/COMPONENTS.md) · [docs/TOKENS.md](./docs/TOKENS.md) · [docs/SPACING.md](./docs/SPACING.md)
175
182
  - [docs/PROPS-VOCABULARY.md](./docs/PROPS-VOCABULARY.md) · [docs/PROPS-REGISTRY.md](./docs/PROPS-REGISTRY.md)
176
183
  - [docs/DATETIME.md](./docs/DATETIME.md) · [docs/FORMS.md](./docs/FORMS.md) · [docs/TESTING.md](./docs/TESTING.md)
177
- - **Architecture decisions** under `debate/*/04-Decision.md` (ADRs from the design debates)
178
184
  - MCP: **godxjp-ui-mcp** (`.mcp.json`) — live catalog for agents
@@ -43,3 +43,5 @@ export declare const isAppFontSize: (v: unknown) => v is AppFontSize;
43
43
  * `data-brand` so the app's own `--primary` token applies.
44
44
  */
45
45
  export declare function applyThemeAxes(el: HTMLElement, axes: Partial<AppThemeAxes>): void;
46
+ /** Apply an entity's action palette, including portals. Call the returned cleanup on scope exit. */
47
+ export declare function applyPrimaryColor(root: HTMLElement, color: string, foreground?: string | null): () => void;
@@ -39,12 +39,57 @@ function applyThemeAxes(el, axes) {
39
39
  else el.style.setProperty("--scaling", String(axes.scaling));
40
40
  }
41
41
  }
42
+ function applyPrimaryColor(root, color, foreground) {
43
+ const parse = (value) => {
44
+ if (!/^#[\da-f]{6}$/i.test(value)) return null;
45
+ return [1, 3, 5].map((offset) => parseInt(value.slice(offset, offset + 2), 16) / 255);
46
+ };
47
+ const rgb = parse(color);
48
+ if (!rgb) return () => {
49
+ };
50
+ const luminance = (channels) => channels.reduce(
51
+ (sum, value, index) => sum + (value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4) * [0.2126, 0.7152, 0.0722][index],
52
+ 0
53
+ );
54
+ const text = foreground && parse(foreground) || (luminance(rgb) > 0.179 ? [0, 0, 0] : [1, 1, 1]);
55
+ const hsl = (channels) => {
56
+ const [r, g, b] = channels;
57
+ const max = Math.max(r, g, b), min = Math.min(r, g, b);
58
+ const delta = max - min, lightness = (max + min) / 2;
59
+ const saturation = delta === 0 ? 0 : delta / (1 - Math.abs(2 * lightness - 1));
60
+ const hue = delta === 0 ? 0 : 60 * (max === r ? ((g - b) / delta + 6) % 6 : max === g ? (b - r) / delta + 2 : (r - g) / delta + 4);
61
+ return `${hue} ${saturation * 100}% ${lightness * 100}%`;
62
+ };
63
+ const away = luminance(text) > 0.5 ? 0 : 1;
64
+ const shade = (amount) => hsl(rgb.map((channel) => channel * (1 - amount) + away * amount));
65
+ const palette = {
66
+ "--primary": hsl(rgb),
67
+ "--primary-foreground": hsl(text),
68
+ "--primary-hover": shade(0.12),
69
+ "--primary-active": shade(0.24),
70
+ "--primary-border": hsl(rgb),
71
+ "--ring": hsl(rgb)
72
+ };
73
+ const previous = Object.keys(palette).map((key) => [
74
+ key,
75
+ root.style.getPropertyValue(key),
76
+ root.style.getPropertyPriority(key)
77
+ ]);
78
+ for (const [key, value] of Object.entries(palette)) root.style.setProperty(key, value);
79
+ return () => {
80
+ for (const [key, value, priority] of previous) {
81
+ if (value) root.style.setProperty(key, value, priority);
82
+ else root.style.removeProperty(key);
83
+ }
84
+ };
85
+ }
42
86
  export {
43
87
  APP_BRANDS,
44
88
  APP_DENSITIES,
45
89
  APP_FONT_SIZES,
46
90
  APP_THEMES,
47
91
  PREFERS_DARK_SCHEME_QUERY,
92
+ applyPrimaryColor,
48
93
  applyThemeAxes,
49
94
  isAppBrand,
50
95
  isAppDensity,
@@ -11,6 +11,7 @@ type CartesianChartProps = {
11
11
  series: ChartSeriesProp[];
12
12
  categoryKey: string;
13
13
  label: React.ReactNode;
14
+ showCaption?: boolean;
14
15
  description?: React.ReactNode;
15
16
  size?: "xs" | "sm" | "md" | "lg";
16
17
  height?: number;
@@ -27,5 +28,5 @@ type CartesianChartProps = {
27
28
  /** bar */
28
29
  horizontal?: boolean;
29
30
  };
30
- export declare function CartesianChart({ kind, data, series, categoryKey, label, description, size, height, showLegend, showGrid, numberFormat, emptyMessage, className, id, curved, stacked, horizontal, }: CartesianChartProps): React.JSX.Element;
31
+ export declare function CartesianChart({ kind, data, series, categoryKey, label, showCaption, description, size, height, showLegend, showGrid, numberFormat, emptyMessage, className, id, curved, stacked, horizontal, }: CartesianChartProps): React.JSX.Element;
31
32
  export {};
@@ -13,18 +13,22 @@ import {
13
13
  ResponsiveContainer,
14
14
  Tooltip,
15
15
  XAxis,
16
- YAxis
17
- } from "recharts";
16
+ YAxis,
17
+ assertRechartsPeer
18
+ } from "./recharts-peer.js";
19
+ import { cn } from "../../lib/utils.js";
18
20
  import { EmptyState } from "../data-display/empty-state.js";
19
21
  import { useTranslation } from "../../i18n/use-translation.js";
20
22
  import { ChartFrame, chartColor, chartHeight, useChartNumberFormat } from "./chart-frame.js";
21
23
  import { buildCartesianSummary } from "./chart-summary.js";
24
+ import { useCategoryAxisMetrics } from "./chart-category-axis.js";
22
25
  function CartesianChart({
23
26
  kind,
24
27
  data,
25
28
  series,
26
29
  categoryKey,
27
30
  label,
31
+ showCaption = true,
28
32
  description,
29
33
  size = "md",
30
34
  height,
@@ -38,6 +42,7 @@ function CartesianChart({
38
42
  stacked = false,
39
43
  horizontal = false
40
44
  }) {
45
+ assertRechartsPeer();
41
46
  const { t, locale } = useTranslation();
42
47
  const fmt = useChartNumberFormat(numberFormat);
43
48
  const list = React.useMemo(
@@ -56,6 +61,41 @@ function CartesianChart({
56
61
  const resolvedHeight = chartHeight(size, height);
57
62
  const tickFormatter = (value) => fmt.format(value);
58
63
  const curve = curved ? "monotone" : "linear";
64
+ const measuresCategoryAxis = kind === "bar" && horizontal;
65
+ const canvasRef = React.useRef(null);
66
+ const probeRef = React.useRef(null);
67
+ const categoryLabels = React.useMemo(
68
+ () => measuresCategoryAxis ? data.map((datum) => String(datum[categoryKey] ?? "")) : [],
69
+ [measuresCategoryAxis, data, categoryKey]
70
+ );
71
+ const categoryAxis = useCategoryAxisMetrics(
72
+ canvasRef,
73
+ probeRef,
74
+ categoryLabels,
75
+ measuresCategoryAxis && hasData
76
+ );
77
+ const categoryTick = (tickProps) => {
78
+ const full = String(tickProps.payload?.value ?? "");
79
+ const shown = categoryAxis.display(full);
80
+ return (
81
+ // `recharts-text` is what chart-layout.css styles the tick typography through; the rest of
82
+ // the class list is recharts' own (it hands `recharts-cartesian-axis-tick-value` in).
83
+ /* @__PURE__ */ jsxs(
84
+ "text",
85
+ {
86
+ className: cn("recharts-text", tickProps.className),
87
+ x: tickProps.x,
88
+ y: tickProps.y,
89
+ textAnchor: tickProps.textAnchor,
90
+ dominantBaseline: "central",
91
+ children: [
92
+ shown === full ? null : /* @__PURE__ */ jsx("title", { children: full }),
93
+ shown
94
+ ]
95
+ }
96
+ )
97
+ );
98
+ };
59
99
  const axes = (numberAxisVertical) => /* @__PURE__ */ jsxs(Fragment, { children: [
60
100
  showGrid ? /* @__PURE__ */ jsx(CartesianGrid, { strokeDasharray: "3 3", stroke: "hsl(var(--border))" }) : null,
61
101
  numberAxisVertical ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -95,7 +135,9 @@ function CartesianChart({
95
135
  dataKey: categoryKey,
96
136
  tickLine: false,
97
137
  stroke: "hsl(var(--muted-foreground))",
98
- fontSize: 12
138
+ fontSize: 12,
139
+ width: categoryAxis.width,
140
+ tick: categoryTick
99
141
  }
100
142
  )
101
143
  ] }),
@@ -158,6 +200,7 @@ function CartesianChart({
158
200
  ChartFrame,
159
201
  {
160
202
  label,
203
+ showCaption,
161
204
  description,
162
205
  summaryRows: summary.rows,
163
206
  imgSummary: summary.img,
@@ -165,6 +208,12 @@ function CartesianChart({
165
208
  height: resolvedHeight,
166
209
  className,
167
210
  id,
211
+ canvasRef,
212
+ canvasExtra: measuresCategoryAxis ? (
213
+ // Out-of-flow, hidden, and inside `.ui-chart` — so it inherits exactly the typography
214
+ // the axis ticks are painted in, including the `[lang="ja"]` face.
215
+ /* @__PURE__ */ jsx("span", { ref: probeRef, className: "ui-chart-axis-probe", "aria-hidden": "true" })
216
+ ) : null,
168
217
  children: hasData ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", minWidth: 0, children: chart() }) : /* @__PURE__ */ jsx(EmptyState, { title: emptyMessage ?? t("chart.empty") })
169
218
  }
170
219
  );
@@ -0,0 +1,55 @@
1
+ import * as React from "react";
2
+ /**
3
+ * Category-axis sizing for the HORIZONTAL bar chart (gh#409 · 1).
4
+ *
5
+ * recharts reserves a fixed 60px for the category axis whatever the ticks say, so a Japanese
6
+ * company name — 8–10 full-width glyphs, 100–125px — was painted OUTSIDE the chart box and the
7
+ * SVG clip cut it from the START, removing exactly the identifying `株式会社` prefix. Latin text
8
+ * degrades gracefully there; 全角 does not, and `docs/DESIGN-AUTHORITY.md` gives anything the user
9
+ * READS to the Japanese standard.
10
+ *
11
+ * The mechanism: measure every tick in the real rendered typography, size the axis from the
12
+ * WIDEST one, cap that at a token-owned fraction of the canvas so one long name can never eat
13
+ * the bars, and truncate anything past the cap at its END — the start, which identifies the row,
14
+ * always survives, and the full string stays reachable through the tick's `<title>` and the
15
+ * figure's screen-reader list.
16
+ *
17
+ * The two knobs are component tokens (`src/tokens/components/chart.css`). They are read back in
18
+ * px from real CSS properties on the probe element, because a custom property's computed value
19
+ * keeps whatever unit its author wrote.
20
+ */
21
+ /** Axis knobs resolved from the probe's computed style. `gap` is px, `maxFraction` unitless. */
22
+ export type CategoryAxisKnobs = {
23
+ /** Space between the tick text and the plot area. */
24
+ gap: number;
25
+ /** Share of the canvas the category axis may take, 0..1. */
26
+ maxFraction: number;
27
+ };
28
+ /** What the chart needs to draw the axis: its reserved width and the text of each tick. */
29
+ export type CategoryAxisMetrics = {
30
+ /** `width` for recharts' `YAxis`, or `undefined` while nothing has been measured yet. */
31
+ width: number | undefined;
32
+ /** Tick text to paint — the original label, or one truncated at its END. */
33
+ display: (label: string) => string;
34
+ };
35
+ /**
36
+ * Axis width from the measured tick widths: the widest tick, capped, plus the gap.
37
+ * Pure — the browser measurement is injected, so the rule is testable without a layout engine.
38
+ */
39
+ export declare function resolveCategoryAxisWidth(textWidths: readonly number[], available: number, { gap, maxFraction }: CategoryAxisKnobs): {
40
+ width: number;
41
+ textCap: number;
42
+ };
43
+ /**
44
+ * Cut `label` at its END until it fits `maxWidth`, appending an ellipsis. The first glyph is
45
+ * always kept: a tick that reads `…` identifies nothing, and a tick cut from the front is the
46
+ * bug this exists to fix.
47
+ */
48
+ export declare function truncateToWidth(label: string, maxWidth: number, measure: (text: string) => number): string;
49
+ /**
50
+ * Measure the category ticks in the live document and keep the axis sized to them.
51
+ *
52
+ * Re-runs when the canvas resizes and once `document.fonts` settles, because a CJK web face
53
+ * arriving after first paint changes every one of these widths.
54
+ */
55
+ export declare function useCategoryAxisMetrics(canvasRef: React.RefObject<HTMLDivElement | null>, probeRef: React.RefObject<HTMLSpanElement | null>, labels: readonly string[], enabled: boolean): CategoryAxisMetrics;
@@ -0,0 +1,93 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ const ELLIPSIS = "\u2026";
4
+ function resolveCategoryAxisWidth(textWidths, available, { gap, maxFraction }) {
5
+ const textCap = Math.max(0, available * maxFraction - gap);
6
+ const widest = textWidths.length > 0 ? Math.max(...textWidths) : 0;
7
+ return { width: Math.ceil(Math.min(widest, textCap) + gap), textCap };
8
+ }
9
+ function truncateToWidth(label, maxWidth, measure) {
10
+ if (measure(label) <= maxWidth) return label;
11
+ const glyphs = [...label];
12
+ for (let keep = glyphs.length - 1; keep > 1; keep -= 1) {
13
+ const candidate = glyphs.slice(0, keep).join("") + ELLIPSIS;
14
+ if (measure(candidate) <= maxWidth) return candidate;
15
+ }
16
+ return `${glyphs[0] ?? ""}${ELLIPSIS}`;
17
+ }
18
+ function readKnobs(probe) {
19
+ const style = getComputedStyle(probe);
20
+ const gap = Number.parseFloat(style.marginInlineEnd);
21
+ const maxFraction = Number.parseFloat(
22
+ style.getPropertyValue("--chart-category-axis-max-fraction")
23
+ );
24
+ return {
25
+ gap: Number.isFinite(gap) ? gap : 0,
26
+ maxFraction: Number.isFinite(maxFraction) ? maxFraction : 1
27
+ };
28
+ }
29
+ function sameTruncations(a, b) {
30
+ if (a.size !== b.size) return false;
31
+ for (const [key, value] of a) {
32
+ if (b.get(key) !== value) return false;
33
+ }
34
+ return true;
35
+ }
36
+ function useCategoryAxisMetrics(canvasRef, probeRef, labels, enabled) {
37
+ const [measured, setMeasured] = React.useState(void 0);
38
+ const labelsRef = React.useRef(labels);
39
+ labelsRef.current = labels;
40
+ const key = labels.join("\0");
41
+ React.useEffect(() => {
42
+ const canvas = canvasRef.current;
43
+ const probe = probeRef.current;
44
+ if (!enabled || !canvas || !probe) {
45
+ setMeasured(void 0);
46
+ return;
47
+ }
48
+ const ticks = labelsRef.current;
49
+ const measure = (text) => {
50
+ probe.textContent = text;
51
+ return probe.getBoundingClientRect().width;
52
+ };
53
+ const run = () => {
54
+ const available = canvas.clientWidth;
55
+ if (available <= 0) return;
56
+ const knobs = readKnobs(probe);
57
+ const widths = ticks.map(measure);
58
+ const { width, textCap } = resolveCategoryAxisWidth(widths, available, knobs);
59
+ const truncations2 = /* @__PURE__ */ new Map();
60
+ ticks.forEach((label, index) => {
61
+ if ((widths[index] ?? 0) > textCap) {
62
+ truncations2.set(label, truncateToWidth(label, textCap, measure));
63
+ }
64
+ });
65
+ probe.textContent = "";
66
+ setMeasured(
67
+ (previous) => previous && previous.width === width && sameTruncations(previous.truncations, truncations2) ? previous : { width, truncations: truncations2 }
68
+ );
69
+ };
70
+ run();
71
+ const observer = new ResizeObserver(run);
72
+ observer.observe(canvas);
73
+ let live = true;
74
+ void document.fonts?.ready.then(() => {
75
+ if (live) run();
76
+ });
77
+ return () => {
78
+ live = false;
79
+ observer.disconnect();
80
+ };
81
+ }, [canvasRef, probeRef, enabled, key]);
82
+ const truncations = measured?.truncations;
83
+ const display = React.useCallback(
84
+ (label) => truncations?.get(label) ?? label,
85
+ [truncations]
86
+ );
87
+ return { width: measured?.width, display };
88
+ }
89
+ export {
90
+ resolveCategoryAxisWidth,
91
+ truncateToWidth,
92
+ useCategoryAxisMetrics
93
+ };
@@ -18,6 +18,11 @@ export declare function useChartNumberFormat(options?: Intl.NumberFormatOptions)
18
18
  type ChartFrameProps = {
19
19
  /** Visible caption; also the accessible name via `aria-labelledby` → `<figcaption>`. */
20
20
  label: React.ReactNode;
21
+ /**
22
+ * Paint the caption. `false` keeps the `<figcaption>` in the DOM as `sr-only`, so
23
+ * `aria-labelledby` still names the figure — only the duplicate visible title goes away.
24
+ */
25
+ showCaption?: boolean;
21
26
  /** Extra context appended to the screen-reader description. */
22
27
  description?: React.ReactNode;
23
28
  /** Pre-formatted "category: value" rows for the screen-reader text alternative. */
@@ -39,11 +44,15 @@ type ChartFrameProps = {
39
44
  id?: string;
40
45
  children: React.ReactNode;
41
46
  ref?: React.Ref<HTMLElement>;
47
+ /** The plot box itself — charts that measure their own axis need to read its width. */
48
+ canvasRef?: React.Ref<HTMLDivElement>;
49
+ /** Rendered inside the canvas, after `children` (the axis measuring probe). */
50
+ canvasExtra?: React.ReactNode;
42
51
  };
43
52
  /**
44
53
  * The accessible figure wrapper. The chart body passed as `children` is wrapped
45
54
  * in a `role="img"` element with a short `aria-label`; the full data is exposed as
46
55
  * a sibling visually-hidden list referenced by the figure's `aria-describedby`.
47
56
  */
48
- export declare function ChartFrame({ label, description, summaryRows, imgSummary, hasData, height, size, footer, className, id, children, ref, }: ChartFrameProps): React.JSX.Element;
57
+ export declare function ChartFrame({ label, showCaption, description, summaryRows, imgSummary, hasData, height, size, footer, className, id, children, ref, canvasRef, canvasExtra, }: ChartFrameProps): React.JSX.Element;
49
58
  export {};
@@ -29,6 +29,7 @@ function useChartNumberFormat(options) {
29
29
  }
30
30
  function ChartFrame({
31
31
  label,
32
+ showCaption = true,
32
33
  description,
33
34
  summaryRows,
34
35
  imgSummary,
@@ -39,7 +40,9 @@ function ChartFrame({
39
40
  className,
40
41
  id,
41
42
  children,
42
- ref
43
+ ref,
44
+ canvasRef,
45
+ canvasExtra
43
46
  }) {
44
47
  const reactId = React.useId();
45
48
  const titleId = `${id ?? reactId}-title`;
@@ -55,8 +58,21 @@ function ChartFrame({
55
58
  "aria-labelledby": titleId,
56
59
  "aria-describedby": descId,
57
60
  children: [
58
- /* @__PURE__ */ jsx("figcaption", { id: titleId, className: "ui-chart-title", children: label }),
59
- /* @__PURE__ */ jsx("div", { className: "ui-chart-canvas", style: { height }, role: "img", "aria-label": imgSummary, children }),
61
+ /* @__PURE__ */ jsx("figcaption", { id: titleId, className: showCaption ? "ui-chart-title" : "sr-only", children: label }),
62
+ /* @__PURE__ */ jsxs(
63
+ "div",
64
+ {
65
+ ref: canvasRef,
66
+ className: "ui-chart-canvas",
67
+ style: { height },
68
+ role: "img",
69
+ "aria-label": imgSummary,
70
+ children: [
71
+ children,
72
+ canvasExtra
73
+ ]
74
+ }
75
+ ),
60
76
  footer ? /* @__PURE__ */ jsx("div", { className: "ui-chart-footer", children: footer }) : null,
61
77
  /* @__PURE__ */ jsxs("p", { id: descId, className: "sr-only", children: [
62
78
  description ? /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -12,4 +12,4 @@ export type { CompactBarTrendProp, CompactBarTrendProp as CompactBarTrendProps,
12
12
  * localized one-line summary, and a visually-hidden per-category value list referenced through
13
13
  * `aria-describedby` (WCAG 1.1.1).
14
14
  */
15
- export declare function CompactBarTrend({ data, categoryKey, valueKey, label, description, size, emphasizedIndex, showCategoryLabels, numberFormat, footer, emptyMessage, className, id, ref, }: CompactBarTrendProp): React.JSX.Element;
15
+ export declare function CompactBarTrend({ data, categoryKey, valueKey, label, showCaption, description, size, emphasizedIndex, showCategoryLabels, numberFormat, footer, emptyMessage, className, id, ref, }: CompactBarTrendProp): React.JSX.Element;
@@ -11,6 +11,7 @@ function CompactBarTrend({
11
11
  categoryKey,
12
12
  valueKey,
13
13
  label,
14
+ showCaption = true,
14
15
  description,
15
16
  size = "xs",
16
17
  emphasizedIndex,
@@ -40,6 +41,7 @@ function CompactBarTrend({
40
41
  {
41
42
  ref,
42
43
  label,
44
+ showCaption,
43
45
  description,
44
46
  summaryRows: summary.rows,
45
47
  imgSummary: summary.img,
@@ -5,4 +5,4 @@ export type { PieChartProp, PieChartProp as PieChartProps, } from "../../props/c
5
5
  * Import from the tree-shaken `@godxjp/ui/charts` entry; requires the `recharts`
6
6
  * optional peer dependency.
7
7
  */
8
- export declare function PieChart({ data, dataKey, nameKey, colors, label, description, size, height, showLegend, numberFormat, donut, emptyMessage, className, id, }: PieChartProp): import("react").JSX.Element;
8
+ export declare function PieChart({ data, dataKey, nameKey, colors, label, showCaption, description, size, height, showLegend, numberFormat, donut, emptyMessage, className, id, }: PieChartProp): import("react").JSX.Element;
@@ -1,6 +1,14 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
- import { Cell, Legend, Pie, PieChart as RPieChart, ResponsiveContainer, Tooltip } from "recharts";
3
+ import {
4
+ Cell,
5
+ Legend,
6
+ Pie,
7
+ PieChart as RPieChart,
8
+ ResponsiveContainer,
9
+ Tooltip,
10
+ assertRechartsPeer
11
+ } from "./recharts-peer.js";
4
12
  import { EmptyState } from "../data-display/empty-state.js";
5
13
  import { useTranslation } from "../../i18n/use-translation.js";
6
14
  import { ChartFrame, chartColor, chartHeight, useChartNumberFormat } from "./chart-frame.js";
@@ -11,6 +19,7 @@ function PieChart({
11
19
  nameKey,
12
20
  colors,
13
21
  label,
22
+ showCaption = true,
14
23
  description,
15
24
  size = "md",
16
25
  height,
@@ -21,6 +30,7 @@ function PieChart({
21
30
  className,
22
31
  id
23
32
  }) {
33
+ assertRechartsPeer();
24
34
  const { t } = useTranslation();
25
35
  const fmt = useChartNumberFormat(numberFormat);
26
36
  const hasData = data.length > 0;
@@ -36,6 +46,7 @@ function PieChart({
36
46
  ChartFrame,
37
47
  {
38
48
  label,
49
+ showCaption,
39
50
  description,
40
51
  summaryRows: summary.rows,
41
52
  imgSummary: summary.img,
@@ -0,0 +1,49 @@
1
+ /**
2
+ * The ONE module in `@godxjp/ui` that touches the optional `recharts` peer.
3
+ *
4
+ * WHY IT EXISTS (gh#409 · 6). `recharts` is declared optional in `peerDependenciesMeta`, so a
5
+ * bundler that cannot resolve it substitutes an empty stub, and EVERY named import of that stub
6
+ * becomes its own `[MISSING_EXPORT]` diagnostic. A consumer who had not installed recharts got 18
7
+ * of them, spread across `dist/components/charts/**`, none of which said what to install.
8
+ *
9
+ * Reading the peer through a NAMESPACE import removes those bindings — there is no named export
10
+ * left to be missing. ONE named import is kept on purpose: it is what still fails the BUILD (a
11
+ * silent build followed by a blank page at load is worse than a red build), and because the
12
+ * bundler prints the offending SOURCE LINE, its local alias is written as the remedy — so the
13
+ * single remaining diagnostic reads
14
+ *
15
+ * [MISSING_EXPORT] "ResponsiveContainer" is not exported by
16
+ * "__vite-optional-peer-dep:recharts:@godxjp/ui".
17
+ * ╭─[ …/dist/components/charts/recharts-peer.js:3:10 ]
18
+ * 3 │ import { ResponsiveContainer as install_recharts_or_use_charts_compact_bar_trend } from "recharts";
19
+ *
20
+ * which names the package, the fix, and the alternative that needs no peer at all.
21
+ *
22
+ * `assertRechartsPeer` covers the bundlers that resolve a missing optional peer to a silent empty
23
+ * object instead of a throwing stub: there the first chart to render says what is missing.
24
+ *
25
+ * Do NOT `import … from "recharts"` anywhere else in the library.
26
+ */
27
+ import * as peer from "recharts";
28
+ /** What a consumer has to do. Kept as one string so the message cannot drift between call sites. */
29
+ export declare const RECHARTS_PEER_MESSAGE: string;
30
+ /**
31
+ * Throw the remedy instead of an `undefined is not a component` render error. Called by each
32
+ * recharts-backed chart before it renders anything.
33
+ */
34
+ export declare function assertRechartsPeer(): void;
35
+ export declare const Area: <DataPointType = any, ValueAxisType = any>(props: peer.AreaProps<DataPointType, ValueAxisType>) => import("react").ReactElement, AreaChart: <DataPointType = any>(props: import("recharts/types/util/types").CartesianChartProps<DataPointType> & {
36
+ ref?: React.Ref<SVGSVGElement>;
37
+ }) => React.ReactElement, Bar: <DataPointType = any, ValueAxisType = any>(props: peer.BarProps<DataPointType, ValueAxisType>) => import("react").ReactElement, BarChart: <DataPointType = any>(props: import("recharts/types/util/types").CartesianChartProps<DataPointType> & {
38
+ ref?: React.Ref<SVGSVGElement>;
39
+ }) => React.ReactElement, CartesianGrid: typeof peer.CartesianGrid, Cell: import("react").FunctionComponent<peer.CellProps>, Legend: import("react").MemoExoticComponent<(outsideProps: peer.LegendProps) => React.ReactPortal | null>, Line: {
40
+ <DataPointType = any, ValueAxisType = any>(props: peer.LineProps<DataPointType, ValueAxisType>): import("react").ReactElement;
41
+ (props: peer.LineProps<any, any>): import("react").ReactElement;
42
+ }, LineChart: <DataPointType = any>(props: import("recharts/types/util/types").CartesianChartProps<DataPointType> & {
43
+ ref?: React.Ref<SVGSVGElement>;
44
+ }) => React.ReactElement, Pie: {
45
+ <DataPointType = any, DataValueType = any>(outsideProps: peer.PieProps<DataPointType, DataValueType>): import("react").ReactElement;
46
+ (outsideProps: peer.PieProps<any, any>): import("react").ReactElement;
47
+ }, PieChart: <DataPointType = any>(props: import("recharts/types/util/types").PolarChartProps<DataPointType> & {
48
+ ref?: React.Ref<SVGSVGElement>;
49
+ }) => React.ReactElement, ResponsiveContainer: import("react").ForwardRefExoticComponent<peer.ResponsiveContainerProps & import("react").RefAttributes<HTMLDivElement>>, Tooltip: typeof peer.Tooltip, XAxis: <DataPointType = any, DataValueType = any>(props: peer.XAxisProps<DataPointType, DataValueType>) => import("react").ReactElement, YAxis: <DataPointType = any, DataValueType = any>(props: peer.YAxisProps<DataPointType, DataValueType>) => import("react").ReactElement;
@@ -0,0 +1,45 @@
1
+ "use client";
2
+ import * as peer from "recharts";
3
+ import { ResponsiveContainer as install_recharts_or_use_charts_compact_bar_trend } from "recharts";
4
+ const RECHARTS_PEER_MESSAGE = '@godxjp/ui charts need the optional peer dependency "recharts" (^2.13.0 || ^3.0.0), which is not installed. Install it \u2014 `pnpm add recharts` / `npm i recharts` \u2014 or, for a dependency-free trend mark, import { CompactBarTrend } from "@godxjp/ui/charts/compact-bar-trend".';
5
+ function assertRechartsPeer() {
6
+ if (typeof install_recharts_or_use_charts_compact_bar_trend === "undefined") {
7
+ throw new Error(RECHARTS_PEER_MESSAGE);
8
+ }
9
+ }
10
+ const {
11
+ Area,
12
+ AreaChart,
13
+ Bar,
14
+ BarChart,
15
+ CartesianGrid,
16
+ Cell,
17
+ Legend,
18
+ Line,
19
+ LineChart,
20
+ Pie,
21
+ PieChart,
22
+ ResponsiveContainer,
23
+ Tooltip,
24
+ XAxis,
25
+ YAxis
26
+ } = peer;
27
+ export {
28
+ Area,
29
+ AreaChart,
30
+ Bar,
31
+ BarChart,
32
+ CartesianGrid,
33
+ Cell,
34
+ Legend,
35
+ Line,
36
+ LineChart,
37
+ Pie,
38
+ PieChart,
39
+ RECHARTS_PEER_MESSAGE,
40
+ ResponsiveContainer,
41
+ Tooltip,
42
+ XAxis,
43
+ YAxis,
44
+ assertRechartsPeer
45
+ };
@@ -1,3 +1,4 @@
1
+ import type { GapProp, PadProp, PadRawProp } from "../../props/vocabulary/index.js";
1
2
  import * as React from "react";
2
3
  import type { LucideIcon } from "lucide-react";
3
4
  import type { HeadingLevelProp } from "../../props/vocabulary/index.js";
@@ -97,6 +98,11 @@ export declare const CardFooter: React.ForwardRefExoticComponent<React.HTMLAttri
97
98
  flush?: boolean;
98
99
  } & React.RefAttributes<HTMLDivElement>>;
99
100
  export type CardBarProps = React.HTMLAttributes<HTMLDivElement> & {
101
+ pad?: PadProp;
102
+ padRaw?: PadRawProp;
103
+ gap?: GapProp;
104
+ surface?: "muted";
105
+ border?: "none" | "block-start" | "block-end" | "both";
100
106
  /** Right-aligned actions slot (settings/save), Ant `tabBarExtraContent`-style. */
101
107
  extra?: React.ReactNode;
102
108
  };
@@ -107,6 +113,11 @@ export type CardBarProps = React.HTMLAttributes<HTMLDivElement> & {
107
113
  * border is enough).
108
114
  */
109
115
  export declare const CardBar: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & {
116
+ pad?: PadProp;
117
+ padRaw?: PadRawProp;
118
+ gap?: GapProp;
119
+ surface?: "muted";
120
+ border?: "none" | "block-start" | "block-end" | "both";
110
121
  /** Right-aligned actions slot (settings/save), Ant `tabBarExtraContent`-style. */
111
122
  extra?: React.ReactNode;
112
123
  } & React.RefAttributes<HTMLDivElement>>;
@@ -134,6 +145,6 @@ export type StatCardProps = React.HTMLAttributes<HTMLDivElement> & {
134
145
  };
135
146
  /** KPI / stat tile — token-driven layout aligned to dashboard KPI cards. */
136
147
  export declare function StatCard({ label, value, hint, icon: Icon, delta, layout, align, inverse, accent, accentPlacement, className, ...props }: StatCardProps): React.JSX.Element;
137
- /** Header actions slot — pair with `CardHeader className="flex flex-row …"`. */
148
+ /** Header actions slot — compose beside CardTitle inside CardHeader; the header owns positioning. */
138
149
  export declare const CardAction: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLDivElement> & React.RefAttributes<HTMLDivElement>>;
139
150
  export {};