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,6 +1,6 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L > (LH > V[g=
|
|
3
|
+
// L > (LH[divider] > V[g=3] > ((H[g=4 a=start] > (SI > V[g=0] > (Lk"All orders" + (V[g=0] > Hd"#1001"[level=1] + (H[g=1 a=center] > Tx"5 ordered items"[t=supporting] + Av"Vlad Dracul" + SD"Unfulfilled" + Tx"02/23/2026"[t=supporting] + Lk"See all")))) + (H[g=2] > B"Restock" + B"Edit")) + (H[g=3 a=center] > (SI > TL > Tab"Details"! + Tab"Invoices" + Tab"Timeline" + TabMenu"More") + B.ghost))) + (LC > V[g=4] > ((S > V[g=4] > ((H[g=2 a=center] > Hd"Items"[level=2] + SD"Unfulfilled") + UL)) + (S > V[g=4] > ((H[g=2 a=center] > Hd"Invoice"[level=2] + SD"Paid") + ML)) + (S > V[g=4] > ((H[g=2 a=center] > Hd"Timeline"[level=2] + B.ghost) + UL)))) + (LP[w=320 p=4] > V[g=4] > Col"Notes" + Col"Customer" + Col"Fraud analysis")
|
|
4
4
|
|
|
5
5
|
import {useState} from 'react';
|
|
6
6
|
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
Section,
|
|
17
17
|
} from '@astryxdesign/core/Layout';
|
|
18
18
|
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
19
|
-
import {
|
|
19
|
+
import {StatusDot} from '@astryxdesign/core/StatusDot';
|
|
20
20
|
import {Avatar} from '@astryxdesign/core/Avatar';
|
|
21
21
|
import {Button} from '@astryxdesign/core/Button';
|
|
22
22
|
import {TabList, Tab, TabMenu} from '@astryxdesign/core/TabList';
|
|
@@ -28,6 +28,7 @@ import {ProgressBar} from '@astryxdesign/core/ProgressBar';
|
|
|
28
28
|
import {Collapsible} from '@astryxdesign/core/Collapsible';
|
|
29
29
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
30
30
|
import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
|
|
31
|
+
import type {SceneHue} from 'astryx-dracula/shared/scene-hues';
|
|
31
32
|
import {
|
|
32
33
|
Calendar,
|
|
33
34
|
Flag,
|
|
@@ -95,9 +96,15 @@ const PRODUCTS = [
|
|
|
95
96
|
},
|
|
96
97
|
];
|
|
97
98
|
|
|
98
|
-
//
|
|
99
|
-
|
|
100
|
-
|
|
99
|
+
// Per-product art tint, read positionally out of a never-reordered array so
|
|
100
|
+
// "Crypt Cup" keeps its hue for the life of the page. Typed `SceneHue` for two
|
|
101
|
+
// reasons, not one: the union is the enforcement, and it also pins the key set
|
|
102
|
+
// so the array cannot grow a sixth entry that silently reuses slot 1 (the old
|
|
103
|
+
// `index % length` wrapped at n>5 — a latent bug, not a live one).
|
|
104
|
+
// The art-treatment remit bans purple, so the leading entry changes to cyan on
|
|
105
|
+
// the same pass.
|
|
106
|
+
const PRODUCT_HUES: readonly SceneHue[] = [
|
|
107
|
+
'var(--dracula-cyan)',
|
|
101
108
|
'var(--dracula-cyan)',
|
|
102
109
|
'var(--dracula-pink)',
|
|
103
110
|
'var(--dracula-yellow)',
|
|
@@ -111,7 +118,7 @@ const sigilClip: CSSProperties = {
|
|
|
111
118
|
flexShrink: 0,
|
|
112
119
|
};
|
|
113
120
|
|
|
114
|
-
function ProductSigil({
|
|
121
|
+
function ProductSigil({index}: {name: string; index: number}) {
|
|
115
122
|
const hue = PRODUCT_HUES[index % PRODUCT_HUES.length];
|
|
116
123
|
return (
|
|
117
124
|
<Card padding={0} style={sigilClip}>
|
|
@@ -119,8 +126,7 @@ function ProductSigil({name, index}: {name: string; index: number}) {
|
|
|
119
126
|
viewBox="0 0 40 40"
|
|
120
127
|
width={40}
|
|
121
128
|
height={40}
|
|
122
|
-
|
|
123
|
-
aria-label={name}>
|
|
129
|
+
aria-hidden="true">
|
|
124
130
|
<rect width="40" height="40" fill="var(--dracula-bg-light)" />
|
|
125
131
|
<circle cx={28} cy={11} r={7} fill={hue} />
|
|
126
132
|
<circle
|
|
@@ -151,7 +157,7 @@ const fmt = (n: number) => `$${n.toFixed(2)}`;
|
|
|
151
157
|
const ACTIVITY = [
|
|
152
158
|
{
|
|
153
159
|
type: 'event' as const,
|
|
154
|
-
user: '
|
|
160
|
+
user: 'Vlad Dracul',
|
|
155
161
|
text: 'placed order #1001',
|
|
156
162
|
reactions: 2,
|
|
157
163
|
time: 'Feb 23 at 9:12 AM',
|
|
@@ -159,7 +165,7 @@ const ACTIVITY = [
|
|
|
159
165
|
{
|
|
160
166
|
type: 'comment' as const,
|
|
161
167
|
user: 'Alex Rivera',
|
|
162
|
-
text: "Customer requested gift wrapping for the mug & plate set. I've added a note to the packing slip
|
|
168
|
+
text: "Customer requested gift wrapping for the mug & plate set. I've added a note to the packing slip. Warehouse team should wrap in recycled kraft paper.",
|
|
163
169
|
reactions: 3,
|
|
164
170
|
time: 'Feb 23 at 10:45 AM',
|
|
165
171
|
},
|
|
@@ -169,8 +175,8 @@ const ACTIVITY = [
|
|
|
169
175
|
text: 'has several information changes',
|
|
170
176
|
time: 'Feb 23 at 11:30 AM',
|
|
171
177
|
changes: [
|
|
172
|
-
'Payment verified via Visa
|
|
173
|
-
'Fraud check passed
|
|
178
|
+
'Payment verified via Visa …7482',
|
|
179
|
+
'Fraud check passed, low risk',
|
|
174
180
|
],
|
|
175
181
|
},
|
|
176
182
|
{
|
|
@@ -185,7 +191,7 @@ const ACTIVITY = [
|
|
|
185
191
|
// ─── Bullet separator ───────────────────────────────────────────────────────
|
|
186
192
|
function Bullet() {
|
|
187
193
|
return (
|
|
188
|
-
<Text type="supporting" color="secondary">
|
|
194
|
+
<Text type="supporting" color="secondary" aria-hidden="true">
|
|
189
195
|
{'・'}
|
|
190
196
|
</Text>
|
|
191
197
|
);
|
|
@@ -206,7 +212,7 @@ function PageHeader({
|
|
|
206
212
|
isNarrow: boolean;
|
|
207
213
|
}) {
|
|
208
214
|
return (
|
|
209
|
-
<LayoutHeader hasDivider padding={
|
|
215
|
+
<LayoutHeader hasDivider padding={6}>
|
|
210
216
|
<VStack gap={3}>
|
|
211
217
|
<HStack gap={4} vAlign="start">
|
|
212
218
|
<StackItem size="fill">
|
|
@@ -229,14 +235,17 @@ function PageHeader({
|
|
|
229
235
|
</Text>
|
|
230
236
|
<HStack gap={1} vAlign="center">
|
|
231
237
|
<Bullet />
|
|
232
|
-
<Avatar name="
|
|
238
|
+
<Avatar name="Vlad Dracul" size="sm" />
|
|
233
239
|
<Text type="supporting" color="secondary" maxLines={1}>
|
|
234
|
-
|
|
240
|
+
Vlad Dracul
|
|
235
241
|
</Text>
|
|
236
242
|
</HStack>
|
|
237
243
|
<HStack gap={1} vAlign="center">
|
|
238
244
|
<Bullet />
|
|
239
|
-
<
|
|
245
|
+
<StatusDot variant="warning" label="Unfulfilled" />
|
|
246
|
+
<Text type="supporting" color="secondary">
|
|
247
|
+
Unfulfilled
|
|
248
|
+
</Text>
|
|
240
249
|
</HStack>
|
|
241
250
|
<HStack gap={1} vAlign="center">
|
|
242
251
|
<Bullet />
|
|
@@ -330,7 +339,10 @@ function ItemsCard() {
|
|
|
330
339
|
<StackItem size="fill">
|
|
331
340
|
<HStack gap={2} vAlign="center">
|
|
332
341
|
<Heading level={2}>Items</Heading>
|
|
333
|
-
<
|
|
342
|
+
<StatusDot variant="warning" label="Unfulfilled" />
|
|
343
|
+
<Text type="supporting" color="secondary">
|
|
344
|
+
Unfulfilled
|
|
345
|
+
</Text>
|
|
334
346
|
</HStack>
|
|
335
347
|
</StackItem>
|
|
336
348
|
<HStack gap={2}>
|
|
@@ -342,13 +354,13 @@ function ItemsCard() {
|
|
|
342
354
|
<List density="spacious" style={itemsList}>
|
|
343
355
|
{PRODUCTS.map((product, i) => (
|
|
344
356
|
<ListItem
|
|
345
|
-
key={
|
|
357
|
+
key={product.name}
|
|
346
358
|
label={product.name}
|
|
347
359
|
description={
|
|
348
360
|
<VStack gap={0}>
|
|
349
|
-
{product.details.split('\n').map(
|
|
361
|
+
{product.details.split('\n').map(line => (
|
|
350
362
|
<Text
|
|
351
|
-
key={
|
|
363
|
+
key={line}
|
|
352
364
|
type="supporting"
|
|
353
365
|
color="secondary"
|
|
354
366
|
hasTabularNumbers>
|
|
@@ -357,7 +369,6 @@ function ItemsCard() {
|
|
|
357
369
|
))}
|
|
358
370
|
</VStack>
|
|
359
371
|
}
|
|
360
|
-
onClick={() => {}}
|
|
361
372
|
startContent={
|
|
362
373
|
<ProductSigil name={product.name} index={i} />
|
|
363
374
|
}
|
|
@@ -396,7 +407,10 @@ function InvoiceCard() {
|
|
|
396
407
|
<StackItem size="fill">
|
|
397
408
|
<HStack gap={2} vAlign="center">
|
|
398
409
|
<Heading level={2}>Invoice</Heading>
|
|
399
|
-
<
|
|
410
|
+
<StatusDot variant="success" label="Paid" />
|
|
411
|
+
<Text type="supporting" color="secondary">
|
|
412
|
+
Paid
|
|
413
|
+
</Text>
|
|
400
414
|
</HStack>
|
|
401
415
|
</StackItem>
|
|
402
416
|
<HStack gap={2}>
|
|
@@ -469,7 +483,7 @@ function InvoiceCard() {
|
|
|
469
483
|
<HStack>
|
|
470
484
|
<StackItem size="fill">
|
|
471
485
|
<Text type="body" hasTabularNumbers>
|
|
472
|
-
Visa
|
|
486
|
+
Visa …7482
|
|
473
487
|
</Text>
|
|
474
488
|
</StackItem>
|
|
475
489
|
<Text type="body" hasTabularNumbers>
|
|
@@ -488,7 +502,11 @@ function TimelineSection() {
|
|
|
488
502
|
return (
|
|
489
503
|
<Section>
|
|
490
504
|
<VStack gap={4}>
|
|
491
|
-
|
|
505
|
+
{/* gap={2}, matching the byte-identical header rows at :338 and
|
|
506
|
+
:407. Core's Stack has no default gap (Stack.tsx:211, pass-through
|
|
507
|
+
at :255/:285), so this rendered 0px -- the heading and the Filters
|
|
508
|
+
button touched -- while both siblings render 8px. */}
|
|
509
|
+
<HStack gap={2} vAlign="center">
|
|
492
510
|
<StackItem size="fill">
|
|
493
511
|
<Heading level={2}>Timeline</Heading>
|
|
494
512
|
</StackItem>
|
|
@@ -500,73 +518,58 @@ function TimelineSection() {
|
|
|
500
518
|
/>
|
|
501
519
|
</HStack>
|
|
502
520
|
|
|
503
|
-
<
|
|
504
|
-
{ACTIVITY.map(
|
|
505
|
-
<
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
</Text>
|
|
528
|
-
</HStack>
|
|
529
|
-
))}
|
|
530
|
-
</VStack>
|
|
531
|
-
)}
|
|
532
|
-
</VStack>
|
|
533
|
-
</Card>
|
|
534
|
-
<HStack gap={3} vAlign="center">
|
|
535
|
-
<HStack gap={1} vAlign="center">
|
|
536
|
-
<Icon
|
|
537
|
-
icon={ThumbsUp}
|
|
538
|
-
size="xsm"
|
|
539
|
-
color="secondary"
|
|
540
|
-
/>
|
|
541
|
-
<Icon icon={Heart} size="xsm" color="secondary" />
|
|
542
|
-
<Text
|
|
543
|
-
type="supporting"
|
|
544
|
-
color="secondary"
|
|
545
|
-
hasTabularNumbers>
|
|
546
|
-
{item.reactions}
|
|
547
|
-
</Text>
|
|
548
|
-
</HStack>
|
|
549
|
-
<Text type="supporting" color="secondary">
|
|
550
|
-
Like
|
|
551
|
-
</Text>
|
|
552
|
-
<Bullet />
|
|
553
|
-
<Text type="supporting" color="secondary">
|
|
554
|
-
Reply
|
|
555
|
-
</Text>
|
|
556
|
-
<Bullet />
|
|
521
|
+
<List density="spacious" style={itemsList}>
|
|
522
|
+
{ACTIVITY.map(item => (
|
|
523
|
+
<ListItem
|
|
524
|
+
key={`${item.user}-${item.time}`}
|
|
525
|
+
label={item.user}
|
|
526
|
+
description={
|
|
527
|
+
<VStack gap={2}>
|
|
528
|
+
<Text type="body">{item.text}</Text>
|
|
529
|
+
{item.changes && (
|
|
530
|
+
<VStack gap={1}>
|
|
531
|
+
{item.changes.map(change => (
|
|
532
|
+
<HStack key={change} gap={2} vAlign="center">
|
|
533
|
+
<Icon icon={SquarePen} size="sm" color="secondary" />
|
|
534
|
+
<Text type="supporting" color="secondary">
|
|
535
|
+
{change}
|
|
536
|
+
</Text>
|
|
537
|
+
</HStack>
|
|
538
|
+
))}
|
|
539
|
+
</VStack>
|
|
540
|
+
)}
|
|
541
|
+
<HStack gap={3} vAlign="center">
|
|
542
|
+
<HStack gap={1} vAlign="center">
|
|
543
|
+
<Icon icon={ThumbsUp} size="xsm" color="secondary" />
|
|
544
|
+
<Icon icon={Heart} size="xsm" color="secondary" />
|
|
557
545
|
<Text
|
|
558
546
|
type="supporting"
|
|
559
547
|
color="secondary"
|
|
560
548
|
hasTabularNumbers>
|
|
561
|
-
{item.
|
|
549
|
+
{item.reactions}
|
|
562
550
|
</Text>
|
|
563
551
|
</HStack>
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
552
|
+
<Text type="supporting" color="secondary">
|
|
553
|
+
Like
|
|
554
|
+
</Text>
|
|
555
|
+
<Bullet />
|
|
556
|
+
<Text type="supporting" color="secondary">
|
|
557
|
+
Reply
|
|
558
|
+
</Text>
|
|
559
|
+
<Bullet />
|
|
560
|
+
<Text
|
|
561
|
+
type="supporting"
|
|
562
|
+
color="secondary"
|
|
563
|
+
hasTabularNumbers>
|
|
564
|
+
{item.time}
|
|
565
|
+
</Text>
|
|
566
|
+
</HStack>
|
|
567
|
+
</VStack>
|
|
568
|
+
}
|
|
569
|
+
startContent={<Avatar name={item.user} size="md" />}
|
|
570
|
+
/>
|
|
568
571
|
))}
|
|
569
|
-
</
|
|
572
|
+
</List>
|
|
570
573
|
</VStack>
|
|
571
574
|
</Section>
|
|
572
575
|
);
|
|
@@ -578,9 +581,9 @@ function TimelineSection() {
|
|
|
578
581
|
function PanelContent() {
|
|
579
582
|
return (
|
|
580
583
|
<VStack gap={4}>
|
|
581
|
-
<Collapsible trigger={<Heading level={
|
|
584
|
+
<Collapsible trigger={<Heading level={3}>Notes</Heading>}>
|
|
582
585
|
<Text type="body">
|
|
583
|
-
Customer is a repeat buyer
|
|
586
|
+
Customer is a repeat buyer, 3rd order this quarter. Prefers
|
|
584
587
|
moonlight and sage glazes. Requested gift wrapping for the mug set.
|
|
585
588
|
Ships to a residential address in CA.{' '}
|
|
586
589
|
<Link href="#/templates/detail-page">
|
|
@@ -588,20 +591,20 @@ function PanelContent() {
|
|
|
588
591
|
</Link>
|
|
589
592
|
</Text>
|
|
590
593
|
</Collapsible>
|
|
591
|
-
<Collapsible trigger={<Heading level={
|
|
594
|
+
<Collapsible trigger={<Heading level={3}>Customer</Heading>}>
|
|
592
595
|
<MetadataList>
|
|
593
|
-
<MetadataListItem label="Name">
|
|
596
|
+
<MetadataListItem label="Name">Vlad Dracul</MetadataListItem>
|
|
594
597
|
<MetadataListItem label="Address">
|
|
595
598
|
321 Smith Road, CA 38238
|
|
596
599
|
</MetadataListItem>
|
|
597
600
|
<MetadataListItem label="Phone">234-555-0134</MetadataListItem>
|
|
598
|
-
<MetadataListItem label="Email">
|
|
601
|
+
<MetadataListItem label="Email">vlad@castle-dracula.transylvania</MetadataListItem>
|
|
599
602
|
<MetadataListItem label="Billing Address">
|
|
600
603
|
Same as shipping address
|
|
601
604
|
</MetadataListItem>
|
|
602
605
|
</MetadataList>
|
|
603
606
|
</Collapsible>
|
|
604
|
-
<Collapsible trigger={<Heading level={
|
|
607
|
+
<Collapsible trigger={<Heading level={3}>Fraud analysis</Heading>}>
|
|
605
608
|
<VStack gap={1}>
|
|
606
609
|
<ProgressBar
|
|
607
610
|
label="Risk level"
|
|
@@ -679,6 +682,7 @@ export default function DetailPage() {
|
|
|
679
682
|
isOpen={isNarrow && isPanelDialogOpen}
|
|
680
683
|
onOpenChange={setPanelDialogOpen}>
|
|
681
684
|
<Layout
|
|
685
|
+
height="auto"
|
|
682
686
|
header={
|
|
683
687
|
<DialogHeader
|
|
684
688
|
title="Order details"
|
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// L > (LC > V[g=8] > (V[g=2] >
|
|
3
|
+
// L > (LC > V[g=8] > (V[g=2] > Hd"Button"[level=1] + Tx"March 30, 2026"[t=supporting]) + (C.muted[p=0] > Ctr[h=360]) + (V[g=4] > Hd"Usage"[level=2] + Tx"Usage"[t=body] + Hd"Best practices"[level=3] + T) + D + (V[g=4] > Hd"Examples"[level=2] + Tx"Explore"[t=body]) + (V[g=8] > (C[p=0] > (S[p=3] > Tx"Semantics"[t=body]) + Ctr[h=280] + (S.muted[p=3] > V[g=3] > (TL > Tab"Description"! + Tab"Code") + Tx"We have four"[t=body])) + (C[p=0] > (S[p=3] > Tx"Default button with badge"[t=body]) + Ctr[h=280] + (S.muted[p=3] > V[g=3] > (TL > Tab"Description"! + Tab"Code") + Tx"Buttons can include"[t=body])))) + (LP > Outline)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Documentation detail — one component, its usage, its guidance and its live examples. (Frame/responsive/container: see XLE header above.)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
useCallback,
|
|
11
|
+
useState,
|
|
12
|
+
useMemo,
|
|
13
|
+
type CSSProperties,
|
|
14
|
+
type ReactNode,
|
|
15
|
+
} from 'react';
|
|
6
16
|
import {Heading, Text} from '@astryxdesign/core/Text';
|
|
7
17
|
import {Button} from '@astryxdesign/core/Button';
|
|
8
|
-
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
9
18
|
import {Card} from '@astryxdesign/core/Card';
|
|
10
19
|
import {Avatar} from '@astryxdesign/core/Avatar';
|
|
11
20
|
import {Badge} from '@astryxdesign/core/Badge';
|
|
@@ -14,7 +23,7 @@ import {Banner} from '@astryxdesign/core/Banner';
|
|
|
14
23
|
import {CodeBlock} from '@astryxdesign/core/CodeBlock';
|
|
15
24
|
import {TabList, Tab} from '@astryxdesign/core/TabList';
|
|
16
25
|
import {Selector} from '@astryxdesign/core/Selector';
|
|
17
|
-
import {HStack, VStack
|
|
26
|
+
import {HStack, VStack} from '@astryxdesign/core/Stack';
|
|
18
27
|
import {Layout, LayoutContent, LayoutPanel} from '@astryxdesign/core/Layout';
|
|
19
28
|
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
20
29
|
import {Dialog, DialogHeader} from '@astryxdesign/core/Dialog';
|
|
@@ -25,14 +34,12 @@ import {Icon} from '@astryxdesign/core/Icon';
|
|
|
25
34
|
import {Section} from '@astryxdesign/core/Section';
|
|
26
35
|
import {Center} from '@astryxdesign/core/Center';
|
|
27
36
|
import {Outline, type OutlineItem} from '@astryxdesign/core/Outline';
|
|
28
|
-
import {
|
|
37
|
+
import {Plus} from 'lucide-react';
|
|
38
|
+
import {outlinePanel} from 'astryx-dracula/shared/chaptered-doc-config';
|
|
29
39
|
|
|
30
|
-
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
top: 24,
|
|
34
|
-
alignSelf: 'start',
|
|
35
|
-
paddingBlockStart: 120,
|
|
40
|
+
// Flush the tab list with the example card's 12px inset (Section padding 3).
|
|
41
|
+
const tabListFlush: CSSProperties = {
|
|
42
|
+
marginInlineStart: 'calc(var(--spacing-3) * -1)',
|
|
36
43
|
};
|
|
37
44
|
|
|
38
45
|
const COMPONENT_OUTLINE_ITEMS: OutlineItem[] = [
|
|
@@ -54,7 +61,9 @@ function DialogPreview() {
|
|
|
54
61
|
const [isOpen, setIsOpen] = useState(false);
|
|
55
62
|
return (
|
|
56
63
|
<VStack gap={3}>
|
|
57
|
-
<
|
|
64
|
+
<Text type="body" weight="semibold">
|
|
65
|
+
Dialog
|
|
66
|
+
</Text>
|
|
58
67
|
<Button
|
|
59
68
|
label="Open Dialog"
|
|
60
69
|
variant="primary"
|
|
@@ -441,7 +450,7 @@ const COMPONENT_DOCS: Record<
|
|
|
441
450
|
title: 'Default button with badge',
|
|
442
451
|
description:
|
|
443
452
|
'Buttons can include a badge to highlight new or updated actions.',
|
|
444
|
-
code: `<Button\n label="Button"\n variant="
|
|
453
|
+
code: `<Button\n label="Button"\n variant="secondary"\n/>`,
|
|
445
454
|
},
|
|
446
455
|
],
|
|
447
456
|
},
|
|
@@ -496,6 +505,57 @@ function getComponentDocs(key: string) {
|
|
|
496
505
|
};
|
|
497
506
|
}
|
|
498
507
|
|
|
508
|
+
const EXAMPLE_PREVIEWS: Record<string, ReactNode[]> = {
|
|
509
|
+
button: [
|
|
510
|
+
<HStack key="semantics" gap={3} vAlign="center" wrap="wrap">
|
|
511
|
+
<Button label="Flat" variant="ghost" />
|
|
512
|
+
<Button label="Default" variant="secondary" />
|
|
513
|
+
<Button label="Primary" variant="primary" />
|
|
514
|
+
<Button label="Destructive" variant="destructive" />
|
|
515
|
+
</HStack>,
|
|
516
|
+
<Button key="badge" label="Button" variant="secondary" />,
|
|
517
|
+
],
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
const COMPONENT_PREVIEWS: Record<string, ReactNode> = {
|
|
521
|
+
button: (
|
|
522
|
+
<Button
|
|
523
|
+
label="Button"
|
|
524
|
+
variant="secondary"
|
|
525
|
+
icon={<Icon icon={Plus} />}
|
|
526
|
+
endContent={<Badge label="New" variant="cyan" />}
|
|
527
|
+
/>
|
|
528
|
+
),
|
|
529
|
+
avatar: <Avatar name="Vlad" size="lg" />,
|
|
530
|
+
badge: <Badge label="Design" variant="yellow" />,
|
|
531
|
+
card: (
|
|
532
|
+
<Card>
|
|
533
|
+
<VStack gap={2}>
|
|
534
|
+
<Text type="body" weight="semibold">
|
|
535
|
+
Castle Card
|
|
536
|
+
</Text>
|
|
537
|
+
<Text type="body" color="secondary">
|
|
538
|
+
Cards group related content and actions.
|
|
539
|
+
</Text>
|
|
540
|
+
</VStack>
|
|
541
|
+
</Card>
|
|
542
|
+
),
|
|
543
|
+
banner: (
|
|
544
|
+
<Banner status="info" title="Information">
|
|
545
|
+
<Text type="body">This is an informational banner message.</Text>
|
|
546
|
+
</Banner>
|
|
547
|
+
),
|
|
548
|
+
dialog: <DialogPreview />,
|
|
549
|
+
text: <Text type="body">Body text</Text>,
|
|
550
|
+
divider: <Divider />,
|
|
551
|
+
token: <Token label="Dracula" />,
|
|
552
|
+
tooltip: (
|
|
553
|
+
<Tooltip content="Primary action">
|
|
554
|
+
<Button label="Hover me" variant="primary" />
|
|
555
|
+
</Tooltip>
|
|
556
|
+
),
|
|
557
|
+
};
|
|
558
|
+
|
|
499
559
|
// ---------------------------------------------------------------------------
|
|
500
560
|
// ComponentDetailView
|
|
501
561
|
// ---------------------------------------------------------------------------
|
|
@@ -516,55 +576,6 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
516
576
|
}
|
|
517
577
|
}, []);
|
|
518
578
|
|
|
519
|
-
const EXAMPLE_PREVIEWS: Record<string, React.ReactNode[]> = {
|
|
520
|
-
button: [
|
|
521
|
-
<HStack key="semantics" gap={3} vAlign="center" wrap="wrap">
|
|
522
|
-
<Button label="Flat" variant="ghost" />
|
|
523
|
-
<Button label="Default" variant="secondary" />
|
|
524
|
-
<Button label="Primary" variant="primary" />
|
|
525
|
-
<Button label="Destructive" variant="destructive" />
|
|
526
|
-
</HStack>,
|
|
527
|
-
<Button key="badge" label="Button" variant="secondary" />,
|
|
528
|
-
],
|
|
529
|
-
};
|
|
530
|
-
|
|
531
|
-
const COMPONENT_PREVIEWS: Record<string, React.ReactNode> = {
|
|
532
|
-
button: (
|
|
533
|
-
<Button
|
|
534
|
-
label="Button"
|
|
535
|
-
variant="secondary"
|
|
536
|
-
icon={<Icon icon={Plus} />}
|
|
537
|
-
endContent={<Badge label="New" variant="cyan" />}
|
|
538
|
-
/>
|
|
539
|
-
),
|
|
540
|
-
avatar: <Avatar name="Vlad" size="lg" />,
|
|
541
|
-
badge: <Badge label="Success" variant="green" />,
|
|
542
|
-
card: (
|
|
543
|
-
<Card>
|
|
544
|
-
<VStack gap={2}>
|
|
545
|
-
<Heading level={4}>Castle Card</Heading>
|
|
546
|
-
<Text type="body" color="secondary">
|
|
547
|
-
Cards group related content and actions.
|
|
548
|
-
</Text>
|
|
549
|
-
</VStack>
|
|
550
|
-
</Card>
|
|
551
|
-
),
|
|
552
|
-
banner: (
|
|
553
|
-
<Banner status="info" title="Information">
|
|
554
|
-
<Text type="body">This is an informational banner message.</Text>
|
|
555
|
-
</Banner>
|
|
556
|
-
),
|
|
557
|
-
dialog: <DialogPreview />,
|
|
558
|
-
text: <Text type="body">Body text</Text>,
|
|
559
|
-
divider: <Divider />,
|
|
560
|
-
token: <Token label="Dracula" />,
|
|
561
|
-
tooltip: (
|
|
562
|
-
<Tooltip content="Primary action">
|
|
563
|
-
<Button label="Hover me" variant="primary" />
|
|
564
|
-
</Tooltip>
|
|
565
|
-
),
|
|
566
|
-
};
|
|
567
|
-
|
|
568
579
|
const docs = useMemo(() => getComponentDocs(activeNav), [activeNav]);
|
|
569
580
|
const previews = EXAMPLE_PREVIEWS[activeNav] ?? [];
|
|
570
581
|
|
|
@@ -590,7 +601,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
590
601
|
<LayoutContent isScrollable={false} padding={8}>
|
|
591
602
|
<VStack gap={8}>
|
|
592
603
|
<VStack gap={2}>
|
|
593
|
-
<
|
|
604
|
+
<Heading level={1} type="display-2">{getComponentName(activeNav)}</Heading>
|
|
594
605
|
<Text type="supporting" color="secondary" hasTabularNumbers>
|
|
595
606
|
March 30, 2026 · Updated 5:40 p.m. PST
|
|
596
607
|
</Text>
|
|
@@ -610,7 +621,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
610
621
|
<Center height={360}>
|
|
611
622
|
{COMPONENT_PREVIEWS[activeNav] ?? (
|
|
612
623
|
<Text type="supporting" color="secondary">
|
|
613
|
-
|
|
624
|
+
Quiet in the crypt — no preview haunts this shelf yet.
|
|
614
625
|
</Text>
|
|
615
626
|
)}
|
|
616
627
|
</Center>
|
|
@@ -620,15 +631,14 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
620
631
|
<Heading id="usage" level={2}>
|
|
621
632
|
Usage
|
|
622
633
|
</Heading>
|
|
623
|
-
<Text type="
|
|
624
|
-
{docs.usage}
|
|
625
|
-
</Text>
|
|
634
|
+
<Text type="body">{docs.usage}</Text>
|
|
626
635
|
<Heading id="best-practices" level={3}>
|
|
627
636
|
Best practices
|
|
628
637
|
</Heading>
|
|
629
638
|
<Table
|
|
630
639
|
data={docs.bestPractices as Record<string, unknown>[]}
|
|
631
640
|
dividers="none"
|
|
641
|
+
hasHover
|
|
632
642
|
columns={[
|
|
633
643
|
{
|
|
634
644
|
key: 'type',
|
|
@@ -661,7 +671,7 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
661
671
|
<Heading id="examples" level={2}>
|
|
662
672
|
Examples
|
|
663
673
|
</Heading>
|
|
664
|
-
<Text type="
|
|
674
|
+
<Text type="body">
|
|
665
675
|
Explore common configurations, variations, and states for this
|
|
666
676
|
component.
|
|
667
677
|
</Text>
|
|
@@ -671,39 +681,16 @@ function ComponentDetailView({activeNav}: {activeNav: string}) {
|
|
|
671
681
|
const tabKey = `${activeNav}-${i}`;
|
|
672
682
|
const activeTab = exampleTabs[tabKey] ?? 'description';
|
|
673
683
|
return (
|
|
674
|
-
<Card key={
|
|
684
|
+
<Card key={example.title} padding={0}>
|
|
675
685
|
<Section padding={3} variant="transparent">
|
|
676
|
-
<
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
{example.title}
|
|
680
|
-
</Text>
|
|
681
|
-
</StackItem>
|
|
682
|
-
<HStack gap={1} vAlign="center" wrap="wrap">
|
|
683
|
-
<Button
|
|
684
|
-
label="Open in Craft"
|
|
685
|
-
variant="ghost"
|
|
686
|
-
size="sm"
|
|
687
|
-
icon={<Icon icon={ExternalLink} />}
|
|
688
|
-
/>
|
|
689
|
-
<Button
|
|
690
|
-
label="Send to CLI"
|
|
691
|
-
variant="ghost"
|
|
692
|
-
size="sm"
|
|
693
|
-
/>
|
|
694
|
-
<IconButton
|
|
695
|
-
label="Fullscreen"
|
|
696
|
-
variant="ghost"
|
|
697
|
-
size="sm"
|
|
698
|
-
icon={<Icon icon={Expand} />}
|
|
699
|
-
/>
|
|
700
|
-
</HStack>
|
|
701
|
-
</HStack>
|
|
686
|
+
<Text type="body" weight="semibold">
|
|
687
|
+
{example.title}
|
|
688
|
+
</Text>
|
|
702
689
|
</Section>
|
|
703
690
|
<Center height={280}>
|
|
704
691
|
{previews[i] ?? (
|
|
705
692
|
<Text type="supporting" color="secondary">
|
|
706
|
-
|
|
693
|
+
Quiet in the crypt — no preview haunts this shelf yet.
|
|
707
694
|
</Text>
|
|
708
695
|
)}
|
|
709
696
|
</Center>
|