@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,38 @@
1
+ export type ReportRowKind = "section" | "group" | "line" | "total";
2
+ export interface ReportRow {
3
+ id: string;
4
+ label: string;
5
+ /** The indent. It comes from the consumer and not from the nesting, because a
6
+ * second-level total lives next to its siblings and not inside them. */
7
+ level?: number;
8
+ kind?: ReportRowKind;
9
+ /** One value per column, in the same order. `null` is «not applicable» and is
10
+ * drawn empty — different from `0`, which is an amount. */
11
+ values: readonly (number | null)[];
12
+ /** What hangs underneath when it opens. Its presence is what puts the chevron
13
+ * there. */
14
+ children?: readonly ReportRow[];
15
+ /** The note on the line: what goes into this account, where the number comes
16
+ * from. */
17
+ note?: string;
18
+ onClick?: () => void;
19
+ }
20
+ export interface ReportColumn {
21
+ id: string;
22
+ header: string;
23
+ /** For a percentage or a variation column, which is not formatted the same
24
+ * way. */
25
+ format?: (value: number) => string;
26
+ }
27
+ export declare function ReportTable({ columns, rows,
28
+ /** Which groups start open. By default, none: a balance sheet with everything
29
+ * expanded is four hundred lines, and what gets looked at first are the
30
+ * totals. */
31
+ defaultOpen, caption, height, }: {
32
+ columns: readonly ReportColumn[];
33
+ rows: readonly ReportRow[];
34
+ defaultOpen?: readonly string[];
35
+ /** What says what this report is about. It is read out loud and it is drawn. */
36
+ caption?: string;
37
+ height?: number;
38
+ }): import("react").JSX.Element;
@@ -0,0 +1,106 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { currentCopyLocale, fillCopy, useUiCopy } from "../../foundations/copy.js";
4
+ import { byDensity, useDensity } from "../../foundations/density.js";
5
+ import { stickyTop, useScrollBox } from "../../foundations/sticky.js";
6
+ import { ChevronRight, Info } from "lucide-react";
7
+ import { useState } from "react";
8
+ /* `useGrouping: "always"` and not the default: without it, four-digit numbers
9
+ lose the thousands separator —`2023.04` next to `258,015.31` in en-US, or
10
+ `2023,04` next to `258.015,31` in es-ES— and in a column of amounts that
11
+ inconsistency reads as a data error. */
12
+ function amountFormat(locale) {
13
+ return new Intl.NumberFormat(locale, {
14
+ minimumFractionDigits: 2,
15
+ maximumFractionDigits: 2,
16
+ /* The assertion is because of the type definitions, not because of the
17
+ engine: `"always"` is ES2023 and TypeScript's `lib` still declares
18
+ `useGrouping` as a boolean. The runtime understands it, and a boolean here
19
+ is not enough — `true` still leaves four-digit numbers without a
20
+ separator. */
21
+ useGrouping: "always",
22
+ });
23
+ }
24
+ const INDENT = 1.25;
25
+ /** The look of each kind of row. It is up here and not spread across the JSX
26
+ * because it is THE decision table of the component: it reads in one go and it
27
+ * changes in one place.
28
+ *
29
+ * `cell` goes on EVERY cell and not on the `<tr>`: the first column is `sticky`
30
+ * and needs a background of its own to cover what passes underneath, so a
31
+ * background on the row plus another one on the pinned cell gives two layers of
32
+ * the same alpha and the band comes out of two greys. The hover of a clickable
33
+ * row is the same: a layer inside each cell, rounded at the two ends, so the
34
+ * pinned name stays opaque and the mark does not run off the sheet. */
35
+ const SKIN = {
36
+ section: { row: "", cell: "bg-ink/8", text: "font-semibold text-ink" },
37
+ group: { row: "", cell: "bg-surface", text: "font-medium text-ink" },
38
+ line: { row: "", cell: "bg-surface", text: "text-ink" },
39
+ total: {
40
+ row: "border-t border-line-strong",
41
+ cell: "bg-surface",
42
+ text: "font-semibold text-ink",
43
+ },
44
+ };
45
+ function Amount({ value, format }) {
46
+ if (value === null)
47
+ return _jsx("span", { className: "text-line-strong", children: "\u00B7" });
48
+ const zero = value === 0;
49
+ const negative = value < 0;
50
+ return (_jsx("span", { className: `tabular-nums ${zero ? "text-muted" : negative ? "text-bad" : "text-ink"}`, children: format === undefined ? amountFormat(currentCopyLocale()).format(value) : format(value) }));
51
+ }
52
+ function ReportLine({ row, columns, open, onToggle, compact, }) {
53
+ const copy = useUiCopy();
54
+ const kind = row.kind ?? "line";
55
+ const skin = SKIN[kind];
56
+ const level = row.level ?? 0;
57
+ const collapsible = row.children !== undefined && row.children.length > 0;
58
+ const isOpen = open.has(row.id);
59
+ const clickable = row.onClick !== undefined;
60
+ return (_jsxs(_Fragment, { children: [_jsxs("tr", { className: `group/row ${skin.row} ${clickable ? "cursor-pointer" : ""}`, onClick: row.onClick, children: [_jsxs("th", { scope: "row",
61
+ /* `sticky left-0` so that the account name does not go away when you
62
+ scroll twelve comparison columns to the right. An amount without its
63
+ line is a loose number. */
64
+ className: `sticky left-0 z-10 text-left font-normal ${skin.cell} ${compact ? "py-1 pr-4 pl-2" : "py-1.5 pr-6 pl-3"}`, style: { paddingLeft: `${(compact ? 0.5 : 0.75) + level * INDENT}rem` }, children: [clickable ? (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-0 rounded-l-control group-hover/row:bg-primary/5" })) : null, _jsxs("span", { className: `relative flex min-w-0 items-center gap-1.5 text-meta ${skin.text}`, children: [collapsible ? (_jsx("button", { type: "button", onClick: (e) => {
65
+ e.stopPropagation();
66
+ onToggle(row.id);
67
+ }, "aria-expanded": isOpen, "aria-label": fillCopy(isOpen ? copy.report.close : copy.report.open, {
68
+ name: row.label,
69
+ }), className: "-ml-1 inline-flex size-4 shrink-0 items-center justify-center rounded-pill text-muted transition-colors duration-(--duration-instant) hover:bg-ink/8 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: _jsx(ChevronRight, { className: `size-(--size-icon-sm) transition-transform duration-(--duration-instant) ${isOpen ? "rotate-90" : ""}`, strokeWidth: 2 }) })) : (
70
+ /* The slot for the chevron is reserved anyway: without it, a leaf
71
+ account and a collapsible one would start on different
72
+ verticals and the hierarchy would stop reading. */
73
+ _jsx("span", { className: "-ml-1 size-4 shrink-0", "aria-hidden": true })), _jsx("span", { className: "truncate", children: row.label })] })] }), columns.map((c, i) => (_jsxs("td", { className: `text-right text-meta whitespace-nowrap ${clickable ? "relative" : ""} ${skin.cell} ${compact ? "px-2 py-1" : "px-3 py-1.5"}`, children: [clickable ? (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-0 group-hover/row:bg-primary/5" })) : null, kind === "section" ? null : (_jsx("span", { className: "relative", children: _jsx(Amount, { value: row.values[i] ?? null, ...(c.format === undefined ? {} : { format: c.format }) }) }))] }, c.id))), _jsxs("td", { className: `w-0 ${clickable ? "relative" : ""} ${skin.cell} ${compact ? "pr-2" : "pr-3"}`, children: [clickable ? (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-0 rounded-r-control group-hover/row:bg-primary/5" })) : null, row.note === undefined ? null : (_jsx("span", { title: row.note, className: "relative inline-flex text-muted transition-colors duration-(--duration-instant) hover:text-ink", children: _jsx(Info, { className: "size-(--size-icon-sm)", strokeWidth: 2, "aria-label": row.note }) }))] })] }), !collapsible || !isOpen
74
+ ? null
75
+ : row.children?.map((child) => (_jsx(ReportLine, { row: child, columns: columns, open: open, onToggle: onToggle, compact: compact }, child.id)))] }));
76
+ }
77
+ export function ReportTable({ columns, rows,
78
+ /** Which groups start open. By default, none: a balance sheet with everything
79
+ * expanded is four hundred lines, and what gets looked at first are the
80
+ * totals. */
81
+ defaultOpen, caption, height, }) {
82
+ const copy = useUiCopy();
83
+ const d = useDensity();
84
+ const compact = d === "compact";
85
+ const [open, setOpen] = useState(() => new Set(defaultOpen ?? []));
86
+ const caja = useScrollBox(height === undefined ? {} : { height });
87
+ function toggle(id) {
88
+ setOpen((prev) => {
89
+ const n = new Set(prev);
90
+ if (n.has(id))
91
+ n.delete(id);
92
+ else
93
+ n.add(id);
94
+ return n;
95
+ });
96
+ }
97
+ return (
98
+ /* Misma decisión que las otras dos tablas, en el mismo lugar: con alto la
99
+ caja scrollea y la pila arranca de cero adentro; sin alto y entrando, no
100
+ hay caja y el encabezado se pega debajo de la barra de la lista. Antes
101
+ esto era `overflow-auto` siempre, así que el encabezado heredaba el
102
+ desplazamiento de la barra y quedaba empujado 68 px hacia adentro de su
103
+ propia caja — banda en blanco arriba y títulos de columna cayendo entre
104
+ las filas. */
105
+ _jsx("div", { ref: caja.ref, className: `min-w-0 ${caja.className}`, style: caja.style, children: _jsxs("table", { className: "w-full border-collapse px-1", children: [caption === undefined ? null : (_jsx("caption", { className: `text-left text-meta text-muted ${byDensity(d, "px-3 pb-2", "px-2 pb-1.5")}`, children: caption })), _jsx("thead", { className: "sticky z-20 bg-ground", style: stickyTop, children: _jsxs("tr", { className: "border-b border-line-strong", children: [_jsx("th", { className: `sticky left-0 z-10 bg-ground text-left ${compact ? "px-2 py-1.5" : "px-3 py-2"}`, children: _jsx("span", { className: "sr-only", children: copy.report.concept }) }), columns.map((c) => (_jsx("th", { scope: "col", className: `text-right text-meta font-medium whitespace-nowrap text-muted ${compact ? "px-2 py-1.5" : "px-3 py-2"}`, children: c.header }, c.id))), _jsx("th", { className: "w-0" })] }) }), _jsx("tbody", { children: rows.map((r) => (_jsx(ReportLine, { row: r, columns: columns, open: open, onToggle: toggle, compact: compact }, r.id))) })] }) }));
106
+ }
@@ -0,0 +1,16 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ export interface RowAction {
3
+ id: string;
4
+ /** What is read out loud, in the `title` and in the menu. */
5
+ label: string;
6
+ icon: LucideIcon;
7
+ tone?: "normal" | "action" | "danger";
8
+ disabled?: boolean;
9
+ onSelect: () => void;
10
+ }
11
+ export declare function RowActions({ actions,
12
+ /** How many stay visible before the rest fall into the `⋯`. */
13
+ inline, }: {
14
+ actions: readonly RowAction[];
15
+ inline?: number;
16
+ }): import("react").JSX.Element;
@@ -0,0 +1,28 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { IconButton } from "../button/Button.js";
5
+ import { Menu } from "../menu/Menu.js";
6
+ import { MoreHorizontal } from "lucide-react";
7
+ export function RowActions({ actions,
8
+ /** How many stay visible before the rest fall into the `⋯`. */
9
+ inline = 2, }) {
10
+ const copy = useUiCopy();
11
+ if (actions.length === 0)
12
+ return _jsx("td", { className: "w-0" });
13
+ /* With only one left over there is no `⋯`: a menu with a single item is one
14
+ extra click to reach the same button. */
15
+ const inlineActions = actions.length <= inline + 1 ? actions : actions.slice(0, inline);
16
+ const overflow = actions.slice(inlineActions.length);
17
+ const byId = new Map(actions.map((a) => [a.id, a]));
18
+ return (_jsx("td", { className: "w-0 py-1 pr-2 pl-1 align-middle whitespace-nowrap", children: _jsxs("div", { onClick: (e) => e.stopPropagation(), className: "flex items-center justify-end gap-0.5 opacity-0 transition-opacity duration-(--duration-instant) group-hover/row:opacity-100 focus-within:opacity-100", children: [inlineActions.map((a) => (_jsx(IconButton, { icon: a.icon, label: a.label, size: "sm", ...(a.tone === undefined ? {} : { tone: a.tone }), ...(a.disabled === undefined ? {} : { disabled: a.disabled }), onClick: a.onSelect }, a.id))), overflow.length === 0 ? null : (_jsx(Menu, { label: copy.selection.more, icon: MoreHorizontal, iconOnly: true, chevron: false, align: "end", groups: [
19
+ {
20
+ options: overflow.map((a) => ({
21
+ id: a.id,
22
+ label: a.label,
23
+ icon: a.icon,
24
+ ...(a.disabled === undefined ? {} : { disabled: a.disabled }),
25
+ })),
26
+ },
27
+ ], onSelect: (id) => byId.get(id)?.onSelect() }))] }) }));
28
+ }
@@ -0,0 +1,73 @@
1
+ import type { Tone } from "../../foundations/types.js";
2
+ import { type ReactNode } from "react";
3
+ export interface SheetColumn {
4
+ id: string;
5
+ /** The name of the column, just as it came from the file. */
6
+ header: string;
7
+ /** The detected type, underneath the name. It is half the value of a glance:
8
+ * seeing that `importe` was read as text explains the rest of the screen. */
9
+ type?: string;
10
+ numeric?: boolean;
11
+ width?: number;
12
+ }
13
+ export declare function SheetTable({ columns, rows,
14
+ /** The value of a cell, by index. It is a function and not a prebuilt matrix
15
+ * so that the consumer can read from wherever they want without copying a
16
+ * million rows into another structure. */
17
+ value,
18
+ /** Which cells are painted and with what. `undefined` is unpainted, which is
19
+ * the normal case — a spreadsheet with everything colored highlights
20
+ * nothing. */
21
+ tone,
22
+ /** What the cell says on hover: «imputado por la mediana». */
23
+ note, height, rowLabel, band, onBandToggle, caption, onCommit, editable, onAddRow, }: {
24
+ columns: readonly SheetColumn[];
25
+ rows: number;
26
+ value: (row: number, column: SheetColumn, columnIndex: number) => string | number | null;
27
+ tone?: (row: number, column: SheetColumn, columnIndex: number) => Tone | undefined;
28
+ note?: (row: number, column: SheetColumn, columnIndex: number) => string | undefined;
29
+ /** Pixel height of the scroll box. `"auto"` grows with the rows so a
30
+ * twenty-row glance is not a window with its own scrollbar. */
31
+ height?: number | "auto";
32
+ rowLabel?: (row: number) => string;
33
+ /**
34
+ * Esta fila no es una fila: es la cabecera del montón que empieza acá.
35
+ *
36
+ * Agrupar sin perder la virtualización es esto — **una banda es una fila
37
+ * más, de otra clase**. La alternativa era una planilla por montón, y eso
38
+ * son N cuadrículas con N encabezados y N scrolls para un solo conjunto.
39
+ *
40
+ * `depth` es cuán adentro está el montón. La planilla no puede recibir un
41
+ * árbol —está virtualizada por índice, y un índice es una posición en una
42
+ * secuencia—, así que la jerarquía la dice la sangría de cada banda y no el
43
+ * anidamiento del marcado.
44
+ */
45
+ band?: (row: number) => {
46
+ field?: string;
47
+ label: string;
48
+ count: number;
49
+ open: boolean;
50
+ depth?: number;
51
+ /**
52
+ * Lo que el montón suma EN CADA COLUMNA, en la posición de esa columna.
53
+ *
54
+ * Una banda que pone su total en un rincón obliga a buscar a qué columna
55
+ * corresponde el número. Alineado bajo su columna no hay nada que buscar:
56
+ * la cuadrícula ya dice qué significa, que es para lo que existe una
57
+ * cuadrícula. `null` en una columna que no suma — que es la mayoría.
58
+ */
59
+ total?: (column: SheetColumn, columnIndex: number) => string | number | null;
60
+ } | undefined;
61
+ onBandToggle?: (row: number) => void;
62
+ caption?: ReactNode;
63
+ /** Present = the spreadsheet is editable. It receives the raw text just as it
64
+ * was typed; converting it to the column's type belongs to the domain, not
65
+ * here. */
66
+ onCommit?: (row: number, column: SheetColumn, columnIndex: number, next: string) => void;
67
+ /** Which cells are editable. Without this, all of them — but a computed column
68
+ * or an identifier is not touched. */
69
+ editable?: (row: number, column: SheetColumn, columnIndex: number) => boolean;
70
+ /** Present = rows can be added (see the file's header). Only with `onCommit`: a row
71
+ * that cannot be written in is not worth adding. */
72
+ onAddRow?: () => void;
73
+ }): import("react").JSX.Element;
@@ -0,0 +1,230 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { stickyTop, useScrollBox } from "../../foundations/sticky.js";
5
+ import { Cell } from "../value/Value.js";
6
+ import { useVirtualizer } from "@tanstack/react-virtual";
7
+ import { ChevronRight, Plus } from "lucide-react";
8
+ import { useEffect, useState } from "react";
9
+ const TONE = {
10
+ neutral: "bg-ink/6",
11
+ info: "bg-info/12",
12
+ good: "bg-good/12",
13
+ warn: "bg-warn/15",
14
+ bad: "bg-bad/12",
15
+ accent: "bg-accent/20",
16
+ };
17
+ const WIDTH = 168;
18
+ const HEIGHT = 28;
19
+ export function SheetTable({ columns, rows,
20
+ /** The value of a cell, by index. It is a function and not a prebuilt matrix
21
+ * so that the consumer can read from wherever they want without copying a
22
+ * million rows into another structure. */
23
+ value,
24
+ /** Which cells are painted and with what. `undefined` is unpainted, which is
25
+ * the normal case — a spreadsheet with everything colored highlights
26
+ * nothing. */
27
+ tone,
28
+ /** What the cell says on hover: «imputado por la mediana». */
29
+ note, height = 420, rowLabel, band, onBandToggle, caption, onCommit, editable, onAddRow, }) {
30
+ const copy = useUiCopy();
31
+ const labelOf = rowLabel ?? ((i) => (i + 1).toLocaleString(copy.locale));
32
+ /* `"auto"` es para un conjunto que ya vino CORTADO —una página de ochenta— y
33
+ deja de virtualizar de hecho: sin scroll propio, la ventana visible es la
34
+ caja entera y se dibujan todas las filas. Para los datos crudos —donde son
35
+ un millón— hay que declarar un alto, y ahí sí se dibujan las ~30 que se
36
+ ven. Cuándo la caja es un contenedor de scroll lo decide `useScrollBox`. */
37
+ const caja = useScrollBox(height === "auto" ? {} : { height });
38
+ const v = useVirtualizer({
39
+ count: rows,
40
+ getScrollElement: () => caja.ref.current,
41
+ estimateSize: () => HEIGHT,
42
+ overscan: 12,
43
+ });
44
+ const items = v.getVirtualItems();
45
+ const [active, setActive] = useState(null);
46
+ const [draft, setDraft] = useState(null);
47
+ const editing = draft !== null;
48
+ const isEditable = onCommit !== undefined;
49
+ function canEdit(r, c) {
50
+ if (!isEditable)
51
+ return false;
52
+ const col = columns[c];
53
+ if (col === undefined)
54
+ return false;
55
+ return editable === undefined || editable(r, col, c);
56
+ }
57
+ function goTo(r, c) {
58
+ const rr = Math.max(0, Math.min(rows - 1, r));
59
+ const cc = Math.max(0, Math.min(columns.length - 1, c));
60
+ setActive({ r: rr, c: cc });
61
+ setDraft(null);
62
+ v.scrollToIndex(rr);
63
+ }
64
+ function openCell(r, c, initial) {
65
+ if (!canEdit(r, c))
66
+ return;
67
+ const col = columns[c];
68
+ setActive({ r, c });
69
+ setDraft(initial ?? String(value(r, col, c) ?? ""));
70
+ }
71
+ function commit(dr, dc) {
72
+ if (active === null || draft === null)
73
+ return;
74
+ const col = columns[active.c];
75
+ if (col !== undefined)
76
+ onCommit?.(active.r, col, active.c, draft);
77
+ setDraft(null);
78
+ if (dr !== 0 || dc !== 0)
79
+ goTo(active.r + dr, active.c + dc);
80
+ }
81
+ /* The column the active cell waits in until the added row arrives, and how many rows
82
+ there were when it was asked for. */
83
+ const canAdd = isEditable && onAddRow !== undefined;
84
+ const [arriving, setArriving] = useState(null);
85
+ function addRow(c) {
86
+ if (!canAdd)
87
+ return;
88
+ setArriving({ c, from: rows });
89
+ onAddRow?.();
90
+ }
91
+ useEffect(() => {
92
+ if (arriving === null || rows <= arriving.from)
93
+ return;
94
+ setArriving(null);
95
+ setActive({ r: rows - 1, c: arriving.c });
96
+ setDraft(null);
97
+ v.scrollToIndex(rows - 1);
98
+ caja.ref.current?.focus();
99
+ /* `v` and `caja` are new objects every render; the row count is what decides. */
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ }, [rows, arriving]);
102
+ /* All the keyboard handling in one place. Splitting it between the box and
103
+ each cell is how you end up with spreadsheets whose arrows sometimes move
104
+ and sometimes scroll. */
105
+ function onKey(e) {
106
+ if (active === null)
107
+ return;
108
+ const { r, c } = active;
109
+ if (editing) {
110
+ if (e.key === "Escape") {
111
+ e.preventDefault();
112
+ setDraft(null);
113
+ }
114
+ else if (e.key === "Enter") {
115
+ e.preventDefault();
116
+ if (!e.shiftKey && canAdd && r === rows - 1) {
117
+ commit(0, 0);
118
+ addRow(c);
119
+ }
120
+ else
121
+ commit(e.shiftKey ? -1 : 1, 0);
122
+ }
123
+ else if (e.key === "Tab") {
124
+ e.preventDefault();
125
+ commit(0, e.shiftKey ? -1 : 1);
126
+ }
127
+ return;
128
+ }
129
+ switch (e.key) {
130
+ case "ArrowDown":
131
+ e.preventDefault();
132
+ goTo(r + 1, c);
133
+ return;
134
+ case "ArrowUp":
135
+ e.preventDefault();
136
+ goTo(r - 1, c);
137
+ return;
138
+ case "ArrowRight":
139
+ case "Tab":
140
+ e.preventDefault();
141
+ goTo(r, c + (e.shiftKey && e.key === "Tab" ? -1 : 1));
142
+ return;
143
+ case "ArrowLeft":
144
+ e.preventDefault();
145
+ goTo(r, c - 1);
146
+ return;
147
+ case "Enter":
148
+ case "F2":
149
+ e.preventDefault();
150
+ openCell(r, c);
151
+ return;
152
+ case "Delete":
153
+ case "Backspace":
154
+ e.preventDefault();
155
+ if (canEdit(r, c)) {
156
+ const col = columns[c];
157
+ if (col !== undefined)
158
+ onCommit?.(r, col, c, "");
159
+ }
160
+ return;
161
+ default:
162
+ /* Typing with the cell active starts editing and keeps the keystroke:
163
+ it is what anyone coming from a spreadsheet does, and losing the first
164
+ character feels like the app is not responding. */
165
+ if (e.key.length === 1 && !e.metaKey && !e.ctrlKey && !e.altKey) {
166
+ e.preventDefault();
167
+ openCell(r, c, e.key);
168
+ }
169
+ }
170
+ }
171
+ return (_jsxs("div", { className: "flex min-w-0 flex-col", children: [caption === undefined ? null : (_jsx("div", { className: "px-3 pb-2 text-meta text-muted", children: caption })), _jsx("div", { ref: caja.ref, style: caja.style, onKeyDown: isEditable ? onKey : undefined, tabIndex: isEditable ? 0 : undefined, className: `relative min-w-0 rounded-box border border-line bg-surface focus:outline-none ${caja.className}`, role: "grid", "aria-rowcount": rows, "aria-colcount": columns.length + 1, children: _jsxs("div", { style: {
172
+ minWidth: `${ROW_LABEL_WIDTH + columns.reduce((a, c) => a + (c.width ?? WIDTH), 0)}px`,
173
+ }, children: [_jsxs("div", { className: "sticky z-20 flex border-b border-line-strong bg-ground", style: stickyTop, role: "row", children: [_jsx("div", { className: "sticky left-0 z-30 shrink-0 border-r border-line bg-ground", style: { width: ROW_LABEL_WIDTH } }), columns.map((c) => (_jsxs("div", { role: "columnheader", style: { width: c.width ?? WIDTH }, className: `flex shrink-0 flex-col justify-center gap-0.5 border-r border-line px-2.5 py-1.5 ${c.numeric === true ? "items-end" : "items-start"}`, children: [_jsx("span", { className: "max-w-full truncate text-meta font-medium text-ink", title: c.header, children: c.header }), c.type === undefined ? null : (_jsx("span", { className: "max-w-full truncate font-mono text-micro text-muted", children: c.type }))] }, c.id))), _jsx("div", { className: "flex-1" })] }), _jsx("div", { style: { height: v.getTotalSize(), position: "relative" }, children: items.map((item) => {
174
+ const montón = band?.(item.index);
175
+ if (montón !== undefined) {
176
+ /* La banda queda pegada a la IZQUIERDA como el número de fila:
177
+ con cuarenta columnas, un rótulo que se va con el scroll
178
+ horizontal deja de decir en qué montón estás justo cuando
179
+ más lejos estás de él. */
180
+ return (
181
+ /* La banda RESPETA la cuadrícula: rótulo en la columna fija de
182
+ la izquierda, y después una celda por columna —vacía, salvo
183
+ que esa columna sume algo—. Antes era un rótulo suelto que
184
+ ignoraba las columnas, y entonces un total quedaba en un
185
+ rincón sin decir de qué columna era. */
186
+ _jsxs("div", { role: "row", "aria-rowindex": item.index + 1, className: "absolute top-0 left-0 flex w-full border-b border-line-strong bg-ground", style: { height: item.size, transform: `translateY(${item.start}px)` }, children: [_jsx("button", { type: "button", onClick: onBandToggle === undefined
187
+ ? undefined
188
+ : () => onBandToggle(item.index), "aria-expanded": montón.open, className: "sticky left-0 z-10 flex min-w-0 shrink-0 items-center gap-1.5 border-r border-line bg-ground px-2.5 text-left", style: {
189
+ height: "100%",
190
+ /* El mismo ancho que la columna del número de fila, para
191
+ que el rótulo no corra la cuadrícula. */
192
+ width: ROW_LABEL_WIDTH,
193
+ ...(montón.depth === undefined || montón.depth === 0
194
+ ? {}
195
+ : {
196
+ paddingLeft: `calc(var(--spacing-indent) * ${montón.depth} + 0.625rem)`,
197
+ }),
198
+ }, children: onBandToggle === undefined ? null : (_jsx(ChevronRight, { className: `size-(--size-icon-sm) shrink-0 text-muted transition-transform duration-(--duration-instant) ${montón.open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true })) }), columns.map((c, ci) => {
199
+ const total = montón.total?.(c, ci) ?? null;
200
+ return (_jsx("div", { role: "gridcell", style: { width: c.width ?? WIDTH }, className: `flex shrink-0 items-center overflow-hidden border-r border-line px-2.5 whitespace-nowrap ${c.numeric === true ? "justify-end" : "justify-start"}`, children: ci === 0 ? (_jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [montón.field === undefined ? null : (_jsx("span", { className: "shrink-0 text-micro text-muted/70", children: montón.field })), _jsx("span", { className: "truncate text-micro font-medium text-ink", children: montón.label }), _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: montón.count.toLocaleString(copy.locale) })] })) : total === null ? null : (_jsx("span", { className: "truncate text-meta font-medium tabular-nums text-ink", children: typeof total === "number"
201
+ ? total.toLocaleString(copy.locale)
202
+ : total })) }, c.id));
203
+ })] }, item.key));
204
+ }
205
+ return (_jsxs("div", { role: "row", "aria-rowindex": item.index + 1, className: "absolute top-0 left-0 flex w-full border-b border-line", style: { height: item.size, transform: `translateY(${item.start}px)` }, children: [_jsx("div", { className: "sticky left-0 z-10 flex shrink-0 items-center justify-end border-r border-line bg-surface px-2 text-micro tabular-nums text-muted", style: { width: ROW_LABEL_WIDTH }, children: labelOf(item.index) }), columns.map((c, ci) => {
206
+ const t = tone?.(item.index, c, ci);
207
+ const n = note?.(item.index, c, ci);
208
+ const isActive = active !== null && active.r === item.index && active.c === ci;
209
+ const isEditingHere = isActive && editing;
210
+ return (_jsx("div", { role: "gridcell", title: n, "aria-selected": isActive, onClick: isEditable ? () => goTo(item.index, ci) : undefined, onDoubleClick: isEditable ? () => openCell(item.index, ci) : undefined, style: { width: c.width ?? WIDTH }, className: `relative flex shrink-0 items-center overflow-hidden border-r border-line px-2.5 text-meta tabular-nums whitespace-nowrap text-ink ${c.numeric === true ? "justify-end" : "justify-start"} ${t === undefined ? "" : TONE[t]} ${
211
+ /* The outline of the active cell goes on the inside
212
+ (`inset-ring`) and not on the outside: a 2 px border
213
+ would push the content and the whole row would dance
214
+ as you move around with the arrows. */
215
+ isActive
216
+ ? "inset-ring-2 inset-ring-primary"
217
+ : isEditable
218
+ ? "cursor-cell"
219
+ : ""}`, children: isEditingHere ? (_jsx("input", { autoFocus: true, value: draft, onChange: (ev) => setDraft(ev.target.value), onBlur: () => commit(0, 0), className: `absolute inset-0 w-full bg-surface px-2.5 text-meta tabular-nums text-ink outline-none ${c.numeric === true ? "text-right" : "text-left"}` })) : (_jsx(Cell, { value: value(item.index, c, ci), max: 24 })) }, c.id));
220
+ }), _jsx("div", { className: "flex-1" })] }, item.key));
221
+ }) })] }) }), canAdd ? (
222
+ /* Under the grid and outside its scroll box, like the editable table's «add»: in
223
+ view however far down a long sheet is scrolled. The new row's first cell that
224
+ can be written takes the place. */
225
+ _jsx("div", { className: "px-1 py-0.5", children: _jsxs("button", { type: "button", onClick: () => {
226
+ const first = columns.findIndex((_, ci) => canEdit(rows, ci));
227
+ addRow(first === -1 ? 0 : first);
228
+ }, className: "flex w-full items-center gap-1.5 rounded-control px-2 py-0.5 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 }), copy.editable.addRow] }) })) : null] }));
229
+ }
230
+ const ROW_LABEL_WIDTH = 64;
@@ -0,0 +1,106 @@
1
+ import type { Tone } from "../../foundations/types.js";
2
+ import { type ReactNode } from "react";
3
+ export type SortDirection = "asc" | "desc";
4
+ export interface SortState {
5
+ key: string;
6
+ direction: SortDirection;
7
+ }
8
+ export declare function Table({ children,
9
+ /** With a height, the table scrolls on its own and the header sticks.
10
+ * Without a height, the page scrolls and the header goes away. */
11
+ height, }: {
12
+ children: ReactNode;
13
+ /** A number means pixels; a string goes through as is, so `26rem` follows the
14
+ * system scale instead of pinning a size in pixels. */
15
+ height?: number | string;
16
+ }): import("react").JSX.Element;
17
+ export declare function THead({ children }: {
18
+ children: ReactNode;
19
+ }): import("react").JSX.Element;
20
+ /** A column. If it gets a `sortKey`, it can be sorted.
21
+ *
22
+ * The icon is ALWAYS there, dimmed when the column is not the sorted one: an
23
+ * indicator that only shows up once you sort does not say which columns are
24
+ * sortable, and then you have to try them one by one. */
25
+ export declare function Th({ children, sortKey, sort, onSort, numeric, align, width, }: {
26
+ children: ReactNode;
27
+ sortKey?: string;
28
+ sort?: SortState | null;
29
+ onSort?: (next: SortState | null) => void;
30
+ /** Numbers align to the right: that is what lets you compare orders of
31
+ * magnitude without reading the figures. */
32
+ numeric?: boolean;
33
+ /** The alignment, when it is NOT because it is a number. They are two
34
+ * different things: `numeric` says «this is a figure» and drags along the
35
+ * right alignment and the tabular figures; `align` only pushes. A status
36
+ * label on the right is not a number. */
37
+ align?: "left" | "right";
38
+ /** A number means pixels; a string goes through as is, so `6rem` follows the
39
+ * system scale instead of pinning a size in pixels. */
40
+ width?: number | string;
41
+ }): import("react").JSX.Element;
42
+ export declare function Tr({ children, onClick, selected, }: {
43
+ children: ReactNode;
44
+ onClick?: () => void;
45
+ selected?: boolean;
46
+ }): import("react").JSX.Element;
47
+ /**
48
+ * A row that opens its own detail underneath.
49
+ *
50
+ * Context: `DataTable` expands GROUPS —TanStack takes care of that— and that is
51
+ * not enough for the most common case in this product: a column that opens up
52
+ * to show its distribution. That is not a group, it is the detail of ONE row,
53
+ * and every view was writing it by hand with its own `colSpan` and its own
54
+ * chevron.
55
+ *
56
+ * The trigger is the first cell and not a column of chevrons: a whole column
57
+ * for a 12 px control is width that a twelve-column table needs.
58
+ */
59
+ export declare function TrDetail({ children, detail, open, onToggle, columns, label, selected, }: {
60
+ children: ReactNode;
61
+ detail: ReactNode;
62
+ open: boolean;
63
+ onToggle: () => void;
64
+ /** How many columns the table has, so the detail spans all of them. */
65
+ columns: number;
66
+ /** What is being opened, for the screen reader: «distribución de importe». */
67
+ label: string;
68
+ selected?: boolean;
69
+ }): import("react").JSX.Element;
70
+ /** The background of a cell, by tone. It replaces the row wash on that cell:
71
+ * a hovered row and a painted cell are two signals, and stacking them reads
72
+ * as a third color. */
73
+ export declare const CELL_TONE: Record<Tone, string>;
74
+ export declare function Td({ children, numeric, align,
75
+ /** A CODE — an id, a hash, a key — set in the monospaced face. Off by default: a
76
+ * number does not need it (a numeric cell gets tabular figures on its own), and a
77
+ * name in mono reads like a log, not like a report. */
78
+ mono,
79
+ /** Paints the whole cell. For what the eye has to find without reading: an
80
+ * imputed value, one out of range, a row that changed.
81
+ *
82
+ * Context: it is the CELL and not the text. Tinting the text says «this
83
+ * number is of another kind»; tinting the cell says «look here», which is
84
+ * something else — and in a table of forty rows by six columns, colored text
85
+ * gets lost. */
86
+ tone, title, }: {
87
+ children: ReactNode;
88
+ numeric?: boolean;
89
+ /** Pushes without saying it is a number. See the note on `Th`. */
90
+ align?: "left" | "right";
91
+ mono?: boolean;
92
+ tone?: Tone;
93
+ /** Why it is painted. A colored cell with no explanation is a question. */
94
+ title?: string;
95
+ }): import("react").JSX.Element;
96
+ /** The selection checkbox, with its third state.
97
+ *
98
+ * «some» is not an ornament: without it, the header checkbox of a table with
99
+ * three of forty rows selected lies in both directions — empty says «none»
100
+ * and filled says «all». */
101
+ export declare function SelectBox({ checked, indeterminate, onChange, label, }: {
102
+ checked: boolean;
103
+ indeterminate?: boolean;
104
+ onChange?: (next: boolean) => void;
105
+ label: string;
106
+ }): import("react").JSX.Element;