@agent-native/toolkit 0.8.3 → 0.9.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 +30 -0
- package/agent-native.eject.json +33 -0
- package/dist/dashboard/DataTable.d.ts +20 -0
- package/dist/dashboard/DataTable.d.ts.map +1 -0
- package/dist/dashboard/DataTable.js +87 -0
- package/dist/dashboard/DataTable.js.map +1 -0
- package/dist/dashboard/DateRangePicker.d.ts +15 -0
- package/dist/dashboard/DateRangePicker.d.ts.map +1 -0
- package/dist/dashboard/DateRangePicker.js +15 -0
- package/dist/dashboard/DateRangePicker.js.map +1 -0
- package/dist/dashboard/GenericChartPanel.d.ts +57 -0
- package/dist/dashboard/GenericChartPanel.d.ts.map +1 -0
- package/dist/dashboard/GenericChartPanel.js +119 -0
- package/dist/dashboard/GenericChartPanel.js.map +1 -0
- package/dist/dashboard/GenericChartPanel.spec.d.ts +2 -0
- package/dist/dashboard/GenericChartPanel.spec.d.ts.map +1 -0
- package/dist/dashboard/GenericChartPanel.spec.js +41 -0
- package/dist/dashboard/GenericChartPanel.spec.js.map +1 -0
- package/dist/dashboard/MetricCard.d.ts +14 -0
- package/dist/dashboard/MetricCard.d.ts.map +1 -0
- package/dist/dashboard/MetricCard.js +10 -0
- package/dist/dashboard/MetricCard.js.map +1 -0
- package/dist/dashboard/StatsCard.d.ts +16 -0
- package/dist/dashboard/StatsCard.d.ts.map +1 -0
- package/dist/dashboard/StatsCard.js +8 -0
- package/dist/dashboard/StatsCard.js.map +1 -0
- package/dist/dashboard/dashboard-layout.d.ts +49 -0
- package/dist/dashboard/dashboard-layout.d.ts.map +1 -0
- package/dist/dashboard/dashboard-layout.js +231 -0
- package/dist/dashboard/dashboard-layout.js.map +1 -0
- package/dist/dashboard/dashboard-layout.spec.d.ts +2 -0
- package/dist/dashboard/dashboard-layout.spec.d.ts.map +1 -0
- package/dist/dashboard/dashboard-layout.spec.js +52 -0
- package/dist/dashboard/dashboard-layout.spec.js.map +1 -0
- package/dist/dashboard/index.d.ts +7 -0
- package/dist/dashboard/index.d.ts.map +1 -0
- package/dist/dashboard/index.js +7 -0
- package/dist/dashboard/index.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +15 -1
- package/src/dashboard/DataTable.tsx +273 -0
- package/src/dashboard/DateRangePicker.tsx +57 -0
- package/src/dashboard/GenericChartPanel.spec.tsx +53 -0
- package/src/dashboard/GenericChartPanel.tsx +461 -0
- package/src/dashboard/MetricCard.tsx +54 -0
- package/src/dashboard/StatsCard.tsx +49 -0
- package/src/dashboard/dashboard-layout.spec.ts +79 -0
- package/src/dashboard/dashboard-layout.ts +329 -0
- package/src/dashboard/index.ts +6 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
export const MIN_DASHBOARD_COLUMNS = 1;
|
|
2
|
+
export const MAX_DASHBOARD_COLUMNS = 6;
|
|
3
|
+
export const DEFAULT_DASHBOARD_COLUMNS = 2;
|
|
4
|
+
|
|
5
|
+
export interface DashboardLayoutPanel {
|
|
6
|
+
id: string;
|
|
7
|
+
width?: number;
|
|
8
|
+
columns?: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type DashboardPanelRow<TPanel extends DashboardLayoutPanel> = {
|
|
12
|
+
key: string;
|
|
13
|
+
panels: TPanel[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type DashboardPanelGroup<TPanel extends DashboardLayoutPanel> = {
|
|
17
|
+
key: string;
|
|
18
|
+
section: TPanel | null;
|
|
19
|
+
panels: TPanel[];
|
|
20
|
+
rows: DashboardPanelRow<TPanel>[];
|
|
21
|
+
columns: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type DashboardDropSlot =
|
|
25
|
+
| { type: "row"; groupKey: string; rowIndex: number }
|
|
26
|
+
| { type: "column"; groupKey: string; rowIndex: number; columnIndex: number };
|
|
27
|
+
|
|
28
|
+
export type DashboardColumnExpansion = {
|
|
29
|
+
columns: number;
|
|
30
|
+
sectionPanelId: string | null;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export type DashboardLayoutOptions<TPanel extends DashboardLayoutPanel> = {
|
|
34
|
+
isSection?: (panel: TPanel) => boolean;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export function clampDashboardColumns(value: unknown): number {
|
|
38
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
39
|
+
return DEFAULT_DASHBOARD_COLUMNS;
|
|
40
|
+
return Math.min(
|
|
41
|
+
MAX_DASHBOARD_COLUMNS,
|
|
42
|
+
Math.max(MIN_DASHBOARD_COLUMNS, Math.floor(value)),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function clampPanelWidth(value: unknown, gridColumns: number): number {
|
|
47
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return 1;
|
|
48
|
+
return Math.min(
|
|
49
|
+
clampDashboardColumns(gridColumns),
|
|
50
|
+
Math.max(1, Math.floor(value)),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function rebalanceRowWidths<TPanel extends DashboardLayoutPanel>(
|
|
55
|
+
panels: TPanel[],
|
|
56
|
+
columns: number,
|
|
57
|
+
): TPanel[] {
|
|
58
|
+
if (panels.length === 0) return [];
|
|
59
|
+
const safeColumns = clampDashboardColumns(columns);
|
|
60
|
+
const base = Math.max(1, Math.floor(safeColumns / panels.length));
|
|
61
|
+
const remainder = safeColumns % panels.length;
|
|
62
|
+
return panels.map((panel, index) => ({
|
|
63
|
+
...panel,
|
|
64
|
+
width: base + (index < remainder ? 1 : 0),
|
|
65
|
+
}));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function buildDashboardRows<TPanel extends DashboardLayoutPanel>(
|
|
69
|
+
panels: TPanel[],
|
|
70
|
+
columns: number,
|
|
71
|
+
): DashboardPanelRow<TPanel>[] {
|
|
72
|
+
const safeColumns = clampDashboardColumns(columns);
|
|
73
|
+
const rows: DashboardPanelRow<TPanel>[] = [];
|
|
74
|
+
let current: TPanel[] = [];
|
|
75
|
+
let usedColumns = 0;
|
|
76
|
+
const push = () => {
|
|
77
|
+
if (!current.length) return;
|
|
78
|
+
rows.push({
|
|
79
|
+
key: current.map((panel) => panel.id).join(":") || `empty-${rows.length}`,
|
|
80
|
+
panels: current,
|
|
81
|
+
});
|
|
82
|
+
current = [];
|
|
83
|
+
usedColumns = 0;
|
|
84
|
+
};
|
|
85
|
+
for (const panel of panels) {
|
|
86
|
+
const width = clampPanelWidth(panel.width, safeColumns);
|
|
87
|
+
if (
|
|
88
|
+
current.length &&
|
|
89
|
+
(usedColumns + width > safeColumns || current.length >= safeColumns)
|
|
90
|
+
)
|
|
91
|
+
push();
|
|
92
|
+
current.push(panel);
|
|
93
|
+
usedColumns += width;
|
|
94
|
+
if (usedColumns >= safeColumns || current.length >= safeColumns) push();
|
|
95
|
+
}
|
|
96
|
+
push();
|
|
97
|
+
return rows;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function buildDashboardPanelGroups<TPanel extends DashboardLayoutPanel>(
|
|
101
|
+
panels: TPanel[],
|
|
102
|
+
dashboardColumns: number,
|
|
103
|
+
{ isSection = () => false }: DashboardLayoutOptions<TPanel> = {},
|
|
104
|
+
): DashboardPanelGroup<TPanel>[] {
|
|
105
|
+
const defaultColumns = clampDashboardColumns(dashboardColumns);
|
|
106
|
+
const groups: DashboardPanelGroup<TPanel>[] = [];
|
|
107
|
+
let current: Omit<DashboardPanelGroup<TPanel>, "rows"> = {
|
|
108
|
+
key: "intro",
|
|
109
|
+
section: null,
|
|
110
|
+
panels: [],
|
|
111
|
+
columns: defaultColumns,
|
|
112
|
+
};
|
|
113
|
+
const push = () => {
|
|
114
|
+
if (!current.section && !current.panels.length) return;
|
|
115
|
+
groups.push({
|
|
116
|
+
...current,
|
|
117
|
+
rows: buildDashboardRows(current.panels, current.columns),
|
|
118
|
+
});
|
|
119
|
+
};
|
|
120
|
+
for (const panel of panels) {
|
|
121
|
+
if (isSection(panel)) {
|
|
122
|
+
push();
|
|
123
|
+
current = {
|
|
124
|
+
key: panel.id,
|
|
125
|
+
section: panel,
|
|
126
|
+
panels: [],
|
|
127
|
+
columns: clampDashboardColumns(panel.columns ?? defaultColumns),
|
|
128
|
+
};
|
|
129
|
+
} else {
|
|
130
|
+
current.panels.push(panel);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
push();
|
|
134
|
+
return groups;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function flattenGroups<TPanel extends DashboardLayoutPanel>(
|
|
138
|
+
groups: DashboardPanelGroup<TPanel>[],
|
|
139
|
+
): TPanel[] {
|
|
140
|
+
return groups.flatMap((group) => [
|
|
141
|
+
...(group.section ? [group.section] : []),
|
|
142
|
+
...group.rows.flatMap((row) =>
|
|
143
|
+
rebalanceRowWidths(row.panels, group.columns),
|
|
144
|
+
),
|
|
145
|
+
]);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function removePanelFromLayout<TPanel extends DashboardLayoutPanel>(
|
|
149
|
+
panels: TPanel[],
|
|
150
|
+
panelId: string,
|
|
151
|
+
dashboardColumns: number,
|
|
152
|
+
options?: DashboardLayoutOptions<TPanel>,
|
|
153
|
+
): TPanel[] {
|
|
154
|
+
const groups = buildDashboardPanelGroups(panels, dashboardColumns, options);
|
|
155
|
+
return flattenGroups(
|
|
156
|
+
groups
|
|
157
|
+
.map((group) => ({
|
|
158
|
+
...group,
|
|
159
|
+
section: group.section?.id === panelId ? null : group.section,
|
|
160
|
+
rows: group.rows
|
|
161
|
+
.map((row) => ({
|
|
162
|
+
...row,
|
|
163
|
+
panels: row.panels.filter((panel) => panel.id !== panelId),
|
|
164
|
+
}))
|
|
165
|
+
.filter((row) => row.panels.length),
|
|
166
|
+
}))
|
|
167
|
+
.filter((group) => group.section || group.rows.length),
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function dropSlotId(slot: DashboardDropSlot): string {
|
|
172
|
+
return slot.type === "row"
|
|
173
|
+
? `dashboard-drop:row:${slot.groupKey}:${slot.rowIndex}`
|
|
174
|
+
: `dashboard-drop:column:${slot.groupKey}:${slot.rowIndex}:${slot.columnIndex}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function readDropSlot(value: unknown): DashboardDropSlot | null {
|
|
178
|
+
if (!value || typeof value !== "object") return null;
|
|
179
|
+
const slot = (value as { slot?: unknown }).slot;
|
|
180
|
+
if (!slot || typeof slot !== "object") return null;
|
|
181
|
+
const candidate = slot as Partial<DashboardDropSlot>;
|
|
182
|
+
if (
|
|
183
|
+
candidate.type === "row" &&
|
|
184
|
+
typeof candidate.groupKey === "string" &&
|
|
185
|
+
typeof candidate.rowIndex === "number"
|
|
186
|
+
) {
|
|
187
|
+
return {
|
|
188
|
+
type: "row",
|
|
189
|
+
groupKey: candidate.groupKey,
|
|
190
|
+
rowIndex: candidate.rowIndex,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (
|
|
194
|
+
candidate.type === "column" &&
|
|
195
|
+
typeof candidate.groupKey === "string" &&
|
|
196
|
+
typeof candidate.rowIndex === "number" &&
|
|
197
|
+
typeof candidate.columnIndex === "number"
|
|
198
|
+
) {
|
|
199
|
+
return {
|
|
200
|
+
type: "column",
|
|
201
|
+
groupKey: candidate.groupKey,
|
|
202
|
+
rowIndex: candidate.rowIndex,
|
|
203
|
+
columnIndex: candidate.columnIndex,
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function sameDropSlot(
|
|
210
|
+
a: DashboardDropSlot | null,
|
|
211
|
+
b: DashboardDropSlot,
|
|
212
|
+
): boolean {
|
|
213
|
+
return (
|
|
214
|
+
!!a &&
|
|
215
|
+
a.type === b.type &&
|
|
216
|
+
a.groupKey === b.groupKey &&
|
|
217
|
+
a.rowIndex === b.rowIndex &&
|
|
218
|
+
(a.type === "row" ||
|
|
219
|
+
(b.type === "column" && a.columnIndex === b.columnIndex))
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function findPanel<TPanel extends DashboardLayoutPanel>(
|
|
224
|
+
groups: DashboardPanelGroup<TPanel>[],
|
|
225
|
+
panelId: string,
|
|
226
|
+
) {
|
|
227
|
+
for (const group of groups) {
|
|
228
|
+
for (let rowIndex = 0; rowIndex < group.rows.length; rowIndex++) {
|
|
229
|
+
const columnIndex = group.rows[rowIndex].panels.findIndex(
|
|
230
|
+
(panel) => panel.id === panelId,
|
|
231
|
+
);
|
|
232
|
+
if (columnIndex >= 0) return { group, rowIndex, columnIndex };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return null;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function columnExpansionForDropSlot<TPanel extends DashboardLayoutPanel>(
|
|
239
|
+
groups: DashboardPanelGroup<TPanel>[],
|
|
240
|
+
panelId: string,
|
|
241
|
+
slot: DashboardDropSlot,
|
|
242
|
+
): DashboardColumnExpansion | null {
|
|
243
|
+
if (slot.type !== "column") return null;
|
|
244
|
+
const group = groups.find((item) => item.key === slot.groupKey);
|
|
245
|
+
const row = group?.rows[slot.rowIndex];
|
|
246
|
+
if (!group || !row) return null;
|
|
247
|
+
const required = row.panels.some((panel) => panel.id === panelId)
|
|
248
|
+
? row.panels.length
|
|
249
|
+
: row.panels.length + 1;
|
|
250
|
+
return required > group.columns
|
|
251
|
+
? {
|
|
252
|
+
columns: clampDashboardColumns(required),
|
|
253
|
+
sectionPanelId: group.section?.id ?? null,
|
|
254
|
+
}
|
|
255
|
+
: null;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/** Moves a panel by visible row/column slot and rebalances persisted widths. */
|
|
259
|
+
export function movePanelToDropSlot<TPanel extends DashboardLayoutPanel>(
|
|
260
|
+
panels: TPanel[],
|
|
261
|
+
panelId: string,
|
|
262
|
+
slot: DashboardDropSlot,
|
|
263
|
+
dashboardColumns: number,
|
|
264
|
+
options?: DashboardLayoutOptions<TPanel>,
|
|
265
|
+
): TPanel[] {
|
|
266
|
+
const groups = buildDashboardPanelGroups(panels, dashboardColumns, options);
|
|
267
|
+
const source = findPanel(groups, panelId);
|
|
268
|
+
if (!source) return panels;
|
|
269
|
+
const moving = source.group.rows[source.rowIndex].panels[source.columnIndex];
|
|
270
|
+
const sourceWasSingle =
|
|
271
|
+
source.group.rows[source.rowIndex].panels.length === 1;
|
|
272
|
+
const next = groups.map((group) => ({
|
|
273
|
+
...group,
|
|
274
|
+
rows: group.rows
|
|
275
|
+
.map((row) => ({
|
|
276
|
+
...row,
|
|
277
|
+
panels: row.panels.filter((panel) => panel.id !== panelId),
|
|
278
|
+
}))
|
|
279
|
+
.filter((row) => row.panels.length),
|
|
280
|
+
}));
|
|
281
|
+
const target = next.find((group) => group.key === slot.groupKey);
|
|
282
|
+
if (!target) return panels;
|
|
283
|
+
if (slot.type === "row") {
|
|
284
|
+
let rowIndex = slot.rowIndex;
|
|
285
|
+
if (
|
|
286
|
+
source.group.key === target.key &&
|
|
287
|
+
sourceWasSingle &&
|
|
288
|
+
source.rowIndex < rowIndex
|
|
289
|
+
)
|
|
290
|
+
rowIndex--;
|
|
291
|
+
if (
|
|
292
|
+
source.group.key === target.key &&
|
|
293
|
+
sourceWasSingle &&
|
|
294
|
+
source.rowIndex === rowIndex
|
|
295
|
+
)
|
|
296
|
+
return panels;
|
|
297
|
+
target.rows.splice(Math.max(0, rowIndex), 0, {
|
|
298
|
+
key: moving.id,
|
|
299
|
+
panels: [moving],
|
|
300
|
+
});
|
|
301
|
+
} else {
|
|
302
|
+
let rowIndex = slot.rowIndex;
|
|
303
|
+
if (
|
|
304
|
+
source.group.key === target.key &&
|
|
305
|
+
sourceWasSingle &&
|
|
306
|
+
source.rowIndex < rowIndex
|
|
307
|
+
)
|
|
308
|
+
rowIndex--;
|
|
309
|
+
const targetRow = target.rows[rowIndex];
|
|
310
|
+
if (!targetRow) return panels;
|
|
311
|
+
let columnIndex = slot.columnIndex;
|
|
312
|
+
if (
|
|
313
|
+
source.group.key === target.key &&
|
|
314
|
+
source.rowIndex === slot.rowIndex &&
|
|
315
|
+
source.columnIndex < columnIndex
|
|
316
|
+
)
|
|
317
|
+
columnIndex--;
|
|
318
|
+
targetRow.panels.splice(
|
|
319
|
+
Math.max(0, Math.min(columnIndex, targetRow.panels.length)),
|
|
320
|
+
0,
|
|
321
|
+
moving,
|
|
322
|
+
);
|
|
323
|
+
target.columns = Math.max(
|
|
324
|
+
target.columns,
|
|
325
|
+
clampDashboardColumns(targetRow.panels.length),
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
return flattenGroups(next);
|
|
329
|
+
}
|