torch-glare 2.5.6 → 2.6.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 (362) hide show
  1. package/README.md +4 -3
  2. package/dist/bin/index.js +9 -24
  3. package/dist/bin/index.js.map +1 -1
  4. package/dist/src/commands/add.d.ts +2 -2
  5. package/dist/src/commands/add.d.ts.map +1 -1
  6. package/dist/src/commands/add.js +4 -70
  7. package/dist/src/commands/add.js.map +1 -1
  8. package/dist/src/commands/hook.d.ts +5 -4
  9. package/dist/src/commands/hook.d.ts.map +1 -1
  10. package/dist/src/commands/hook.js +7 -75
  11. package/dist/src/commands/hook.js.map +1 -1
  12. package/dist/src/commands/init.js +1 -1
  13. package/dist/src/commands/init.js.map +1 -1
  14. package/dist/src/commands/layout.d.ts +5 -4
  15. package/dist/src/commands/layout.d.ts.map +1 -1
  16. package/dist/src/commands/layout.js +7 -75
  17. package/dist/src/commands/layout.js.map +1 -1
  18. package/dist/src/commands/provider.d.ts +5 -4
  19. package/dist/src/commands/provider.d.ts.map +1 -1
  20. package/dist/src/commands/provider.js +7 -75
  21. package/dist/src/commands/provider.js.map +1 -1
  22. package/dist/src/commands/update.d.ts.map +1 -1
  23. package/dist/src/commands/update.js +78 -43
  24. package/dist/src/commands/update.js.map +1 -1
  25. package/dist/src/commands/utils.d.ts +5 -4
  26. package/dist/src/commands/utils.d.ts.map +1 -1
  27. package/dist/src/commands/utils.js +7 -80
  28. package/dist/src/commands/utils.js.map +1 -1
  29. package/dist/src/shared/addFromRegistry.d.ts +16 -0
  30. package/dist/src/shared/addFromRegistry.d.ts.map +1 -0
  31. package/dist/src/shared/addFromRegistry.js +88 -0
  32. package/dist/src/shared/addFromRegistry.js.map +1 -0
  33. package/dist/src/shared/getInstallCommand.d.ts.map +1 -1
  34. package/dist/src/shared/getInstallCommand.js +5 -0
  35. package/dist/src/shared/getInstallCommand.js.map +1 -1
  36. package/dist/src/shared/installDependencies.d.ts +0 -10
  37. package/dist/src/shared/installDependencies.d.ts.map +1 -1
  38. package/dist/src/shared/installDependencies.js +0 -17
  39. package/dist/src/shared/installDependencies.js.map +1 -1
  40. package/dist/src/shared/installFromPlan.d.ts +7 -5
  41. package/dist/src/shared/installFromPlan.d.ts.map +1 -1
  42. package/dist/src/shared/installFromPlan.js +103 -36
  43. package/dist/src/shared/installFromPlan.js.map +1 -1
  44. package/dist/src/shared/loadRegistry.d.ts +12 -3
  45. package/dist/src/shared/loadRegistry.d.ts.map +1 -1
  46. package/dist/src/shared/loadRegistry.js +19 -20
  47. package/dist/src/shared/loadRegistry.js.map +1 -1
  48. package/dist/src/shared/registryClient.d.ts +19 -0
  49. package/dist/src/shared/registryClient.d.ts.map +1 -0
  50. package/dist/src/shared/registryClient.js +128 -0
  51. package/dist/src/shared/registryClient.js.map +1 -0
  52. package/dist/src/shared/resolveEntry.d.ts +10 -7
  53. package/dist/src/shared/resolveEntry.d.ts.map +1 -1
  54. package/dist/src/shared/resolveEntry.js +18 -13
  55. package/dist/src/shared/resolveEntry.js.map +1 -1
  56. package/dist/src/shared/suggestOtherCommand.d.ts +7 -1
  57. package/dist/src/shared/suggestOtherCommand.d.ts.map +1 -1
  58. package/dist/src/shared/suggestOtherCommand.js +29 -23
  59. package/dist/src/shared/suggestOtherCommand.js.map +1 -1
  60. package/dist/src/types/main.d.ts +31 -2
  61. package/dist/src/types/main.d.ts.map +1 -1
  62. package/package.json +6 -11
  63. package/apps/lib/components/ActionButton.tsx +0 -93
  64. package/apps/lib/components/ActionsGroup.tsx +0 -27
  65. package/apps/lib/components/AlertDialog.tsx +0 -204
  66. package/apps/lib/components/Avatar.tsx +0 -46
  67. package/apps/lib/components/Badge.tsx +0 -250
  68. package/apps/lib/components/BadgeField.tsx +0 -403
  69. package/apps/lib/components/Breadcrumb.tsx +0 -280
  70. package/apps/lib/components/Button.tsx +0 -325
  71. package/apps/lib/components/ButtonGroup.tsx +0 -190
  72. package/apps/lib/components/Calendar.tsx +0 -115
  73. package/apps/lib/components/Card.tsx +0 -95
  74. package/apps/lib/components/Checkbox.tsx +0 -42
  75. package/apps/lib/components/ColorPicker.tsx +0 -441
  76. package/apps/lib/components/ConclusionHeader.tsx +0 -148
  77. package/apps/lib/components/ContextMenu.tsx +0 -571
  78. package/apps/lib/components/CountBadge.tsx +0 -52
  79. package/apps/lib/components/DataTable.tsx +0 -213
  80. package/apps/lib/components/DataViews/badge.ts +0 -49
  81. package/apps/lib/components/DataViews/cell.tsx +0 -324
  82. package/apps/lib/components/DataViews/context.ts +0 -144
  83. package/apps/lib/components/DataViews/data-views.tsx +0 -395
  84. package/apps/lib/components/DataViews/filters/children.tsx +0 -98
  85. package/apps/lib/components/DataViews/filters/custom.tsx +0 -34
  86. package/apps/lib/components/DataViews/filters/filters.tsx +0 -157
  87. package/apps/lib/components/DataViews/filters/index.ts +0 -4
  88. package/apps/lib/components/DataViews/filters/labelled.tsx +0 -20
  89. package/apps/lib/components/DataViews/filters/presets.tsx +0 -65
  90. package/apps/lib/components/DataViews/filters/sync.tsx +0 -35
  91. package/apps/lib/components/DataViews/filters/values.ts +0 -173
  92. package/apps/lib/components/DataViews/header.tsx +0 -220
  93. package/apps/lib/components/DataViews/hooks/index.ts +0 -5
  94. package/apps/lib/components/DataViews/hooks/useActiveRow.ts +0 -22
  95. package/apps/lib/components/DataViews/hooks/useControllable.ts +0 -52
  96. package/apps/lib/components/DataViews/index.ts +0 -88
  97. package/apps/lib/components/DataViews/panel/columns.tsx +0 -153
  98. package/apps/lib/components/DataViews/panel/controls.tsx +0 -106
  99. package/apps/lib/components/DataViews/panel/index.ts +0 -3
  100. package/apps/lib/components/DataViews/panel/panel.tsx +0 -164
  101. package/apps/lib/components/DataViews/panel/saved-views.tsx +0 -67
  102. package/apps/lib/components/DataViews/panel/section.tsx +0 -81
  103. package/apps/lib/components/DataViews/panel/sort.tsx +0 -42
  104. package/apps/lib/components/DataViews/panel/tab.tsx +0 -31
  105. package/apps/lib/components/DataViews/slots.ts +0 -72
  106. package/apps/lib/components/DataViews/states.tsx +0 -73
  107. package/apps/lib/components/DataViews/types.ts +0 -510
  108. package/apps/lib/components/DataViews/views/board-view.tsx +0 -381
  109. package/apps/lib/components/DataViews/views/card-rows.tsx +0 -36
  110. package/apps/lib/components/DataViews/views/inbox-view.tsx +0 -260
  111. package/apps/lib/components/DataViews/views/pane-views.tsx +0 -196
  112. package/apps/lib/components/DataViews/views/table-view.tsx +0 -493
  113. package/apps/lib/components/DataViews/views/tree-view.tsx +0 -367
  114. package/apps/lib/components/DatePicker.tsx +0 -232
  115. package/apps/lib/components/Dialog.tsx +0 -116
  116. package/apps/lib/components/Divider.tsx +0 -28
  117. package/apps/lib/components/Drawer.tsx +0 -451
  118. package/apps/lib/components/DropdownMenu.tsx +0 -554
  119. package/apps/lib/components/FieldHint.tsx +0 -69
  120. package/apps/lib/components/Form.tsx +0 -182
  121. package/apps/lib/components/FormBuilder/context.ts +0 -85
  122. package/apps/lib/components/FormBuilder/field-kind.ts +0 -28
  123. package/apps/lib/components/FormBuilder/fields/ChoiceFields.tsx +0 -60
  124. package/apps/lib/components/FormBuilder/fields/ColorField.tsx +0 -59
  125. package/apps/lib/components/FormBuilder/fields/CustomField.tsx +0 -11
  126. package/apps/lib/components/FormBuilder/fields/DateField.tsx +0 -32
  127. package/apps/lib/components/FormBuilder/fields/FieldArray.tsx +0 -74
  128. package/apps/lib/components/FormBuilder/fields/FieldShell.tsx +0 -168
  129. package/apps/lib/components/FormBuilder/fields/FileField.tsx +0 -39
  130. package/apps/lib/components/FormBuilder/fields/OptionListFields.tsx +0 -132
  131. package/apps/lib/components/FormBuilder/fields/OtpField.tsx +0 -31
  132. package/apps/lib/components/FormBuilder/fields/PhoneField.tsx +0 -109
  133. package/apps/lib/components/FormBuilder/fields/RichTextEditorField.tsx +0 -31
  134. package/apps/lib/components/FormBuilder/fields/SelectField.tsx +0 -114
  135. package/apps/lib/components/FormBuilder/fields/SignatureField.tsx +0 -157
  136. package/apps/lib/components/FormBuilder/fields/SliderField.tsx +0 -67
  137. package/apps/lib/components/FormBuilder/fields/SwitchBoxField.tsx +0 -41
  138. package/apps/lib/components/FormBuilder/fields/TableField.tsx +0 -336
  139. package/apps/lib/components/FormBuilder/fields/TextField.tsx +0 -213
  140. package/apps/lib/components/FormBuilder/fields/TreeSelectField.tsx +0 -43
  141. package/apps/lib/components/FormBuilder/fields/countries.ts +0 -303
  142. package/apps/lib/components/FormBuilder/fields/index.ts +0 -25
  143. package/apps/lib/components/FormBuilder/form-builder.tsx +0 -198
  144. package/apps/lib/components/FormBuilder/index.ts +0 -31
  145. package/apps/lib/components/FormBuilder/numberFormat.ts +0 -16
  146. package/apps/lib/components/FormBuilder/submit.tsx +0 -67
  147. package/apps/lib/components/FormBuilder/types.ts +0 -316
  148. package/apps/lib/components/FormRenderer/FormDrawer.tsx +0 -250
  149. package/apps/lib/components/FormRenderer/detail.tsx +0 -256
  150. package/apps/lib/components/FormRenderer/form-renderer.tsx +0 -358
  151. package/apps/lib/components/FormRenderer/header.tsx +0 -88
  152. package/apps/lib/components/FormRenderer/index.ts +0 -17
  153. package/apps/lib/components/FormRenderer/notch-action.tsx +0 -64
  154. package/apps/lib/components/FormRenderer/section.tsx +0 -39
  155. package/apps/lib/components/FormRenderer/stepper.tsx +0 -393
  156. package/apps/lib/components/FormRenderer/types.ts +0 -115
  157. package/apps/lib/components/FormSummary.tsx +0 -282
  158. package/apps/lib/components/HeaderBar.tsx +0 -117
  159. package/apps/lib/components/ImageAttachment.tsx +0 -209
  160. package/apps/lib/components/InnerLabelField.tsx +0 -154
  161. package/apps/lib/components/Input.tsx +0 -224
  162. package/apps/lib/components/InputField.tsx +0 -147
  163. package/apps/lib/components/InputOTP.tsx +0 -84
  164. package/apps/lib/components/Label.tsx +0 -129
  165. package/apps/lib/components/LabelField.tsx +0 -76
  166. package/apps/lib/components/LabeledCheckBox.tsx +0 -51
  167. package/apps/lib/components/LabeledRadio.tsx +0 -51
  168. package/apps/lib/components/LinkButton.tsx +0 -94
  169. package/apps/lib/components/LoginButton.tsx +0 -51
  170. package/apps/lib/components/PasswordLevel.tsx +0 -58
  171. package/apps/lib/components/Popover.tsx +0 -312
  172. package/apps/lib/components/ProfileMenu.tsx +0 -85
  173. package/apps/lib/components/Radio.tsx +0 -59
  174. package/apps/lib/components/RadioCard.tsx +0 -50
  175. package/apps/lib/components/ScrollArea.tsx +0 -54
  176. package/apps/lib/components/SearchField.tsx +0 -62
  177. package/apps/lib/components/SearchableSelect.tsx +0 -318
  178. package/apps/lib/components/SearchableTable.tsx +0 -344
  179. package/apps/lib/components/SearchableTree.tsx +0 -485
  180. package/apps/lib/components/SearchableTreeDialog.tsx +0 -502
  181. package/apps/lib/components/SectionBlock.tsx +0 -168
  182. package/apps/lib/components/Select.tsx +0 -347
  183. package/apps/lib/components/SimpleSelect.tsx +0 -225
  184. package/apps/lib/components/Skeleton.tsx +0 -12
  185. package/apps/lib/components/SlideDatePicker.tsx +0 -255
  186. package/apps/lib/components/SpinLoading.tsx +0 -174
  187. package/apps/lib/components/Stepper.tsx +0 -480
  188. package/apps/lib/components/Switch.tsx +0 -82
  189. package/apps/lib/components/TabFormItem.tsx +0 -153
  190. package/apps/lib/components/TabSwitch.tsx +0 -172
  191. package/apps/lib/components/Table.tsx +0 -597
  192. package/apps/lib/components/TextEditor/ChartBlockTool.ts +0 -676
  193. package/apps/lib/components/TextEditor/RichTextField.tsx +0 -46
  194. package/apps/lib/components/TextEditor/TableDnDWrapper.ts +0 -462
  195. package/apps/lib/components/TextEditor/TextEditor.tsx +0 -1164
  196. package/apps/lib/components/TextEditor/TextEditorToolbar.tsx +0 -429
  197. package/apps/lib/components/TextEditor/editor-tools/AlignmentTune.ts +0 -70
  198. package/apps/lib/components/TextEditor/editor-tools/ColorInlineTool.ts +0 -50
  199. package/apps/lib/components/TextEditor/editor-tools/StrikethroughInlineTool.ts +0 -48
  200. package/apps/lib/components/TextEditor/editor-tools/inlineFormat.ts +0 -98
  201. package/apps/lib/components/TextEditor/editorjs.d.ts +0 -60
  202. package/apps/lib/components/TextEditor/index.ts +0 -7
  203. package/apps/lib/components/TextEditor/markdownParser.ts +0 -346
  204. package/apps/lib/components/Textarea.tsx +0 -117
  205. package/apps/lib/components/Timeline.tsx +0 -263
  206. package/apps/lib/components/Toast.tsx +0 -87
  207. package/apps/lib/components/Toggle.tsx +0 -125
  208. package/apps/lib/components/ToggleButton.tsx +0 -140
  209. package/apps/lib/components/Tooltip.tsx +0 -116
  210. package/apps/lib/components/TransparentLabel.tsx +0 -71
  211. package/apps/lib/components/TreeDropDown.tsx +0 -115
  212. package/apps/lib/components/TreeFolder/TreeFolder.tsx +0 -380
  213. package/apps/lib/components/TreeFolder/TreeFolderBreadcrumb.tsx +0 -75
  214. package/apps/lib/components/TreeFolder/TreeFolderRow.tsx +0 -354
  215. package/apps/lib/components/TreeFolder/TreeFolderStyles.tsx +0 -60
  216. package/apps/lib/components/TreeFolder/icons.tsx +0 -60
  217. package/apps/lib/components/TreeFolder/index.ts +0 -24
  218. package/apps/lib/components/TreeFolder/treeFolderUtils.ts +0 -109
  219. package/apps/lib/components/TreeFolder/types.ts +0 -77
  220. package/apps/lib/components/TreeFolder/useTreeFolderDnD.ts +0 -118
  221. package/apps/lib/hooks/useActiveTreeItem.ts +0 -61
  222. package/apps/lib/hooks/useClickOutside.ts +0 -27
  223. package/apps/lib/hooks/useDragDrop.tsx +0 -365
  224. package/apps/lib/hooks/useHtmlDir.ts +0 -31
  225. package/apps/lib/hooks/useInfiniteScroll.ts +0 -108
  226. package/apps/lib/hooks/useIsMobile.ts +0 -21
  227. package/apps/lib/hooks/useResize.ts +0 -67
  228. package/apps/lib/hooks/useTagSelection.ts +0 -300
  229. package/apps/lib/layouts/CNLayout.tsx +0 -372
  230. package/apps/lib/layouts/DataViewCard.tsx +0 -62
  231. package/apps/lib/layouts/FieldSection.tsx +0 -91
  232. package/apps/lib/layouts/TreeSubLayout.tsx +0 -173
  233. package/apps/lib/providers/ThemeProvider.tsx +0 -84
  234. package/apps/lib/registry.json +0 -1217
  235. package/apps/lib/tsconfig.json +0 -9
  236. package/apps/lib/tsconfig.tsbuildinfo +0 -1
  237. package/apps/lib/utils/cn.ts +0 -6
  238. package/apps/lib/utils/color.ts +0 -175
  239. package/apps/lib/utils/dataViews/path.ts +0 -67
  240. package/apps/lib/utils/dataViews/query.ts +0 -73
  241. package/apps/lib/utils/dataViews/types.ts +0 -187
  242. package/apps/lib/utils/dateFormat.ts +0 -108
  243. package/apps/lib/utils/resize.ts +0 -31
  244. package/apps/lib/utils/scroller.ts +0 -26
  245. package/apps/lib/utils/types.ts +0 -16
  246. package/docs/BLOCKS.md +0 -129
  247. package/docs/CHANGELOG-1.1.16.md +0 -108
  248. package/docs/Cover.png +0 -0
  249. package/docs/README.md +0 -215
  250. package/docs/components/action-button.md +0 -638
  251. package/docs/components/actions-group.md +0 -708
  252. package/docs/components/alert-dialog.md +0 -920
  253. package/docs/components/avatar.md +0 -709
  254. package/docs/components/badge-field.md +0 -738
  255. package/docs/components/badge.md +0 -301
  256. package/docs/components/breadcrumb.md +0 -930
  257. package/docs/components/button-group.md +0 -650
  258. package/docs/components/button.md +0 -639
  259. package/docs/components/calendar.md +0 -555
  260. package/docs/components/card.md +0 -622
  261. package/docs/components/chart-block-tool.md +0 -129
  262. package/docs/components/checkbox.md +0 -699
  263. package/docs/components/cn-layout.md +0 -854
  264. package/docs/components/color-picker.md +0 -101
  265. package/docs/components/conclusion-header.md +0 -80
  266. package/docs/components/context-menu.md +0 -483
  267. package/docs/components/count-badge.md +0 -560
  268. package/docs/components/data-table.md +0 -854
  269. package/docs/components/data-views/backend-response.md +0 -324
  270. package/docs/components/data-views/examples/a11y-rtl.md +0 -250
  271. package/docs/components/data-views/examples/api-orders-route.md +0 -130
  272. package/docs/components/data-views/examples/fields.md +0 -362
  273. package/docs/components/data-views/examples/filters.md +0 -307
  274. package/docs/components/data-views/examples/inbox-routing.md +0 -218
  275. package/docs/components/data-views/examples/index.md +0 -29
  276. package/docs/components/data-views/examples/overview.md +0 -244
  277. package/docs/components/data-views/examples/panel.md +0 -212
  278. package/docs/components/data-views/examples/scale.md +0 -231
  279. package/docs/components/data-views/examples/server-side.md +0 -210
  280. package/docs/components/data-views/examples/state.md +0 -250
  281. package/docs/components/data-views/examples/tree-custom.md +0 -388
  282. package/docs/components/data-views/examples/view-registry.md +0 -313
  283. package/docs/components/data-views/examples/views.md +0 -534
  284. package/docs/components/data-views/guide.md +0 -405
  285. package/docs/components/data-views/index.md +0 -1521
  286. package/docs/components/data-views/migration.md +0 -81
  287. package/docs/components/date-picker.md +0 -857
  288. package/docs/components/dialog.md +0 -929
  289. package/docs/components/divider.md +0 -766
  290. package/docs/components/drawer.md +0 -654
  291. package/docs/components/dropdown-menu.md +0 -668
  292. package/docs/components/field-hint.md +0 -898
  293. package/docs/components/field-section.md +0 -781
  294. package/docs/components/form-builder.md +0 -284
  295. package/docs/components/form-renderer.md +0 -396
  296. package/docs/components/form-summary.md +0 -138
  297. package/docs/components/form.md +0 -785
  298. package/docs/components/header-bar.md +0 -188
  299. package/docs/components/image-attachment.md +0 -906
  300. package/docs/components/inner-label-field.md +0 -819
  301. package/docs/components/input-field.md +0 -623
  302. package/docs/components/input-otp.md +0 -756
  303. package/docs/components/input.md +0 -634
  304. package/docs/components/label-field.md +0 -770
  305. package/docs/components/label.md +0 -761
  306. package/docs/components/labeled-check-box.md +0 -642
  307. package/docs/components/labeled-radio.md +0 -796
  308. package/docs/components/link-button.md +0 -685
  309. package/docs/components/login-button.md +0 -872
  310. package/docs/components/password-level.md +0 -803
  311. package/docs/components/popover.md +0 -649
  312. package/docs/components/profile-menu.md +0 -656
  313. package/docs/components/radio-card.md +0 -801
  314. package/docs/components/radio.md +0 -735
  315. package/docs/components/scroll-area.md +0 -691
  316. package/docs/components/search-field.md +0 -702
  317. package/docs/components/searchable-select.md +0 -368
  318. package/docs/components/searchable-table.md +0 -438
  319. package/docs/components/searchable-tree-dialog.md +0 -190
  320. package/docs/components/searchable-tree.md +0 -200
  321. package/docs/components/section-block.md +0 -483
  322. package/docs/components/select.md +0 -709
  323. package/docs/components/simple-select.md +0 -741
  324. package/docs/components/skeleton.md +0 -706
  325. package/docs/components/slide-date-picker.md +0 -837
  326. package/docs/components/spin-loading.md +0 -696
  327. package/docs/components/stepper.md +0 -316
  328. package/docs/components/switch.md +0 -770
  329. package/docs/components/tab-form-item.md +0 -773
  330. package/docs/components/tab-switch.md +0 -170
  331. package/docs/components/table-dnd-wrapper.md +0 -107
  332. package/docs/components/table.md +0 -1088
  333. package/docs/components/text-editor.md +0 -735
  334. package/docs/components/textarea.md +0 -681
  335. package/docs/components/timeline.md +0 -254
  336. package/docs/components/toast.md +0 -773
  337. package/docs/components/toggle-button.md +0 -643
  338. package/docs/components/toggle.md +0 -882
  339. package/docs/components/tooltip.md +0 -513
  340. package/docs/components/transparent-label.md +0 -721
  341. package/docs/components/tree-drop-down.md +0 -740
  342. package/docs/components/tree-folder.md +0 -110
  343. package/docs/components/tree-sub-layout.md +0 -707
  344. package/docs/explanation/architecture.md +0 -54
  345. package/docs/explanation/design-system.md +0 -52
  346. package/docs/how-to/form-and-list-recipes.md +0 -386
  347. package/docs/how-to/forms-with-form-builder.md +0 -472
  348. package/docs/how-to/guides.md +0 -1027
  349. package/docs/migration/changelog.md +0 -60
  350. package/docs/migration/form-builder-2.5.2.md +0 -113
  351. package/docs/reference/cli.md +0 -112
  352. package/docs/reference/components.md +0 -572
  353. package/docs/reference/hooks.md +0 -1523
  354. package/docs/reference/providers.md +0 -866
  355. package/docs/reference/tailwind-plugins.md +0 -942
  356. package/docs/reference/theme.md +0 -85
  357. package/docs/reference/types.md +0 -1016
  358. package/docs/reference/utilities.md +0 -726
  359. package/docs/tutorials/building-first-form.md +0 -846
  360. package/docs/tutorials/component-composition.md +0 -882
  361. package/docs/tutorials/getting-started.md +0 -302
  362. package/docs/tutorials/theming-basics.md +0 -785
@@ -1,1088 +0,0 @@
1
- ---
2
- name: Table
3
- version: 2.4.5
4
- status: stable
5
- category: components/data-display
6
- tags: [table, data, grid, sortable, resizable, accessible, compound]
7
- last-reviewed: 2024-11-05
8
- bundle-size: 4.2kb
9
- dependencies:
10
- - "class-variance-authority": "^0.7.0"
11
- - "@/components/Button": "internal"
12
- - "@/components/Checkbox": "internal"
13
- - "@/hooks/useResize": "internal"
14
- ---
15
-
16
- # Table
17
-
18
- > A comprehensive data table component with resizable columns, sortable headers, row states, and compound architecture. Built for complex data presentation with accessibility and performance in mind.
19
-
20
- ## Installation
21
-
22
- TORCH Glare is a copy-in library: the CLI copies this component's source into your project
23
- (you do **not** install it from the npm package). Run `init` once, then `add`:
24
-
25
- ```bash
26
- npx torch-glare@latest init
27
- npx torch-glare@latest add Table
28
- ```
29
-
30
- `add` also copies any components, hooks, and utilities that `Table` depends on.
31
-
32
- ## Import
33
-
34
- Import from your project's local path — the alias configured in `glare.json` (e.g. `@/*`):
35
-
36
- ```tsx
37
- import { Table } from "@/components/Table";
38
- ```
39
-
40
- ## Quick Examples
41
-
42
- ### Basic Table
43
-
44
- ```typescript
45
- import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from '@/components/Table'
46
-
47
- function BasicTable() {
48
- return (
49
- <Table>
50
- <TableHeader>
51
- <TableRow>
52
- <TableHead>Name</TableHead>
53
- <TableHead>Email</TableHead>
54
- <TableHead>Status</TableHead>
55
- </TableRow>
56
- </TableHeader>
57
- <TableBody>
58
- <TableRow>
59
- <TableCell>John Doe</TableCell>
60
- <TableCell>john@example.com</TableCell>
61
- <TableCell>Active</TableCell>
62
- </TableRow>
63
- <TableRow>
64
- <TableCell>Jane Smith</TableCell>
65
- <TableCell>jane@example.com</TableCell>
66
- <TableCell>Pending</TableCell>
67
- </TableRow>
68
- </TableBody>
69
- </Table>
70
- )
71
- }
72
- ```
73
-
74
- ### Sortable Table
75
-
76
- ```typescript
77
- function SortableTable() {
78
- const [sortField, setSortField] = useState<string | null>(null)
79
- const [sortType, setSortType] = useState<'asc' | 'desc' | undefined>()
80
- const [data, setData] = useState(initialData)
81
-
82
- const handleSort = (field: string) => {
83
- const newSortType = sortType === 'asc' ? 'desc' : 'asc'
84
- setSortType(newSortType)
85
- setSortField(field)
86
-
87
- // Sort data
88
- const sorted = [...data].sort((a, b) => {
89
- if (newSortType === 'asc') {
90
- return a[field] > b[field] ? 1 : -1
91
- }
92
- return a[field] < b[field] ? 1 : -1
93
- })
94
- setData(sorted)
95
- }
96
-
97
- return (
98
- <Table>
99
- <TableHeader>
100
- <TableRow>
101
- <TableHead
102
- sortType={sortField === 'name' ? sortType : undefined}
103
- onSort={() => handleSort('name')}
104
- >
105
- Name
106
- </TableHead>
107
- <TableHead
108
- sortType={sortField === 'date' ? sortType : undefined}
109
- onSort={() => handleSort('date')}
110
- >
111
- Date
112
- </TableHead>
113
- <TableHead
114
- sortType={sortField === 'amount' ? sortType : undefined}
115
- onSort={() => handleSort('amount')}
116
- >
117
- Amount
118
- </TableHead>
119
- </TableRow>
120
- </TableHeader>
121
- <TableBody>
122
- {data.map((item) => (
123
- <TableRow key={item.id}>
124
- <TableCell>{item.name}</TableCell>
125
- <TableCell>{item.date}</TableCell>
126
- <TableCell>${item.amount}</TableCell>
127
- </TableRow>
128
- ))}
129
- </TableBody>
130
- </Table>
131
- )
132
- }
133
- ```
134
-
135
- ### Table with Row States
136
-
137
- ```typescript
138
- function TableWithStates() {
139
- const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set())
140
-
141
- return (
142
- <Table>
143
- <TableHeader>
144
- <TableRow>
145
- <TableHead>Item</TableHead>
146
- <TableHead>Status</TableHead>
147
- <TableHead>Action</TableHead>
148
- </TableRow>
149
- </TableHeader>
150
- <TableBody>
151
- <TableRow state="add">
152
- <TableCell>New Item</TableCell>
153
- <TableCell>Added</TableCell>
154
- <TableCell>Review</TableCell>
155
- </TableRow>
156
- <TableRow state="update">
157
- <TableCell>Updated Item</TableCell>
158
- <TableCell>Modified</TableCell>
159
- <TableCell>Confirm</TableCell>
160
- </TableRow>
161
- <TableRow state="delete">
162
- <TableCell>Deleted Item</TableCell>
163
- <TableCell>Removed</TableCell>
164
- <TableCell>Undo</TableCell>
165
- </TableRow>
166
- <TableRow state="selected">
167
- <TableCell>Selected Item</TableCell>
168
- <TableCell>Active</TableCell>
169
- <TableCell>Edit</TableCell>
170
- </TableRow>
171
- </TableBody>
172
- </Table>
173
- )
174
- }
175
- ```
176
-
177
- ### Table with Checkbox Selection
178
-
179
- ```typescript
180
- function SelectableTable() {
181
- const [selectedRows, setSelectedRows] = useState<Set<string>>(new Set())
182
- const [data] = useState(items)
183
-
184
- const toggleRow = (id: string) => {
185
- const newSet = new Set(selectedRows)
186
- if (newSet.has(id)) {
187
- newSet.delete(id)
188
- } else {
189
- newSet.add(id)
190
- }
191
- setSelectedRows(newSet)
192
- }
193
-
194
- const toggleAll = () => {
195
- if (selectedRows.size === data.length) {
196
- setSelectedRows(new Set())
197
- } else {
198
- setSelectedRows(new Set(data.map(d => d.id)))
199
- }
200
- }
201
-
202
- return (
203
- <Table>
204
- <TableHeader>
205
- <TableRow>
206
- <TableHead size="S">
207
- <TableCheckbox
208
- id="select-all"
209
- checked={
210
- selectedRows.size === data.length
211
- ? true
212
- : selectedRows.size > 0
213
- ? 'indeterminate'
214
- : false
215
- }
216
- onCheckedChange={toggleAll}
217
- />
218
- </TableHead>
219
- <TableHead>Name</TableHead>
220
- <TableHead>Email</TableHead>
221
- </TableRow>
222
- </TableHeader>
223
- <TableBody>
224
- {data.map((item) => (
225
- <TableRow
226
- key={item.id}
227
- state={selectedRows.has(item.id) ? 'selected' : undefined}
228
- >
229
- <TableCell>
230
- <TableCheckbox
231
- id={`select-${item.id}`}
232
- checked={selectedRows.has(item.id)}
233
- onCheckedChange={() => toggleRow(item.id)}
234
- />
235
- </TableCell>
236
- <TableCell>{item.name}</TableCell>
237
- <TableCell>{item.email}</TableCell>
238
- </TableRow>
239
- ))}
240
- </TableBody>
241
- </Table>
242
- )
243
- }
244
- ```
245
-
246
- ### Table with Themed Styles
247
-
248
- ```typescript
249
- function ThemedTable() {
250
- return (
251
- <>
252
- <Table theme="light">
253
- <TableCaption>Light Theme Table</TableCaption>
254
- <TableHeader>
255
- <TableRow>
256
- <TableHead>Column 1</TableHead>
257
- <TableHead>Column 2</TableHead>
258
- </TableRow>
259
- </TableHeader>
260
- <TableBody>
261
- <TableRow>
262
- <TableCell>Data 1</TableCell>
263
- <TableCell>Data 2</TableCell>
264
- </TableRow>
265
- </TableBody>
266
- </Table>
267
-
268
- <Table theme="dark">
269
- <TableCaption>Dark Theme Table</TableCaption>
270
- <TableHeader>
271
- <TableRow>
272
- <TableHead>Column 1</TableHead>
273
- <TableHead>Column 2</TableHead>
274
- </TableRow>
275
- </TableHeader>
276
- <TableBody>
277
- <TableRow>
278
- <TableCell>Data 1</TableCell>
279
- <TableCell>Data 2</TableCell>
280
- </TableRow>
281
- </TableBody>
282
- </Table>
283
- </>
284
- )
285
- }
286
- ```
287
-
288
- ### Table with Footer Actions
289
-
290
- ```typescript
291
- function TableWithFooter() {
292
- const [showMore, setShowMore] = useState(false)
293
-
294
- return (
295
- <Table>
296
- <TableHeader>
297
- <TableRow>
298
- <TableHead>Product</TableHead>
299
- <TableHead>Price</TableHead>
300
- <TableHead>Stock</TableHead>
301
- </TableRow>
302
- </TableHeader>
303
- <TableBody>
304
- {products.slice(0, showMore ? undefined : 5).map((product) => (
305
- <TableRow key={product.id}>
306
- <TableCell>{product.name}</TableCell>
307
- <TableCell>${product.price}</TableCell>
308
- <TableCell>{product.stock}</TableCell>
309
- </TableRow>
310
- ))}
311
- </TableBody>
312
- <TableFooter>
313
- <TableFooterButton onClick={() => setShowMore(!showMore)}>
314
- <i className="ri-add-line" />
315
- {showMore ? 'Show Less' : 'Show More Items'}
316
- </TableFooterButton>
317
- </TableFooter>
318
- </Table>
319
- )
320
- }
321
- ```
322
-
323
- ### Resizable Columns
324
-
325
- ```typescript
326
- function ResizableTable() {
327
- // Column resizing is automatic with the useResize hook
328
- return (
329
- <Table>
330
- <TableHeader>
331
- <TableRow>
332
- <TableHead>Resizable Column 1</TableHead>
333
- <TableHead>Resizable Column 2</TableHead>
334
- <TableHead isDummy>Fixed Width</TableHead>
335
- </TableRow>
336
- </TableHeader>
337
- <TableBody>
338
- <TableRow>
339
- <TableCell>Drag the resize handle</TableCell>
340
- <TableCell>On column borders</TableCell>
341
- <TableCell isDummy>Cannot resize</TableCell>
342
- </TableRow>
343
- </TableBody>
344
- </Table>
345
- )
346
- }
347
- ```
348
-
349
- ## API Reference
350
-
351
- ### Table Props
352
-
353
- | Prop | Type | Default | Description |
354
- |------|------|---------|-------------|
355
- | `theme` | `'dark' \| 'light' \| 'default'` | `'default'` | Theme variant |
356
- | `className` | `string` | - | Additional CSS classes |
357
- | `children` | `React.ReactNode` | - | Table content |
358
-
359
- ### TableHeader Props
360
-
361
- | Prop | Type | Default | Description |
362
- |------|------|---------|-------------|
363
- | `className` | `string` | - | Additional CSS classes |
364
- | `children` | `React.ReactNode` | - | Header rows |
365
-
366
- #### Sticky header
367
-
368
- `TableHeader` carries `position: sticky; top: 0`, so a table inside a vertically scrolling container
369
- can keep its header in view while the rows move under it. Sticky keeps the element in normal flow,
370
- so it changes nothing in a table that does not scroll.
371
-
372
- It needs two things from you.
373
-
374
- **Pass `overflow-visible`.** Sticky resolves against the **nearest scrollport**, and `Table` defaults
375
- to `overflow-hidden` — which makes the table itself a scrollport, so the header pins to a box that
376
- never scrolls and appears not to stick. Turning that clipping off binds it to your scroller instead.
377
-
378
- Only do this when your scroller is `min-w-0` (or otherwise width-constrained). That default clipping
379
- is also what stops a `w-auto` table wider than its container from pushing the whole page wide, so
380
- opting out without a constrained scroller trades a sticky header for a horizontal scrollbar.
381
-
382
- **Give the header an opaque background** — see below.
383
-
384
- ```tsx
385
- {/* `min-w-0` so a wide table scrolls in here rather than widening the layout */}
386
- <div className="min-w-0 max-h-[400px] overflow-auto">
387
- <Table className="w-full overflow-visible">
388
- <TableHeader className="bg-background-presentation-form-base">
389
- ...
390
- ```
391
-
392
- The default header background is translucent, which was fine when nothing ever passed beneath it.
393
- Once rows scroll under a stuck header they read through it, so give
394
- the header the opaque colour of whatever surface it sits on — `Table` cannot pick one for you,
395
- since forcing a surface would recolour every table not on it. `DataViews`' table view is the worked
396
- example: it paints the surface token as the background colour and re-applies the header tint as a
397
- `background-image`, which stacks above it, landing on the exact colour the header always had.
398
-
399
- ### TableBody Props
400
-
401
- | Prop | Type | Default | Description |
402
- |------|------|---------|-------------|
403
- | `className` | `string` | - | Additional CSS classes |
404
- | `children` | `React.ReactNode` | - | Body rows |
405
-
406
- ### TableRow Props
407
-
408
- | Prop | Type | Default | Description |
409
- |------|------|---------|-------------|
410
- | `state` | `'delete' \| 'update' \| 'add' \| 'selected' \| 'open'` | - | Row state styling |
411
- | `className` | `string` | - | Additional CSS classes |
412
- | `children` | `React.ReactNode` | - | Row cells |
413
-
414
- ### TableHead Props
415
-
416
- | Prop | Type | Default | Description |
417
- |------|------|---------|-------------|
418
- | `size` | `'S' \| 'M'` | `'M'` | Header cell size |
419
- | `disabled` | `boolean` | `false` | Disables interactions |
420
- | `sortType` | `'asc' \| 'desc' \| undefined` | - | Sort indicator |
421
- | `onSort` | `() => void` | - | Sort handler |
422
- | `sortLabel` | `string` | - | Column name for the sort button's accessible label |
423
- | `onResize` | `(width: number) => void` | - | Fires while the column is drag-resized. Passing it makes the width **controlled** — you own the value and feed it back via `style.width`. Required whenever the table needs a definite width (`table-layout: fixed`), since only the owner of every column width can total them. Omit for uncontrolled resizing. |
424
- | `isDummy` | `boolean` | `false` | Non-interactive header |
425
- | `className` / `style` | `string` / `CSSProperties` | - | Applied to the `<th>` — this is how you size a column (`style={{ width: 200 }}`) |
426
- | `contentClassName` | `string` | - | Classes for the inner layout box (the flex row holding the label and sort toggle). Use it for typography or colour; `className` styles the `<th>` itself. |
427
-
428
- **Every** prop you pass — `className`, `style`, `aria-*`, `role`, `tabIndex` and event
429
- handlers alike — lands on the `<th>`. They deliberately share one element: libraries like
430
- dnd-kit hand back accessibility attributes and event listeners as a matched pair, and
431
- splitting them across two nodes leaves the focusable element without its handlers. Reach the
432
- inner layout box with `contentClassName`.
433
-
434
- ### TableCell Props
435
-
436
- | Prop | Type | Default | Description |
437
- |------|------|---------|-------------|
438
- | `isDummy` | `boolean` | `false` | Non-interactive cell |
439
- | `minWidth` | `number` | `200` | Minimum width of the content box, in px. Pass `0` when the column width is driven by the caller — otherwise this floor silently overrides any narrower column. |
440
- | `fade` | `boolean` | `true` | Whether the cell **crops** its content and fades the last 25% of it, signalling text clipped by the column width. Set `false` for cells holding a **control**: the fade washes out whatever sits at the right edge (a Select's chevron, a date button), and the crop — the content box hugs the control exactly — would clip every side of the control's hover/focus drop shadow. `isDummy` cells never fade. |
441
- | `childrenClassName` | `string` | - | Classes for content wrapper |
442
- | `className` | `string` | - | Additional CSS classes |
443
- | `children` | `React.ReactNode` | - | Cell content |
444
-
445
- ### TableScroller Props
446
-
447
- Horizontal scroll container for a table, with the thin design-system scrollbar (4px track
448
- that thickens and turns blue on hover). Accepts any `div` props. Keep header actions and
449
- `TableEndAction` **outside** it so they don't scroll away on a wide table.
450
-
451
- ### TableEndAction Props
452
-
453
- The full-width action bar that sits **below** a table — e.g. `+ Add New`. A `<button>`, not
454
- a `<tr>`, so it is a sibling of `TableScroller` and stays put while the columns scroll. Accepts
455
- any `button` props. Use `TableFooterButton` instead when the action should scroll with the grid.
456
-
457
- ### TableCheckbox Props
458
-
459
- | Prop | Type | Default | Description |
460
- |------|------|---------|-------------|
461
- | `id` | `string` | - | Required checkbox ID |
462
- | `checked` | `boolean \| 'indeterminate'` | - | Checked state; pass `'indeterminate'` for the mixed state |
463
- | `onCheckedChange` | `(checked: boolean \| 'indeterminate') => void` | - | Change handler (Radix Checkbox) |
464
-
465
- ### TableFooterButton Props
466
-
467
- | Prop | Type | Default | Description |
468
- |------|------|---------|-------------|
469
- | `onClick` | `() => void` | - | Click handler |
470
- | `className` | `string` | - | Additional CSS classes |
471
- | `children` | `React.ReactNode` | - | Button content |
472
-
473
- ### TypeScript
474
-
475
- ```typescript
476
- // Table types
477
- interface TableProps extends HTMLAttributes<HTMLTableElement> {
478
- theme?: 'dark' | 'light' | 'default'
479
- className?: string
480
- }
481
-
482
- // TableRow types
483
- interface TableRowProps extends HTMLAttributes<HTMLTableRowElement> {
484
- state?: 'delete' | 'update' | 'add' | 'selected' | 'open'
485
- }
486
-
487
- // TableHead types
488
- interface TableHeadProps extends ThHTMLAttributes<HTMLTableCellElement> {
489
- size?: 'S' | 'M'
490
- disabled?: boolean
491
- sortType?: 'asc' | 'desc' | undefined
492
- onSort?: () => void
493
- sortLabel?: string
494
- onResize?: (width: number) => void
495
- contentClassName?: string
496
- isDummy?: boolean
497
- }
498
-
499
- // TableCell types
500
- interface TableCellProps extends TdHTMLAttributes<HTMLTableCellElement> {
501
- isDummy?: boolean
502
- minWidth?: number
503
- fade?: boolean
504
- childrenClassName?: string
505
- }
506
-
507
- // TableScroller / TableEndAction types
508
- type TableScrollerProps = HTMLAttributes<HTMLDivElement>
509
- type TableEndActionProps = ButtonHTMLAttributes<HTMLButtonElement>
510
-
511
- // TableCheckbox types
512
- interface TableCheckboxProps extends ButtonHTMLAttributes<HTMLButtonElement> {
513
- id: string
514
- checked?: boolean | 'indeterminate'
515
- onCheckedChange?: (checked: boolean | 'indeterminate') => void
516
- }
517
-
518
- // Export structure
519
- export {
520
- Table,
521
- TableHeader,
522
- TableBody,
523
- TableFooter,
524
- TableHead,
525
- TableRow,
526
- TableCell,
527
- TableCaption,
528
- TableCheckbox,
529
- SubTableButton,
530
- TableFooterButton
531
- }
532
- ```
533
-
534
- ## Common Patterns
535
-
536
- ### Data Table with Pagination
537
-
538
- ```typescript
539
- function PaginatedTable() {
540
- const [page, setPage] = useState(1)
541
- const [rowsPerPage, setRowsPerPage] = useState(10)
542
- const totalPages = Math.ceil(data.length / rowsPerPage)
543
-
544
- const paginatedData = data.slice(
545
- (page - 1) * rowsPerPage,
546
- page * rowsPerPage
547
- )
548
-
549
- return (
550
- <>
551
- <Table>
552
- <TableHeader>
553
- <TableRow>
554
- <TableHead>ID</TableHead>
555
- <TableHead>Name</TableHead>
556
- <TableHead>Date</TableHead>
557
- </TableRow>
558
- </TableHeader>
559
- <TableBody>
560
- {paginatedData.map((item) => (
561
- <TableRow key={item.id}>
562
- <TableCell>{item.id}</TableCell>
563
- <TableCell>{item.name}</TableCell>
564
- <TableCell>{item.date}</TableCell>
565
- </TableRow>
566
- ))}
567
- </TableBody>
568
- </Table>
569
- <div className="flex justify-between items-center mt-4">
570
- <select
571
- value={rowsPerPage}
572
- onChange={(e) => setRowsPerPage(Number(e.target.value))}
573
- >
574
- <option value={10}>10 rows</option>
575
- <option value={25}>25 rows</option>
576
- <option value={50}>50 rows</option>
577
- </select>
578
- <div className="flex gap-2">
579
- <button
580
- disabled={page === 1}
581
- onClick={() => setPage(page - 1)}
582
- >
583
- Previous
584
- </button>
585
- <span>Page {page} of {totalPages}</span>
586
- <button
587
- disabled={page === totalPages}
588
- onClick={() => setPage(page + 1)}
589
- >
590
- Next
591
- </button>
592
- </div>
593
- </div>
594
- </>
595
- )
596
- }
597
- ```
598
-
599
- ### Expandable Rows
600
-
601
- ```typescript
602
- function ExpandableTable() {
603
- const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set())
604
-
605
- const toggleRow = (id: string) => {
606
- const newSet = new Set(expandedRows)
607
- if (newSet.has(id)) {
608
- newSet.delete(id)
609
- } else {
610
- newSet.add(id)
611
- }
612
- setExpandedRows(newSet)
613
- }
614
-
615
- return (
616
- <Table>
617
- <TableHeader>
618
- <TableRow>
619
- <TableHead size="S"></TableHead>
620
- <TableHead>Order ID</TableHead>
621
- <TableHead>Customer</TableHead>
622
- <TableHead>Total</TableHead>
623
- </TableRow>
624
- </TableHeader>
625
- <TableBody>
626
- {orders.map((order) => (
627
- <React.Fragment key={order.id}>
628
- <TableRow state={expandedRows.has(order.id) ? 'open' : undefined}>
629
- <TableCell>
630
- <SubTableButton
631
- isActive={expandedRows.has(order.id)}
632
- onClick={() => toggleRow(order.id)}
633
- />
634
- </TableCell>
635
- <TableCell>{order.id}</TableCell>
636
- <TableCell>{order.customer}</TableCell>
637
- <TableCell>${order.total}</TableCell>
638
- </TableRow>
639
- {expandedRows.has(order.id) && (
640
- <TableRow>
641
- <TableCell colSpan={4}>
642
- <div className="p-4">
643
- <h4>Order Details</h4>
644
- {order.items.map((item) => (
645
- <div key={item.id}>
646
- {item.name} - ${item.price} x {item.quantity}
647
- </div>
648
- ))}
649
- </div>
650
- </TableCell>
651
- </TableRow>
652
- )}
653
- </React.Fragment>
654
- ))}
655
- </TableBody>
656
- </Table>
657
- )
658
- }
659
- ```
660
-
661
- ### Editable Table
662
-
663
- ```typescript
664
- function EditableTable() {
665
- const [data, setData] = useState(initialData)
666
- const [editingId, setEditingId] = useState<string | null>(null)
667
-
668
- const handleEdit = (id: string, field: string, value: string) => {
669
- setData(data.map(item =>
670
- item.id === id ? { ...item, [field]: value } : item
671
- ))
672
- }
673
-
674
- return (
675
- <Table>
676
- <TableHeader>
677
- <TableRow>
678
- <TableHead>Name</TableHead>
679
- <TableHead>Email</TableHead>
680
- <TableHead>Actions</TableHead>
681
- </TableRow>
682
- </TableHeader>
683
- <TableBody>
684
- {data.map((item) => (
685
- <TableRow key={item.id}>
686
- <TableCell>
687
- {editingId === item.id ? (
688
- <Input
689
- value={item.name}
690
- onChange={(e) => handleEdit(item.id, 'name', e.target.value)}
691
- onBlur={() => setEditingId(null)}
692
- />
693
- ) : (
694
- item.name
695
- )}
696
- </TableCell>
697
- <TableCell>
698
- {editingId === item.id ? (
699
- <Input
700
- value={item.email}
701
- onChange={(e) => handleEdit(item.id, 'email', e.target.value)}
702
- onBlur={() => setEditingId(null)}
703
- />
704
- ) : (
705
- item.email
706
- )}
707
- </TableCell>
708
- <TableCell>
709
- <button onClick={() => setEditingId(item.id)}>
710
- Edit
711
- </button>
712
- </TableCell>
713
- </TableRow>
714
- ))}
715
- </TableBody>
716
- </Table>
717
- )
718
- }
719
- ```
720
-
721
- ## Testing
722
-
723
- ### Unit Test Example
724
-
725
- ```typescript
726
- import { render, screen, fireEvent } from '@testing-library/react'
727
- import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell } from '@/components/Table'
728
-
729
- describe('Table', () => {
730
- it('renders table with data', () => {
731
- render(
732
- <Table>
733
- <TableHeader>
734
- <TableRow>
735
- <TableHead>Header 1</TableHead>
736
- </TableRow>
737
- </TableHeader>
738
- <TableBody>
739
- <TableRow>
740
- <TableCell>Cell 1</TableCell>
741
- </TableRow>
742
- </TableBody>
743
- </Table>
744
- )
745
-
746
- expect(screen.getByText('Header 1')).toBeInTheDocument()
747
- expect(screen.getByText('Cell 1')).toBeInTheDocument()
748
- })
749
-
750
- it('handles sorting', () => {
751
- const handleSort = jest.fn()
752
- render(
753
- <Table>
754
- <TableHeader>
755
- <TableRow>
756
- <TableHead onSort={handleSort} sortType="asc">
757
- Sortable
758
- </TableHead>
759
- </TableRow>
760
- </TableHeader>
761
- </Table>
762
- )
763
-
764
- const sortButton = screen.getByRole('button')
765
- fireEvent.pointerDown(sortButton)
766
- expect(handleSort).toHaveBeenCalled()
767
- })
768
-
769
- it('applies row states correctly', () => {
770
- const { container } = render(
771
- <Table>
772
- <TableBody>
773
- <TableRow state="selected">
774
- <TableCell>Selected</TableCell>
775
- </TableRow>
776
- </TableBody>
777
- </Table>
778
- )
779
-
780
- const row = container.querySelector('tr')
781
- expect(row).toHaveClass('bg-background-presentation-table-row-selected')
782
- })
783
- })
784
- ```
785
-
786
- ### Accessibility Test
787
-
788
- ```typescript
789
- import { axe } from '@axe-core/react'
790
-
791
- test('Table meets WCAG standards', async () => {
792
- const { container } = render(
793
- <Table>
794
- <TableCaption>User Data</TableCaption>
795
- <TableHeader>
796
- <TableRow>
797
- <TableHead scope="col">Name</TableHead>
798
- <TableHead scope="col">Email</TableHead>
799
- </TableRow>
800
- </TableHeader>
801
- <TableBody>
802
- <TableRow>
803
- <TableCell>John Doe</TableCell>
804
- <TableCell>john@example.com</TableCell>
805
- </TableRow>
806
- </TableBody>
807
- </Table>
808
- )
809
-
810
- const results = await axe(container)
811
- expect(results).toHaveNoViolations()
812
- })
813
- ```
814
-
815
- ## Known Limitations & Frontend Patterns
816
-
817
- ### `Table` defaults to `w-auto` — always pass `className="w-full"` for data tables
818
-
819
- The shipped `Table` base classes include `w-auto`, so the table sizes to its content. Inside a `Card`, `Dialog`, or any flex/grid container with short rows, the table collapses to a narrow column on the left with empty space on the right. This is rarely what you want for a data list.
820
-
821
- **Always pass `className="w-full"`** for list/data tables:
822
-
823
- ```tsx
824
- <Card className="p-0 overflow-hidden">
825
- <CardContent className="p-0 overflow-x-auto">
826
- <Table className="w-full">
827
- <TableHeader>
828
- <TableRow>
829
- <TableHead>Number</TableHead>
830
- <TableHead>Status</TableHead>
831
- <TableHead className="text-right">Total Debit</TableHead>
832
- </TableRow>
833
- </TableHeader>
834
- <TableBody>...</TableBody>
835
- </Table>
836
- </CardContent>
837
- </Card>
838
- ```
839
-
840
- **Wrap with `overflow-x-auto`** on the parent so wide tables (10+ columns) scroll horizontally inside the card instead of overflowing it.
841
-
842
- ### Numeric columns: `text-right` on both head and cell
843
-
844
- Right-align debit, credit, amount, count columns on **both** `TableHead` and the corresponding `TableCell`:
845
-
846
- ```tsx
847
- <TableHead className="text-right">Total Debit</TableHead>
848
- <TableCell className="text-right font-mono">{voucher.totalDebit}</TableCell>
849
- ```
850
-
851
- Use `font-mono` on the cell so digits line up across rows.
852
-
853
- ### `whitespace-nowrap` on date and numeric cells
854
-
855
- Dates and amounts wrap awkwardly when the column is narrow. Add `whitespace-nowrap` on those cells (not on heads):
856
-
857
- ```tsx
858
- <TableCell className="whitespace-nowrap">{formatDate(voucher.date)}</TableCell>
859
- ```
860
-
861
- ### `TableCell` force-wraps children — use `childrenClassName` to override
862
-
863
- `TableCell` unconditionally wraps children in an inner `<div>` with `flex justify-start items-center gap-1`, a `min-width` of 200px, `overflow-hidden`, and a fade-out gradient mask. Three common breakages:
864
-
865
- 1. **Empty-state rows don't center.** A `flex flex-col items-center` empty state ends up flush left because the outer wrapper's `justify-start` already decided alignment.
866
- 2. **Multi-line content is clipped** by `overflow-hidden` + the gradient mask.
867
- 3. **`flex-col` doesn't work** because the wrapper is `flex` (row) by default.
868
-
869
- **Workaround — use the `childrenClassName` prop**, which merges into the inner wrapper, and skip your own outer `<div>`:
870
-
871
- ```tsx
872
- <TableRow>
873
- <TableCell
874
- colSpan={7}
875
- minWidth={0}
876
- fade={false}
877
- childrenClassName="flex flex-col items-center justify-center gap-3 py-12 w-full text-content-presentation-global-secondary"
878
- >
879
- <i className="ri-inbox-line text-4xl opacity-60" />
880
- <p className="typography-body-medium-regular">No data</p>
881
- <p className="typography-body-small-regular opacity-80">Try adjusting filters</p>
882
- </TableCell>
883
- </TableRow>
884
- ```
885
-
886
- Key overrides:
887
-
888
- - `flex flex-col` on `childrenClassName` overrides the default `flex-row`
889
- - `items-center justify-center` on `childrenClassName` overrides `justify-start`
890
- - **`minWidth={0}`** clears the 200px floor. It is an inline style, so a class such as
891
- `min-w-0` on `childrenClassName` cannot beat it — use the prop.
892
- - **`fade={false}`** drops both the gradient mask and the `overflow-hidden` that clips
893
- multi-line content.
894
-
895
- ## Accessibility
896
-
897
- ### Keyboard Support
898
-
899
- - **Tab**: Navigate through interactive elements
900
- - **Space/Enter**: Activate buttons and checkboxes
901
- - **Arrow Keys**: Navigate cells (with additional implementation)
902
- - **Home/End**: Jump to first/last cell in row
903
-
904
- ### ARIA Attributes
905
-
906
- ```typescript
907
- // Sortable headers
908
- <TableHead
909
- role="columnheader"
910
- aria-sort={sortType === 'asc' ? 'ascending' : 'descending'}
911
- >
912
-
913
- // Row selection
914
- <TableRow
915
- role="row"
916
- aria-selected={isSelected}
917
- >
918
-
919
- // Table caption for screen readers
920
- <TableCaption>
921
- Showing {start} to {end} of {total} results
922
- </TableCaption>
923
- ```
924
-
925
- ### Screen Reader Support
926
-
927
- - Announces table structure and dimensions
928
- - Reads column headers with cells
929
- - Announces sort state changes
930
- - Communicates row selection state
931
-
932
- ## Styling
933
-
934
- ### Custom Styles with className
935
-
936
- ```typescript
937
- <Table className="border rounded-lg">
938
- <TableHeader className="bg-gray-100">
939
- <TableRow className="border-b-2">
940
- <TableHead className="font-bold">Custom Styled</TableHead>
941
- </TableRow>
942
- </TableHeader>
943
- </Table>
944
- ```
945
-
946
- ### CSS Variables
947
-
948
- ```css
949
- /* Custom theme variables */
950
- [data-theme="custom"] {
951
- --table-border: #your-border;
952
- --table-header-bg: #your-header-bg;
953
- --table-row-hover: #your-hover;
954
- --table-row-selected: #your-selected;
955
- --table-sort-active: #your-sort-color;
956
- }
957
- ```
958
-
959
- ### Row State Classes
960
-
961
- ```css
962
- /* Row states */
963
- .state-delete { background: var(--color-negative); }
964
- .state-update { background: var(--color-information); }
965
- .state-add { background: var(--color-success); }
966
- .state-selected { background: var(--color-selected); }
967
- .state-open { background: var(--color-hover); }
968
- ```
969
-
970
- ## Performance
971
-
972
- | Metric | Value |
973
- |--------|-------|
974
- | Bundle size (gzip) | 4.2kb |
975
- | First render (100 rows) | <20ms |
976
- | Re-render (100 rows) | <10ms |
977
- | Column resize | <5ms |
978
- | Tree-shakeable | ✅ |
979
-
980
- ### Optimization Tips
981
-
982
- 1. Use `React.memo()` for row components with large datasets
983
- 2. Implement virtual scrolling for tables with >100 rows
984
- 3. Debounce sort and filter operations
985
- 4. Use `useCallback` for event handlers
986
- 5. Consider pagination for large datasets
987
-
988
- ## Migration
989
-
990
- ### From v1.0.x
991
-
992
- ```diff
993
- // Import path changed
994
- - import Table from 'torch-glare/Table'
995
- + import { Table, TableHeader, TableBody } from '@/components/Table'
996
-
997
- // Component structure
998
- - <Table headers={headers} data={data} />
999
- + <Table>
1000
- + <TableHeader>
1001
- + <TableRow>
1002
- + {headers.map(h => <TableHead key={h}>{h}</TableHead>)}
1003
- + </TableRow>
1004
- + </TableHeader>
1005
- + <TableBody>
1006
- + {data.map(row => (
1007
- + <TableRow key={row.id}>
1008
- + {row.cells.map(cell => <TableCell key={cell}>{cell}</TableCell>)}
1009
- + </TableRow>
1010
- + ))}
1011
- + </TableBody>
1012
- + </Table>
1013
- ```
1014
-
1015
- ## Troubleshooting
1016
-
1017
- ### Common Issues
1018
-
1019
- #### Columns not resizing
1020
-
1021
- **Solution:** Ensure resize handles are not disabled
1022
-
1023
- ```typescript
1024
- // ❌ Wrong - disables resize
1025
- <TableHead isDummy>Column</TableHead>
1026
-
1027
- // ✅ Correct - allows resize
1028
- <TableHead>Column</TableHead>
1029
- ```
1030
-
1031
- #### Sort not working
1032
-
1033
- **Solution:** Implement sort handler properly
1034
-
1035
- ```typescript
1036
- // ❌ Wrong - no state update
1037
- <TableHead onSort={() => console.log('sort')}>
1038
-
1039
- // ✅ Correct - updates data
1040
- <TableHead
1041
- onSort={() => handleSort('field')}
1042
- sortType={sortType}
1043
- >
1044
- ```
1045
-
1046
- #### Row selection not visible
1047
-
1048
- **Solution:** Apply state prop to TableRow
1049
-
1050
- ```typescript
1051
- // ❌ Wrong - no visual feedback
1052
- <TableRow>
1053
-
1054
- // ✅ Correct - shows selection
1055
- <TableRow state={isSelected ? 'selected' : undefined}>
1056
- ```
1057
-
1058
- ## Related Components
1059
-
1060
- - [DataTable](/docs/components/data-table.md) - Advanced table with built-in features
1061
- - [Card](/docs/components/card.md) - Alternative data display format
1062
- - [ScrollArea](/docs/components/scroll-area.md) - For scrollable tables
1063
- - [Checkbox](/docs/components/checkbox.md) - For row selection
1064
- - [Button](/docs/components/button.md) - For table actions
1065
-
1066
- ## Browser Support
1067
-
1068
- - Chrome 90+ ✅
1069
- - Firefox 88+ ✅
1070
- - Safari 14+ ✅
1071
- - Edge 90+ ✅
1072
- - Mobile browsers ✅
1073
-
1074
- ## Changelog
1075
-
1076
- ### v1.1.15
1077
- - Added column resizing functionality
1078
- - Improved sorting indicators
1079
- - Enhanced row state styling
1080
- - Better TypeScript types
1081
-
1082
- ### v1.1.14
1083
- - Table component refactor
1084
- - Added compound architecture
1085
- - Performance optimizations
1086
-
1087
- ### v1.1.0
1088
- - Initial stable release