@djangocfg/ui-core 2.1.422 → 2.1.424
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": "@djangocfg/ui-core",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.424",
|
|
4
4
|
"description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-components",
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"check": "tsc --noEmit"
|
|
107
107
|
},
|
|
108
108
|
"peerDependencies": {
|
|
109
|
-
"@djangocfg/i18n": "^2.1.
|
|
109
|
+
"@djangocfg/i18n": "^2.1.424",
|
|
110
110
|
"consola": "^3.4.2",
|
|
111
111
|
"lucide-react": "^0.545.0",
|
|
112
112
|
"moment": "^2.30.1",
|
|
@@ -180,8 +180,8 @@
|
|
|
180
180
|
"@chenglou/pretext": "*"
|
|
181
181
|
},
|
|
182
182
|
"devDependencies": {
|
|
183
|
-
"@djangocfg/i18n": "^2.1.
|
|
184
|
-
"@djangocfg/typescript-config": "^2.1.
|
|
183
|
+
"@djangocfg/i18n": "^2.1.424",
|
|
184
|
+
"@djangocfg/typescript-config": "^2.1.424",
|
|
185
185
|
"@types/node": "^25.2.3",
|
|
186
186
|
"@types/react": "^19.2.15",
|
|
187
187
|
"@types/react-dom": "^19.2.3",
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Table
|
|
2
|
+
|
|
3
|
+
Composable, headless data table built from semantic primitives. The root `Table`
|
|
4
|
+
renders a bordered, rounded **`--card`** surface (a contained data panel that reads
|
|
5
|
+
correctly in both light and dark), and you assemble the structure from the
|
|
6
|
+
sub-components — there is no monolithic `columns`/`data` prop.
|
|
7
|
+
|
|
8
|
+
All colors come from theme tokens (`--card`, `--border`, `--muted`, `--accent`),
|
|
9
|
+
so light/dark is automatic. Don't reach for raw `bg-gray-*` / `border-zinc-*`.
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import {
|
|
13
|
+
Table, TableHeader, TableBody, TableFooter,
|
|
14
|
+
TableRow, TableHead, TableCell, TableCaption,
|
|
15
|
+
} from '@djangocfg/ui-core';
|
|
16
|
+
|
|
17
|
+
<Table>
|
|
18
|
+
<TableHeader>
|
|
19
|
+
<TableRow>
|
|
20
|
+
<TableHead>Invoice</TableHead>
|
|
21
|
+
<TableHead className="text-right">Amount</TableHead>
|
|
22
|
+
</TableRow>
|
|
23
|
+
</TableHeader>
|
|
24
|
+
<TableBody>
|
|
25
|
+
<TableRow>
|
|
26
|
+
<TableCell className="font-medium">INV001</TableCell>
|
|
27
|
+
<TableCell className="text-right">$250.00</TableCell>
|
|
28
|
+
</TableRow>
|
|
29
|
+
</TableBody>
|
|
30
|
+
</Table>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Components
|
|
34
|
+
|
|
35
|
+
| Component | Element | Notes |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `Table` | `<table>` (wrapped) | Card frame + scroll container. `className` → `<table>`; see `containerClassName` below. |
|
|
38
|
+
| `TableHeader` | `<thead>` | Tinted header band. Supports `sticky`. |
|
|
39
|
+
| `TableBody` | `<tbody>` | Supports `striped`. |
|
|
40
|
+
| `TableFooter` | `<tfoot>` | Tinted band + `font-medium` for totals/summary rows. |
|
|
41
|
+
| `TableRow` | `<tr>` | Hover highlight; `data-state="selected"` → accent fill. |
|
|
42
|
+
| `TableHead` | `<th>` | Muted, medium-weight column label. |
|
|
43
|
+
| `TableCell` | `<td>` | Body cell. |
|
|
44
|
+
| `TableCaption` | `<caption>` | Bottom footer strip inside the card frame. |
|
|
45
|
+
|
|
46
|
+
All forward their ref and spread native attributes. Per-element `className` always wins.
|
|
47
|
+
|
|
48
|
+
## Custom props
|
|
49
|
+
|
|
50
|
+
These are the only props beyond native HTML attributes:
|
|
51
|
+
|
|
52
|
+
| Component | Prop | Type | Default | Description |
|
|
53
|
+
|---|---|---|---|---|
|
|
54
|
+
| `Table` | `containerClassName` | `string` | — | Class for the **inner scroll `<div>`** (`overflow-auto`), not the `<table>`. Cap its height (e.g. `"max-h-80"`) to get a scrollable body — required for a sticky header to have something to stick within. |
|
|
55
|
+
| `TableHeader` | `sticky` | `boolean` | `false` | Pins the header (`sticky top-0 z-10`) and makes the band **fully opaque** (solid `bg-muted` instead of the `/50` tint) so scrolled rows don't bleed through. |
|
|
56
|
+
| `TableBody` | `striped` | `boolean` | `false` | Zebra rows via `nth-child(even)` on the body — no need to compute `i % 2` per row. Per-row `className` still overrides. |
|
|
57
|
+
|
|
58
|
+
## Selected rows
|
|
59
|
+
|
|
60
|
+
`TableRow` reacts to `data-state="selected"` with an accent fill — drive it from your
|
|
61
|
+
own selection state:
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
<TableRow data-state={isSelected ? 'selected' : undefined}>…</TableRow>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Scrollable table with a sticky header
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
<Table containerClassName="max-h-[320px]">
|
|
71
|
+
<TableHeader sticky>…</TableHeader>
|
|
72
|
+
<TableBody>{/* many rows */}</TableBody>
|
|
73
|
+
</Table>
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
The height cap goes on the **scroll container** (`containerClassName`), and `sticky`
|
|
77
|
+
goes on the header — both are needed together.
|
|
78
|
+
|
|
79
|
+
Storybook: `apps/storybook/stories/ui-core/data/Table.stories.tsx`
|
|
80
|
+
(`Default`, `WithBadges`, `Striped`, `Compact`, `Sortable`, `Selectable`,
|
|
81
|
+
`WithFooter`, `StickyHeader`, `Loading`, `Empty`).
|
|
@@ -4,33 +4,60 @@ import { cn } from '../../../lib/utils';
|
|
|
4
4
|
|
|
5
5
|
const Table = React.forwardRef<
|
|
6
6
|
HTMLTableElement,
|
|
7
|
-
React.HTMLAttributes<HTMLTableElement>
|
|
8
|
-
>(({ className, ...props }, ref) => (
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
7
|
+
React.HTMLAttributes<HTMLTableElement> & { containerClassName?: string }
|
|
8
|
+
>(({ className, containerClassName, ...props }, ref) => (
|
|
9
|
+
// Card-style frame: a bordered, rounded `--card` surface so the table
|
|
10
|
+
// reads as a contained data panel in both themes (the page `--background`
|
|
11
|
+
// alone gives no separation). `overflow-hidden` clips the header band and
|
|
12
|
+
// row borders to the rounded corners.
|
|
13
|
+
<div className="relative w-full overflow-hidden rounded-[var(--radius)] border border-border bg-card text-card-foreground">
|
|
14
|
+
{/* Scroll container. Cap its height via `containerClassName`
|
|
15
|
+
* (e.g. "max-h-80") to get a scrollable body with a sticky header. */}
|
|
16
|
+
<div className={cn("w-full overflow-auto", containerClassName)}>
|
|
17
|
+
<table
|
|
18
|
+
ref={ref}
|
|
19
|
+
className={cn("w-full caption-bottom text-sm", className)}
|
|
20
|
+
{...props}
|
|
21
|
+
/>
|
|
22
|
+
</div>
|
|
15
23
|
</div>
|
|
16
24
|
))
|
|
17
25
|
Table.displayName = "Table"
|
|
18
26
|
|
|
19
27
|
const TableHeader = React.forwardRef<
|
|
20
28
|
HTMLTableSectionElement,
|
|
21
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
22
|
-
>(({ className, ...props }, ref) => (
|
|
23
|
-
|
|
29
|
+
React.HTMLAttributes<HTMLTableSectionElement> & { sticky?: boolean }
|
|
30
|
+
>(({ className, sticky = false, ...props }, ref) => (
|
|
31
|
+
// Tinted header band with a solid bottom border so the column labels
|
|
32
|
+
// separate clearly from the body in both light and dark.
|
|
33
|
+
// When `sticky`, the band must be fully opaque (solid `bg-muted`, not the
|
|
34
|
+
// /50 tint) so scrolled body rows don't bleed through it.
|
|
35
|
+
<thead
|
|
36
|
+
ref={ref}
|
|
37
|
+
className={cn(
|
|
38
|
+
"[&_tr]:border-b [&_tr]:border-border",
|
|
39
|
+
sticky ? "sticky top-0 z-10 bg-muted" : "bg-muted/50",
|
|
40
|
+
className
|
|
41
|
+
)}
|
|
42
|
+
{...props}
|
|
43
|
+
/>
|
|
24
44
|
))
|
|
25
45
|
TableHeader.displayName = "TableHeader"
|
|
26
46
|
|
|
27
47
|
const TableBody = React.forwardRef<
|
|
28
48
|
HTMLTableSectionElement,
|
|
29
|
-
React.HTMLAttributes<HTMLTableSectionElement>
|
|
30
|
-
>(({ className, ...props }, ref) => (
|
|
49
|
+
React.HTMLAttributes<HTMLTableSectionElement> & { striped?: boolean }
|
|
50
|
+
>(({ className, striped = false, ...props }, ref) => (
|
|
51
|
+
// `striped` opts into zebra rows via a structural selector on the body, so
|
|
52
|
+
// callers don't have to compute `i % 2` per row. Keeps the headless API:
|
|
53
|
+
// per-row className still wins and the default (false) is unchanged.
|
|
31
54
|
<tbody
|
|
32
55
|
ref={ref}
|
|
33
|
-
className={cn(
|
|
56
|
+
className={cn(
|
|
57
|
+
"[&_tr:last-child]:border-0",
|
|
58
|
+
striped && "[&_tr:nth-child(even)]:bg-muted/50",
|
|
59
|
+
className
|
|
60
|
+
)}
|
|
34
61
|
{...props}
|
|
35
62
|
/>
|
|
36
63
|
))
|
|
@@ -58,7 +85,7 @@ const TableRow = React.forwardRef<
|
|
|
58
85
|
<tr
|
|
59
86
|
ref={ref}
|
|
60
87
|
className={cn(
|
|
61
|
-
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-
|
|
88
|
+
"border-b border-border transition-colors hover:bg-muted/50 data-[state=selected]:bg-accent",
|
|
62
89
|
className
|
|
63
90
|
)}
|
|
64
91
|
{...props}
|
|
@@ -100,9 +127,14 @@ const TableCaption = React.forwardRef<
|
|
|
100
127
|
HTMLTableCaptionElement,
|
|
101
128
|
React.HTMLAttributes<HTMLTableCaptionElement>
|
|
102
129
|
>(({ className, ...props }, ref) => (
|
|
130
|
+
// Caption sits at the bottom inside the card frame; a top border + padding
|
|
131
|
+
// make it read as a footer strip rather than floating text.
|
|
103
132
|
<caption
|
|
104
133
|
ref={ref}
|
|
105
|
-
className={cn(
|
|
134
|
+
className={cn(
|
|
135
|
+
"border-t border-border px-3 py-2.5 text-left text-sm text-muted-foreground",
|
|
136
|
+
className
|
|
137
|
+
)}
|
|
106
138
|
{...props}
|
|
107
139
|
/>
|
|
108
140
|
))
|