astryx-dracula 0.2.1 → 0.3.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/BRAND.md +30 -18
- package/README.md +17 -12
- package/USAGE.md +29 -10
- package/astryx-dracula.js +19 -13
- package/astryx-dracula.variants.d.ts +15 -0
- package/package.json +59 -17
- package/shared/auth-chrome-config.ts +18 -0
- package/shared/auth-chrome.tsx +25 -0
- package/shared/auth-copy.ts +22 -0
- package/shared/chaptered-doc-config.ts +92 -0
- package/shared/chaptered-doc.tsx +332 -0
- package/shared/chart-hues.ts +27 -0
- package/shared/chart-labels.tsx +44 -0
- package/shared/chart-legend.tsx +100 -0
- package/shared/chart-panel-style.ts +13 -0
- package/shared/data-bar.tsx +160 -0
- package/shared/gallery-image.ts +20 -0
- package/shared/login-demo.ts +7 -0
- package/shared/metric-delta.tsx +36 -0
- package/shared/order-desk.tsx +273 -0
- package/shared/revenue-chart.tsx +174 -0
- package/shared/scene-castle.tsx +322 -0
- package/shared/scene-frame.tsx +88 -0
- package/shared/scene-hues.ts +49 -0
- package/shared/scene-tile.tsx +128 -0
- package/shared/settings-data.ts +125 -0
- package/shared/settings-rows.tsx +179 -0
- package/shared/sparkline.tsx +94 -0
- package/shared/sso-icons.tsx +30 -0
- package/templates/ai-chat-landing.tsx +66 -33
- package/templates/ai-chat.tsx +36 -19
- package/templates/blank.tsx +4 -3
- package/templates/centered-hero.tsx +14 -125
- package/templates/classic-gallery.tsx +20 -66
- package/templates/contact-form.tsx +59 -39
- package/templates/dashboard-portfolio.tsx +143 -226
- package/templates/dashboard.tsx +122 -122
- package/templates/detail-page.tsx +95 -91
- package/templates/documentation-design.template.mjs +1 -1
- package/templates/documentation-design.tsx +83 -96
- package/templates/documentation-technical.template.mjs +1 -1
- package/templates/documentation-technical.tsx +29 -19
- package/templates/documentation.template.mjs +1 -1
- package/templates/documentation.tsx +52 -74
- package/templates/editor.tsx +112 -84
- package/templates/file-explorer.tsx +131 -84
- package/templates/form-two-column.tsx +61 -125
- package/templates/gallery-hero.tsx +15 -171
- package/templates/ide.tsx +22 -20
- package/templates/incident-console.tsx +104 -70
- package/templates/kanban-board.tsx +99 -42
- package/templates/library.template.mjs +2 -2
- package/templates/library.tsx +37 -42
- package/templates/login-card.tsx +58 -87
- package/templates/login-split.tsx +88 -126
- package/templates/login-sso.tsx +105 -64
- package/templates/login.tsx +107 -53
- package/templates/messaging-shell.tsx +171 -144
- package/templates/mixed-gallery.tsx +29 -130
- package/templates/payment-form.tsx +368 -290
- package/templates/product-detail.tsx +39 -58
- package/templates/product-gallery.tsx +30 -41
- package/templates/product-tour.template.mjs +6 -0
- package/templates/product-tour.tsx +916 -0
- package/templates/settings-dialog.tsx +135 -282
- package/templates/settings-sidebar.tsx +178 -268
- package/templates/settings.tsx +41 -14
- package/templates/shell-nav.tsx +31 -24
- package/templates/shell-side-nav.tsx +29 -20
- package/templates/shell-top-nav.tsx +80 -44
- package/templates/side-gallery.tsx +38 -94
- package/templates/table-grouped.tsx +62 -34
- package/templates/table-page-chart.tsx +23 -302
- package/templates/table-page-heatmap-status.tsx +156 -92
- package/templates/table-page-shoe-store-heatmap.tsx +18 -293
- package/templates/table-page.tsx +23 -7
- package/templates/table.tsx +23 -8
- package/templates/tech-report.template.mjs +6 -0
- package/templates/tech-report.tsx +1486 -0
- package/templates/theme-showcase.template.mjs +1 -1
- package/templates/theme-showcase.tsx +299 -237
- package/theme.css +76 -14
- package/tokens.css +425 -30
|
@@ -1,7 +1,25 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L > (LH > H[g2 a=center] > Hd"Status"[level=1] + IB
|
|
3
|
+
// L > (LH[divider] > H[g2 a=center] > Hd"Status"[level=1] + IB"Filter"[variant=ghost] + IB"Export"[variant=ghost] + B.secondary"Refresh") + (LC[p3] > V[g4] > (V[g3] > C[p4] + Tx"Incidents by day and hour"[t=supporting] + (H[g4] > (H[g2] > Tx[t=supporting])*4)) + (T[hover] > (TR > THC"Incident" + THC"Product" + THC"Description" + THC"Started" + THC"Duration" + THC"On-call" + THC"Status" + THC"Date") + (TR > TC"INC-4008" + TC"Business Suite" + TC"Notification delivery delays" + TC"09:15" + TC"ongoing" + TC"Carlos Mendez" + (TC > Tk.red"Ongoing") + TC"2025-01-15")*5))
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* Status — the incident heatmap for the week plus the log behind it.
|
|
7
|
+
*
|
|
8
|
+
* Frame: page header (title + icon actions) | content column (heatmap, table).
|
|
9
|
+
*
|
|
10
|
+
* Container policy: the day/hour heatmap is a single Card widget; incidents
|
|
11
|
+
* render as dense rows in one edge-to-edge table. Every heat cell prints its
|
|
12
|
+
* count, so the green/orange/red ramp reinforces the numbers rather than
|
|
13
|
+
* carrying the reading on its own, and status is a Token instead of a badge.
|
|
14
|
+
*
|
|
15
|
+
* Responsive contract:
|
|
16
|
+
* no media queries — the heatmap is an SVG that scales to its column, so it
|
|
17
|
+
* reflows at any width. The table declares a ~910px floor across its eight
|
|
18
|
+
* columns and truncates cells (description to two lines), so narrower
|
|
19
|
+
* viewports scroll the table horizontally instead of widening the page.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import type {CSSProperties} from 'react';
|
|
5
23
|
import {
|
|
6
24
|
VStack,
|
|
7
25
|
HStack,
|
|
@@ -15,12 +33,16 @@ import {Button} from '@astryxdesign/core/Button';
|
|
|
15
33
|
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
16
34
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
17
35
|
import {Avatar} from '@astryxdesign/core/Avatar';
|
|
18
|
-
import {
|
|
36
|
+
import {Token} from '@astryxdesign/core/Token';
|
|
19
37
|
import {Card} from '@astryxdesign/core/Card';
|
|
20
38
|
import {Link} from '@astryxdesign/core/Link';
|
|
21
39
|
import {Table, proportional, pixel} from '@astryxdesign/core/Table';
|
|
22
40
|
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
23
41
|
import {Filter, Download, RotateCw} from 'lucide-react';
|
|
42
|
+
import {CHART_HUES} from 'astryx-dracula/shared/chart-hues';
|
|
43
|
+
import {ChartLegend, type ChartLegendEntry} from 'astryx-dracula/shared/chart-legend';
|
|
44
|
+
import {CHART_PANEL_STYLE} from 'astryx-dracula/shared/chart-panel-style';
|
|
45
|
+
import {ChartLabel} from 'astryx-dracula/shared/chart-labels';
|
|
24
46
|
|
|
25
47
|
// ============= ICONS (verified lucide-react exports) =============
|
|
26
48
|
// Filter ← FunnelIcon, Download ← ArrowDownTrayIcon, RotateCw ← ArrowPathIcon.
|
|
@@ -58,7 +80,7 @@ const incidents: IncidentRow[] = [
|
|
|
58
80
|
id: 'INC-4008',
|
|
59
81
|
product: 'Business Suite',
|
|
60
82
|
title:
|
|
61
|
-
'Notification delivery delays affecting mobile push and email channels
|
|
83
|
+
'Notification delivery delays affecting mobile push and email channels, queue saturation under investigation',
|
|
62
84
|
severity: 'major',
|
|
63
85
|
oncall: 'Carlos Mendez',
|
|
64
86
|
status: 'ongoing',
|
|
@@ -73,7 +95,7 @@ const incidents: IncidentRow[] = [
|
|
|
73
95
|
id: 'INC-4001',
|
|
74
96
|
product: 'Ads Manager',
|
|
75
97
|
title:
|
|
76
|
-
'Campaign creation timing out for ~12% of advertisers in EMEA region
|
|
98
|
+
'Campaign creation timing out for ~12% of advertisers in EMEA region, root caused to elevated DB write latency',
|
|
77
99
|
severity: 'critical',
|
|
78
100
|
oncall: 'Sarah Chen',
|
|
79
101
|
status: 'resolved',
|
|
@@ -160,7 +182,7 @@ const incidents: IncidentRow[] = [
|
|
|
160
182
|
id: 'INC-4007',
|
|
161
183
|
product: 'Messenger API',
|
|
162
184
|
title:
|
|
163
|
-
'Webhook delivery failures for subscribed page events
|
|
185
|
+
'Webhook delivery failures for subscribed page events, retries succeeded but signaled false alerts',
|
|
164
186
|
severity: 'minor',
|
|
165
187
|
oncall: 'Mei Lin',
|
|
166
188
|
status: 'resolved',
|
|
@@ -176,7 +198,7 @@ const incidents: IncidentRow[] = [
|
|
|
176
198
|
id: 'INC-4009',
|
|
177
199
|
product: 'Pages',
|
|
178
200
|
title:
|
|
179
|
-
'Image upload timeouts for posts larger than 4MB
|
|
201
|
+
'Image upload timeouts for posts larger than 4MB, auto-recovered after CDN region failover',
|
|
180
202
|
severity: 'minor',
|
|
181
203
|
oncall: 'Fatima Al-Rashid',
|
|
182
204
|
status: 'resolved',
|
|
@@ -190,7 +212,7 @@ const incidents: IncidentRow[] = [
|
|
|
190
212
|
id: 'INC-4010',
|
|
191
213
|
product: 'Graph API',
|
|
192
214
|
title:
|
|
193
|
-
'Intermittent 503 errors on a single load balancer in us-east-1
|
|
215
|
+
'Intermittent 503 errors on a single load balancer in us-east-1, auto-healed by health check rotation',
|
|
194
216
|
severity: 'minor',
|
|
195
217
|
oncall: 'Lucas Andersson',
|
|
196
218
|
status: 'resolved',
|
|
@@ -246,7 +268,7 @@ const incidents: IncidentRow[] = [
|
|
|
246
268
|
id: 'INC-4014',
|
|
247
269
|
product: 'Business Suite',
|
|
248
270
|
title:
|
|
249
|
-
'Slow inbox loading times (>3s p95) for accounts with large message histories
|
|
271
|
+
'Slow inbox loading times (>3s p95) for accounts with large message histories, index rebuild in progress',
|
|
250
272
|
severity: 'minor',
|
|
251
273
|
oncall: 'Andre Santos',
|
|
252
274
|
status: 'resolved',
|
|
@@ -291,19 +313,28 @@ function buildHeatmapData(data: IncidentRow[]) {
|
|
|
291
313
|
);
|
|
292
314
|
}
|
|
293
315
|
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
316
|
+
const STATUS_TOKEN_COLOR: Record<
|
|
317
|
+
IncidentRow['status'],
|
|
318
|
+
'red' | 'yellow' | 'cyan' | 'green'
|
|
319
|
+
> = {
|
|
320
|
+
ongoing: 'red',
|
|
321
|
+
identified: 'yellow',
|
|
322
|
+
monitoring: 'cyan',
|
|
323
|
+
resolved: 'green',
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
const STATUS_LABEL: Record<IncidentRow['status'], string> = {
|
|
327
|
+
ongoing: 'Ongoing',
|
|
328
|
+
identified: 'Identified',
|
|
329
|
+
monitoring: 'Monitoring',
|
|
330
|
+
resolved: 'Resolved',
|
|
331
|
+
};
|
|
301
332
|
|
|
302
333
|
const columns: TableColumn<IncidentRow>[] = [
|
|
303
334
|
{
|
|
304
335
|
key: 'id',
|
|
305
336
|
header: 'Incident',
|
|
306
|
-
width: pixel(
|
|
337
|
+
width: pixel(96),
|
|
307
338
|
renderCell: (item: IncidentRow) => (
|
|
308
339
|
<Link href="#/templates/table-page-heatmap-status" isStandalone>
|
|
309
340
|
{item.id}
|
|
@@ -313,21 +344,29 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
313
344
|
{
|
|
314
345
|
key: 'product',
|
|
315
346
|
header: 'Product',
|
|
316
|
-
width: proportional(2),
|
|
317
|
-
renderCell: (item: IncidentRow) =>
|
|
347
|
+
width: proportional(2, {minWidth: 120}),
|
|
348
|
+
renderCell: (item: IncidentRow) => (
|
|
349
|
+
<Text type="body" maxLines={1}>
|
|
350
|
+
{item.product}
|
|
351
|
+
</Text>
|
|
352
|
+
),
|
|
318
353
|
},
|
|
319
354
|
{
|
|
320
355
|
key: 'title',
|
|
321
356
|
header: 'Description',
|
|
322
|
-
width: proportional(5),
|
|
323
|
-
renderCell: (item: IncidentRow) =>
|
|
357
|
+
width: proportional(5, {minWidth: 160}),
|
|
358
|
+
renderCell: (item: IncidentRow) => (
|
|
359
|
+
<Text type="body" maxLines={2}>
|
|
360
|
+
{item.title}
|
|
361
|
+
</Text>
|
|
362
|
+
),
|
|
324
363
|
},
|
|
325
364
|
{
|
|
326
365
|
key: 'startTime',
|
|
327
366
|
header: 'Started',
|
|
328
|
-
width: pixel(
|
|
367
|
+
width: pixel(88),
|
|
329
368
|
renderCell: (item: IncidentRow) => (
|
|
330
|
-
<Text type="body" hasTabularNumbers>
|
|
369
|
+
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
331
370
|
{item.startTime}
|
|
332
371
|
</Text>
|
|
333
372
|
),
|
|
@@ -335,9 +374,9 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
335
374
|
{
|
|
336
375
|
key: 'duration',
|
|
337
376
|
header: 'Duration',
|
|
338
|
-
width: pixel(
|
|
377
|
+
width: pixel(96),
|
|
339
378
|
renderCell: (item: IncidentRow) => (
|
|
340
|
-
<Text type="body" hasTabularNumbers>
|
|
379
|
+
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
341
380
|
{item.duration}
|
|
342
381
|
</Text>
|
|
343
382
|
),
|
|
@@ -345,11 +384,15 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
345
384
|
{
|
|
346
385
|
key: 'oncall',
|
|
347
386
|
header: 'On-call',
|
|
348
|
-
width: proportional(2),
|
|
387
|
+
width: proportional(2, {minWidth: 120}),
|
|
349
388
|
renderCell: (item: IncidentRow) => (
|
|
350
389
|
<HStack gap={2} vAlign="center">
|
|
351
390
|
<Avatar name={item.oncall} size="sm" />
|
|
352
|
-
<
|
|
391
|
+
<StackItem size="fill">
|
|
392
|
+
<Text type="body" maxLines={1}>
|
|
393
|
+
{item.oncall}
|
|
394
|
+
</Text>
|
|
395
|
+
</StackItem>
|
|
353
396
|
</HStack>
|
|
354
397
|
),
|
|
355
398
|
},
|
|
@@ -358,18 +401,19 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
358
401
|
header: 'Status',
|
|
359
402
|
width: pixel(120),
|
|
360
403
|
renderCell: (item: IncidentRow) => (
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
|
|
404
|
+
<Token
|
|
405
|
+
size="sm"
|
|
406
|
+
color={STATUS_TOKEN_COLOR[item.status]}
|
|
407
|
+
label={STATUS_LABEL[item.status]}
|
|
364
408
|
/>
|
|
365
409
|
),
|
|
366
410
|
},
|
|
367
411
|
{
|
|
368
412
|
key: 'date',
|
|
369
413
|
header: 'Date',
|
|
370
|
-
width: pixel(
|
|
414
|
+
width: pixel(112),
|
|
371
415
|
renderCell: (item: IncidentRow) => (
|
|
372
|
-
<Text type="body" hasTabularNumbers>
|
|
416
|
+
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
373
417
|
{item.date}
|
|
374
418
|
</Text>
|
|
375
419
|
),
|
|
@@ -380,24 +424,32 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
380
424
|
|
|
381
425
|
// Incident-count ramp: quiet days rest on the Dracula surface, busier cells
|
|
382
426
|
// climb green → orange → red.
|
|
427
|
+
// Delegates to CHART_HUES, the sanctioned categorical source. Writing the
|
|
428
|
+
// literal here is what let a --dracula-* primitive reach a chart mark: the
|
|
429
|
+
// dictionary itself held primitives, so "use the module" was not available.
|
|
383
430
|
function heatFill(count: number): string {
|
|
384
431
|
if (count >= 3) {
|
|
385
|
-
return
|
|
432
|
+
return CHART_HUES.red;
|
|
386
433
|
}
|
|
387
434
|
if (count === 2) {
|
|
388
|
-
return
|
|
435
|
+
return CHART_HUES.orange;
|
|
389
436
|
}
|
|
390
437
|
if (count === 1) {
|
|
391
|
-
return
|
|
438
|
+
return CHART_HUES.green;
|
|
392
439
|
}
|
|
393
|
-
|
|
440
|
+
// Zero is a magnitude, not a severity, so it wears the surface tier rather
|
|
441
|
+
// than a data hue -- which is why the legend type needs a background arm.
|
|
442
|
+
return 'var(--color-background-card)';
|
|
394
443
|
}
|
|
395
444
|
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
{label: '
|
|
445
|
+
// The zero swatch and its cell MUST move together: the legend is how a reader
|
|
446
|
+
// learns what a colour means, so a legend that disagrees with the grid teaches
|
|
447
|
+
// them the wrong mapping.
|
|
448
|
+
const HEAT_LEGEND: readonly ChartLegendEntry[] = [
|
|
449
|
+
{label: '0', color: 'var(--color-background-card)'},
|
|
450
|
+
{label: '1', color: CHART_HUES.green},
|
|
451
|
+
{label: '2', color: CHART_HUES.orange},
|
|
452
|
+
{label: '3+', color: CHART_HUES.red},
|
|
401
453
|
];
|
|
402
454
|
|
|
403
455
|
function OutageHeatmap() {
|
|
@@ -413,40 +465,28 @@ function OutageHeatmap() {
|
|
|
413
465
|
heatmapData.find(d => d.day === day && d.hour === hour)?.incidents ?? 0;
|
|
414
466
|
return (
|
|
415
467
|
<VStack gap={3}>
|
|
416
|
-
<Card
|
|
417
|
-
padding={3}
|
|
418
|
-
style={{
|
|
419
|
-
backgroundColor: 'var(--color-background)',
|
|
420
|
-
border: 'var(--border-width) solid var(--color-separator)',
|
|
421
|
-
}}>
|
|
468
|
+
<Card padding={4} style={CHART_PANEL_STYLE}>
|
|
422
469
|
<svg
|
|
423
470
|
viewBox={`0 0 ${W} ${H}`}
|
|
424
471
|
width="100%"
|
|
425
472
|
role="img"
|
|
426
473
|
aria-label="Incidents by day and hour, January 12 to 18">
|
|
427
474
|
{DAYS.map((day, di) => (
|
|
428
|
-
<
|
|
475
|
+
<ChartLabel
|
|
429
476
|
key={day}
|
|
430
477
|
x={labelW + di * (cellW + gap) + cellW / 2}
|
|
431
|
-
y={14}
|
|
432
|
-
textAnchor="middle"
|
|
433
|
-
fontSize={9}
|
|
434
|
-
fill="var(--color-text-paragraph)"
|
|
435
|
-
fontFamily="var(--font-family-mono)">
|
|
478
|
+
y={14}>
|
|
436
479
|
{day}
|
|
437
|
-
</
|
|
480
|
+
</ChartLabel>
|
|
438
481
|
))}
|
|
439
482
|
{HOURS.map((hour, hi) => (
|
|
440
483
|
<g key={hour}>
|
|
441
|
-
<
|
|
484
|
+
<ChartLabel
|
|
442
485
|
x={labelW - 6}
|
|
443
486
|
y={labelH + hi * (cellH + gap) + cellH / 2 + 3}
|
|
444
|
-
textAnchor="end"
|
|
445
|
-
fontSize={9}
|
|
446
|
-
fill="var(--color-text-paragraph)"
|
|
447
|
-
fontFamily="var(--font-family-mono)">
|
|
487
|
+
textAnchor="end">
|
|
448
488
|
{hour}
|
|
449
|
-
</
|
|
489
|
+
</ChartLabel>
|
|
450
490
|
{DAYS.map((day, di) => {
|
|
451
491
|
const count = countFor(day, hour);
|
|
452
492
|
return (
|
|
@@ -459,17 +499,28 @@ function OutageHeatmap() {
|
|
|
459
499
|
rx={4}
|
|
460
500
|
fill={heatFill(count)}
|
|
461
501
|
/>
|
|
462
|
-
{count
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
502
|
+
{/* Every cell prints its count, empty ones included, so
|
|
503
|
+
the ramp reinforces the number instead of carrying the
|
|
504
|
+
reading on its own. */}
|
|
505
|
+
<ChartLabel
|
|
506
|
+
x={labelW + di * (cellW + gap) + cellW / 2}
|
|
507
|
+
y={labelH + hi * (cellH + gap) + cellH / 2 + 3}
|
|
508
|
+
// On-fill ink follows the cell it sits on, so it takes
|
|
509
|
+
// the --color-on-* arm for that status rather than one
|
|
510
|
+
// fixed token. --color-on-dark is the wrong target here:
|
|
511
|
+
// it is #F8F8F2, near-white, which would fail on the
|
|
512
|
+
// saturated green/orange/red fills.
|
|
513
|
+
fill={
|
|
514
|
+
count >= 3
|
|
515
|
+
? 'var(--color-on-error)'
|
|
516
|
+
: count === 2
|
|
517
|
+
? 'var(--color-on-warning)'
|
|
518
|
+
: count === 1
|
|
519
|
+
? 'var(--color-on-success)'
|
|
520
|
+
: undefined
|
|
521
|
+
}>
|
|
522
|
+
{count}
|
|
523
|
+
</ChartLabel>
|
|
473
524
|
</g>
|
|
474
525
|
);
|
|
475
526
|
})}
|
|
@@ -477,40 +528,48 @@ function OutageHeatmap() {
|
|
|
477
528
|
))}
|
|
478
529
|
</svg>
|
|
479
530
|
</Card>
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
</HStack>
|
|
531
|
+
{/* Legend rows go through shared/chart-legend.tsx. The ramp itself stays
|
|
532
|
+
local: it encodes SEVERITY (0/1/2/3+ against green/orange/red), which
|
|
533
|
+
is a status vocabulary, not a magnitude. A sequential magnitude ramp
|
|
534
|
+
would invert this one — on a dark page a high count reads lighter,
|
|
535
|
+
which is the opposite of "more is worse" here. The --color-data-<family>-
|
|
536
|
+
1..5 steps remain in tokens.css for a consumer that genuinely needs
|
|
537
|
+
one; nothing in this kit does, which is why the module that wrapped
|
|
538
|
+
them is gone. */}
|
|
539
|
+
<ChartLegend
|
|
540
|
+
entries={HEAT_LEGEND}
|
|
541
|
+
gap={2}
|
|
542
|
+
swatchSize={12}
|
|
543
|
+
caption={
|
|
544
|
+
<Text type="supporting" color="secondary">
|
|
545
|
+
Incidents by day and hour · Jan 12–18
|
|
546
|
+
</Text>
|
|
547
|
+
}
|
|
548
|
+
/>
|
|
499
549
|
</VStack>
|
|
500
550
|
);
|
|
501
551
|
}
|
|
502
552
|
|
|
503
553
|
// ============= PAGE =============
|
|
504
554
|
|
|
555
|
+
// A definite ancestor for `Layout height="fill"`. 100dvh, not minHeight: '100%':
|
|
556
|
+
// a percentage min-height against an indefinite containing block computes to 0
|
|
557
|
+
// (CSS 2.1 10.5), so the soft failure would survive and the document would keep
|
|
558
|
+
// the scroll. LayoutContent is overflow:auto, so the definite height lands as a
|
|
559
|
+
// content-pane scroller rather than a clip. Same shape as dashboard.tsx and
|
|
560
|
+
// editor.tsx:302.
|
|
561
|
+
const pageStyle: CSSProperties = {height: '100dvh'};
|
|
562
|
+
|
|
505
563
|
export default function HeatmapTable() {
|
|
506
564
|
return (
|
|
507
565
|
<Layout
|
|
566
|
+
style={pageStyle}
|
|
508
567
|
height="fill"
|
|
509
568
|
header={
|
|
510
|
-
<LayoutHeader hasDivider>
|
|
569
|
+
<LayoutHeader hasDivider padding={6}>
|
|
511
570
|
<HStack gap={2} vAlign="center">
|
|
512
571
|
<StackItem size="fill">
|
|
513
|
-
<Heading level={1}>Status</Heading>
|
|
572
|
+
<Heading level={1} type="display-2">Status</Heading>
|
|
514
573
|
</StackItem>
|
|
515
574
|
<IconButton
|
|
516
575
|
label="Filter"
|
|
@@ -524,7 +583,11 @@ export default function HeatmapTable() {
|
|
|
524
583
|
variant="ghost"
|
|
525
584
|
tooltip="Export"
|
|
526
585
|
/>
|
|
527
|
-
<Button
|
|
586
|
+
<Button
|
|
587
|
+
label="Refresh"
|
|
588
|
+
variant="secondary"
|
|
589
|
+
icon={<Icon icon={RotateCw} size="sm" />}
|
|
590
|
+
/>
|
|
528
591
|
</HStack>
|
|
529
592
|
</LayoutHeader>
|
|
530
593
|
}
|
|
@@ -539,6 +602,7 @@ export default function HeatmapTable() {
|
|
|
539
602
|
idKey="id"
|
|
540
603
|
density="balanced"
|
|
541
604
|
dividers="rows"
|
|
605
|
+
textOverflow="truncate"
|
|
542
606
|
hasHover
|
|
543
607
|
/>
|
|
544
608
|
</VStack>
|