@dashforge/tw 0.5.0-beta → 0.6.0-beta

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +268 -0
  2. package/dist/index.esm.js +1553 -4
  3. package/dist/src/components/Table/Table.d.ts +24 -0
  4. package/dist/src/components/Table/Table.d.ts.map +1 -0
  5. package/dist/src/components/Table/_internal/getNestedValue.d.ts +20 -0
  6. package/dist/src/components/Table/_internal/getNestedValue.d.ts.map +1 -0
  7. package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts +45 -0
  8. package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts.map +1 -0
  9. package/dist/src/components/Table/_internal/useControllableState.d.ts +23 -0
  10. package/dist/src/components/Table/_internal/useControllableState.d.ts.map +1 -0
  11. package/dist/src/components/Table/_internal/useDebouncedValue.d.ts +10 -0
  12. package/dist/src/components/Table/_internal/useDebouncedValue.d.ts.map +1 -0
  13. package/dist/src/components/Table/_internal/useTableFilter.d.ts +13 -0
  14. package/dist/src/components/Table/_internal/useTableFilter.d.ts.map +1 -0
  15. package/dist/src/components/Table/_internal/useTableSearch.d.ts +23 -0
  16. package/dist/src/components/Table/_internal/useTableSearch.d.ts.map +1 -0
  17. package/dist/src/components/Table/_internal/useTableSelection.d.ts +25 -0
  18. package/dist/src/components/Table/_internal/useTableSelection.d.ts.map +1 -0
  19. package/dist/src/components/Table/_internal/useTableSort.d.ts +20 -0
  20. package/dist/src/components/Table/_internal/useTableSort.d.ts.map +1 -0
  21. package/dist/src/components/Table/cells/RenderButton.d.ts +13 -0
  22. package/dist/src/components/Table/cells/RenderButton.d.ts.map +1 -0
  23. package/dist/src/components/Table/cells/RenderChip.d.ts +80 -0
  24. package/dist/src/components/Table/cells/RenderChip.d.ts.map +1 -0
  25. package/dist/src/components/Table/cells/RenderText.d.ts +21 -0
  26. package/dist/src/components/Table/cells/RenderText.d.ts.map +1 -0
  27. package/dist/src/components/Table/cells/RenderTwoLine.d.ts +20 -0
  28. package/dist/src/components/Table/cells/RenderTwoLine.d.ts.map +1 -0
  29. package/dist/src/components/Table/cells/RowActionsMenu.d.ts +36 -0
  30. package/dist/src/components/Table/cells/RowActionsMenu.d.ts.map +1 -0
  31. package/dist/src/components/Table/cells/index.d.ts +11 -0
  32. package/dist/src/components/Table/cells/index.d.ts.map +1 -0
  33. package/dist/src/components/Table/table.types.d.ts +304 -0
  34. package/dist/src/components/Table/table.types.d.ts.map +1 -0
  35. package/dist/src/components/Table/table.variants.d.ts +285 -0
  36. package/dist/src/components/Table/table.variants.d.ts.map +1 -0
  37. package/dist/src/index.d.ts +7 -1
  38. package/dist/src/index.d.ts.map +1 -1
  39. package/package.json +1 -1
  40. package/src/components/Table/Table.test.tsx +603 -0
  41. package/src/components/Table/Table.tsx +816 -0
  42. package/src/components/Table/_internal/getNestedValue.test.ts +50 -0
  43. package/src/components/Table/_internal/getNestedValue.ts +32 -0
  44. package/src/components/Table/_internal/themeIdentity.test.ts +84 -0
  45. package/src/components/Table/_internal/useColumnAutoDetect.test.ts +123 -0
  46. package/src/components/Table/_internal/useColumnAutoDetect.ts +100 -0
  47. package/src/components/Table/_internal/useControllableState.ts +48 -0
  48. package/src/components/Table/_internal/useDebouncedValue.ts +24 -0
  49. package/src/components/Table/_internal/useTableFilter.ts +37 -0
  50. package/src/components/Table/_internal/useTableSearch.test.ts +115 -0
  51. package/src/components/Table/_internal/useTableSearch.ts +66 -0
  52. package/src/components/Table/_internal/useTableSelection.test.ts +100 -0
  53. package/src/components/Table/_internal/useTableSelection.ts +100 -0
  54. package/src/components/Table/_internal/useTableSort.test.ts +150 -0
  55. package/src/components/Table/_internal/useTableSort.ts +141 -0
  56. package/src/components/Table/cells/RenderButton.tsx +23 -0
  57. package/src/components/Table/cells/RenderChip.tsx +103 -0
  58. package/src/components/Table/cells/RenderText.tsx +37 -0
  59. package/src/components/Table/cells/RenderTwoLine.tsx +34 -0
  60. package/src/components/Table/cells/RowActionsMenu.tsx +147 -0
  61. package/src/components/Table/cells/index.ts +14 -0
  62. package/src/components/Table/table.types.ts +337 -0
  63. package/src/components/Table/table.variants.ts +207 -0
  64. package/src/index.ts +43 -1
package/CHANGELOG.md CHANGED
@@ -12,6 +12,274 @@ This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
12
12
  > duplicated intentionally — no shared "lowest common denominator" headless
13
13
  > layer.
14
14
 
15
+ ## [0.6.0-beta] — 2026-05-19
16
+
17
+ **Sprint 4.1 release.** Ships the central piece of the data layer
18
+ — a market-grounded `<Table>` built from scratch with NO new
19
+ runtime deps. The design references Stripe (visual style),
20
+ Atlassian / Jira (column UX), and Pencil & Paper UX research
21
+ (density tiers, hover-revealed row actions, selection patterns).
22
+ The existing MUI Table inherited from `hub-ws/admin` informed the
23
+ public API shape but not the implementation (we discarded the
24
+ `@mui/x-data-grid` backing — Table v1 is a hand-rolled native
25
+ `<table>`).
26
+
27
+ **Companion to Sprint 4.2** (DataGrid, virtualization, 10k+ rows)
28
+ — Table v1 covers the 80% admin-list use case up to ~500 rows.
29
+
30
+ **Minor bump** for the new public API surface — 15+ new exports.
31
+ Strictly additive — zero breaking changes. Drop-in upgrade from
32
+ `0.5.0-beta`.
33
+
34
+ ### Added
35
+
36
+ - **`<Table>`** — declarative-first data table component.
37
+ - **Smart defaults**: column types auto-detected from the first
38
+ non-null value across visible rows. Number columns get
39
+ `text-right` + `tabular-nums` (digit grid alignment), date
40
+ columns get `tabular-nums` too, booleans get `text-center`,
41
+ strings get `text-left`. **The library never changes the font
42
+ family** — `tabular-nums` is a font-feature setting that
43
+ preserves the consumer's theme `font-sans`. Explicit
44
+ `align` / `tabularNums` / `monospace` per column wins.
45
+ - **Sort**: per-column `sortable: true` (default comparator
46
+ handles string / number / Date / boolean / bigint with
47
+ null-last invariant) or custom `(a, b) => number`. Click
48
+ cycles asc → desc → none. **Shift-click** adds the column to
49
+ a multi-sort model. Controlled via `sortModel` /
50
+ `onSortChange` or uncontrolled with internal state.
51
+ - **Search**: `enableSearch` renders a debounced input above
52
+ the table (default 200 ms). Matches any column flagged
53
+ `searchable: true`. **Nested keys supported** at runtime via
54
+ `getNestedValue(row, 'address.city')` — the type-level
55
+ autocomplete from `NestedKeyOf<T>` matches the runtime
56
+ behavior. Stringification covers string / number / boolean /
57
+ Date / array / object.
58
+ - **Selection**: `rowSelection: 'none' | 'single' | 'multiple'`,
59
+ optional select-all checkbox in the header (multiple mode),
60
+ `selectedRowIds` controlled state, **sticky bulk-action
61
+ footer** rendered only when at least one row is selected
62
+ (Pencil & Paper UX pattern: "appear only when selected").
63
+ - **Expandable rows**: `expandable={{ render, expandedRowIds?,
64
+ onExpandChange? }}`. Chevron toggle in a dedicated column;
65
+ `aria-expanded` updates; expanded detail renders as a
66
+ full-width sibling `<tr>` below the row.
67
+ - **Row actions revealed on hover**: `rowActions={(row) => …}`
68
+ slot — by default hidden via opacity, revealed on
69
+ `tr:hover` / `tr:focus-within` (Stripe pattern — reduces
70
+ visual density while keeping actions discoverable).
71
+ - **RBAC at 3 levels**: table-level `access` (hides / disables
72
+ the whole table), per-column `cols[i].access` (`hide` removes
73
+ the column entirely from header AND every row cell), and
74
+ per-action `actions[i].access` on `<RowActionsMenu>`.
75
+ - **A11Y**: semantic `<table>` + `<th scope="col">` + `aria-sort`
76
+ on sortable headers + `aria-selected` on selected rows +
77
+ `aria-expanded` on expand toggles + `<caption>` (optional,
78
+ `sr-only` by default) + keyboard nav via Tab + Enter/Space.
79
+ - **Loading state**: `loading={true}` renders N `<Skeleton>` rows
80
+ (the Sprint 4 component) with `aria-busy="true"`. Count
81
+ configurable via `loadingRowCount` (default 5).
82
+ - **i18n**: column `header` accepts plain strings (pass
83
+ `t('...')`); all internal default strings configurable via
84
+ `labels` prop with English defaults (search placeholder, a11y
85
+ announcements for sort / select / expand, selection counter
86
+ with `{count}` placeholder, density / filter labels). Same
87
+ pattern as `<Pagination>`.
88
+ - **5 variants** (`plain` · `lines` default — Stripe-style ·
89
+ `striped` · `bordered` · `card`) × **3 sizes** (`sm` · `md` ·
90
+ `lg`) × **3 densities** (`compact` 40px · `comfortable`
91
+ default 48px · `spacious` 56px — Pencil & Paper UX research).
92
+ - **Sticky header** by default (overridable).
93
+ - **`sx` + 15 slot props** for the standard customization
94
+ escape hatches.
95
+
96
+ - **Cell renderer library** — pre-built renderers for common
97
+ patterns, exported from `@dashforge/tw`:
98
+ - `RenderText` — one-line, optionally truncated / muted
99
+ - `RenderTwoLine` — bold primary + muted secondary
100
+ (name + email pattern)
101
+ - `RenderChip` — internal status badge, 7 intent colors × 3
102
+ variants (soft / solid / outline) × 2 sizes (sm / md)
103
+ - `RenderButton` — inline button wrapper (defaults `ghost` +
104
+ `sm`)
105
+ - `RowActionsMenu` — 3-dot Popover-backed menu with per-action
106
+ RBAC
107
+
108
+ - **Helpers exported for power use**:
109
+ - `getNestedValue(row, path)` — dotted-path lookup powering
110
+ cell rendering. Useful in consumer custom renderers.
111
+
112
+ - **Theme identity regression guard** —
113
+ `_internal/themeIdentity.test.ts` scans every Table source file
114
+ and fails if `dark:*-neutral-N` classes are introduced. The
115
+ dashforgePreset auto-inverts the neutral palette via CSS var
116
+ swap; adding `dark:` variants on neutral creates double
117
+ inversion and breaks dark mode. The Sprint 4.1 fix removes the
118
+ anti-pattern from Table; **Sprint 4.3 will sweep the rest of
119
+ the catalog** (Typography, Box, etc. carry the same latent bug).
120
+
121
+ - **Smoke-test page** in `dash` consumer at `/test-table` —
122
+ exercises every Table feature with 30 realistic users + nested
123
+ meta + chip-rendered status + RBAC per-column + i18n labels in
124
+ Italian + 5 variants × 3 sizes × 3 densities switcher.
125
+
126
+ - **Doc MDX** `/tw/docs/components/table` — full feature catalog,
127
+ i18n example with `react-i18next`, override matrix
128
+ (align / tabularNums / monospace), 15+ slot props reference,
129
+ cell renderer library, A11Y guarantees, roadmap.
130
+
131
+ ### Internal
132
+
133
+ - **Theme identity rule** (now codified): the dashforgePreset
134
+ default IS the Dashforge visual identity. Consumer apps consume
135
+ it as-is and never override. Adding `dark:` variants on the
136
+ neutral palette = double inversion = breaks dark mode. Use
137
+ canonical patterns (LeftNav `itemActive` for selected,
138
+ auto-invert for `bg-neutral-*` and `text-neutral-*`).
139
+ - **Font family rule**: the library never picks a font family —
140
+ `tabular-nums` is safe to auto-apply (font-feature setting),
141
+ `font-mono` is consumer opt-in only via `col.monospace: true`.
142
+ The dashforgePreset does not own the `fontFamily` axis; the
143
+ consumer configures their mono stack in their own
144
+ `tailwind.config.ts` `theme.extend.fontFamily.mono`.
145
+ - **+147 new unit tests** for Table:
146
+ - `getNestedValue` (11) — nested keys, null-safety, zero / empty
147
+ string / false preservation
148
+ - `useTableSearch` (16) — stringification across primitive types
149
+ + nested keys
150
+ - `useTableSort` (15) — null-last invariant (direction-independent),
151
+ multi-column tie-breaking, custom comparator
152
+ - `useTableSelection` (13) — single / multiple / none modes
153
+ - `useColumnAutoDetect` (14) — type inference + align /
154
+ tabularNums / monospace resolution
155
+ - `themeIdentity` (18 file scanner) — regression guard for
156
+ `dark:` on neutral palette
157
+ - `Table.test.tsx` (57) — rendering, smart defaults, sort,
158
+ search, selection, expandable, row actions, RBAC, variants,
159
+ densities, sizes, sx + slotProps, i18n
160
+ - Full TW suite at **828/828 passing** (46 files; +147 from
161
+ Table, +newer guard).
162
+
163
+ ### Compatibility
164
+
165
+ | Axis | Pre-`0.6.0` | Post-`0.6.0` |
166
+ |---|---|---|
167
+ | Public API surface | 31 components | **+ 1 (`Table`)** + 5 cell renderers + `getNestedValue` helper + `Table*` types (`TableProps`, `TableColumn`, `TableSortModel`, `TableFilterModel`, `TableLabels`, `TableCellContext`, `NestedKeyOf`, `TableRowAction`, …) |
168
+ | Peer deps | `react ^18 \|\| ^19`, `tw-theme workspace`, `tw-tokens workspace` | unchanged |
169
+ | Bridge deps | `forms` / `rbac` / `ui-core` `workspace:*` | unchanged |
170
+ | New runtime deps | — | **none** (no `@tanstack/*`, no DnD libs — constraint honored) |
171
+ | Breaking changes | — | Zero |
172
+ | Bundle size | 336 KB raw / 73.9 KB gzipped | **402 KB raw / 91 KB gzipped** (+66 KB raw / +17 KB gz / **+23% gz**) |
173
+ | Migration | — | Drop-in. Zero code changes required on existing usages. |
174
+
175
+ > **Bundle regression note**: the +23% gz delta is **above the
176
+ > 10% reviewer-sign-off threshold** documented in
177
+ > `PERFORMANCE.md`. Justification:
178
+ > Table is the lib's central data-display primitive — it includes
179
+ > sort + search + selection + expandable + RBAC at 3 levels + 5
180
+ > cell renderers + RowActionsMenu (using Popover) + 5 variants ×
181
+ > 3 sizes × 3 densities. The bundle weight is proportional to
182
+ > the feature surface. Sign-off rationale: this is the
183
+ > "MUI X DataGrid Community alternative" component — the single
184
+ > highest-value addition before 1.0. Sprint 4.3 (theme identity
185
+ > sweep) will recover -1 to -3 KB gz by removing redundant
186
+ > `dark:` variants across the catalog.
187
+
188
+ ### Migration
189
+
190
+ No code changes required:
191
+
192
+ ```bash
193
+ pnpm up @dashforge/tw@^0.6.0-beta
194
+ ```
195
+
196
+ To adopt the new Table:
197
+
198
+ ```tsx
199
+ import { Table, RenderTwoLine, RenderChip, RowActionsMenu } from '@dashforge/tw';
200
+ import { useTranslation } from 'react-i18next';
201
+
202
+ function UsersTable({ users }) {
203
+ const { t } = useTranslation();
204
+ const [selected, setSelected] = useState<string[]>([]);
205
+
206
+ return (
207
+ <Table
208
+ rows={users}
209
+ cols={[
210
+ {
211
+ field: 'name',
212
+ header: t('users.fields.name'),
213
+ sortable: true,
214
+ searchable: true,
215
+ cellRenderer: ({ row }) => (
216
+ <RenderTwoLine primary={row.name} secondary={row.email} />
217
+ ),
218
+ },
219
+ {
220
+ field: 'salary',
221
+ header: t('users.fields.salary'),
222
+ sortable: true,
223
+ // Right-aligned + tabular-nums automatically. Font family
224
+ // stays whatever the consumer's theme provides — opt into
225
+ // `monospace: true` if you want font-mono explicitly.
226
+ access: {
227
+ resource: 'employee.salary',
228
+ action: 'read',
229
+ onUnauthorized: 'hide',
230
+ },
231
+ },
232
+ {
233
+ field: 'status',
234
+ header: t('users.fields.status'),
235
+ cellRenderer: ({ value }) => (
236
+ <RenderChip
237
+ color={value === 'active' ? 'success' : 'warning'}
238
+ >
239
+ {String(value)}
240
+ </RenderChip>
241
+ ),
242
+ },
243
+ ]}
244
+ getRowId={(r) => r.id}
245
+ enableSearch
246
+ rowSelection="multiple"
247
+ selectedRowIds={selected}
248
+ onSelectionChange={setSelected}
249
+ bulkActions={(rows) => (
250
+ <Button color="danger">
251
+ {t('actions.delete', { count: rows.length })}
252
+ </Button>
253
+ )}
254
+ rowActions={(row) => (
255
+ <RowActionsMenu
256
+ row={row}
257
+ actions={[
258
+ { label: t('actions.edit'), onClick: edit },
259
+ { label: t('actions.delete'), onClick: del, color: 'danger' },
260
+ ]}
261
+ />
262
+ )}
263
+ labels={{
264
+ searchPlaceholder: t('table.search'),
265
+ selectedCount: t('table.selected', { defaultValue: '{count} selected' }),
266
+ }}
267
+ />
268
+ );
269
+ }
270
+ ```
271
+
272
+ ### Heads-up — companion releases coming
273
+
274
+ - **Sprint 4.3 → `0.7.0-beta`**: theme identity audit across the
275
+ whole catalog (Typography, Box, etc. carry the same latent
276
+ `dark:` anti-pattern Table just fixed). Expected bundle delta:
277
+ **−1 to −3 KB gz** (removing redundant classes).
278
+ - **Sprint 4.2 → `0.8.0-beta`**: DataGrid with homemade
279
+ virtualization (`IntersectionObserver`-based, no new deps) for
280
+ 10k+ row data sets, advanced filter model, sticky columns,
281
+ per-column RBAC.
282
+
15
283
  ## [0.5.0-beta] — 2026-05-19
16
284
 
17
285
  **Sprint 4 release.** Two TW-only utility primitives — `<Skeleton>`