@dashforge/tw 0.4.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 (87) hide show
  1. package/CHANGELOG.md +391 -0
  2. package/dist/index.esm.js +2113 -2
  3. package/dist/src/components/Pagination/Pagination.d.ts +32 -0
  4. package/dist/src/components/Pagination/Pagination.d.ts.map +1 -0
  5. package/dist/src/components/Pagination/pagination.helpers.d.ts +23 -0
  6. package/dist/src/components/Pagination/pagination.helpers.d.ts.map +1 -0
  7. package/dist/src/components/Pagination/pagination.types.d.ts +120 -0
  8. package/dist/src/components/Pagination/pagination.types.d.ts.map +1 -0
  9. package/dist/src/components/Pagination/pagination.variants.d.ts +150 -0
  10. package/dist/src/components/Pagination/pagination.variants.d.ts.map +1 -0
  11. package/dist/src/components/Skeleton/Skeleton.d.ts +23 -0
  12. package/dist/src/components/Skeleton/Skeleton.d.ts.map +1 -0
  13. package/dist/src/components/Skeleton/skeleton.types.d.ts +56 -0
  14. package/dist/src/components/Skeleton/skeleton.types.d.ts.map +1 -0
  15. package/dist/src/components/Skeleton/skeleton.variants.d.ts +110 -0
  16. package/dist/src/components/Skeleton/skeleton.variants.d.ts.map +1 -0
  17. package/dist/src/components/Table/Table.d.ts +24 -0
  18. package/dist/src/components/Table/Table.d.ts.map +1 -0
  19. package/dist/src/components/Table/_internal/getNestedValue.d.ts +20 -0
  20. package/dist/src/components/Table/_internal/getNestedValue.d.ts.map +1 -0
  21. package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts +45 -0
  22. package/dist/src/components/Table/_internal/useColumnAutoDetect.d.ts.map +1 -0
  23. package/dist/src/components/Table/_internal/useControllableState.d.ts +23 -0
  24. package/dist/src/components/Table/_internal/useControllableState.d.ts.map +1 -0
  25. package/dist/src/components/Table/_internal/useDebouncedValue.d.ts +10 -0
  26. package/dist/src/components/Table/_internal/useDebouncedValue.d.ts.map +1 -0
  27. package/dist/src/components/Table/_internal/useTableFilter.d.ts +13 -0
  28. package/dist/src/components/Table/_internal/useTableFilter.d.ts.map +1 -0
  29. package/dist/src/components/Table/_internal/useTableSearch.d.ts +23 -0
  30. package/dist/src/components/Table/_internal/useTableSearch.d.ts.map +1 -0
  31. package/dist/src/components/Table/_internal/useTableSelection.d.ts +25 -0
  32. package/dist/src/components/Table/_internal/useTableSelection.d.ts.map +1 -0
  33. package/dist/src/components/Table/_internal/useTableSort.d.ts +20 -0
  34. package/dist/src/components/Table/_internal/useTableSort.d.ts.map +1 -0
  35. package/dist/src/components/Table/cells/RenderButton.d.ts +13 -0
  36. package/dist/src/components/Table/cells/RenderButton.d.ts.map +1 -0
  37. package/dist/src/components/Table/cells/RenderChip.d.ts +80 -0
  38. package/dist/src/components/Table/cells/RenderChip.d.ts.map +1 -0
  39. package/dist/src/components/Table/cells/RenderText.d.ts +21 -0
  40. package/dist/src/components/Table/cells/RenderText.d.ts.map +1 -0
  41. package/dist/src/components/Table/cells/RenderTwoLine.d.ts +20 -0
  42. package/dist/src/components/Table/cells/RenderTwoLine.d.ts.map +1 -0
  43. package/dist/src/components/Table/cells/RowActionsMenu.d.ts +36 -0
  44. package/dist/src/components/Table/cells/RowActionsMenu.d.ts.map +1 -0
  45. package/dist/src/components/Table/cells/index.d.ts +11 -0
  46. package/dist/src/components/Table/cells/index.d.ts.map +1 -0
  47. package/dist/src/components/Table/table.types.d.ts +304 -0
  48. package/dist/src/components/Table/table.types.d.ts.map +1 -0
  49. package/dist/src/components/Table/table.variants.d.ts +285 -0
  50. package/dist/src/components/Table/table.variants.d.ts.map +1 -0
  51. package/dist/src/index.d.ts +13 -1
  52. package/dist/src/index.d.ts.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/Pagination/Pagination.test.tsx +314 -0
  55. package/src/components/Pagination/Pagination.tsx +275 -0
  56. package/src/components/Pagination/pagination.helpers.ts +102 -0
  57. package/src/components/Pagination/pagination.types.ts +117 -0
  58. package/src/components/Pagination/pagination.variants.ts +111 -0
  59. package/src/components/Skeleton/Skeleton.test.tsx +127 -0
  60. package/src/components/Skeleton/Skeleton.tsx +49 -0
  61. package/src/components/Skeleton/skeleton.types.ts +59 -0
  62. package/src/components/Skeleton/skeleton.variants.ts +86 -0
  63. package/src/components/Table/Table.test.tsx +603 -0
  64. package/src/components/Table/Table.tsx +816 -0
  65. package/src/components/Table/_internal/getNestedValue.test.ts +50 -0
  66. package/src/components/Table/_internal/getNestedValue.ts +32 -0
  67. package/src/components/Table/_internal/themeIdentity.test.ts +84 -0
  68. package/src/components/Table/_internal/useColumnAutoDetect.test.ts +123 -0
  69. package/src/components/Table/_internal/useColumnAutoDetect.ts +100 -0
  70. package/src/components/Table/_internal/useControllableState.ts +48 -0
  71. package/src/components/Table/_internal/useDebouncedValue.ts +24 -0
  72. package/src/components/Table/_internal/useTableFilter.ts +37 -0
  73. package/src/components/Table/_internal/useTableSearch.test.ts +115 -0
  74. package/src/components/Table/_internal/useTableSearch.ts +66 -0
  75. package/src/components/Table/_internal/useTableSelection.test.ts +100 -0
  76. package/src/components/Table/_internal/useTableSelection.ts +100 -0
  77. package/src/components/Table/_internal/useTableSort.test.ts +150 -0
  78. package/src/components/Table/_internal/useTableSort.ts +141 -0
  79. package/src/components/Table/cells/RenderButton.tsx +23 -0
  80. package/src/components/Table/cells/RenderChip.tsx +103 -0
  81. package/src/components/Table/cells/RenderText.tsx +37 -0
  82. package/src/components/Table/cells/RenderTwoLine.tsx +34 -0
  83. package/src/components/Table/cells/RowActionsMenu.tsx +147 -0
  84. package/src/components/Table/cells/index.ts +14 -0
  85. package/src/components/Table/table.types.ts +337 -0
  86. package/src/components/Table/table.variants.ts +207 -0
  87. package/src/index.ts +60 -1
package/CHANGELOG.md CHANGED
@@ -12,6 +12,397 @@ 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
+
283
+ ## [0.5.0-beta] — 2026-05-19
284
+
285
+ **Sprint 4 release.** Two TW-only utility primitives — `<Skeleton>`
286
+ and `<Pagination>` — that were missing from the catalog and are
287
+ prerequisite visuals for the upcoming Sprint 4.1 (Table + DataGrid).
288
+ Both are pure-UI, no bridge integration, no RBAC sensible — they
289
+ exist to compose with the data layer arriving next sprint.
290
+
291
+ **MUI side note.** Neither component ships in `@dashforge/ui`:
292
+ `@mui/material/Skeleton` and `@mui/material/Pagination` already
293
+ cover the bridge-free use case fully. Per the Dashforge design rule
294
+ (`@dashforge/ui` wraps MUI only when adding bridge / RBAC /
295
+ validation / custom-behavior value), MUI consumers reach for the
296
+ upstream components directly.
297
+
298
+ **Minor bump** for 2 new public exports. Strictly additive — zero
299
+ breaking changes. Drop-in upgrade from `0.4.0-beta`.
300
+
301
+ ### Added
302
+
303
+ - **`<Skeleton>`** — loading placeholder primitive.
304
+ Three shapes (`text` default · `rectangle` · `circle`), three
305
+ animations (`pulse` default · `wave` · `none`). Width / height as
306
+ inline CSS lengths; circle's height defaults to its width.
307
+ Rendered as `<span aria-hidden="true" role="presentation">` — screen
308
+ readers skip it; the surrounding container is responsible for
309
+ `aria-busy` / `aria-live` announcements. **WCAG 2.3.3**: both
310
+ animations gated on `prefers-reduced-motion: reduce`. Compose
311
+ multiple `<Skeleton>`s to mimic card / row shapes during fetch.
312
+ 19 unit tests covering variants × animations × sizing × sx +
313
+ slotProps override + a11y attributes.
314
+ - **`<Pagination>`** — controlled pagination primitive. Three
315
+ variants:
316
+ - `default` — summary + page numbers + first/prev/next/last +
317
+ page-size selector + direct jump input
318
+ - `compact` — page numbers + nav buttons only
319
+ - `minimal` — "Page X of Y" + prev/next (mobile-friendly)
320
+ Three sizes (`sm` / `md` / `lg`). Configurable
321
+ `siblingCount` / `boundaryCount` for the ellipsis range
322
+ algorithm — small totals (≤ `2·boundary + 2·sibling + 3` pages)
323
+ short-circuit to the full range, no ellipsis. Full i18n via
324
+ `labels` prop (English defaults). A11Y: `<nav aria-label="Pagination">`
325
+ landmark, `aria-current="page"` on the active button, native
326
+ `<select>` for page size, native `<input type="number">` for
327
+ the jump input (commit on Enter / blur, clamped to range).
328
+ Page-size selector hidden when `onPageSizeChange` is omitted.
329
+ Jump input toggle via `showJumpInput`. 28 unit tests covering
330
+ the range helper (7) + rendering / interactions / variants /
331
+ i18n / disabled / overrides / edge cases (21).
332
+
333
+ ### Internal
334
+
335
+ - **47 new unit tests** for the 2 components — full TW suite at
336
+ **681/681 passing** (39 files).
337
+ - **`TestUtilities.tsx`** added to the `dash` consumer
338
+ (`/test-utilities`) as the Sprint 4 smoke-test page: variant
339
+ matrix for both components + card-placeholder compose pattern +
340
+ Pagination i18n / size / variant / edge cases + a 50-instance
341
+ Skeleton stress test wrapped in `React.Profiler`. Validated
342
+ end-to-end before docs were written (per Dashforge workflow
343
+ policy: dash smoke test PRECEDES docs).
344
+ - **Sidebar entries** for the new `Skeleton` + `Pagination` doc
345
+ pages added to `dashforge-docs-lab/src/tw-docs/sidebar.model.ts`.
346
+
347
+ ### Compatibility
348
+
349
+ | Axis | Pre-`0.5.0` | Post-`0.5.0` |
350
+ |---|---|---|
351
+ | Public API surface | 29 components | **+ 2 (`Skeleton`, `Pagination`)** + their `*Props` / `*SlotProps` types + `*Variants` recipes + `PaginationLabels` i18n type |
352
+ | Peer deps | `react ^18 \|\| ^19`, `tw-theme workspace`, `tw-tokens workspace` | unchanged |
353
+ | Bridge deps | `forms` / `rbac` / `ui-core` `workspace:*` | unchanged |
354
+ | New runtime deps | — | **none** (no new external libraries — constraint honored) |
355
+ | Breaking changes | — | Zero |
356
+ | Bundle size | 312 KB raw / 68.85 KB gzipped | **336 KB raw / 73.9 KB gzipped** (+24 KB raw / +5.05 KB gz / **+7.3% gz**) |
357
+ | Migration | — | Drop-in. Zero code changes required on existing usages. |
358
+
359
+ > **Bundle regression note**: the +7.3% gzipped delta is **above
360
+ > the 5% threshold** documented in `PERFORMANCE.md`. Justification:
361
+ > Pagination contributes ~4 KB gz (3 variants × 3 sizes × i18n
362
+ > surface + range-computation helper + jump-input commit logic);
363
+ > Skeleton contributes ~1 KB gz. The delta sits well under the
364
+ > 10% reviewer-sign-off threshold and is the necessary cost of
365
+ > adding two new public components. Filed as informational per
366
+ > the regression policy.
367
+
368
+ ### Migration
369
+
370
+ No code changes required:
371
+
372
+ ```bash
373
+ pnpm up @dashforge/tw@^0.5.0-beta
374
+ ```
375
+
376
+ To adopt the new components:
377
+
378
+ ```tsx
379
+ import { Skeleton, Pagination } from '@dashforge/tw';
380
+
381
+ // Loading placeholder while fetching
382
+ {isLoading ? (
383
+ <Skeleton variant="text" width="200px" />
384
+ ) : (
385
+ <UserName>{user.name}</UserName>
386
+ )}
387
+
388
+ // Paginated list (controlled — wire to your data slice)
389
+ const [page, setPage] = useState(1);
390
+ const [pageSize, setPageSize] = useState(20);
391
+
392
+ <Pagination
393
+ page={page}
394
+ pageSize={pageSize}
395
+ totalCount={users.length}
396
+ onPageChange={setPage}
397
+ onPageSizeChange={setPageSize}
398
+ />
399
+ ```
400
+
401
+ MUI consumers: continue using `@mui/material/Skeleton` and
402
+ `@mui/material/Pagination` directly. `@dashforge/ui` will NOT ship
403
+ wrappers for these (per the design rule documented in
404
+ `PARITY.md` — no value-add wrappers).
405
+
15
406
  ## [0.4.0-beta] — 2026-05-19
16
407
 
17
408
  **Sprint 3 release.** Five new Tier-4 overlay & disclosure