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
package/templates/editor.tsx
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
+
// L > (LP[w=320] > V[g=4] > (S[p=4] > V[g=4] > Hd"Page Editor"[level=1] + Tbar) + (V[g=4] > (TL > Tab"Blocks"! + Tab"Properties") + D + (S[p=4] > V[g=2] > Hd"Add Block"[level=3] + UL + Hd"Layers"[level=3] + UL))) + (LC > V[g=4] > C*3)
|
|
2
4
|
|
|
3
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
useState,
|
|
7
|
+
useCallback,
|
|
8
|
+
type ComponentType,
|
|
9
|
+
type CSSProperties,
|
|
10
|
+
type KeyboardEvent,
|
|
11
|
+
type MouseEvent,
|
|
12
|
+
type SVGProps,
|
|
13
|
+
} from 'react';
|
|
4
14
|
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
5
15
|
import {Button} from '@astryxdesign/core/Button';
|
|
6
|
-
import {
|
|
16
|
+
import {SelectableCard} from '@astryxdesign/core/SelectableCard';
|
|
7
17
|
import {Center} from '@astryxdesign/core/Center';
|
|
8
18
|
import {Dialog} from '@astryxdesign/core/Dialog';
|
|
9
19
|
import {Divider} from '@astryxdesign/core/Divider';
|
|
@@ -18,7 +28,7 @@ import {
|
|
|
18
28
|
} from '@astryxdesign/core/Layout';
|
|
19
29
|
import {Icon} from '@astryxdesign/core/Icon';
|
|
20
30
|
import {List, ListItem} from '@astryxdesign/core/List';
|
|
21
|
-
import {Table, proportional
|
|
31
|
+
import {Table, proportional} from '@astryxdesign/core/Table';
|
|
22
32
|
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
23
33
|
import {Section} from '@astryxdesign/core/Section';
|
|
24
34
|
import {
|
|
@@ -54,10 +64,8 @@ import {
|
|
|
54
64
|
Banknote,
|
|
55
65
|
CarFront,
|
|
56
66
|
CirclePlay,
|
|
57
|
-
Ellipsis,
|
|
58
67
|
Lock,
|
|
59
68
|
} from 'lucide-react';
|
|
60
|
-
import {Spinner} from '@astryxdesign/core/Spinner';
|
|
61
69
|
|
|
62
70
|
type BlockType =
|
|
63
71
|
'hero' | 'text' | 'image' | 'button' | 'cards' | 'features' | 'cta';
|
|
@@ -71,7 +79,7 @@ interface Block {
|
|
|
71
79
|
|
|
72
80
|
type ViewportSize = 'desktop' | 'tablet' | 'phone';
|
|
73
81
|
type SidebarTab = 'blocks' | 'properties';
|
|
74
|
-
type IconComponent =
|
|
82
|
+
type IconComponent = ComponentType<SVGProps<SVGSVGElement>>;
|
|
75
83
|
|
|
76
84
|
const BLOCK_META: Record<BlockType, {label: string; icon: IconComponent}> = {
|
|
77
85
|
hero: {label: 'Hero', icon: LayoutGrid},
|
|
@@ -100,16 +108,19 @@ const CATEGORY_ICONS: Record<string, IconComponent> = {
|
|
|
100
108
|
Entertainment: CirclePlay,
|
|
101
109
|
};
|
|
102
110
|
|
|
111
|
+
// The minimums clear the 375px phone canvas once the preview card's 24px
|
|
112
|
+
// padding is taken out; the widest column carries the slack and every cell
|
|
113
|
+
// truncates rather than pushing the table past the card.
|
|
103
114
|
const TRANSACTION_COLUMNS: TableColumn<Transaction>[] = [
|
|
104
115
|
{
|
|
105
116
|
key: 'name',
|
|
106
117
|
header: 'Transaction',
|
|
107
|
-
width: proportional(2, {minWidth:
|
|
118
|
+
width: proportional(2, {minWidth: 104}),
|
|
108
119
|
renderCell: (item: Transaction) => (
|
|
109
120
|
<HStack gap={3} vAlign="center">
|
|
110
121
|
<Icon icon={CATEGORY_ICONS[item.category] || Sparkles} />
|
|
111
122
|
<VStack gap={0}>
|
|
112
|
-
<Text type="
|
|
123
|
+
<Text type="body" weight="semibold">
|
|
113
124
|
{item.name}
|
|
114
125
|
</Text>
|
|
115
126
|
<Text type="supporting" color="secondary">
|
|
@@ -122,7 +133,7 @@ const TRANSACTION_COLUMNS: TableColumn<Transaction>[] = [
|
|
|
122
133
|
{
|
|
123
134
|
key: 'date',
|
|
124
135
|
header: 'Date',
|
|
125
|
-
width: proportional(1, {minWidth:
|
|
136
|
+
width: proportional(1, {minWidth: 64}),
|
|
126
137
|
renderCell: (item: Transaction) => (
|
|
127
138
|
<Text type="body" color="secondary" hasTabularNumbers>
|
|
128
139
|
{item.date}
|
|
@@ -132,27 +143,13 @@ const TRANSACTION_COLUMNS: TableColumn<Transaction>[] = [
|
|
|
132
143
|
{
|
|
133
144
|
key: 'amount',
|
|
134
145
|
header: 'Amount',
|
|
135
|
-
width: proportional(1, {minWidth:
|
|
146
|
+
width: proportional(1, {minWidth: 88}),
|
|
136
147
|
renderCell: (item: Transaction) => (
|
|
137
|
-
<Text type="
|
|
148
|
+
<Text type="body" weight="semibold" hasTabularNumbers>
|
|
138
149
|
{item.amount}
|
|
139
150
|
</Text>
|
|
140
151
|
),
|
|
141
152
|
},
|
|
142
|
-
{
|
|
143
|
-
key: 'actions',
|
|
144
|
-
header: '',
|
|
145
|
-
width: pixel(44),
|
|
146
|
-
renderCell: () => (
|
|
147
|
-
<Button
|
|
148
|
-
label="More"
|
|
149
|
-
icon={<Icon icon={Ellipsis} size="sm" />}
|
|
150
|
-
variant="ghost"
|
|
151
|
-
size="sm"
|
|
152
|
-
isIconOnly
|
|
153
|
-
/>
|
|
154
|
-
),
|
|
155
|
-
},
|
|
156
153
|
];
|
|
157
154
|
|
|
158
155
|
const VIEWPORT_MAX: Record<ViewportSize, number> = {
|
|
@@ -241,9 +238,9 @@ function defaultProps(type: BlockType): Record<string, unknown> {
|
|
|
241
238
|
switch (type) {
|
|
242
239
|
case 'hero':
|
|
243
240
|
return {
|
|
244
|
-
heading: '
|
|
245
|
-
subheading: '
|
|
246
|
-
buttonLabel: '
|
|
241
|
+
heading: 'Midnight at the Castle Gates',
|
|
242
|
+
subheading: 'Gather the coven — the night is young and the candles are lit.',
|
|
243
|
+
buttonLabel: 'Enter the night',
|
|
247
244
|
alignment: 'center',
|
|
248
245
|
};
|
|
249
246
|
case 'text':
|
|
@@ -281,10 +278,11 @@ function defaultProps(type: BlockType): Record<string, unknown> {
|
|
|
281
278
|
};
|
|
282
279
|
case 'cta':
|
|
283
280
|
return {
|
|
284
|
-
heading: '
|
|
285
|
-
description:
|
|
286
|
-
|
|
287
|
-
|
|
281
|
+
heading: 'Join the Midnight Coven',
|
|
282
|
+
description:
|
|
283
|
+
'Claim your seat at the table — letters from the crypt, once a moon.',
|
|
284
|
+
primaryLabel: 'Join the coven',
|
|
285
|
+
secondaryLabel: 'Read the lore',
|
|
288
286
|
};
|
|
289
287
|
}
|
|
290
288
|
}
|
|
@@ -308,20 +306,22 @@ const canvasStyle = (maxWidth: number): CSSProperties => ({
|
|
|
308
306
|
width: '100%',
|
|
309
307
|
marginInline: 'auto',
|
|
310
308
|
});
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
//
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
//
|
|
309
|
+
// `clickable` and `selectedCard` are gone: the eight block cards are core's
|
|
310
|
+
// `SelectableCard` now, which supplies the cursor, the hover overlay
|
|
311
|
+
// (`SelectableCard.tsx:90` reads `--color-overlay-hover`) and the selected ring
|
|
312
|
+
// from the same tokens this file was hand-rolling. Worth recording why the old
|
|
313
|
+
// pair was wrong twice over: it was reachable only as an inline `style` spread,
|
|
314
|
+
// and a spread of a lookup is not a measurement — those values were never
|
|
315
|
+
// checked against what `Card` actually renders. The 1.4.11 non-claim on the
|
|
316
|
+
// ring colour is unchanged and still stands: Comment #6272A4 cannot reach
|
|
317
|
+
// 3:1 on #343746 at any alpha, and core's own selected ring has that ceiling.
|
|
318
|
+
// The sidebar keeps its width when the canvas is wider than the window.
|
|
319
|
+
const panelShrink: CSSProperties = {flexShrink: 0};
|
|
320
|
+
// Square muted chip behind the CTA icon — Center handles the centering
|
|
321
321
|
// and sizing; only the surface (radius + fill) needs custom CSS.
|
|
322
322
|
const iconCircle: CSSProperties = {
|
|
323
|
-
borderRadius: '
|
|
324
|
-
backgroundColor: 'var(--color-
|
|
323
|
+
borderRadius: 'var(--radius-element)',
|
|
324
|
+
backgroundColor: 'var(--color-selection)',
|
|
325
325
|
};
|
|
326
326
|
|
|
327
327
|
// ---------------------------------------------------------------------------
|
|
@@ -457,37 +457,41 @@ function BlockPreview({
|
|
|
457
457
|
onSelect: () => void;
|
|
458
458
|
}) {
|
|
459
459
|
const {type, props} = block;
|
|
460
|
-
const cardStyle: CSSProperties = {
|
|
461
|
-
...clickable,
|
|
462
|
-
...(isSelected ? selectedCard : null),
|
|
463
|
-
};
|
|
464
460
|
|
|
465
461
|
switch (type) {
|
|
466
462
|
case 'hero':
|
|
467
463
|
return (
|
|
468
|
-
<
|
|
464
|
+
<SelectableCard
|
|
465
|
+
padding={4}
|
|
466
|
+
label={block.label}
|
|
467
|
+
isSelected={isSelected}
|
|
468
|
+
onChange={onSelect}>
|
|
469
469
|
<VStack gap={4}>
|
|
470
|
-
<Heading level={
|
|
470
|
+
<Heading level={3}>
|
|
471
471
|
{(props.heading as string) || 'Hero Heading'}
|
|
472
472
|
</Heading>
|
|
473
|
-
<Text type="
|
|
474
|
-
{(props.subheading as string) || '
|
|
473
|
+
<Text type="body" color="secondary">
|
|
474
|
+
{(props.subheading as string) || 'A whisper from the crypt…'}
|
|
475
475
|
</Text>
|
|
476
476
|
{(props.buttonLabel as string) && (
|
|
477
477
|
<Button label={props.buttonLabel as string} />
|
|
478
478
|
)}
|
|
479
479
|
</VStack>
|
|
480
|
-
|
|
480
|
+
</SelectableCard>
|
|
481
481
|
);
|
|
482
482
|
|
|
483
483
|
case 'text':
|
|
484
484
|
if (props.heading) {
|
|
485
485
|
return (
|
|
486
|
-
<
|
|
486
|
+
<SelectableCard
|
|
487
|
+
padding={4}
|
|
488
|
+
label={block.label}
|
|
489
|
+
isSelected={isSelected}
|
|
490
|
+
onChange={onSelect}>
|
|
487
491
|
<EmptyState
|
|
488
492
|
title={props.heading as string}
|
|
489
493
|
description={props.description as string}
|
|
490
|
-
icon={<
|
|
494
|
+
icon={<Icon icon={FileText} color="secondary" />}
|
|
491
495
|
actions={
|
|
492
496
|
(props.buttonLabel as string) ? (
|
|
493
497
|
<Button
|
|
@@ -497,32 +501,44 @@ function BlockPreview({
|
|
|
497
501
|
) : undefined
|
|
498
502
|
}
|
|
499
503
|
/>
|
|
500
|
-
</
|
|
504
|
+
</SelectableCard>
|
|
501
505
|
);
|
|
502
506
|
}
|
|
503
507
|
return (
|
|
504
|
-
<
|
|
508
|
+
<SelectableCard
|
|
509
|
+
padding={4}
|
|
510
|
+
label={block.label}
|
|
511
|
+
isSelected={isSelected}
|
|
512
|
+
onChange={onSelect}>
|
|
505
513
|
<Text type="body">
|
|
506
|
-
{(props.content as string) || '
|
|
514
|
+
{(props.content as string) || 'Ink your midnight thoughts here…'}
|
|
507
515
|
</Text>
|
|
508
|
-
|
|
516
|
+
</SelectableCard>
|
|
509
517
|
);
|
|
510
518
|
|
|
511
519
|
case 'image':
|
|
512
520
|
return (
|
|
513
|
-
<
|
|
521
|
+
<SelectableCard
|
|
522
|
+
padding={4}
|
|
523
|
+
label={block.label}
|
|
524
|
+
isSelected={isSelected}
|
|
525
|
+
onChange={onSelect}>
|
|
514
526
|
<EmptyState
|
|
515
527
|
title="Image Block"
|
|
516
528
|
description="Drop an image or enter a URL"
|
|
517
|
-
icon={<Icon icon={Image} />}
|
|
529
|
+
icon={<Icon icon={Image} color="secondary" />}
|
|
518
530
|
isCompact
|
|
519
531
|
/>
|
|
520
|
-
|
|
532
|
+
</SelectableCard>
|
|
521
533
|
);
|
|
522
534
|
|
|
523
535
|
case 'button':
|
|
524
536
|
return (
|
|
525
|
-
<
|
|
537
|
+
<SelectableCard
|
|
538
|
+
padding={4}
|
|
539
|
+
label={block.label}
|
|
540
|
+
isSelected={isSelected}
|
|
541
|
+
onChange={onSelect}>
|
|
526
542
|
<Center>
|
|
527
543
|
<Button
|
|
528
544
|
label={(props.label as string) || 'Button'}
|
|
@@ -533,13 +549,17 @@ function BlockPreview({
|
|
|
533
549
|
size={(props.size as 'sm' | 'md' | 'lg') || 'md'}
|
|
534
550
|
/>
|
|
535
551
|
</Center>
|
|
536
|
-
|
|
552
|
+
</SelectableCard>
|
|
537
553
|
);
|
|
538
554
|
|
|
539
555
|
case 'features': {
|
|
540
556
|
const items = (props.items as Transaction[]) || [];
|
|
541
557
|
return (
|
|
542
|
-
<
|
|
558
|
+
<SelectableCard
|
|
559
|
+
padding={4}
|
|
560
|
+
label={block.label}
|
|
561
|
+
isSelected={isSelected}
|
|
562
|
+
onChange={onSelect}>
|
|
543
563
|
<VStack gap={4}>
|
|
544
564
|
<HStack gap={3} vAlign="start" hAlign="between">
|
|
545
565
|
<VStack gap={1}>
|
|
@@ -547,12 +567,12 @@ function BlockPreview({
|
|
|
547
567
|
{(props.heading as string) || 'Features'}
|
|
548
568
|
</Heading>
|
|
549
569
|
{(props.description as string) && (
|
|
550
|
-
<Text type="
|
|
570
|
+
<Text type="body" color="secondary">
|
|
551
571
|
{props.description as string}
|
|
552
572
|
</Text>
|
|
553
573
|
)}
|
|
554
574
|
</VStack>
|
|
555
|
-
<Button label="View All" variant="secondary"
|
|
575
|
+
<Button label="View All" variant="secondary" />
|
|
556
576
|
</HStack>
|
|
557
577
|
<Table
|
|
558
578
|
data={items}
|
|
@@ -562,7 +582,7 @@ function BlockPreview({
|
|
|
562
582
|
textOverflow="truncate"
|
|
563
583
|
/>
|
|
564
584
|
</VStack>
|
|
565
|
-
|
|
585
|
+
</SelectableCard>
|
|
566
586
|
);
|
|
567
587
|
}
|
|
568
588
|
|
|
@@ -570,41 +590,50 @@ function BlockPreview({
|
|
|
570
590
|
const cardItems =
|
|
571
591
|
(props.cards as Array<{title: string; description: string}>) || [];
|
|
572
592
|
return (
|
|
573
|
-
<
|
|
593
|
+
<SelectableCard
|
|
594
|
+
padding={4}
|
|
595
|
+
label={block.label}
|
|
596
|
+
isSelected={isSelected}
|
|
597
|
+
onChange={onSelect}>
|
|
574
598
|
<VStack gap={4}>
|
|
575
599
|
<Heading level={3}>Cards</Heading>
|
|
576
600
|
<Divider />
|
|
577
601
|
<List density="balanced" hasDividers={false}>
|
|
578
|
-
{cardItems.map((card
|
|
602
|
+
{cardItems.map((card) => (
|
|
579
603
|
<ListItem
|
|
580
|
-
key={
|
|
604
|
+
key={card.title}
|
|
581
605
|
label={card.title}
|
|
582
606
|
description={card.description}
|
|
583
607
|
/>
|
|
584
608
|
))}
|
|
585
609
|
</List>
|
|
586
610
|
</VStack>
|
|
587
|
-
|
|
611
|
+
</SelectableCard>
|
|
588
612
|
);
|
|
589
613
|
}
|
|
590
614
|
|
|
591
615
|
case 'cta':
|
|
592
616
|
return (
|
|
593
|
-
<
|
|
617
|
+
<SelectableCard
|
|
618
|
+
padding={4}
|
|
619
|
+
label={block.label}
|
|
620
|
+
isSelected={isSelected}
|
|
621
|
+
onChange={onSelect}>
|
|
594
622
|
<HStack gap={4} vAlign="start">
|
|
595
623
|
<Center width={40} height={40} style={iconCircle}>
|
|
596
624
|
<Icon icon={Lock} color="secondary" />
|
|
597
625
|
</Center>
|
|
598
626
|
<VStack gap={1}>
|
|
599
|
-
<
|
|
627
|
+
<Heading level={3}>
|
|
600
628
|
{(props.heading as string) || 'Notice'}
|
|
601
|
-
</
|
|
602
|
-
<Text type="
|
|
603
|
-
{(props.description as string) ||
|
|
629
|
+
</Heading>
|
|
630
|
+
<Text type="body" color="secondary">
|
|
631
|
+
{(props.description as string) ||
|
|
632
|
+
'Words from the crypt gather here…'}
|
|
604
633
|
</Text>
|
|
605
634
|
</VStack>
|
|
606
635
|
</HStack>
|
|
607
|
-
|
|
636
|
+
</SelectableCard>
|
|
608
637
|
);
|
|
609
638
|
|
|
610
639
|
default:
|
|
@@ -701,7 +730,7 @@ export default function PageEditor() {
|
|
|
701
730
|
const blocksTabContent = (
|
|
702
731
|
<VStack gap={2}>
|
|
703
732
|
<VStack gap={1}>
|
|
704
|
-
<Heading level={
|
|
733
|
+
<Heading level={3}>Add Block</Heading>
|
|
705
734
|
<List density="balanced" hasDividers={false}>
|
|
706
735
|
{(Object.keys(BLOCK_META) as BlockType[]).map(type => (
|
|
707
736
|
<ListItem
|
|
@@ -717,7 +746,7 @@ export default function PageEditor() {
|
|
|
717
746
|
</VStack>
|
|
718
747
|
|
|
719
748
|
<VStack gap={1}>
|
|
720
|
-
<Heading level={
|
|
749
|
+
<Heading level={3}>Layers</Heading>
|
|
721
750
|
<List density="balanced" hasDividers={false}>
|
|
722
751
|
{blocks.map(block => (
|
|
723
752
|
<ListItem
|
|
@@ -735,7 +764,7 @@ export default function PageEditor() {
|
|
|
735
764
|
icon={<Icon icon={ChevronUp} size="sm" />}
|
|
736
765
|
variant="ghost"
|
|
737
766
|
size="sm"
|
|
738
|
-
onClick={(e:
|
|
767
|
+
onClick={(e: MouseEvent) => {
|
|
739
768
|
e.stopPropagation();
|
|
740
769
|
moveBlock(block.id, -1);
|
|
741
770
|
}}
|
|
@@ -746,7 +775,7 @@ export default function PageEditor() {
|
|
|
746
775
|
icon={<Icon icon={ChevronDown} size="sm" />}
|
|
747
776
|
variant="ghost"
|
|
748
777
|
size="sm"
|
|
749
|
-
onClick={(e:
|
|
778
|
+
onClick={(e: MouseEvent) => {
|
|
750
779
|
e.stopPropagation();
|
|
751
780
|
moveBlock(block.id, 1);
|
|
752
781
|
}}
|
|
@@ -757,7 +786,7 @@ export default function PageEditor() {
|
|
|
757
786
|
icon={<Icon icon={Trash2} size="sm" />}
|
|
758
787
|
variant="ghost"
|
|
759
788
|
size="sm"
|
|
760
|
-
onClick={(e:
|
|
789
|
+
onClick={(e: MouseEvent) => {
|
|
761
790
|
e.stopPropagation();
|
|
762
791
|
deleteBlock(block.id);
|
|
763
792
|
}}
|
|
@@ -809,15 +838,16 @@ export default function PageEditor() {
|
|
|
809
838
|
<LayoutPanel
|
|
810
839
|
hasDivider={!isMobile}
|
|
811
840
|
padding={0}
|
|
812
|
-
|
|
841
|
+
width={isMobile ? '100%' : 320}
|
|
842
|
+
style={panelShrink}>
|
|
813
843
|
<VStack gap={4}>
|
|
814
|
-
{/* Panel Header */}
|
|
844
|
+
{/* Panel Header — mobile/desktop branches are exclusive: exactly one h1. */}
|
|
815
845
|
<Section variant="transparent" padding={4}>
|
|
816
846
|
{isMobile ? (
|
|
817
847
|
// Mobile: the title, an Edit button that opens the customizations
|
|
818
848
|
// dialog, and the primary action.
|
|
819
849
|
<HStack gap={3} vAlign="center" hAlign="between">
|
|
820
|
-
<Heading level={2
|
|
850
|
+
<Heading level={1} type="display-2">{pageTitle}</Heading>
|
|
821
851
|
<HStack gap={2} vAlign="center">
|
|
822
852
|
<Button
|
|
823
853
|
label="Edit"
|
|
@@ -837,7 +867,7 @@ export default function PageEditor() {
|
|
|
837
867
|
isLabelHidden
|
|
838
868
|
value={pageTitle}
|
|
839
869
|
onChange={setPageTitle}
|
|
840
|
-
onKeyDown={(e:
|
|
870
|
+
onKeyDown={(e: KeyboardEvent) => {
|
|
841
871
|
if (e.key === 'Enter') {
|
|
842
872
|
setIsEditingTitle(false);
|
|
843
873
|
}
|
|
@@ -846,7 +876,7 @@ export default function PageEditor() {
|
|
|
846
876
|
onBlur={() => setIsEditingTitle(false)}
|
|
847
877
|
/>
|
|
848
878
|
) : (
|
|
849
|
-
<Heading level={2
|
|
879
|
+
<Heading level={1} type="display-2">{pageTitle}</Heading>
|
|
850
880
|
)}
|
|
851
881
|
|
|
852
882
|
<Toolbar
|
|
@@ -920,7 +950,7 @@ export default function PageEditor() {
|
|
|
920
950
|
<EmptyState
|
|
921
951
|
title="No blocks yet"
|
|
922
952
|
description="Add blocks from the sidebar to start building your page"
|
|
923
|
-
icon={<Icon icon={CirclePlus} />}
|
|
953
|
+
icon={<Icon icon={CirclePlus} color="secondary" />}
|
|
924
954
|
/>
|
|
925
955
|
)}
|
|
926
956
|
</VStack>
|