@uselets/ui 1.2.2 → 1.4.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 (218) hide show
  1. package/README.md +96 -5
  2. package/dist/chart-DHS2QeM8.d.ts +80 -0
  3. package/dist/cli/uselets.mjs.map +1 -1
  4. package/dist/components/accordion.d.ts +4 -5
  5. package/dist/components/accordion.js.map +1 -1
  6. package/dist/components/alert-dialog.d.ts +12 -13
  7. package/dist/components/alert-dialog.js.map +1 -1
  8. package/dist/components/alert.d.ts +5 -7
  9. package/dist/components/alert.js.map +1 -1
  10. package/dist/components/anchor.d.ts +2 -4
  11. package/dist/components/anchor.js.map +1 -1
  12. package/dist/components/area-chart.d.ts +58 -0
  13. package/dist/components/area-chart.js +2 -0
  14. package/dist/components/area-chart.js.map +1 -0
  15. package/dist/components/avatar.d.ts +6 -7
  16. package/dist/components/avatar.js.map +1 -1
  17. package/dist/components/badge.d.ts +2 -4
  18. package/dist/components/badge.js.map +1 -1
  19. package/dist/components/bar-chart.d.ts +54 -0
  20. package/dist/components/bar-chart.js +2 -0
  21. package/dist/components/bar-chart.js.map +1 -0
  22. package/dist/components/breadcrumb.d.ts +7 -8
  23. package/dist/components/breadcrumb.js.map +1 -1
  24. package/dist/components/button-group.d.ts +4 -6
  25. package/dist/components/button-group.js.map +1 -1
  26. package/dist/components/button.d.ts +2 -4
  27. package/dist/components/button.js.map +1 -1
  28. package/dist/components/calendar.d.ts +2 -3
  29. package/dist/components/calendar.js.map +1 -1
  30. package/dist/components/card.d.ts +7 -8
  31. package/dist/components/card.js.map +1 -1
  32. package/dist/components/carousel.d.ts +17 -7
  33. package/dist/components/carousel.js +1 -1
  34. package/dist/components/carousel.js.map +1 -1
  35. package/dist/components/carousel.spec.d.ts +1 -0
  36. package/dist/components/carousel.spec.js +2 -0
  37. package/dist/components/carousel.spec.js.map +1 -0
  38. package/dist/components/center.d.ts +2 -4
  39. package/dist/components/center.js.map +1 -1
  40. package/dist/components/chart.d.ts +2 -263
  41. package/dist/components/chart.js +1 -1
  42. package/dist/components/chart.js.map +1 -1
  43. package/dist/components/checkbox.d.ts +2 -4
  44. package/dist/components/checkbox.js +1 -1
  45. package/dist/components/checkbox.js.map +1 -1
  46. package/dist/components/code.d.ts +1 -2
  47. package/dist/components/code.js.map +1 -1
  48. package/dist/components/collapsible.d.ts +3 -4
  49. package/dist/components/collapsible.js.map +1 -1
  50. package/dist/components/combobox.d.ts +15 -16
  51. package/dist/components/combobox.js.map +1 -1
  52. package/dist/components/command.d.ts +9 -10
  53. package/dist/components/command.js.map +1 -1
  54. package/dist/components/container.d.ts +2 -4
  55. package/dist/components/container.js.map +1 -1
  56. package/dist/components/data-table.d.ts +164 -0
  57. package/dist/components/data-table.js +2 -0
  58. package/dist/components/data-table.js.map +1 -0
  59. package/dist/components/date-range-picker.d.ts +11 -12
  60. package/dist/components/date-range-picker.js.map +1 -1
  61. package/dist/components/dialog.d.ts +11 -13
  62. package/dist/components/dialog.js.map +1 -1
  63. package/dist/components/donut-chart.d.ts +22 -0
  64. package/dist/components/donut-chart.js +2 -0
  65. package/dist/components/donut-chart.js.map +1 -0
  66. package/dist/components/drawer.d.ts +12 -14
  67. package/dist/components/drawer.js.map +1 -1
  68. package/dist/components/dropdown-menu.d.ts +15 -16
  69. package/dist/components/dropdown-menu.js.map +1 -1
  70. package/dist/components/dropzone.d.ts +1 -2
  71. package/dist/components/dropzone.js.map +1 -1
  72. package/dist/components/empty.d.ts +7 -9
  73. package/dist/components/empty.js.map +1 -1
  74. package/dist/components/field.d.ts +11 -13
  75. package/dist/components/field.js.map +1 -1
  76. package/dist/components/field.spec.js +1 -1
  77. package/dist/components/field.spec.js.map +1 -1
  78. package/dist/components/flex.d.ts +25 -0
  79. package/dist/components/flex.js +2 -0
  80. package/dist/components/flex.js.map +1 -0
  81. package/dist/components/grid.d.ts +2 -3
  82. package/dist/components/grid.js +1 -1
  83. package/dist/components/grid.js.map +1 -1
  84. package/dist/components/group.d.ts +6 -4
  85. package/dist/components/group.js +1 -1
  86. package/dist/components/group.js.map +1 -1
  87. package/dist/components/heading.d.ts +2 -4
  88. package/dist/components/heading.js.map +1 -1
  89. package/dist/components/hover-card.d.ts +3 -4
  90. package/dist/components/hover-card.js.map +1 -1
  91. package/dist/components/input-group.d.ts +8 -10
  92. package/dist/components/input-group.js.map +1 -1
  93. package/dist/components/input-otp.d.ts +4 -5
  94. package/dist/components/input-otp.js.map +1 -1
  95. package/dist/components/input.d.ts +1 -2
  96. package/dist/components/input.js.map +1 -1
  97. package/dist/components/kbd.d.ts +2 -3
  98. package/dist/components/kbd.js.map +1 -1
  99. package/dist/components/label.d.ts +1 -2
  100. package/dist/components/label.js.map +1 -1
  101. package/dist/components/list.d.ts +4 -6
  102. package/dist/components/list.js.map +1 -1
  103. package/dist/components/mask-input.d.ts +25 -0
  104. package/dist/components/mask-input.js +2 -0
  105. package/dist/components/mask-input.js.map +1 -0
  106. package/dist/components/native-select.d.ts +3 -4
  107. package/dist/components/native-select.js.map +1 -1
  108. package/dist/components/navigation-menu.d.ts +9 -11
  109. package/dist/components/navigation-menu.js.map +1 -1
  110. package/dist/components/pagination.d.ts +7 -8
  111. package/dist/components/pagination.js.map +1 -1
  112. package/dist/components/pie-chart.d.ts +58 -0
  113. package/dist/components/pie-chart.js +2 -0
  114. package/dist/components/pie-chart.js.map +1 -0
  115. package/dist/components/popover.d.ts +7 -8
  116. package/dist/components/popover.js.map +1 -1
  117. package/dist/components/progress.d.ts +1 -2
  118. package/dist/components/progress.js.map +1 -1
  119. package/dist/components/radio-group.d.ts +3 -5
  120. package/dist/components/radio-group.js.map +1 -1
  121. package/dist/components/resizable.d.ts +3 -4
  122. package/dist/components/resizable.js.map +1 -1
  123. package/dist/components/scroll-area.d.ts +2 -3
  124. package/dist/components/scroll-area.js.map +1 -1
  125. package/dist/components/select.d.ts +10 -11
  126. package/dist/components/select.js.map +1 -1
  127. package/dist/components/separator.d.ts +1 -2
  128. package/dist/components/separator.js.map +1 -1
  129. package/dist/components/sidebar.d.ts +24 -26
  130. package/dist/components/sidebar.js.map +1 -1
  131. package/dist/components/simple-grid.d.ts +1 -2
  132. package/dist/components/simple-grid.js +1 -1
  133. package/dist/components/simple-grid.js.map +1 -1
  134. package/dist/components/skeleton.d.ts +1 -2
  135. package/dist/components/skeleton.js.map +1 -1
  136. package/dist/components/skeleton.spec.js +1 -1
  137. package/dist/components/skeleton.spec.js.map +1 -1
  138. package/dist/components/slider.d.ts +1 -2
  139. package/dist/components/slider.js.map +1 -1
  140. package/dist/components/sonner.d.ts +1 -2
  141. package/dist/components/sonner.js.map +1 -1
  142. package/dist/components/spinner.d.ts +2 -4
  143. package/dist/components/spinner.js.map +1 -1
  144. package/dist/components/stack.d.ts +6 -4
  145. package/dist/components/stack.js +1 -1
  146. package/dist/components/stack.js.map +1 -1
  147. package/dist/components/switch.d.ts +1 -2
  148. package/dist/components/switch.js.map +1 -1
  149. package/dist/components/table.d.ts +29 -11
  150. package/dist/components/table.js +1 -1
  151. package/dist/components/table.js.map +1 -1
  152. package/dist/components/tabs.d.ts +5 -7
  153. package/dist/components/tabs.js.map +1 -1
  154. package/dist/components/text.d.ts +2 -4
  155. package/dist/components/text.js.map +1 -1
  156. package/dist/components/textarea.d.ts +1 -2
  157. package/dist/components/textarea.js.map +1 -1
  158. package/dist/components/theme.d.ts +2 -3
  159. package/dist/components/theme.js +1 -1
  160. package/dist/components/theme.js.map +1 -1
  161. package/dist/components/timeline.d.ts +3 -4
  162. package/dist/components/timeline.js.map +1 -1
  163. package/dist/components/toggle-group.d.ts +2 -3
  164. package/dist/components/toggle-group.js.map +1 -1
  165. package/dist/components/toggle.d.ts +2 -4
  166. package/dist/components/toggle.js.map +1 -1
  167. package/dist/components/tooltip.d.ts +5 -6
  168. package/dist/components/tooltip.js.map +1 -1
  169. package/dist/components/unstyled-button.d.ts +1 -2
  170. package/dist/components/unstyled-button.js.map +1 -1
  171. package/dist/components/visibility.d.ts +2 -3
  172. package/dist/components/visibility.js.map +1 -1
  173. package/dist/dist-C35vdEt2.js +129 -0
  174. package/dist/dist-C35vdEt2.js.map +1 -0
  175. package/dist/hooks/use-breakpoint-value.js.map +1 -1
  176. package/dist/hooks/use-breakpoint-value.spec.js +1 -14
  177. package/dist/hooks/use-breakpoint-value.spec.js.map +1 -1
  178. package/dist/hooks/use-breakpoint.js.map +1 -1
  179. package/dist/hooks/use-breakpoint.spec.js +1 -1
  180. package/dist/hooks/use-breakpoint.spec.js.map +1 -1
  181. package/dist/hooks/use-disclosure.js.map +1 -1
  182. package/dist/hooks/use-disclosure.spec.js +1 -1
  183. package/dist/hooks/use-disclosure.spec.js.map +1 -1
  184. package/dist/hooks/use-document-visibility.js.map +1 -1
  185. package/dist/hooks/use-document-visibility.spec.js +1 -1
  186. package/dist/hooks/use-document-visibility.spec.js.map +1 -1
  187. package/dist/hooks/use-idle.js.map +1 -1
  188. package/dist/hooks/use-idle.spec.js +1 -1
  189. package/dist/hooks/use-idle.spec.js.map +1 -1
  190. package/dist/hooks/use-interval.js.map +1 -1
  191. package/dist/hooks/use-interval.spec.js +1 -1
  192. package/dist/hooks/use-interval.spec.js.map +1 -1
  193. package/dist/hooks/use-local-storage.js.map +1 -1
  194. package/dist/hooks/use-local-storage.spec.js +1 -1
  195. package/dist/hooks/use-local-storage.spec.js.map +1 -1
  196. package/dist/hooks/use-mask.d.ts +9 -0
  197. package/dist/hooks/use-mask.js +2 -0
  198. package/dist/hooks/use-mask.js.map +1 -0
  199. package/dist/hooks/use-mobile.js.map +1 -1
  200. package/dist/hooks/use-mobile.spec.js +1 -1
  201. package/dist/hooks/use-mobile.spec.js.map +1 -1
  202. package/dist/index.d.ts +12 -4
  203. package/dist/index.js +1 -1
  204. package/dist/lib/button.js.map +1 -1
  205. package/dist/lib/color.js.map +1 -1
  206. package/dist/lib/utils.js.map +1 -1
  207. package/dist/magic-string.es-C-uSEkg2.js +15 -0
  208. package/dist/magic-string.es-C-uSEkg2.js.map +1 -0
  209. package/dist/postcss.config.js.map +1 -1
  210. package/dist/{react.esm-B3a5z6DL.js → react.esm-BagSCtI3.js} +12 -3
  211. package/dist/{react.esm-B3a5z6DL.js.map → react.esm-BagSCtI3.js.map} +1 -1
  212. package/dist/skills/uselets-ui/SKILL.md +1 -0
  213. package/dist/skills/uselets-ui/references/component-catalog.md +37 -7
  214. package/dist/skills/uselets-ui/references/package-guide.md +139 -2
  215. package/package.json +15 -13
  216. package/skills/uselets-ui/SKILL.md +1 -0
  217. package/skills/uselets-ui/references/component-catalog.md +37 -7
  218. package/skills/uselets-ui/references/package-guide.md +139 -2
@@ -56,6 +56,7 @@ To set up a consumer project after installing `@uselets/ui`, run `bun uselets in
56
56
  - For toast usage, mount `Toaster` near the app root before calling `toast(...)`.
57
57
  - For package theme helpers, use `ThemeProvider`, `useTheme`, and `ModeToggle` from `@uselets/ui/theme` or the root barrel.
58
58
  - For form fields, `Field` automatically shares a generated control id with `FieldLabel` and labelable controls when explicit ids are omitted; pass explicit matching `htmlFor` and `id` only when a stable custom id is required.
59
+ - For client-side tabular data, use `DataTable` from `@uselets/ui/data-table`. Defaults are bare: `enableRowSelection`, `enableSorting`, and `showPagination` are all `false`. Set `enableRowSelection` to auto-prepend the selection checkbox column (no need to add `createDataTableSelectColumn` manually); set `enableSorting` to make `DataTableColumnHeader` clickable for sort. `filterColumns` accepts a single id or an array; with 2+ ids the toolbar renders an `InputGroup` with a column selector on the left. `toolbarActions` always sits at the right of the toolbar. All labels default to pt-BR; override any subset through `labels` (`Partial<DataTableLabels>`), with full defaults exposed as `defaultDataTableLabels`. Use `variant` on `DataTable` (or directly on the base `Table`) to switch chrome: `"line"` (default) renders only horizontal separators between rows; `"outline"` adds a rounded outer border around the table. The base `Table` from `@uselets/ui/table` renders only the `<table>` element and is striped by default (pass `striped={false}` to disable). Wrap it in `TableContainer` to control scroll; `minWidth` lives on `TableContainer`. Combine `stickyHeader` on `Table` with `max-h` + `overflow-y-auto` on the `TableContainer` to keep the `<thead>` visible during vertical scroll. `DataTable` wraps its `Table` in `TableContainer` automatically.
59
60
 
60
61
  ## Quick Routing
61
62
 
@@ -50,6 +50,10 @@ Unless noted otherwise, each module also exports its public `*Props` types from
50
50
  - Import path: `@uselets/ui/hooks/use-local-storage`
51
51
  - Exports: `useLocalStorage`, `UseLocalStorageOptions`
52
52
  - Notes: returns a `[value, setValue]` tuple, stores values with JSON serialization, and syncs storage changes between browser tabs by default (`sync: true`)
53
+ - `use-mask`
54
+ - Import path: `@uselets/ui/hooks/use-mask`
55
+ - Exports: `useMask`, `UseMaskOptions`, `MaskOptions`, `Replacement`, `Track`, `TrackingData`, `Modify`, `ModifiedData`
56
+ - Notes: thin wrapper over `@react-input/mask`'s `useMask` that defaults `replacement` to `{ 9: /\d/, a: /[A-Za-z]/, '*': /./ }`; returns a `MutableRefObject<HTMLInputElement>` to attach to any input. Consumers may override `replacement`.
53
57
  - `utils`
54
58
  - Import path: `@uselets/ui/utils`
55
59
  - Exports: `cn`, `randomId`
@@ -119,10 +123,10 @@ Unless noted otherwise, each module also exports its public `*Props` types from
119
123
  - Notes: form layout and validation helpers; `Field` automatically provides a generated control id to `FieldLabel` and labelable descendants when explicit ids are omitted
120
124
  - `grid`
121
125
  - Exports: `Grid`, `GridItem`
122
- - Notes: 12-column CSS grid by default. `Grid` supports spacing props plus direct `templateRows` and `templateColumns`; `GridItem` defaults to `colSpan={12}` and supports numeric `colSpan` and `rowSpan`. Use `useBreakpointValue` when these values need to change by breakpoint.
126
+ - Notes: 12-column CSS grid by default. `Grid` supports spacing props plus direct `templateRows` and `templateColumns`; spacing values `xs` to `xl` map to `4/8/16/24/32px`; `GridItem` defaults to `colSpan={12}` and supports numeric `colSpan` and `rowSpan`. Use `useBreakpointValue` when these values need to change by breakpoint.
123
127
  - `simple-grid`
124
128
  - Exports: `SimpleGrid`
125
- - Notes: simple grid with `cols`, `gap`, `rowGap`, and `columnGap`. `cols` accepts a number; spacing uses the `xs` to `xl` scale. Use `useBreakpointValue` when column count needs responsive values.
129
+ - Notes: simple grid with `cols`, `gap`, `rowGap`, and `columnGap`. `cols` accepts a number; spacing values `xs` to `xl` map to `4/8/16/24/32px`. Use `useBreakpointValue` when column count needs responsive values.
126
130
  - `visibility`
127
131
  - Exports: `HiddenFrom`, `VisibleFrom`
128
132
  - Notes: breakpoint visibility wrappers. `HiddenFrom breakpoint="md"` hides at `md` and above; `VisibleFrom breakpoint="md"` shows at `md` and above.
@@ -135,6 +139,9 @@ Unless noted otherwise, each module also exports its public `*Props` types from
135
139
  - Notes: compose controls with prefixes, suffixes, or inline actions
136
140
  - `input-otp`
137
141
  - Exports: `InputOTP`, `InputOTPGroup`, `InputOTPSeparator`, `InputOTPSlot`, `REGEXP_ONLY_CHARS`, `REGEXP_ONLY_DIGITS`, `REGEXP_ONLY_DIGITS_AND_CHARS`
142
+ - `mask-input`
143
+ - Exports: `MaskInput`
144
+ - Notes: formatted input backed by `@react-input/mask`. Required prop `mask`; optional `replacement` (string or `Record<string, RegExp>`), `showMask`, `separate`, `track`, `modify`. Default replacement tokens are `9` (digit), `a` (letter), `*` (any). Renders the standard `Input`, so `Field`/`FieldLabel` and all native `<input>` props work as usual.
138
145
  - `checkbox`
139
146
  - Exports: `Checkbox`
140
147
  - Notes: supports `size: 'sm' | 'md' | 'lg'`, `color: UIColor` (default `primary`), `label?: React.ReactNode`, `labelProps`, and automatic `id`/`htmlFor` wiring when a label is provided without an explicit `id`; composed label text follows checkbox size (`sm` 12px, `md` 14px, `lg` 20px)
@@ -209,18 +216,34 @@ Unless noted otherwise, each module also exports its public `*Props` types from
209
216
  - `card`
210
217
  - Exports: `Card`, `CardAction`, `CardContent`, `CardDescription`, `CardFooter`, `CardHeader`, `CardTitle`
211
218
  - `carousel`
212
- - Exports: `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `useCarousel`
219
+ - Exports: `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `CarouselIndicators`, `useCarousel`
213
220
  - `center`
214
221
  - Exports: `Center`, `centerVariants`
215
222
  - `chart`
216
- - Exports: `Bar`, `BarChart`, `CartesianGrid`, `XAxis`, `ChartContainer`, `ChartLegend`, `ChartLegendContent`, `ChartStyle`, `ChartTooltip`, `ChartTooltipContent`
223
+ - Exports: `ChartContainer`, `ChartLegend`, `ChartLegendContent`, `ChartStyle`, `ChartTooltip`, `ChartTooltipContent`, `ChartConfig`
224
+ - Notes: low-level chart primitives. For app usage prefer the high-level `area-chart`, `bar-chart`, `pie-chart`, `donut-chart` modules. Import raw recharts primitives (`Bar`, `XAxis`, etc.) directly from `recharts` when composing with `ChartContainer`.
225
+ - `area-chart`
226
+ - Exports: `AreaChart`, `AreaChartSeries`, `AreaChartType`, `AreaChartCurveType`, `AreaChartGridAxis`
227
+ - Notes: high-level area chart. Pass `data`, `series` (array of `{ name, label?, color? }`), and `dataKey` (X axis field). Supports `type` (default/stacked/percent/split), `curveType`, `withXAxis`, `withYAxis`, `gridAxis`, `withDots`, `withTooltip`, `withLegend`. Defaults to chart colors `--chart-1..5`.
228
+ - `bar-chart`
229
+ - Exports: `BarChart`, `BarChartSeries`, `BarChartType`, `BarChartOrientation`, `BarChartGridAxis`
230
+ - Notes: high-level bar chart. Same shape as `area-chart` plus `orientation` (vertical/horizontal), `barSize`, `radius`. Supports stacked and percent types.
231
+ - `pie-chart`
232
+ - Exports: `PieChart`, `PieChartBase`, `PieChartDatum`, `buildPieConfig`
233
+ - Notes: high-level pie chart. Pass `data` as `Array<{ name, value, color? }>`. Supports `withLabels`, `labelsPosition`, `startAngle`, `endAngle`, `paddingAngle`, `withLegend`, `withTooltip`. `PieChartBase` is the shared internal renderer used by `donut-chart`.
234
+ - `donut-chart`
235
+ - Exports: `DonutChart`
236
+ - Notes: same data shape as `pie-chart`. Adds `thickness` (donut hole size in px) and `withTotal` + `totalLabel` (renders sum in the center).
217
237
  - `collapsible`
218
238
  - Exports: `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent`
219
239
  - `container`
220
240
  - Exports: `Container`, `containerVariants`
241
+ - `flex`
242
+ - Exports: `Flex`, `flexVariants`
243
+ - Notes: generic flexbox layout primitive. Supports `direction: 'row' | 'row-reverse' | 'column' | 'column-reverse'`, `align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'`, `justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'`, `wrap`, and `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (`4/8/16/24/32px`).
221
244
  - `group`
222
245
  - Exports: `Group`, `groupVariants`
223
- - Notes: supports `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` and `wrap`
246
+ - Notes: horizontal row primitive. Supports `align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'` (default `center`), `justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'`, `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (`4/8/16/24/32px`), and `wrap`
224
247
  - `kbd`
225
248
  - Exports: `Kbd`, `KbdGroup`
226
249
  - `resizable`
@@ -231,9 +254,16 @@ Unless noted otherwise, each module also exports its public `*Props` types from
231
254
  - Exports: `Separator`
232
255
  - `stack`
233
256
  - Exports: `Stack`, `stackVariants`
234
- - Notes: supports `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'`
257
+ - Notes: vertical column primitive. Supports `align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'`, `justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'`, and `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (`4/8/16/24/32px`)
235
258
  - `table`
236
- - Exports: `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableHead`, `TableRow`, `TableCell`, `TableCaption`
259
+ - Exports: `Table`, `TableContainer`, `TableHeader`, `TableBody`, `TableFooter`, `TableHead`, `TableRow`, `TableCell`, `TableCaption`, `tableVariants`
260
+ - Notes: `Table` renders only the `<table>` element (no auto wrapper); striped by default (`striped` prop, default `true`) and supports `stickyHeader`. `variant` (`"line"` default | `"outline"`) controls outer chrome — `"line"` has only horizontal separators between rows; `"outline"` adds a rounded outer border. Outline works standalone (chrome on the `<table>`) or wrapped in `TableContainer` (container detects `data-variant=outline` via `:has()`, applies the chrome on itself, and zeroes it on the `<table>` — keeps sticky headers from painting over the border). Wrap in `TableContainer` for horizontal scroll; `TableContainer` accepts `minWidth` (number in px or any CSS length). For sticky header, set `max-h` + `overflow-y-auto` on the `TableContainer` (via `className` or `style`)
261
+ - `data-table`
262
+ - Exports: `DataTable`, `DataTableRoot`, `DataTableToolbar`, `DataTableHeader`, `DataTableBody`, `DataTablePagination`, `DataTableColumnHeader`, `createDataTableSelectColumn`, `useDataTable`, `useDataTableContext`, `defaultDataTableLabels`, `DataTableLabels`, `UseDataTableOptions`, `DataTableRootProps`, `DataTableToolbarProps`
263
+ - Notes: dual API — high-level `DataTable` (monolithic, simple cases) and compound (`DataTableRoot` + sub-parts, power cases including virtualization). Both share the same underlying logic via `useDataTable`.
264
+ - **High-level `DataTable`:** client-side TanStack Table composition. Defaults are bare — `enableRowSelection`, `enableSorting`, `showPagination` all default to `false` and must be explicitly enabled. Setting `enableRowSelection` auto-prepends the selection column; setting `enableSorting` activates sort buttons in `DataTableColumnHeader`. `filterColumns` accepts `string | string[]`; with 2+ ids the toolbar renders an `InputGroup` with a column selector at the left. `toolbarActions` always renders at the right of the toolbar. `variant` (`"line"` default | `"outline"`) is forwarded to the internal `Table` — pass `"outline"` to wrap the table in a rounded border. All user-facing labels default to pt-BR and are overridable through the `labels` prop (`Partial<DataTableLabels>`)
265
+ - **Compound API:** `DataTableRoot` accepts either `{ columns, data, ...options }` (uncontrolled — creates the `table` instance internally) or `{ table }` (controlled — table instance comes from `useDataTable` called by the consumer). `DataTableToolbar`, `DataTableHeader`, `DataTableBody`, and `DataTablePagination` read the `table` instance from context. `DataTableHeader` renders a full `<thead>`; `DataTableBody` renders a full `<tbody>` — to virtualize, omit `<DataTableBody />` and render your own `<tbody>` instead. `useDataTable(options)` is a thin wrapper around `useReactTable` that bakes in the same defaults `DataTable` uses (row models, selection auto-injection); pass `enablePagination: false` when virtualizing so `table.getRowModel()` returns all rows. `useDataTableContext<TData>()` retrieves the current `table` and merged `labels` inside a custom compound child
266
+ - **Virtualization:** the package is virtualizer-agnostic — it has zero runtime dependency on `@tanstack/react-virtual`. Bring your own virtualizer (TanStack Virtual, `react-window`, `virtua`, or manual). See the *Virtualized lists* section in `references/package-guide.md` for the canonical TanStack Virtual recipe
237
267
  - `timeline`
238
268
  - Exports: `Timeline`, `TimelineConnector`, `TimelineContent`, `TimelineDescription`, `TimelineEmpty`, `TimelineHeader`, `TimelineIcon`, `TimelineItem`, `TimelineTime`, `TimelineTitle`, `timelineVariants`
239
269
  - Notes: timeline flow with root `size`, inherited `iconSize`, item `status`, optional loading/error states, and semantic icon or connector colors
@@ -146,6 +146,7 @@ Use these patterns:
146
146
  ```tsx
147
147
  import { Alert, Button, UnstyledButton, cn, randomId } from '@uselets/ui'
148
148
  import type { UIColor } from '@uselets/ui'
149
+ import { DataTable } from '@uselets/ui/data-table'
149
150
  import { Dialog } from '@uselets/ui/dialog'
150
151
  import { useIsMobile } from '@uselets/ui/hooks/use-mobile'
151
152
  import { useDisclosure } from '@uselets/ui/hooks/use-disclosure'
@@ -161,7 +162,9 @@ Guidance:
161
162
  - Use `@uselets/ui/hooks/<hook-module>` for dedicated hook subpaths.
162
163
  - Do not invent undocumented subpaths.
163
164
 
164
- Public hook subpaths currently include `hooks/use-mobile`, `hooks/use-disclosure`, `hooks/use-interval`, `hooks/use-idle`, `hooks/use-document-visibility`, and `hooks/use-local-storage`. Legacy `@uselets/ui/use-*` paths are not public entrypoints. Utility helpers are available from `@uselets/ui/utils`, and shared UI color helpers are available from the root barrel or `@uselets/ui/lib/color`.
165
+ Public hook subpaths currently include `hooks/use-mobile`, `hooks/use-disclosure`, `hooks/use-interval`, `hooks/use-idle`, `hooks/use-document-visibility`, `hooks/use-local-storage`, and `hooks/use-mask`. Legacy `@uselets/ui/use-*` paths are not public entrypoints. Utility helpers are available from `@uselets/ui/utils`, and shared UI color helpers are available from the root barrel or `@uselets/ui/lib/color`.
166
+
167
+ Use `DataTable` from `@uselets/ui/data-table` for client-side tables with sorting, filtering, pagination, row selection, column visibility, formatted cells, and custom action columns. All user-facing labels default to Portuguese (pt-BR) and can be overridden through the `labels` prop (`Partial<DataTableLabels>`); the full defaults are exported as `defaultDataTableLabels`.
165
168
 
166
169
  `useLocalStorage` uses an options object and returns a tuple like `useState`:
167
170
 
@@ -290,6 +293,138 @@ The `RadioGroup` component supports `color: UIColor`, with `primary` as the defa
290
293
 
291
294
  The `DateRangePicker` component is a compound popover date range picker built on `Calendar` range mode. Use `DateRange | null` for controlled values; applying an empty draft or resetting emits `null` through `onValueChange`. Fixed presets, relative-day inputs, date inputs, and the calendar all edit the same draft, and `DateRangePickerPreset` displays a selected state when its range matches the current draft by calendar day.
292
295
 
296
+ ## DataTable compound API and virtualization
297
+
298
+ `DataTable` ships two complementary APIs:
299
+
300
+ - **High-level `DataTable`** — monolithic, ergonomic, covers the common case (pagination, sorting, row selection, filters, toolbar actions, custom labels). Public surface is unchanged from earlier versions.
301
+ - **Compound parts** — `DataTableRoot`, `DataTableToolbar`, `DataTableHeader`, `DataTableBody`, `DataTablePagination`. The high-level component is itself implemented in terms of these, so behavior never diverges.
302
+
303
+ Use the compound API when you need to:
304
+
305
+ - Virtualize the body (large datasets).
306
+ - Place the toolbar, pagination, or other actions in non-default positions (e.g. toolbar inside a card header, pagination in a footer slot).
307
+ - Share the same `table` instance with code that lives outside the `DataTable` subtree (e.g. an "Export selected" button rendered above the page).
308
+
309
+ ### Compound shape (non-virtualized)
310
+
311
+ ```tsx
312
+ import {
313
+ DataTableBody,
314
+ DataTableHeader,
315
+ DataTablePagination,
316
+ DataTableRoot,
317
+ DataTableToolbar
318
+ } from '@uselets/ui/data-table'
319
+ import { Table, TableContainer } from '@uselets/ui/table'
320
+
321
+ <DataTableRoot columns={columns} data={data} enableRowSelection enableSorting>
322
+ <DataTableToolbar filterColumns={['name', 'email']} />
323
+ <TableContainer className="rounded-lg border">
324
+ <Table>
325
+ <DataTableHeader />
326
+ <DataTableBody />
327
+ </Table>
328
+ </TableContainer>
329
+ <DataTablePagination />
330
+ </DataTableRoot>
331
+ ```
332
+
333
+ `DataTableRoot` accepts either `{ columns, data, ...options }` (uncontrolled — creates the `table` instance internally) or `{ table }` (controlled — table instance comes from `useDataTable` called by the consumer). Use the controlled form whenever you need access to the `table` instance in the same scope as the JSX (e.g. for virtualization or external action buttons).
334
+
335
+ ### Virtualized lists (bring your own virtualizer)
336
+
337
+ `@uselets/ui` has **zero runtime dependency** on any virtualization library. Pick the one you prefer (`@tanstack/react-virtual`, `react-window`, `virtua`, or even a manual implementation) and replace `<DataTableBody />` with a `<tbody>` that renders only the virtual rows.
338
+
339
+ Canonical recipe using `@tanstack/react-virtual` (the most common choice in the React ecosystem):
340
+
341
+ ```tsx
342
+ import { useVirtualizer } from '@tanstack/react-virtual'
343
+ import {
344
+ DataTableHeader,
345
+ DataTableRoot,
346
+ DataTableToolbar,
347
+ useDataTable
348
+ } from '@uselets/ui/data-table'
349
+ import { Table, TableCell, TableContainer, TableRow } from '@uselets/ui/table'
350
+ import * as React from 'react'
351
+
352
+ function VirtualizedPayments({ data }: { data: Payment[] }) {
353
+ const table = useDataTable<Payment>({
354
+ columns,
355
+ data,
356
+ enableRowSelection: true,
357
+ enableSorting: true,
358
+ enablePagination: false // IMPORTANT: returns all rows from getRowModel()
359
+ })
360
+
361
+ const parentRef = React.useRef<HTMLDivElement>(null)
362
+ const rows = table.getRowModel().rows
363
+ const colSpan = table.getVisibleLeafColumns().length
364
+
365
+ const virtualizer = useVirtualizer({
366
+ count: rows.length,
367
+ getScrollElement: () => parentRef.current,
368
+ estimateSize: () => 52,
369
+ overscan: 8
370
+ })
371
+
372
+ const virtualItems = virtualizer.getVirtualItems()
373
+ const totalSize = virtualizer.getTotalSize()
374
+ const paddingTop = virtualItems[0]?.start ?? 0
375
+ const paddingBottom = totalSize - (virtualItems.at(-1)?.end ?? 0)
376
+
377
+ return (
378
+ <DataTableRoot table={table}>
379
+ <DataTableToolbar filterColumns={['customer']} />
380
+ <TableContainer ref={parentRef} className="h-[600px] rounded-lg border">
381
+ <Table stickyHeader>
382
+ <DataTableHeader />
383
+ <tbody>
384
+ {paddingTop > 0 && (
385
+ <tr aria-hidden>
386
+ <td colSpan={colSpan} style={{ height: paddingTop, padding: 0, border: 0 }} />
387
+ </tr>
388
+ )}
389
+ {virtualItems.map((vi) => {
390
+ const row = rows[vi.index]
391
+ return (
392
+ <TableRow key={row.id} data-state={row.getIsSelected() ? 'selected' : undefined}>
393
+ {row.getVisibleCells().map((cell) => {
394
+ const renderer = cell.column.columnDef.cell
395
+ return (
396
+ <TableCell key={cell.id}>
397
+ {typeof renderer === 'function' ? renderer(cell.getContext()) : (cell.getValue() as React.ReactNode)}
398
+ </TableCell>
399
+ )
400
+ })}
401
+ </TableRow>
402
+ )
403
+ })}
404
+ {paddingBottom > 0 && (
405
+ <tr aria-hidden>
406
+ <td colSpan={colSpan} style={{ height: paddingBottom, padding: 0, border: 0 }} />
407
+ </tr>
408
+ )}
409
+ </tbody>
410
+ </Table>
411
+ </TableContainer>
412
+ </DataTableRoot>
413
+ )
414
+ }
415
+ ```
416
+
417
+ Key points and gotchas:
418
+
419
+ - **Bring your own virtualizer.** The example uses `@tanstack/react-virtual` for clarity, but any virtualizer works. Replace `useVirtualizer` and the `paddingTop`/`paddingBottom` spacer-row computation with the equivalent from `react-window`, `virtua`, or your own. The compounds (`DataTableRoot`, `DataTableToolbar`, `DataTableHeader`) and primitives (`Table`, `TableContainer`, `TableRow`, `TableCell`) do not change.
420
+ - **`enablePagination: false`** is required so `table.getRowModel()` returns the full filtered/sorted dataset instead of just the current page. Pagination and virtualization are mutually exclusive — pick one.
421
+ - **Sticky header.** Set `stickyHeader` on `<Table>` and a fixed height on `<TableContainer>`. The CSS-only sticky header in the primitive `Table` continues to work because `<thead>` stays in normal flow.
422
+ - **Row height.** `estimateSize` is fine when rows are uniform. For dynamic heights, pass `measureElement={virtualizer.measureElement}` on each `TableRow`'s `ref` to let TanStack Virtual measure them.
423
+ - **Spacer rows vs absolute positioning.** Two valid approaches exist: spacer rows (`paddingTop`/`paddingBottom` as shown — keeps rows in normal flow, simpler) or `position: absolute; transform: translateY(start)` on every row (requires `position: relative` on `<tbody>` and `height: totalSize`). The spacer-row approach is generally easier and works across virtualizers.
424
+ - **Row selection, sorting, and filtering all work** in the virtualized mode because they live on the `table` instance — only rendering of the body is virtualized.
425
+
426
+ A full working example lives in `apps/web/src/stories/data-table-virtualized.stories.tsx`.
427
+
293
428
  ## Component selection
294
429
 
295
430
  Choose components by intent:
@@ -305,7 +440,9 @@ Choose components by intent:
305
440
  - typography:
306
441
  - `Anchor`, `Code`, `Heading`, `List`, `Text`
307
442
  - layout and display:
308
- - `Card`, `Accordion`, `Collapsible`, `Container`, `Group`, `Stack`, `Center`, `Separator`, `ScrollArea`, `Resizable`, `Table`, `Timeline`, `Chart`, `Carousel`, `Avatar`, `Empty`, `Kbd`
443
+ - `Card`, `Accordion`, `Collapsible`, `Container`, `Flex`, `Group`, `Stack`, `Center`, `Separator`, `ScrollArea`, `Resizable`, `Table`, `Timeline`, `Chart`, `Carousel`, `Avatar`, `Empty`, `Kbd`
444
+
445
+ Layout spacing props named `gap`, `rowGap`, and `columnGap` use the shared `xs` to `xl` scale: `4/8/16/24/32px`.
309
446
 
310
447
  ## Inspection workflow
311
448
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@uselets/ui",
3
- "version": "1.2.2",
3
+ "version": "1.4.0",
4
4
  "type": "module",
5
5
  "private": false,
6
6
  "description": "React component library for the LET'S design system.",
@@ -57,7 +57,9 @@
57
57
  "dependencies": {
58
58
  "@base-ui/react": "1.4.1",
59
59
  "@fontsource-variable/inter": "5.2.8",
60
- "@tabler/icons-react": "3.41.1",
60
+ "@react-input/mask": "2.0.4",
61
+ "@tabler/icons-react": "3.44.0",
62
+ "@tanstack/react-table": "8.21.3",
61
63
  "class-variance-authority": "0.7.1",
62
64
  "clsx": "2.1.1",
63
65
  "cmdk": "1.1.1",
@@ -69,11 +71,11 @@
69
71
  "radix-ui": "1.4.3",
70
72
  "react-day-picker": "9.14.0",
71
73
  "react-dropzone": "15.0.0",
72
- "react-resizable-panels": "4.10.0",
73
- "recharts": "2.15.4",
74
+ "react-resizable-panels": "4.11.1",
75
+ "recharts": "3.8.1",
74
76
  "shadcn": "4.6.0",
75
77
  "sonner": "2.0.7",
76
- "tailwind-merge": "3.5.0",
78
+ "tailwind-merge": "3.6.0",
77
79
  "tw-animate-css": "1.4.0",
78
80
  "vaul": "1.1.2"
79
81
  },
@@ -83,17 +85,17 @@
83
85
  },
84
86
  "devDependencies": {
85
87
  "@bosh-code/tsdown-plugin-tailwindcss": "1.0.1",
86
- "@tailwindcss/postcss": "4.2.4",
88
+ "@tailwindcss/postcss": "4.3.0",
87
89
  "@testing-library/react": "16.3.2",
88
- "@tsdown/css": "0.21.10",
89
- "@turbo/gen": "2.9.8",
90
- "@types/node": "25.6.0",
91
- "@types/react": "19.2.14",
90
+ "@tsdown/css": "0.22.0",
91
+ "@turbo/gen": "2.9.14",
92
+ "@types/node": "25.9.1",
93
+ "@types/react": "19.2.15",
92
94
  "@types/react-dom": "19.2.3",
93
95
  "jsdom": "29.1.1",
94
- "tailwindcss": "4.2.4",
95
- "tsdown": "0.21.10",
96
+ "tailwindcss": "4.3.0",
97
+ "tsdown": "0.22.0",
96
98
  "typescript": "6.0.3",
97
- "vitest": "4.1.5"
99
+ "vitest": "4.1.6"
98
100
  }
99
101
  }
@@ -56,6 +56,7 @@ To set up a consumer project after installing `@uselets/ui`, run `bun uselets in
56
56
  - For toast usage, mount `Toaster` near the app root before calling `toast(...)`.
57
57
  - For package theme helpers, use `ThemeProvider`, `useTheme`, and `ModeToggle` from `@uselets/ui/theme` or the root barrel.
58
58
  - For form fields, `Field` automatically shares a generated control id with `FieldLabel` and labelable controls when explicit ids are omitted; pass explicit matching `htmlFor` and `id` only when a stable custom id is required.
59
+ - For client-side tabular data, use `DataTable` from `@uselets/ui/data-table`. Defaults are bare: `enableRowSelection`, `enableSorting`, and `showPagination` are all `false`. Set `enableRowSelection` to auto-prepend the selection checkbox column (no need to add `createDataTableSelectColumn` manually); set `enableSorting` to make `DataTableColumnHeader` clickable for sort. `filterColumns` accepts a single id or an array; with 2+ ids the toolbar renders an `InputGroup` with a column selector on the left. `toolbarActions` always sits at the right of the toolbar. All labels default to pt-BR; override any subset through `labels` (`Partial<DataTableLabels>`), with full defaults exposed as `defaultDataTableLabels`. Use `variant` on `DataTable` (or directly on the base `Table`) to switch chrome: `"line"` (default) renders only horizontal separators between rows; `"outline"` adds a rounded outer border around the table. The base `Table` from `@uselets/ui/table` renders only the `<table>` element and is striped by default (pass `striped={false}` to disable). Wrap it in `TableContainer` to control scroll; `minWidth` lives on `TableContainer`. Combine `stickyHeader` on `Table` with `max-h` + `overflow-y-auto` on the `TableContainer` to keep the `<thead>` visible during vertical scroll. `DataTable` wraps its `Table` in `TableContainer` automatically.
59
60
 
60
61
  ## Quick Routing
61
62
 
@@ -50,6 +50,10 @@ Unless noted otherwise, each module also exports its public `*Props` types from
50
50
  - Import path: `@uselets/ui/hooks/use-local-storage`
51
51
  - Exports: `useLocalStorage`, `UseLocalStorageOptions`
52
52
  - Notes: returns a `[value, setValue]` tuple, stores values with JSON serialization, and syncs storage changes between browser tabs by default (`sync: true`)
53
+ - `use-mask`
54
+ - Import path: `@uselets/ui/hooks/use-mask`
55
+ - Exports: `useMask`, `UseMaskOptions`, `MaskOptions`, `Replacement`, `Track`, `TrackingData`, `Modify`, `ModifiedData`
56
+ - Notes: thin wrapper over `@react-input/mask`'s `useMask` that defaults `replacement` to `{ 9: /\d/, a: /[A-Za-z]/, '*': /./ }`; returns a `MutableRefObject<HTMLInputElement>` to attach to any input. Consumers may override `replacement`.
53
57
  - `utils`
54
58
  - Import path: `@uselets/ui/utils`
55
59
  - Exports: `cn`, `randomId`
@@ -119,10 +123,10 @@ Unless noted otherwise, each module also exports its public `*Props` types from
119
123
  - Notes: form layout and validation helpers; `Field` automatically provides a generated control id to `FieldLabel` and labelable descendants when explicit ids are omitted
120
124
  - `grid`
121
125
  - Exports: `Grid`, `GridItem`
122
- - Notes: 12-column CSS grid by default. `Grid` supports spacing props plus direct `templateRows` and `templateColumns`; `GridItem` defaults to `colSpan={12}` and supports numeric `colSpan` and `rowSpan`. Use `useBreakpointValue` when these values need to change by breakpoint.
126
+ - Notes: 12-column CSS grid by default. `Grid` supports spacing props plus direct `templateRows` and `templateColumns`; spacing values `xs` to `xl` map to `4/8/16/24/32px`; `GridItem` defaults to `colSpan={12}` and supports numeric `colSpan` and `rowSpan`. Use `useBreakpointValue` when these values need to change by breakpoint.
123
127
  - `simple-grid`
124
128
  - Exports: `SimpleGrid`
125
- - Notes: simple grid with `cols`, `gap`, `rowGap`, and `columnGap`. `cols` accepts a number; spacing uses the `xs` to `xl` scale. Use `useBreakpointValue` when column count needs responsive values.
129
+ - Notes: simple grid with `cols`, `gap`, `rowGap`, and `columnGap`. `cols` accepts a number; spacing values `xs` to `xl` map to `4/8/16/24/32px`. Use `useBreakpointValue` when column count needs responsive values.
126
130
  - `visibility`
127
131
  - Exports: `HiddenFrom`, `VisibleFrom`
128
132
  - Notes: breakpoint visibility wrappers. `HiddenFrom breakpoint="md"` hides at `md` and above; `VisibleFrom breakpoint="md"` shows at `md` and above.
@@ -135,6 +139,9 @@ Unless noted otherwise, each module also exports its public `*Props` types from
135
139
  - Notes: compose controls with prefixes, suffixes, or inline actions
136
140
  - `input-otp`
137
141
  - Exports: `InputOTP`, `InputOTPGroup`, `InputOTPSeparator`, `InputOTPSlot`, `REGEXP_ONLY_CHARS`, `REGEXP_ONLY_DIGITS`, `REGEXP_ONLY_DIGITS_AND_CHARS`
142
+ - `mask-input`
143
+ - Exports: `MaskInput`
144
+ - Notes: formatted input backed by `@react-input/mask`. Required prop `mask`; optional `replacement` (string or `Record<string, RegExp>`), `showMask`, `separate`, `track`, `modify`. Default replacement tokens are `9` (digit), `a` (letter), `*` (any). Renders the standard `Input`, so `Field`/`FieldLabel` and all native `<input>` props work as usual.
138
145
  - `checkbox`
139
146
  - Exports: `Checkbox`
140
147
  - Notes: supports `size: 'sm' | 'md' | 'lg'`, `color: UIColor` (default `primary`), `label?: React.ReactNode`, `labelProps`, and automatic `id`/`htmlFor` wiring when a label is provided without an explicit `id`; composed label text follows checkbox size (`sm` 12px, `md` 14px, `lg` 20px)
@@ -209,18 +216,34 @@ Unless noted otherwise, each module also exports its public `*Props` types from
209
216
  - `card`
210
217
  - Exports: `Card`, `CardAction`, `CardContent`, `CardDescription`, `CardFooter`, `CardHeader`, `CardTitle`
211
218
  - `carousel`
212
- - Exports: `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `useCarousel`
219
+ - Exports: `Carousel`, `CarouselContent`, `CarouselItem`, `CarouselPrevious`, `CarouselNext`, `CarouselIndicators`, `useCarousel`
213
220
  - `center`
214
221
  - Exports: `Center`, `centerVariants`
215
222
  - `chart`
216
- - Exports: `Bar`, `BarChart`, `CartesianGrid`, `XAxis`, `ChartContainer`, `ChartLegend`, `ChartLegendContent`, `ChartStyle`, `ChartTooltip`, `ChartTooltipContent`
223
+ - Exports: `ChartContainer`, `ChartLegend`, `ChartLegendContent`, `ChartStyle`, `ChartTooltip`, `ChartTooltipContent`, `ChartConfig`
224
+ - Notes: low-level chart primitives. For app usage prefer the high-level `area-chart`, `bar-chart`, `pie-chart`, `donut-chart` modules. Import raw recharts primitives (`Bar`, `XAxis`, etc.) directly from `recharts` when composing with `ChartContainer`.
225
+ - `area-chart`
226
+ - Exports: `AreaChart`, `AreaChartSeries`, `AreaChartType`, `AreaChartCurveType`, `AreaChartGridAxis`
227
+ - Notes: high-level area chart. Pass `data`, `series` (array of `{ name, label?, color? }`), and `dataKey` (X axis field). Supports `type` (default/stacked/percent/split), `curveType`, `withXAxis`, `withYAxis`, `gridAxis`, `withDots`, `withTooltip`, `withLegend`. Defaults to chart colors `--chart-1..5`.
228
+ - `bar-chart`
229
+ - Exports: `BarChart`, `BarChartSeries`, `BarChartType`, `BarChartOrientation`, `BarChartGridAxis`
230
+ - Notes: high-level bar chart. Same shape as `area-chart` plus `orientation` (vertical/horizontal), `barSize`, `radius`. Supports stacked and percent types.
231
+ - `pie-chart`
232
+ - Exports: `PieChart`, `PieChartBase`, `PieChartDatum`, `buildPieConfig`
233
+ - Notes: high-level pie chart. Pass `data` as `Array<{ name, value, color? }>`. Supports `withLabels`, `labelsPosition`, `startAngle`, `endAngle`, `paddingAngle`, `withLegend`, `withTooltip`. `PieChartBase` is the shared internal renderer used by `donut-chart`.
234
+ - `donut-chart`
235
+ - Exports: `DonutChart`
236
+ - Notes: same data shape as `pie-chart`. Adds `thickness` (donut hole size in px) and `withTotal` + `totalLabel` (renders sum in the center).
217
237
  - `collapsible`
218
238
  - Exports: `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent`
219
239
  - `container`
220
240
  - Exports: `Container`, `containerVariants`
241
+ - `flex`
242
+ - Exports: `Flex`, `flexVariants`
243
+ - Notes: generic flexbox layout primitive. Supports `direction: 'row' | 'row-reverse' | 'column' | 'column-reverse'`, `align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'`, `justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'`, `wrap`, and `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (`4/8/16/24/32px`).
221
244
  - `group`
222
245
  - Exports: `Group`, `groupVariants`
223
- - Notes: supports `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` and `wrap`
246
+ - Notes: horizontal row primitive. Supports `align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'` (default `center`), `justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'`, `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (`4/8/16/24/32px`), and `wrap`
224
247
  - `kbd`
225
248
  - Exports: `Kbd`, `KbdGroup`
226
249
  - `resizable`
@@ -231,9 +254,16 @@ Unless noted otherwise, each module also exports its public `*Props` types from
231
254
  - Exports: `Separator`
232
255
  - `stack`
233
256
  - Exports: `Stack`, `stackVariants`
234
- - Notes: supports `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'`
257
+ - Notes: vertical column primitive. Supports `align: 'start' | 'end' | 'center' | 'baseline' | 'stretch'`, `justify: 'start' | 'end' | 'center' | 'between' | 'around' | 'evenly'`, and `gap: 'xs' | 'sm' | 'md' | 'lg' | 'xl'` (`4/8/16/24/32px`)
235
258
  - `table`
236
- - Exports: `Table`, `TableHeader`, `TableBody`, `TableFooter`, `TableHead`, `TableRow`, `TableCell`, `TableCaption`
259
+ - Exports: `Table`, `TableContainer`, `TableHeader`, `TableBody`, `TableFooter`, `TableHead`, `TableRow`, `TableCell`, `TableCaption`, `tableVariants`
260
+ - Notes: `Table` renders only the `<table>` element (no auto wrapper); striped by default (`striped` prop, default `true`) and supports `stickyHeader`. `variant` (`"line"` default | `"outline"`) controls outer chrome — `"line"` has only horizontal separators between rows; `"outline"` adds a rounded outer border. Outline works standalone (chrome on the `<table>`) or wrapped in `TableContainer` (container detects `data-variant=outline` via `:has()`, applies the chrome on itself, and zeroes it on the `<table>` — keeps sticky headers from painting over the border). Wrap in `TableContainer` for horizontal scroll; `TableContainer` accepts `minWidth` (number in px or any CSS length). For sticky header, set `max-h` + `overflow-y-auto` on the `TableContainer` (via `className` or `style`)
261
+ - `data-table`
262
+ - Exports: `DataTable`, `DataTableRoot`, `DataTableToolbar`, `DataTableHeader`, `DataTableBody`, `DataTablePagination`, `DataTableColumnHeader`, `createDataTableSelectColumn`, `useDataTable`, `useDataTableContext`, `defaultDataTableLabels`, `DataTableLabels`, `UseDataTableOptions`, `DataTableRootProps`, `DataTableToolbarProps`
263
+ - Notes: dual API — high-level `DataTable` (monolithic, simple cases) and compound (`DataTableRoot` + sub-parts, power cases including virtualization). Both share the same underlying logic via `useDataTable`.
264
+ - **High-level `DataTable`:** client-side TanStack Table composition. Defaults are bare — `enableRowSelection`, `enableSorting`, `showPagination` all default to `false` and must be explicitly enabled. Setting `enableRowSelection` auto-prepends the selection column; setting `enableSorting` activates sort buttons in `DataTableColumnHeader`. `filterColumns` accepts `string | string[]`; with 2+ ids the toolbar renders an `InputGroup` with a column selector at the left. `toolbarActions` always renders at the right of the toolbar. `variant` (`"line"` default | `"outline"`) is forwarded to the internal `Table` — pass `"outline"` to wrap the table in a rounded border. All user-facing labels default to pt-BR and are overridable through the `labels` prop (`Partial<DataTableLabels>`)
265
+ - **Compound API:** `DataTableRoot` accepts either `{ columns, data, ...options }` (uncontrolled — creates the `table` instance internally) or `{ table }` (controlled — table instance comes from `useDataTable` called by the consumer). `DataTableToolbar`, `DataTableHeader`, `DataTableBody`, and `DataTablePagination` read the `table` instance from context. `DataTableHeader` renders a full `<thead>`; `DataTableBody` renders a full `<tbody>` — to virtualize, omit `<DataTableBody />` and render your own `<tbody>` instead. `useDataTable(options)` is a thin wrapper around `useReactTable` that bakes in the same defaults `DataTable` uses (row models, selection auto-injection); pass `enablePagination: false` when virtualizing so `table.getRowModel()` returns all rows. `useDataTableContext<TData>()` retrieves the current `table` and merged `labels` inside a custom compound child
266
+ - **Virtualization:** the package is virtualizer-agnostic — it has zero runtime dependency on `@tanstack/react-virtual`. Bring your own virtualizer (TanStack Virtual, `react-window`, `virtua`, or manual). See the *Virtualized lists* section in `references/package-guide.md` for the canonical TanStack Virtual recipe
237
267
  - `timeline`
238
268
  - Exports: `Timeline`, `TimelineConnector`, `TimelineContent`, `TimelineDescription`, `TimelineEmpty`, `TimelineHeader`, `TimelineIcon`, `TimelineItem`, `TimelineTime`, `TimelineTitle`, `timelineVariants`
239
269
  - Notes: timeline flow with root `size`, inherited `iconSize`, item `status`, optional loading/error states, and semantic icon or connector colors
@@ -146,6 +146,7 @@ Use these patterns:
146
146
  ```tsx
147
147
  import { Alert, Button, UnstyledButton, cn, randomId } from '@uselets/ui'
148
148
  import type { UIColor } from '@uselets/ui'
149
+ import { DataTable } from '@uselets/ui/data-table'
149
150
  import { Dialog } from '@uselets/ui/dialog'
150
151
  import { useIsMobile } from '@uselets/ui/hooks/use-mobile'
151
152
  import { useDisclosure } from '@uselets/ui/hooks/use-disclosure'
@@ -161,7 +162,9 @@ Guidance:
161
162
  - Use `@uselets/ui/hooks/<hook-module>` for dedicated hook subpaths.
162
163
  - Do not invent undocumented subpaths.
163
164
 
164
- Public hook subpaths currently include `hooks/use-mobile`, `hooks/use-disclosure`, `hooks/use-interval`, `hooks/use-idle`, `hooks/use-document-visibility`, and `hooks/use-local-storage`. Legacy `@uselets/ui/use-*` paths are not public entrypoints. Utility helpers are available from `@uselets/ui/utils`, and shared UI color helpers are available from the root barrel or `@uselets/ui/lib/color`.
165
+ Public hook subpaths currently include `hooks/use-mobile`, `hooks/use-disclosure`, `hooks/use-interval`, `hooks/use-idle`, `hooks/use-document-visibility`, `hooks/use-local-storage`, and `hooks/use-mask`. Legacy `@uselets/ui/use-*` paths are not public entrypoints. Utility helpers are available from `@uselets/ui/utils`, and shared UI color helpers are available from the root barrel or `@uselets/ui/lib/color`.
166
+
167
+ Use `DataTable` from `@uselets/ui/data-table` for client-side tables with sorting, filtering, pagination, row selection, column visibility, formatted cells, and custom action columns. All user-facing labels default to Portuguese (pt-BR) and can be overridden through the `labels` prop (`Partial<DataTableLabels>`); the full defaults are exported as `defaultDataTableLabels`.
165
168
 
166
169
  `useLocalStorage` uses an options object and returns a tuple like `useState`:
167
170
 
@@ -290,6 +293,138 @@ The `RadioGroup` component supports `color: UIColor`, with `primary` as the defa
290
293
 
291
294
  The `DateRangePicker` component is a compound popover date range picker built on `Calendar` range mode. Use `DateRange | null` for controlled values; applying an empty draft or resetting emits `null` through `onValueChange`. Fixed presets, relative-day inputs, date inputs, and the calendar all edit the same draft, and `DateRangePickerPreset` displays a selected state when its range matches the current draft by calendar day.
292
295
 
296
+ ## DataTable compound API and virtualization
297
+
298
+ `DataTable` ships two complementary APIs:
299
+
300
+ - **High-level `DataTable`** — monolithic, ergonomic, covers the common case (pagination, sorting, row selection, filters, toolbar actions, custom labels). Public surface is unchanged from earlier versions.
301
+ - **Compound parts** — `DataTableRoot`, `DataTableToolbar`, `DataTableHeader`, `DataTableBody`, `DataTablePagination`. The high-level component is itself implemented in terms of these, so behavior never diverges.
302
+
303
+ Use the compound API when you need to:
304
+
305
+ - Virtualize the body (large datasets).
306
+ - Place the toolbar, pagination, or other actions in non-default positions (e.g. toolbar inside a card header, pagination in a footer slot).
307
+ - Share the same `table` instance with code that lives outside the `DataTable` subtree (e.g. an "Export selected" button rendered above the page).
308
+
309
+ ### Compound shape (non-virtualized)
310
+
311
+ ```tsx
312
+ import {
313
+ DataTableBody,
314
+ DataTableHeader,
315
+ DataTablePagination,
316
+ DataTableRoot,
317
+ DataTableToolbar
318
+ } from '@uselets/ui/data-table'
319
+ import { Table, TableContainer } from '@uselets/ui/table'
320
+
321
+ <DataTableRoot columns={columns} data={data} enableRowSelection enableSorting>
322
+ <DataTableToolbar filterColumns={['name', 'email']} />
323
+ <TableContainer className="rounded-lg border">
324
+ <Table>
325
+ <DataTableHeader />
326
+ <DataTableBody />
327
+ </Table>
328
+ </TableContainer>
329
+ <DataTablePagination />
330
+ </DataTableRoot>
331
+ ```
332
+
333
+ `DataTableRoot` accepts either `{ columns, data, ...options }` (uncontrolled — creates the `table` instance internally) or `{ table }` (controlled — table instance comes from `useDataTable` called by the consumer). Use the controlled form whenever you need access to the `table` instance in the same scope as the JSX (e.g. for virtualization or external action buttons).
334
+
335
+ ### Virtualized lists (bring your own virtualizer)
336
+
337
+ `@uselets/ui` has **zero runtime dependency** on any virtualization library. Pick the one you prefer (`@tanstack/react-virtual`, `react-window`, `virtua`, or even a manual implementation) and replace `<DataTableBody />` with a `<tbody>` that renders only the virtual rows.
338
+
339
+ Canonical recipe using `@tanstack/react-virtual` (the most common choice in the React ecosystem):
340
+
341
+ ```tsx
342
+ import { useVirtualizer } from '@tanstack/react-virtual'
343
+ import {
344
+ DataTableHeader,
345
+ DataTableRoot,
346
+ DataTableToolbar,
347
+ useDataTable
348
+ } from '@uselets/ui/data-table'
349
+ import { Table, TableCell, TableContainer, TableRow } from '@uselets/ui/table'
350
+ import * as React from 'react'
351
+
352
+ function VirtualizedPayments({ data }: { data: Payment[] }) {
353
+ const table = useDataTable<Payment>({
354
+ columns,
355
+ data,
356
+ enableRowSelection: true,
357
+ enableSorting: true,
358
+ enablePagination: false // IMPORTANT: returns all rows from getRowModel()
359
+ })
360
+
361
+ const parentRef = React.useRef<HTMLDivElement>(null)
362
+ const rows = table.getRowModel().rows
363
+ const colSpan = table.getVisibleLeafColumns().length
364
+
365
+ const virtualizer = useVirtualizer({
366
+ count: rows.length,
367
+ getScrollElement: () => parentRef.current,
368
+ estimateSize: () => 52,
369
+ overscan: 8
370
+ })
371
+
372
+ const virtualItems = virtualizer.getVirtualItems()
373
+ const totalSize = virtualizer.getTotalSize()
374
+ const paddingTop = virtualItems[0]?.start ?? 0
375
+ const paddingBottom = totalSize - (virtualItems.at(-1)?.end ?? 0)
376
+
377
+ return (
378
+ <DataTableRoot table={table}>
379
+ <DataTableToolbar filterColumns={['customer']} />
380
+ <TableContainer ref={parentRef} className="h-[600px] rounded-lg border">
381
+ <Table stickyHeader>
382
+ <DataTableHeader />
383
+ <tbody>
384
+ {paddingTop > 0 && (
385
+ <tr aria-hidden>
386
+ <td colSpan={colSpan} style={{ height: paddingTop, padding: 0, border: 0 }} />
387
+ </tr>
388
+ )}
389
+ {virtualItems.map((vi) => {
390
+ const row = rows[vi.index]
391
+ return (
392
+ <TableRow key={row.id} data-state={row.getIsSelected() ? 'selected' : undefined}>
393
+ {row.getVisibleCells().map((cell) => {
394
+ const renderer = cell.column.columnDef.cell
395
+ return (
396
+ <TableCell key={cell.id}>
397
+ {typeof renderer === 'function' ? renderer(cell.getContext()) : (cell.getValue() as React.ReactNode)}
398
+ </TableCell>
399
+ )
400
+ })}
401
+ </TableRow>
402
+ )
403
+ })}
404
+ {paddingBottom > 0 && (
405
+ <tr aria-hidden>
406
+ <td colSpan={colSpan} style={{ height: paddingBottom, padding: 0, border: 0 }} />
407
+ </tr>
408
+ )}
409
+ </tbody>
410
+ </Table>
411
+ </TableContainer>
412
+ </DataTableRoot>
413
+ )
414
+ }
415
+ ```
416
+
417
+ Key points and gotchas:
418
+
419
+ - **Bring your own virtualizer.** The example uses `@tanstack/react-virtual` for clarity, but any virtualizer works. Replace `useVirtualizer` and the `paddingTop`/`paddingBottom` spacer-row computation with the equivalent from `react-window`, `virtua`, or your own. The compounds (`DataTableRoot`, `DataTableToolbar`, `DataTableHeader`) and primitives (`Table`, `TableContainer`, `TableRow`, `TableCell`) do not change.
420
+ - **`enablePagination: false`** is required so `table.getRowModel()` returns the full filtered/sorted dataset instead of just the current page. Pagination and virtualization are mutually exclusive — pick one.
421
+ - **Sticky header.** Set `stickyHeader` on `<Table>` and a fixed height on `<TableContainer>`. The CSS-only sticky header in the primitive `Table` continues to work because `<thead>` stays in normal flow.
422
+ - **Row height.** `estimateSize` is fine when rows are uniform. For dynamic heights, pass `measureElement={virtualizer.measureElement}` on each `TableRow`'s `ref` to let TanStack Virtual measure them.
423
+ - **Spacer rows vs absolute positioning.** Two valid approaches exist: spacer rows (`paddingTop`/`paddingBottom` as shown — keeps rows in normal flow, simpler) or `position: absolute; transform: translateY(start)` on every row (requires `position: relative` on `<tbody>` and `height: totalSize`). The spacer-row approach is generally easier and works across virtualizers.
424
+ - **Row selection, sorting, and filtering all work** in the virtualized mode because they live on the `table` instance — only rendering of the body is virtualized.
425
+
426
+ A full working example lives in `apps/web/src/stories/data-table-virtualized.stories.tsx`.
427
+
293
428
  ## Component selection
294
429
 
295
430
  Choose components by intent:
@@ -305,7 +440,9 @@ Choose components by intent:
305
440
  - typography:
306
441
  - `Anchor`, `Code`, `Heading`, `List`, `Text`
307
442
  - layout and display:
308
- - `Card`, `Accordion`, `Collapsible`, `Container`, `Group`, `Stack`, `Center`, `Separator`, `ScrollArea`, `Resizable`, `Table`, `Timeline`, `Chart`, `Carousel`, `Avatar`, `Empty`, `Kbd`
443
+ - `Card`, `Accordion`, `Collapsible`, `Container`, `Flex`, `Group`, `Stack`, `Center`, `Separator`, `ScrollArea`, `Resizable`, `Table`, `Timeline`, `Chart`, `Carousel`, `Avatar`, `Empty`, `Kbd`
444
+
445
+ Layout spacing props named `gap`, `rowGap`, and `columnGap` use the shared `xs` to `xl` scale: `4/8/16/24/32px`.
309
446
 
310
447
  ## Inspection workflow
311
448