@gridframe/core 1.0.0 → 1.0.1

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/src/index.ts DELETED
@@ -1,454 +0,0 @@
1
- import { z } from "zod";
2
-
3
- const nullableScalarSchema = z.union([z.string(), z.number(), z.null()]);
4
-
5
- export const VisualizationTypeSchema = z.enum([
6
- "metric",
7
- "area",
8
- "bar",
9
- "line",
10
- "pie",
11
- "radar",
12
- "radial",
13
- "table",
14
- ]);
15
- export type VisualizationType = z.infer<typeof VisualizationTypeSchema>;
16
-
17
- export const DashboardFooterConfigSchema = z.object({
18
- text: z.string(),
19
- href: z.string().optional(),
20
- });
21
- export type DashboardFooterConfig = z.infer<typeof DashboardFooterConfigSchema>;
22
-
23
- export const CardDeeplinkConfigSchema = z.object({
24
- href: z.string(),
25
- label: z.string().optional(),
26
- });
27
- export type CardDeeplinkConfig = z.infer<typeof CardDeeplinkConfigSchema>;
28
-
29
- export const DashboardCardLayoutSchema = z.object({
30
- x: z.number().int().nonnegative(),
31
- y: z.number().int().nonnegative(),
32
- width: z.number().int().positive(),
33
- height: z.number().int().positive(),
34
- });
35
- export type DashboardCardLayout = z.infer<typeof DashboardCardLayoutSchema>;
36
-
37
- export const DashboardCardConfigSchema = z.object({
38
- id: z.string(),
39
- name: z.string(),
40
- visualization: VisualizationTypeSchema,
41
- query: z.string(),
42
- deeplink: CardDeeplinkConfigSchema.optional(),
43
- layout: DashboardCardLayoutSchema.optional(),
44
- });
45
- export type DashboardCardConfig = z.infer<typeof DashboardCardConfigSchema>;
46
-
47
- export const ApiDashboardCardConfigSchema = DashboardCardConfigSchema.extend({
48
- layout: DashboardCardLayoutSchema,
49
- });
50
- export type ApiDashboardCardConfig = z.infer<
51
- typeof ApiDashboardCardConfigSchema
52
- >;
53
-
54
- export const PanelDashboardConfigSchema = z.object({
55
- title: z.string(),
56
- description: z.string().optional(),
57
- footer: DashboardFooterConfigSchema.optional(),
58
- cards: z.array(DashboardCardConfigSchema),
59
- });
60
- export type PanelDashboardConfig = z.infer<typeof PanelDashboardConfigSchema>;
61
-
62
- export const ApiPanelDashboardConfigSchema = PanelDashboardConfigSchema.extend({
63
- cards: z.array(ApiDashboardCardConfigSchema),
64
- });
65
- export type ApiPanelDashboardConfig = z.infer<
66
- typeof ApiPanelDashboardConfigSchema
67
- >;
68
-
69
- export const MetricTrendSchema = z.object({
70
- direction: z.enum(["up", "down", "neutral"]),
71
- value: z.string(),
72
- label: z.string().optional(),
73
- });
74
- export type MetricTrend = z.infer<typeof MetricTrendSchema>;
75
-
76
- export const MetricCardDataSchema = z.object({
77
- visualization: z.literal("metric"),
78
- value: z.union([z.string(), z.number()]),
79
- label: z.string().optional(),
80
- helperText: z.string().optional(),
81
- trend: MetricTrendSchema.optional(),
82
- });
83
- export type MetricCardData = z.infer<typeof MetricCardDataSchema>;
84
-
85
- export const ChartSeriesSchema = z.object({
86
- key: z.string(),
87
- label: z.string(),
88
- color: z.string(),
89
- icon: z.string().optional(),
90
- });
91
- export type ChartSeries = z.infer<typeof ChartSeriesSchema>;
92
-
93
- export const ChartDatumSchema = z.record(z.string(), nullableScalarSchema);
94
- export type ChartDatum = z.infer<typeof ChartDatumSchema>;
95
-
96
- export const ChartTooltipOptionsSchema = z.object({
97
- indicator: z.enum(["dot", "line", "dashed", "none"]).optional(),
98
- hideLabel: z.boolean().optional(),
99
- label: z.string().optional(),
100
- labelFormatter: z.literal("date").optional(),
101
- valueFormatter: z.enum(["compact", "currency", "percent"]).optional(),
102
- advanced: z.boolean().optional(),
103
- });
104
- export type ChartTooltipOptions = z.infer<typeof ChartTooltipOptionsSchema>;
105
-
106
- export const BarChartCardDataSchema = z.object({
107
- visualization: z.literal("bar"),
108
- indexKey: z.string(),
109
- data: z.array(ChartDatumSchema),
110
- series: z.array(ChartSeriesSchema),
111
- variant: z.string().optional(),
112
- layout: z.enum(["vertical", "horizontal"]).optional(),
113
- stacked: z.boolean().optional(),
114
- showLabels: z.boolean().optional(),
115
- customLabels: z.boolean().optional(),
116
- mixed: z.boolean().optional(),
117
- activeIndex: z.number().int().optional(),
118
- tooltip: ChartTooltipOptionsSchema.optional(),
119
- });
120
- export type BarChartCardData = z.infer<typeof BarChartCardDataSchema>;
121
-
122
- export const AreaChartCardDataSchema = z.object({
123
- visualization: z.literal("area"),
124
- indexKey: z.string(),
125
- data: z.array(ChartDatumSchema),
126
- series: z.array(ChartSeriesSchema),
127
- variant: z.string().optional(),
128
- curveType: z.enum(["natural", "linear", "step"]).optional(),
129
- stacked: z.boolean().optional(),
130
- stackOffset: z.literal("expand").optional(),
131
- showLegend: z.boolean().optional(),
132
- showGradient: z.boolean().optional(),
133
- showAxes: z.boolean().optional(),
134
- interactive: z.boolean().optional(),
135
- tooltip: ChartTooltipOptionsSchema.optional(),
136
- });
137
- export type AreaChartCardData = z.infer<typeof AreaChartCardDataSchema>;
138
-
139
- export const LineChartCardDataSchema = z.object({
140
- visualization: z.literal("line"),
141
- indexKey: z.string(),
142
- data: z.array(ChartDatumSchema),
143
- series: z.array(ChartSeriesSchema),
144
- variant: z.string().optional(),
145
- curveType: z.enum(["monotone", "linear", "step"]).optional(),
146
- showDots: z.boolean().optional(),
147
- customDots: z.boolean().optional(),
148
- colorDots: z.boolean().optional(),
149
- showLabels: z.boolean().optional(),
150
- customLabels: z.boolean().optional(),
151
- interactive: z.boolean().optional(),
152
- tooltip: ChartTooltipOptionsSchema.optional(),
153
- });
154
- export type LineChartCardData = z.infer<typeof LineChartCardDataSchema>;
155
-
156
- export const PieChartCardDataSchema = z.object({
157
- visualization: z.literal("pie"),
158
- nameKey: z.string(),
159
- valueKey: z.string(),
160
- data: z.array(ChartDatumSchema),
161
- series: z.array(ChartSeriesSchema),
162
- variant: z.string().optional(),
163
- separator: z.boolean().optional(),
164
- showLabels: z.boolean().optional(),
165
- customLabels: z.boolean().optional(),
166
- labelList: z.boolean().optional(),
167
- showLegend: z.boolean().optional(),
168
- donut: z.boolean().optional(),
169
- activeIndex: z.number().int().optional(),
170
- centerText: z.string().optional(),
171
- stacked: z.boolean().optional(),
172
- interactive: z.boolean().optional(),
173
- tooltip: ChartTooltipOptionsSchema.optional(),
174
- });
175
- export type PieChartCardData = z.infer<typeof PieChartCardDataSchema>;
176
-
177
- export const RadarChartCardDataSchema = z.object({
178
- visualization: z.literal("radar"),
179
- indexKey: z.string(),
180
- data: z.array(ChartDatumSchema),
181
- series: z.array(ChartSeriesSchema),
182
- variant: z.string().optional(),
183
- showDots: z.boolean().optional(),
184
- linesOnly: z.boolean().optional(),
185
- customLabels: z.boolean().optional(),
186
- gridType: z.enum(["polygon", "circle"]).optional(),
187
- gridFill: z.boolean().optional(),
188
- gridLines: z.boolean().optional(),
189
- radialLines: z.boolean().optional(),
190
- showLegend: z.boolean().optional(),
191
- tooltip: ChartTooltipOptionsSchema.optional(),
192
- });
193
- export type RadarChartCardData = z.infer<typeof RadarChartCardDataSchema>;
194
-
195
- export const RadialChartCardDataSchema = z.object({
196
- visualization: z.literal("radial"),
197
- nameKey: z.string(),
198
- valueKey: z.string(),
199
- data: z.array(ChartDatumSchema),
200
- series: z.array(ChartSeriesSchema),
201
- variant: z.string().optional(),
202
- showLabel: z.boolean().optional(),
203
- showGrid: z.boolean().optional(),
204
- centerText: z.string().optional(),
205
- shape: z.enum(["round", "square"]).optional(),
206
- stacked: z.boolean().optional(),
207
- tooltip: ChartTooltipOptionsSchema.optional(),
208
- });
209
- export type RadialChartCardData = z.infer<typeof RadialChartCardDataSchema>;
210
-
211
- export const TableColumnSchema = z.object({
212
- key: z.string(),
213
- label: z.string(),
214
- align: z.enum(["left", "right"]).optional(),
215
- });
216
- export type TableColumn = z.infer<typeof TableColumnSchema>;
217
-
218
- export const TableRowSchema = z.record(z.string(), nullableScalarSchema);
219
- export type TableRow = z.infer<typeof TableRowSchema>;
220
-
221
- export const TableCardDataSchema = z.object({
222
- visualization: z.literal("table"),
223
- columns: z.array(TableColumnSchema),
224
- rows: z.array(TableRowSchema),
225
- });
226
- export type TableCardData = z.infer<typeof TableCardDataSchema>;
227
-
228
- export const PanelCardPayloadSchema = z.discriminatedUnion("visualization", [
229
- MetricCardDataSchema,
230
- AreaChartCardDataSchema,
231
- BarChartCardDataSchema,
232
- LineChartCardDataSchema,
233
- PieChartCardDataSchema,
234
- RadarChartCardDataSchema,
235
- RadialChartCardDataSchema,
236
- TableCardDataSchema,
237
- ]);
238
- export type PanelCardPayload = z.infer<typeof PanelCardPayloadSchema>;
239
-
240
- export const SourceDataTableSchema = z.object({
241
- columns: z.array(TableColumnSchema),
242
- rows: z.array(TableRowSchema),
243
- });
244
- export type SourceDataTable = z.infer<typeof SourceDataTableSchema>;
245
-
246
- const panelCardDataSuccessSchema = z.object({
247
- status: z.literal("success"),
248
- data: PanelCardPayloadSchema,
249
- sourceData: SourceDataTableSchema.optional(),
250
- });
251
- const panelCardDataEmptySchema = z.object({
252
- status: z.literal("empty"),
253
- message: z.string().optional(),
254
- });
255
- const panelCardDataErrorSchema = z.object({
256
- status: z.literal("error"),
257
- message: z.string(),
258
- });
259
-
260
- export const PanelCardDataResponseSchema = z.discriminatedUnion("status", [
261
- panelCardDataSuccessSchema,
262
- panelCardDataEmptySchema,
263
- panelCardDataErrorSchema,
264
- ]);
265
- export type PanelCardDataResponse = z.infer<typeof PanelCardDataResponseSchema>;
266
-
267
- export const PanelCardDataWithSourceResponseSchema = z.discriminatedUnion(
268
- "status",
269
- [
270
- panelCardDataSuccessSchema.extend({ sourceData: SourceDataTableSchema }),
271
- panelCardDataEmptySchema,
272
- panelCardDataErrorSchema,
273
- ],
274
- );
275
- export type PanelCardDataWithSourceResponse = z.infer<
276
- typeof PanelCardDataWithSourceResponseSchema
277
- >;
278
-
279
- export const DashboardSummarySchema = z.object({
280
- id: z.string(),
281
- title: z.string(),
282
- isDefault: z.boolean(),
283
- });
284
- export type DashboardSummary = z.infer<typeof DashboardSummarySchema>;
285
-
286
- export const DashboardDocumentSchema = z.object({
287
- id: z.string(),
288
- revision: z.string(),
289
- config: ApiPanelDashboardConfigSchema,
290
- });
291
- export type DashboardDocument = z.infer<typeof DashboardDocumentSchema>;
292
-
293
- export const DashboardBootstrapRequestSchema = z.object({
294
- dashboardId: z.string().optional(),
295
- });
296
- export type DashboardBootstrapRequest = z.infer<
297
- typeof DashboardBootstrapRequestSchema
298
- >;
299
-
300
- export const DashboardBootstrapResponseSchema = z.object({
301
- dashboards: z.array(DashboardSummarySchema),
302
- dashboard: DashboardDocumentSchema,
303
- });
304
- export type DashboardBootstrapResponse = z.infer<
305
- typeof DashboardBootstrapResponseSchema
306
- >;
307
-
308
- export const DashboardApiErrorCodeSchema = z.enum([
309
- "INVALID_REQUEST",
310
- "DASHBOARD_NOT_FOUND",
311
- "DASHBOARD_LOAD_FAILED",
312
- "DASHBOARD_CARD_NOT_FOUND",
313
- "CARD_QUERY_FAILED",
314
- "REVISION_CONFLICT",
315
- "CARD_ALREADY_ADDED",
316
- ]);
317
- export type DashboardApiErrorCode = z.infer<typeof DashboardApiErrorCodeSchema>;
318
-
319
- export const DashboardApiErrorSchema = z.object({
320
- error: z.object({
321
- code: DashboardApiErrorCodeSchema,
322
- message: z.string(),
323
- }),
324
- });
325
- export type DashboardApiError = z.infer<typeof DashboardApiErrorSchema>;
326
-
327
- export const UpdateDashboardLayoutRequestSchema = z.object({
328
- revision: z.string(),
329
- cards: z.array(
330
- z.object({
331
- id: z.string(),
332
- ...DashboardCardLayoutSchema.shape,
333
- }),
334
- ),
335
- });
336
- export type UpdateDashboardLayoutRequest = z.infer<
337
- typeof UpdateDashboardLayoutRequestSchema
338
- >;
339
- export type DashboardLayoutItem = UpdateDashboardLayoutRequest["cards"][number];
340
-
341
- export const UpdateDashboardCardRequestSchema = z.object({
342
- revision: z.string(),
343
- name: z.string(),
344
- });
345
- export type UpdateDashboardCardRequest = z.infer<
346
- typeof UpdateDashboardCardRequestSchema
347
- >;
348
-
349
- export const CardLibraryItemSchema = z.object({
350
- key: z.string(),
351
- name: z.string(),
352
- description: z.string().optional(),
353
- visualization: VisualizationTypeSchema,
354
- defaultLayout: z.object({
355
- width: z.number().int().positive(),
356
- height: z.number().int().positive(),
357
- }),
358
- addedCardId: z.string().optional(),
359
- });
360
- export type CardLibraryItem = z.infer<typeof CardLibraryItemSchema>;
361
-
362
- export const CardLibraryResponseSchema = z.object({
363
- items: z.array(CardLibraryItemSchema),
364
- });
365
- export type CardLibraryResponse = z.infer<typeof CardLibraryResponseSchema>;
366
-
367
- export const AddDashboardCardRequestSchema = z.object({
368
- revision: z.string(),
369
- libraryItemKey: z.string(),
370
- });
371
- export type AddDashboardCardRequest = z.infer<
372
- typeof AddDashboardCardRequestSchema
373
- >;
374
-
375
- export const RemoveDashboardCardRequestSchema = z.object({
376
- revision: z.string(),
377
- });
378
- export type RemoveDashboardCardRequest = z.infer<
379
- typeof RemoveDashboardCardRequestSchema
380
- >;
381
-
382
- export const DashboardCardMutationResponseSchema = z.object({
383
- dashboard: DashboardDocumentSchema,
384
- cardLibrary: CardLibraryResponseSchema,
385
- });
386
- export type DashboardCardMutationResponse = z.infer<
387
- typeof DashboardCardMutationResponseSchema
388
- >;
389
-
390
- export const DASHBOARD_GRID_COLUMNS = 4;
391
-
392
- export type DashboardLayoutValidationResult =
393
- | { valid: true }
394
- | { valid: false; errors: string[] };
395
-
396
- export function validateDashboardLayout(
397
- cards: readonly DashboardLayoutItem[],
398
- expectedCardIds: readonly string[],
399
- ): DashboardLayoutValidationResult {
400
- const errors: string[] = [];
401
- const cardIds = cards.map((card) => card.id);
402
- const uniqueCardIds = new Set(cardIds);
403
-
404
- if (uniqueCardIds.size !== cardIds.length) {
405
- errors.push("Card IDs must be unique");
406
- }
407
-
408
- const expectedIds = new Set(expectedCardIds);
409
- if (
410
- cards.length !== expectedCardIds.length ||
411
- [...uniqueCardIds].some((cardId) => !expectedIds.has(cardId)) ||
412
- [...expectedIds].some((cardId) => !uniqueCardIds.has(cardId))
413
- ) {
414
- errors.push("Layout must contain exactly the Dashboard's Cards");
415
- }
416
-
417
- for (const card of cards) {
418
- if (card.x + card.width > DASHBOARD_GRID_COLUMNS) {
419
- errors.push(
420
- `Card ${card.id} extends beyond the ${DASHBOARD_GRID_COLUMNS}-column grid`,
421
- );
422
- }
423
- }
424
-
425
- for (let index = 0; index < cards.length; index += 1) {
426
- const first = cards[index];
427
-
428
- for (
429
- let otherIndex = index + 1;
430
- otherIndex < cards.length;
431
- otherIndex += 1
432
- ) {
433
- const second = cards[otherIndex];
434
-
435
- if (first && second && rectanglesOverlap(first, second)) {
436
- errors.push(`Cards ${first.id} and ${second.id} overlap`);
437
- }
438
- }
439
- }
440
-
441
- return errors.length ? { valid: false, errors } : { valid: true };
442
- }
443
-
444
- function rectanglesOverlap(
445
- first: DashboardCardLayout,
446
- second: DashboardCardLayout,
447
- ) {
448
- return (
449
- first.x < second.x + second.width &&
450
- first.x + first.width > second.x &&
451
- first.y < second.y + second.height &&
452
- first.y + first.height > second.y
453
- );
454
- }