astryx-dracula 0.2.0 → 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 +22 -11
- 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 +67 -32
- package/templates/ai-chat.tsx +41 -20
- package/templates/blank.tsx +5 -2
- package/templates/centered-hero.tsx +15 -124
- package/templates/classic-gallery.tsx +21 -65
- package/templates/contact-form.tsx +62 -40
- package/templates/dashboard-portfolio.tsx +146 -227
- package/templates/dashboard.tsx +123 -121
- package/templates/detail-page.tsx +101 -95
- package/templates/documentation-design.template.mjs +1 -1
- package/templates/documentation-design.tsx +85 -96
- package/templates/documentation-technical.template.mjs +1 -1
- package/templates/documentation-technical.tsx +33 -21
- package/templates/documentation.template.mjs +1 -1
- package/templates/documentation.tsx +54 -74
- package/templates/editor.tsx +119 -89
- package/templates/file-explorer.tsx +133 -84
- package/templates/form-two-column.tsx +62 -124
- package/templates/gallery-hero.tsx +17 -171
- package/templates/ide.tsx +27 -21
- package/templates/incident-console.tsx +105 -69
- package/templates/kanban-board.tsx +100 -41
- package/templates/library.template.mjs +2 -2
- package/templates/library.tsx +38 -41
- package/templates/login-card.tsx +59 -86
- package/templates/login-split.tsx +88 -128
- package/templates/login-sso.tsx +107 -68
- package/templates/login.tsx +108 -52
- package/templates/messaging-shell.tsx +172 -143
- package/templates/mixed-gallery.tsx +31 -130
- package/templates/payment-form.tsx +369 -289
- package/templates/product-detail.tsx +40 -57
- package/templates/product-gallery.tsx +31 -40
- package/templates/product-tour.template.mjs +6 -0
- package/templates/product-tour.tsx +916 -0
- package/templates/settings-dialog.tsx +145 -286
- package/templates/settings-sidebar.tsx +179 -267
- package/templates/settings.tsx +42 -13
- package/templates/shell-nav.tsx +32 -23
- package/templates/shell-side-nav.tsx +30 -19
- package/templates/shell-top-nav.tsx +81 -43
- package/templates/side-gallery.tsx +39 -93
- package/templates/table-grouped.tsx +63 -33
- package/templates/table-page-chart.tsx +24 -297
- package/templates/table-page-heatmap-status.tsx +157 -91
- package/templates/table-page-shoe-store-heatmap.tsx +19 -288
- package/templates/table-page.tsx +24 -6
- package/templates/table.tsx +24 -7
- 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 +313 -249
- package/theme.css +76 -14
- package/tokens.css +425 -30
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
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))
|
|
2
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';
|
|
3
23
|
import {
|
|
4
24
|
VStack,
|
|
5
25
|
HStack,
|
|
@@ -13,12 +33,16 @@ import {Button} from '@astryxdesign/core/Button';
|
|
|
13
33
|
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
14
34
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
15
35
|
import {Avatar} from '@astryxdesign/core/Avatar';
|
|
16
|
-
import {
|
|
36
|
+
import {Token} from '@astryxdesign/core/Token';
|
|
17
37
|
import {Card} from '@astryxdesign/core/Card';
|
|
18
38
|
import {Link} from '@astryxdesign/core/Link';
|
|
19
39
|
import {Table, proportional, pixel} from '@astryxdesign/core/Table';
|
|
20
40
|
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
21
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';
|
|
22
46
|
|
|
23
47
|
// ============= ICONS (verified lucide-react exports) =============
|
|
24
48
|
// Filter ← FunnelIcon, Download ← ArrowDownTrayIcon, RotateCw ← ArrowPathIcon.
|
|
@@ -56,7 +80,7 @@ const incidents: IncidentRow[] = [
|
|
|
56
80
|
id: 'INC-4008',
|
|
57
81
|
product: 'Business Suite',
|
|
58
82
|
title:
|
|
59
|
-
'Notification delivery delays affecting mobile push and email channels
|
|
83
|
+
'Notification delivery delays affecting mobile push and email channels, queue saturation under investigation',
|
|
60
84
|
severity: 'major',
|
|
61
85
|
oncall: 'Carlos Mendez',
|
|
62
86
|
status: 'ongoing',
|
|
@@ -71,7 +95,7 @@ const incidents: IncidentRow[] = [
|
|
|
71
95
|
id: 'INC-4001',
|
|
72
96
|
product: 'Ads Manager',
|
|
73
97
|
title:
|
|
74
|
-
'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',
|
|
75
99
|
severity: 'critical',
|
|
76
100
|
oncall: 'Sarah Chen',
|
|
77
101
|
status: 'resolved',
|
|
@@ -158,7 +182,7 @@ const incidents: IncidentRow[] = [
|
|
|
158
182
|
id: 'INC-4007',
|
|
159
183
|
product: 'Messenger API',
|
|
160
184
|
title:
|
|
161
|
-
'Webhook delivery failures for subscribed page events
|
|
185
|
+
'Webhook delivery failures for subscribed page events, retries succeeded but signaled false alerts',
|
|
162
186
|
severity: 'minor',
|
|
163
187
|
oncall: 'Mei Lin',
|
|
164
188
|
status: 'resolved',
|
|
@@ -174,7 +198,7 @@ const incidents: IncidentRow[] = [
|
|
|
174
198
|
id: 'INC-4009',
|
|
175
199
|
product: 'Pages',
|
|
176
200
|
title:
|
|
177
|
-
'Image upload timeouts for posts larger than 4MB
|
|
201
|
+
'Image upload timeouts for posts larger than 4MB, auto-recovered after CDN region failover',
|
|
178
202
|
severity: 'minor',
|
|
179
203
|
oncall: 'Fatima Al-Rashid',
|
|
180
204
|
status: 'resolved',
|
|
@@ -188,7 +212,7 @@ const incidents: IncidentRow[] = [
|
|
|
188
212
|
id: 'INC-4010',
|
|
189
213
|
product: 'Graph API',
|
|
190
214
|
title:
|
|
191
|
-
'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',
|
|
192
216
|
severity: 'minor',
|
|
193
217
|
oncall: 'Lucas Andersson',
|
|
194
218
|
status: 'resolved',
|
|
@@ -244,7 +268,7 @@ const incidents: IncidentRow[] = [
|
|
|
244
268
|
id: 'INC-4014',
|
|
245
269
|
product: 'Business Suite',
|
|
246
270
|
title:
|
|
247
|
-
'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',
|
|
248
272
|
severity: 'minor',
|
|
249
273
|
oncall: 'Andre Santos',
|
|
250
274
|
status: 'resolved',
|
|
@@ -289,19 +313,28 @@ function buildHeatmapData(data: IncidentRow[]) {
|
|
|
289
313
|
);
|
|
290
314
|
}
|
|
291
315
|
|
|
292
|
-
const
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
+
};
|
|
299
332
|
|
|
300
333
|
const columns: TableColumn<IncidentRow>[] = [
|
|
301
334
|
{
|
|
302
335
|
key: 'id',
|
|
303
336
|
header: 'Incident',
|
|
304
|
-
width: pixel(
|
|
337
|
+
width: pixel(96),
|
|
305
338
|
renderCell: (item: IncidentRow) => (
|
|
306
339
|
<Link href="#/templates/table-page-heatmap-status" isStandalone>
|
|
307
340
|
{item.id}
|
|
@@ -311,21 +344,29 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
311
344
|
{
|
|
312
345
|
key: 'product',
|
|
313
346
|
header: 'Product',
|
|
314
|
-
width: proportional(2),
|
|
315
|
-
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
|
+
),
|
|
316
353
|
},
|
|
317
354
|
{
|
|
318
355
|
key: 'title',
|
|
319
356
|
header: 'Description',
|
|
320
|
-
width: proportional(5),
|
|
321
|
-
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
|
+
),
|
|
322
363
|
},
|
|
323
364
|
{
|
|
324
365
|
key: 'startTime',
|
|
325
366
|
header: 'Started',
|
|
326
|
-
width: pixel(
|
|
367
|
+
width: pixel(88),
|
|
327
368
|
renderCell: (item: IncidentRow) => (
|
|
328
|
-
<Text type="body" hasTabularNumbers>
|
|
369
|
+
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
329
370
|
{item.startTime}
|
|
330
371
|
</Text>
|
|
331
372
|
),
|
|
@@ -333,9 +374,9 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
333
374
|
{
|
|
334
375
|
key: 'duration',
|
|
335
376
|
header: 'Duration',
|
|
336
|
-
width: pixel(
|
|
377
|
+
width: pixel(96),
|
|
337
378
|
renderCell: (item: IncidentRow) => (
|
|
338
|
-
<Text type="body" hasTabularNumbers>
|
|
379
|
+
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
339
380
|
{item.duration}
|
|
340
381
|
</Text>
|
|
341
382
|
),
|
|
@@ -343,11 +384,15 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
343
384
|
{
|
|
344
385
|
key: 'oncall',
|
|
345
386
|
header: 'On-call',
|
|
346
|
-
width: proportional(2),
|
|
387
|
+
width: proportional(2, {minWidth: 120}),
|
|
347
388
|
renderCell: (item: IncidentRow) => (
|
|
348
389
|
<HStack gap={2} vAlign="center">
|
|
349
390
|
<Avatar name={item.oncall} size="sm" />
|
|
350
|
-
<
|
|
391
|
+
<StackItem size="fill">
|
|
392
|
+
<Text type="body" maxLines={1}>
|
|
393
|
+
{item.oncall}
|
|
394
|
+
</Text>
|
|
395
|
+
</StackItem>
|
|
351
396
|
</HStack>
|
|
352
397
|
),
|
|
353
398
|
},
|
|
@@ -356,18 +401,19 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
356
401
|
header: 'Status',
|
|
357
402
|
width: pixel(120),
|
|
358
403
|
renderCell: (item: IncidentRow) => (
|
|
359
|
-
<
|
|
360
|
-
|
|
361
|
-
|
|
404
|
+
<Token
|
|
405
|
+
size="sm"
|
|
406
|
+
color={STATUS_TOKEN_COLOR[item.status]}
|
|
407
|
+
label={STATUS_LABEL[item.status]}
|
|
362
408
|
/>
|
|
363
409
|
),
|
|
364
410
|
},
|
|
365
411
|
{
|
|
366
412
|
key: 'date',
|
|
367
413
|
header: 'Date',
|
|
368
|
-
width: pixel(
|
|
414
|
+
width: pixel(112),
|
|
369
415
|
renderCell: (item: IncidentRow) => (
|
|
370
|
-
<Text type="body" hasTabularNumbers>
|
|
416
|
+
<Text type="body" hasTabularNumbers maxLines={1}>
|
|
371
417
|
{item.date}
|
|
372
418
|
</Text>
|
|
373
419
|
),
|
|
@@ -378,24 +424,32 @@ const columns: TableColumn<IncidentRow>[] = [
|
|
|
378
424
|
|
|
379
425
|
// Incident-count ramp: quiet days rest on the Dracula surface, busier cells
|
|
380
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.
|
|
381
430
|
function heatFill(count: number): string {
|
|
382
431
|
if (count >= 3) {
|
|
383
|
-
return
|
|
432
|
+
return CHART_HUES.red;
|
|
384
433
|
}
|
|
385
434
|
if (count === 2) {
|
|
386
|
-
return
|
|
435
|
+
return CHART_HUES.orange;
|
|
387
436
|
}
|
|
388
437
|
if (count === 1) {
|
|
389
|
-
return
|
|
438
|
+
return CHART_HUES.green;
|
|
390
439
|
}
|
|
391
|
-
|
|
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)';
|
|
392
443
|
}
|
|
393
444
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
{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},
|
|
399
453
|
];
|
|
400
454
|
|
|
401
455
|
function OutageHeatmap() {
|
|
@@ -411,40 +465,28 @@ function OutageHeatmap() {
|
|
|
411
465
|
heatmapData.find(d => d.day === day && d.hour === hour)?.incidents ?? 0;
|
|
412
466
|
return (
|
|
413
467
|
<VStack gap={3}>
|
|
414
|
-
<Card
|
|
415
|
-
padding={3}
|
|
416
|
-
style={{
|
|
417
|
-
backgroundColor: 'var(--color-background)',
|
|
418
|
-
border: 'var(--border-width) solid var(--color-separator)',
|
|
419
|
-
}}>
|
|
468
|
+
<Card padding={4} style={CHART_PANEL_STYLE}>
|
|
420
469
|
<svg
|
|
421
470
|
viewBox={`0 0 ${W} ${H}`}
|
|
422
471
|
width="100%"
|
|
423
472
|
role="img"
|
|
424
473
|
aria-label="Incidents by day and hour, January 12 to 18">
|
|
425
474
|
{DAYS.map((day, di) => (
|
|
426
|
-
<
|
|
475
|
+
<ChartLabel
|
|
427
476
|
key={day}
|
|
428
477
|
x={labelW + di * (cellW + gap) + cellW / 2}
|
|
429
|
-
y={14}
|
|
430
|
-
textAnchor="middle"
|
|
431
|
-
fontSize={9}
|
|
432
|
-
fill="var(--color-text-paragraph)"
|
|
433
|
-
fontFamily="var(--font-family-mono)">
|
|
478
|
+
y={14}>
|
|
434
479
|
{day}
|
|
435
|
-
</
|
|
480
|
+
</ChartLabel>
|
|
436
481
|
))}
|
|
437
482
|
{HOURS.map((hour, hi) => (
|
|
438
483
|
<g key={hour}>
|
|
439
|
-
<
|
|
484
|
+
<ChartLabel
|
|
440
485
|
x={labelW - 6}
|
|
441
486
|
y={labelH + hi * (cellH + gap) + cellH / 2 + 3}
|
|
442
|
-
textAnchor="end"
|
|
443
|
-
fontSize={9}
|
|
444
|
-
fill="var(--color-text-paragraph)"
|
|
445
|
-
fontFamily="var(--font-family-mono)">
|
|
487
|
+
textAnchor="end">
|
|
446
488
|
{hour}
|
|
447
|
-
</
|
|
489
|
+
</ChartLabel>
|
|
448
490
|
{DAYS.map((day, di) => {
|
|
449
491
|
const count = countFor(day, hour);
|
|
450
492
|
return (
|
|
@@ -457,17 +499,28 @@ function OutageHeatmap() {
|
|
|
457
499
|
rx={4}
|
|
458
500
|
fill={heatFill(count)}
|
|
459
501
|
/>
|
|
460
|
-
{count
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
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>
|
|
471
524
|
</g>
|
|
472
525
|
);
|
|
473
526
|
})}
|
|
@@ -475,40 +528,48 @@ function OutageHeatmap() {
|
|
|
475
528
|
))}
|
|
476
529
|
</svg>
|
|
477
530
|
</Card>
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
</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
|
+
/>
|
|
497
549
|
</VStack>
|
|
498
550
|
);
|
|
499
551
|
}
|
|
500
552
|
|
|
501
553
|
// ============= PAGE =============
|
|
502
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
|
+
|
|
503
563
|
export default function HeatmapTable() {
|
|
504
564
|
return (
|
|
505
565
|
<Layout
|
|
566
|
+
style={pageStyle}
|
|
506
567
|
height="fill"
|
|
507
568
|
header={
|
|
508
|
-
<LayoutHeader hasDivider>
|
|
569
|
+
<LayoutHeader hasDivider padding={6}>
|
|
509
570
|
<HStack gap={2} vAlign="center">
|
|
510
571
|
<StackItem size="fill">
|
|
511
|
-
<Heading level={1}>Status</Heading>
|
|
572
|
+
<Heading level={1} type="display-2">Status</Heading>
|
|
512
573
|
</StackItem>
|
|
513
574
|
<IconButton
|
|
514
575
|
label="Filter"
|
|
@@ -522,7 +583,11 @@ export default function HeatmapTable() {
|
|
|
522
583
|
variant="ghost"
|
|
523
584
|
tooltip="Export"
|
|
524
585
|
/>
|
|
525
|
-
<Button
|
|
586
|
+
<Button
|
|
587
|
+
label="Refresh"
|
|
588
|
+
variant="secondary"
|
|
589
|
+
icon={<Icon icon={RotateCw} size="sm" />}
|
|
590
|
+
/>
|
|
526
591
|
</HStack>
|
|
527
592
|
</LayoutHeader>
|
|
528
593
|
}
|
|
@@ -537,6 +602,7 @@ export default function HeatmapTable() {
|
|
|
537
602
|
idKey="id"
|
|
538
603
|
density="balanced"
|
|
539
604
|
dividers="rows"
|
|
605
|
+
textOverflow="truncate"
|
|
540
606
|
hasHover
|
|
541
607
|
/>
|
|
542
608
|
</VStack>
|