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,74 +1,20 @@
|
|
|
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"Midnight Kicks"[level=1] + IB"Filter"[variant=ghost] + IB"Export"[variant=ghost] + B.primary"New order") + (LC[p3] > V[g4] > (V[g3] > C[p=4] + Tx"Daily revenue"[t=supporting] + (H[g2 a=center] > Ic + Tx"Revenue"[t=supporting])) + (T[hover] > (TR > THC"Order" + THC"Product" + THC"Amount" + THC"Customer" + THC"Email" + THC"Status" + THC"Date") + (TR > TC"ORD-1001" + TC"Air Max 90" + TC"$130" + TC"Sarah Chen" + TC"sarah.chen@acme.co" + (TC > Tk.green"Completed") + TC"2025-01-12")*5))
|
|
2
4
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
HStack,
|
|
7
|
-
StackItem,
|
|
8
|
-
Layout,
|
|
9
|
-
LayoutContent,
|
|
10
|
-
LayoutHeader,
|
|
11
|
-
} from '@astryxdesign/core/Layout';
|
|
12
|
-
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
13
|
-
import {Button} from '@astryxdesign/core/Button';
|
|
14
|
-
import {IconButton} from '@astryxdesign/core/IconButton';
|
|
15
|
-
import {Icon} from '@astryxdesign/core/Icon';
|
|
16
|
-
import {Badge} from '@astryxdesign/core/Badge';
|
|
17
|
-
import {Card} from '@astryxdesign/core/Card';
|
|
18
|
-
import {Link} from '@astryxdesign/core/Link';
|
|
19
|
-
import {Table, proportional, pixel} from '@astryxdesign/core/Table';
|
|
20
|
-
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
21
|
-
import {Filter, Download, Plus, Square} from 'lucide-react';
|
|
5
|
+
/**
|
|
6
|
+
* Midnight Kicks — sneaker order desk: a daily revenue line over the order log. Frame: page header (title + icon actions) | content column (c (Frame/responsive/container: see XLE header above.)
|
|
7
|
+
*/
|
|
22
8
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
// Square marks the chart legend swatch.
|
|
26
|
-
|
|
27
|
-
const swatchStyle: CSSProperties = {flexShrink: 0};
|
|
28
|
-
|
|
29
|
-
// Rounded product swatch: Dracula surface with a per-product accent glyph.
|
|
30
|
-
function ProductSwatch({accent, label}: {accent: string; label: string}) {
|
|
31
|
-
return (
|
|
32
|
-
<svg
|
|
33
|
-
viewBox="0 0 36 36"
|
|
34
|
-
width={36}
|
|
35
|
-
height={36}
|
|
36
|
-
style={swatchStyle}
|
|
37
|
-
role="img"
|
|
38
|
-
aria-label={`${label} swatch`}>
|
|
39
|
-
<rect width={36} height={36} rx={5} fill="var(--dracula-bg-light)" />
|
|
40
|
-
<rect
|
|
41
|
-
x={1}
|
|
42
|
-
y={1}
|
|
43
|
-
width={34}
|
|
44
|
-
height={34}
|
|
45
|
-
rx={4}
|
|
46
|
-
fill="none"
|
|
47
|
-
stroke="var(--color-widget-content-border)"
|
|
48
|
-
/>
|
|
49
|
-
<circle cx={18} cy={18} r={7} fill="none" stroke={accent} strokeWidth={3} />
|
|
50
|
-
</svg>
|
|
51
|
-
);
|
|
52
|
-
}
|
|
9
|
+
import {OrderDesk, type OrderDeskRow} from 'astryx-dracula/shared/order-desk';
|
|
10
|
+
import type {SceneHue} from 'astryx-dracula/shared/scene-hues';
|
|
53
11
|
|
|
54
12
|
// ============= DATA =============
|
|
55
13
|
|
|
56
14
|
type ProductCategory =
|
|
57
15
|
'Running' | 'Lifestyle' | 'Basketball' | 'Training' | 'Skateboarding';
|
|
58
16
|
|
|
59
|
-
|
|
60
|
-
id: string;
|
|
61
|
-
customer: string;
|
|
62
|
-
email: string;
|
|
63
|
-
product: string;
|
|
64
|
-
category: ProductCategory;
|
|
65
|
-
imageIndex: number;
|
|
66
|
-
amount: number;
|
|
67
|
-
status: 'completed' | 'processing' | 'shipped' | 'refunded';
|
|
68
|
-
date: string;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
const PRODUCTS = [
|
|
17
|
+
const PRODUCTS: Array<{name: string; category: string; accent: SceneHue; price: number}> = [
|
|
72
18
|
{
|
|
73
19
|
name: 'Air Max 90',
|
|
74
20
|
category: 'Lifestyle' as ProductCategory,
|
|
@@ -90,7 +36,7 @@ const PRODUCTS = [
|
|
|
90
36
|
{
|
|
91
37
|
name: 'Jordan 1 Retro',
|
|
92
38
|
category: 'Basketball' as ProductCategory,
|
|
93
|
-
accent: 'var(--dracula-
|
|
39
|
+
accent: 'var(--dracula-red)',
|
|
94
40
|
price: 180,
|
|
95
41
|
},
|
|
96
42
|
{
|
|
@@ -107,7 +53,7 @@ const PRODUCTS = [
|
|
|
107
53
|
},
|
|
108
54
|
];
|
|
109
55
|
|
|
110
|
-
const orders:
|
|
56
|
+
const orders: OrderDeskRow[] = [
|
|
111
57
|
// Sun (0) — spread across 9am–5pm, very hot
|
|
112
58
|
{
|
|
113
59
|
id: 'ORD-1001',
|
|
@@ -806,233 +752,18 @@ const revenueData = [
|
|
|
806
752
|
{date: 'Jan 15', revenue: 7100},
|
|
807
753
|
];
|
|
808
754
|
|
|
809
|
-
|
|
810
|
-
completed: 'green',
|
|
811
|
-
shipped: 'blue',
|
|
812
|
-
processing: 'orange',
|
|
813
|
-
refunded: 'red',
|
|
814
|
-
};
|
|
815
|
-
|
|
816
|
-
const columns: TableColumn<OrderRow>[] = [
|
|
817
|
-
{
|
|
818
|
-
key: 'id',
|
|
819
|
-
header: 'Order',
|
|
820
|
-
width: pixel(110),
|
|
821
|
-
renderCell: (item: OrderRow) => (
|
|
822
|
-
<Link href="#/templates/table-page-shoe-store-heatmap" isStandalone>
|
|
823
|
-
{item.id}
|
|
824
|
-
</Link>
|
|
825
|
-
),
|
|
826
|
-
},
|
|
827
|
-
{
|
|
828
|
-
key: 'product',
|
|
829
|
-
header: 'Product',
|
|
830
|
-
width: proportional(3),
|
|
831
|
-
renderCell: (item: OrderRow) => (
|
|
832
|
-
<HStack gap={3} vAlign="center">
|
|
833
|
-
<ProductSwatch
|
|
834
|
-
accent={PRODUCTS[item.imageIndex].accent}
|
|
835
|
-
label={item.product}
|
|
836
|
-
/>
|
|
837
|
-
<VStack gap={0}>
|
|
838
|
-
<Text type="body">{item.product}</Text>
|
|
839
|
-
<Text type="supporting" color="secondary">
|
|
840
|
-
{item.category}
|
|
841
|
-
</Text>
|
|
842
|
-
</VStack>
|
|
843
|
-
</HStack>
|
|
844
|
-
),
|
|
845
|
-
},
|
|
846
|
-
{
|
|
847
|
-
key: 'amount',
|
|
848
|
-
header: 'Amount',
|
|
849
|
-
width: pixel(90),
|
|
850
|
-
renderCell: (item: OrderRow) => (
|
|
851
|
-
<Text type="body" hasTabularNumbers>
|
|
852
|
-
${item.amount}
|
|
853
|
-
</Text>
|
|
854
|
-
),
|
|
855
|
-
},
|
|
856
|
-
{
|
|
857
|
-
key: 'customer',
|
|
858
|
-
header: 'Customer',
|
|
859
|
-
width: proportional(2),
|
|
860
|
-
renderCell: (item: OrderRow) => <Text type="body">{item.customer}</Text>,
|
|
861
|
-
},
|
|
862
|
-
{
|
|
863
|
-
key: 'email',
|
|
864
|
-
header: 'Email',
|
|
865
|
-
width: proportional(2),
|
|
866
|
-
renderCell: (item: OrderRow) => <Text type="body">{item.email}</Text>,
|
|
867
|
-
},
|
|
868
|
-
{
|
|
869
|
-
key: 'status',
|
|
870
|
-
header: 'Status',
|
|
871
|
-
width: pixel(120),
|
|
872
|
-
renderCell: (item: OrderRow) => (
|
|
873
|
-
<Badge
|
|
874
|
-
label={item.status.charAt(0).toUpperCase() + item.status.slice(1)}
|
|
875
|
-
variant={statusColor[item.status]}
|
|
876
|
-
/>
|
|
877
|
-
),
|
|
878
|
-
},
|
|
879
|
-
{
|
|
880
|
-
key: 'date',
|
|
881
|
-
header: 'Date',
|
|
882
|
-
width: pixel(110),
|
|
883
|
-
renderCell: (item: OrderRow) => (
|
|
884
|
-
<Text type="body" hasTabularNumbers>
|
|
885
|
-
{item.date}
|
|
886
|
-
</Text>
|
|
887
|
-
),
|
|
888
|
-
},
|
|
889
|
-
];
|
|
890
|
-
|
|
891
|
-
// ============= REVENUE CHART (hand SVG, Dracula ramp) =============
|
|
892
|
-
|
|
893
|
-
const revenueLine = 'var(--dracula-cyan)';
|
|
894
|
-
|
|
895
|
-
function RevenueChart() {
|
|
896
|
-
const W = 540;
|
|
897
|
-
const H = 200;
|
|
898
|
-
const padLeft = 44;
|
|
899
|
-
const padRight = 12;
|
|
900
|
-
const padTop = 12;
|
|
901
|
-
const baseline = 164;
|
|
902
|
-
const max = 10000;
|
|
903
|
-
const plotW = W - padLeft - padRight;
|
|
904
|
-
const plotH = baseline - padTop;
|
|
905
|
-
const points = revenueData.map((d, i) => ({
|
|
906
|
-
...d,
|
|
907
|
-
x: padLeft + (i / (revenueData.length - 1)) * plotW,
|
|
908
|
-
y: baseline - (d.revenue / max) * plotH,
|
|
909
|
-
}));
|
|
910
|
-
const linePath = points
|
|
911
|
-
.map((p, i) => `${i === 0 ? 'M' : 'L'}${p.x.toFixed(1)},${p.y.toFixed(1)}`)
|
|
912
|
-
.join(' ');
|
|
913
|
-
const areaPath = `${linePath} L${points[points.length - 1].x.toFixed(1)},${baseline} L${points[0].x.toFixed(1)},${baseline} Z`;
|
|
914
|
-
const gridTicks = [0, 2000, 4000, 6000, 8000, 10000];
|
|
915
|
-
return (
|
|
916
|
-
<VStack gap={3}>
|
|
917
|
-
<Card
|
|
918
|
-
padding={3}
|
|
919
|
-
style={{
|
|
920
|
-
backgroundColor: 'var(--color-background)',
|
|
921
|
-
border: 'var(--border-width) solid var(--color-separator)',
|
|
922
|
-
}}>
|
|
923
|
-
<svg
|
|
924
|
-
viewBox={`0 0 ${W} ${H}`}
|
|
925
|
-
width="100%"
|
|
926
|
-
role="img"
|
|
927
|
-
aria-label="Daily revenue, January 1 to 15">
|
|
928
|
-
{gridTicks.map(tick => {
|
|
929
|
-
const y = baseline - (tick / max) * plotH;
|
|
930
|
-
return (
|
|
931
|
-
<g key={tick}>
|
|
932
|
-
<line
|
|
933
|
-
x1={padLeft}
|
|
934
|
-
y1={y}
|
|
935
|
-
x2={W - padRight}
|
|
936
|
-
y2={y}
|
|
937
|
-
stroke="var(--color-separator)"
|
|
938
|
-
strokeDasharray={tick === 0 ? undefined : '3 3'}
|
|
939
|
-
opacity={tick === 0 ? 1 : 0.5}
|
|
940
|
-
/>
|
|
941
|
-
<text
|
|
942
|
-
x={padLeft - 6}
|
|
943
|
-
y={y + 3}
|
|
944
|
-
textAnchor="end"
|
|
945
|
-
fontSize={9}
|
|
946
|
-
fill="var(--color-text-paragraph)"
|
|
947
|
-
fontFamily="var(--font-family-mono)">
|
|
948
|
-
{tick === 0 ? '$0' : `$${tick / 1000}k`}
|
|
949
|
-
</text>
|
|
950
|
-
</g>
|
|
951
|
-
);
|
|
952
|
-
})}
|
|
953
|
-
<path d={areaPath} fill={revenueLine} opacity={0.25} />
|
|
954
|
-
<path
|
|
955
|
-
d={linePath}
|
|
956
|
-
fill="none"
|
|
957
|
-
stroke={revenueLine}
|
|
958
|
-
strokeWidth={2}
|
|
959
|
-
strokeLinejoin="round"
|
|
960
|
-
strokeLinecap="round"
|
|
961
|
-
/>
|
|
962
|
-
{points.map(
|
|
963
|
-
(p, i) =>
|
|
964
|
-
(i % 3 === 0 || i === points.length - 1) && (
|
|
965
|
-
<text
|
|
966
|
-
key={p.date}
|
|
967
|
-
x={i === points.length - 1 ? W - padRight - 2 : p.x}
|
|
968
|
-
y={H - 8}
|
|
969
|
-
textAnchor={i === points.length - 1 ? 'end' : 'middle'}
|
|
970
|
-
fontSize={9}
|
|
971
|
-
fill="var(--color-text-paragraph)"
|
|
972
|
-
fontFamily="var(--font-family-mono)">
|
|
973
|
-
{p.date}
|
|
974
|
-
</text>
|
|
975
|
-
),
|
|
976
|
-
)}
|
|
977
|
-
</svg>
|
|
978
|
-
</Card>
|
|
979
|
-
<Text type="supporting" color="secondary">
|
|
980
|
-
Daily revenue · Jan 1–15
|
|
981
|
-
</Text>
|
|
982
|
-
<HStack gap={2} vAlign="center">
|
|
983
|
-
<Icon icon={Square} size="xsm" style={{color: revenueLine}} />
|
|
984
|
-
<Text type="supporting" color="secondary">
|
|
985
|
-
Revenue
|
|
986
|
-
</Text>
|
|
987
|
-
</HStack>
|
|
988
|
-
</VStack>
|
|
989
|
-
);
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// ============= PAGE =============
|
|
755
|
+
// ============= PAGE (parametric OrderDesk; catalogue + rows + scale only) =============
|
|
993
756
|
|
|
994
757
|
export default function ShoeStoreTable() {
|
|
995
758
|
return (
|
|
996
|
-
<
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
<IconButton
|
|
1005
|
-
label="Filter"
|
|
1006
|
-
icon={<Icon icon={Filter} size="sm" />}
|
|
1007
|
-
variant="ghost"
|
|
1008
|
-
tooltip="Filter"
|
|
1009
|
-
/>
|
|
1010
|
-
<IconButton
|
|
1011
|
-
label="Export"
|
|
1012
|
-
icon={<Icon icon={Download} size="sm" />}
|
|
1013
|
-
variant="ghost"
|
|
1014
|
-
tooltip="Export"
|
|
1015
|
-
/>
|
|
1016
|
-
<Button label="New order" icon={<Icon icon={Plus} size="sm" />} />
|
|
1017
|
-
</HStack>
|
|
1018
|
-
</LayoutHeader>
|
|
1019
|
-
}
|
|
1020
|
-
content={
|
|
1021
|
-
<LayoutContent padding={3}>
|
|
1022
|
-
<VStack gap={4}>
|
|
1023
|
-
<RevenueChart />
|
|
1024
|
-
|
|
1025
|
-
<Table<OrderRow>
|
|
1026
|
-
data={orders}
|
|
1027
|
-
columns={columns}
|
|
1028
|
-
idKey="id"
|
|
1029
|
-
density="balanced"
|
|
1030
|
-
dividers="rows"
|
|
1031
|
-
hasHover
|
|
1032
|
-
/>
|
|
1033
|
-
</VStack>
|
|
1034
|
-
</LayoutContent>
|
|
1035
|
-
}
|
|
759
|
+
<OrderDesk
|
|
760
|
+
title="Midnight Kicks"
|
|
761
|
+
selfHash="#/templates/table-page-shoe-store-heatmap"
|
|
762
|
+
products={PRODUCTS}
|
|
763
|
+
orders={orders}
|
|
764
|
+
revenueData={revenueData}
|
|
765
|
+
chartMax={10000}
|
|
766
|
+
gridTicks={[0, 2000, 4000, 6000, 8000, 10000]}
|
|
1036
767
|
/>
|
|
1037
768
|
);
|
|
1038
769
|
}
|
package/templates/table-page.tsx
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
+
// L > (LH[divider] > H[g=2 a=center] > (SI[fill] > Hd"Familiars"[level=1]) + IB"Filter"[variant=ghost] + IB"Download"[variant=ghost] + B.primary"Add") + (LC[p=3] > V[g=4] > PS + (T[hover] > (TR > THC*3) + (TR > TC*3)*3))
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Familiars — the full roster of the crypt's companions, with a PowerSearch field filter bar above one dense table. (Frame/responsive/container: see XLE header above.)
|
|
7
|
+
*/
|
|
2
8
|
|
|
3
9
|
import {useState, useMemo} from 'react';
|
|
4
10
|
import {
|
|
@@ -41,7 +47,7 @@ const allFamiliars: FamiliarRow[] = [
|
|
|
41
47
|
name: 'Vlad',
|
|
42
48
|
kind: 'Vampire Bat',
|
|
43
49
|
biography:
|
|
44
|
-
'I love brooding in the belfry and hate garlic bread
|
|
50
|
+
'I love brooding in the belfry and hate garlic bread, and you might wonder how well those things mix. The answer is "not that well."',
|
|
45
51
|
age: 17,
|
|
46
52
|
},
|
|
47
53
|
{
|
|
@@ -121,7 +127,7 @@ const allFamiliars: FamiliarRow[] = [
|
|
|
121
127
|
name: 'Bruno',
|
|
122
128
|
kind: 'Gargoyle',
|
|
123
129
|
biography:
|
|
124
|
-
"Bruno is a gargoyle. He's a stony fellow
|
|
130
|
+
"Bruno is a gargoyle. He's a stony fellow. I never needed more than a day to teach him to scowl.",
|
|
125
131
|
age: 5,
|
|
126
132
|
},
|
|
127
133
|
{
|
|
@@ -398,22 +404,34 @@ export default function TablePage() {
|
|
|
398
404
|
<Layout
|
|
399
405
|
height="fill"
|
|
400
406
|
header={
|
|
401
|
-
<LayoutHeader hasDivider>
|
|
407
|
+
<LayoutHeader hasDivider padding={6}>
|
|
402
408
|
<HStack gap={2} vAlign="center">
|
|
403
409
|
<StackItem size="fill">
|
|
404
|
-
|
|
410
|
+
{/* maxLines={1} for symmetry with the other order-desk headers.
|
|
411
|
+
"Familiars" measures 189px at 35px and fits on one line today,
|
|
412
|
+
so this is a floor against a longer title rather than a fix
|
|
413
|
+
for a visible break — but the row has 129.2px of controls and
|
|
414
|
+
24px of gaps against a 342px content box, so it has ~0px of
|
|
415
|
+
slack. Tooltip keeps the full value reachable (SC 1.4.10). */}
|
|
416
|
+
<Heading level={1} type="display-2" maxLines={1}>Familiars</Heading>
|
|
405
417
|
</StackItem>
|
|
406
418
|
<IconButton
|
|
407
419
|
label="Filter"
|
|
408
420
|
icon={<Icon icon={Filter} size="sm" />}
|
|
409
421
|
variant="ghost"
|
|
422
|
+
tooltip="Filter"
|
|
410
423
|
/>
|
|
411
424
|
<IconButton
|
|
412
425
|
label="Download"
|
|
413
426
|
icon={<Icon icon={Download} size="sm" />}
|
|
414
427
|
variant="ghost"
|
|
428
|
+
tooltip="Export"
|
|
429
|
+
/>
|
|
430
|
+
<Button
|
|
431
|
+
label="Add"
|
|
432
|
+
variant="primary"
|
|
433
|
+
icon={<Icon icon={Plus} size="sm" />}
|
|
415
434
|
/>
|
|
416
|
-
<Button label="Add" icon={<Icon icon={Plus} size="sm" />} />
|
|
417
435
|
</HStack>
|
|
418
436
|
</LayoutHeader>
|
|
419
437
|
}
|
|
@@ -426,7 +444,7 @@ export default function TablePage() {
|
|
|
426
444
|
onChange={newFilters => {
|
|
427
445
|
setFilters([...newFilters]);
|
|
428
446
|
}}
|
|
429
|
-
placeholder="Search familiars
|
|
447
|
+
placeholder="Search familiars…"
|
|
430
448
|
resultCount={filtered.length}
|
|
431
449
|
/>
|
|
432
450
|
<Table<FamiliarRow>
|
package/templates/table.tsx
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
+
// L > (LH[divider] > H[a=center j=between] > Hd"Artifacts"[level=1] + B.primary"Add artifact") + (LC > T[hover] > (TR > THC"Name" + THC"Status" + THC"Updated" + THC"Actions") + (TR > TC"Blood Vial" + (TC > SD.success + Tx"Active") + TC"2025-01-15" + (TC > B.secondary"Edit"))*3)
|
|
2
4
|
|
|
3
|
-
import {useState} from 'react';
|
|
4
5
|
import {Layout, LayoutHeader, LayoutContent, HStack} from '@astryxdesign/core/Layout';
|
|
5
6
|
import {Text, Heading} from '@astryxdesign/core/Text';
|
|
6
7
|
import {Button} from '@astryxdesign/core/Button';
|
|
7
8
|
import {Table} from '@astryxdesign/core/Table';
|
|
8
|
-
import {
|
|
9
|
+
import {StatusDot} from '@astryxdesign/core/StatusDot';
|
|
9
10
|
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
10
11
|
|
|
11
12
|
type Relic = {
|
|
@@ -15,6 +16,16 @@ type Relic = {
|
|
|
15
16
|
updatedAt: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
const STATUS_LABEL: Record<Relic['status'], string> = {
|
|
20
|
+
active: 'Active',
|
|
21
|
+
inactive: 'Inactive',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const STATUS_VARIANT: Record<Relic['status'], 'success' | 'neutral'> = {
|
|
25
|
+
active: 'success',
|
|
26
|
+
inactive: 'neutral',
|
|
27
|
+
};
|
|
28
|
+
|
|
18
29
|
const SAMPLE_DATA: Relic[] = [
|
|
19
30
|
{id: '1', name: 'Blood Vial', status: 'active', updatedAt: '2025-01-15'},
|
|
20
31
|
{id: '2', name: 'Garlic Charm', status: 'inactive', updatedAt: '2025-01-14'},
|
|
@@ -35,10 +46,15 @@ const columns: TableColumn<Relic>[] = [
|
|
|
35
46
|
key: 'status',
|
|
36
47
|
header: 'Status',
|
|
37
48
|
renderCell: (item: Relic) => (
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
<HStack gap={1} vAlign="center">
|
|
50
|
+
<StatusDot
|
|
51
|
+
variant={STATUS_VARIANT[item.status]}
|
|
52
|
+
label={STATUS_LABEL[item.status]}
|
|
53
|
+
/>
|
|
54
|
+
<Text type="body" color="secondary">
|
|
55
|
+
{STATUS_LABEL[item.status]}
|
|
56
|
+
</Text>
|
|
57
|
+
</HStack>
|
|
42
58
|
),
|
|
43
59
|
},
|
|
44
60
|
{
|
|
@@ -62,10 +78,11 @@ const columns: TableColumn<Relic>[] = [
|
|
|
62
78
|
];
|
|
63
79
|
|
|
64
80
|
export default function SimpleTable() {
|
|
65
|
-
const
|
|
81
|
+
const data = SAMPLE_DATA;
|
|
66
82
|
|
|
67
83
|
return (
|
|
68
84
|
<Layout
|
|
85
|
+
height="auto"
|
|
69
86
|
header={
|
|
70
87
|
<LayoutHeader hasDivider>
|
|
71
88
|
<HStack vAlign="center" hAlign="between">
|