@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,69 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { PAGE_SIZES } from "./pages.js";
4
+ import { fillCopy, useUiCopy } from "../../foundations/copy.js";
5
+ import { byDensity, useDensity } from "../../foundations/density.js";
6
+ import { bySurface, useSurface } from "../../foundations/surface.js";
7
+ import { ChevronLeft, ChevronRight } from "lucide-react";
8
+ import { useId, useState } from "react";
9
+ /* ---------------------------------------------------------------- *
10
+ * THE FOOTER AND ITS PAGINATION
11
+ * ---------------------------------------------------------------- */
12
+ /** How many of how many, and how I move to the next page.
13
+ *
14
+ * **It lives ONCE and it is mounted in two places.** The normal destination is
15
+ * the search bar: you search, you narrow down and you paginate on the same
16
+ * line, without looking down to the footer to discover there are another
17
+ * hundred and sixty. The footer (`ListingFooter`) existed only for the lists
18
+ * that do NOT have a search box — there is no narrowing line there to put it on.
19
+ *
20
+ * Two implementations of one pagination diverge at the first fix, so this is
21
+ * the only one and both places receive it.
22
+ *
23
+ * ── The range, not the page number ──────────────────────────────────
24
+ *
25
+ * «1–25 de 187» answers three questions —where I am, how much I see, how much
26
+ * there is— and «page 1 of 8» answers a single one, in units that depend on a
27
+ * page size the user did not choose.
28
+ *
29
+ * ── Why the page size is visible ────────────────────────────────────
30
+ *
31
+ * Because it is the difference between scrolling eight times and scrolling
32
+ * once. In a tool where somebody compares a hundred and eighty invoices,
33
+ * leaving it fixed at 25 forces paginating over something that is not a
34
+ * product decision. */
35
+ export function Pagination({ from, to, total, pageSize, pageSizes = PAGE_SIZES, onPageSizeChange, onPrev, onNext, surface, }) {
36
+ const d = useDensity();
37
+ const copy = useUiCopy();
38
+ const activeSurface = useSurface(surface);
39
+ const [editing, setEditing] = useState(false);
40
+ const [draft, setDraft] = useState("");
41
+ const listId = useId();
42
+ const faint = bySurface(activeSurface, "text-muted", "text-chrome-muted");
43
+ const strong = bySurface(activeSurface, "text-ink", "text-chrome-ink");
44
+ const arrow = `inline-flex size-(--size-control-sm) items-center justify-center rounded-pill transition-colors duration-(--duration-instant) disabled:opacity-30 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary ${bySurface(activeSurface, "text-muted not-disabled:hover:bg-ink/6 not-disabled:hover:text-ink", "text-chrome-muted not-disabled:hover:bg-chrome-ink/12 not-disabled:hover:text-chrome-ink")}`;
45
+ const canEdit = pageSize !== undefined && onPageSizeChange !== undefined && d === "comfortable";
46
+ const sizes = pageSize !== undefined && !pageSizes.includes(pageSize)
47
+ ? [...pageSizes, pageSize]
48
+ : pageSizes;
49
+ function commitSize(raw) {
50
+ const next = Number(raw);
51
+ if (!Number.isFinite(next) || next < 1) {
52
+ setEditing(false);
53
+ return;
54
+ }
55
+ onPageSizeChange?.(Math.floor(next));
56
+ setEditing(false);
57
+ }
58
+ return (_jsxs("div", { className: `flex shrink-0 items-center ${byDensity(d, "gap-1", "gap-0.5")}`, children: [editing && canEdit ? (_jsxs(_Fragment, { children: [_jsx("input", { autoFocus: true, type: "number", min: 1, step: 1, list: listId, value: draft, onChange: (e) => setDraft(e.target.value), onBlur: () => commitSize(draft), onKeyDown: (e) => {
59
+ if (e.key === "Enter") {
60
+ e.preventDefault();
61
+ commitSize(draft);
62
+ }
63
+ if (e.key === "Escape")
64
+ setEditing(false);
65
+ }, "aria-label": copy.pager.size, className: `h-(--size-control-sm) w-24 rounded-control border px-1.5 text-micro tabular-nums focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary ${bySurface(activeSurface, "border-line-strong bg-surface text-ink", "border-chrome-ink/45 bg-chrome-ink/8 text-chrome-ink")}` }), _jsx("datalist", { id: listId, children: sizes.map((n) => (_jsx("option", { value: n }, n))) })] })) : (_jsxs("span", { className: `text-micro tabular-nums whitespace-nowrap ${faint}`, children: [canEdit ? (_jsxs("button", { type: "button", onClick: () => {
66
+ setDraft(String(pageSize));
67
+ setEditing(true);
68
+ }, title: fillCopy(copy.pager.sizeHint, { size: pageSize ?? 0 }), className: `rounded-control underline decoration-dotted underline-offset-4 transition-colors duration-(--duration-instant) ${strong} ${bySurface(activeSurface, "decoration-line-strong hover:decoration-primary", "decoration-chrome-ink/40 hover:decoration-chrome-link")} focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary`, children: [from.toLocaleString(copy.locale), "\u2013", to.toLocaleString(copy.locale)] })) : (_jsxs("span", { className: strong, children: [from.toLocaleString(copy.locale), "\u2013", to.toLocaleString(copy.locale)] })), _jsx("span", { className: "mx-1", children: "/" }), total.toLocaleString(copy.locale)] })), _jsxs("span", { className: "flex items-center", children: [_jsx("button", { type: "button", onClick: onPrev, disabled: from <= 1, "aria-label": copy.pager.prev, title: copy.pager.prev, className: arrow, children: _jsx(ChevronLeft, { className: "size-(--size-icon-md)", strokeWidth: 2 }) }), _jsx("button", { type: "button", onClick: onNext, disabled: to >= total, "aria-label": copy.pager.next, title: copy.pager.next, className: arrow, children: _jsx(ChevronRight, { className: "size-(--size-icon-md)", strokeWidth: 2 }) })] })] }));
69
+ }
@@ -0,0 +1,33 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+ /**
4
+ * One record as a card — the board presentation of a list.
5
+ *
6
+ * Context: the board drew this card inline, and a list that wanted its own card wrote its
7
+ * own `div` — which looked the same and could not be opened: the default card was a button
8
+ * with `onOpen`, the custom one was not. A card is the record's door in the board the way a
9
+ * row is in the table, so it is a component and it is always pressable when there is
10
+ * somewhere to go.
11
+ *
12
+ * Its hierarchy is the row's: the state first (what it is), the name as the entry point,
13
+ * the facts below in the quiet size.
14
+ */
15
+ export declare function RecordCard({ title, tags, meta, figure, media, onOpen, }: {
16
+ /** The record's name — the entry point of the card. */
17
+ title: ReactNode;
18
+ /** The state, as a `Tag`: read before the name, the way a board is scanned. */
19
+ tags?: ReactNode;
20
+ /** The facts that tell two cards apart — a number, a date. */
21
+ meta?: ReactNode;
22
+ /** The record's key figure, on the right of the name — an amount. */
23
+ figure?: ReactNode;
24
+ /** The record's picture — a product photo, a customer's logo — or its icon. A board of
25
+ * products is recognised by the pictures before a single name is read. */
26
+ media?: {
27
+ src?: string;
28
+ alt?: string;
29
+ icon?: LucideIcon;
30
+ };
31
+ /** Opens the record. Without it the card is not pressable. */
32
+ onOpen?: () => void;
33
+ }): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Thumb } from "../../primitives/cell/CellStack.js";
4
+ /**
5
+ * One record as a card — the board presentation of a list.
6
+ *
7
+ * Context: the board drew this card inline, and a list that wanted its own card wrote its
8
+ * own `div` — which looked the same and could not be opened: the default card was a button
9
+ * with `onOpen`, the custom one was not. A card is the record's door in the board the way a
10
+ * row is in the table, so it is a component and it is always pressable when there is
11
+ * somewhere to go.
12
+ *
13
+ * Its hierarchy is the row's: the state first (what it is), the name as the entry point,
14
+ * the facts below in the quiet size.
15
+ */
16
+ export function RecordCard({ title, tags, meta, figure, media, onOpen, }) {
17
+ const body = (_jsxs(_Fragment, { children: [media === undefined ? null : (_jsx(Thumb, { size: "lg", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), tags, _jsxs("div", { className: "flex min-w-0 items-baseline justify-between gap-3", children: [_jsx("p", { className: "min-w-0 truncate text-lead font-medium text-ink", children: title }), figure === undefined ? null : (_jsx("p", { className: "shrink-0 text-lead font-semibold tabular-nums text-ink", children: figure }))] }), meta === undefined ? null : (_jsx("p", { className: "text-micro tabular-nums text-muted", children: meta }))] }));
18
+ const box = "flex min-w-0 flex-col gap-2 rounded-box border border-line bg-surface p-4 text-left";
19
+ if (onOpen === undefined)
20
+ return _jsx("div", { className: box, children: body });
21
+ return (_jsx("button", { type: "button", onClick: onOpen, className: `${box} transition-colors duration-(--duration-instant) hover:border-line-strong focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary`, children: body }));
22
+ }
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function RecordLink({ children, onClick, title, }: {
3
+ children: ReactNode;
4
+ onClick?: () => void;
5
+ /** The whole name, when the cell shows only what fits. */
6
+ title?: string;
7
+ }): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ /* ==================================================================== *
4
+ * THE NAME THAT OPENS THE RECORD.
5
+ *
6
+ * Every list was drawing this button by hand, and each one picked a
7
+ * different cutoff: 28ch, 32ch, 42ch. The name is a link — blue, because
8
+ * it goes somewhere — and it truncates at the cell, not at a number
9
+ * somebody guessed. The click does not also open the row: the name and
10
+ * the row are two gestures, and both have to arrive.
11
+ * ==================================================================== */
12
+ export function RecordLink({ children, onClick, title, }) {
13
+ /* With nowhere to go it is the record's name, not a button: a control that looks pressable
14
+ and does nothing is a dead end. */
15
+ if (onClick === undefined) {
16
+ return (_jsx("span", { ...(title === undefined ? {} : { title }), className: "block min-w-0 max-w-full truncate text-meta font-medium text-ink", children: children }));
17
+ }
18
+ return (_jsx("button", { type: "button", ...(title === undefined ? {} : { title }), onClick: (event) => {
19
+ event.stopPropagation();
20
+ onClick?.();
21
+ }, className: "block min-w-0 max-w-full truncate rounded-control text-left text-meta font-medium text-primary transition-colors duration-(--duration-instant) hover:underline hover:underline-offset-4 focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary", children: children }));
22
+ }
@@ -0,0 +1,110 @@
1
+ import { type MenuGroup } from "../../primitives/menu/Menu.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ import type { ReactNode } from "react";
4
+ /** A record in two lines: big name on top, metadata below.
5
+ *
6
+ * ── The name opens, the body peeks ──────────────────────────────────
7
+ *
8
+ * Two different targets and zero waiting. The name is a LINK and opens with
9
+ * one click; the rest of the row does a preview. It used to open with a double
10
+ * click, and to be able to tell it apart from the single one ALL clicks had to
11
+ * be delayed by 220 ms — a name painted as a link discovers itself, «try
12
+ * double clicking» is written nowhere.
13
+ *
14
+ * The gesture is invisible if nothing says so, so on hover `⇱ abrir` appears:
15
+ * it is learned in one hover and never has to be explained.
16
+ *
17
+ * ── The metadata is in plain sight ──────────────────────────────────
18
+ *
19
+ * «some metadata that might help is not visible» — and it was literal: to know
20
+ * whether `FAC/2026/0118` was paid or overdue you had to open it. */
21
+ export declare function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, copy: copyAs, }: {
22
+ icon?: LucideIcon;
23
+ name: string;
24
+ /** The copy button among the row's actions: on by default, copying the row's name — the
25
+ * number of an invoice, a customer — without opening it. A string copies that instead;
26
+ * `false` removes it. */
27
+ copy?: string | false;
28
+ onOpen?: () => void;
29
+ onPeek?: () => void;
30
+ tags?: ReactNode;
31
+ /** The secondary data, already formatted. In mono: it aligns across rows. */
32
+ meta?: string;
33
+ selected?: boolean;
34
+ onSelectedChange?: (next: boolean) => void;
35
+ actions?: ReactNode;
36
+ /** The `⋯` menu. **Without this the button is not drawn**: there used to be a
37
+ * fixed one that opened nothing — a control that exists and does nothing
38
+ * teaches people not to press it, and after that nobody presses the one that
39
+ * does work. */
40
+ more?: readonly MenuGroup[];
41
+ onMoreSelect?: (id: string) => void;
42
+ /** What happens on open, when «as a panel» is not true: «open the order that
43
+ * produced it». It is the `title` and the accessible name. */
44
+ openHint?: string;
45
+ /** What the resource PRODUCED, hanging below. Context: the indent and the
46
+ * rule are put there by the system and not by the view, because the
47
+ * relationship «this came out of that» has to look the same in the four
48
+ * lists that need it. */
49
+ children?: ReactNode;
50
+ }): import("react").JSX.Element;
51
+ /**
52
+ * EL ENCABEZADO DE LA PRESENTACIÓN DE FILAS.
53
+ *
54
+ * Context: la tabla y la planilla dicen qué es cada columna; la lista de filas
55
+ * no decía nada. Ochenta renglones con «Ferretería Sur · 13 sept · Bs 12.480» a la
56
+ * derecha y una ficha al final, y había que deducir qué era cada número — o
57
+ * cambiar a la tabla para averiguarlo, que es cambiar de presentación para leer
58
+ * la misma pantalla.
59
+ *
60
+ * **No son columnas: son las ZONAS de la fila, rotuladas.** Una fila tiene un
61
+ * nombre que se estira, un `meta` que es UNA cadena ya formateada, y una
62
+ * columna de fichas de ancho fijo. Rotularlas como si fueran tres columnas
63
+ * alineadas sería mentir sobre lo que hay debajo: el `meta` de una fila puede
64
+ * decir tres cosas y el de otra dos.
65
+ *
66
+ * Por eso calca la caja de `Row` —mismo padding, mismos huecos, mismos
67
+ * espaciadores de casilla, ícono y acciones— en vez de declarar un ancho
68
+ * propio. Si `Row` cambia su geometría, esto la sigue.
69
+ */
70
+ export declare function RowHeader({ name, meta, tags, withSelection, withIcon, withActions, }: {
71
+ name: string;
72
+ meta?: string;
73
+ tags?: string;
74
+ /** Los espaciadores de la izquierda. Los pone quien dibuja las filas, porque
75
+ * es el único que sabe si esta lista elige y si sus filas tienen ícono. */
76
+ withSelection?: boolean;
77
+ withIcon?: boolean;
78
+ /** La zona de acciones de la derecha ocupa su lugar aunque esté invisible
79
+ * —así nada se corre al pasar el puntero—, así que el rótulo de las fichas
80
+ * queda desalineado si acá no se reserva lo mismo. */
81
+ withActions?: boolean;
82
+ }): import("react").JSX.Element;
83
+ /**
84
+ * The header of a group inside a list.
85
+ *
86
+ * Context: grouping existed ONLY inside `DataTable`, so changing presentation
87
+ * —from table to rows— lost the grouping. Grouping belongs to the SET and not
88
+ * to the presentation; that is why it lives here.
89
+ *
90
+ * **`field` is drawn, and it is not decoration.** With one level nobody needed
91
+ * it: there was a single axis and everyone knew what it was. With several,
92
+ * «2026-09» does not say whether it is when the thing was registered or when it
93
+ * ran, and two consecutive headers can read as siblings when one is inside the
94
+ * other. The field names the cut; the indent says how deep it is.
95
+ */
96
+ export declare function GroupHeader({ field, label, count, detail, depth, open, onToggle, }: {
97
+ /** By which field this pile was cut. Without it a value is ambiguous as soon
98
+ * as there is more than one level. */
99
+ field?: string;
100
+ label: string;
101
+ count?: number;
102
+ /** What the pile ADDS UP TO, already written: «412 facturas · Bs 1.204.933». A
103
+ * folded pile that only says how many there are hides the answer instead of
104
+ * summarising it. */
105
+ detail?: string;
106
+ /** How deep the pile is. `0` is the first level. */
107
+ depth?: number;
108
+ open?: boolean;
109
+ onToggle?: () => void;
110
+ }): import("react").JSX.Element;
@@ -0,0 +1,123 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { RecordLink } from "./RecordLink.js";
4
+ import { fillCopy, useUiCopy } from "../../foundations/copy.js";
5
+ import { byDensity, useDensity } from "../../foundations/density.js";
6
+ import { stickyTop } from "../../foundations/sticky.js";
7
+ import { Menu } from "../../primitives/menu/Menu.js";
8
+ import { CopyButton } from "../../primitives/value/Copyable.js";
9
+ import { ChevronRight, MoreHorizontal, SquareArrowOutUpRight } from "lucide-react";
10
+ /* ---------------------------------------------------------------- *
11
+ * THE RICH ROW — the other density
12
+ * ---------------------------------------------------------------- */
13
+ /** A record in two lines: big name on top, metadata below.
14
+ *
15
+ * ── The name opens, the body peeks ──────────────────────────────────
16
+ *
17
+ * Two different targets and zero waiting. The name is a LINK and opens with
18
+ * one click; the rest of the row does a preview. It used to open with a double
19
+ * click, and to be able to tell it apart from the single one ALL clicks had to
20
+ * be delayed by 220 ms — a name painted as a link discovers itself, «try
21
+ * double clicking» is written nowhere.
22
+ *
23
+ * The gesture is invisible if nothing says so, so on hover `⇱ abrir` appears:
24
+ * it is learned in one hover and never has to be explained.
25
+ *
26
+ * ── The metadata is in plain sight ──────────────────────────────────
27
+ *
28
+ * «some metadata that might help is not visible» — and it was literal: to know
29
+ * whether `FAC/2026/0118` was paid or overdue you had to open it. */
30
+ export function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, copy: copyAs, }) {
31
+ const d = useDensity();
32
+ const copy = useUiCopy();
33
+ const openAs = openHint ?? copy.shell.openAsPanel;
34
+ const row = (
35
+ /* A `div` and not an `li`: whoever composes decides whether this is a list,
36
+ a table or a branch of a tree. And the zone that peeks is a `div` with
37
+ `onClick` and not a `button` — the actions are buttons too, and a button
38
+ inside another one is invalid HTML: the browser takes it apart and the
39
+ clicks get lost. */
40
+ _jsx("div", { className: "border-b border-line px-1 py-0.5 last:border-b-0", children: _jsxs("div", { "data-slot": "row",
41
+ /* The hairline stays on the shell, full width: eighty similar names with
42
+ no rule between them read as a paragraph. The wash is the inner row,
43
+ inset and rounded, so the hover is a mark and not a stripe. The inner
44
+ padding is shorter by exactly that inset, so the text does not move. */
45
+ className: `group/row flex min-w-0 items-center rounded-control transition-colors duration-(--duration-instant) hover:bg-ink/4 ${byDensity(d, "gap-3 px-2 py-2", "gap-2 px-1.5 py-1")}`,
46
+ /* `onPeek` con `onOpen` de reserva: quien sólo declara `onOpen` —lo
47
+ natural si nunca oyó hablar de la vista previa— tiene que poder abrir
48
+ igual haciendo click en cualquier parte de la fila, no sólo en el
49
+ nombre. Con la vista previa apagada (`PeekLayer.tsx`) esto es hoy el
50
+ caso de todos: un click en la fila abre, sin más. */
51
+ onClick: onPeek ?? onOpen, role: "presentation", children: [onSelectedChange === undefined ? null : (_jsx("input", { type: "checkbox", checked: selected ?? false, "aria-label": fillCopy(copy.selection.named, { name }), onChange: (e) => onSelectedChange(e.target.checked), onClick: (e) => e.stopPropagation(), className: "size-3.5 shrink-0 cursor-pointer accent-primary" })), Icon === undefined ? null : (_jsx(Icon, { className: "size-(--size-icon-md) shrink-0 text-muted", strokeWidth: 1.75, "aria-hidden": true })), _jsxs("div", { className: `flex min-w-0 shrink grow basis-(--container-name) flex-wrap items-center gap-y-0 ${byDensity(d, "gap-x-3", "gap-x-2")}`, children: [_jsx("span", { className: "min-w-0 grow basis-(--container-name)", children: _jsx(RecordLink, { ...((onOpen ?? onPeek) === undefined ? {} : { onClick: (onOpen ?? onPeek) }), title: name, children: name }) }), meta === undefined ? null : (
52
+ /* Wrapped or not, the metadata is still there —it is what saves
53
+ having to open a set to know whether it is a thousand rows or a
54
+ million— and it truncates instead of wrapping into itself: three
55
+ lines per row in a 480 px drawer means three rows fit on screen. */
56
+ _jsx("span", { className: "min-w-0 truncate text-micro tabular-nums text-muted", children: meta }))] }), tags === undefined ? null : (_jsx("span", { className: "flex min-w-fit shrink basis-(--container-tags) items-center justify-end gap-1.5", children: tags })), _jsxs("span", { className: "flex shrink-0 items-center gap-1 opacity-0 transition-opacity duration-(--duration-instant) group-hover/row:opacity-100 focus-within:opacity-100 pointer-coarse:opacity-100", children: [actions, copyAs === false ? null : (_jsx(CopyButton, { value: copyAs ?? name, label: fillCopy(copy.copyable.field, { label: name }) })), onOpen === undefined ? null : (_jsx("button", { type: "button", onClick: (e) => {
57
+ e.stopPropagation();
58
+ onOpen();
59
+ }, title: openAs, "aria-label": `${openAs}: ${name}`, className: "inline-flex size-(--size-control-sm) items-center justify-center rounded-pill text-muted transition-colors duration-(--duration-instant) hover:bg-ink/6 hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: _jsx(SquareArrowOutUpRight, { className: "size-(--size-icon-sm)", strokeWidth: 2 }) })), more === undefined || more.length === 0 ? null : (_jsx("span", { onClick: (e) => e.stopPropagation(), role: "presentation", children: _jsx(Menu, { label: fillCopy(copy.selection.moreOf, { name }), groups: more, icon: MoreHorizontal, iconOnly: true, chevron: false, align: "end", width: "sm", variant: "text", ...(onMoreSelect === undefined ? {} : { onSelect: onMoreSelect }) }) }))] })] }) }));
60
+ if (children === undefined)
61
+ return row;
62
+ /* What the resource produced, hanging below. The indent reaches up to where
63
+ the name starts —not up to the edge— so the rule falls exactly below the
64
+ icon and the relationship reads without reading: this came out of that.
65
+
66
+ The child is NOT another `Row`: an invoice line does not open, does not get
67
+ selected and has no actions of its own. Treating it as a row would make it promise
68
+ all of that. */
69
+ return (_jsxs("div", { "data-slot": "branch", className: "flex min-w-0 flex-col", children: [row, _jsx("div", { className: `flex min-w-0 flex-col gap-1 border-l border-line-strong ${byDensity(d, "mb-2 ml-8 pb-0.5 pl-3 pr-3", "mb-1.5 ml-6 pl-2.5 pr-2.5")}`, children: children })] }));
70
+ }
71
+ /**
72
+ * EL ENCABEZADO DE LA PRESENTACIÓN DE FILAS.
73
+ *
74
+ * Context: la tabla y la planilla dicen qué es cada columna; la lista de filas
75
+ * no decía nada. Ochenta renglones con «Ferretería Sur · 13 sept · Bs 12.480» a la
76
+ * derecha y una ficha al final, y había que deducir qué era cada número — o
77
+ * cambiar a la tabla para averiguarlo, que es cambiar de presentación para leer
78
+ * la misma pantalla.
79
+ *
80
+ * **No son columnas: son las ZONAS de la fila, rotuladas.** Una fila tiene un
81
+ * nombre que se estira, un `meta` que es UNA cadena ya formateada, y una
82
+ * columna de fichas de ancho fijo. Rotularlas como si fueran tres columnas
83
+ * alineadas sería mentir sobre lo que hay debajo: el `meta` de una fila puede
84
+ * decir tres cosas y el de otra dos.
85
+ *
86
+ * Por eso calca la caja de `Row` —mismo padding, mismos huecos, mismos
87
+ * espaciadores de casilla, ícono y acciones— en vez de declarar un ancho
88
+ * propio. Si `Row` cambia su geometría, esto la sigue.
89
+ */
90
+ export function RowHeader({ name, meta, tags, withSelection = false, withIcon = false, withActions = false, }) {
91
+ const d = useDensity();
92
+ const rotulo = "truncate text-micro font-medium text-muted";
93
+ return (_jsxs("div", { "data-slot": "row-header",
94
+ /* Se pega debajo de la barra de la lista, como el encabezado de la tabla
95
+ y el de la planilla. Ver `system/sticky.tsx`. */
96
+ className: `sticky z-10 flex min-w-0 items-center border-b border-line-strong bg-ground ${byDensity(d, "gap-3 px-3 py-2", "gap-2 px-2.5 py-1.5")}`, style: stickyTop, role: "presentation", children: [!withSelection ? null : _jsx("span", { className: "size-3.5 shrink-0", "aria-hidden": true }), !withIcon ? null : _jsx("span", { className: "size-(--size-icon-md) shrink-0", "aria-hidden": true }), _jsxs("div", { className: `flex min-w-0 shrink grow basis-(--container-name) flex-wrap items-center gap-y-0 ${byDensity(d, "gap-x-3", "gap-x-2")}`, children: [_jsx("span", { className: `min-w-0 grow basis-(--container-name) ${rotulo}`, children: name }), meta === undefined ? null : _jsx("span", { className: `min-w-0 ${rotulo}`, children: meta })] }), tags === undefined ? null : (_jsx("span", { className: "flex min-w-fit shrink basis-(--container-tags) items-center justify-end", children: _jsx("span", { className: rotulo, children: tags }) })), !withActions ? null : (_jsx("span", { className: "size-(--size-control-sm) shrink-0", "aria-hidden": true }))] }));
97
+ }
98
+ /**
99
+ * The header of a group inside a list.
100
+ *
101
+ * Context: grouping existed ONLY inside `DataTable`, so changing presentation
102
+ * —from table to rows— lost the grouping. Grouping belongs to the SET and not
103
+ * to the presentation; that is why it lives here.
104
+ *
105
+ * **`field` is drawn, and it is not decoration.** With one level nobody needed
106
+ * it: there was a single axis and everyone knew what it was. With several,
107
+ * «2026-09» does not say whether it is when the thing was registered or when it
108
+ * ran, and two consecutive headers can read as siblings when one is inside the
109
+ * other. The field names the cut; the indent says how deep it is.
110
+ */
111
+ export function GroupHeader({ field, label, count, detail, depth = 0, open = true, onToggle, }) {
112
+ const d = useDensity();
113
+ const copy = useUiCopy();
114
+ const content = (_jsxs(_Fragment, { children: [onToggle === undefined ? null : (_jsx(ChevronRight, { className: `size-(--size-icon-sm) shrink-0 text-muted transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2, "aria-hidden": true })), field === undefined ? null : (_jsx("span", { className: "shrink-0 text-micro text-muted/70", children: field })), _jsx("span", { className: "truncate text-micro font-medium text-ink", children: label }), count === undefined ? null : (_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: count.toLocaleString(copy.locale) })), detail === undefined ? null : (_jsx("span", { className: "shrink-0 text-micro tabular-nums text-muted", children: detail }))] }));
115
+ /* La sangría va en el relleno de la izquierda y no en un margen: el
116
+ encabezado tiene fondo y borde inferior, y un margen los cortaría antes del
117
+ borde de la lista — la banda dejaría de leerse como una franja. El hover
118
+ no reemplaza ese fondo: es una marca metida adentro, redondeada. */
119
+ const box = `flex min-w-0 items-center gap-2 border-b border-line bg-ground ${byDensity(d, "px-3 py-1.5", "px-2.5 py-1")}`;
120
+ const indent = depth === 0 ? undefined : { paddingLeft: `calc(var(--spacing-indent) * ${depth})` };
121
+ const mark = (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-x-1 inset-y-0.5 rounded-control group-hover/head:bg-ink/4" }));
122
+ return onToggle === undefined ? (_jsx("div", { className: box, style: indent, children: content })) : (_jsxs("button", { type: "button", onClick: onToggle, "aria-expanded": open, style: indent, className: `group/head relative ${box} w-full text-left focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary`, children: [mark, content] }));
123
+ }
@@ -0,0 +1,62 @@
1
+ import type { Surface } from "../../foundations/types.js";
2
+ import type { SortState } from "../../primitives/table/Table.js";
3
+ /**
4
+ * ORDENAR, cuando no hay encabezados donde hacer clic.
5
+ *
6
+ * Context: ordenar era una capacidad de `DataTable` y de nadie más. Se hacía
7
+ * clic en el encabezado de la columna, que es la forma correcta **cuando hay
8
+ * encabezados**. En filas, en tablero y en la planilla no los hay, así que
9
+ * cambiar de presentación no perdía sólo el orden puesto: perdía la posibilidad
10
+ * de ordenar. Es exactamente el mismo drift que tenía la agrupación.
11
+ *
12
+ * El orden es del CONJUNTO y no de la presentación, igual que los niveles y que
13
+ * lo plegado. Por eso el control vive en la barra y no adentro de la tabla, y
14
+ * por eso la tabla sigue teniendo además sus encabezados: son dos puertas a lo
15
+ * mismo, y la de los encabezados es más rápida cuando existe.
16
+ *
17
+ * ── Por qué es un `Menu` y el agrupador no ──────────────────────────
18
+ *
19
+ * Porque ordenar SÍ es elegir una opción: un campo y una dirección. No hay orden
20
+ * ni jerarquía que expresar —ordenar por dos campos a la vez es un desempate,
21
+ * que es otra pregunta y la hace el motor— así que una lista de tildes alcanza y
22
+ * no hace falta un editor.
23
+ */
24
+ export type SortField<T = unknown> = {
25
+ field: string;
26
+ label: string;
27
+ /**
28
+ * Por qué valor se ordena, cuando ordena el NAVEGADOR.
29
+ *
30
+ * Ausente = ya viene ordenado de donde salió. Es la misma división que tiene
31
+ * el agrupado con `piles`: un conjunto que el servidor pagina no se puede
32
+ * ordenar acá —ordenar doce filas de ciento ochenta y siete da un orden
33
+ * falso— así que ahí el orden viaja en la lectura y esto sólo lo refleja.
34
+ */
35
+ value?: (item: T) => string | number | null | undefined;
36
+ /** Qué quiere decir «ascendente» para este campo. Sin esto, «A → Z».
37
+ * Una fecha asciende de vieja a nueva y un importe de chico a grande, y
38
+ * decirlo evita el clic de prueba. */
39
+ ascLabel?: string;
40
+ descLabel?: string;
41
+ };
42
+ export type SortMenuProps = {
43
+ /** Por qué campos se puede ordenar. Sin ninguno, el control no se dibuja. */
44
+ fields: readonly SortField<never>[];
45
+ sort: SortState | null;
46
+ onSortChange: (next: SortState | null) => void;
47
+ surface?: Surface;
48
+ };
49
+ /**
50
+ * Las filas, ordenadas acá.
51
+ *
52
+ * Sólo cuando el campo activo declara `value`; si no, se devuelven tal cual —
53
+ * ordenar una página de un conjunto que se pagina afuera produce un orden que es
54
+ * mentira, porque ordena las doce que llegaron y no las ciento ochenta y siete.
55
+ *
56
+ * Los faltantes van SIEMPRE al final, suba o baje el orden. Es lo que hace
57
+ * cualquier planilla, y la razón es que un vacío no es «el más chico»: es la
58
+ * ausencia de respuesta, y ponerlo primero al invertir el orden llena la primera
59
+ * pantalla con las filas que menos dicen.
60
+ */
61
+ export declare function sortItems<T>(items: readonly T[], fields: readonly SortField<T>[], sort: SortState | null | undefined): readonly T[];
62
+ export declare function SortMenu({ fields, sort, onSortChange, surface }: SortMenuProps): import("react").JSX.Element | null;
@@ -0,0 +1,105 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { Menu } from "../../primitives/menu/Menu.js";
5
+ import { ArrowDownUp, ArrowDownWideNarrow, ArrowUpNarrowWide } from "lucide-react";
6
+ /**
7
+ * Las filas, ordenadas acá.
8
+ *
9
+ * Sólo cuando el campo activo declara `value`; si no, se devuelven tal cual —
10
+ * ordenar una página de un conjunto que se pagina afuera produce un orden que es
11
+ * mentira, porque ordena las doce que llegaron y no las ciento ochenta y siete.
12
+ *
13
+ * Los faltantes van SIEMPRE al final, suba o baje el orden. Es lo que hace
14
+ * cualquier planilla, y la razón es que un vacío no es «el más chico»: es la
15
+ * ausencia de respuesta, y ponerlo primero al invertir el orden llena la primera
16
+ * pantalla con las filas que menos dicen.
17
+ */
18
+ export function sortItems(items, fields, sort) {
19
+ if (sort === null || sort === undefined)
20
+ return items;
21
+ const field = fields.find((one) => one.field === sort.key);
22
+ if (field?.value === undefined)
23
+ return items;
24
+ const read = field.value;
25
+ const sign = sort.direction === "asc" ? 1 : -1;
26
+ return [...items].sort((a, b) => {
27
+ const left = read(a);
28
+ const right = read(b);
29
+ const leftMissing = left === null || left === undefined || left === "";
30
+ const rightMissing = right === null || right === undefined || right === "";
31
+ if (leftMissing && rightMissing)
32
+ return 0;
33
+ if (leftMissing)
34
+ return 1;
35
+ if (rightMissing)
36
+ return -1;
37
+ if (typeof left === "number" && typeof right === "number")
38
+ return (left - right) * sign;
39
+ return String(left).localeCompare(String(right)) * sign;
40
+ });
41
+ }
42
+ export function SortMenu({ fields, sort, onSortChange, surface }) {
43
+ const copy = useUiCopy();
44
+ if (fields.length === 0)
45
+ return null;
46
+ const current = fields.find((one) => one.field === sort?.key);
47
+ const groups = [
48
+ {
49
+ label: copy.sorting.by,
50
+ options: fields.map((one) => ({
51
+ id: one.field,
52
+ label: one.label,
53
+ checked: one.field === sort?.key,
54
+ })),
55
+ },
56
+ /* La dirección sólo cuando hay algo ordenado: «descendente» sin campo no
57
+ ordena nada, y una opción que no hace nada es una opción que se prueba. */
58
+ ...(current === undefined
59
+ ? []
60
+ : [
61
+ {
62
+ options: [
63
+ {
64
+ id: "__asc__",
65
+ label: current.ascLabel ?? copy.sorting.asc,
66
+ icon: ArrowUpNarrowWide,
67
+ checked: sort?.direction === "asc",
68
+ },
69
+ {
70
+ id: "__desc__",
71
+ label: current.descLabel ?? copy.sorting.desc,
72
+ icon: ArrowDownWideNarrow,
73
+ checked: sort?.direction === "desc",
74
+ },
75
+ ],
76
+ },
77
+ { options: [{ id: "__none__", label: copy.sorting.clear }] },
78
+ ]),
79
+ ];
80
+ return (_jsx(Menu, { label: current === undefined ? copy.sorting.label : current.label, icon: sort === null || sort === undefined
81
+ ? ArrowDownUp
82
+ : sort.direction === "asc"
83
+ ? ArrowUpNarrowWide
84
+ : ArrowDownWideNarrow, groups: groups, active: sort !== null && sort !== undefined, size: "sm", width: "sm", closeOnSelect: false, ...(surface === undefined ? {} : { surface }), onSelect: (id) => {
85
+ if (id === "__none__")
86
+ return onSortChange(null);
87
+ if (id === "__asc__") {
88
+ return onSortChange(sort === null ? null : { ...sort, direction: "asc" });
89
+ }
90
+ if (id === "__desc__") {
91
+ return onSortChange(sort === null ? null : { ...sort, direction: "desc" });
92
+ }
93
+ /* Volver a elegir el campo que ya está puesto DA VUELTA la dirección, que
94
+ es lo que hace el clic en un encabezado. Sin eso, invertir el orden son
95
+ dos gestos en un menú y uno en la tabla — la misma capacidad costando
96
+ distinto según la presentación. */
97
+ if (sort?.key === id) {
98
+ return onSortChange({
99
+ key: id,
100
+ direction: sort.direction === "asc" ? "desc" : "asc",
101
+ });
102
+ }
103
+ onSortChange({ key: id, direction: "asc" });
104
+ } }));
105
+ }
@@ -0,0 +1,24 @@
1
+ import type { Surface } from "../../foundations/types.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ /** How the same list is shown: table, board, timeline.
4
+ *
5
+ * The icons go together and touching —a segmented group and not three loose
6
+ * buttons— because they are MUTUALLY EXCLUSIVE options of a single question.
7
+ * Three separate buttons read as three different actions, and then it is not
8
+ * understood that choosing one turns the other off.
9
+ *
10
+ * The label goes in the `title` and in the `aria-label`, never drawn: they are
11
+ * three silhouettes that are learned once and recognized afterwards without
12
+ * reading, and writing them out would burn the width the search box needs. */
13
+ export interface ViewOption {
14
+ id: string;
15
+ label: string;
16
+ icon: LucideIcon;
17
+ }
18
+ export declare function ViewSwitcher({ options, current, onSelect, surface, }: {
19
+ options: readonly ViewOption[];
20
+ current: string;
21
+ onSelect?: (id: string) => void;
22
+ /** By default it inherits it from the surface that wraps it. */
23
+ surface?: Surface;
24
+ }): import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { bySurface, useSurface } from "../../foundations/surface.js";
5
+ export function ViewSwitcher({ options, current, onSelect, surface, }) {
6
+ const activeSurface = useSurface(surface);
7
+ const copy = useUiCopy();
8
+ const rule = bySurface(activeSurface, "border-line-strong", "border-chrome-ink/45");
9
+ return (_jsx("div", { role: "group", "aria-label": copy.present.group, className: `flex shrink-0 items-center overflow-hidden rounded-control border ${rule}`, children: options.map((o, i) => {
10
+ const here = o.id === current;
11
+ const Icon = o.icon;
12
+ return (_jsx("button", { type: "button", onClick: () => onSelect?.(o.id), "aria-pressed": here, title: o.label, "aria-label": o.label, className: `inline-flex size-(--size-control-sm) items-center justify-center transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:-outline-offset-1 focus-visible:outline-primary ${i === 0 ? "" : `border-l ${rule}`} ${here
13
+ ? bySurface(activeSurface, "bg-ink/8 text-ink", "bg-chrome-link/20 text-chrome-link")
14
+ : bySurface(activeSurface, "bg-surface text-muted hover:bg-ink/5 hover:text-ink", "bg-chrome-ink/6 text-chrome-muted hover:bg-chrome-ink/14 hover:text-chrome-ink")}`, children: _jsx(Icon, { className: "size-(--size-icon-md)", strokeWidth: 1.75 }) }, o.id));
15
+ }) }));
16
+ }