@vendure-io/ui 2.0.0-beta.10 → 2.0.0-beta.11
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.
|
|
3
|
+
"version": "2.0.0-beta.11",
|
|
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",
|
|
@@ -2,6 +2,12 @@ import * as React from "react"
|
|
|
2
2
|
|
|
3
3
|
import { cn } from "@vendure-io/ui/lib/utils"
|
|
4
4
|
|
|
5
|
+
// The size variant publishes the card's spacing contract as CSS variables
|
|
6
|
+
// (--card-px: horizontal content padding, --card-gap: vertical rhythm shared
|
|
7
|
+
// by the root padding and the flex gap). Slots consume the variables instead
|
|
8
|
+
// of `group-data-[size=sm]` hooks so that nested cards resolve to the NEAREST
|
|
9
|
+
// card — a group selector matches any ancestor and leaks the outer card's
|
|
10
|
+
// size into an inner one.
|
|
5
11
|
function Card({
|
|
6
12
|
className,
|
|
7
13
|
size = "default",
|
|
@@ -12,7 +18,7 @@ function Card({
|
|
|
12
18
|
data-slot="card"
|
|
13
19
|
data-size={size}
|
|
14
20
|
className={cn(
|
|
15
|
-
"bg-card text-card-foreground group/card flex flex-col gap-
|
|
21
|
+
"bg-card text-card-foreground group/card [--card-gap:--spacing(6)] [--card-px:--spacing(6)] data-[size=sm]:[--card-gap:--spacing(4)] data-[size=sm]:[--card-px:--spacing(4)] flex flex-col gap-(--card-gap) overflow-hidden rounded-xl border border-border/60 py-(--card-gap) text-sm has-[>img:first-child]:pt-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
|
|
16
22
|
className
|
|
17
23
|
)}
|
|
18
24
|
{...props}
|
|
@@ -20,12 +26,15 @@ function Card({
|
|
|
20
26
|
)
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
// Band dividers (consumer-applied border-b / border-t on header/footer) render
|
|
30
|
+
// at row-separator strength (border/50) so the card outline (border/60) stays
|
|
31
|
+
// the strongest structural line.
|
|
23
32
|
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
|
|
24
33
|
return (
|
|
25
34
|
<div
|
|
26
35
|
data-slot="card-header"
|
|
27
36
|
className={cn(
|
|
28
|
-
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-
|
|
37
|
+
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-px) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:border-border/50 [.border-b]:pb-(--card-gap)",
|
|
29
38
|
className
|
|
30
39
|
)}
|
|
31
40
|
{...props}
|
|
@@ -73,7 +82,32 @@ function CardContent({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
73
82
|
return (
|
|
74
83
|
<div
|
|
75
84
|
data-slot="card-content"
|
|
76
|
-
className={cn("px-
|
|
85
|
+
className={cn("px-(--card-px)", className)}
|
|
86
|
+
{...props}
|
|
87
|
+
/>
|
|
88
|
+
)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Hosts a Table full-bleed inside the card: negative vertical margins cancel
|
|
92
|
+
// the card's flex gap against a border-b header / border-t footer, and cancel
|
|
93
|
+
// the card's own padding when the table is the first/last child, so rows and
|
|
94
|
+
// band dividers run edge to edge (the card's overflow-hidden clips the
|
|
95
|
+
// corners). Edge cells get the card's content padding so cell content aligns
|
|
96
|
+
// with CardHeader/CardFooter content. Two contract notes:
|
|
97
|
+
// - The gap cancellation is unconditional: an adjacent CardHeader/CardFooter
|
|
98
|
+
// must carry its band border (border-b / border-t), otherwise the table
|
|
99
|
+
// sits directly against the band's content.
|
|
100
|
+
// - The edge-cell selector ((0,2,1)) out-specifies a plain padding utility on
|
|
101
|
+
// the cell ((0,1,0)): a `pl-*`/`pr-*` on a first/last cell silently loses —
|
|
102
|
+
// override with the important modifier (`pl-0!`) when a cell must opt out.
|
|
103
|
+
function CardTable({ className, ...props }: React.ComponentProps<"div">) {
|
|
104
|
+
return (
|
|
105
|
+
<div
|
|
106
|
+
data-slot="card-table"
|
|
107
|
+
className={cn(
|
|
108
|
+
"-my-(--card-gap) [&_tr>*:first-child]:pl-(--card-px) [&_tr>*:last-child]:pr-(--card-px)",
|
|
109
|
+
className
|
|
110
|
+
)}
|
|
77
111
|
{...props}
|
|
78
112
|
/>
|
|
79
113
|
)
|
|
@@ -84,7 +118,7 @@ function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
|
|
|
84
118
|
<div
|
|
85
119
|
data-slot="card-footer"
|
|
86
120
|
className={cn(
|
|
87
|
-
"flex items-center rounded-b-xl px-
|
|
121
|
+
"flex items-center rounded-b-xl px-(--card-px) [.border-t]:border-border/50 [.border-t]:pt-(--card-gap)",
|
|
88
122
|
className
|
|
89
123
|
)}
|
|
90
124
|
{...props}
|
|
@@ -100,4 +134,5 @@ export {
|
|
|
100
134
|
CardAction,
|
|
101
135
|
CardDescription,
|
|
102
136
|
CardContent,
|
|
137
|
+
CardTable,
|
|
103
138
|
}
|
|
@@ -9,11 +9,12 @@ import type { DataTableBulkActionContext } from '@vendure-io/ui/components/molec
|
|
|
9
9
|
import { cn } from '@vendure-io/ui/lib/utils';
|
|
10
10
|
import * as React from 'react';
|
|
11
11
|
|
|
12
|
-
// The selection
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
//
|
|
12
|
+
// The selection bar that replaces the controls row inside the header band
|
|
13
|
+
// while rows are selected (returns null with an empty selection; the core
|
|
14
|
+
// decides where it renders). The cross-page selection cache lives here (donor
|
|
15
|
+
// parity): `rows` only ever holds the current page, so we accumulate
|
|
16
|
+
// `row.original` by id as pages are seen, letting `bulkActions` receive every
|
|
17
|
+
// selected original — not just the ones still on screen.
|
|
17
18
|
|
|
18
19
|
/**
|
|
19
20
|
* Accumulate `row.original` keyed by row id across page changes. Populated during
|
|
@@ -53,12 +54,12 @@ function DataTableBulkActions<TData>({
|
|
|
53
54
|
};
|
|
54
55
|
|
|
55
56
|
return (
|
|
57
|
+
// A plain band row, not a box: it takes the controls row's place inside
|
|
58
|
+
// the header band, so it carries no chrome of its own. min-h-9 matches
|
|
59
|
+
// the controls row's h-9 inputs so the swap holds the band height steady.
|
|
56
60
|
<div
|
|
57
61
|
data-slot="data-table-bulk-actions"
|
|
58
|
-
className={cn(
|
|
59
|
-
'bg-muted/50 flex flex-wrap items-center gap-2 rounded-md border px-3 py-2',
|
|
60
|
-
className,
|
|
61
|
-
)}
|
|
62
|
+
className={cn('flex min-h-9 flex-wrap items-center gap-2', className)}
|
|
62
63
|
{...props}
|
|
63
64
|
>
|
|
64
65
|
{render(ctx)}
|
|
@@ -182,7 +182,11 @@ export interface DataTableProps<TData> {
|
|
|
182
182
|
header?: React.ReactNode;
|
|
183
183
|
/** Controls row (search, faceted filters, custom buttons). Render-prop gets the live table. */
|
|
184
184
|
toolbar?: React.ReactNode | ((table: Table<TData>) => React.ReactNode);
|
|
185
|
-
/**
|
|
185
|
+
/**
|
|
186
|
+
* Replaces the controls row inside the header band while selection is
|
|
187
|
+
* non-empty (title and applied-filter chips stay). Only meaningful with
|
|
188
|
+
* `rowSelection` wired.
|
|
189
|
+
*/
|
|
186
190
|
bulkActions?: (ctx: DataTableBulkActionContext<TData>) => React.ReactNode;
|
|
187
191
|
/**
|
|
188
192
|
* Per-row actions → appends an actions column. First arg is `row.original`
|
|
@@ -225,8 +229,31 @@ export interface DataTableProps<TData> {
|
|
|
225
229
|
*/
|
|
226
230
|
setTableOptions?: (options: TableOptions<TData>) => TableOptions<TData>;
|
|
227
231
|
|
|
232
|
+
/**
|
|
233
|
+
* The table's frame. `'card'` (default) renders the band anatomy on a real
|
|
234
|
+
* `Card`: a `CardHeader` (border-b) hosting the header/controls/chips zones,
|
|
235
|
+
* `CardTable` hosting the table flush to the card edges, and a `CardFooter`
|
|
236
|
+
* (border-t) hosting pagination when it is wired — no footer means the last
|
|
237
|
+
* row sits flush against the card's bottom edge. `'plain'` renders the same
|
|
238
|
+
* band structure without any card chrome, for a table embedded in an
|
|
239
|
+
* existing card (e.g. a dashboard widget): it MUST sit inside a `Card`,
|
|
240
|
+
* whose spacing variables (`--card-px`/`--card-gap`) drive the bands and
|
|
241
|
+
* edge-cell alignment — the host card is the frame.
|
|
242
|
+
*/
|
|
243
|
+
frame?: 'card' | 'plain';
|
|
244
|
+
/**
|
|
245
|
+
* Extra `<TableRow>`s appended inside `TableBody` after the data rows — e.g.
|
|
246
|
+
* an order table's subtotal/shipping/total rows. Rendered only alongside
|
|
247
|
+
* real rows, never with the skeleton or empty states, and has no interaction
|
|
248
|
+
* with selection, sorting, or pagination. Use the function form to receive
|
|
249
|
+
* `columnCount` for `colSpan`s — display columns (select, actions) are
|
|
250
|
+
* injected around your columns, so a hard-coded count desyncs. Rows are
|
|
251
|
+
* presentational: give them `hover:bg-transparent`.
|
|
252
|
+
*/
|
|
253
|
+
footerRows?: React.ReactNode | ((ctx: { columnCount: number }) => React.ReactNode);
|
|
254
|
+
|
|
228
255
|
labels?: DataTableLabels;
|
|
229
|
-
className?: string; //
|
|
256
|
+
className?: string; // the frame root (data-slot="data-table")
|
|
230
257
|
}
|
|
231
258
|
|
|
232
259
|
/**
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
useReactTable,
|
|
20
20
|
type VisibilityState,
|
|
21
21
|
} from '@tanstack/react-table';
|
|
22
|
+
import { Card, CardFooter, CardHeader, CardTable } from '@vendure-io/ui/components/atoms/card';
|
|
22
23
|
import { Checkbox } from '@vendure-io/ui/components/atoms/checkbox';
|
|
23
24
|
import {
|
|
24
25
|
ContextMenu,
|
|
@@ -45,6 +46,7 @@ import {
|
|
|
45
46
|
} from '@vendure-io/ui/components/molecules/data-table/data-table-filters';
|
|
46
47
|
import {
|
|
47
48
|
buildDisplayColumns,
|
|
49
|
+
getSelectedRowIds,
|
|
48
50
|
resolveSlot,
|
|
49
51
|
} from '@vendure-io/ui/components/molecules/data-table/data-table-helpers';
|
|
50
52
|
import type {
|
|
@@ -59,13 +61,17 @@ import {
|
|
|
59
61
|
} from '@vendure-io/ui/components/molecules/data-table/list-header';
|
|
60
62
|
import { TablePagination } from '@vendure-io/ui/components/molecules/data-table/table-pagination';
|
|
61
63
|
import { cn } from '@vendure-io/ui/lib/utils';
|
|
64
|
+
import { AnimatePresence, motion } from 'motion/react';
|
|
62
65
|
import * as React from 'react';
|
|
63
66
|
|
|
64
67
|
// The composition root. Owns the single `useReactTable` instance and the
|
|
65
|
-
// controlled/uncontrolled bridge, then lays out
|
|
66
|
-
//
|
|
67
|
-
//
|
|
68
|
-
//
|
|
68
|
+
// controlled/uncontrolled bridge, then lays out the card-framed table anatomy:
|
|
69
|
+
// a CardHeader band (header → controls → chips → bulk overlay), a CardTable
|
|
70
|
+
// hosting the Table flush to the card edges, and a CardFooter band for
|
|
71
|
+
// pagination. `frame="plain"` keeps the band structure without the card
|
|
72
|
+
// chrome. Every capability follows the Phase-1 rule verbatim: a feature
|
|
73
|
+
// renders only if its config is wired — no feature flags, no disabled
|
|
74
|
+
// placeholders. Only this folder imports TanStack; the composed
|
|
69
75
|
// ListHeader/TablePagination/Chip primitives stay TanStack-free.
|
|
70
76
|
|
|
71
77
|
/**
|
|
@@ -98,6 +104,16 @@ function headerLabelText<TData>(column: Column<TData, unknown>): string {
|
|
|
98
104
|
return typeof header === 'string' ? header : column.id;
|
|
99
105
|
}
|
|
100
106
|
|
|
107
|
+
// The controls/bulk replace-on-select swap: the outgoing row drifts slightly
|
|
108
|
+
// up as it fades, the incoming row rises in from below — a subtle vertical
|
|
109
|
+
// hand-off (4px) rather than a plain crossfade.
|
|
110
|
+
const bandRowFade = {
|
|
111
|
+
initial: { opacity: 0, y: 4 },
|
|
112
|
+
animate: { opacity: 1, y: 0 },
|
|
113
|
+
exit: { opacity: 0, y: -4 },
|
|
114
|
+
transition: { duration: 0.1, ease: 'easeOut' },
|
|
115
|
+
} as const;
|
|
116
|
+
|
|
101
117
|
function ariaSort<TData>(
|
|
102
118
|
hasSorting: boolean,
|
|
103
119
|
column: Column<TData, unknown>,
|
|
@@ -126,6 +142,8 @@ function DataTable<TData>({
|
|
|
126
142
|
emptyState,
|
|
127
143
|
renderRow,
|
|
128
144
|
setTableOptions,
|
|
145
|
+
frame = 'card',
|
|
146
|
+
footerRows,
|
|
129
147
|
labels,
|
|
130
148
|
className,
|
|
131
149
|
}: DataTableProps<TData>) {
|
|
@@ -307,7 +325,11 @@ function DataTable<TData>({
|
|
|
307
325
|
? table.getVisibleLeafColumns().find((column) => column.id !== selectColumnId)?.id
|
|
308
326
|
: undefined;
|
|
309
327
|
const columnClass = (id: string): string | undefined => {
|
|
310
|
-
|
|
328
|
+
// `pl-0!`: the select cell is the first child, so CardTable's edge-cell
|
|
329
|
+
// padding would otherwise widen the zero-width cell and push the whole
|
|
330
|
+
// gutter arrangement out; important because CardTable's descendant rule
|
|
331
|
+
// out-specifies a plain utility on the cell.
|
|
332
|
+
if (id === selectColumnId) return 'relative w-0 p-0 pl-0!';
|
|
311
333
|
if (id === leadingColumnId) return 'pl-8';
|
|
312
334
|
// Actions cells drop vertical padding so a row-action button (taller than a
|
|
313
335
|
// text line) sits inside the natural row height rather than inflating every
|
|
@@ -327,47 +349,76 @@ function DataTable<TData>({
|
|
|
327
349
|
filters.showAppliedFilters !== false &&
|
|
328
350
|
table.getState().columnFilters.length > 0;
|
|
329
351
|
const showHeader = header != null || showControls || showChips;
|
|
330
|
-
|
|
352
|
+
// Unlike the other zones, the bulk bar exists only while rows are selected,
|
|
353
|
+
// so it opens the header band on its own only then — a controls-less
|
|
354
|
+
// selectable table must not render an empty band. (Deliberate trade-off:
|
|
355
|
+
// for a table with no header content at all, the band pops in on first
|
|
356
|
+
// selection rather than reserving empty space.)
|
|
357
|
+
const showBulk =
|
|
358
|
+
rowSelection != null &&
|
|
359
|
+
bulkActions != null &&
|
|
360
|
+
getSelectedRowIds(table.getState().rowSelection).length > 0;
|
|
331
361
|
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
{showHeader && (
|
|
335
|
-
<
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
362
|
+
const bands = (
|
|
363
|
+
<>
|
|
364
|
+
{(showHeader || showBulk) && (
|
|
365
|
+
<CardHeader className="border-b">
|
|
366
|
+
<ListHeader>
|
|
367
|
+
{header}
|
|
368
|
+
{/* Replace-on-select: while rows are selected the bulk bar takes
|
|
369
|
+
the controls row's place (title and applied-filter chips stay).
|
|
370
|
+
The swap fades out then in — one --transition-duration-fast
|
|
371
|
+
(100ms) per phase; mode="wait" keeps the rows sequential so
|
|
372
|
+
they never stack. */}
|
|
373
|
+
{(showControls || (rowSelection != null && bulkActions != null)) && (
|
|
374
|
+
<AnimatePresence initial={false} mode="wait">
|
|
375
|
+
{showBulk && bulkActions ? (
|
|
376
|
+
<motion.div key="bulk-actions" {...bandRowFade}>
|
|
377
|
+
<DataTableBulkActions
|
|
378
|
+
table={table}
|
|
379
|
+
cache={selectionCache}
|
|
380
|
+
render={bulkActions}
|
|
381
|
+
/>
|
|
382
|
+
</motion.div>
|
|
383
|
+
) : showControls ? (
|
|
384
|
+
<motion.div key="controls" {...bandRowFade}>
|
|
385
|
+
<ListHeaderControls>
|
|
386
|
+
{toolbarNode}
|
|
387
|
+
{hasFilterMenu && filters?.columns && (
|
|
388
|
+
<DataTableAddFilter
|
|
389
|
+
table={table}
|
|
390
|
+
columns={filters.columns}
|
|
391
|
+
label={l.addFilter}
|
|
392
|
+
/>
|
|
393
|
+
)}
|
|
394
|
+
{showViewOptions && (
|
|
395
|
+
<DataTableViewOptions
|
|
396
|
+
table={table}
|
|
397
|
+
triggerLabel={l.columnsTrigger}
|
|
398
|
+
heading={l.columnsHeading}
|
|
399
|
+
/>
|
|
400
|
+
)}
|
|
401
|
+
</ListHeaderControls>
|
|
402
|
+
</motion.div>
|
|
403
|
+
) : null}
|
|
404
|
+
</AnimatePresence>
|
|
405
|
+
)}
|
|
406
|
+
{showChips && (
|
|
407
|
+
<ListHeaderChips>
|
|
408
|
+
<DataTableAppliedFilters
|
|
345
409
|
table={table}
|
|
346
|
-
|
|
347
|
-
|
|
410
|
+
columns={filters?.columns}
|
|
411
|
+
inlineChipLimit={filters?.inlineChipLimit}
|
|
412
|
+
removeLabel={l.removeFilter}
|
|
413
|
+
collapsedLabel={l.filtersCollapsed}
|
|
348
414
|
/>
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
<ListHeaderChips>
|
|
354
|
-
<DataTableAppliedFilters
|
|
355
|
-
table={table}
|
|
356
|
-
columns={filters?.columns}
|
|
357
|
-
inlineChipLimit={filters?.inlineChipLimit}
|
|
358
|
-
removeLabel={l.removeFilter}
|
|
359
|
-
collapsedLabel={l.filtersCollapsed}
|
|
360
|
-
/>
|
|
361
|
-
</ListHeaderChips>
|
|
362
|
-
)}
|
|
363
|
-
</ListHeader>
|
|
364
|
-
)}
|
|
365
|
-
|
|
366
|
-
{showBulk && bulkActions && (
|
|
367
|
-
<DataTableBulkActions table={table} cache={selectionCache} render={bulkActions} />
|
|
415
|
+
</ListHeaderChips>
|
|
416
|
+
)}
|
|
417
|
+
</ListHeader>
|
|
418
|
+
</CardHeader>
|
|
368
419
|
)}
|
|
369
420
|
|
|
370
|
-
<
|
|
421
|
+
<CardTable>
|
|
371
422
|
<Table>
|
|
372
423
|
<TableHeader>
|
|
373
424
|
{table.getHeaderGroups().map((headerGroup) => (
|
|
@@ -419,63 +470,94 @@ function DataTable<TData>({
|
|
|
419
470
|
</TableCell>
|
|
420
471
|
</TableRow>
|
|
421
472
|
) : (
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
{
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
473
|
+
<>
|
|
474
|
+
{bodyRows.map((row) => {
|
|
475
|
+
const rowNode = (
|
|
476
|
+
<TableRow
|
|
477
|
+
className="group/row"
|
|
478
|
+
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
479
|
+
>
|
|
480
|
+
{row.getVisibleCells().map((cell) => (
|
|
481
|
+
<TableCell key={cell.id} className={columnClass(cell.column.id)}>
|
|
482
|
+
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
|
483
|
+
</TableCell>
|
|
484
|
+
))}
|
|
485
|
+
</TableRow>
|
|
486
|
+
);
|
|
487
|
+
// Right-click accelerator: the row itself is the context-menu
|
|
488
|
+
// trigger, so the consumer supplies only the items and the core
|
|
489
|
+
// owns the menu chrome.
|
|
490
|
+
const defaultRow = contextActions ? (
|
|
491
|
+
<ContextMenu>
|
|
492
|
+
<ContextMenuTrigger render={rowNode} />
|
|
493
|
+
<ContextMenuContent>
|
|
494
|
+
{contextActions(row.original, { row, table })}
|
|
495
|
+
</ContextMenuContent>
|
|
496
|
+
</ContextMenu>
|
|
497
|
+
) : (
|
|
498
|
+
rowNode
|
|
499
|
+
);
|
|
500
|
+
// Row-render seam: consumers can swap the default row for a
|
|
501
|
+
// full-width utility row or a per-row wrapper the cell grid can't
|
|
502
|
+
// express. Returning `defaultRow` keeps the built-in rendering.
|
|
503
|
+
return (
|
|
504
|
+
<React.Fragment key={row.id}>
|
|
505
|
+
{renderRow ? renderRow(row, { table, columnCount, defaultRow }) : defaultRow}
|
|
506
|
+
</React.Fragment>
|
|
507
|
+
);
|
|
508
|
+
})}
|
|
509
|
+
{typeof footerRows === 'function' ? footerRows({ columnCount }) : footerRows}
|
|
510
|
+
</>
|
|
457
511
|
)}
|
|
458
512
|
</TableBody>
|
|
459
513
|
</Table>
|
|
460
|
-
</
|
|
514
|
+
</CardTable>
|
|
461
515
|
|
|
462
516
|
{pagination && (
|
|
463
|
-
<
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
517
|
+
<CardFooter className="border-t">
|
|
518
|
+
<TablePagination
|
|
519
|
+
className="w-full"
|
|
520
|
+
page={pagination.page}
|
|
521
|
+
pageSize={pagination.pageSize}
|
|
522
|
+
totalItems={pagination.totalItems ?? table.getPrePaginationRowModel().rows.length}
|
|
523
|
+
onPageChange={pagination.onPageChange}
|
|
524
|
+
onPageSizeChange={pagination.onPageSizeChange}
|
|
525
|
+
pageSizeOptions={pagination.pageSizeOptions}
|
|
526
|
+
getPageHref={pagination.getPageHref}
|
|
527
|
+
formatRange={pagination.formatRange}
|
|
528
|
+
navLabel={l.pagination?.navLabel}
|
|
529
|
+
previousLabel={l.pagination?.previousLabel}
|
|
530
|
+
nextLabel={l.pagination?.nextLabel}
|
|
531
|
+
pageSizeLabel={l.pagination?.pageSizeLabel}
|
|
532
|
+
/>
|
|
533
|
+
</CardFooter>
|
|
534
|
+
)}
|
|
535
|
+
</>
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
// `plain` renders a chrome-less stack instead of a Card so the bands and
|
|
539
|
+
// CardTable consume the HOST card's spacing variables (--card-px /
|
|
540
|
+
// --card-gap): edge cells align with the host's padding regardless of its
|
|
541
|
+
// size, and a trailing CardTable's negative margin reaches through the
|
|
542
|
+
// host's bottom padding so the last row sits flush against the host's edge.
|
|
543
|
+
// With no header band the leading CardTable's negative margin would eat the
|
|
544
|
+
// host's flex gap too and press the rows against the preceding content, so
|
|
545
|
+
// the stack restores that one gap as padding.
|
|
546
|
+
return frame === 'plain' ? (
|
|
547
|
+
<div
|
|
548
|
+
data-slot="data-table"
|
|
549
|
+
className={cn(
|
|
550
|
+
'flex flex-col gap-(--card-gap)',
|
|
551
|
+
!(showHeader || showBulk) && 'pt-(--card-gap)',
|
|
552
|
+
className,
|
|
477
553
|
)}
|
|
554
|
+
>
|
|
555
|
+
{bands}
|
|
478
556
|
</div>
|
|
557
|
+
) : (
|
|
558
|
+
<Card data-slot="data-table" className={className}>
|
|
559
|
+
{bands}
|
|
560
|
+
</Card>
|
|
479
561
|
);
|
|
480
562
|
}
|
|
481
563
|
|