@acronis-platform/ui-react 2.0.0 → 4.0.0

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 (243) hide show
  1. package/dist/components/ui/accordion-container/accordion-container.js +144 -0
  2. package/dist/components/ui/accordion-container/accordion-container.js.map +1 -0
  3. package/dist/components/ui/app-shell-chat/app-shell-chat.js.map +1 -1
  4. package/dist/components/ui/area-chart/area-chart.js +141 -139
  5. package/dist/components/ui/area-chart/area-chart.js.map +1 -1
  6. package/dist/components/ui/avatar/avatar.js +33 -22
  7. package/dist/components/ui/avatar/avatar.js.map +1 -1
  8. package/dist/components/ui/bar-chart/bar-chart.js +295 -293
  9. package/dist/components/ui/bar-chart/bar-chart.js.map +1 -1
  10. package/dist/components/ui/card/card.js +150 -74
  11. package/dist/components/ui/card/card.js.map +1 -1
  12. package/dist/components/ui/card-section/card-section.js +111 -0
  13. package/dist/components/ui/card-section/card-section.js.map +1 -0
  14. package/dist/components/ui/category-bar/category-bar.js +109 -93
  15. package/dist/components/ui/category-bar/category-bar.js.map +1 -1
  16. package/dist/components/ui/chart/chart-palette.js +113 -0
  17. package/dist/components/ui/chart/chart-palette.js.map +1 -0
  18. package/dist/components/ui/chart/chart.js +176 -129
  19. package/dist/components/ui/chart/chart.js.map +1 -1
  20. package/dist/components/ui/chart-state/chart-state-illustrations.js +310 -0
  21. package/dist/components/ui/chart-state/chart-state-illustrations.js.map +1 -0
  22. package/dist/components/ui/chart-state/chart-state.js +48 -34
  23. package/dist/components/ui/chart-state/chart-state.js.map +1 -1
  24. package/dist/components/ui/chart-widget/chart-widget.js +87 -0
  25. package/dist/components/ui/chart-widget/chart-widget.js.map +1 -0
  26. package/dist/components/ui/composed-chart/composed-chart.js +117 -115
  27. package/dist/components/ui/composed-chart/composed-chart.js.map +1 -1
  28. package/dist/components/ui/confidence-cone/confidence-cone.js +166 -158
  29. package/dist/components/ui/confidence-cone/confidence-cone.js.map +1 -1
  30. package/dist/components/ui/data-table/data-table.js +4 -2
  31. package/dist/components/ui/data-table/data-table.js.map +1 -1
  32. package/dist/components/ui/empty-overlay/empty-overlay.js +49 -0
  33. package/dist/components/ui/empty-overlay/empty-overlay.js.map +1 -0
  34. package/dist/components/ui/funnel-chart/funnel-chart.js +162 -161
  35. package/dist/components/ui/funnel-chart/funnel-chart.js.map +1 -1
  36. package/dist/components/ui/histogram/histogram.js +94 -92
  37. package/dist/components/ui/histogram/histogram.js.map +1 -1
  38. package/dist/components/ui/input/input.js.map +1 -1
  39. package/dist/components/ui/label/label.js +2 -9
  40. package/dist/components/ui/label/label.js.map +1 -1
  41. package/dist/components/ui/line-chart/line-chart.js +143 -141
  42. package/dist/components/ui/line-chart/line-chart.js.map +1 -1
  43. package/dist/components/ui/metric/metric.js +74 -185
  44. package/dist/components/ui/metric/metric.js.map +1 -1
  45. package/dist/components/ui/page-content/page-content.js.map +1 -1
  46. package/dist/components/ui/pie-chart/pie-chart.js +201 -195
  47. package/dist/components/ui/pie-chart/pie-chart.js.map +1 -1
  48. package/dist/components/ui/radar-chart/radar-chart.js +133 -131
  49. package/dist/components/ui/radar-chart/radar-chart.js.map +1 -1
  50. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js +297 -265
  51. package/dist/components/ui/radial-bar-chart/radial-bar-chart.js.map +1 -1
  52. package/dist/components/ui/sankey-chart/sankey-chart.js +123 -101
  53. package/dist/components/ui/sankey-chart/sankey-chart.js.map +1 -1
  54. package/dist/components/ui/scatter-chart/scatter-chart.js +76 -74
  55. package/dist/components/ui/scatter-chart/scatter-chart.js.map +1 -1
  56. package/dist/components/ui/section/section.js +157 -31
  57. package/dist/components/ui/section/section.js.map +1 -1
  58. package/dist/components/ui/stepper/stepper.js +24 -24
  59. package/dist/components/ui/stepper/stepper.js.map +1 -1
  60. package/dist/components/ui/stepper-item/stepper-item.js +43 -24
  61. package/dist/components/ui/stepper-item/stepper-item.js.map +1 -1
  62. package/dist/components/ui/table/table.js +27 -27
  63. package/dist/components/ui/table/table.js.map +1 -1
  64. package/dist/components/ui/treemap/treemap.js +133 -124
  65. package/dist/components/ui/treemap/treemap.js.map +1 -1
  66. package/dist/components/ui/trend-indicator/trend-indicator.js +34 -69
  67. package/dist/components/ui/trend-indicator/trend-indicator.js.map +1 -1
  68. package/dist/components/ui/wizard/wizard.js +59 -0
  69. package/dist/components/ui/wizard/wizard.js.map +1 -0
  70. package/dist/hooks/use-wizard.js +75 -0
  71. package/dist/hooks/use-wizard.js.map +1 -0
  72. package/dist/index.js +540 -525
  73. package/dist/index.js.map +1 -1
  74. package/dist/llms/accordion-container.md +181 -0
  75. package/dist/llms/accordion.md +124 -0
  76. package/dist/llms/alert.md +225 -0
  77. package/dist/llms/app-shell-chat.md +415 -0
  78. package/dist/llms/area-chart.md +355 -0
  79. package/dist/llms/avatar.md +208 -0
  80. package/dist/llms/bar-chart.md +406 -0
  81. package/dist/llms/breadcrumb.md +326 -0
  82. package/dist/llms/button-group.md +298 -0
  83. package/dist/llms/button-icon-input.md +142 -0
  84. package/dist/llms/button-icon-menu.md +209 -0
  85. package/dist/llms/button-icon.md +128 -0
  86. package/dist/llms/button-menu.md +172 -0
  87. package/dist/llms/button.md +185 -0
  88. package/dist/llms/calendar-panel.md +733 -0
  89. package/dist/llms/calendar.md +188 -0
  90. package/dist/llms/card-filter.md +214 -0
  91. package/dist/llms/card-section.md +293 -0
  92. package/dist/llms/card.md +447 -0
  93. package/dist/llms/carousel-dialog.md +156 -0
  94. package/dist/llms/category-bar.md +193 -0
  95. package/dist/llms/chart-state.md +190 -0
  96. package/dist/llms/chart-widget.md +209 -0
  97. package/dist/llms/chart.md +182 -0
  98. package/dist/llms/checkbox.md +257 -0
  99. package/dist/llms/chip.md +195 -0
  100. package/dist/llms/combobox.md +164 -0
  101. package/dist/llms/composed-chart.md +508 -0
  102. package/dist/llms/confidence-cone.md +277 -0
  103. package/dist/llms/data-table.md +474 -0
  104. package/dist/llms/date-range-picker.md +272 -0
  105. package/dist/llms/description-list.md +131 -0
  106. package/dist/llms/dialog-footer-carousel.md +147 -0
  107. package/dist/llms/dialog-footer-default.md +142 -0
  108. package/dist/llms/dialog-root.md +246 -0
  109. package/dist/llms/dialog-welcome.md +223 -0
  110. package/dist/llms/dialog.md +403 -0
  111. package/dist/llms/dropdown-menu.md +193 -0
  112. package/dist/llms/empty-overlay.md +146 -0
  113. package/dist/llms/empty.md +156 -0
  114. package/dist/llms/field.md +185 -0
  115. package/dist/llms/filter-cards.md +157 -0
  116. package/dist/llms/filter-chips.md +323 -0
  117. package/dist/llms/filter-search.md +512 -0
  118. package/dist/llms/form.md +154 -0
  119. package/dist/llms/funnel-chart.md +333 -0
  120. package/dist/llms/grid.md +80 -0
  121. package/dist/llms/histogram.md +190 -0
  122. package/dist/llms/input-date-picker.md +203 -0
  123. package/dist/llms/input-otp.md +173 -0
  124. package/dist/llms/input-password.md +197 -0
  125. package/dist/llms/input-search.md +170 -0
  126. package/dist/llms/input-select.md +278 -0
  127. package/dist/llms/input-text-area.md +271 -0
  128. package/dist/llms/input-text.md +192 -0
  129. package/dist/llms/line-chart.md +369 -0
  130. package/dist/llms/link.md +209 -0
  131. package/dist/llms/loading.md +130 -0
  132. package/dist/llms/meter.md +177 -0
  133. package/dist/llms/metric.md +204 -0
  134. package/dist/llms/number-field.md +122 -0
  135. package/dist/llms/page-content.md +69 -0
  136. package/dist/llms/page-header.md +182 -0
  137. package/dist/llms/pagination.md +133 -0
  138. package/dist/llms/pie-chart.md +307 -0
  139. package/dist/llms/popover.md +252 -0
  140. package/dist/llms/progress-circle.md +126 -0
  141. package/dist/llms/progress.md +111 -0
  142. package/dist/llms/radar-chart.md +316 -0
  143. package/dist/llms/radial-bar-chart.md +319 -0
  144. package/dist/llms/radio.md +255 -0
  145. package/dist/llms/resizable.md +195 -0
  146. package/dist/llms/sankey-chart.md +208 -0
  147. package/dist/llms/scatter-chart.md +239 -0
  148. package/dist/llms/scroll-area.md +183 -0
  149. package/dist/llms/section.md +401 -0
  150. package/dist/llms/select.md +210 -0
  151. package/dist/llms/separator.md +87 -0
  152. package/dist/llms/sheet.md +202 -0
  153. package/dist/llms/sidebar-primary.md +360 -0
  154. package/dist/llms/sidebar-secondary.md +417 -0
  155. package/dist/llms/skeleton.md +81 -0
  156. package/dist/llms/slider.md +97 -0
  157. package/dist/llms/spinner.md +100 -0
  158. package/dist/llms/stack.md +80 -0
  159. package/dist/llms/stepper-item.md +309 -0
  160. package/dist/llms/stepper.md +251 -0
  161. package/dist/llms/switch.md +140 -0
  162. package/dist/llms/table.md +436 -0
  163. package/dist/llms/tabs.md +197 -0
  164. package/dist/llms/tag.md +139 -0
  165. package/dist/llms/timeline.md +361 -0
  166. package/dist/llms/timer.md +259 -0
  167. package/dist/llms/toast.md +241 -0
  168. package/dist/llms/toggle-group.md +121 -0
  169. package/dist/llms/toolbar.md +453 -0
  170. package/dist/llms/tooltip.md +164 -0
  171. package/dist/llms/treemap.md +254 -0
  172. package/dist/llms/trend-indicator.md +132 -0
  173. package/dist/llms/widget-placeholder.md +169 -0
  174. package/dist/llms/wizard.md +412 -0
  175. package/dist/llms.txt +142 -0
  176. package/dist/react.js +540 -525
  177. package/dist/react.js.map +1 -1
  178. package/dist/src/components/ui/accordion-container/accordion-container.d.ts +44 -0
  179. package/dist/src/components/ui/accordion-container/index.d.ts +1 -0
  180. package/dist/src/components/ui/area-chart/area-chart.d.ts +9 -4
  181. package/dist/src/components/ui/avatar/avatar.d.ts +27 -8
  182. package/dist/src/components/ui/bar-chart/bar-chart.d.ts +9 -4
  183. package/dist/src/components/ui/button/button.d.ts +1 -1
  184. package/dist/src/components/ui/card/card.d.ts +69 -8
  185. package/dist/src/components/ui/card/index.d.ts +1 -1
  186. package/dist/src/components/ui/card-section/card-section.d.ts +50 -0
  187. package/dist/src/components/ui/card-section/index.d.ts +1 -0
  188. package/dist/src/components/ui/category-bar/category-bar.d.ts +10 -4
  189. package/dist/src/components/ui/chart/__stories__/palette-control.d.ts +48 -0
  190. package/dist/src/components/ui/chart/__tests__/chart-palette.test.d.ts +1 -0
  191. package/dist/src/components/ui/chart/chart-palette.d.ts +128 -0
  192. package/dist/src/components/ui/chart/chart.d.ts +66 -15
  193. package/dist/src/components/ui/chart/index.d.ts +4 -2
  194. package/dist/src/components/ui/chart-state/chart-state-illustrations.d.ts +6 -0
  195. package/dist/src/components/ui/chart-state/chart-state.d.ts +13 -2
  196. package/dist/src/components/ui/chart-state/index.d.ts +1 -0
  197. package/dist/src/components/ui/chart-widget/chart-widget.d.ts +64 -0
  198. package/dist/src/components/ui/chart-widget/index.d.ts +1 -0
  199. package/dist/src/components/ui/composed-chart/composed-chart.d.ts +12 -7
  200. package/dist/src/components/ui/confidence-cone/confidence-cone.d.ts +10 -5
  201. package/dist/src/components/ui/dialog/dialog.d.ts +1 -1
  202. package/dist/src/components/ui/empty-overlay/empty-overlay.d.ts +20 -0
  203. package/dist/src/components/ui/empty-overlay/index.d.ts +2 -0
  204. package/dist/src/components/ui/funnel-chart/funnel-chart.d.ts +11 -5
  205. package/dist/src/components/ui/grid/grid.d.ts +1 -1
  206. package/dist/src/components/ui/histogram/histogram.d.ts +10 -4
  207. package/dist/src/components/ui/label/index.d.ts +1 -1
  208. package/dist/src/components/ui/label/label.d.ts +1 -5
  209. package/dist/src/components/ui/line-chart/line-chart.d.ts +9 -4
  210. package/dist/src/components/ui/metric/metric.d.ts +13 -14
  211. package/dist/src/components/ui/pie-chart/pie-chart.d.ts +17 -8
  212. package/dist/src/components/ui/radar-chart/radar-chart.d.ts +10 -5
  213. package/dist/src/components/ui/radial-bar-chart/radial-bar-chart.d.ts +17 -6
  214. package/dist/src/components/ui/sankey-chart/sankey-chart.d.ts +10 -5
  215. package/dist/src/components/ui/scatter-chart/scatter-chart.d.ts +10 -5
  216. package/dist/src/components/ui/section/index.d.ts +1 -1
  217. package/dist/src/components/ui/section/section.d.ts +75 -6
  218. package/dist/src/components/ui/sheet/sheet.d.ts +1 -1
  219. package/dist/src/components/ui/stack/stack.d.ts +1 -1
  220. package/dist/src/components/ui/stepper-item/stepper-item.d.ts +6 -5
  221. package/dist/src/components/ui/tag/tag.d.ts +1 -1
  222. package/dist/src/components/ui/treemap/treemap.d.ts +11 -5
  223. package/dist/src/components/ui/trend-indicator/trend-indicator.d.ts +3 -7
  224. package/dist/src/components/ui/wizard/index.d.ts +1 -0
  225. package/dist/src/components/ui/wizard/wizard.d.ts +6 -0
  226. package/dist/src/hooks/index.d.ts +1 -0
  227. package/dist/src/hooks/use-wizard.d.ts +80 -0
  228. package/dist/src/index.d.ts +5 -3
  229. package/dist/ui-react.css +1 -1
  230. package/package.json +12 -6
  231. package/dist/components/ui/app-shell/app-shell.js +0 -75
  232. package/dist/components/ui/app-shell/app-shell.js.map +0 -1
  233. package/dist/components/ui/auth-layout/auth-layout.js +0 -56
  234. package/dist/components/ui/auth-layout/auth-layout.js.map +0 -1
  235. package/dist/components/ui/collapsible/collapsible.js +0 -22
  236. package/dist/components/ui/collapsible/collapsible.js.map +0 -1
  237. package/dist/src/components/ui/app-shell/app-shell.d.ts +0 -8
  238. package/dist/src/components/ui/app-shell/index.d.ts +0 -1
  239. package/dist/src/components/ui/auth-layout/auth-layout.d.ts +0 -6
  240. package/dist/src/components/ui/auth-layout/index.d.ts +0 -1
  241. package/dist/src/components/ui/collapsible/collapsible.d.ts +0 -5
  242. package/dist/src/components/ui/collapsible/index.d.ts +0 -1
  243. package/dist/src/components/ui/label/label.docs.d.ts +0 -10
@@ -0,0 +1,436 @@
1
+ # Table
2
+
3
+ > Displays rows and columns of data. A set of composable parts (header, body, footer, row, column header, cell, caption) with sortable column headers, a selected row state, and wrappable cells, plus two TanStack-independent companions — a pagination bar (TablePagination) and a show/hide-columns menu (TableViewOptions) — and two headless hooks (useSortState, useTableUrlState). Ported from the legacy shadcn-uikit `Table` and informed by the pre-release Table design; themed by the `--ui-table-*` token tier. The TanStack-backed data grid (`DataTable`) composes these primitives.
4
+
5
+ **Status:** draft · **Category:** data-display · **Since:** v0.10.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Table, TableHeader, TableBody, TableFooter, TableRow, TableHead, TableCell, TableSelectCell, TableActionsCell, TableSettingsCell, TableCaption, TablePagination, TableViewOptions, useSortState, useTableUrlState } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <Table>
17
+ <TableHeader>
18
+ <TableRow>
19
+ <TableHead sortable sortDirection={dir} onSort={toggleSort}>
20
+ Name
21
+ </TableHead>
22
+ <TableHead>Status</TableHead>
23
+ </TableRow>
24
+ </TableHeader>
25
+ <TableBody>
26
+ <TableRow selected={isSelected}>
27
+ <TableCell>web-server-01</TableCell>
28
+ <TableCell wrap>{longDescription}</TableCell>
29
+ <TableCell><Tag>Protected</Tag></TableCell>
30
+ </TableRow>
31
+ </TableBody>
32
+ </Table>
33
+ <TablePagination
34
+ pageIndex={pageIndex}
35
+ pageCount={pageCount}
36
+ pageSize={pageSize}
37
+ onPageIndexChange={setPageIndex}
38
+ onPageSizeChange={setPageSize}
39
+ />
40
+ ```
41
+
42
+ Displays rows and columns of data. Composable from native table parts, with
43
+ sortable column headers, a selected row state, wrappable cells, fixed-width
44
+ leading selection and trailing actions/settings cells, and — as
45
+ TanStack-independent companions — a pagination bar and a show/hide-columns
46
+ menu. Two headless hooks (`useSortState`, `useTableUrlState`) supply optional
47
+ sort and URL-synced state.
48
+
49
+ > **Status: draft (design-pending v1).** Ported from the legacy
50
+ > `@acronis-platform/shadcn-uikit` `Table` and informed by the pre-release Table
51
+ > design (shadcn-uikit Figma, node 2948-2416). Themed by the existing
52
+ > `--ui-table-*` token tier. The TanStack-backed **`DataTable`** (sorting,
53
+ > selection, pagination over these primitives) now ships alongside — also draft
54
+ > — and composes these parts. Reconcile with
55
+ > `/figma-component Table <url> --update` once the design is ready for dev.
56
+
57
+ ## When to use
58
+
59
+ - Showing structured, comparable data in rows and columns (lists of workloads,
60
+ invoices, devices, …).
61
+ - When you need sortable columns and/or row selection on top of native table
62
+ semantics.
63
+ - When you want the pagination bar or the show/hide-columns menu without pulling
64
+ in `@tanstack/react-table` — pair the parts with your own or the
65
+ `useSortState` / `useTableUrlState` hooks.
66
+
67
+ ## When not to use
68
+
69
+ - For a full data grid whose state (sorting/filtering/pagination/selection/
70
+ column resizing/sticky columns) is managed for you — use the `DataTable`,
71
+ which composes these primitives.
72
+ - For non-tabular layout — use CSS grid/flex, not a table.
73
+
74
+ ## Parts
75
+
76
+ | Part | Element | Purpose |
77
+ | ------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------- |
78
+ | `Table` | `table` | The table, in a horizontally scrollable container. |
79
+ | `TableHeader` | `thead` | Column-header section. |
80
+ | `TableBody` | `tbody` | Data rows section. |
81
+ | `TableFooter` | `tfoot` | Summary section with a top divider. |
82
+ | `TableRow` | `tr` | A row; `selected` applies the active state. |
83
+ | `TableHead` | `th` | Column header; `sortable` + `sortDirection` + `onSort`; `wrap`. |
84
+ | `TableCell` | `td` | A data cell; `wrap` lets it grow to fit multi-line content. |
85
+ | `TableSelectCell` | `td`/`th` | 32px leading selection cell for a `Checkbox`; `header` renders the select-all `th`. |
86
+ | `TableActionsCell` | `td` | 48px trailing cell for a row's overflow trigger; owns the hover/press tint and focus ring. |
87
+ | `TableSettingsCell` | `th` | 48px trailing header cell for the column-settings trigger. |
88
+ | `TableCaption` | `caption` | Optional caption below the table. |
89
+ | `TablePagination` | `div` | Plain-prop pagination bar (no TanStack dependency). |
90
+ | `TableViewOptions` | `div` | Show/hide-columns dropdown driven by a plain columns array. `iconOnly` gives the cog trigger that fits `TableSettingsCell`. |
91
+
92
+ ## Hooks
93
+
94
+ Headless helpers for the `Table` primitives — no TanStack dependency, no visual
95
+ output. They drive the parts' props and own the state.
96
+
97
+ | Hook | Purpose |
98
+ | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
99
+ | `useSortState` | Client-side single-column sort. Returns `sortedData` + `getSortDirection` / `toggleSort` shaped for `TableHead`. |
100
+ | `useTableUrlState` | Syncs pagination / sorting / column-filter state to and from the URL query string (namespaced `tbl_*` keys) via `history.pushState` + `popstate` — router-agnostic. |
101
+
102
+ ## Examples
103
+
104
+ ```tsx
105
+ import {
106
+ Table,
107
+ TableHeader,
108
+ TableBody,
109
+ TableRow,
110
+ TableHead,
111
+ TableCell,
112
+ TableSelectCell,
113
+ TableActionsCell,
114
+ TableSettingsCell,
115
+ TablePagination,
116
+ TableViewOptions,
117
+ ButtonIcon,
118
+ Checkbox,
119
+ Tag,
120
+ } from '@acronis-platform/ui-react';
121
+ import { useSortState } from '@acronis-platform/ui-react'; // headless hook
122
+ import { EllipsisIcon } from '@acronis-platform/icons-react/stroke-mono';
123
+
124
+ // Sortable header driven by the headless sort hook
125
+ const { sortedData, getSortDirection, toggleSort } = useSortState({ data });
126
+
127
+ <TableHead
128
+ sortable
129
+ sortDirection={getSortDirection('name')}
130
+ onSort={() => toggleSort('name')}
131
+ >
132
+ Name
133
+ </TableHead>
134
+
135
+ // Wrappable cell — grows to fit multi-line content instead of truncating
136
+ <TableCell wrap>{longDescription}</TableCell>
137
+
138
+ // Header row — select-all cell leading, column-settings cell trailing
139
+ <TableRow>
140
+ <TableSelectCell header>
141
+ <Checkbox checked={allChecked} onCheckedChange={toggleAll} aria-label="Select all" />
142
+ </TableSelectCell>
143
+ <TableHead>Name</TableHead>
144
+ <TableSettingsCell>
145
+ <TableViewOptions iconOnly columns={columns} onToggle={toggleColumn} />
146
+ </TableSettingsCell>
147
+ </TableRow>
148
+
149
+ // Selectable row, with the trailing overflow-actions cell
150
+ <TableRow selected={checked}>
151
+ <TableSelectCell>
152
+ <Checkbox checked={checked} onCheckedChange={setChecked} aria-label="Select row" />
153
+ </TableSelectCell>
154
+ <TableCell>web-server-01</TableCell>
155
+ <TableCell><Tag>Protected</Tag></TableCell>
156
+ <TableActionsCell bulkSelectionActive={bulkSelectionActive}>
157
+ <ButtonIcon aria-label="Row actions"><EllipsisIcon /></ButtonIcon>
158
+ </TableActionsCell>
159
+ </TableRow>
160
+
161
+ // Pagination bar — plain props, no TanStack
162
+ <TablePagination
163
+ pageIndex={pageIndex}
164
+ pageCount={pageCount}
165
+ pageSize={pageSize}
166
+ totalRows={rows.length}
167
+ onPageIndexChange={setPageIndex}
168
+ onPageSizeChange={setPageSize}
169
+ />;
170
+ ```
171
+
172
+ ## Props
173
+
174
+ | Prop | Type | Default | Required | Description |
175
+ |------|------|---------|----------|-------------|
176
+ | `selected` | `boolean` | `false` | no | On a row — marks it selected (active row token + `data-state="selected"`). |
177
+ | `sortable` | `boolean` | `false` | no | On a column header — renders a focusable sort affordance and exposes `aria-sort`. |
178
+ | `sort-direction` | `'asc' | 'desc' | false` | `false` | no | On a sortable column header — the current sort direction (`false` = sortable but unsorted). Drives the sort icon and `aria-sort`. |
179
+ | `wrap` | `boolean` | `false` | no | On a cell or column header — allow the content to wrap onto multiple lines (`whitespace-normal`) and drop the fixed row height so the cell grows to fit multi-line content instead of truncating. |
180
+ | `header` | `boolean` | `false` | no | On the select-cell — render the header's select-all cell (`<th>`) instead of a row's selection cell (`<td>`). |
181
+ | `bulk-selection-active` | `boolean` | `false` | no | On the actions-cell — suppress this row's actions because a *bulk* selection is in play: the cell keeps its 48px column (so the grid doesn't reflow) but renders no children and no hover/press tint. The threshold is one or more rows being selected — a single selected row is already a bulk selection and loses its row actions. With a TanStack table, pass `isBulkSelectionActive(table)` (from the data-table part) instead of re-deriving it. |
182
+ | `page-index` | `number` | — | yes | On the pagination part — the zero-based index of the current page. |
183
+ | `page-count` | `number` | — | yes | On the pagination part — the total number of pages. |
184
+ | `page-size` | `number` | — | yes | On the pagination part — the number of rows shown per page. |
185
+ | `page-size-options` | `number[]` | `[10, 20, 30, 40, 50]` | no | On the pagination part — the page-size options offered in the rows-per-page select. |
186
+ | `total-rows` | `number` | — | no | On the pagination part — total rows across all pages (drives the summary text). |
187
+ | `selected-rows` | `number` | — | no | On the pagination part — number of currently selected rows (drives the selection summary text). |
188
+ | `columns` | `{ id: string; label: string; hidden: boolean }[]` | — | yes | On the view-options part — the toggleable columns and their current visibility. |
189
+ | `rows-per-page-label` | `string` | `'Rows per page'` | no | On the pagination part — label of the rows-per-page select (also its accessible name). Override to localize. |
190
+ | `first-page-label` | `string` | `'Go to first page'` | no | On the pagination part — accessible name of the first-page control. |
191
+ | `previous-page-label` | `string` | `'Go to previous page'` | no | On the pagination part — accessible name of the previous-page control. |
192
+ | `next-page-label` | `string` | `'Go to next page'` | no | On the pagination part — accessible name of the next-page control. |
193
+ | `last-page-label` | `string` | `'Go to last page'` | no | On the pagination part — accessible name of the last-page control. |
194
+ | `page-label` | `(page: number, pageCount: number) => string` | `(page, count) => count === 0 ? 'No pages' : `Page ${page} of ${count}`` | no | On the pagination part — builds the page indicator from the 1-based page number and the total page count. |
195
+ | `summary-label` | `(selectedRows: number | undefined, totalRows: number) => string | null` | `(selected, total) => selected != null ? `${selected} of ${total} row(s) selected.` : `${total} row(s).`` | no | On the pagination part — builds the leading row summary. Return `null` for no summary. |
196
+ | `trigger-label` | `ReactNode` | `'View'` | no | On the view-options part — the trigger button label. Ignored when `icon-only` is set. |
197
+ | `icon-only` | `boolean` | `false` | no | On the view-options part — render the trigger as a cog-only icon button instead of a labelled button: the shape that fits the 48px `TableSettingsCell` column. |
198
+ | `trigger-aria-label` | `string` | `'Column settings'` | no | On the view-options part — accessible name of the `icon-only` trigger. Override to localize. |
199
+ | `menu-label` | `ReactNode` | `'Toggle columns'` | no | On the view-options part — the dropdown heading. |
200
+
201
+ ## Events
202
+
203
+ | Event | Payload | Description |
204
+ |-------|---------|-------------|
205
+ | `sort` | `void` | Fired when a sortable header is activated by click / Enter / Space (React: `onSort`). The consumer owns the sorting logic and updates `sort-direction`. |
206
+ | `page-index-change` | `number` | On the pagination part — fired with the next zero-based page index (React: `onPageIndexChange`). |
207
+ | `page-size-change` | `number` | On the pagination part — fired with the next page size (React: `onPageSizeChange`). |
208
+ | `toggle` | `string` | On the view-options part — fired with the column id whose visibility was toggled (React: `onToggle`). |
209
+
210
+ ## Content slots
211
+
212
+ | Slot | Description |
213
+ |------|-------------|
214
+ | `default` | The composed table. Author it from the parts: a header (rows of column headers), a body (rows of cells), an optional footer, and an optional caption. Cell content (text, tags, links, checkboxes, …) is arbitrary. |
215
+
216
+ ## Behavior
217
+
218
+ Table is a composable, presentational data grid. It holds no sorting or
219
+ selection _logic_ — the consumer owns that and drives the relevant props. The
220
+ scenarios below describe the affordances the parts provide.
221
+
222
+ ### Sorting
223
+
224
+ ```gherkin
225
+ Scenario: A sortable header exposes its state
226
+ Given a TableHead with sortable = true and sort-direction = false
227
+ Then it renders an inactive sort icon and aria-sort = "none"
228
+ ```
229
+
230
+ ```gherkin
231
+ Scenario: Activating a sortable header
232
+ Given a sortable TableHead with an onSort handler
233
+ When the user clicks it (or focuses it and presses Enter / Space)
234
+ Then the sort event fires
235
+ And the consumer updates sort-direction, which swaps the icon (↑ asc / ↓ desc)
236
+ and sets aria-sort to "ascending" / "descending"
237
+ ```
238
+
239
+ ### Selection
240
+
241
+ ```gherkin
242
+ Scenario: A selected row
243
+ Given a TableRow with selected = true
244
+ Then it carries data-state="selected" and the active row background token
245
+ ```
246
+
247
+ ```gherkin
248
+ Scenario: Row selection is consumer-driven
249
+ Given a checkbox rendered inside a leading TableSelectCell
250
+ When the user toggles it
251
+ Then the consumer updates the row's selected prop (Table does not manage it)
252
+ ```
253
+
254
+ ```gherkin
255
+ Scenario: The select cell renders for a header or a row
256
+ Given a TableSelectCell
257
+ When header = true
258
+ Then it renders a <th> (the select-all cell) with the header cell background
259
+ When header is omitted
260
+ Then it renders a <td> with the data cell background
261
+ And either way it keeps its fixed 32px width and inline-start-only padding, so
262
+ the checkbox sits flush against the first data cell
263
+ ```
264
+
265
+ ### Row actions and column settings
266
+
267
+ ```gherkin
268
+ Scenario: The row-actions cell owns the interaction feedback
269
+ Given a TableActionsCell holding an overflow trigger
270
+ When the pointer is over it
271
+ Then the cell itself takes the hover tint (and the press tint on :active)
272
+ And a focus-visible trigger inside it draws the cell's inset focus ring
273
+ ```
274
+
275
+ ```gherkin
276
+ Scenario: Row actions step aside for bulk actions
277
+ Given a TableActionsCell with bulk-selection-active = true
278
+ Then it renders no children and no hover/press tint
279
+ And it still reserves its 48px column, so the grid does not reflow
280
+ # The threshold itself is the consumer's call — with a TanStack table, pass
281
+ # isBulkSelectionActive(table) rather than re-deriving it (see api.yaml).
282
+ ```
283
+
284
+ ```gherkin
285
+ Scenario: The settings cell holds the column-settings trigger
286
+ Given a TableSettingsCell in the header row
287
+ Then it renders a 48px end-aligned <th> that tints on hover/press and draws
288
+ the inset focus ring for a focus-visible trigger inside it
289
+ And what the trigger opens is the consumer's choice — TableViewOptions with
290
+ iconOnly is the ready-made cog + column-visibility menu
291
+ ```
292
+
293
+ ### Layout
294
+
295
+ ```gherkin
296
+ Scenario: Overflow scrolls horizontally
297
+ Given a table wider than its container
298
+ When it renders
299
+ Then the wrapping container scrolls horizontally, keeping the page intact
300
+ ```
301
+
302
+ ```gherkin
303
+ Scenario: Hover feedback
304
+ Given a row in the body
305
+ When the pointer is over it
306
+ Then it takes the hover background token (unless selected, which wins)
307
+ ```
308
+
309
+ ```gherkin
310
+ Scenario: A wrapping cell grows the row
311
+ Given a TableCell (or TableHead) with wrap = true holding multi-line content
312
+ When it renders
313
+ Then it uses whitespace-normal and drops the fixed row height
314
+ And the row grows to fit the content instead of truncating it
315
+ ```
316
+
317
+ ### Pagination (TablePagination)
318
+
319
+ TablePagination is a controlled, TanStack-independent bar. It renders from plain
320
+ props and never holds page state itself.
321
+
322
+ ```gherkin
323
+ Scenario: Page controls report the range
324
+ Given a TablePagination with page-index, page-count, page-size
325
+ Then it shows "Page {page-index + 1} of {page-count}"
326
+ And first/prev are disabled on the first page, next/last on the last page
327
+ ```
328
+
329
+ ```gherkin
330
+ Scenario: Changing the page
331
+ Given a TablePagination
332
+ When the user clicks first / prev / next / last
333
+ Then the page-index-change event fires with the target zero-based index
334
+ When the user picks a rows-per-page option
335
+ Then the page-size-change event fires with that size
336
+ ```
337
+
338
+ ```gherkin
339
+ Scenario: Selection summary
340
+ Given a TablePagination with total-rows and selected-rows
341
+ Then it shows "{selected-rows} of {total-rows} row(s) selected."
342
+ ```
343
+
344
+ ### View options (TableViewOptions)
345
+
346
+ ```gherkin
347
+ Scenario: Toggling column visibility
348
+ Given a TableViewOptions with a { id, label, hidden }[] of columns
349
+ When the user toggles a column's checkbox in the dropdown
350
+ Then the toggle event fires with that column's id
351
+ And the consumer updates the column's hidden flag
352
+ ```
353
+
354
+ ```gherkin
355
+ Scenario: The icon-only trigger fits the settings cell
356
+ Given a TableViewOptions with icon-only = true
357
+ Then its trigger is a cog-only icon button named by trigger-aria-label
358
+ ("Column settings" by default), the shape that fits the 48px settings cell
359
+ And the trigger-label is ignored in that form
360
+ ```
361
+
362
+ ### Headless state hooks
363
+
364
+ ```gherkin
365
+ Scenario: useSortState drives a sortable header
366
+ Given useSortState({ data })
367
+ When a header calls toggleSort(columnId)
368
+ Then the sort cycles none → asc → desc → none
369
+ And sortedData reflects the current column + direction (alphanumeric by default)
370
+ And getSortDirection(columnId) returns the value for TableHead's sort-direction
371
+ ```
372
+
373
+ ```gherkin
374
+ Scenario: useTableUrlState writes to the URL
375
+ Given useTableUrlState()
376
+ When the consumer advances the page via setPagination
377
+ Then the namespaced tbl_page param is pushed onto the URL (history.pushState)
378
+ ```
379
+
380
+ ```gherkin
381
+ Scenario: useTableUrlState restores from the URL
382
+ Given a URL that already carries tbl_page / tbl_sort params
383
+ When the hook mounts
384
+ Then it seeds its initial state from those params
385
+ And a browser back/forward (popstate) re-syncs the state to the URL
386
+ ```
387
+
388
+ ## Accessibility
389
+
390
+ Table renders native table semantics (`<table>`/`<thead>`/`<tbody>`/`<tr>`/
391
+ `<th>`/`<td>`/`<caption>`), so assistive tech announces rows, columns, and
392
+ headers natively. Use the parts as their elements intend.
393
+
394
+ ## Roles & semantics
395
+
396
+ - The root is a native `<table>` (role `table`); `<th>` are column headers
397
+ (role `columnheader`), `<td>` are cells (role `cell`).
398
+ - Provide a `TableCaption` (or an `aria-label`/`aria-labelledby` on the table)
399
+ so the table has an accessible name.
400
+ - For row/column header association in complex tables, set `scope="col"` /
401
+ `scope="row"` on the relevant `<th>` (passes through as a native attribute).
402
+
403
+ ## Sorting
404
+
405
+ - A `sortable` `TableHead` sets **`aria-sort`** on the `<th>`: `none` when
406
+ unsorted, `ascending` / `descending` when sorted — so screen readers announce
407
+ the current sort.
408
+ - The sort affordance is a real **`<button>`**, so it is reachable by Tab and
409
+ activates with **Enter / Space**; it shows a `--ui-focus-primary` focus ring.
410
+
411
+ ## Selection
412
+
413
+ - Selection is expressed by rendering a `Checkbox` in a leading cell; label it
414
+ (`aria-label`) and use an `aria-label="Select all"` checkbox in the header.
415
+ - A selected `TableRow` sets `data-state="selected"` for styling — pair it with
416
+ the checkbox's checked state so the visual and programmatic states agree.
417
+
418
+ ## Pagination & view options
419
+
420
+ - `TablePagination`'s page controls are icon buttons with explicit labels ("Go
421
+ to first page" / "…previous page" / "…next page" / "…last page"); the
422
+ rows-per-page control is a labelled `Select` ("Rows per page"). Disabled
423
+ buttons at the range ends carry the native `disabled` state.
424
+ - `TableViewOptions` is a Base UI dropdown menu (keyboard-navigable) of checkbox
425
+ items; each row shows a presentational checkbox box beside the column name and
426
+ the row itself carries `role="menuitemcheckbox"` + `aria-checked`. The trigger
427
+ has a visible "View" label, or — with `iconOnly` — is a cog-only icon button
428
+ named by `triggerAriaLabel` (default "Column settings").
429
+
430
+ ## Contrast
431
+
432
+ - Cell text uses `--ui-table-data-value-color-idle`, headers
433
+ `--ui-table-header-label-color`, dividers `--ui-table-global-row-border-color`;
434
+ row hover/active use `--ui-table-data-row-color-{hover,active}`. These meet
435
+ WCAG AA against the page surface in light and dark themes. Re-verify against
436
+ the final palette once the design is confirmed ready for dev.
@@ -0,0 +1,197 @@
1
+ # Tabs
2
+
3
+ > Switches between related panels of content within the same context. A bordered segmented-control tab group (list, trigger, content) built on the Base UI Tabs primitive. Ported from the legacy shadcn-uikit `Tabs`; themed by the shared semantic tokens (no `--ui-tabs-*` tier yet).
4
+
5
+ **Status:** draft · **Category:** navigation · **Since:** v0.11.0
6
+
7
+ ## Import
8
+
9
+ ```tsx
10
+ import { Tabs, TabsList, TabsTrigger, TabsContent } from '@acronis-platform/ui-react'
11
+ ```
12
+
13
+ ## Quick example
14
+
15
+ ```tsx
16
+ <Tabs defaultValue="account">
17
+ <TabsList>
18
+ <TabsTrigger value="account">Account</TabsTrigger>
19
+ <TabsTrigger value="password">Password</TabsTrigger>
20
+ </TabsList>
21
+ <TabsContent value="account">Account settings</TabsContent>
22
+ <TabsContent value="password">Password settings</TabsContent>
23
+ </Tabs>
24
+ ```
25
+
26
+ Switches between related panels of content within the same context. A bordered
27
+ segmented-control tab group built on the Base UI Tabs primitive.
28
+
29
+ > **Status: draft (design-pending v1).** Ported from the legacy
30
+ > `@acronis-platform/shadcn-uikit` `Tabs`. No `--ui-tabs-*` token tier yet, so
31
+ > the segments use the shared brand tokens — the **`secondary`** bridge (the same
32
+ > action blue Button uses), not `primary` (which is the dark navy brand). The
33
+ > active segment is rendered **filled** (`bg-secondary` + a pure-white
34
+ > `text-primary-foreground` label, like the default Button); the legacy
35
+ > `bg-primary/10` tint was dropped to avoid an opacity hack — the final treatment
36
+ > (filled vs tinted vs underline) is to be confirmed. Reconcile with
37
+ > `/figma-component Tabs <url> --update` once a mockup lands.
38
+
39
+ ## When to use
40
+
41
+ - Letting the user switch between a few peer views of the same area (Account /
42
+ Password; Overview / Analytics / Reports) without navigating away.
43
+
44
+ ## When not to use
45
+
46
+ - For navigation between pages/routes — use links or a nav, not tabs.
47
+ - For more tabs than fit comfortably in a segmented strip — consider a
48
+ different navigation pattern.
49
+ - For sequential steps — use a wizard/stepper.
50
+
51
+ ## Parts
52
+
53
+ | Part | Element | Purpose |
54
+ | ------------- | -------------------- | ------------------------------------------ |
55
+ | `Tabs` | `div` (Root) | Owns the selected value and orientation. |
56
+ | `TabsList` | `div[role=tablist]` | The segmented strip of triggers. |
57
+ | `TabsTrigger` | `button[role=tab]` | A tab; identified by `value`. |
58
+ | `TabsContent` | `div[role=tabpanel]` | A panel; shown when its `value` is active. |
59
+
60
+ ## Example
61
+
62
+ ```tsx
63
+ import {
64
+ Tabs,
65
+ TabsList,
66
+ TabsTrigger,
67
+ TabsContent,
68
+ } from '@acronis-platform/ui-react';
69
+
70
+ <Tabs defaultValue="account" className="w-[400px]">
71
+ <TabsList className="grid w-full grid-cols-2">
72
+ <TabsTrigger value="account">Account</TabsTrigger>
73
+ <TabsTrigger value="password">Password</TabsTrigger>
74
+ </TabsList>
75
+ <TabsContent value="account">Make changes to your account here.</TabsContent>
76
+ <TabsContent value="password">Change your password here.</TabsContent>
77
+ </Tabs>;
78
+ ```
79
+
80
+ Controlled:
81
+
82
+ ```tsx
83
+ const [tab, setTab] = useState('account');
84
+
85
+ <Tabs value={tab} onValueChange={setTab}>
86
+ {/* … */}
87
+ </Tabs>;
88
+ ```
89
+
90
+ ## Props
91
+
92
+ | Prop | Type | Default | Required | Description |
93
+ |------|------|---------|----------|-------------|
94
+ | `value` | `string` | — | no | Controlled selected-tab value. Pair with the change event. |
95
+ | `default-value` | `string` | — | no | Initially selected tab value when uncontrolled. |
96
+ | `orientation` | `'horizontal' | 'vertical'` | `'horizontal'` | no | Layout and arrow-key navigation orientation. |
97
+ | `tab-value` | `string` | — | yes | On a trigger and its content — the identifier that links them and is emitted on selection. A trigger also accepts a `disabled` flag (pass-through to the underlying tab) to prevent selecting it. |
98
+
99
+ ## Events
100
+
101
+ | Event | Payload | Description |
102
+ |-------|---------|-------------|
103
+ | `value-change` | `string` | Fired when the selected tab changes (React: `onValueChange`). Payload is the newly selected tab value. |
104
+
105
+ ## Content slots
106
+
107
+ | Slot | Description |
108
+ |------|-------------|
109
+ | `default` | A `list` of `trigger`s followed by one `content` panel per tab. Each trigger and its panel share a `tab-value`. |
110
+
111
+ ## Behavior
112
+
113
+ Tabs shows one panel at a time and lets the user switch between them. It owns
114
+ the selected-tab state (or the consumer controls it via `value`).
115
+
116
+ ### Selecting
117
+
118
+ ```gherkin
119
+ Scenario: Activating a tab by pointer
120
+ Given a tab group with the first tab selected
121
+ When the user clicks another trigger
122
+ Then that trigger becomes active (data-active, aria-selected="true")
123
+ And its content panel replaces the previous one
124
+ And value-change fires with the new value
125
+ ```
126
+
127
+ ```gherkin
128
+ Scenario: Keyboard navigation (manual activation)
129
+ Given focus is on the active trigger
130
+ When the user presses Arrow keys (per orientation)
131
+ Then focus moves between triggers without changing the selection
132
+ And pressing Enter or Space activates the focused trigger
133
+ ```
134
+
135
+ ### Controlled vs uncontrolled
136
+
137
+ ```gherkin
138
+ Scenario: Uncontrolled with default-value
139
+ Given a Tabs with default-value = "account" and no value prop
140
+ When it mounts
141
+ Then the "account" tab is selected and the user can switch freely
142
+ ```
143
+
144
+ ```gherkin
145
+ Scenario: Controlled
146
+ Given a Tabs with a fixed value prop
147
+ When the user activates another tab
148
+ Then the selection does NOT change on its own
149
+ And value-change fires so the consumer can update value
150
+ ```
151
+
152
+ ### Orientation
153
+
154
+ ```gherkin
155
+ Scenario: Vertical orientation
156
+ Given a Tabs with orientation = "vertical"
157
+ Then Up/Down arrows move between triggers (instead of Left/Right)
158
+ ```
159
+
160
+ ## Accessibility
161
+
162
+ Tabs leans on the Base UI Tabs primitive for the WAI-ARIA tabs pattern: roles,
163
+ selection state, roving focus, and keyboard navigation.
164
+
165
+ ## Roles & semantics
166
+
167
+ - `TabsList` is `role="tablist"`; `TabsTrigger` is `role="tab"`; `TabsContent`
168
+ is `role="tabpanel"`.
169
+ - The active trigger exposes `aria-selected="true"` (and `data-active` for
170
+ styling); its panel is associated via `aria-controls` / `aria-labelledby`.
171
+ - Set `orientation="vertical"` so `aria-orientation` and arrow-key handling match
172
+ a vertical strip.
173
+
174
+ ## Keyboard
175
+
176
+ - **Tab** moves into the tablist (one stop — roving tabindex), then into the
177
+ active panel.
178
+ - **Arrow keys** (Left/Right when horizontal, Up/Down when vertical) move focus
179
+ between triggers **without** activating them (manual activation).
180
+ - **Enter / Space** activates the focused trigger.
181
+ - **Home / End** move to the first / last trigger.
182
+
183
+ ## Screen reader
184
+
185
+ - Entering the tablist announces the tab role, the selected state, and the
186
+ position ("tab 1 of 2"). Activating a tab announces the newly selected tab and
187
+ its panel.
188
+
189
+ ## Contrast
190
+
191
+ - Idle triggers use the brand action blue for border + label
192
+ (`--ui-background-brand-secondary`, the same blue Button uses); the active
193
+ trigger fills with that blue and uses a pure-white label
194
+ (`--ui-glyph-on-brand-primary`, like the default Button), and shows a
195
+ `--ui-focus-primary` ring on keyboard focus — all meeting WCAG AA in light and
196
+ dark. Re-verify against the final palette once a `--ui-tabs-*` tier and Figma
197
+ reference exist (the filled-vs-tinted active treatment is provisional).