@vendure-io/ui 2.0.0-beta.1 → 2.0.0-beta.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vendure-io/ui",
3
- "version": "2.0.0-beta.1",
3
+ "version": "2.0.0-beta.3",
4
4
  "description": "React component library for Vendure, built on shadcn/ui and Tailwind v4",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "homepage": "https://github.com/vendurehq/design/tree/main/packages/ui",
@@ -50,7 +50,7 @@
50
50
  "dependencies": {
51
51
  "@base-ui/react": "^1.2.0",
52
52
  "@tanstack/react-table": "^8.21.3",
53
- "@vendure-io/design-tokens": "^2.0.0-beta.0",
53
+ "@vendure-io/design-tokens": "^2.0.0-beta.1",
54
54
  "class-variance-authority": "^0.7.1",
55
55
  "clsx": "^2.1.1",
56
56
  "cmdk": "^1.1.1",
@@ -12,7 +12,7 @@ function Card({
12
12
  data-slot="card"
13
13
  data-size={size}
14
14
  className={cn(
15
- "bg-card text-card-foreground group/card flex flex-col gap-6 overflow-hidden rounded-xl py-6 text-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
15
+ "bg-card text-card-foreground group/card flex flex-col gap-6 overflow-hidden rounded-xl border border-border/60 py-6 text-sm has-[>img:first-child]:pt-0 data-[size=sm]:gap-4 data-[size=sm]:py-4 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
16
16
  className
17
17
  )}
18
18
  {...props}
@@ -303,11 +303,16 @@ function SidebarRail({ className, ...props }: React.ComponentProps<"button">) {
303
303
  }
304
304
 
305
305
  function SidebarInset({ className, ...props }: React.ComponentProps<"main">) {
306
+ // The inset variant is our recessed content pane, built on the surface ramp: it
307
+ // steps up to --surface while the wrapper stays on --background, so the shell
308
+ // recedes and cards (--surface-raised) lift off the pane. A border carries the
309
+ // tier in light mode where the ramp collapses to white; flat by design, no drop
310
+ // shadow (see AGENTS.md "flat look"). The default variant stays flush on canvas.
306
311
  return (
307
312
  <main
308
313
  data-slot="sidebar-inset"
309
314
  className={cn(
310
- "bg-background relative flex w-full flex-1 flex-col md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:shadow-sm md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
315
+ "bg-background relative flex w-full flex-1 flex-col md:peer-data-[variant=inset]:bg-surface md:peer-data-[variant=inset]:m-2 md:peer-data-[variant=inset]:ml-0 md:peer-data-[variant=inset]:rounded-xl md:peer-data-[variant=inset]:border md:peer-data-[variant=inset]:border-border/60 md:peer-data-[variant=inset]:peer-data-[state=collapsed]:ml-2",
311
316
  className
312
317
  )}
313
318
  {...props}
@@ -57,7 +57,7 @@ function TableRow({ className, ...props }: React.ComponentProps<"tr">) {
57
57
  <tr
58
58
  data-slot="table-row"
59
59
  className={cn(
60
- "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
60
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b border-border/50 transition-colors",
61
61
  className
62
62
  )}
63
63
  {...props}
@@ -0,0 +1,60 @@
1
+ import { cn } from '@vendure-io/ui/lib/utils';
2
+ import type * as React from 'react';
3
+
4
+ // AppShell — the recessed-surface app layout, built on the surface ramp.
5
+ //
6
+ // Nests three surface tiers so the chrome reads as "deep" the way Linear's does:
7
+ //
8
+ // AppShell → --background the canvas the whole window sits on
9
+ // AppShellSidebar → --sidebar nav rail; equals the canvas, so it recedes
10
+ // AppShellContent → --surface the content pane, stepped forward + inset
11
+ //
12
+ // Cards dropped into the content pane resolve to --surface-raised (via the `card`
13
+ // token), completing the third step. In dark mode each tier is a lightness step;
14
+ // in light mode the ramp collapses to white, so the pane's border + shadow carry
15
+ // the step instead. That asymmetry is intentional, not a gap.
16
+ //
17
+ // The sidebar slot is deliberately unopinionated: drop the full `Sidebar` atom in
18
+ // for a real app, or use `AppShellSidebar` for a lightweight rail.
19
+
20
+ function AppShell({ className, ...props }: React.ComponentProps<'div'>) {
21
+ return (
22
+ <div
23
+ data-slot="app-shell"
24
+ className={cn('bg-background text-foreground flex min-h-svh w-full', className)}
25
+ {...props}
26
+ />
27
+ );
28
+ }
29
+
30
+ function AppShellSidebar({ className, ...props }: React.ComponentProps<'aside'>) {
31
+ return (
32
+ <aside
33
+ data-slot="app-shell-sidebar"
34
+ className={cn(
35
+ 'bg-sidebar text-sidebar-foreground flex w-64 shrink-0 flex-col gap-1 p-3',
36
+ className,
37
+ )}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function AppShellContent({ className, ...props }: React.ComponentProps<'main'>) {
44
+ return (
45
+ <main
46
+ data-slot="app-shell-content"
47
+ className={cn(
48
+ // The missing tier: step up to --surface and inset from the canvas so the
49
+ // shell frames the pane on all sides, including a gutter between the
50
+ // sidebar and the pane. Flat by design: the border carries the tier in
51
+ // light mode (see AGENTS.md "flat look"), so no drop shadow.
52
+ 'bg-surface m-3 flex flex-1 flex-col overflow-hidden rounded-xl border border-border/60',
53
+ className,
54
+ )}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ export { AppShell, AppShellSidebar, AppShellContent };
@@ -367,95 +367,97 @@ function DataTable<TData>({
367
367
  <DataTableBulkActions table={table} cache={selectionCache} render={bulkActions} />
368
368
  )}
369
369
 
370
- <Table>
371
- <TableHeader>
372
- {table.getHeaderGroups().map((headerGroup) => (
373
- <TableRow key={headerGroup.id} className="group/header-row">
374
- {headerGroup.headers.map((headerCell) => {
375
- const content = headerCell.isPlaceholder
376
- ? null
377
- : flexRender(headerCell.column.columnDef.header, headerCell.getContext());
378
- return (
379
- <TableHead
380
- key={headerCell.id}
381
- aria-sort={ariaSort(sorting != null, headerCell.column)}
382
- className={columnClass(headerCell.column.id)}
383
- >
384
- {sorting != null && !headerCell.isPlaceholder ? (
385
- <DataTableColumnHeader
386
- column={headerCell.column}
387
- sortLabel={l.sortLabel(headerLabelText(headerCell.column))}
388
- >
389
- {content}
390
- </DataTableColumnHeader>
391
- ) : (
392
- content
393
- )}
394
- </TableHead>
395
- );
396
- })}
397
- </TableRow>
398
- ))}
399
- </TableHeader>
400
- <TableBody>
401
- {isLoading && rows.length === 0 ? (
402
- Array.from({ length: skeletonRowCount }).map((_, rowIndex) => (
403
- <TableRow key={`skeleton-${rowIndex}`}>
404
- {Array.from({ length: Math.max(columnCount, 1) }).map((__, cellIndex) => (
405
- <TableCell key={`skeleton-cell-${cellIndex}`}>
406
- <Skeleton className="h-4 w-full" />
407
- </TableCell>
408
- ))}
370
+ <div className="bg-card overflow-hidden rounded-xl border border-border/60">
371
+ <Table>
372
+ <TableHeader>
373
+ {table.getHeaderGroups().map((headerGroup) => (
374
+ <TableRow key={headerGroup.id} className="group/header-row">
375
+ {headerGroup.headers.map((headerCell) => {
376
+ const content = headerCell.isPlaceholder
377
+ ? null
378
+ : flexRender(headerCell.column.columnDef.header, headerCell.getContext());
379
+ return (
380
+ <TableHead
381
+ key={headerCell.id}
382
+ aria-sort={ariaSort(sorting != null, headerCell.column)}
383
+ className={columnClass(headerCell.column.id)}
384
+ >
385
+ {sorting != null && !headerCell.isPlaceholder ? (
386
+ <DataTableColumnHeader
387
+ column={headerCell.column}
388
+ sortLabel={l.sortLabel(headerLabelText(headerCell.column))}
389
+ >
390
+ {content}
391
+ </DataTableColumnHeader>
392
+ ) : (
393
+ content
394
+ )}
395
+ </TableHead>
396
+ );
397
+ })}
409
398
  </TableRow>
410
- ))
411
- ) : bodyRows.length === 0 ? (
412
- <TableRow className="hover:bg-transparent">
413
- <TableCell
414
- colSpan={Math.max(columnCount, 1)}
415
- className="text-muted-foreground h-24 text-center"
416
- >
417
- {emptyState ?? l.empty}
418
- </TableCell>
419
- </TableRow>
420
- ) : (
421
- bodyRows.map((row) => {
422
- const rowNode = (
423
- <TableRow
424
- className="group/row"
425
- data-state={row.getIsSelected() ? 'selected' : undefined}
426
- >
427
- {row.getVisibleCells().map((cell) => (
428
- <TableCell key={cell.id} className={columnClass(cell.column.id)}>
429
- {flexRender(cell.column.columnDef.cell, cell.getContext())}
399
+ ))}
400
+ </TableHeader>
401
+ <TableBody>
402
+ {isLoading && rows.length === 0 ? (
403
+ Array.from({ length: skeletonRowCount }).map((_, rowIndex) => (
404
+ <TableRow key={`skeleton-${rowIndex}`}>
405
+ {Array.from({ length: Math.max(columnCount, 1) }).map((__, cellIndex) => (
406
+ <TableCell key={`skeleton-cell-${cellIndex}`}>
407
+ <Skeleton className="h-4 w-full" />
430
408
  </TableCell>
431
409
  ))}
432
410
  </TableRow>
433
- );
434
- // Right-click accelerator: the row itself is the context-menu
435
- // trigger, so the consumer supplies only the items and the core
436
- // owns the menu chrome.
437
- const defaultRow = contextActions ? (
438
- <ContextMenu>
439
- <ContextMenuTrigger render={rowNode} />
440
- <ContextMenuContent>
441
- {contextActions(row.original, { row, table })}
442
- </ContextMenuContent>
443
- </ContextMenu>
444
- ) : (
445
- rowNode
446
- );
447
- // Row-render seam: consumers can swap the default row for a
448
- // full-width utility row or a per-row wrapper the cell grid can't
449
- // express. Returning `defaultRow` keeps the built-in rendering.
450
- return (
451
- <React.Fragment key={row.id}>
452
- {renderRow ? renderRow(row, { table, columnCount, defaultRow }) : defaultRow}
453
- </React.Fragment>
454
- );
455
- })
456
- )}
457
- </TableBody>
458
- </Table>
411
+ ))
412
+ ) : bodyRows.length === 0 ? (
413
+ <TableRow className="hover:bg-transparent">
414
+ <TableCell
415
+ colSpan={Math.max(columnCount, 1)}
416
+ className="text-muted-foreground h-24 text-center"
417
+ >
418
+ {emptyState ?? l.empty}
419
+ </TableCell>
420
+ </TableRow>
421
+ ) : (
422
+ bodyRows.map((row) => {
423
+ const rowNode = (
424
+ <TableRow
425
+ className="group/row"
426
+ data-state={row.getIsSelected() ? 'selected' : undefined}
427
+ >
428
+ {row.getVisibleCells().map((cell) => (
429
+ <TableCell key={cell.id} className={columnClass(cell.column.id)}>
430
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
431
+ </TableCell>
432
+ ))}
433
+ </TableRow>
434
+ );
435
+ // Right-click accelerator: the row itself is the context-menu
436
+ // trigger, so the consumer supplies only the items and the core
437
+ // owns the menu chrome.
438
+ const defaultRow = contextActions ? (
439
+ <ContextMenu>
440
+ <ContextMenuTrigger render={rowNode} />
441
+ <ContextMenuContent>
442
+ {contextActions(row.original, { row, table })}
443
+ </ContextMenuContent>
444
+ </ContextMenu>
445
+ ) : (
446
+ rowNode
447
+ );
448
+ // Row-render seam: consumers can swap the default row for a
449
+ // full-width utility row or a per-row wrapper the cell grid can't
450
+ // express. Returning `defaultRow` keeps the built-in rendering.
451
+ return (
452
+ <React.Fragment key={row.id}>
453
+ {renderRow ? renderRow(row, { table, columnCount, defaultRow }) : defaultRow}
454
+ </React.Fragment>
455
+ );
456
+ })
457
+ )}
458
+ </TableBody>
459
+ </Table>
460
+ </div>
459
461
 
460
462
  {pagination && (
461
463
  <TablePagination