@godxjp/ui 33.15.0 → 33.17.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 (88) hide show
  1. package/README.md +4 -4
  2. package/agent/START-HERE.md +7 -7
  3. package/agent/components/AppShell.json +1 -1
  4. package/agent/components/AreaChart.json +6 -0
  5. package/agent/components/BarChart.json +6 -0
  6. package/agent/components/Card.json +1 -1
  7. package/agent/components/DashboardGrid.json +76 -0
  8. package/agent/components/DataTable.json +1 -1
  9. package/agent/components/Heading.json +6 -0
  10. package/agent/components/HeatmapChart.json +6 -0
  11. package/agent/components/LineChart.json +6 -0
  12. package/agent/components/Pagination.json +1 -1
  13. package/agent/components/PieChart.json +6 -0
  14. package/agent/components/Table.json +2 -1
  15. package/agent/components-index.json +9 -3
  16. package/agent/components.json +118 -5
  17. package/agent/index.json +8 -8
  18. package/agent/llms.txt +6 -6
  19. package/agent/rules.json +1 -1
  20. package/agent/tokens.json +74 -2
  21. package/agent/vocabulary.json +12 -0
  22. package/dist/components/charts/chart-cartesian.d.ts +3 -2
  23. package/dist/components/charts/chart-cartesian.js +146 -46
  24. package/dist/components/charts/chart-datum-selection.d.ts +15 -0
  25. package/dist/components/charts/chart-datum-selection.js +97 -0
  26. package/dist/components/charts/chart-frame.d.ts +2 -1
  27. package/dist/components/charts/chart-frame.js +2 -1
  28. package/dist/components/charts/heatmap-chart.d.ts +1 -1
  29. package/dist/components/charts/heatmap-chart.js +20 -0
  30. package/dist/components/charts/index.d.ts +1 -1
  31. package/dist/components/charts/pie-chart.d.ts +2 -1
  32. package/dist/components/charts/pie-chart.js +36 -1
  33. package/dist/components/charts/recharts-peer.d.ts +1 -1
  34. package/dist/components/charts/recharts-peer.js +6 -0
  35. package/dist/components/data-display/index.d.ts +1 -1
  36. package/dist/components/data-display/index.js +10 -1
  37. package/dist/components/data-display/table.d.ts +9 -0
  38. package/dist/components/data-display/table.js +3 -0
  39. package/dist/components/general/typography.d.ts +1 -0
  40. package/dist/components/general/typography.js +3 -0
  41. package/dist/contracts/measurement.json +1 -1
  42. package/dist/i18n/messages/en.json +10 -0
  43. package/dist/i18n/messages/ja.json +10 -0
  44. package/dist/i18n/messages/vi.json +10 -0
  45. package/dist/lab/dashboard-grid-layout.d.ts +4 -0
  46. package/dist/lab/dashboard-grid-layout.js +36 -0
  47. package/dist/lab/dashboard-grid.d.ts +5 -0
  48. package/dist/lab/dashboard-grid.js +258 -0
  49. package/dist/lab/index.d.ts +2 -0
  50. package/dist/lab/index.js +2 -0
  51. package/dist/lib/hooks.d.ts +8 -0
  52. package/dist/lib/hooks.js +20 -21
  53. package/dist/props/components/charts.prop.d.ts +11 -0
  54. package/dist/props/components/dashboard-grid.prop.d.ts +27 -0
  55. package/dist/props/components/dashboard-grid.prop.js +0 -0
  56. package/dist/props/components/general.prop.d.ts +8 -1
  57. package/dist/props/components/index.d.ts +1 -1
  58. package/dist/props/registry.d.ts +89 -6
  59. package/dist/props/registry.js +85 -0
  60. package/dist/props/vocabulary/index.d.ts +1 -1
  61. package/dist/props/vocabulary/interaction.prop.d.ts +6 -0
  62. package/dist/styles/badge-layout.css +1 -0
  63. package/dist/styles/base.css +8 -0
  64. package/dist/styles/card-layout.css +8 -0
  65. package/dist/styles/chart-layout.css +28 -0
  66. package/dist/styles/control.css +82 -0
  67. package/dist/styles/dialog-layout.css +7 -3
  68. package/dist/styles/layers.json +10 -1
  69. package/dist/styles/layout.css +2 -0
  70. package/dist/styles/logo-layout.css +18 -12
  71. package/dist/styles/navigation-layout.css +2 -0
  72. package/dist/styles/shell-layout.css +40 -1
  73. package/dist/styles/table-layout.css +30 -0
  74. package/dist/styles/text-layout.css +5 -0
  75. package/dist/styles.css +1 -1
  76. package/dist/theme/famgia.service.css +10 -8
  77. package/dist/themes/godx-tokens.css +20 -16
  78. package/dist/tokens/base.css +2 -0
  79. package/dist/tokens/components/dashboard-grid.css +14 -0
  80. package/dist/tokens/components/feedback.css +2 -0
  81. package/dist/tokens/components/logo.css +6 -4
  82. package/dist/tokens/components/table.css +5 -1
  83. package/dist/tokens/derived.css +28 -24
  84. package/dist/tokens/foundation.css +62 -56
  85. package/dist/tokens/semantic/layout.css +5 -3
  86. package/docs/COMPOSITION-VS-COMPONENT.md +15 -14
  87. package/docs/TOKENS.md +33 -0
  88. package/package.json +2 -2
@@ -2,6 +2,8 @@
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import {
5
+ Dot,
6
+ Rectangle,
5
7
  Area,
6
8
  AreaChart as RAreaChart,
7
9
  Bar,
@@ -22,6 +24,7 @@ import { useTranslation } from "../../i18n/use-translation.js";
22
24
  import { ChartFrame, chartColor, chartHeight, useChartNumberFormat } from "./chart-frame.js";
23
25
  import { buildCartesianSummary } from "./chart-summary.js";
24
26
  import { useCategoryAxisMetrics } from "./chart-category-axis.js";
27
+ import { useChartDatumSelection } from "./chart-datum-selection.js";
25
28
  import { listFormat } from "../../lib/intl-cache.js";
26
29
  function CartesianChart({
27
30
  kind,
@@ -40,6 +43,7 @@ function CartesianChart({
40
43
  numberFormat,
41
44
  emptyMessage,
42
45
  onCategoryClick,
46
+ onDatumSelect,
43
47
  className,
44
48
  id,
45
49
  curved = false,
@@ -51,6 +55,71 @@ function CartesianChart({
51
55
  const { t, locale } = useTranslation();
52
56
  const fmt = useChartNumberFormat(numberFormat);
53
57
  const list = React.useMemo(() => listFormat(locale, { style: "narrow", type: "unit" }), [locale]);
58
+ const points = series.flatMap(
59
+ (s, seriesIndex) => data.flatMap(
60
+ (datum, index) => typeof datum[s.dataKey] === "number" && Number.isFinite(datum[s.dataKey]) ? [{ index, series: seriesIndex }] : []
61
+ )
62
+ );
63
+ const markProps = useChartDatumSelection(points);
64
+ const mark = (seriesIndex, shape, raw) => {
65
+ const s = series[seriesIndex];
66
+ const props = raw;
67
+ const { x = 0, y = 0, width = 0, height: height2 = 0 } = props;
68
+ const index = props.index;
69
+ const datum = data[index];
70
+ if (!datum || typeof datum[s.dataKey] !== "number" || !Number.isFinite(datum[s.dataKey]))
71
+ return /* @__PURE__ */ jsx("g", {});
72
+ const action = markProps(
73
+ index,
74
+ seriesIndex,
75
+ t("chart.datumLabel", {
76
+ label: String(datum[categoryKey] ?? ""),
77
+ series: s.label ?? s.dataKey,
78
+ value: fmt.format(Number(datum[s.dataKey]))
79
+ }),
80
+ () => onDatumSelect?.(datum, { seriesKey: s.dataKey, index })
81
+ );
82
+ return shape === "bar" ? /* @__PURE__ */ jsxs("g", { ...action, children: [
83
+ /* @__PURE__ */ jsx(
84
+ "rect",
85
+ {
86
+ x: Math.min(x, x + width),
87
+ y: Math.min(y, y + height2),
88
+ width: Math.abs(width),
89
+ height: Math.abs(height2),
90
+ className: "ui-chart-bar-hit",
91
+ fill: "transparent",
92
+ style: {
93
+ "--chart-mark-width": `${Math.abs(width)}px`,
94
+ "--chart-mark-height": `${Math.abs(height2)}px`
95
+ }
96
+ }
97
+ ),
98
+ /* @__PURE__ */ jsx(
99
+ Rectangle,
100
+ {
101
+ x: props.x,
102
+ y: props.y,
103
+ width: props.width,
104
+ height: props.height,
105
+ fill: props.fill,
106
+ radius: 2
107
+ }
108
+ )
109
+ ] }) : /* @__PURE__ */ jsxs("g", { ...action, children: [
110
+ /* @__PURE__ */ jsx("circle", { cx: props.cx, cy: props.cy, className: "ui-chart-point-hit", fill: "transparent" }),
111
+ /* @__PURE__ */ jsx(Dot, { cx: props.cx, cy: props.cy, fill: chartColor(seriesIndex, s.color) })
112
+ ] });
113
+ };
114
+ const markRef = React.useRef(mark);
115
+ markRef.current = mark;
116
+ const shapes = React.useMemo(
117
+ () => Array.from({ length: series.length }, (_, i) => ({
118
+ bar: (raw) => markRef.current(i, "bar", raw),
119
+ dot: (raw) => markRef.current(i, "dot", raw)
120
+ })),
121
+ [series.length]
122
+ );
54
123
  const hasData = data.length > 0 && series.length > 0;
55
124
  const summary = buildCartesianSummary(
56
125
  data,
@@ -161,59 +230,90 @@ function CartesianChart({
161
230
  const onBarClick = onCategoryClick ? (_entry, index) => pickCategory(index) : void 0;
162
231
  const chart = () => {
163
232
  if (kind === "line") {
164
- return /* @__PURE__ */ jsxs(RLineChart, { data, onClick: onChartClick, children: [
165
- axes(true),
166
- series.map((s, i) => /* @__PURE__ */ jsx(
167
- Line,
168
- {
169
- type: curve,
170
- dataKey: s.dataKey,
171
- name: s.label ?? s.dataKey,
172
- stroke: chartColor(i, s.color),
173
- dot: showDots
174
- },
175
- s.dataKey
176
- ))
177
- ] });
233
+ return /* @__PURE__ */ jsxs(
234
+ RLineChart,
235
+ {
236
+ accessibilityLayer: onDatumSelect ? false : void 0,
237
+ data,
238
+ onClick: onChartClick,
239
+ children: [
240
+ axes(true),
241
+ series.map((s, i) => /* @__PURE__ */ jsx(
242
+ Line,
243
+ {
244
+ type: curve,
245
+ dataKey: s.dataKey,
246
+ name: s.label ?? s.dataKey,
247
+ stroke: chartColor(i, s.color),
248
+ dot: onDatumSelect ? shapes[i].dot : showDots,
249
+ activeDot: onDatumSelect ? false : void 0,
250
+ isAnimationActive: onDatumSelect ? false : void 0
251
+ },
252
+ s.dataKey
253
+ ))
254
+ ]
255
+ }
256
+ );
178
257
  }
179
258
  if (kind === "area") {
180
- return /* @__PURE__ */ jsxs(RAreaChart, { data, onClick: onChartClick, children: [
181
- axes(true),
182
- series.map((s, i) => /* @__PURE__ */ jsx(
183
- Area,
184
- {
185
- type: curve,
186
- dataKey: s.dataKey,
187
- name: s.label ?? s.dataKey,
188
- stackId: stacked ? "stack" : void 0,
189
- stroke: chartColor(i, s.color),
190
- fill: s.fillColor ?? chartColor(i, s.color),
191
- dot: showDots
192
- },
193
- s.dataKey
194
- ))
195
- ] });
196
- }
197
- return /* @__PURE__ */ jsxs(RBarChart, { data, layout: horizontal ? "vertical" : "horizontal", children: [
198
- axes(!horizontal),
199
- series.map((s, i) => /* @__PURE__ */ jsx(
200
- Bar,
259
+ return /* @__PURE__ */ jsxs(
260
+ RAreaChart,
201
261
  {
202
- dataKey: s.dataKey,
203
- name: s.label ?? s.dataKey,
204
- stackId: stacked ? "stack" : void 0,
205
- fill: chartColor(i, s.color),
206
- radius: 2,
207
- onClick: onBarClick,
208
- background: onCategoryClick ? { fill: "transparent" } : void 0
209
- },
210
- s.dataKey
211
- ))
212
- ] });
262
+ accessibilityLayer: onDatumSelect ? false : void 0,
263
+ data,
264
+ onClick: onChartClick,
265
+ children: [
266
+ axes(true),
267
+ series.map((s, i) => /* @__PURE__ */ jsx(
268
+ Area,
269
+ {
270
+ type: curve,
271
+ dataKey: s.dataKey,
272
+ name: s.label ?? s.dataKey,
273
+ stackId: stacked ? "stack" : void 0,
274
+ stroke: chartColor(i, s.color),
275
+ fill: s.fillColor ?? chartColor(i, s.color),
276
+ dot: onDatumSelect ? shapes[i].dot : showDots,
277
+ activeDot: onDatumSelect ? false : void 0,
278
+ isAnimationActive: onDatumSelect ? false : void 0
279
+ },
280
+ s.dataKey
281
+ ))
282
+ ]
283
+ }
284
+ );
285
+ }
286
+ return /* @__PURE__ */ jsxs(
287
+ RBarChart,
288
+ {
289
+ accessibilityLayer: onDatumSelect ? false : void 0,
290
+ data,
291
+ layout: horizontal ? "vertical" : "horizontal",
292
+ children: [
293
+ axes(!horizontal),
294
+ series.map((s, i) => /* @__PURE__ */ jsx(
295
+ Bar,
296
+ {
297
+ dataKey: s.dataKey,
298
+ name: s.label ?? s.dataKey,
299
+ stackId: stacked ? "stack" : void 0,
300
+ fill: chartColor(i, s.color),
301
+ radius: 2,
302
+ shape: onDatumSelect ? shapes[i].bar : void 0,
303
+ isAnimationActive: onDatumSelect ? false : void 0,
304
+ onClick: onBarClick,
305
+ background: onCategoryClick ? { fill: "transparent" } : void 0
306
+ },
307
+ s.dataKey
308
+ ))
309
+ ]
310
+ }
311
+ );
213
312
  };
214
313
  return /* @__PURE__ */ jsx(
215
314
  ChartFrame,
216
315
  {
316
+ interactive: !!onDatumSelect,
217
317
  label,
218
318
  showCaption,
219
319
  description,
@@ -0,0 +1,15 @@
1
+ import * as React from "react";
2
+ /** Roving mark focus. Geometry stays in recharts; data identity stays in the original array. */
3
+ export declare function useChartDatumSelection(points: {
4
+ index: number;
5
+ series: number;
6
+ }[]): (index: number, series: number, name: string, select: () => void) => {
7
+ role: string;
8
+ "aria-label": string;
9
+ "data-chart-datum": string;
10
+ className: string;
11
+ tabIndex: number;
12
+ onFocus: (event: React.FocusEvent<SVGElement>) => void;
13
+ onClick: (event: React.MouseEvent<SVGElement>) => void;
14
+ onKeyDown: (event: React.KeyboardEvent<SVGElement>) => void;
15
+ };
@@ -0,0 +1,97 @@
1
+ "use client";
2
+ import * as React from "react";
3
+ function useChartDatumSelection(points) {
4
+ const active = React.useRef("");
5
+ const recovery = React.useRef(null);
6
+ React.useEffect(() => () => recovery.current?.(), []);
7
+ const key = (index, series) => `${series}:${index}`;
8
+ const keys = points.map((p) => key(p.index, p.series));
9
+ const current = keys.includes(active.current) ? active.current : keys[0];
10
+ const activate = (node, select) => {
11
+ const canvas = node.closest(".ui-chart-canvas");
12
+ const identity = node.dataset.chartDatum;
13
+ recovery.current?.();
14
+ let focused = node;
15
+ const stop = () => {
16
+ observer.disconnect();
17
+ document.removeEventListener("keydown", stop, true);
18
+ document.removeEventListener("pointerdown", stop, true);
19
+ document.removeEventListener("focusin", onFocusIn, true);
20
+ };
21
+ const onFocusIn = (event) => {
22
+ if (event.target !== focused) stop();
23
+ };
24
+ const observer = new MutationObserver(() => {
25
+ if (focused.isConnected || !canvas?.isConnected || document.activeElement !== document.body)
26
+ return;
27
+ const replacement = Array.from(
28
+ canvas.querySelectorAll("[data-chart-datum]")
29
+ ).find((mark) => mark.dataset.chartDatum === identity);
30
+ if (replacement) {
31
+ focused = replacement;
32
+ replacement.focus();
33
+ }
34
+ });
35
+ recovery.current = stop;
36
+ if (canvas) observer.observe(canvas, { childList: true, subtree: true });
37
+ document.addEventListener("keydown", stop, true);
38
+ document.addEventListener("pointerdown", stop, true);
39
+ document.addEventListener("focusin", onFocusIn, true);
40
+ select();
41
+ };
42
+ return (index, series, name, select) => ({
43
+ role: "button",
44
+ "aria-label": name,
45
+ "data-chart-datum": key(index, series),
46
+ className: "ui-chart-datum ui-focus-ring",
47
+ tabIndex: current === key(index, series) ? 0 : -1,
48
+ onFocus: (event) => {
49
+ event.stopPropagation();
50
+ active.current = key(index, series);
51
+ event.currentTarget.closest(".ui-chart-canvas")?.querySelectorAll("[data-chart-datum]").forEach((node) => {
52
+ node.tabIndex = node === event.currentTarget ? 0 : -1;
53
+ });
54
+ },
55
+ onClick: (event) => {
56
+ event.stopPropagation();
57
+ event.currentTarget.focus();
58
+ activate(event.currentTarget, select);
59
+ },
60
+ onKeyDown: (event) => {
61
+ if (event.key === "Enter" || event.key === " ") {
62
+ event.preventDefault();
63
+ event.stopPropagation();
64
+ if (!event.repeat) activate(event.currentTarget, select);
65
+ return;
66
+ }
67
+ const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
68
+ let target;
69
+ const along = event.key === "ArrowRight" ? rtl ? -1 : 1 : event.key === "ArrowLeft" ? rtl ? 1 : -1 : 0;
70
+ const across = event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0;
71
+ if (along) {
72
+ const row = points.filter((p) => p.series === series);
73
+ const at = row.findIndex((p) => p.index === index);
74
+ target = row[Math.max(0, Math.min(row.length - 1, at + along))];
75
+ } else if (across) {
76
+ const column = points.filter((p) => p.index === index);
77
+ const at = column.findIndex((p) => p.series === series);
78
+ target = column[Math.max(0, Math.min(column.length - 1, at + across))];
79
+ } else if (event.key === "Home" || event.key === "End") {
80
+ const row = event.ctrlKey || event.metaKey ? points : points.filter((p) => p.series === series);
81
+ target = event.key === "Home" ? row[0] : row.at(-1);
82
+ } else return;
83
+ event.preventDefault();
84
+ event.stopPropagation();
85
+ if (!target) return;
86
+ const next = key(target.index, target.series);
87
+ const canvas = event.currentTarget.closest(".ui-chart-canvas");
88
+ const mark = Array.from(
89
+ canvas?.querySelectorAll("[data-chart-datum]") ?? []
90
+ ).find((node) => node.dataset.chartDatum === next);
91
+ mark?.focus();
92
+ }
93
+ });
94
+ }
95
+ export {
96
+ useChartDatumSelection
97
+ };
@@ -16,6 +16,7 @@ export declare function chartHeight(size?: "xs" | "sm" | "md" | "lg", height?: n
16
16
  /** A locale-bound number formatter for the active app locale. */
17
17
  export declare function useChartNumberFormat(options?: Intl.NumberFormatOptions): Intl.NumberFormat;
18
18
  type ChartFrameProps = {
19
+ interactive?: boolean;
19
20
  /** Visible caption; also the accessible name via `aria-labelledby` → `<figcaption>`. */
20
21
  label: React.ReactNode;
21
22
  /**
@@ -54,5 +55,5 @@ type ChartFrameProps = {
54
55
  * in a `role="img"` element with a short `aria-label`; the full data is exposed as
55
56
  * a sibling visually-hidden list referenced by the figure's `aria-describedby`.
56
57
  */
57
- export declare function ChartFrame({ label, showCaption, description, summaryRows, imgSummary, hasData, height, size, footer, className, id, children, ref, canvasRef, canvasExtra, }: ChartFrameProps): React.JSX.Element;
58
+ export declare function ChartFrame({ interactive, label, showCaption, description, summaryRows, imgSummary, hasData, height, size, footer, className, id, children, ref, canvasRef, canvasExtra, }: ChartFrameProps): React.JSX.Element;
58
59
  export {};
@@ -29,6 +29,7 @@ function useChartNumberFormat(options) {
29
29
  return React.useMemo(() => numberFormat(locale, options), [locale, options]);
30
30
  }
31
31
  function ChartFrame({
32
+ interactive = false,
32
33
  label,
33
34
  showCaption = true,
34
35
  description,
@@ -66,7 +67,7 @@ function ChartFrame({
66
67
  ref: canvasRef,
67
68
  className: "ui-chart-canvas",
68
69
  style: { height },
69
- role: "img",
70
+ role: interactive ? "group" : "img",
70
71
  "aria-label": imgSummary,
71
72
  children: [
72
73
  children,
@@ -16,4 +16,4 @@ export type { HeatmapChartProp, HeatmapChartProp as HeatmapChartProps, HeatmapCa
16
16
  * - **Narrow screens** — the matrix never shrinks a cell below its `size` tier; it scrolls inside
17
17
  * its own box with the row header pinned, so the page never scrolls sideways.
18
18
  */
19
- export declare function HeatmapChart({ rows, columns, data, label, showCaption, description, size, numberFormat, topN, showLegend, emptyMessage, className, id, ref, }: HeatmapChartProp): React.JSX.Element;
19
+ export declare function HeatmapChart({ onDatumSelect, rows, columns, data, label, showCaption, description, size, numberFormat, topN, showLegend, emptyMessage, className, id, ref, }: HeatmapChartProp): React.JSX.Element;
@@ -11,6 +11,7 @@ const OPEN_DELAY = 200;
11
11
  const CLOSE_GRACE = 100;
12
12
  const RAMP = Array.from({ length: HEAT_STEPS }, (_, index) => index + 1);
13
13
  function HeatmapChart({
14
+ onDatumSelect,
14
15
  rows,
15
16
  columns,
16
17
  data,
@@ -91,6 +92,12 @@ function HeatmapChart({
91
92
  reveal(cell);
92
93
  }
93
94
  };
95
+ const selectCell = (r, c) => {
96
+ const index = data.findIndex(
97
+ (datum) => String(datum.row) === String(rows[r]?.key) && String(datum.column) === String(columns[c]?.key) && typeof datum.value === "number" && Number.isFinite(datum.value)
98
+ );
99
+ if (index >= 0) onDatumSelect?.(data[index], { seriesKey: String(columns[c].key), index });
100
+ };
94
101
  const onGridKeyDown = (event) => {
95
102
  const rtl = getComputedStyle(event.currentTarget).direction === "rtl";
96
103
  const lastRow = rows.length - 1;
@@ -193,6 +200,19 @@ function HeatmapChart({
193
200
  },
194
201
  tabIndex: r === activeRow && c === activeColumn ? 0 : -1,
195
202
  className: "ui-chart-heatmap-cell ui-focus-ring",
203
+ "data-interactive": onDatumSelect && !empty ? "" : void 0,
204
+ "aria-label": onDatumSelect ? t("chart.heatmapCell", {
205
+ row: row.label,
206
+ column: column.label,
207
+ value: text.value
208
+ }) : void 0,
209
+ onKeyDown: onDatumSelect ? (event) => {
210
+ if (event.key !== "Enter" && event.key !== " ") return;
211
+ event.preventDefault();
212
+ event.stopPropagation();
213
+ if (!event.repeat && !empty) selectCell(r, c);
214
+ } : void 0,
215
+ onClick: onDatumSelect && !empty ? () => selectCell(r, c) : void 0,
196
216
  "data-step": empty ? void 0 : model.steps.get(key),
197
217
  "data-empty": empty ? "" : void 0,
198
218
  "data-emphasized": model.ranks.has(key) ? "" : void 0,
@@ -16,5 +16,5 @@ export { AreaChart } from "./area-chart.js";
16
16
  export type { AreaChartProp, AreaChartProps } from "./area-chart.js";
17
17
  export { PieChart } from "./pie-chart.js";
18
18
  export type { PieChartProp, PieChartProps } from "./pie-chart.js";
19
- export type { ChartSeriesProp, ChartDatum } from "../../props/components/charts.prop.js";
19
+ export type { ChartSeriesProp, ChartDatum, ChartDatumSelect } from "../../props/components/charts.prop.js";
20
20
  export { CHART_COLORS } from "./chart-frame.js";
@@ -1,3 +1,4 @@
1
+ import * as React from "react";
1
2
  import type { PieChartProp } from "../../props/components/charts.prop.js";
2
3
  export type { PieChartProp, PieChartProp as PieChartProps, } from "../../props/components/charts.prop.js";
3
4
  /**
@@ -5,4 +6,4 @@ export type { PieChartProp, PieChartProp as PieChartProps, } from "../../props/c
5
6
  * Import from the tree-shaken `@godxjp/ui/charts` entry; requires the `recharts`
6
7
  * optional peer dependency.
7
8
  */
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;
9
+ export declare function PieChart({ onDatumSelect, data, dataKey, nameKey, colors, label, showCaption, description, size, height, showLegend, numberFormat, donut, emptyMessage, className, id, }: PieChartProp): React.JSX.Element;
@@ -1,7 +1,9 @@
1
1
  "use client";
2
2
  import { jsx, jsxs } from "react/jsx-runtime";
3
+ import * as React from "react";
3
4
  import {
4
5
  Cell,
6
+ Sector,
5
7
  Legend,
6
8
  Pie,
7
9
  PieChart as RPieChart,
@@ -12,8 +14,10 @@ import {
12
14
  import { EmptyState } from "../data-display/empty-state.js";
13
15
  import { useTranslation } from "../../i18n/use-translation.js";
14
16
  import { ChartFrame, chartColor, chartHeight, useChartNumberFormat } from "./chart-frame.js";
17
+ import { useChartDatumSelection } from "./chart-datum-selection.js";
15
18
  import { buildPieSummary } from "./chart-summary.js";
16
19
  function PieChart({
20
+ onDatumSelect,
17
21
  data,
18
22
  dataKey,
19
23
  nameKey,
@@ -33,6 +37,34 @@ function PieChart({
33
37
  assertRechartsPeer();
34
38
  const { t } = useTranslation();
35
39
  const fmt = useChartNumberFormat(numberFormat);
40
+ const points = data.flatMap(
41
+ (datum, index) => typeof datum[dataKey] === "number" && Number.isFinite(datum[dataKey]) && Number(datum[dataKey]) > 0 ? [{ index, series: 0 }] : []
42
+ );
43
+ const markProps = useChartDatumSelection(points);
44
+ const shape = (raw) => {
45
+ const props = raw;
46
+ const datum = data[props.index];
47
+ if (!datum || !points.some((p) => p.index === props.index)) return /* @__PURE__ */ jsx("g", {});
48
+ return /* @__PURE__ */ jsx(
49
+ Sector,
50
+ {
51
+ ...props,
52
+ ...markProps(
53
+ props.index,
54
+ 0,
55
+ t("chart.datumLabel", {
56
+ label: String(datum[nameKey] ?? ""),
57
+ series: dataKey,
58
+ value: fmt.format(Number(datum[dataKey]))
59
+ }),
60
+ () => onDatumSelect?.(datum, { seriesKey: dataKey, index: props.index })
61
+ )
62
+ }
63
+ );
64
+ };
65
+ const shapeRef = React.useRef(shape);
66
+ shapeRef.current = shape;
67
+ const stableShape = React.useCallback((raw) => shapeRef.current(raw), []);
36
68
  const hasData = data.length > 0;
37
69
  const summary = buildPieSummary(
38
70
  data,
@@ -45,6 +77,7 @@ function PieChart({
45
77
  return /* @__PURE__ */ jsx(
46
78
  ChartFrame,
47
79
  {
80
+ interactive: !!onDatumSelect,
48
81
  label,
49
82
  showCaption,
50
83
  description,
@@ -54,10 +87,12 @@ function PieChart({
54
87
  height: resolvedHeight,
55
88
  className,
56
89
  id,
57
- children: hasData ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", minWidth: 0, children: /* @__PURE__ */ jsxs(RPieChart, { children: [
90
+ children: hasData ? /* @__PURE__ */ jsx(ResponsiveContainer, { width: "100%", height: "100%", minWidth: 0, children: /* @__PURE__ */ jsxs(RPieChart, { accessibilityLayer: onDatumSelect ? false : void 0, children: [
58
91
  /* @__PURE__ */ jsx(
59
92
  Pie,
60
93
  {
94
+ shape: onDatumSelect ? stableShape : void 0,
95
+ isAnimationActive: onDatumSelect ? false : void 0,
61
96
  data,
62
97
  dataKey,
63
98
  nameKey,
@@ -32,7 +32,7 @@ export declare const RECHARTS_PEER_MESSAGE: string;
32
32
  * recharts-backed chart before it renders anything.
33
33
  */
34
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> & {
35
+ export declare const Dot: import("react").FC<peer.DotProps>, Rectangle: import("react").FC<peer.RectangleProps>, Sector: import("react").FC<peer.SectorProps>, 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
36
  ref?: React.Ref<SVGSVGElement>;
37
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
38
  ref?: React.Ref<SVGSVGElement>;
@@ -8,6 +8,9 @@ function assertRechartsPeer() {
8
8
  }
9
9
  }
10
10
  const {
11
+ Dot,
12
+ Rectangle,
13
+ Sector,
11
14
  Area,
12
15
  AreaChart,
13
16
  Bar,
@@ -31,13 +34,16 @@ export {
31
34
  BarChart,
32
35
  CartesianGrid,
33
36
  Cell,
37
+ Dot,
34
38
  Legend,
35
39
  Line,
36
40
  LineChart,
37
41
  Pie,
38
42
  PieChart,
39
43
  RECHARTS_PEER_MESSAGE,
44
+ Rectangle,
40
45
  ResponsiveContainer,
46
+ Sector,
41
47
  Tooltip,
42
48
  XAxis,
43
49
  YAxis,
@@ -20,7 +20,7 @@ export { Card, CardBar, CardContent, CardCover, CardDescription, CardFooter, Car
20
20
  export type { StatCardProps, CardBarProps, CardTabItemProp, CardTabItemProps } from "./card.js";
21
21
  export { ServiceCatalogCta, ServiceLauncherCard, ServiceLauncherCardSkeleton, } from "./service-launcher-card.js";
22
22
  export type { ServiceCatalogCtaProps, ServiceLauncherCardProps, ServiceLauncherCardSkeletonProps, ServiceLauncherStatusTone, } from "./service-launcher-card.js";
23
- export { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
23
+ export { Table, TableBody, TableCaption, TableCell, TableHead, TableHeader, TableRow, } from "./table.js";
24
24
  export { Descriptions } from "./descriptions.js";
25
25
  export { DataTable, flexRender } from "./data-table.js";
26
26
  export type { ColumnDef, Density as TableDensity } from "./data-table.js";
@@ -24,7 +24,15 @@ import {
24
24
  ServiceLauncherCard,
25
25
  ServiceLauncherCardSkeleton
26
26
  } from "./service-launcher-card.js";
27
- import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./table.js";
27
+ import {
28
+ Table,
29
+ TableBody,
30
+ TableCaption,
31
+ TableCell,
32
+ TableHead,
33
+ TableHeader,
34
+ TableRow
35
+ } from "./table.js";
28
36
  import { Descriptions } from "./descriptions.js";
29
37
  import { DataTable, flexRender } from "./data-table.js";
30
38
  import { EmptyState } from "./empty-state.js";
@@ -100,6 +108,7 @@ export {
100
108
  Swatch,
101
109
  Table,
102
110
  TableBody,
111
+ TableCaption,
103
112
  TableCell,
104
113
  TableHead,
105
114
  TableHeader,
@@ -157,6 +157,15 @@ export declare const Table: React.ForwardRefExoticComponent<React.HTMLAttributes
157
157
  } & React.RefAttributes<HTMLTableElement>>;
158
158
  export declare const TableHeader: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
159
159
  export declare const TableBody: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableSectionElement> & React.RefAttributes<HTMLTableSectionElement>>;
160
+ /**
161
+ * The table's `<caption>` — its accessible name AND the visible sentence that says what the rows
162
+ * are and under which conditions they were collected ("Prices for the Holo variant · shipping not
163
+ * included"). Place it as the FIRST child of `<Table>` (HTML requires it). Without this part a
164
+ * consumer had to hand-roll a raw `<caption>` or put the sentence outside the table, where it no
165
+ * longer names it. It sits above the rows (HTML's own side); colour, size and inset are knobs:
166
+ * `--table-caption-color`, `--table-caption-font-size`, `--table-caption-space-y`.
167
+ */
168
+ export declare const TableCaption: React.ForwardRefExoticComponent<React.HTMLAttributes<HTMLTableCaptionElement> & React.RefAttributes<HTMLTableCaptionElement>>;
160
169
  /**
161
170
  * The row's STATE — same wash/rail/`--surface-*` grounding `DataTable`'s own `rowTone` already
162
171
  * writes onto `data-tone` (gh#866). Typed here so a hand-composed row can discover it instead of
@@ -196,6 +196,8 @@ const TableBody = React.forwardRef(({ className, ...props }, ref) => (
196
196
  /* @__PURE__ */ jsx("tbody", { ref, "data-slot": "table-body", className: cn(className), ...props })
197
197
  ));
198
198
  TableBody.displayName = "TableBody";
199
+ const TableCaption = React.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("caption", { ref, "data-slot": "table-caption", className: cn(className), ...props }));
200
+ TableCaption.displayName = "TableCaption";
199
201
  const TableRow = React.forwardRef(({ className, tone, interactive, ...props }, ref) => /* @__PURE__ */ jsx(
200
202
  "tr",
201
203
  {
@@ -293,6 +295,7 @@ TableCell.displayName = "TableCell";
293
295
  export {
294
296
  Table,
295
297
  TableBody,
298
+ TableCaption,
296
299
  TableCell,
297
300
  TableHead,
298
301
  TableHeader,
@@ -93,4 +93,5 @@ export declare const Heading: React.ForwardRefExoticComponent<Omit<React.HTMLAtt
93
93
  align?: import("../../props/index.js").TextAlignProp;
94
94
  truncate?: boolean;
95
95
  weight?: import("../../props/index.js").FontWeightProp;
96
+ family?: import("../../props/index.js").HeadingFamilyProp;
96
97
  } & React.RefAttributes<HTMLHeadingElement>>;
@@ -530,6 +530,7 @@ const Heading = React.forwardRef(
530
530
  align,
531
531
  truncate,
532
532
  weight = "medium",
533
+ family,
533
534
  className,
534
535
  ...props
535
536
  }, ref) => React.createElement(as ?? `h${level}`, {
@@ -543,6 +544,8 @@ const Heading = React.forwardRef(
543
544
  "data-align": align,
544
545
  "data-weight": weight,
545
546
  "data-truncate": truncate ? "" : void 0,
547
+ // Only the opt-in face is stamped; a default heading keeps its markup byte-for-byte.
548
+ "data-family": family === "editorial" ? "editorial" : void 0,
546
549
  className: cn("ui-heading", className),
547
550
  ...props
548
551
  })
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$comment": "AUTO-GENERATED by scripts/gen-measurement-contract.mjs — do not edit. Read this instead of guessing: docs/MEASUREMENT-CONTRACT.md.",
3
- "version": "33.15.0",
3
+ "version": "33.17.0",
4
4
  "targetSize": {
5
5
  "standard": "WCAG 2.2 SC 2.5.8 Target Size (Minimum), level AA — 24×24 CSS px",
6
6
  "min": 24,