@djangocfg/ui-core 2.1.422 → 2.1.423

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.422",
3
+ "version": "2.1.423",
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.422",
109
+ "@djangocfg/i18n": "^2.1.423",
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.422",
184
- "@djangocfg/typescript-config": "^2.1.422",
183
+ "@djangocfg/i18n": "^2.1.423",
184
+ "@djangocfg/typescript-config": "^2.1.423",
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
- <div className="relative w-full overflow-auto">
10
- <table
11
- ref={ref}
12
- className={cn("w-full caption-bottom text-sm", className)}
13
- {...props}
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
- <thead ref={ref} className={cn("[&_tr]:border-b", className)} {...props} />
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("[&_tr:last-child]:border-0", className)}
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-muted",
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("mt-4 text-sm text-muted-foreground", className)}
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
  ))