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,10 @@
|
|
|
1
1
|
// Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
2
|
// XLE (canonical structure, validated with `bunx astryx layout check`):
|
|
3
|
-
// TN"Nocturne" + (S[p6] > V[g10] > (Ctr > V[g4 a=center] >
|
|
3
|
+
// TN"Nocturne" + (S[p6] > V[g10] > (Ctr > V[g4 a=center] > Hd"Little haunts"[level=1 type=display-2] + Tx"We believe"[t=body]) + (G[c={min:200,max:3} g4] > (C[p0] > AR + V[g2 a=center] > Bd + Hd"Product"[level=2] + Tx"Description"[t=body] + (H[g2] > NI + B"Add to cart"))*3)) + (V[g8] > (G[c={min:200} g4] > (GS[c=1] > C > Hd"Checkout"[level=2]) + (GS[c=2] > C > Hd"Night Owl AI"[level=2])) + (G[c={min:200} g4] > (GS[c=3] > C > T) + (GS[c=1] > C > Hd"Revenue"[level=2])))
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Theme Showcase — the storefront page each theme is previewed against, and the only template that is deliberately shell-less: it paints its o (Frame/responsive/container: see XLE header above.)
|
|
7
|
+
*/
|
|
4
8
|
|
|
5
9
|
import {type CSSProperties, type ReactNode} from 'react';
|
|
6
10
|
import {
|
|
@@ -35,6 +39,10 @@ import {Divider} from '@astryxdesign/core/Divider';
|
|
|
35
39
|
import {CheckboxInput} from '@astryxdesign/core/CheckboxInput';
|
|
36
40
|
import {Item} from '@astryxdesign/core/Item';
|
|
37
41
|
import {Table, proportional, pixel} from '@astryxdesign/core/Table';
|
|
42
|
+
import type {TableColumn} from '@astryxdesign/core/Table';
|
|
43
|
+
import {StatusDot} from '@astryxdesign/core/StatusDot';
|
|
44
|
+
import {ProductSwatch} from 'astryx-dracula/shared/revenue-chart';
|
|
45
|
+
import {useMediaQuery} from '@astryxdesign/core/hooks';
|
|
38
46
|
import {TextInput} from '@astryxdesign/core/TextInput';
|
|
39
47
|
import {Selector} from '@astryxdesign/core/Selector';
|
|
40
48
|
import {RadioList, RadioListItem} from '@astryxdesign/core/RadioList';
|
|
@@ -53,6 +61,7 @@ import {
|
|
|
53
61
|
ChatMessageList,
|
|
54
62
|
ChatSystemMessage,
|
|
55
63
|
} from '@astryxdesign/core/Chat';
|
|
64
|
+
import type {SceneHue} from 'astryx-dracula/shared/scene-hues';
|
|
56
65
|
|
|
57
66
|
// Styles passed to Astryx components via their `style` prop. Astryx components
|
|
58
67
|
// forward the DOM `style` prop, so these work with no CSS compiler — in
|
|
@@ -100,6 +109,10 @@ const styles: Record<string, CSSProperties> = {
|
|
|
100
109
|
minWidth: 0,
|
|
101
110
|
maxWidth: 240,
|
|
102
111
|
},
|
|
112
|
+
filterRowFill: {
|
|
113
|
+
flex: 1,
|
|
114
|
+
minWidth: 0,
|
|
115
|
+
},
|
|
103
116
|
activityCard: {
|
|
104
117
|
backgroundColor: 'var(--color-background-surface)',
|
|
105
118
|
color: 'var(--color-text-primary)',
|
|
@@ -121,6 +134,9 @@ const styles: Record<string, CSSProperties> = {
|
|
|
121
134
|
activityCardStack: {
|
|
122
135
|
height: '100%',
|
|
123
136
|
},
|
|
137
|
+
inventoryItemText: {
|
|
138
|
+
minWidth: 0,
|
|
139
|
+
},
|
|
124
140
|
activityListFade: {
|
|
125
141
|
flex: 1,
|
|
126
142
|
minHeight: 0,
|
|
@@ -131,13 +147,11 @@ const styles: Record<string, CSSProperties> = {
|
|
|
131
147
|
'linear-gradient(to bottom, black calc(100% - 48px), transparent)',
|
|
132
148
|
marginInline: 'calc(var(--spacing-2) * -1)',
|
|
133
149
|
},
|
|
134
|
-
|
|
135
|
-
maxWidth: 960,
|
|
136
|
-
marginInline: 'auto',
|
|
137
|
-
minWidth: 0,
|
|
138
|
-
},
|
|
150
|
+
// The hero column: centered, narrower than the grid below it.
|
|
139
151
|
contentFluid: {
|
|
140
152
|
maxWidth: 880,
|
|
153
|
+
marginInline: 'auto',
|
|
154
|
+
minWidth: 0,
|
|
141
155
|
},
|
|
142
156
|
heroText: {
|
|
143
157
|
textAlign: 'center' as const,
|
|
@@ -151,6 +165,17 @@ const styles: Record<string, CSSProperties> = {
|
|
|
151
165
|
},
|
|
152
166
|
cardDescription: {
|
|
153
167
|
flex: 1,
|
|
168
|
+
textAlign: 'center' as const,
|
|
169
|
+
},
|
|
170
|
+
// Store surfaces: the shell is intentionally absent, so the page paints its
|
|
171
|
+
// own body and card-showcase bands.
|
|
172
|
+
storeRoot: {
|
|
173
|
+
minHeight: '100%',
|
|
174
|
+
backgroundColor: 'var(--color-background-body)',
|
|
175
|
+
},
|
|
176
|
+
showcaseBand: {
|
|
177
|
+
padding: 'var(--spacing-6)',
|
|
178
|
+
backgroundColor: 'var(--color-background-surface)',
|
|
154
179
|
},
|
|
155
180
|
quantityInput: {
|
|
156
181
|
// minWidth (not a hard width) so the field grows to fit the digit + the
|
|
@@ -175,7 +200,10 @@ const inlineStyles: Record<string, CSSProperties> = {
|
|
|
175
200
|
chatBody: {
|
|
176
201
|
flex: 1,
|
|
177
202
|
minHeight: 0,
|
|
178
|
-
|
|
203
|
+
// The message region is the scroll owner: the card is stretched to the
|
|
204
|
+
// grid row height, so longer conversations must scroll here rather than
|
|
205
|
+
// clip against the card.
|
|
206
|
+
overflowY: 'auto' as const,
|
|
179
207
|
},
|
|
180
208
|
chatSuggestions: {
|
|
181
209
|
paddingInline: 'var(--spacing-4)',
|
|
@@ -185,22 +213,21 @@ const inlineStyles: Record<string, CSSProperties> = {
|
|
|
185
213
|
paddingInline: 'var(--spacing-4)',
|
|
186
214
|
paddingBottom: 'var(--spacing-4)',
|
|
187
215
|
},
|
|
216
|
+
// Center supplies the 32px box and the centering; only the paint stays here.
|
|
217
|
+
// Surface, not muted: --color-text-secondary #9AA1BC is 3.57:1 on
|
|
218
|
+
// --color-background-muted #44475A, under the 4.5:1 text floor. The chip is
|
|
219
|
+
// aria-hidden decorative art so it clears the 3:1 non-text bar as-is, but the
|
|
220
|
+
// surface fill carries the same 4.60:1 pairing at no cost and cannot drop
|
|
221
|
+
// under the floor if anything smaller is ever put in the box.
|
|
188
222
|
activityIcon: {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
borderRadius: 'var(--radius-full)',
|
|
192
|
-
backgroundColor: 'var(--color-background-muted)',
|
|
223
|
+
borderRadius: 'var(--radius-element)', // square chip: no circles except dots/avatars
|
|
224
|
+
backgroundColor: 'var(--color-background-surface)',
|
|
193
225
|
color: 'var(--color-text-secondary)',
|
|
194
|
-
display: 'flex',
|
|
195
|
-
alignItems: 'center',
|
|
196
|
-
justifyContent: 'center',
|
|
197
226
|
flexShrink: 0,
|
|
198
227
|
},
|
|
199
228
|
cardBody: {
|
|
200
229
|
padding: 'var(--spacing-4)',
|
|
201
230
|
flex: 1,
|
|
202
|
-
display: 'flex',
|
|
203
|
-
flexDirection: 'column',
|
|
204
231
|
},
|
|
205
232
|
};
|
|
206
233
|
|
|
@@ -211,20 +238,23 @@ const artImage: CSSProperties = {
|
|
|
211
238
|
height: '100%',
|
|
212
239
|
display: 'block',
|
|
213
240
|
};
|
|
214
|
-
// Rounded inventory swatch. No radius prop where the swatch renders, so the
|
|
215
|
-
// SVG carries its own rounded corners (rx=4).
|
|
216
|
-
const thumbSwatch: CSSProperties = {
|
|
217
|
-
flexShrink: 0,
|
|
218
|
-
display: 'block',
|
|
219
|
-
};
|
|
220
241
|
|
|
221
|
-
// One Dracula accent per hero product slot.
|
|
242
|
+
// One Dracula accent per hero product slot. Purple stays off decorative art:
|
|
243
|
+
// it reads as interactive, and nothing here is tappable.
|
|
222
244
|
const PRODUCT_HUES = [
|
|
223
|
-
'var(--dracula-
|
|
245
|
+
'var(--dracula-green)',
|
|
224
246
|
'var(--dracula-cyan)',
|
|
225
247
|
'var(--dracula-yellow)',
|
|
226
248
|
];
|
|
227
249
|
|
|
250
|
+
// Grid recipes the store renders at every width above the phone layout. Kept
|
|
251
|
+
// at module scope: an object literal rebuilt per render is a new value prop
|
|
252
|
+
// every time.
|
|
253
|
+
const SHOWCASE_COLUMNS = {minWidth: 200, repeat: 'fit'} as const;
|
|
254
|
+
const PRODUCT_COLUMNS = {minWidth: 200, max: 3} as const;
|
|
255
|
+
const PAYMENT_COLUMNS = {minWidth: 70, max: 3} as const;
|
|
256
|
+
const EXPIRY_COLUMNS = {minWidth: 90, max: 2} as const;
|
|
257
|
+
|
|
228
258
|
/** Categorical badge variants usable for showcase product/inventory tags. */
|
|
229
259
|
export type ShowcaseBadgeVariant =
|
|
230
260
|
| 'blue'
|
|
@@ -249,7 +279,7 @@ const DEFAULT_PRODUCTS: ProductSpec[] = [
|
|
|
249
279
|
name: 'Moonphase Watch',
|
|
250
280
|
description: 'Clean lines and lume that carry through the longest night.',
|
|
251
281
|
badge: 'New',
|
|
252
|
-
badgeVariant: '
|
|
282
|
+
badgeVariant: 'cyan',
|
|
253
283
|
},
|
|
254
284
|
{
|
|
255
285
|
name: 'Night-Owl Headphones',
|
|
@@ -307,31 +337,6 @@ function ProductArt({hue, label}: {hue: string; label: string}) {
|
|
|
307
337
|
);
|
|
308
338
|
}
|
|
309
339
|
|
|
310
|
-
// Rounded inventory swatch: Dracula surface with a per-row accent ring.
|
|
311
|
-
function ThumbSwatch({hue, label}: {hue: string; label: string}) {
|
|
312
|
-
return (
|
|
313
|
-
<svg
|
|
314
|
-
viewBox="0 0 40 40"
|
|
315
|
-
width={40}
|
|
316
|
-
height={40}
|
|
317
|
-
style={thumbSwatch}
|
|
318
|
-
role="img"
|
|
319
|
-
aria-label={`${label} swatch`}>
|
|
320
|
-
<rect width={40} height={40} rx={5} fill="var(--dracula-bg-light)" />
|
|
321
|
-
<rect
|
|
322
|
-
x={1}
|
|
323
|
-
y={1}
|
|
324
|
-
width={38}
|
|
325
|
-
height={38}
|
|
326
|
-
rx={4}
|
|
327
|
-
fill="none"
|
|
328
|
-
stroke="var(--color-widget-content-border)"
|
|
329
|
-
/>
|
|
330
|
-
<circle cx={20} cy={20} r={8} fill="none" stroke={hue} strokeWidth={3} />
|
|
331
|
-
</svg>
|
|
332
|
-
);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
340
|
export interface ThemeShowcaseProps {
|
|
336
341
|
/** The three hero product cards. Defaults to the nocturne store products. */
|
|
337
342
|
products?: ProductSpec[];
|
|
@@ -353,20 +358,12 @@ export function ThemeShowcaseStore({
|
|
|
353
358
|
}: ThemeShowcaseProps = {}) {
|
|
354
359
|
const {isMobile} = useAppShellMobile();
|
|
355
360
|
return (
|
|
356
|
-
<
|
|
357
|
-
style={{
|
|
358
|
-
minHeight: '100%',
|
|
359
|
-
backgroundColor: 'var(--color-background-body)',
|
|
360
|
-
}}>
|
|
361
|
+
<VStack gap={0} style={styles.storeRoot}>
|
|
361
362
|
<StorePreview products={products} isMobile={isMobile} />
|
|
362
|
-
<
|
|
363
|
-
style={{
|
|
364
|
-
padding: 'var(--spacing-6)',
|
|
365
|
-
backgroundColor: 'var(--color-background-surface)',
|
|
366
|
-
}}>
|
|
363
|
+
<VStack gap={0} style={styles.showcaseBand}>
|
|
367
364
|
<CardShowcase inventory={inventory} isMobile={isMobile} />
|
|
368
|
-
</
|
|
369
|
-
</
|
|
365
|
+
</VStack>
|
|
366
|
+
</VStack>
|
|
370
367
|
);
|
|
371
368
|
}
|
|
372
369
|
|
|
@@ -377,7 +374,7 @@ function CardShowcase({
|
|
|
377
374
|
inventory: InventoryRow[];
|
|
378
375
|
isMobile: boolean;
|
|
379
376
|
}) {
|
|
380
|
-
const columns = isMobile ? 1 :
|
|
377
|
+
const columns = isMobile ? 1 : SHOWCASE_COLUMNS;
|
|
381
378
|
|
|
382
379
|
return (
|
|
383
380
|
<VStack gap={8}>
|
|
@@ -408,14 +405,18 @@ function StorePreview({
|
|
|
408
405
|
products: ProductSpec[];
|
|
409
406
|
isMobile: boolean;
|
|
410
407
|
}) {
|
|
408
|
+
// Shell-less preview: no AppShell above means the host context always
|
|
409
|
+
// reports desktop, so the nav reads the real viewport instead.
|
|
410
|
+
const isNarrowViewport = useMediaQuery('(max-width: 640px)');
|
|
411
|
+
const collapseNav = isMobile || isNarrowViewport;
|
|
411
412
|
return (
|
|
412
|
-
<
|
|
413
|
+
<VStack gap={0} data-theme-preview="true">
|
|
413
414
|
<VStack gap={0}>
|
|
414
415
|
<TopNav
|
|
415
416
|
label="Theme preview navigation"
|
|
416
417
|
heading={<TopNavHeading heading="Nocturne" />}
|
|
417
418
|
centerContent={
|
|
418
|
-
|
|
419
|
+
collapseNav ? undefined : (
|
|
419
420
|
<>
|
|
420
421
|
<TopNavItem label="Shop" href="#/templates/theme-showcase" isSelected />
|
|
421
422
|
<TopNavItem label="New In" href="#/templates/theme-showcase" />
|
|
@@ -425,47 +426,58 @@ function StorePreview({
|
|
|
425
426
|
)
|
|
426
427
|
}
|
|
427
428
|
endContent={
|
|
428
|
-
|
|
429
|
-
<
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
429
|
+
collapseNav ? (
|
|
430
|
+
<Button
|
|
431
|
+
label="Cart"
|
|
432
|
+
tooltip="Cart"
|
|
433
|
+
variant="ghost"
|
|
434
|
+
isIconOnly
|
|
435
|
+
icon={<ShoppingBag size={20} />}
|
|
436
|
+
href="#/templates/theme-showcase"
|
|
437
|
+
/>
|
|
438
|
+
) : (
|
|
439
|
+
<HStack gap={2} vAlign="center">
|
|
440
|
+
<HStack gap={0.5}>
|
|
441
|
+
<Button
|
|
442
|
+
label="Search"
|
|
443
|
+
tooltip="Search"
|
|
444
|
+
variant="ghost"
|
|
445
|
+
isIconOnly
|
|
446
|
+
icon={<Search size={20} />}
|
|
447
|
+
href="#/templates/theme-showcase"
|
|
448
|
+
/>
|
|
449
|
+
<Button
|
|
450
|
+
label="Account"
|
|
451
|
+
tooltip="Account"
|
|
452
|
+
variant="ghost"
|
|
453
|
+
isIconOnly
|
|
454
|
+
icon={<User size={20} />}
|
|
455
|
+
href="#/templates/theme-showcase"
|
|
456
|
+
/>
|
|
457
|
+
<Button
|
|
458
|
+
label="Cart"
|
|
459
|
+
tooltip="Cart"
|
|
460
|
+
variant="ghost"
|
|
461
|
+
isIconOnly
|
|
462
|
+
icon={<ShoppingBag size={20} />}
|
|
463
|
+
href="#/templates/theme-showcase"
|
|
464
|
+
/>
|
|
465
|
+
</HStack>
|
|
466
|
+
<Button label="Sign in" variant="primary" href="#/templates/theme-showcase" />
|
|
454
467
|
</HStack>
|
|
455
|
-
|
|
456
|
-
</HStack>
|
|
468
|
+
)
|
|
457
469
|
}
|
|
458
470
|
/>
|
|
459
471
|
|
|
460
472
|
<Section padding={6} variant="transparent">
|
|
461
|
-
<VStack gap={10} style={
|
|
473
|
+
<VStack gap={10} style={styles.contentFluid}>
|
|
462
474
|
<Center>
|
|
463
475
|
<VStack gap={4} hAlign="center" style={styles.heroText}>
|
|
464
|
-
<
|
|
476
|
+
<Heading level={1} type="display-2">
|
|
465
477
|
Little haunts,
|
|
466
478
|
<br />
|
|
467
479
|
everywhere you roam
|
|
468
|
-
</
|
|
480
|
+
</Heading>
|
|
469
481
|
<Text type="body" color="secondary">
|
|
470
482
|
We believe the smallest shadows are the ones that matter most.
|
|
471
483
|
Turn an ordinary evening into something worth remembering.
|
|
@@ -473,7 +485,7 @@ function StorePreview({
|
|
|
473
485
|
</VStack>
|
|
474
486
|
</Center>
|
|
475
487
|
|
|
476
|
-
<Grid columns={isMobile ? 1 :
|
|
488
|
+
<Grid columns={isMobile ? 1 : PRODUCT_COLUMNS} gap={4}>
|
|
477
489
|
{products.map((p, i) => (
|
|
478
490
|
<Card key={p.name} padding={0} height="100%">
|
|
479
491
|
<VStack gap={0} style={styles.cardStack}>
|
|
@@ -483,44 +495,42 @@ function StorePreview({
|
|
|
483
495
|
label={p.name}
|
|
484
496
|
/>
|
|
485
497
|
</AspectRatio>
|
|
486
|
-
<
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
<
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
<
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
</VStack>
|
|
523
|
-
</div>
|
|
498
|
+
<VStack
|
|
499
|
+
gap={2}
|
|
500
|
+
hAlign="center"
|
|
501
|
+
style={inlineStyles.cardBody}>
|
|
502
|
+
<HStack>
|
|
503
|
+
<Badge label={p.badge} variant={p.badgeVariant} />
|
|
504
|
+
</HStack>
|
|
505
|
+
<Heading level={2} style={styles.centerText}>
|
|
506
|
+
{p.name}
|
|
507
|
+
</Heading>
|
|
508
|
+
<Text
|
|
509
|
+
type="body"
|
|
510
|
+
color="secondary"
|
|
511
|
+
style={styles.cardDescription}>
|
|
512
|
+
{p.description}
|
|
513
|
+
</Text>
|
|
514
|
+
<HStack gap={2} vAlign="center" hAlign="center">
|
|
515
|
+
<NumberInput
|
|
516
|
+
label="Quantity"
|
|
517
|
+
isLabelHidden
|
|
518
|
+
value={1}
|
|
519
|
+
onChange={() => {}}
|
|
520
|
+
min={1}
|
|
521
|
+
max={99}
|
|
522
|
+
size="sm"
|
|
523
|
+
style={styles.quantityInput}
|
|
524
|
+
/>
|
|
525
|
+
<Button
|
|
526
|
+
label="Add to cart"
|
|
527
|
+
variant="secondary"
|
|
528
|
+
size="sm"
|
|
529
|
+
href="#/templates/theme-showcase"
|
|
530
|
+
style={styles.cartButton}
|
|
531
|
+
/>
|
|
532
|
+
</HStack>
|
|
533
|
+
</VStack>
|
|
524
534
|
</VStack>
|
|
525
535
|
</Card>
|
|
526
536
|
))}
|
|
@@ -528,13 +538,13 @@ function StorePreview({
|
|
|
528
538
|
</VStack>
|
|
529
539
|
</Section>
|
|
530
540
|
</VStack>
|
|
531
|
-
</
|
|
541
|
+
</VStack>
|
|
532
542
|
);
|
|
533
543
|
}
|
|
534
544
|
|
|
535
545
|
function CheckoutCard({isMobile}: {isMobile: boolean}) {
|
|
536
546
|
return (
|
|
537
|
-
<Card padding={
|
|
547
|
+
<Card padding={4} style={styles.card}>
|
|
538
548
|
<VStack gap={4} style={styles.checkoutStack}>
|
|
539
549
|
<Heading level={2}>Checkout</Heading>
|
|
540
550
|
|
|
@@ -588,7 +598,7 @@ function CheckoutCard({isMobile}: {isMobile: boolean}) {
|
|
|
588
598
|
<Text type="label" weight="semibold">
|
|
589
599
|
Payment method
|
|
590
600
|
</Text>
|
|
591
|
-
<Grid columns={isMobile ? 1 :
|
|
601
|
+
<Grid columns={isMobile ? 1 : PAYMENT_COLUMNS} gap={2}>
|
|
592
602
|
<SelectableCard
|
|
593
603
|
label="Pay with card"
|
|
594
604
|
isSelected={true}
|
|
@@ -646,7 +656,7 @@ function CheckoutCard({isMobile}: {isMobile: boolean}) {
|
|
|
646
656
|
size="lg"
|
|
647
657
|
/>
|
|
648
658
|
|
|
649
|
-
<Grid columns={isMobile ? 1 :
|
|
659
|
+
<Grid columns={isMobile ? 1 : EXPIRY_COLUMNS} gap={2}>
|
|
650
660
|
<TextInput
|
|
651
661
|
label="Expiry"
|
|
652
662
|
placeholder="MM / YY"
|
|
@@ -735,26 +745,32 @@ function ChatCard() {
|
|
|
735
745
|
|
|
736
746
|
<Divider variant="subtle" />
|
|
737
747
|
|
|
738
|
-
<
|
|
748
|
+
<VStack
|
|
749
|
+
gap={0}
|
|
750
|
+
style={inlineStyles.chatBody}
|
|
751
|
+
isScrollable={false}
|
|
752
|
+
tabIndex={0}
|
|
753
|
+
role="region"
|
|
754
|
+
aria-label="Night Owl chat messages">
|
|
739
755
|
<ChatMessageList>
|
|
740
756
|
<ChatSystemMessage>Today</ChatSystemMessage>
|
|
741
757
|
|
|
742
758
|
<ChatMessage sender="user">
|
|
743
759
|
<ChatMessageBubble variant="filled">
|
|
744
|
-
Where
|
|
760
|
+
Where's my order?
|
|
745
761
|
</ChatMessageBubble>
|
|
746
762
|
</ChatMessage>
|
|
747
763
|
|
|
748
764
|
<ChatMessage sender="assistant">
|
|
749
765
|
<VStack gap={3}>
|
|
750
766
|
<Text type="body">
|
|
751
|
-
Your order #1043
|
|
752
|
-
shipped this morning from the
|
|
753
|
-
in transit with
|
|
767
|
+
Your order #1043 (the Moonphase Watch and Belfry Throw)
|
|
768
|
+
shipped this morning from the courier-raven roost and is currently
|
|
769
|
+
in transit with courier-raven. It's on track to arrive at your address by
|
|
754
770
|
end of day tomorrow.
|
|
755
771
|
</Text>
|
|
756
772
|
<Text type="body">
|
|
757
|
-
Let me know if you
|
|
773
|
+
Let me know if you'd like to reschedule the delivery, redirect
|
|
758
774
|
it to a pickup point, or start a return once it arrives.
|
|
759
775
|
</Text>
|
|
760
776
|
</VStack>
|
|
@@ -768,7 +784,7 @@ function ChatCard() {
|
|
|
768
784
|
|
|
769
785
|
<ChatMessage sender="assistant">
|
|
770
786
|
<VStack gap={3}>
|
|
771
|
-
<Text type="body">Here
|
|
787
|
+
<Text type="body">Here's everything I have on order #1043:</Text>
|
|
772
788
|
<Card padding={3}>
|
|
773
789
|
<VStack gap={1}>
|
|
774
790
|
<Item
|
|
@@ -782,7 +798,7 @@ function ChatCard() {
|
|
|
782
798
|
/>
|
|
783
799
|
<Item
|
|
784
800
|
label="Shipping"
|
|
785
|
-
description="
|
|
801
|
+
description="Courier-raven ground"
|
|
786
802
|
endContent={
|
|
787
803
|
<Text type="body" weight="semibold" hasTabularNumbers>
|
|
788
804
|
$12
|
|
@@ -792,21 +808,28 @@ function ChatCard() {
|
|
|
792
808
|
<Item
|
|
793
809
|
label="Estimated arrival"
|
|
794
810
|
description="Tomorrow by 8pm"
|
|
795
|
-
endContent={
|
|
811
|
+
endContent={
|
|
812
|
+
<HStack gap={1} vAlign="center">
|
|
813
|
+
<StatusDot variant="success" label="On time" />
|
|
814
|
+
<Text type="supporting" color="secondary">
|
|
815
|
+
On time
|
|
816
|
+
</Text>
|
|
817
|
+
</HStack>
|
|
818
|
+
}
|
|
796
819
|
/>
|
|
797
820
|
<Item
|
|
798
821
|
label="Tracking"
|
|
799
|
-
description="
|
|
800
|
-
endContent={<Link href="#/templates/theme-showcase">Track
|
|
822
|
+
description="RVN 1Z 999 AA1 0123 4567 84"
|
|
823
|
+
endContent={<Link href="#/templates/theme-showcase">Track</Link>}
|
|
801
824
|
/>
|
|
802
825
|
</VStack>
|
|
803
826
|
</Card>
|
|
804
827
|
</VStack>
|
|
805
828
|
</ChatMessage>
|
|
806
829
|
</ChatMessageList>
|
|
807
|
-
</
|
|
830
|
+
</VStack>
|
|
808
831
|
|
|
809
|
-
<
|
|
832
|
+
<VStack gap={0} style={inlineStyles.chatSuggestions}>
|
|
810
833
|
<HStack gap={1} hAlign="center" wrap="wrap">
|
|
811
834
|
{SUGGESTED_QUESTIONS.map(question => (
|
|
812
835
|
<Button
|
|
@@ -817,14 +840,14 @@ function ChatCard() {
|
|
|
817
840
|
/>
|
|
818
841
|
))}
|
|
819
842
|
</HStack>
|
|
820
|
-
</
|
|
843
|
+
</VStack>
|
|
821
844
|
|
|
822
|
-
<
|
|
845
|
+
<VStack gap={0} style={inlineStyles.chatComposer}>
|
|
823
846
|
<ChatComposer
|
|
824
847
|
value=""
|
|
825
848
|
onChange={() => {}}
|
|
826
849
|
onSubmit={() => {}}
|
|
827
|
-
placeholder="Ask Night Owl
|
|
850
|
+
placeholder="Ask Night Owl..."
|
|
828
851
|
footerActions={
|
|
829
852
|
<Button
|
|
830
853
|
variant="ghost"
|
|
@@ -846,7 +869,7 @@ function ChatCard() {
|
|
|
846
869
|
/>
|
|
847
870
|
}
|
|
848
871
|
/>
|
|
849
|
-
</
|
|
872
|
+
</VStack>
|
|
850
873
|
</Card>
|
|
851
874
|
);
|
|
852
875
|
}
|
|
@@ -910,7 +933,7 @@ function formatAmount(amount: number): string {
|
|
|
910
933
|
|
|
911
934
|
function LatestActivityCard({isMobile}: {isMobile: boolean}) {
|
|
912
935
|
return (
|
|
913
|
-
<Card padding={
|
|
936
|
+
<Card padding={4} style={styles.activityCard}>
|
|
914
937
|
<VStack gap={4} style={styles.activityCardStack}>
|
|
915
938
|
<Heading level={2}>Revenue</Heading>
|
|
916
939
|
|
|
@@ -941,13 +964,31 @@ function LatestActivityCard({isMobile}: {isMobile: boolean}) {
|
|
|
941
964
|
</HStack>
|
|
942
965
|
|
|
943
966
|
<VStack gap={1} style={styles.activityListFade}>
|
|
967
|
+
{/* The sign travels in the string (formatAmount prefixes U+2212), so
|
|
968
|
+
direction is never colour-alone and 1.4.1 holds without a tint.
|
|
969
|
+
A negative text role is not available here, and the reason is
|
|
970
|
+
worth recording rather than working around:
|
|
971
|
+
- TextColorMap is six keys (primary, secondary, disabled,
|
|
972
|
+
placeholder, accent, inherit). No negative.
|
|
973
|
+
- `accent` is Purple in this theme, and principle 2 makes
|
|
974
|
+
purple tappable-only, so a refund would read as a link.
|
|
975
|
+
- --color-negative #FF5555 measures 3.75:1 on this card
|
|
976
|
+
#343746, under the 4.5:1 floor, so a style-prop tint would
|
|
977
|
+
trade a weak signal for a real contrast failure.
|
|
978
|
+
secondary therefore stays and the minus sign is the signal. A
|
|
979
|
+
kit-wide negative text role needs a new token AND a colour that
|
|
980
|
+
clears 4.5:1 on a card — a human call, not a call-site one. */}
|
|
944
981
|
{ACTIVITY.map(item => (
|
|
945
982
|
<Item
|
|
946
983
|
key={item.id}
|
|
947
984
|
startContent={
|
|
948
|
-
<
|
|
985
|
+
<Center
|
|
986
|
+
width={32}
|
|
987
|
+
height={32}
|
|
988
|
+
style={inlineStyles.activityIcon}
|
|
989
|
+
aria-hidden="true">
|
|
949
990
|
{item.icon}
|
|
950
|
-
</
|
|
991
|
+
</Center>
|
|
951
992
|
}
|
|
952
993
|
label={item.label}
|
|
953
994
|
description={item.detail}
|
|
@@ -978,7 +1019,7 @@ export interface InventoryRow extends Record<string, unknown> {
|
|
|
978
1019
|
available: number;
|
|
979
1020
|
location: string;
|
|
980
1021
|
tags: TagSpec[];
|
|
981
|
-
hue:
|
|
1022
|
+
hue: SceneHue;
|
|
982
1023
|
selected: boolean;
|
|
983
1024
|
}
|
|
984
1025
|
|
|
@@ -988,9 +1029,9 @@ const DEFAULT_INVENTORY: InventoryRow[] = [
|
|
|
988
1029
|
name: 'Moonphase Watch',
|
|
989
1030
|
meta: 'Steel case, moonphase dial',
|
|
990
1031
|
available: 42,
|
|
991
|
-
location: '
|
|
992
|
-
tags: [{label: 'New', variant: '
|
|
993
|
-
hue: 'var(--dracula-
|
|
1032
|
+
location: 'Roost 3',
|
|
1033
|
+
tags: [{label: 'New', variant: 'cyan'}],
|
|
1034
|
+
hue: 'var(--dracula-comment)',
|
|
994
1035
|
selected: false,
|
|
995
1036
|
},
|
|
996
1037
|
{
|
|
@@ -998,7 +1039,7 @@ const DEFAULT_INVENTORY: InventoryRow[] = [
|
|
|
998
1039
|
name: 'Night-Owl Headphones',
|
|
999
1040
|
meta: 'ANC, 30hr battery',
|
|
1000
1041
|
available: 128,
|
|
1001
|
-
location: '
|
|
1042
|
+
location: 'Roost 1',
|
|
1002
1043
|
tags: [{label: 'Popular', variant: 'green'}],
|
|
1003
1044
|
hue: 'var(--dracula-cyan)',
|
|
1004
1045
|
selected: true,
|
|
@@ -1008,7 +1049,7 @@ const DEFAULT_INVENTORY: InventoryRow[] = [
|
|
|
1008
1049
|
name: 'Coven Canvas Backpack',
|
|
1009
1050
|
meta: 'Waxed canvas, 25L',
|
|
1010
1051
|
available: 63,
|
|
1011
|
-
location: '
|
|
1052
|
+
location: 'Roost 2',
|
|
1012
1053
|
tags: [{label: 'Limited', variant: 'yellow'}],
|
|
1013
1054
|
hue: 'var(--dracula-yellow)',
|
|
1014
1055
|
selected: false,
|
|
@@ -1018,7 +1059,7 @@ const DEFAULT_INVENTORY: InventoryRow[] = [
|
|
|
1018
1059
|
name: 'Night Market Wallet',
|
|
1019
1060
|
meta: 'Full-grain, RFID blocking',
|
|
1020
1061
|
available: 15,
|
|
1021
|
-
location: '
|
|
1062
|
+
location: 'Roost 4',
|
|
1022
1063
|
tags: [{label: 'Leather', variant: 'yellow'}],
|
|
1023
1064
|
hue: 'var(--dracula-orange)',
|
|
1024
1065
|
selected: true,
|
|
@@ -1028,8 +1069,8 @@ const DEFAULT_INVENTORY: InventoryRow[] = [
|
|
|
1028
1069
|
name: 'Midnight Tumbler',
|
|
1029
1070
|
meta: 'Vacuum insulated, 16oz',
|
|
1030
1071
|
available: 87,
|
|
1031
|
-
location: '
|
|
1032
|
-
tags: [{label: 'Drinkware', variant: '
|
|
1072
|
+
location: 'Roost 5',
|
|
1073
|
+
tags: [{label: 'Drinkware', variant: 'yellow'}],
|
|
1033
1074
|
hue: 'var(--dracula-pink)',
|
|
1034
1075
|
selected: false,
|
|
1035
1076
|
},
|
|
@@ -1038,8 +1079,8 @@ const DEFAULT_INVENTORY: InventoryRow[] = [
|
|
|
1038
1079
|
name: 'Belfry Throw',
|
|
1039
1080
|
meta: 'Heavyweight, oat',
|
|
1040
1081
|
available: 24,
|
|
1041
|
-
location: '
|
|
1042
|
-
tags: [{label: 'Home', variant: '
|
|
1082
|
+
location: 'Roost 6',
|
|
1083
|
+
tags: [{label: 'Home', variant: 'yellow'}],
|
|
1043
1084
|
hue: 'var(--dracula-green)',
|
|
1044
1085
|
selected: true,
|
|
1045
1086
|
},
|
|
@@ -1061,8 +1102,8 @@ function SelectCell({row}: {row: InventoryRow}) {
|
|
|
1061
1102
|
function ItemCell({row}: {row: InventoryRow}) {
|
|
1062
1103
|
return (
|
|
1063
1104
|
<HStack gap={3} vAlign="center">
|
|
1064
|
-
<
|
|
1065
|
-
<VStack gap={0} style={
|
|
1105
|
+
<ProductSwatch accent={row.hue} label={row.name} />
|
|
1106
|
+
<VStack gap={0} style={styles.inventoryItemText}>
|
|
1066
1107
|
<Text type="body" weight="semibold">
|
|
1067
1108
|
{row.name}
|
|
1068
1109
|
</Text>
|
|
@@ -1100,7 +1141,79 @@ function ActionsCell() {
|
|
|
1100
1141
|
);
|
|
1101
1142
|
}
|
|
1102
1143
|
|
|
1144
|
+
// The full column set needs 64+80+100+100+80+64 = 488px of grid and the card
|
|
1145
|
+
// insets cost 2 × --spacing-6 on top, so it stops fitting below ~600px. A 375px
|
|
1146
|
+
// phone leaves ~280px inside the card: the phone layout drops the selection
|
|
1147
|
+
// control (no bulk action bar acts on it) and the two metadata columns, which
|
|
1148
|
+
// needs 244px. Table keeps its own scroll wrapper either way; the point is that
|
|
1149
|
+
// a row should not have to be read sideways.
|
|
1150
|
+
const NARROW_TABLE_QUERY = '(max-width: 600px)';
|
|
1151
|
+
const NARROW_COLUMN_KEYS: Record<string, true> = {
|
|
1152
|
+
select: true,
|
|
1153
|
+
location: true,
|
|
1154
|
+
tags: true,
|
|
1155
|
+
};
|
|
1156
|
+
|
|
1157
|
+
const INVENTORY_COLUMNS: TableColumn<InventoryRow>[] = [
|
|
1158
|
+
{
|
|
1159
|
+
key: 'select',
|
|
1160
|
+
header: '',
|
|
1161
|
+
// Wide enough that the control + the theme's cell padding (up to
|
|
1162
|
+
// --spacing-4 = 16px/side on spacious density) fit inside the cell,
|
|
1163
|
+
// so the control's hover background doesn't overflow toward the
|
|
1164
|
+
// card's clipped (rounded) edge on larger-padding themes.
|
|
1165
|
+
width: pixel(64),
|
|
1166
|
+
renderCell: row => <SelectCell row={row} />,
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
key: 'item',
|
|
1170
|
+
header: 'Item',
|
|
1171
|
+
// Lower min-width (default 120) so the table fits its container on
|
|
1172
|
+
// larger-spacing themes instead of overflowing the actions column.
|
|
1173
|
+
width: proportional(3, {minWidth: 80}),
|
|
1174
|
+
renderCell: row => <ItemCell row={row} />,
|
|
1175
|
+
},
|
|
1176
|
+
{
|
|
1177
|
+
key: 'available',
|
|
1178
|
+
header: 'Available',
|
|
1179
|
+
width: pixel(100),
|
|
1180
|
+
renderCell: row => (
|
|
1181
|
+
<Text type="body" hasTabularNumbers>
|
|
1182
|
+
{row.available}
|
|
1183
|
+
</Text>
|
|
1184
|
+
),
|
|
1185
|
+
},
|
|
1186
|
+
{
|
|
1187
|
+
key: 'location',
|
|
1188
|
+
header: 'Location',
|
|
1189
|
+
width: pixel(100),
|
|
1190
|
+
renderCell: row => <Text type="body">{row.location}</Text>,
|
|
1191
|
+
},
|
|
1192
|
+
{
|
|
1193
|
+
key: 'tags',
|
|
1194
|
+
header: 'Tags',
|
|
1195
|
+
width: proportional(2, {minWidth: 80}),
|
|
1196
|
+
align: 'end',
|
|
1197
|
+
renderCell: row => <TagsCell row={row} />,
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
key: 'actions',
|
|
1201
|
+
header: '',
|
|
1202
|
+
// Match the select column: fit the sm more-menu button + cell
|
|
1203
|
+
// padding so its hover background stays clear of the card's
|
|
1204
|
+
// clipped rounded edge across themes.
|
|
1205
|
+
width: pixel(64),
|
|
1206
|
+
align: 'end',
|
|
1207
|
+
renderCell: () => <ActionsCell />,
|
|
1208
|
+
},
|
|
1209
|
+
];
|
|
1210
|
+
|
|
1211
|
+
const NARROW_INVENTORY_COLUMNS = INVENTORY_COLUMNS.filter(
|
|
1212
|
+
column => !NARROW_COLUMN_KEYS[column.key],
|
|
1213
|
+
);
|
|
1214
|
+
|
|
1103
1215
|
function InventoryCard({inventory}: {inventory: InventoryRow[]}) {
|
|
1216
|
+
const isNarrow = useMediaQuery(NARROW_TABLE_QUERY);
|
|
1104
1217
|
const lowStockCount = inventory.filter(
|
|
1105
1218
|
row => row.available < LOW_STOCK_THRESHOLD,
|
|
1106
1219
|
).length;
|
|
@@ -1111,7 +1224,6 @@ function InventoryCard({inventory}: {inventory: InventoryRow[]}) {
|
|
|
1111
1224
|
<Button
|
|
1112
1225
|
label="Add item"
|
|
1113
1226
|
variant="primary"
|
|
1114
|
-
size="sm"
|
|
1115
1227
|
icon={<Plus size={16} />}
|
|
1116
1228
|
/>
|
|
1117
1229
|
</HStack>
|
|
@@ -1123,7 +1235,7 @@ function InventoryCard({inventory}: {inventory: InventoryRow[]}) {
|
|
|
1123
1235
|
vAlign="center"
|
|
1124
1236
|
hAlign="between"
|
|
1125
1237
|
style={styles.inventoryFilterRow}>
|
|
1126
|
-
<HStack gap={2} vAlign="center" style={
|
|
1238
|
+
<HStack gap={2} vAlign="center" style={styles.filterRowFill}>
|
|
1127
1239
|
<TextInput
|
|
1128
1240
|
label="Search inventory"
|
|
1129
1241
|
isLabelHidden
|
|
@@ -1162,12 +1274,12 @@ function InventoryCard({inventory}: {inventory: InventoryRow[]}) {
|
|
|
1162
1274
|
value={undefined}
|
|
1163
1275
|
onChange={() => {}}
|
|
1164
1276
|
options={[
|
|
1165
|
-
'
|
|
1166
|
-
'
|
|
1167
|
-
'
|
|
1168
|
-
'
|
|
1169
|
-
'
|
|
1170
|
-
'
|
|
1277
|
+
'Roost 1',
|
|
1278
|
+
'Roost 2',
|
|
1279
|
+
'Roost 3',
|
|
1280
|
+
'Roost 4',
|
|
1281
|
+
'Roost 5',
|
|
1282
|
+
'Roost 6',
|
|
1171
1283
|
]}
|
|
1172
1284
|
/>
|
|
1173
1285
|
<Selector
|
|
@@ -1210,75 +1322,25 @@ function InventoryCard({inventory}: {inventory: InventoryRow[]}) {
|
|
|
1210
1322
|
</HStack>
|
|
1211
1323
|
|
|
1212
1324
|
{lowStockCount > 0 && (
|
|
1213
|
-
<
|
|
1325
|
+
<VStack gap={0} style={inlineStyles.inventoryBannerWrap}>
|
|
1214
1326
|
<Banner
|
|
1215
1327
|
status="warning"
|
|
1216
1328
|
title={lowStockCount + ' items are running low'}
|
|
1217
1329
|
/>
|
|
1218
|
-
</
|
|
1330
|
+
</VStack>
|
|
1219
1331
|
)}
|
|
1220
1332
|
|
|
1221
|
-
<
|
|
1333
|
+
<VStack gap={0} style={styles.inventoryTableWrap}>
|
|
1222
1334
|
<Table<InventoryRow>
|
|
1223
1335
|
data={inventory}
|
|
1224
|
-
columns={
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
header: '',
|
|
1228
|
-
// Wide enough that the control + the theme's cell padding (up to
|
|
1229
|
-
// --spacing-4 = 16px/side on spacious density) fit inside the cell,
|
|
1230
|
-
// so the control's hover background doesn't overflow toward the
|
|
1231
|
-
// card's clipped (rounded) edge on larger-padding themes.
|
|
1232
|
-
width: pixel(64),
|
|
1233
|
-
renderCell: row => <SelectCell row={row} />,
|
|
1234
|
-
},
|
|
1235
|
-
{
|
|
1236
|
-
key: 'item',
|
|
1237
|
-
header: 'Item',
|
|
1238
|
-
// Lower min-width (default 120) so the table fits its container on
|
|
1239
|
-
// larger-spacing themes instead of overflowing the actions column.
|
|
1240
|
-
width: proportional(3, {minWidth: 80}),
|
|
1241
|
-
renderCell: row => <ItemCell row={row} />,
|
|
1242
|
-
},
|
|
1243
|
-
{
|
|
1244
|
-
key: 'available',
|
|
1245
|
-
header: 'Available',
|
|
1246
|
-
width: pixel(100),
|
|
1247
|
-
renderCell: row => (
|
|
1248
|
-
<Text type="body" hasTabularNumbers>
|
|
1249
|
-
{row.available}
|
|
1250
|
-
</Text>
|
|
1251
|
-
),
|
|
1252
|
-
},
|
|
1253
|
-
{
|
|
1254
|
-
key: 'location',
|
|
1255
|
-
header: 'Location',
|
|
1256
|
-
width: pixel(100),
|
|
1257
|
-
renderCell: row => <Text type="body">{row.location}</Text>,
|
|
1258
|
-
},
|
|
1259
|
-
{
|
|
1260
|
-
key: 'tags',
|
|
1261
|
-
header: 'Tags',
|
|
1262
|
-
width: proportional(2, {minWidth: 80}),
|
|
1263
|
-
align: 'end',
|
|
1264
|
-
renderCell: row => <TagsCell row={row} />,
|
|
1265
|
-
},
|
|
1266
|
-
{
|
|
1267
|
-
key: 'actions',
|
|
1268
|
-
header: '',
|
|
1269
|
-
// Match the select column: fit the sm more-menu button + cell
|
|
1270
|
-
// padding so its hover background stays clear of the card's
|
|
1271
|
-
// clipped rounded edge across themes.
|
|
1272
|
-
width: pixel(64),
|
|
1273
|
-
align: 'end',
|
|
1274
|
-
renderCell: () => <ActionsCell />,
|
|
1275
|
-
},
|
|
1276
|
-
]}
|
|
1336
|
+
columns={
|
|
1337
|
+
isNarrow ? NARROW_INVENTORY_COLUMNS : INVENTORY_COLUMNS
|
|
1338
|
+
}
|
|
1277
1339
|
density="spacious"
|
|
1278
1340
|
dividers="rows"
|
|
1279
1341
|
hasHover
|
|
1280
1342
|
/>
|
|
1281
|
-
</
|
|
1343
|
+
</VStack>
|
|
1282
1344
|
</Card>
|
|
1283
1345
|
);
|
|
1284
1346
|
}
|