@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.
- package/README.md +4 -4
- package/agent/START-HERE.md +7 -7
- package/agent/components/AppShell.json +1 -1
- package/agent/components/AreaChart.json +6 -0
- package/agent/components/BarChart.json +6 -0
- package/agent/components/Card.json +1 -1
- package/agent/components/DashboardGrid.json +76 -0
- package/agent/components/DataTable.json +1 -1
- package/agent/components/Heading.json +6 -0
- package/agent/components/HeatmapChart.json +6 -0
- package/agent/components/LineChart.json +6 -0
- package/agent/components/Pagination.json +1 -1
- package/agent/components/PieChart.json +6 -0
- package/agent/components/Table.json +2 -1
- package/agent/components-index.json +9 -3
- package/agent/components.json +118 -5
- package/agent/index.json +8 -8
- package/agent/llms.txt +6 -6
- package/agent/rules.json +1 -1
- package/agent/tokens.json +74 -2
- package/agent/vocabulary.json +12 -0
- package/dist/components/charts/chart-cartesian.d.ts +3 -2
- package/dist/components/charts/chart-cartesian.js +146 -46
- package/dist/components/charts/chart-datum-selection.d.ts +15 -0
- package/dist/components/charts/chart-datum-selection.js +97 -0
- package/dist/components/charts/chart-frame.d.ts +2 -1
- package/dist/components/charts/chart-frame.js +2 -1
- package/dist/components/charts/heatmap-chart.d.ts +1 -1
- package/dist/components/charts/heatmap-chart.js +20 -0
- package/dist/components/charts/index.d.ts +1 -1
- package/dist/components/charts/pie-chart.d.ts +2 -1
- package/dist/components/charts/pie-chart.js +36 -1
- package/dist/components/charts/recharts-peer.d.ts +1 -1
- package/dist/components/charts/recharts-peer.js +6 -0
- package/dist/components/data-display/index.d.ts +1 -1
- package/dist/components/data-display/index.js +10 -1
- package/dist/components/data-display/table.d.ts +9 -0
- package/dist/components/data-display/table.js +3 -0
- package/dist/components/general/typography.d.ts +1 -0
- package/dist/components/general/typography.js +3 -0
- package/dist/contracts/measurement.json +1 -1
- package/dist/i18n/messages/en.json +10 -0
- package/dist/i18n/messages/ja.json +10 -0
- package/dist/i18n/messages/vi.json +10 -0
- package/dist/lab/dashboard-grid-layout.d.ts +4 -0
- package/dist/lab/dashboard-grid-layout.js +36 -0
- package/dist/lab/dashboard-grid.d.ts +5 -0
- package/dist/lab/dashboard-grid.js +258 -0
- package/dist/lab/index.d.ts +2 -0
- package/dist/lab/index.js +2 -0
- package/dist/lib/hooks.d.ts +8 -0
- package/dist/lib/hooks.js +20 -21
- package/dist/props/components/charts.prop.d.ts +11 -0
- package/dist/props/components/dashboard-grid.prop.d.ts +27 -0
- package/dist/props/components/dashboard-grid.prop.js +0 -0
- package/dist/props/components/general.prop.d.ts +8 -1
- package/dist/props/components/index.d.ts +1 -1
- package/dist/props/registry.d.ts +89 -6
- package/dist/props/registry.js +85 -0
- package/dist/props/vocabulary/index.d.ts +1 -1
- package/dist/props/vocabulary/interaction.prop.d.ts +6 -0
- package/dist/styles/badge-layout.css +1 -0
- package/dist/styles/base.css +8 -0
- package/dist/styles/card-layout.css +8 -0
- package/dist/styles/chart-layout.css +28 -0
- package/dist/styles/control.css +82 -0
- package/dist/styles/dialog-layout.css +7 -3
- package/dist/styles/layers.json +10 -1
- package/dist/styles/layout.css +2 -0
- package/dist/styles/logo-layout.css +18 -12
- package/dist/styles/navigation-layout.css +2 -0
- package/dist/styles/shell-layout.css +40 -1
- package/dist/styles/table-layout.css +30 -0
- package/dist/styles/text-layout.css +5 -0
- package/dist/styles.css +1 -1
- package/dist/theme/famgia.service.css +10 -8
- package/dist/themes/godx-tokens.css +20 -16
- package/dist/tokens/base.css +2 -0
- package/dist/tokens/components/dashboard-grid.css +14 -0
- package/dist/tokens/components/feedback.css +2 -0
- package/dist/tokens/components/logo.css +6 -4
- package/dist/tokens/components/table.css +5 -1
- package/dist/tokens/derived.css +28 -24
- package/dist/tokens/foundation.css +62 -56
- package/dist/tokens/semantic/layout.css +5 -3
- package/docs/COMPOSITION-VS-COMPONENT.md +15 -14
- package/docs/TOKENS.md +33 -0
- 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(
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
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(
|
|
181
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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):
|
|
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 {
|
|
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.
|
|
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,
|