@sincpro/web-ui 0.0.0-stage → 0.2.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 (376) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +184 -2
  3. package/dist/components/activity/Activity.d.ts +69 -0
  4. package/dist/components/activity/Activity.js +279 -0
  5. package/dist/components/activity/Messages.d.ts +66 -0
  6. package/dist/components/activity/Messages.js +46 -0
  7. package/dist/components/activity/Timeline.d.ts +21 -0
  8. package/dist/components/activity/Timeline.js +30 -0
  9. package/dist/components/activity/index.d.ts +3 -0
  10. package/dist/components/activity/index.js +4 -0
  11. package/dist/components/assist/AssistMark.d.ts +64 -0
  12. package/dist/components/assist/AssistMark.js +108 -0
  13. package/dist/components/assist/AssistPending.d.ts +24 -0
  14. package/dist/components/assist/AssistPending.js +26 -0
  15. package/dist/components/assist/AssistSuggestion.d.ts +42 -0
  16. package/dist/components/assist/AssistSuggestion.js +55 -0
  17. package/dist/components/assist/ChangeReview.d.ts +56 -0
  18. package/dist/components/assist/ChangeReview.js +121 -0
  19. package/dist/components/assist/assist.d.ts +77 -0
  20. package/dist/components/assist/assist.js +87 -0
  21. package/dist/components/assist/index.d.ts +5 -0
  22. package/dist/components/assist/index.js +7 -0
  23. package/dist/components/form/Form.d.ts +52 -0
  24. package/dist/components/form/Form.js +49 -0
  25. package/dist/components/form/Sheet.d.ts +47 -0
  26. package/dist/components/form/Sheet.js +90 -0
  27. package/dist/components/form/index.d.ts +2 -0
  28. package/dist/components/form/index.js +4 -0
  29. package/dist/components/frame/Chart.d.ts +13 -0
  30. package/dist/components/frame/Chart.js +24 -0
  31. package/dist/components/frame/FrameView.d.ts +33 -0
  32. package/dist/components/frame/FrameView.js +125 -0
  33. package/dist/components/frame/index.d.ts +3 -0
  34. package/dist/components/frame/index.js +3 -0
  35. package/dist/components/frame/model.d.ts +43 -0
  36. package/dist/components/frame/model.js +101 -0
  37. package/dist/components/index.d.ts +10 -0
  38. package/dist/components/index.js +11 -0
  39. package/dist/components/lines/LineItems.d.ts +111 -0
  40. package/dist/components/lines/LineItems.js +317 -0
  41. package/dist/components/lines/index.d.ts +1 -0
  42. package/dist/components/lines/index.js +2 -0
  43. package/dist/components/lines/lines.d.ts +23 -0
  44. package/dist/components/lines/lines.js +26 -0
  45. package/dist/components/listing/ColumnsMenu.d.ts +20 -0
  46. package/dist/components/listing/ColumnsMenu.js +46 -0
  47. package/dist/components/listing/GroupingLevels.d.ts +35 -0
  48. package/dist/components/listing/GroupingLevels.js +45 -0
  49. package/dist/components/listing/Listing.d.ts +27 -0
  50. package/dist/components/listing/Listing.js +86 -0
  51. package/dist/components/listing/ListingBar.d.ts +112 -0
  52. package/dist/components/listing/ListingBar.js +144 -0
  53. package/dist/components/listing/Pagination.d.ts +36 -0
  54. package/dist/components/listing/Pagination.js +69 -0
  55. package/dist/components/listing/RecordCard.d.ts +33 -0
  56. package/dist/components/listing/RecordCard.js +22 -0
  57. package/dist/components/listing/RecordLink.d.ts +7 -0
  58. package/dist/components/listing/RecordLink.js +22 -0
  59. package/dist/components/listing/Row.d.ts +110 -0
  60. package/dist/components/listing/Row.js +123 -0
  61. package/dist/components/listing/SortMenu.d.ts +62 -0
  62. package/dist/components/listing/SortMenu.js +105 -0
  63. package/dist/components/listing/ViewSwitcher.d.ts +24 -0
  64. package/dist/components/listing/ViewSwitcher.js +16 -0
  65. package/dist/components/listing/columns.d.ts +47 -0
  66. package/dist/components/listing/columns.js +72 -0
  67. package/dist/components/listing/grouping.d.ts +35 -0
  68. package/dist/components/listing/grouping.js +47 -0
  69. package/dist/components/listing/index.d.ts +17 -0
  70. package/dist/components/listing/index.js +20 -0
  71. package/dist/components/listing/modelColumns.d.ts +50 -0
  72. package/dist/components/listing/modelColumns.js +103 -0
  73. package/dist/components/listing/pages.d.ts +78 -0
  74. package/dist/components/listing/pages.js +135 -0
  75. package/dist/components/listing/selection.d.ts +21 -0
  76. package/dist/components/listing/selection.js +43 -0
  77. package/dist/components/listing/set.d.ts +48 -0
  78. package/dist/components/listing/set.js +24 -0
  79. package/dist/components/listing/tree.d.ts +28 -0
  80. package/dist/components/listing/tree.js +23 -0
  81. package/dist/components/navigation/CommandPalette.d.ts +40 -0
  82. package/dist/components/navigation/CommandPalette.js +114 -0
  83. package/dist/components/navigation/NavigationTree.d.ts +31 -0
  84. package/dist/components/navigation/NavigationTree.js +79 -0
  85. package/dist/components/navigation/commands.d.ts +30 -0
  86. package/dist/components/navigation/commands.js +30 -0
  87. package/dist/components/navigation/index.d.ts +3 -0
  88. package/dist/components/navigation/index.js +4 -0
  89. package/dist/components/record/Breadcrumb.d.ts +42 -0
  90. package/dist/components/record/Breadcrumb.js +98 -0
  91. package/dist/components/record/DirtyActions.d.ts +26 -0
  92. package/dist/components/record/DirtyActions.js +22 -0
  93. package/dist/components/record/Pager.d.ts +18 -0
  94. package/dist/components/record/Pager.js +33 -0
  95. package/dist/components/record/RecordHeader.d.ts +75 -0
  96. package/dist/components/record/RecordHeader.js +104 -0
  97. package/dist/components/record/RecordNotices.d.ts +37 -0
  98. package/dist/components/record/RecordNotices.js +75 -0
  99. package/dist/components/record/RecordSkeleton.d.ts +3 -0
  100. package/dist/components/record/RecordSkeleton.js +21 -0
  101. package/dist/components/record/SmartButtons.d.ts +35 -0
  102. package/dist/components/record/SmartButtons.js +38 -0
  103. package/dist/components/record/StatusBar.d.ts +51 -0
  104. package/dist/components/record/StatusBar.js +168 -0
  105. package/dist/components/record/ThumbBar.d.ts +36 -0
  106. package/dist/components/record/ThumbBar.js +86 -0
  107. package/dist/components/record/index.d.ts +6 -0
  108. package/dist/components/record/index.js +8 -0
  109. package/dist/components/search/CriteriaBuilder.d.ts +58 -0
  110. package/dist/components/search/CriteriaBuilder.js +278 -0
  111. package/dist/components/search/CriteriaChips.d.ts +22 -0
  112. package/dist/components/search/CriteriaChips.js +64 -0
  113. package/dist/components/search/FilterBuilder.d.ts +142 -0
  114. package/dist/components/search/FilterBuilder.js +421 -0
  115. package/dist/components/search/JoinWord.d.ts +8 -0
  116. package/dist/components/search/JoinWord.js +47 -0
  117. package/dist/components/search/JsonEditor.d.ts +19 -0
  118. package/dist/components/search/JsonEditor.js +78 -0
  119. package/dist/components/search/SearchBar.d.ts +78 -0
  120. package/dist/components/search/SearchBar.js +387 -0
  121. package/dist/components/search/criteria.d.ts +116 -0
  122. package/dist/components/search/criteria.js +159 -0
  123. package/dist/components/search/datePeriods.d.ts +122 -0
  124. package/dist/components/search/datePeriods.js +134 -0
  125. package/dist/components/search/dateRanges.d.ts +54 -0
  126. package/dist/components/search/dateRanges.js +89 -0
  127. package/dist/components/search/index.d.ts +9 -0
  128. package/dist/components/search/index.js +20 -0
  129. package/dist/components/search/model.d.ts +54 -0
  130. package/dist/components/search/model.js +37 -0
  131. package/dist/components/search/query.d.ts +166 -0
  132. package/dist/components/search/query.js +436 -0
  133. package/dist/components/shell/AppFrame.d.ts +14 -0
  134. package/dist/components/shell/AppFrame.js +80 -0
  135. package/dist/components/shell/TopBar.d.ts +73 -0
  136. package/dist/components/shell/TopBar.js +54 -0
  137. package/dist/components/shell/headerSlot.d.ts +17 -0
  138. package/dist/components/shell/headerSlot.js +21 -0
  139. package/dist/components/shell/index.d.ts +2 -0
  140. package/dist/components/shell/index.js +3 -0
  141. package/dist/foundations/aria.d.ts +14 -0
  142. package/dist/foundations/aria.js +18 -0
  143. package/dist/foundations/calendar.d.ts +28 -0
  144. package/dist/foundations/calendar.js +183 -0
  145. package/dist/foundations/copy.d.ts +703 -0
  146. package/dist/foundations/copy.es.d.ts +2 -0
  147. package/dist/foundations/copy.es.js +430 -0
  148. package/dist/foundations/copy.js +520 -0
  149. package/dist/foundations/density.d.ts +16 -0
  150. package/dist/foundations/density.js +21 -0
  151. package/dist/foundations/grouping.d.ts +281 -0
  152. package/dist/foundations/grouping.js +0 -0
  153. package/dist/foundations/index.d.ts +14 -0
  154. package/dist/foundations/index.js +30 -0
  155. package/dist/foundations/layout.d.ts +9 -0
  156. package/dist/foundations/layout.js +9 -0
  157. package/dist/foundations/pivot.d.ts +222 -0
  158. package/dist/foundations/pivot.js +198 -0
  159. package/dist/foundations/pivotSheet.d.ts +61 -0
  160. package/dist/foundations/pivotSheet.js +0 -0
  161. package/dist/foundations/slots.d.ts +67 -0
  162. package/dist/foundations/slots.js +63 -0
  163. package/dist/foundations/sticky.d.ts +53 -0
  164. package/dist/foundations/sticky.js +150 -0
  165. package/dist/foundations/surface.d.ts +9 -0
  166. package/dist/foundations/surface.js +26 -0
  167. package/dist/foundations/theme.d.ts +42 -0
  168. package/dist/foundations/theme.js +88 -0
  169. package/dist/foundations/types.d.ts +23 -0
  170. package/dist/foundations/types.js +50 -0
  171. package/dist/foundations/variants.d.ts +19 -0
  172. package/dist/foundations/variants.js +48 -0
  173. package/dist/foundations/xlsx.d.ts +86 -0
  174. package/dist/foundations/xlsx.js +0 -0
  175. package/dist/index.d.ts +19 -0
  176. package/dist/index.js +19 -0
  177. package/dist/primitives/block/Block.d.ts +66 -0
  178. package/dist/primitives/block/Block.js +34 -0
  179. package/dist/primitives/block/Line.d.ts +18 -0
  180. package/dist/primitives/block/Line.js +45 -0
  181. package/dist/primitives/block/SectionGrid.d.ts +30 -0
  182. package/dist/primitives/block/SectionGrid.js +29 -0
  183. package/dist/primitives/block/Steps.d.ts +76 -0
  184. package/dist/primitives/block/Steps.js +43 -0
  185. package/dist/primitives/block/index.d.ts +4 -0
  186. package/dist/primitives/block/index.js +7 -0
  187. package/dist/primitives/button/Button.d.ts +137 -0
  188. package/dist/primitives/button/Button.js +234 -0
  189. package/dist/primitives/button/index.d.ts +1 -0
  190. package/dist/primitives/button/index.js +2 -0
  191. package/dist/primitives/cell/CellStack.d.ts +47 -0
  192. package/dist/primitives/cell/CellStack.js +33 -0
  193. package/dist/primitives/cell/index.d.ts +1 -0
  194. package/dist/primitives/cell/index.js +3 -0
  195. package/dist/primitives/choice/ComboBox.d.ts +19 -0
  196. package/dist/primitives/choice/ComboBox.js +52 -0
  197. package/dist/primitives/choice/RadioGroup.d.ts +18 -0
  198. package/dist/primitives/choice/RadioGroup.js +50 -0
  199. package/dist/primitives/choice/SearchField.d.ts +33 -0
  200. package/dist/primitives/choice/SearchField.js +46 -0
  201. package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
  202. package/dist/primitives/choice/SegmentedControl.js +69 -0
  203. package/dist/primitives/choice/Slider.d.ts +16 -0
  204. package/dist/primitives/choice/Slider.js +46 -0
  205. package/dist/primitives/choice/Switch.d.ts +14 -0
  206. package/dist/primitives/choice/Switch.js +65 -0
  207. package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
  208. package/dist/primitives/choice/ThemeSwitcher.js +25 -0
  209. package/dist/primitives/choice/index.d.ts +8 -0
  210. package/dist/primitives/choice/index.js +11 -0
  211. package/dist/primitives/choice/options.d.ts +28 -0
  212. package/dist/primitives/choice/options.js +6 -0
  213. package/dist/primitives/date/Calendar.d.ts +31 -0
  214. package/dist/primitives/date/Calendar.js +134 -0
  215. package/dist/primitives/date/DatePicker.d.ts +42 -0
  216. package/dist/primitives/date/DatePicker.js +103 -0
  217. package/dist/primitives/date/DateTime.d.ts +35 -0
  218. package/dist/primitives/date/DateTime.js +100 -0
  219. package/dist/primitives/date/index.d.ts +3 -0
  220. package/dist/primitives/date/index.js +6 -0
  221. package/dist/primitives/dialog/Dialog.d.ts +118 -0
  222. package/dist/primitives/dialog/Dialog.js +121 -0
  223. package/dist/primitives/dialog/Drill.d.ts +90 -0
  224. package/dist/primitives/dialog/Drill.js +172 -0
  225. package/dist/primitives/dialog/index.d.ts +2 -0
  226. package/dist/primitives/dialog/index.js +4 -0
  227. package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
  228. package/dist/primitives/disclosure/Disclosure.js +52 -0
  229. package/dist/primitives/disclosure/index.d.ts +1 -0
  230. package/dist/primitives/disclosure/index.js +2 -0
  231. package/dist/primitives/field/Checks.d.ts +57 -0
  232. package/dist/primitives/field/Checks.js +72 -0
  233. package/dist/primitives/field/DropZone.d.ts +42 -0
  234. package/dist/primitives/field/DropZone.js +52 -0
  235. package/dist/primitives/field/Field.d.ts +178 -0
  236. package/dist/primitives/field/Field.js +251 -0
  237. package/dist/primitives/field/Suggest.d.ts +48 -0
  238. package/dist/primitives/field/Suggest.js +43 -0
  239. package/dist/primitives/field/aria.d.ts +61 -0
  240. package/dist/primitives/field/aria.js +70 -0
  241. package/dist/primitives/field/context.d.ts +30 -0
  242. package/dist/primitives/field/context.js +13 -0
  243. package/dist/primitives/field/copy.d.ts +16 -0
  244. package/dist/primitives/field/copy.js +69 -0
  245. package/dist/primitives/field/index.d.ts +9 -0
  246. package/dist/primitives/field/index.js +15 -0
  247. package/dist/primitives/field/layout.d.ts +24 -0
  248. package/dist/primitives/field/layout.js +10 -0
  249. package/dist/primitives/field/mode.d.ts +9 -0
  250. package/dist/primitives/field/mode.js +13 -0
  251. package/dist/primitives/field/note.d.ts +55 -0
  252. package/dist/primitives/field/note.js +117 -0
  253. package/dist/primitives/hint/Hint.d.ts +31 -0
  254. package/dist/primitives/hint/Hint.js +42 -0
  255. package/dist/primitives/hint/index.d.ts +1 -0
  256. package/dist/primitives/hint/index.js +1 -0
  257. package/dist/primitives/index.d.ts +20 -0
  258. package/dist/primitives/index.js +21 -0
  259. package/dist/primitives/menu/Menu.d.ts +128 -0
  260. package/dist/primitives/menu/Menu.js +89 -0
  261. package/dist/primitives/menu/index.d.ts +1 -0
  262. package/dist/primitives/menu/index.js +2 -0
  263. package/dist/primitives/notice/Callout.d.ts +49 -0
  264. package/dist/primitives/notice/Callout.js +28 -0
  265. package/dist/primitives/notice/Flag.d.ts +13 -0
  266. package/dist/primitives/notice/Flag.js +31 -0
  267. package/dist/primitives/notice/Notice.d.ts +45 -0
  268. package/dist/primitives/notice/Notice.js +61 -0
  269. package/dist/primitives/notice/Toast.d.ts +55 -0
  270. package/dist/primitives/notice/Toast.js +142 -0
  271. package/dist/primitives/notice/index.d.ts +4 -0
  272. package/dist/primitives/notice/index.js +6 -0
  273. package/dist/primitives/overlay/Popover.d.ts +6 -0
  274. package/dist/primitives/overlay/Popover.js +27 -0
  275. package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
  276. package/dist/primitives/overlay/PopoverTrigger.js +38 -0
  277. package/dist/primitives/overlay/Preview.d.ts +19 -0
  278. package/dist/primitives/overlay/Preview.js +60 -0
  279. package/dist/primitives/overlay/Tooltip.d.ts +38 -0
  280. package/dist/primitives/overlay/Tooltip.js +74 -0
  281. package/dist/primitives/overlay/index.d.ts +4 -0
  282. package/dist/primitives/overlay/index.js +7 -0
  283. package/dist/primitives/picker/Picker.d.ts +89 -0
  284. package/dist/primitives/picker/Picker.js +204 -0
  285. package/dist/primitives/picker/index.d.ts +1 -0
  286. package/dist/primitives/picker/index.js +2 -0
  287. package/dist/primitives/surface/Peek.d.ts +60 -0
  288. package/dist/primitives/surface/Peek.js +71 -0
  289. package/dist/primitives/surface/index.d.ts +1 -0
  290. package/dist/primitives/surface/index.js +2 -0
  291. package/dist/primitives/table/CellCopy.d.ts +20 -0
  292. package/dist/primitives/table/CellCopy.js +23 -0
  293. package/dist/primitives/table/CellEditor.d.ts +14 -0
  294. package/dist/primitives/table/CellEditor.js +83 -0
  295. package/dist/primitives/table/DataTable.d.ts +189 -0
  296. package/dist/primitives/table/DataTable.js +330 -0
  297. package/dist/primitives/table/EditableTable.d.ts +191 -0
  298. package/dist/primitives/table/EditableTable.js +617 -0
  299. package/dist/primitives/table/PivotTable.d.ts +14 -0
  300. package/dist/primitives/table/PivotTable.js +0 -0
  301. package/dist/primitives/table/ReportTable.d.ts +38 -0
  302. package/dist/primitives/table/ReportTable.js +106 -0
  303. package/dist/primitives/table/RowActions.d.ts +16 -0
  304. package/dist/primitives/table/RowActions.js +28 -0
  305. package/dist/primitives/table/SheetTable.d.ts +73 -0
  306. package/dist/primitives/table/SheetTable.js +230 -0
  307. package/dist/primitives/table/Table.d.ts +106 -0
  308. package/dist/primitives/table/Table.js +116 -0
  309. package/dist/primitives/table/index.d.ts +7 -0
  310. package/dist/primitives/table/index.js +9 -0
  311. package/dist/primitives/tabs/Tabs.d.ts +91 -0
  312. package/dist/primitives/tabs/Tabs.js +56 -0
  313. package/dist/primitives/tabs/index.d.ts +1 -0
  314. package/dist/primitives/tabs/index.js +1 -0
  315. package/dist/primitives/tag/Tag.d.ts +36 -0
  316. package/dist/primitives/tag/Tag.js +68 -0
  317. package/dist/primitives/tag/Tags.d.ts +57 -0
  318. package/dist/primitives/tag/Tags.js +104 -0
  319. package/dist/primitives/tag/TagsField.d.ts +35 -0
  320. package/dist/primitives/tag/TagsField.js +186 -0
  321. package/dist/primitives/tag/choices.d.ts +38 -0
  322. package/dist/primitives/tag/choices.js +26 -0
  323. package/dist/primitives/tag/index.d.ts +4 -0
  324. package/dist/primitives/tag/index.js +8 -0
  325. package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
  326. package/dist/primitives/toolbar/Toolbar.js +36 -0
  327. package/dist/primitives/toolbar/index.d.ts +1 -0
  328. package/dist/primitives/toolbar/index.js +2 -0
  329. package/dist/primitives/tree/Tree.d.ts +56 -0
  330. package/dist/primitives/tree/Tree.js +74 -0
  331. package/dist/primitives/tree/index.d.ts +1 -0
  332. package/dist/primitives/tree/index.js +3 -0
  333. package/dist/primitives/value/Copyable.d.ts +48 -0
  334. package/dist/primitives/value/Copyable.js +71 -0
  335. package/dist/primitives/value/Fecha.d.ts +12 -0
  336. package/dist/primitives/value/Fecha.js +89 -0
  337. package/dist/primitives/value/Meter.d.ts +29 -0
  338. package/dist/primitives/value/Meter.js +46 -0
  339. package/dist/primitives/value/Metric.d.ts +42 -0
  340. package/dist/primitives/value/Metric.js +38 -0
  341. package/dist/primitives/value/Person.d.ts +90 -0
  342. package/dist/primitives/value/Person.js +208 -0
  343. package/dist/primitives/value/ProgressBar.d.ts +31 -0
  344. package/dist/primitives/value/ProgressBar.js +53 -0
  345. package/dist/primitives/value/Relation.d.ts +74 -0
  346. package/dist/primitives/value/Relation.js +74 -0
  347. package/dist/primitives/value/Totals.d.ts +121 -0
  348. package/dist/primitives/value/Totals.js +54 -0
  349. package/dist/primitives/value/Value.d.ts +40 -0
  350. package/dist/primitives/value/Value.js +80 -0
  351. package/dist/primitives/value/index.d.ts +9 -0
  352. package/dist/primitives/value/index.js +10 -0
  353. package/dist/tokens.css +1062 -0
  354. package/dist/views/index.d.ts +2 -0
  355. package/dist/views/index.js +3 -0
  356. package/dist/views/list/List.d.ts +119 -0
  357. package/dist/views/list/List.js +101 -0
  358. package/dist/views/list/ListView.d.ts +6 -0
  359. package/dist/views/list/ListView.js +295 -0
  360. package/dist/views/list/columns.d.ts +18 -0
  361. package/dist/views/list/columns.js +27 -0
  362. package/dist/views/list/derive.d.ts +87 -0
  363. package/dist/views/list/derive.js +207 -0
  364. package/dist/views/list/index.d.ts +2 -0
  365. package/dist/views/list/index.js +4 -0
  366. package/dist/views/list/presentations.d.ts +39 -0
  367. package/dist/views/list/presentations.js +105 -0
  368. package/dist/views/list/types.d.ts +284 -0
  369. package/dist/views/list/types.js +1 -0
  370. package/dist/views/list/useNarrowList.d.ts +17 -0
  371. package/dist/views/list/useNarrowList.js +32 -0
  372. package/dist/views/record/Record.d.ts +145 -0
  373. package/dist/views/record/Record.js +211 -0
  374. package/dist/views/record/index.d.ts +1 -0
  375. package/dist/views/record/index.js +2 -0
  376. package/package.json +113 -4
@@ -0,0 +1,189 @@
1
+ import type { Tone } from "../../foundations/types.js";
2
+ import { type RowAction } from "./RowActions.js";
3
+ import { type SortState } from "./Table.js";
4
+ import { type ReactNode } from "react";
5
+ /** How a column is aggregated when its rows are pulled together into a group.
6
+ *
7
+ * **`count` is the default and it is the right one almost always**: summing
8
+ * identifiers or averaging file sizes produces numbers that look like data and
9
+ * mean nothing. An aggregate has to be chosen on purpose. */
10
+ export type Aggregate = "count" | "sum" | "mean" | "min" | "max" | "unique";
11
+ /**
12
+ * How the aggregate of a column is written: its `format` when the aggregate is in the
13
+ * column's units, `undefined` (the bare number) otherwise.
14
+ *
15
+ * Exported for the other places that draw `aggregateOf` — the bands of the sheet, the report
16
+ * built from the piles — so the footer and the band never write the same total two ways.
17
+ * Not public.
18
+ */
19
+ export declare function aggregateFormat<T>(column: DataColumn<T>): ((value: number) => string) | undefined;
20
+ /** An aggregate of a column, written — or the bare number when nothing says how. */
21
+ export declare function aggregateText<T>(column: DataColumn<T>, value: number): string | number;
22
+ export interface DataColumn<T> {
23
+ id: string;
24
+ header: string;
25
+ /** Qué campo del MODELO muestra esta columna, cuando no se llama igual que
26
+ * él: «filas» muestra `row_count`. Lo lee `columnsFromModel` para no volver
27
+ * a agregar, en crudo, un campo que esta columna ya contesta. */
28
+ field?: string;
29
+ /** The raw value: this is what sorting and grouping run on. */
30
+ value: (row: T) => string | number | boolean | null;
31
+ /** How it is drawn. Without this the raw value is drawn. */
32
+ cell?: (row: T) => ReactNode;
33
+ /** A column of CODES — a SKU, a hash, a key — set in the monospaced face. Numbers do not
34
+ * need it: they align with tabular figures in the same face as the rest of the row. */
35
+ mono?: boolean;
36
+ /**
37
+ * Cómo se LEE la columna donde no se puede dibujar: la planilla, que es una
38
+ * cuadrícula de texto y números y no puede recibir un nodo.
39
+ *
40
+ * Context: sin esto, la planilla caía a `value`, que es la clave de ORDEN y
41
+ * no lo que una persona lee. La columna «estado» de Facturas mostraba
42
+ * `posted` y `paid` —los valores del contrato— en vez de «publicada» y
43
+ * «pagada», que es lo que dice la ficha de al lado en la tabla. Dos
44
+ * presentaciones del mismo conjunto diciendo cosas distintas.
45
+ */
46
+ text?: (row: T) => string;
47
+ /** Paints the BACKGROUND of the cell, row by row. It is what turns a table
48
+ * into something you look at: «this amount is out of range», «this value was
49
+ * imputed by the engine». Returning `undefined` leaves the cell unpainted,
50
+ * which is the normal case — a table with everything colored highlights
51
+ * nothing. */
52
+ tone?: (row: T) => Tone | undefined;
53
+ /** Why it is painted, on hover. */
54
+ note?: (row: T) => string | undefined;
55
+ numeric?: boolean;
56
+ width?: number;
57
+ aggregate?: Aggregate;
58
+ /**
59
+ * How a number of this column is WRITTEN where the code draws it instead of a row: the
60
+ * footer's aggregate, the total of a band of the sheet, the lines of a report built
61
+ * from the piles. `(n) => money(n)` makes the footer read «Bs. 78.752,90» like the cells
62
+ * above it, and not «78.752,9».
63
+ *
64
+ * Context: the cells already had their way (`cell`), and that way takes a ROW — the
65
+ * footer has no row, only a number. A string and not a node because two of the three
66
+ * places are grids of text: the sheet and the report. Applies to the aggregates in the
67
+ * column's units (`sum`, `mean`, `min`, `max`); a `count` stays a count. Without it,
68
+ * the number in the copy table's locale, as before.
69
+ */
70
+ format?: (value: number) => string;
71
+ sortable?: boolean;
72
+ /** Odoo: fields without `optional` cannot leave the view. When true, the
73
+ * column appears in the columns submenu and can be hidden. */
74
+ optional?: boolean;
75
+ /** Odoo `optional="hide"`: start off. Only with `optional`. */
76
+ defaultHidden?: boolean;
77
+ /**
78
+ * Edit this cell IN the list, through a small popover anchored to it: the salesperson of
79
+ * an invoice, its due date, its stage. The cell becomes a button (with a pencil on hover
80
+ * and focus) and `render` draws the editor inside the popover.
81
+ *
82
+ * Context: one function and not a kind of editor, because the editor is the system's own
83
+ * control — a `Picker`, a `Calendar`, a `Select` — written by whoever knows the field.
84
+ * The editor commits with its own gesture and then calls `done`, which closes the popover
85
+ * and returns the focus to the cell. Escape and a click outside close without committing.
86
+ * Do not draw links in an editable column: the cell is already a button.
87
+ */
88
+ edit?: {
89
+ render: (row: T, done: () => void) => ReactNode;
90
+ /** Which rows can be changed — a paid invoice cannot. All of them by default. */
91
+ when?: (row: T) => boolean;
92
+ };
93
+ /** The control of this column in the row of a NEW record (`DataTable`'s `create`).
94
+ * The same contract as `EditableTable`'s `edit`: `set` replaces the draft. Give it an
95
+ * `aria-label` — a cell has no `Field` to name it. Without it the column stays
96
+ * empty in that row: a computed amount, a sequence the backend assigns. */
97
+ input?: (draft: T, set: (next: T) => void) => ReactNode;
98
+ /** The copy button of the cells (`CellCopy`): on by default, copying what the cell shows.
99
+ * A function copies what it returns for the row instead; `false` removes it from this
100
+ * column — a column of tags or of buttons has nothing worth pasting. */
101
+ copy?: false | ((row: T) => string);
102
+ }
103
+ /** Writing a new record in the list. See the file's header. */
104
+ export type DataTableCreate<T> = {
105
+ /** The record being written, or `null` when there is none. */
106
+ draft: T | null;
107
+ /** «Add» was asked for: the consumer sets a blank `draft`. */
108
+ onStart: () => void;
109
+ onChange: (next: T) => void;
110
+ /** `Enter`, leaving the row after writing in it, or `Tab` past its last control. The
111
+ * consumer validates and saves, and sets `draft` back to `null` — or keeps it, to
112
+ * say what is missing. */
113
+ onCommit: (draft: T) => void;
114
+ /** `Escape`, or leaving the row without having written anything. */
115
+ onCancel: () => void;
116
+ /** The «add» under the table. Defaults to the copy table's. */
117
+ label?: string;
118
+ };
119
+ /**
120
+ * El agregado de una columna sobre un conjunto de filas.
121
+ *
122
+ * Vive afuera del componente porque lo preguntan tres lugares: el pie de la
123
+ * tabla, la banda de un montón en la planilla, y la línea de un informe armado
124
+ * a partir de los montones. Tres implementaciones de «cuánto suma esto» es que
125
+ * el pie diga un número y el montón otro.
126
+ *
127
+ * `null` cuando la columna no declaró agregado, no es numérica, o no hay ningún
128
+ * número que sumar — que es distinto de sumar cero.
129
+ */
130
+ export declare function aggregateOf<T extends object>(column: DataColumn<T>, rows: readonly T[]): number | null;
131
+ /**
132
+ * UN NIVEL, como lo necesita una tabla.
133
+ *
134
+ * La identidad es la del NIVEL y no la del campo: un mismo `registered_at`
135
+ * puesto por mes y por día son dos cortes, y con el nombre del campo serían el
136
+ * mismo repetido. `keyOf` da la clave del motor —la misma que arman las otras
137
+ * presentaciones, así que lo plegado vale para todas— y `labelOf` la lee en el
138
+ * idioma activo.
139
+ */
140
+ export type GroupColumn<T> = {
141
+ /** La identidad del nivel: `levelKey(level)`. */
142
+ id: string;
143
+ /** Cómo se llama el corte, a la izquierda del valor. Sin él, «Septiembre» no
144
+ * dice si es cuándo se registró o cuándo se corrió. */
145
+ label: string;
146
+ /** A qué montón va cada fila en este nivel. */
147
+ keyOf: (item: T) => string;
148
+ /** Cómo se lee esa clave. */
149
+ labelOf: (key: string) => string;
150
+ };
151
+ export declare function DataTable<T extends object>({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals, collapsed, onGroupToggle, create, copyable, }: {
152
+ data: readonly T[];
153
+ columns: readonly DataColumn<T>[];
154
+ getRowId: (row: T) => string;
155
+ sort?: SortState | null;
156
+ onSortChange?: (next: SortState | null) => void;
157
+ /**
158
+ * Los NIVELES por los que se parte, de afuera hacia adentro. Vacío o ausente
159
+ * es «sin agrupar».
160
+ *
161
+ * Varios, porque agrupar no es una pregunta: «por cliente y después por
162
+ * mes» es un árbol y no una lista de montones. El orden ES la jerarquía.
163
+ */
164
+ levels?: readonly GroupColumn<T>[];
165
+ selected?: ReadonlySet<string>;
166
+ onSelectedChange?: (next: Set<string>) => void;
167
+ height?: number;
168
+ onRowClick?: (row: T) => void;
169
+ /** What you do to ONE row without going into it. They are drawn hidden and
170
+ * show up on the hovered row — see `RowActions.tsx`. */
171
+ rowActions?: (row: T) => readonly RowAction[];
172
+ /** The footer with the aggregate of every column that declared one.
173
+ *
174
+ * Context: up to now the aggregate only existed INSIDE a group, so an
175
+ * ungrouped table had no way to say how much it adds up to — and «how much
176
+ * does it add up to» is the question that makes a table of amounts a report
177
+ * and not a list. It stays pinned at the bottom: in a four-hundred-row table,
178
+ * a total you have to scroll to is a total nobody sees. */
179
+ totals?: boolean;
180
+ /** Group keys that are closed. Same keys as `ListGroup.collapsed`, so the
181
+ * table does not reopen what rows just folded. */
182
+ collapsed?: readonly string[];
183
+ onGroupToggle?: (key: string) => void;
184
+ /** Write a new record as a row of the list. See the file's header. */
185
+ create?: DataTableCreate<T>;
186
+ /** The cells' copy buttons, on by default. `false` takes them off the whole table; a
187
+ * column turns its own off with `copy: false`. */
188
+ copyable?: boolean;
189
+ }): import("react").JSX.Element;
@@ -0,0 +1,330 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { fillCopy, useUiCopy } from "../../foundations/copy.js";
4
+ import { pathKey } from "../../foundations/grouping.js";
5
+ import { CellCopy } from "./CellCopy.js";
6
+ import { CellEditor } from "./CellEditor.js";
7
+ import { EditRow, firstControl } from "./EditableTable.js";
8
+ import { RowActions } from "./RowActions.js";
9
+ import { SelectBox, Table, Td, Th, THead, Tr, } from "./Table.js";
10
+ import { aggregationFns, columnGroupingFeature, createColumnHelper, createExpandedRowModel, createGroupedRowModel, createSortedRowModel, rowAggregationFeature, rowExpandingFeature, rowSortingFeature, sortFns, tableFeatures, useTable, } from "@tanstack/react-table";
11
+ import { ChevronRight, Plus } from "lucide-react";
12
+ import { useEffect, useMemo, useRef, useState } from "react";
13
+ /** The aggregates that are an amount OF the column — a sum of bolivianos is bolivianos — and
14
+ * so are written the way the column writes its numbers. `count` and `unique` count rows
15
+ * and values: «Bs. 12,00 rows» would be nonsense. */
16
+ const IN_COLUMN_UNITS = new Set(["sum", "mean", "min", "max"]);
17
+ /**
18
+ * How the aggregate of a column is written: its `format` when the aggregate is in the
19
+ * column's units, `undefined` (the bare number) otherwise.
20
+ *
21
+ * Exported for the other places that draw `aggregateOf` — the bands of the sheet, the report
22
+ * built from the piles — so the footer and the band never write the same total two ways.
23
+ * Not public.
24
+ */
25
+ export function aggregateFormat(column) {
26
+ return column.aggregate !== undefined && IN_COLUMN_UNITS.has(column.aggregate)
27
+ ? column.format
28
+ : undefined;
29
+ }
30
+ /** An aggregate of a column, written — or the bare number when nothing says how. */
31
+ export function aggregateText(column, value) {
32
+ const format = aggregateFormat(column);
33
+ return format === undefined ? value : format(value);
34
+ }
35
+ function aggregateLabel(copy, column, v) {
36
+ const n = v.toLocaleString(copy.locale);
37
+ /* Raw, a sum of amounts read «78.752,9»: one decimal lost and no currency, under a
38
+ column whose every cell says «Bs. 12.300,00». */
39
+ const amount = (fallback) => column.format === undefined ? fallback : column.format(v);
40
+ switch (column.aggregate ?? "count") {
41
+ case "sum":
42
+ return amount(n);
43
+ case "count":
44
+ return `${n} ${v === 1 ? copy.table.row : copy.table.rows}`;
45
+ case "mean":
46
+ return fillCopy(copy.table.mean, {
47
+ value: amount(v.toLocaleString(copy.locale, { maximumFractionDigits: 2 })),
48
+ });
49
+ case "min":
50
+ return fillCopy(copy.table.min, { value: amount(n) });
51
+ case "max":
52
+ return fillCopy(copy.table.max, { value: amount(n) });
53
+ case "unique":
54
+ return fillCopy(copy.table.distinct, { value: n });
55
+ }
56
+ }
57
+ function raw(copy, v) {
58
+ if (v === null)
59
+ return "—";
60
+ if (typeof v === "number")
61
+ return v.toLocaleString(copy.locale);
62
+ if (typeof v === "boolean")
63
+ return v ? copy.table.yes : copy.table.no;
64
+ return v;
65
+ }
66
+ /** What a cell draws: its `cell`, or the raw value. */
67
+ function drawCell(copy, column, row) {
68
+ return column.cell === undefined ? raw(copy, column.value(row)) : column.cell(row);
69
+ }
70
+ /* The features are declared once. In v9 they are opt-in: asking only for what
71
+ is used is what keeps the bundle down to what is actually needed. */
72
+ const FEATURES = tableFeatures({
73
+ rowSortingFeature,
74
+ columnGroupingFeature,
75
+ rowExpandingFeature,
76
+ // `rowAggregationFeature` is what enables `aggregationFn` on a column.
77
+ // Without it, grouping pulls rows together but each column does not know what
78
+ // to show in the group row — which is half of the reason for grouping.
79
+ rowAggregationFeature,
80
+ sortedRowModel: createSortedRowModel(),
81
+ groupedRowModel: createGroupedRowModel(),
82
+ expandedRowModel: createExpandedRowModel(),
83
+ sortFns,
84
+ aggregationFns,
85
+ });
86
+ /**
87
+ * El agregado de una columna sobre un conjunto de filas.
88
+ *
89
+ * Vive afuera del componente porque lo preguntan tres lugares: el pie de la
90
+ * tabla, la banda de un montón en la planilla, y la línea de un informe armado
91
+ * a partir de los montones. Tres implementaciones de «cuánto suma esto» es que
92
+ * el pie diga un número y el montón otro.
93
+ *
94
+ * `null` cuando la columna no declaró agregado, no es numérica, o no hay ningún
95
+ * número que sumar — que es distinto de sumar cero.
96
+ */
97
+ export function aggregateOf(column, rows) {
98
+ if (column.aggregate === undefined || column.numeric !== true)
99
+ return null;
100
+ const numbers = rows
101
+ .map((row) => column.value(row))
102
+ .filter((value) => typeof value === "number");
103
+ if (numbers.length === 0)
104
+ return null;
105
+ switch (column.aggregate) {
106
+ case "sum":
107
+ return numbers.reduce((a, b) => a + b, 0);
108
+ case "mean":
109
+ return numbers.reduce((a, b) => a + b, 0) / numbers.length;
110
+ case "min":
111
+ return Math.min(...numbers);
112
+ case "max":
113
+ return Math.max(...numbers);
114
+ case "unique":
115
+ return new Set(numbers).size;
116
+ default:
117
+ return numbers.length;
118
+ }
119
+ }
120
+ export function DataTable({ data, columns, getRowId, sort, onSortChange, levels, selected, onSelectedChange, height, onRowClick, rowActions, totals = false, collapsed, onGroupToggle, create, copyable = true, }) {
121
+ const copy = useUiCopy();
122
+ const draft = create?.draft ?? null;
123
+ const draftRow = useRef(null);
124
+ const addButton = useRef(null);
125
+ /* Whether anything was written in the draft: leaving a blank one drops it. */
126
+ const touched = useRef(false);
127
+ const writing = draft !== null;
128
+ /* Each «add» is a session. With `Tab` the next draft replaces the saved one without
129
+ ever being `null`, so `writing` alone would not say a new record began. */
130
+ const [session, setSession] = useState(0);
131
+ /* «add» is hidden while writing: the focus goes back to it once it is there again. */
132
+ const backToAdd = useRef(false);
133
+ const start = () => {
134
+ setSession((n) => n + 1);
135
+ create?.onStart();
136
+ };
137
+ /* A draft that appears takes the focus on its first control — «add» is a request to
138
+ write, not to look for where to. */
139
+ useEffect(() => {
140
+ if (!writing) {
141
+ if (backToAdd.current)
142
+ addButton.current?.focus();
143
+ backToAdd.current = false;
144
+ return;
145
+ }
146
+ touched.current = false;
147
+ const node = draftRow.current;
148
+ if (node !== null)
149
+ (firstControl(node) ?? node).focus();
150
+ }, [writing, session]);
151
+ const saveDraft = (refocus) => {
152
+ if (create === undefined || draft === null)
153
+ return;
154
+ backToAdd.current = refocus;
155
+ if (touched.current)
156
+ create.onCommit(draft);
157
+ else
158
+ create.onCancel();
159
+ };
160
+ // In v9 the helper is bound to the features AND to the data type: that is what
161
+ // makes the compiler know about `enableGrouping` only if we ask for the
162
+ // feature.
163
+ const helper = useMemo(() => createColumnHelper(), []);
164
+ const defs = useMemo(() => [
165
+ ...columns.map((c) => helper.accessor((row) => c.value(row), {
166
+ id: c.id,
167
+ header: c.header,
168
+ enableSorting: c.sortable !== false,
169
+ /* Ninguna columna agrupa. Lo que parte el conjunto son los NIVELES, y
170
+ cada uno entra como una columna suya —abajo— que existe sólo para
171
+ eso: nadie la dibuja y nadie la ordena. */
172
+ enableGrouping: false,
173
+ aggregationFn: c.aggregate ?? "count",
174
+ })),
175
+ /* Una columna por NIVEL, no por campo. Es la única forma de que «por mes y
176
+ adentro por día» sea dos cortes: con el id de la columna, los dos niveles
177
+ de `registered_at` eran el mismo id repetido, y un mismo id no se puede
178
+ agrupar dos veces.
179
+
180
+ Y la clave la pone el nivel, no la columna: `value` es la clave de
181
+ ORDEN —para una fecha, los milisegundos— y agrupar por ella deja un
182
+ montón por instante, con «1789294903895» de rótulo. Acá entra la misma
183
+ clave que arman las otras cinco presentaciones, así que los montones de
184
+ la tabla son LOS MISMOS y lo plegado vale para todas. */
185
+ ...(levels ?? []).map((level) => helper.accessor((row) => level.keyOf(row), {
186
+ id: level.id,
187
+ header: level.label,
188
+ enableSorting: false,
189
+ enableGrouping: true,
190
+ })),
191
+ ], [columns, helper, levels]);
192
+ /* The assertion, and why it is safe: `ColumnDef` is INVARIANT in its value
193
+ type (`in out TValue`), so an array of columns with concrete values is not
194
+ assignable to the one of `unknown` even though each one on its own is. The
195
+ library solves this with `helper.columns([...])`, which needs a literal
196
+ tuple — and here the columns come out of a `map` over what the consumer
197
+ declared, which is not literal.
198
+
199
+ It is safe because that value is only used by the library to sort and
200
+ group; it is never read with its type from our side. What gets drawn comes
201
+ from `c.cell(row)` or from `c.value(row)`, both typed against `T`. */
202
+ const table = useTable({
203
+ features: FEATURES,
204
+ data,
205
+ columns: defs,
206
+ getRowId: (row) => getRowId(row),
207
+ state: {
208
+ sorting: sort === null || sort === undefined
209
+ ? []
210
+ : [{ id: sort.key, desc: sort.direction === "desc" }],
211
+ grouping: (levels ?? []).map((level) => level.id),
212
+ },
213
+ // Groups start open: grouping and then having to open each group is two
214
+ // gestures for what the user asked for in one.
215
+ initialState: { expanded: true },
216
+ });
217
+ const rows = table.getRowModel().rows;
218
+ /**
219
+ * The PATH of a row: the values of every group it hangs from, outermost
220
+ * first.
221
+ *
222
+ * The identity of a pile is its route and not its value, because with several
223
+ * levels two piles can be called the same — «2026-09» inside «Ferretería Sur» and
224
+ * «2026-09» inside «Distribuidora Andina» are different piles, and folding one must not
225
+ * fold the other. It walks up because that is where TanStack keeps the
226
+ * ancestry; a leaf contributes nothing, since `groupingColumnId` is only set
227
+ * on grouped rows.
228
+ */
229
+ function pathOf(row) {
230
+ const path = [];
231
+ let node = row;
232
+ while (node !== undefined) {
233
+ const columnId = node.groupingColumnId;
234
+ if (columnId !== undefined && columnId !== null) {
235
+ path.unshift(String(node.getGroupingValue(columnId) ?? "—"));
236
+ }
237
+ node = node.getParentRow();
238
+ }
239
+ return path;
240
+ }
241
+ /* TanStack keeps every leaf in the model when `expanded: true`. Folding is
242
+ ours: the same path keys as the rows presentation, so switching present
243
+ does not reopen a group.
244
+
245
+ It checks EVERY ancestor and not just the parent. With one level they were
246
+ the same thing; with several, folding the outer pile has to take the inner
247
+ ones with it — otherwise closing «Ferretería Sur» leaves its months on screen,
248
+ orphaned under a header that says it is closed. */
249
+ const drawn = collapsed === undefined
250
+ ? rows
251
+ : rows.filter((row) => {
252
+ let node = row.getParentRow();
253
+ while (node !== undefined) {
254
+ if (collapsed.includes(pathKey(pathOf(node))))
255
+ return false;
256
+ node = node.getParentRow();
257
+ }
258
+ return true;
259
+ });
260
+ const selecting = onSelectedChange !== undefined;
261
+ const leaves = useMemo(() => rows.filter((r) => r.subRows.length === 0), [rows]);
262
+ /* The aggregates are computed by the component and not by TanStack: its
263
+ `aggregationFn` lives in the GROUP model, and a footer exists precisely when
264
+ there are no groups. */
265
+ const footer = useMemo(() => (totals ? columns.map((c) => aggregateOf(c, data)) : null), [totals, columns, data]);
266
+ const all = leaves.length > 0 && leaves.every((r) => selected?.has(r.id) === true);
267
+ const some = leaves.some((r) => selected?.has(r.id) === true);
268
+ function toggle(id, on) {
269
+ const n = new Set(selected ?? []);
270
+ if (on)
271
+ n.add(id);
272
+ else
273
+ n.delete(id);
274
+ onSelectedChange?.(n);
275
+ }
276
+ const grid = (_jsxs(Table, { height: height, children: [_jsxs(THead, { children: [selecting ? (_jsx(Th, { width: 36, children: _jsx(SelectBox, { label: copy.selection.all, checked: all, indeterminate: some, onChange: (v) => onSelectedChange?.(v ? new Set(leaves.map((r) => r.id)) : new Set()) }) })) : null, columns.map((c) => (_jsx(Th, { sortKey: c.sortable === false ? undefined : c.id, sort: sort, onSort: onSortChange, numeric: c.numeric, width: c.width, children: c.header }, c.id))), rowActions === undefined ? null : _jsx(Th, { width: 1, children: "" })] }), _jsxs("tbody", { children: [drawn.map((row) => {
277
+ const isGroup = row.subRows.length > 0;
278
+ if (isGroup) {
279
+ const path = pathOf(row);
280
+ const key = pathKey(path);
281
+ const depth = path.length - 1;
282
+ const id = row.groupingColumnId ?? undefined;
283
+ const level = levels?.find((one) => one.id === id);
284
+ /* La CLAVE del montón —la del motor— y el rótulo, que lo pone el
285
+ nivel: son dos textos y sólo el segundo cambia con el idioma. */
286
+ const value = path[depth] ?? key;
287
+ const open = collapsed === undefined
288
+ ? row.getIsExpanded()
289
+ : collapsed.includes(key) !== true;
290
+ return (
291
+ /* The group row: it spans the whole width, says which value it
292
+ groups by and how many are inside. The counter is what makes it
293
+ useful without opening it. */
294
+ _jsx("tr", { className: "border-b border-line bg-ground", children: _jsx("td", { colSpan: columns.length + (selecting ? 1 : 0) + (rowActions === undefined ? 0 : 1), className: "px-3 py-1.5", style: depth === 0
295
+ ? undefined
296
+ : { paddingLeft: `calc(var(--spacing-indent) * ${depth} + 0.75rem)` }, children: _jsxs("button", { type: "button", onClick: () => onGroupToggle === undefined ? row.toggleExpanded() : onGroupToggle(key), "aria-expanded": open, className: "inline-flex items-center gap-1.5 rounded-control text-meta font-medium text-ink transition-colors duration-(--duration-instant) hover:text-primary focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: [_jsx(ChevronRight, { className: `size-(--size-icon-sm) text-muted transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true }), level === undefined ? null : (_jsx("span", { className: "text-micro font-normal text-muted/70", children: level.label })), _jsx("span", { children: level === undefined ? value : level.labelOf(value) }), _jsx("span", { className: "inline-flex shrink-0 items-center rounded-pill border border-line-strong bg-ink/5 px-2 py-0.5 text-micro tabular-nums text-muted", children: row.getLeafRows().length.toLocaleString(copy.locale) })] }) }) }, row.id));
297
+ }
298
+ const original = row.original;
299
+ return (_jsxs(Tr, { selected: selected?.has(row.id) === true, onClick: onRowClick === undefined ? undefined : () => onRowClick(original), children: [selecting ? (_jsx(Td, { children: _jsx(SelectBox, { label: copy.selection.row, checked: selected?.has(row.id) === true, onChange: (v) => toggle(row.id, v) }) })) : null, columns.map((c) => {
300
+ const t = c.tone?.(original);
301
+ const n = c.note?.(original);
302
+ const edit = c.edit?.when?.(original) === false ? undefined : c.edit;
303
+ return (_jsx(Td, { numeric: c.numeric, mono: c.mono === true, ...(t === undefined ? {} : { tone: t }), ...(n === undefined ? {} : { title: n }), children: edit !== undefined ? (_jsx(CellEditor, { label: fillCopy(copy.inlineEdit.editCell, { column: c.header }), numeric: c.numeric === true, editor: (done) => edit.render(original, done), children: drawCell(copy, c, original) })) : copyable && c.copy !== false ? (_jsx(CellCopy, { header: c.header, numeric: c.numeric === true, ...(c.copy === undefined ? {} : { value: c.copy(original) }), children: drawCell(copy, c, original) })) : (drawCell(copy, c, original)) }, c.id));
304
+ }), rowActions === undefined ? null : (_jsx(RowActions, { actions: rowActions(original) }))] }, row.id));
305
+ }), create === undefined || draft === null ? null : (_jsxs(EditRow, { as: "tr", rowId: "\\u0000draft", rowRef: (node) => {
306
+ draftRow.current = node;
307
+ }, editable: true, editing: true, tabbable: false, className: "group/row", onStart: () => { }, onCommit: saveDraft, onCancel: () => {
308
+ backToAdd.current = true;
309
+ create.onCancel();
310
+ }, onMove: () => { }, onFocusRow: () => { }, onTabOut: (direction) => {
311
+ /* Past the last control: saved, and the next one started — unless nothing
312
+ was written, which is «I am done». Backwards, the browser's way. */
313
+ if (direction === "prev" || !touched.current)
314
+ return false;
315
+ create.onCommit(draft);
316
+ start();
317
+ return true;
318
+ }, children: [selecting ? _jsx("td", { className: DRAFT_CELL }) : null, columns.map((c) => (_jsx("td", { ...(c.input === undefined ? {} : { "data-edit": "" }), className: `${DRAFT_CELL} ${c.numeric === true ? "text-right tabular-nums" : ""}`, children: c.input?.(draft, (next) => {
319
+ touched.current = true;
320
+ create.onChange(next);
321
+ }) ?? null }, c.id))), rowActions === undefined ? null : _jsx("td", { className: DRAFT_CELL })] }))] }), footer === null ? null : (_jsx("tfoot", { className: "sticky bottom-0 z-10 bg-ground", children: _jsxs("tr", { className: "border-t border-line-strong", children: [selecting ? _jsx("td", {}) : null, columns.map((c, i) => {
322
+ const value = footer[i] ?? null;
323
+ return (_jsx("td", { className: `px-3 py-1.5 text-meta font-semibold text-ink ${c.numeric === true ? "text-right tabular-nums" : ""}`, children: i === 0 && value === null ? (_jsxs("span", { className: "text-muted", children: [data.length.toLocaleString(copy.locale), " ", data.length === 1 ? copy.table.row : copy.table.rows] })) : value === null ? null : (aggregateLabel(copy, c, value)) }, c.id));
324
+ }), rowActions === undefined ? null : _jsx("td", {})] }) }))] }));
325
+ if (create === undefined)
326
+ return grid;
327
+ return (_jsxs("div", { className: "flex min-w-0 flex-col", children: [grid, writing ? null : (_jsx("div", { className: "px-1 py-0.5", children: _jsxs("button", { ref: addButton, type: "button", onClick: start, className: "flex w-full items-center gap-1.5 rounded-control px-3 py-1 text-left text-meta text-primary transition-colors duration-(--duration-instant) hover:bg-primary/5 focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-primary", children: [_jsx(Plus, { className: "size-(--size-icon-sm)", strokeWidth: 2.5, "aria-hidden": true }), create.label ?? copy.table.add] }) }))] }));
328
+ }
329
+ /* A cell of the row being written: the table's cell, washed like an edited row. */
330
+ const DRAFT_CELL = "border-b border-line bg-primary/5 px-3 py-1 align-middle text-meta text-ink first:rounded-l-control last:rounded-r-control";