@godxjp/ui 33.15.0 → 33.16.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 +5 -5
- package/agent/components/AreaChart.json +6 -0
- package/agent/components/BarChart.json +6 -0
- package/agent/components/DashboardGrid.json +76 -0
- package/agent/components/DataTable.json +1 -1
- 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 +1 -1
- package/agent/components-index.json +7 -1
- package/agent/components.json +109 -3
- package/agent/index.json +6 -6
- package/agent/llms.txt +6 -6
- package/agent/tokens.json +38 -2
- 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/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/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/index.d.ts +1 -1
- package/dist/props/registry.d.ts +83 -5
- package/dist/props/registry.js +79 -0
- package/dist/styles/chart-layout.css +28 -0
- package/dist/styles/control.css +82 -0
- package/dist/styles/layers.json +10 -1
- package/dist/styles/navigation-layout.css +2 -0
- package/dist/styles.css +1 -1
- package/dist/tokens/base.css +2 -0
- package/dist/tokens/components/dashboard-grid.css +14 -0
- package/dist/tokens/components/table.css +1 -1
- package/docs/COMPOSITION-VS-COMPONENT.md +15 -14
- package/package.json +2 -2
|
@@ -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,
|
|
@@ -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.16.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,
|
|
@@ -797,6 +797,7 @@
|
|
|
797
797
|
}
|
|
798
798
|
},
|
|
799
799
|
"chart": {
|
|
800
|
+
"datumLabel": "{label}, {series}: {value}",
|
|
800
801
|
"empty": "No data to display",
|
|
801
802
|
"summaryCartesian": "Chart with {series} series across {points} data points",
|
|
802
803
|
"summaryPie": "Pie chart with {slices} slices",
|
|
@@ -868,5 +869,14 @@
|
|
|
868
869
|
"error": "failed"
|
|
869
870
|
}
|
|
870
871
|
}
|
|
872
|
+
},
|
|
873
|
+
"dashboardGrid": {
|
|
874
|
+
"move": "Move {label}",
|
|
875
|
+
"resize": "Resize {label}",
|
|
876
|
+
"hint": "Enter or Space grabs and drops. Arrow keys move; Shift and arrows resize. Escape cancels.",
|
|
877
|
+
"grabbed": "Grabbed {label}. Column {x}, row {y}, width {w}, height {h}.",
|
|
878
|
+
"moved": "{label}. Column {x}, row {y}, width {w}, height {h}.",
|
|
879
|
+
"dropped": "Saved {label}. Column {x}, row {y}, width {w}, height {h}.",
|
|
880
|
+
"cancelled": "Cancelled changes to {label}."
|
|
871
881
|
}
|
|
872
882
|
}
|
|
@@ -771,6 +771,7 @@
|
|
|
771
771
|
}
|
|
772
772
|
},
|
|
773
773
|
"chart": {
|
|
774
|
+
"datumLabel": "{label}, {series}: {value}",
|
|
774
775
|
"empty": "データがありません",
|
|
775
776
|
"summaryCartesian": "{series}系列・{points}データ点のグラフ",
|
|
776
777
|
"summaryPie": "{slices}区分の円グラフ",
|
|
@@ -841,5 +842,14 @@
|
|
|
841
842
|
"error": "失敗"
|
|
842
843
|
}
|
|
843
844
|
}
|
|
845
|
+
},
|
|
846
|
+
"dashboardGrid": {
|
|
847
|
+
"move": "{label}を移動",
|
|
848
|
+
"resize": "{label}のサイズ変更",
|
|
849
|
+
"hint": "Enter またはスペースでつかむ・確定。矢印キーで移動、Shift と矢印でサイズ変更。Escape で取り消します。",
|
|
850
|
+
"grabbed": "{label}をつかみました。列{x}、行{y}、幅{w}、高さ{h}。",
|
|
851
|
+
"moved": "{label}。列{x}、行{y}、幅{w}、高さ{h}。",
|
|
852
|
+
"dropped": "{label}を保存しました。列{x}、行{y}、幅{w}、高さ{h}。",
|
|
853
|
+
"cancelled": "{label}の変更を取り消しました。"
|
|
844
854
|
}
|
|
845
855
|
}
|
|
@@ -772,6 +772,7 @@
|
|
|
772
772
|
}
|
|
773
773
|
},
|
|
774
774
|
"chart": {
|
|
775
|
+
"datumLabel": "{label}, {series}: {value}",
|
|
775
776
|
"empty": "Không có dữ liệu",
|
|
776
777
|
"summaryCartesian": "Biểu đồ gồm {series} chuỗi trên {points} điểm dữ liệu",
|
|
777
778
|
"summaryPie": "Biểu đồ tròn gồm {slices} phần",
|
|
@@ -842,5 +843,14 @@
|
|
|
842
843
|
"error": "thất bại"
|
|
843
844
|
}
|
|
844
845
|
}
|
|
846
|
+
},
|
|
847
|
+
"dashboardGrid": {
|
|
848
|
+
"move": "Di chuyển {label}",
|
|
849
|
+
"resize": "Đổi kích thước {label}",
|
|
850
|
+
"hint": "Enter hoặc Space để nắm và thả. Phím mũi tên để di chuyển; Shift và mũi tên để đổi kích thước. Escape để hủy.",
|
|
851
|
+
"grabbed": "Đã nắm {label}. Cột {x}, hàng {y}, rộng {w}, cao {h}.",
|
|
852
|
+
"moved": "{label}. Cột {x}, hàng {y}, rộng {w}, cao {h}.",
|
|
853
|
+
"dropped": "Đã lưu {label}. Cột {x}, hàng {y}, rộng {w}, cao {h}.",
|
|
854
|
+
"cancelled": "Đã hủy thay đổi {label}."
|
|
845
855
|
}
|
|
846
856
|
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { DashboardGridItemProp } from "../props/components/dashboard-grid.prop.js";
|
|
2
|
+
export declare const gridColumns: (value: number) => number;
|
|
3
|
+
/** Active tile wins. Others push down in stable visual order, then compact upward without overlap. */
|
|
4
|
+
export declare function resolveDashboardLayout(layout: readonly DashboardGridItemProp[], columns: number, active?: string): DashboardGridItemProp[];
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
const integer = (value, fallback) => Number.isFinite(value) ? Math.trunc(value) : fallback;
|
|
2
|
+
const gridColumns = (value) => Math.max(1, integer(value, 1));
|
|
3
|
+
const overlaps = (a, b) => a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
|
|
4
|
+
function resolveDashboardLayout(layout, columns, active) {
|
|
5
|
+
const count = gridColumns(columns);
|
|
6
|
+
const normalized = layout.map((item) => {
|
|
7
|
+
const w = Math.min(count, Math.max(1, integer(item.w, 1)));
|
|
8
|
+
return {
|
|
9
|
+
...item,
|
|
10
|
+
w,
|
|
11
|
+
h: Math.max(1, integer(item.h, 1)),
|
|
12
|
+
x: Math.max(0, Math.min(count - w, integer(item.x, 0))),
|
|
13
|
+
y: Math.max(0, integer(item.y, 0))
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
const sorted = normalized.slice().sort((a, b) => a.y - b.y || a.x - b.x);
|
|
17
|
+
const held = normalized.find((item) => item.id === active);
|
|
18
|
+
const placed = held ? [held] : [];
|
|
19
|
+
for (const item of sorted) {
|
|
20
|
+
if (item === held) continue;
|
|
21
|
+
let collision;
|
|
22
|
+
while (collision = placed.find((other) => overlaps(item, other)))
|
|
23
|
+
item.y = collision.y + collision.h;
|
|
24
|
+
while (item.y > 0 && !placed.some((other) => overlaps({ ...item, y: item.y - 1 }, other)))
|
|
25
|
+
item.y--;
|
|
26
|
+
placed.push(item);
|
|
27
|
+
}
|
|
28
|
+
if (held)
|
|
29
|
+
while (held.y > 0 && !placed.some((other) => other !== held && overlaps({ ...held, y: held.y - 1 }, other)))
|
|
30
|
+
held.y--;
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
export {
|
|
34
|
+
gridColumns,
|
|
35
|
+
resolveDashboardLayout
|
|
36
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { DashboardGridProp } from "../props/components/dashboard-grid.prop.js";
|
|
3
|
+
export type { DashboardGridItemProp, DashboardGridProp, DashboardGridProp as DashboardGridProps, } from "../props/components/dashboard-grid.prop.js";
|
|
4
|
+
/** Optional BI layout editor. DOM reading order remains items order; x starts at inline-start. */
|
|
5
|
+
export declare const DashboardGrid: React.ForwardRefExoticComponent<DashboardGridProp & React.RefAttributes<HTMLDivElement>>;
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { GripVertical } from "lucide-react";
|
|
5
|
+
import { Card, CardHeader, CardTitle } from "../components/data-display/card.js";
|
|
6
|
+
import { Button } from "../components/general/button.js";
|
|
7
|
+
import { useTranslation } from "../i18n/use-translation.js";
|
|
8
|
+
import { useMediaQuery } from "../lib/hooks.js";
|
|
9
|
+
import { cn } from "../lib/utils.js";
|
|
10
|
+
import { gridColumns, resolveDashboardLayout } from "./dashboard-grid-layout.js";
|
|
11
|
+
const DashboardGrid = React.forwardRef(
|
|
12
|
+
function DashboardGrid2({
|
|
13
|
+
items,
|
|
14
|
+
layout,
|
|
15
|
+
defaultLayout,
|
|
16
|
+
onLayoutChange,
|
|
17
|
+
columns = 12,
|
|
18
|
+
rowHeight,
|
|
19
|
+
editable = false,
|
|
20
|
+
className,
|
|
21
|
+
...props
|
|
22
|
+
}, ref) {
|
|
23
|
+
const { t, locale } = useTranslation();
|
|
24
|
+
const sm = useMediaQuery("(min-width: 40rem)");
|
|
25
|
+
const md = useMediaQuery("(min-width: 48rem)");
|
|
26
|
+
const lg = useMediaQuery("(min-width: 64rem)");
|
|
27
|
+
const xl = useMediaQuery("(min-width: 80rem)");
|
|
28
|
+
const count = gridColumns(
|
|
29
|
+
typeof columns === "number" ? columns : xl && columns.xl || lg && columns.lg || md && columns.md || sm && columns.sm || columns.base
|
|
30
|
+
);
|
|
31
|
+
const [inner, setInner] = React.useState(defaultLayout ?? []);
|
|
32
|
+
const source = layout ?? inner;
|
|
33
|
+
const committed = resolveDashboardLayout(
|
|
34
|
+
items.map(
|
|
35
|
+
(item, index) => source.find((entry) => entry.id === item.id) ?? {
|
|
36
|
+
id: item.id,
|
|
37
|
+
x: 0,
|
|
38
|
+
y: index,
|
|
39
|
+
w: 1,
|
|
40
|
+
h: 1
|
|
41
|
+
}
|
|
42
|
+
),
|
|
43
|
+
count
|
|
44
|
+
);
|
|
45
|
+
const root = React.useRef(null);
|
|
46
|
+
const session = React.useRef(null);
|
|
47
|
+
const [preview, setPreview] = React.useState(null);
|
|
48
|
+
const [announcement, announce] = React.useState("");
|
|
49
|
+
const hint = React.useId();
|
|
50
|
+
const format = new Intl.NumberFormat(locale);
|
|
51
|
+
const speak = (action, entries, id) => {
|
|
52
|
+
const item = entries.find((entry) => entry.id === id);
|
|
53
|
+
if (!item) return;
|
|
54
|
+
announce(
|
|
55
|
+
t(`dashboardGrid.${action}`, {
|
|
56
|
+
label: items.find((entry) => entry.id === id)?.label ?? id,
|
|
57
|
+
x: format.format(item.x + 1),
|
|
58
|
+
y: format.format(item.y + 1),
|
|
59
|
+
w: format.format(item.w),
|
|
60
|
+
h: format.format(item.h)
|
|
61
|
+
})
|
|
62
|
+
);
|
|
63
|
+
};
|
|
64
|
+
const begin = (id) => {
|
|
65
|
+
const next = committed.map((item) => ({ ...item }));
|
|
66
|
+
session.current = { id, base: next, next };
|
|
67
|
+
setPreview(next);
|
|
68
|
+
speak("grabbed", next, id);
|
|
69
|
+
};
|
|
70
|
+
const finish = (cancel) => {
|
|
71
|
+
const current = session.current;
|
|
72
|
+
if (!current) return;
|
|
73
|
+
session.current = null;
|
|
74
|
+
setPreview(null);
|
|
75
|
+
speak(cancel ? "cancelled" : "dropped", cancel ? current.base : current.next, current.id);
|
|
76
|
+
if (!cancel && JSON.stringify(current.base) !== JSON.stringify(current.next)) {
|
|
77
|
+
if (layout === void 0) setInner(current.next);
|
|
78
|
+
onLayoutChange?.(current.next);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
const update = (dx, dy, resize, fromBase = false) => {
|
|
82
|
+
const current = session.current;
|
|
83
|
+
if (!current) return;
|
|
84
|
+
current.next = resolveDashboardLayout(
|
|
85
|
+
(fromBase ? current.base : current.next).map(
|
|
86
|
+
(item) => item.id !== current.id ? item : resize ? { ...item, w: item.w + dx, h: item.h + dy } : { ...item, x: item.x + dx, y: item.y + dy }
|
|
87
|
+
),
|
|
88
|
+
count,
|
|
89
|
+
current.id
|
|
90
|
+
);
|
|
91
|
+
setPreview(current.next);
|
|
92
|
+
speak("moved", current.next, current.id);
|
|
93
|
+
};
|
|
94
|
+
React.useEffect(() => {
|
|
95
|
+
session.current = null;
|
|
96
|
+
setPreview(null);
|
|
97
|
+
}, [layout, columns, count, editable, items]);
|
|
98
|
+
const latest = React.useRef({ finish, update });
|
|
99
|
+
latest.current = { finish, update };
|
|
100
|
+
React.useEffect(() => {
|
|
101
|
+
const move = (event) => {
|
|
102
|
+
const pointer = session.current?.pointer;
|
|
103
|
+
if (!pointer || event.pointerId !== pointer.id) return;
|
|
104
|
+
event.preventDefault();
|
|
105
|
+
latest.current.update(
|
|
106
|
+
Math.round((event.clientX - pointer.x) / pointer.stepX) * (pointer.rtl ? -1 : 1),
|
|
107
|
+
Math.round((event.clientY - pointer.y) / pointer.stepY),
|
|
108
|
+
pointer.resize,
|
|
109
|
+
true
|
|
110
|
+
);
|
|
111
|
+
};
|
|
112
|
+
const end = (event) => {
|
|
113
|
+
if (session.current?.pointer?.id === event.pointerId)
|
|
114
|
+
latest.current.finish(event.type === "pointercancel");
|
|
115
|
+
};
|
|
116
|
+
const escape = (event) => {
|
|
117
|
+
if (event.key === "Escape" && session.current) {
|
|
118
|
+
event.preventDefault();
|
|
119
|
+
latest.current.finish(true);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
window.addEventListener("pointermove", move, { passive: false });
|
|
123
|
+
window.addEventListener("pointerup", end);
|
|
124
|
+
window.addEventListener("pointercancel", end);
|
|
125
|
+
window.addEventListener("keydown", escape);
|
|
126
|
+
return () => {
|
|
127
|
+
window.removeEventListener("pointermove", move);
|
|
128
|
+
window.removeEventListener("pointerup", end);
|
|
129
|
+
window.removeEventListener("pointercancel", end);
|
|
130
|
+
window.removeEventListener("keydown", escape);
|
|
131
|
+
};
|
|
132
|
+
}, []);
|
|
133
|
+
const pointerDown = (id, resize) => (event) => {
|
|
134
|
+
if (!editable || event.button !== 0 || session.current || !root.current) return;
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
event.currentTarget.focus();
|
|
137
|
+
const css = getComputedStyle(root.current);
|
|
138
|
+
const gap = parseFloat(css.columnGap) || 0;
|
|
139
|
+
begin(id);
|
|
140
|
+
session.current.pointer = {
|
|
141
|
+
id: event.pointerId,
|
|
142
|
+
x: event.clientX,
|
|
143
|
+
y: event.clientY,
|
|
144
|
+
stepX: (root.current.clientWidth + gap) / count,
|
|
145
|
+
stepY: (parseFloat(css.gridAutoRows) || rowHeight || 80) + (parseFloat(css.rowGap) || 0),
|
|
146
|
+
rtl: css.direction === "rtl",
|
|
147
|
+
resize
|
|
148
|
+
};
|
|
149
|
+
};
|
|
150
|
+
const keyDown = (id) => (event) => {
|
|
151
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
152
|
+
event.preventDefault();
|
|
153
|
+
if (!session.current) begin(id);
|
|
154
|
+
else if (session.current.id === id) finish(false);
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (session.current?.id !== id || !event.key.startsWith("Arrow")) return;
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
const rtl = root.current && getComputedStyle(root.current).direction === "rtl";
|
|
160
|
+
update(
|
|
161
|
+
(event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0) * (rtl ? -1 : 1),
|
|
162
|
+
event.key === "ArrowDown" ? 1 : event.key === "ArrowUp" ? -1 : 0,
|
|
163
|
+
event.shiftKey
|
|
164
|
+
);
|
|
165
|
+
};
|
|
166
|
+
const shown = preview ?? committed;
|
|
167
|
+
return /* @__PURE__ */ jsxs("div", { ...props, ref, className: cn("ui-dashboard-grid", className), children: [
|
|
168
|
+
/* @__PURE__ */ jsx(
|
|
169
|
+
"div",
|
|
170
|
+
{
|
|
171
|
+
ref: root,
|
|
172
|
+
className: "ui-dashboard-grid-items",
|
|
173
|
+
"data-stacked": count === 1 || void 0,
|
|
174
|
+
style: {
|
|
175
|
+
"--dashboard-grid-columns": count,
|
|
176
|
+
...rowHeight && Number.isFinite(rowHeight) && rowHeight > 0 ? { "--dashboard-grid-row-height": `${rowHeight}px` } : {}
|
|
177
|
+
},
|
|
178
|
+
children: items.map((item) => {
|
|
179
|
+
const box = shown.find((entry) => entry.id === item.id) ?? committed.find((entry) => entry.id === item.id);
|
|
180
|
+
const held = session.current?.id === item.id;
|
|
181
|
+
return /* @__PURE__ */ jsx(
|
|
182
|
+
"section",
|
|
183
|
+
{
|
|
184
|
+
"aria-label": item.label,
|
|
185
|
+
className: "ui-dashboard-grid-item",
|
|
186
|
+
"data-dashboard-id": item.id,
|
|
187
|
+
"data-x": box.x,
|
|
188
|
+
"data-y": box.y,
|
|
189
|
+
"data-w": box.w,
|
|
190
|
+
"data-h": box.h,
|
|
191
|
+
"data-editable": editable || void 0,
|
|
192
|
+
"data-held": held || void 0,
|
|
193
|
+
style: {
|
|
194
|
+
gridColumn: `${box.x + 1} / span ${box.w}`,
|
|
195
|
+
gridRow: count === 1 ? "auto" : `${box.y + 1} / span ${box.h}`
|
|
196
|
+
},
|
|
197
|
+
children: /* @__PURE__ */ jsxs(Card, { className: "ui-dashboard-grid-surface", children: [
|
|
198
|
+
/* @__PURE__ */ jsx(CardHeader, { className: "ui-dashboard-grid-header", children: /* @__PURE__ */ jsx(CardTitle, { children: item.label }) }),
|
|
199
|
+
editable && /* @__PURE__ */ jsx(
|
|
200
|
+
Button,
|
|
201
|
+
{
|
|
202
|
+
variant: "ghost",
|
|
203
|
+
size: "icon-sm",
|
|
204
|
+
className: "ui-dashboard-grid-move",
|
|
205
|
+
"aria-label": t("dashboardGrid.move", { label: item.label }),
|
|
206
|
+
"aria-describedby": hint,
|
|
207
|
+
"aria-pressed": held,
|
|
208
|
+
onPointerDown: pointerDown(item.id, false),
|
|
209
|
+
onKeyDown: keyDown(item.id),
|
|
210
|
+
onBlur: () => {
|
|
211
|
+
if (!session.current?.pointer) finish(true);
|
|
212
|
+
},
|
|
213
|
+
children: /* @__PURE__ */ jsx(GripVertical, { "aria-hidden": "true" })
|
|
214
|
+
}
|
|
215
|
+
),
|
|
216
|
+
/* @__PURE__ */ jsx("div", { className: "ui-dashboard-grid-content", children: item.content }),
|
|
217
|
+
editable && /* @__PURE__ */ jsx(
|
|
218
|
+
Button,
|
|
219
|
+
{
|
|
220
|
+
variant: "ghost",
|
|
221
|
+
size: "icon-sm",
|
|
222
|
+
className: "ui-dashboard-grid-resize",
|
|
223
|
+
"aria-label": t("dashboardGrid.resize", { label: item.label }),
|
|
224
|
+
"aria-describedby": hint,
|
|
225
|
+
"aria-pressed": held,
|
|
226
|
+
onPointerDown: pointerDown(item.id, true),
|
|
227
|
+
onKeyDown: keyDown(item.id),
|
|
228
|
+
onBlur: () => {
|
|
229
|
+
if (!session.current?.pointer) finish(true);
|
|
230
|
+
},
|
|
231
|
+
children: /* @__PURE__ */ jsx(
|
|
232
|
+
"svg",
|
|
233
|
+
{
|
|
234
|
+
viewBox: "0 0 24 24",
|
|
235
|
+
fill: "none",
|
|
236
|
+
stroke: "currentColor",
|
|
237
|
+
strokeWidth: "1.5",
|
|
238
|
+
"aria-hidden": "true",
|
|
239
|
+
children: /* @__PURE__ */ jsx("path", { d: "M8 20 20 8M14 20 20 14" })
|
|
240
|
+
}
|
|
241
|
+
)
|
|
242
|
+
}
|
|
243
|
+
)
|
|
244
|
+
] })
|
|
245
|
+
},
|
|
246
|
+
item.id
|
|
247
|
+
);
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
),
|
|
251
|
+
/* @__PURE__ */ jsx("span", { id: hint, className: "sr-only", children: t("dashboardGrid.hint") }),
|
|
252
|
+
/* @__PURE__ */ jsx("span", { role: "status", "aria-live": "polite", "aria-atomic": "true", className: "sr-only", children: announcement })
|
|
253
|
+
] });
|
|
254
|
+
}
|
|
255
|
+
);
|
|
256
|
+
export {
|
|
257
|
+
DashboardGrid
|
|
258
|
+
};
|
package/dist/lab/index.d.ts
CHANGED
|
@@ -37,3 +37,5 @@ export { MegaMenu } from "./mega-menu.js";
|
|
|
37
37
|
export type { MegaMenuProp, MegaMenuProps, MegaMenuItemProp, MegaMenuItemProps, MegaMenuPanelProp, MegaMenuPanelProps, MegaMenuGroupProp, MegaMenuGroupProps, MegaMenuLinkProp, MegaMenuLinkProps, MegaMenuLinkComponentProp, MegaMenuLinkComponentProps, MegaMenuTriggerActionProp, MegaMenuTriggerActionProps, } from "./mega-menu.js";
|
|
38
38
|
export { Anchor } from "./anchor.js";
|
|
39
39
|
export type { AnchorContainerProp, AnchorDirectionProp, AnchorItemProp, AnchorProp, AnchorProps, } from "./anchor.js";
|
|
40
|
+
export { DashboardGrid } from "./dashboard-grid.js";
|
|
41
|
+
export type { DashboardGridProp, DashboardGridProps, DashboardGridItemProp } from "./dashboard-grid.js";
|
package/dist/lab/index.js
CHANGED
|
@@ -21,6 +21,7 @@ import { EmojiPicker } from "./emoji-picker.js";
|
|
|
21
21
|
import { SortableList } from "./sortable-list.js";
|
|
22
22
|
import { MegaMenu } from "./mega-menu.js";
|
|
23
23
|
import { Anchor } from "./anchor.js";
|
|
24
|
+
import { DashboardGrid } from "./dashboard-grid.js";
|
|
24
25
|
export {
|
|
25
26
|
Anchor,
|
|
26
27
|
Carousel,
|
|
@@ -29,6 +30,7 @@ export {
|
|
|
29
30
|
CarouselItem,
|
|
30
31
|
CarouselNext,
|
|
31
32
|
CarouselPrevious,
|
|
33
|
+
DashboardGrid,
|
|
32
34
|
DraggablePanel,
|
|
33
35
|
EmojiPicker,
|
|
34
36
|
FloatButton,
|
|
@@ -21,6 +21,11 @@ export type ChartSeriesProp = {
|
|
|
21
21
|
*/
|
|
22
22
|
fillColor?: string;
|
|
23
23
|
};
|
|
24
|
+
/** Selection is an action: the dashboard owns filter state. Index refers to the original data array. */
|
|
25
|
+
export type ChartDatumSelect<T = ChartDatum> = (datum: T, context: {
|
|
26
|
+
seriesKey: string;
|
|
27
|
+
index: number;
|
|
28
|
+
}) => void;
|
|
24
29
|
/** Shared base for the cartesian charts (Line / Bar / Area). */
|
|
25
30
|
type ChartCartesianBase = {
|
|
26
31
|
/** Row data. Each row is one category with a value per series. */
|
|
@@ -69,6 +74,8 @@ type ChartCartesianBase = {
|
|
|
69
74
|
numberFormat?: Intl.NumberFormatOptions;
|
|
70
75
|
/** Message shown when `data` is empty. Defaults to a localized "no data". */
|
|
71
76
|
emptyMessage?: EmptyMessageProp;
|
|
77
|
+
/** Select a finite datum by pointer or Enter/Space. Arrows navigate categories and series. */
|
|
78
|
+
onDatumSelect?: ChartDatumSelect;
|
|
72
79
|
/**
|
|
73
80
|
* Called with the clicked category's datum and its index — drill-down, zoom-to-bucket. The whole
|
|
74
81
|
* category column is the target, not only the mark, so a zero-height bar is still clickable.
|
|
@@ -161,6 +168,8 @@ export type HeatmapDatum = {
|
|
|
161
168
|
* steps through the `--chart-heat-1..5` ramp; an empty cell is outlined, a zero cell is filled.
|
|
162
169
|
*/
|
|
163
170
|
export type HeatmapChartProp = {
|
|
171
|
+
/** Select a present cell. seriesKey is String(column); index is the original data index. */
|
|
172
|
+
onDatumSelect?: ChartDatumSelect<HeatmapDatum>;
|
|
164
173
|
/** Row categories, top to bottom (the y axis — weekdays). */
|
|
165
174
|
rows: HeatmapCategory[];
|
|
166
175
|
/** Column categories, start to end (the x axis — hours). */
|
|
@@ -207,6 +216,8 @@ export type AreaChartProp = ChartCartesianBase & {
|
|
|
207
216
|
};
|
|
208
217
|
/** @see PieChart */
|
|
209
218
|
export type PieChartProp = {
|
|
219
|
+
/** Select a slice by pointer or Enter/Space; seriesKey is dataKey. Arrows navigate slices. */
|
|
220
|
+
onDatumSelect?: ChartDatumSelect;
|
|
210
221
|
/** Row data — one slice per row. */
|
|
211
222
|
data: ChartDatum[];
|
|
212
223
|
/** Key into each datum holding the slice's numeric value. */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type * as React from "react";
|
|
2
|
+
import type { BreakpointProp } from "../vocabulary/index.js";
|
|
3
|
+
/** Integer grid coordinates, zero-based from inline-start. Stable id owns persistence. */
|
|
4
|
+
export interface DashboardGridItemProp {
|
|
5
|
+
id: string;
|
|
6
|
+
x: number;
|
|
7
|
+
y: number;
|
|
8
|
+
w: number;
|
|
9
|
+
h: number;
|
|
10
|
+
}
|
|
11
|
+
export interface DashboardGridProp extends Omit<React.HTMLAttributes<HTMLDivElement>, "children"> {
|
|
12
|
+
/** Each tile owns its Card and visible label header; content is the body (e.g. CardContent). */
|
|
13
|
+
items: {
|
|
14
|
+
id: string;
|
|
15
|
+
label: string;
|
|
16
|
+
content: React.ReactNode;
|
|
17
|
+
}[];
|
|
18
|
+
layout?: DashboardGridItemProp[];
|
|
19
|
+
defaultLayout?: DashboardGridItemProp[];
|
|
20
|
+
onLayoutChange?: (layout: DashboardGridItemProp[]) => void;
|
|
21
|
+
columns?: number | ({
|
|
22
|
+
base: number;
|
|
23
|
+
} & Partial<Record<BreakpointProp, number>>);
|
|
24
|
+
/** Row track size in pixels; omitted reads --dashboard-grid-row-height. */
|
|
25
|
+
rowHeight?: number;
|
|
26
|
+
editable?: boolean;
|
|
27
|
+
}
|
|
File without changes
|
|
@@ -2,7 +2,7 @@ export type { AppLauncherApp, AppLauncherGroup, AppLauncherLabels, AppLauncherPr
|
|
|
2
2
|
export type { ButtonProp } from "./general.prop.js";
|
|
3
3
|
export type { CommandProp, CommandSeparatorProp, InputProp, TextareaProp, FormFieldProp, SearchInputProp, CheckboxProp, CheckboxGroupProp, ChoiceOptionProp, RadioProp, RadioGroupProp, SwitchProp, SliderProp, CalendarProp, DatePickerProp, TimePickerProp, ColorPickerProp, ColorPickerPresetProp, UploadProp, UploadFileItemProp, UploadVariantProp, TreeOptionProp, TreeFieldNamesProp, CascaderProp, TreeSelectProp, ShowCheckedStrategyProp, TransferProp, TransferItemProp, } from "./data-entry.prop.js";
|
|
4
4
|
export type { AvatarProp, EmptyStateProp, DescriptionsProp, DescriptionsItemProp, BadgeProp, DataTableProp, QrCodeProp, } from "./data-display.prop.js";
|
|
5
|
-
export type { ChartDatum, ChartSeriesProp, LineChartProp, BarChartProp, AreaChartProp, PieChartProp, } from "./charts.prop.js";
|
|
5
|
+
export type { ChartDatumSelect, ChartDatum, ChartSeriesProp, LineChartProp, BarChartProp, AreaChartProp, PieChartProp, } from "./charts.prop.js";
|
|
6
6
|
export type { AlertQueryErrorProp, AuthExpiryProviderProp, AlertProp, DialogProp, DialogContentProp, AlertTitleProp, AlertContentProp, AlertDescriptionProp, AlertActionsProp, SheetResponsiveProp, SkeletonRowsProp, } from "./feedback.prop.js";
|
|
7
7
|
export type { DataStateProp, InfiniteQueryStateProp, InfiniteQueryHelpers, PrefetchLinkProp, } from "./query.prop.js";
|
|
8
8
|
export type { DropdownMenuPlacementProp, DropdownMenuTriggerActionProp, PaginationProp, PaginationSizeProp, PaginationAlignProp, StepsProp, StepItemProp, StepStatusProp, StepsTypeProp, TabsProp, TabItemProp, TabsVariantProp, TabsPlacementProp, TabsExtraProp, TabsOnEditProp, } from "./navigation.prop.js";
|
package/dist/props/registry.d.ts
CHANGED
|
@@ -717,6 +717,64 @@ export declare const VOCABULARY_REGISTRY: {
|
|
|
717
717
|
};
|
|
718
718
|
export type VocabularyPropName = keyof typeof VOCABULARY_REGISTRY;
|
|
719
719
|
export declare const COMPONENT_PROP_REGISTRY: {
|
|
720
|
+
readonly DashboardGridProp: {
|
|
721
|
+
readonly group: "layout";
|
|
722
|
+
readonly file: "components/dashboard-grid.prop.ts";
|
|
723
|
+
readonly vocabulary: readonly ["ClassNameProp", "IdProp", {
|
|
724
|
+
readonly field: "items";
|
|
725
|
+
readonly local: true;
|
|
726
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
727
|
+
}, {
|
|
728
|
+
readonly field: "layout";
|
|
729
|
+
readonly local: true;
|
|
730
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
731
|
+
}, {
|
|
732
|
+
readonly field: "defaultLayout";
|
|
733
|
+
readonly local: true;
|
|
734
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
735
|
+
}, {
|
|
736
|
+
readonly field: "onLayoutChange";
|
|
737
|
+
readonly local: true;
|
|
738
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
739
|
+
}, {
|
|
740
|
+
readonly field: "columns";
|
|
741
|
+
readonly local: true;
|
|
742
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
743
|
+
}, {
|
|
744
|
+
readonly field: "rowHeight";
|
|
745
|
+
readonly local: true;
|
|
746
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
747
|
+
}, {
|
|
748
|
+
readonly field: "editable";
|
|
749
|
+
readonly local: true;
|
|
750
|
+
readonly reason: "DashboardGrid cell-layout editing contract (#1397), owner-approved lab API.";
|
|
751
|
+
}];
|
|
752
|
+
};
|
|
753
|
+
readonly DashboardGridItemProp: {
|
|
754
|
+
readonly group: "layout";
|
|
755
|
+
readonly file: "components/dashboard-grid.prop.ts";
|
|
756
|
+
readonly vocabulary: readonly [{
|
|
757
|
+
readonly field: "id";
|
|
758
|
+
readonly local: true;
|
|
759
|
+
readonly reason: "Persisted grid cell coordinate or identity.";
|
|
760
|
+
}, {
|
|
761
|
+
readonly field: "x";
|
|
762
|
+
readonly local: true;
|
|
763
|
+
readonly reason: "Persisted grid cell coordinate or identity.";
|
|
764
|
+
}, {
|
|
765
|
+
readonly field: "y";
|
|
766
|
+
readonly local: true;
|
|
767
|
+
readonly reason: "Persisted grid cell coordinate or identity.";
|
|
768
|
+
}, {
|
|
769
|
+
readonly field: "w";
|
|
770
|
+
readonly local: true;
|
|
771
|
+
readonly reason: "Persisted grid cell coordinate or identity.";
|
|
772
|
+
}, {
|
|
773
|
+
readonly field: "h";
|
|
774
|
+
readonly local: true;
|
|
775
|
+
readonly reason: "Persisted grid cell coordinate or identity.";
|
|
776
|
+
}];
|
|
777
|
+
};
|
|
720
778
|
readonly AppProviderProp: {
|
|
721
779
|
readonly group: "app";
|
|
722
780
|
readonly file: "components/app.prop.ts";
|
|
@@ -2319,7 +2377,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2319
2377
|
readonly LineChartProp: {
|
|
2320
2378
|
readonly group: "data-display";
|
|
2321
2379
|
readonly file: "components/charts.prop.ts";
|
|
2322
|
-
readonly vocabulary: readonly [
|
|
2380
|
+
readonly vocabulary: readonly [{
|
|
2381
|
+
readonly field: "onDatumSelect";
|
|
2382
|
+
readonly local: true;
|
|
2383
|
+
readonly reason: "Pointer and keyboard datum selection action, independent of dashboard state.";
|
|
2384
|
+
}, "LabelProp", "DescriptionProp", "SizeProp", "EmptyMessageProp", "ClassNameProp", "IdProp", {
|
|
2323
2385
|
readonly field: "data";
|
|
2324
2386
|
readonly local: true;
|
|
2325
2387
|
readonly reason: "Chart row data (category + value per series).";
|
|
@@ -2372,7 +2434,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2372
2434
|
readonly BarChartProp: {
|
|
2373
2435
|
readonly group: "data-display";
|
|
2374
2436
|
readonly file: "components/charts.prop.ts";
|
|
2375
|
-
readonly vocabulary: readonly [
|
|
2437
|
+
readonly vocabulary: readonly [{
|
|
2438
|
+
readonly field: "onDatumSelect";
|
|
2439
|
+
readonly local: true;
|
|
2440
|
+
readonly reason: "Pointer and keyboard datum selection action, independent of dashboard state.";
|
|
2441
|
+
}, "LabelProp", "DescriptionProp", "SizeProp", "EmptyMessageProp", "ClassNameProp", "IdProp", {
|
|
2376
2442
|
readonly field: "data";
|
|
2377
2443
|
readonly local: true;
|
|
2378
2444
|
readonly reason: "Chart row data (category + value per series).";
|
|
@@ -2462,7 +2528,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2462
2528
|
readonly HeatmapChartProp: {
|
|
2463
2529
|
readonly group: "data-display";
|
|
2464
2530
|
readonly file: "components/charts.prop.ts";
|
|
2465
|
-
readonly vocabulary: readonly [
|
|
2531
|
+
readonly vocabulary: readonly [{
|
|
2532
|
+
readonly field: "onDatumSelect";
|
|
2533
|
+
readonly local: true;
|
|
2534
|
+
readonly reason: "Pointer and keyboard datum selection action, independent of dashboard state.";
|
|
2535
|
+
}, "LabelProp", "DescriptionProp", "SizeProp", "EmptyMessageProp", "ClassNameProp", "IdProp", {
|
|
2466
2536
|
readonly field: "rows";
|
|
2467
2537
|
readonly local: true;
|
|
2468
2538
|
readonly reason: "Row categories (y axis) — key + translated label.";
|
|
@@ -2499,7 +2569,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2499
2569
|
readonly AreaChartProp: {
|
|
2500
2570
|
readonly group: "data-display";
|
|
2501
2571
|
readonly file: "components/charts.prop.ts";
|
|
2502
|
-
readonly vocabulary: readonly [
|
|
2572
|
+
readonly vocabulary: readonly [{
|
|
2573
|
+
readonly field: "onDatumSelect";
|
|
2574
|
+
readonly local: true;
|
|
2575
|
+
readonly reason: "Pointer and keyboard datum selection action, independent of dashboard state.";
|
|
2576
|
+
}, "LabelProp", "DescriptionProp", "SizeProp", "EmptyMessageProp", "ClassNameProp", "IdProp", {
|
|
2503
2577
|
readonly field: "data";
|
|
2504
2578
|
readonly local: true;
|
|
2505
2579
|
readonly reason: "Chart row data (category + value per series).";
|
|
@@ -2556,7 +2630,11 @@ export declare const COMPONENT_PROP_REGISTRY: {
|
|
|
2556
2630
|
readonly PieChartProp: {
|
|
2557
2631
|
readonly group: "data-display";
|
|
2558
2632
|
readonly file: "components/charts.prop.ts";
|
|
2559
|
-
readonly vocabulary: readonly [
|
|
2633
|
+
readonly vocabulary: readonly [{
|
|
2634
|
+
readonly field: "onDatumSelect";
|
|
2635
|
+
readonly local: true;
|
|
2636
|
+
readonly reason: "Pointer and keyboard datum selection action, independent of dashboard state.";
|
|
2637
|
+
}, "LabelProp", "DescriptionProp", "SizeProp", "EmptyMessageProp", "ClassNameProp", "IdProp", {
|
|
2560
2638
|
readonly field: "data";
|
|
2561
2639
|
readonly local: true;
|
|
2562
2640
|
readonly reason: "Chart row data (one slice per row).";
|