@sincpro/web-ui 0.0.0-stage → 0.1.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 (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -2
  3. package/dist/components/assist/AssistMark.d.ts +64 -0
  4. package/dist/components/assist/AssistMark.js +108 -0
  5. package/dist/components/assist/AssistPending.d.ts +24 -0
  6. package/dist/components/assist/AssistPending.js +26 -0
  7. package/dist/components/assist/AssistSuggestion.d.ts +42 -0
  8. package/dist/components/assist/AssistSuggestion.js +55 -0
  9. package/dist/components/assist/ChangeReview.d.ts +56 -0
  10. package/dist/components/assist/ChangeReview.js +121 -0
  11. package/dist/components/assist/assist.d.ts +77 -0
  12. package/dist/components/assist/assist.js +87 -0
  13. package/dist/components/assist/index.d.ts +5 -0
  14. package/dist/components/assist/index.js +7 -0
  15. package/dist/components/chatter/Chatter.d.ts +161 -0
  16. package/dist/components/chatter/Chatter.js +232 -0
  17. package/dist/components/chatter/Messages.d.ts +66 -0
  18. package/dist/components/chatter/Messages.js +43 -0
  19. package/dist/components/chatter/Timeline.d.ts +21 -0
  20. package/dist/components/chatter/Timeline.js +30 -0
  21. package/dist/components/chatter/index.d.ts +3 -0
  22. package/dist/components/chatter/index.js +4 -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 +43 -0
  26. package/dist/components/form/Sheet.js +83 -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 +74 -0
  40. package/dist/components/lines/LineItems.js +229 -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 +73 -0
  52. package/dist/components/listing/ListingBar.js +105 -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 +106 -0
  60. package/dist/components/listing/Row.js +122 -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 +71 -0
  96. package/dist/components/record/RecordHeader.js +72 -0
  97. package/dist/components/record/RecordSkeleton.d.ts +3 -0
  98. package/dist/components/record/RecordSkeleton.js +21 -0
  99. package/dist/components/record/SmartButtons.d.ts +35 -0
  100. package/dist/components/record/SmartButtons.js +38 -0
  101. package/dist/components/record/StatusBar.d.ts +51 -0
  102. package/dist/components/record/StatusBar.js +168 -0
  103. package/dist/components/record/ThumbBar.d.ts +36 -0
  104. package/dist/components/record/ThumbBar.js +86 -0
  105. package/dist/components/record/index.d.ts +6 -0
  106. package/dist/components/record/index.js +8 -0
  107. package/dist/components/search/CriteriaBuilder.d.ts +58 -0
  108. package/dist/components/search/CriteriaBuilder.js +278 -0
  109. package/dist/components/search/CriteriaChips.d.ts +22 -0
  110. package/dist/components/search/CriteriaChips.js +64 -0
  111. package/dist/components/search/FilterBuilder.d.ts +142 -0
  112. package/dist/components/search/FilterBuilder.js +421 -0
  113. package/dist/components/search/JoinWord.d.ts +8 -0
  114. package/dist/components/search/JoinWord.js +47 -0
  115. package/dist/components/search/JsonEditor.d.ts +19 -0
  116. package/dist/components/search/JsonEditor.js +78 -0
  117. package/dist/components/search/SearchBar.d.ts +78 -0
  118. package/dist/components/search/SearchBar.js +387 -0
  119. package/dist/components/search/criteria.d.ts +116 -0
  120. package/dist/components/search/criteria.js +159 -0
  121. package/dist/components/search/datePeriods.d.ts +122 -0
  122. package/dist/components/search/datePeriods.js +134 -0
  123. package/dist/components/search/dateRanges.d.ts +54 -0
  124. package/dist/components/search/dateRanges.js +89 -0
  125. package/dist/components/search/index.d.ts +9 -0
  126. package/dist/components/search/index.js +20 -0
  127. package/dist/components/search/model.d.ts +54 -0
  128. package/dist/components/search/model.js +37 -0
  129. package/dist/components/search/query.d.ts +166 -0
  130. package/dist/components/search/query.js +436 -0
  131. package/dist/components/shell/AppFrame.d.ts +14 -0
  132. package/dist/components/shell/AppFrame.js +80 -0
  133. package/dist/components/shell/TopBar.d.ts +73 -0
  134. package/dist/components/shell/TopBar.js +54 -0
  135. package/dist/components/shell/headerSlot.d.ts +17 -0
  136. package/dist/components/shell/headerSlot.js +21 -0
  137. package/dist/components/shell/index.d.ts +2 -0
  138. package/dist/components/shell/index.js +3 -0
  139. package/dist/foundations/aria.d.ts +14 -0
  140. package/dist/foundations/aria.js +18 -0
  141. package/dist/foundations/calendar.d.ts +28 -0
  142. package/dist/foundations/calendar.js +183 -0
  143. package/dist/foundations/copy.d.ts +619 -0
  144. package/dist/foundations/copy.es.d.ts +2 -0
  145. package/dist/foundations/copy.es.js +377 -0
  146. package/dist/foundations/copy.js +472 -0
  147. package/dist/foundations/density.d.ts +16 -0
  148. package/dist/foundations/density.js +21 -0
  149. package/dist/foundations/grouping.d.ts +281 -0
  150. package/dist/foundations/grouping.js +0 -0
  151. package/dist/foundations/index.d.ts +14 -0
  152. package/dist/foundations/index.js +30 -0
  153. package/dist/foundations/layout.d.ts +9 -0
  154. package/dist/foundations/layout.js +9 -0
  155. package/dist/foundations/pivot.d.ts +222 -0
  156. package/dist/foundations/pivot.js +198 -0
  157. package/dist/foundations/pivotSheet.d.ts +61 -0
  158. package/dist/foundations/pivotSheet.js +0 -0
  159. package/dist/foundations/slots.d.ts +67 -0
  160. package/dist/foundations/slots.js +63 -0
  161. package/dist/foundations/sticky.d.ts +53 -0
  162. package/dist/foundations/sticky.js +150 -0
  163. package/dist/foundations/surface.d.ts +9 -0
  164. package/dist/foundations/surface.js +26 -0
  165. package/dist/foundations/theme.d.ts +42 -0
  166. package/dist/foundations/theme.js +88 -0
  167. package/dist/foundations/types.d.ts +23 -0
  168. package/dist/foundations/types.js +50 -0
  169. package/dist/foundations/variants.d.ts +19 -0
  170. package/dist/foundations/variants.js +48 -0
  171. package/dist/foundations/xlsx.d.ts +86 -0
  172. package/dist/foundations/xlsx.js +0 -0
  173. package/dist/index.d.ts +19 -0
  174. package/dist/index.js +19 -0
  175. package/dist/primitives/block/Block.d.ts +66 -0
  176. package/dist/primitives/block/Block.js +34 -0
  177. package/dist/primitives/block/Line.d.ts +18 -0
  178. package/dist/primitives/block/Line.js +45 -0
  179. package/dist/primitives/block/SectionGrid.d.ts +30 -0
  180. package/dist/primitives/block/SectionGrid.js +29 -0
  181. package/dist/primitives/block/Steps.d.ts +76 -0
  182. package/dist/primitives/block/Steps.js +43 -0
  183. package/dist/primitives/block/index.d.ts +4 -0
  184. package/dist/primitives/block/index.js +7 -0
  185. package/dist/primitives/button/Button.d.ts +137 -0
  186. package/dist/primitives/button/Button.js +234 -0
  187. package/dist/primitives/button/index.d.ts +1 -0
  188. package/dist/primitives/button/index.js +2 -0
  189. package/dist/primitives/cell/CellStack.d.ts +47 -0
  190. package/dist/primitives/cell/CellStack.js +33 -0
  191. package/dist/primitives/cell/index.d.ts +1 -0
  192. package/dist/primitives/cell/index.js +3 -0
  193. package/dist/primitives/choice/ComboBox.d.ts +16 -0
  194. package/dist/primitives/choice/ComboBox.js +52 -0
  195. package/dist/primitives/choice/RadioGroup.d.ts +18 -0
  196. package/dist/primitives/choice/RadioGroup.js +50 -0
  197. package/dist/primitives/choice/SearchField.d.ts +24 -0
  198. package/dist/primitives/choice/SearchField.js +27 -0
  199. package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
  200. package/dist/primitives/choice/SegmentedControl.js +69 -0
  201. package/dist/primitives/choice/Slider.d.ts +16 -0
  202. package/dist/primitives/choice/Slider.js +46 -0
  203. package/dist/primitives/choice/Switch.d.ts +14 -0
  204. package/dist/primitives/choice/Switch.js +65 -0
  205. package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
  206. package/dist/primitives/choice/ThemeSwitcher.js +25 -0
  207. package/dist/primitives/choice/index.d.ts +8 -0
  208. package/dist/primitives/choice/index.js +11 -0
  209. package/dist/primitives/choice/options.d.ts +28 -0
  210. package/dist/primitives/choice/options.js +6 -0
  211. package/dist/primitives/date/Calendar.d.ts +31 -0
  212. package/dist/primitives/date/Calendar.js +110 -0
  213. package/dist/primitives/date/DatePicker.d.ts +34 -0
  214. package/dist/primitives/date/DatePicker.js +99 -0
  215. package/dist/primitives/date/index.d.ts +2 -0
  216. package/dist/primitives/date/index.js +4 -0
  217. package/dist/primitives/dialog/Dialog.d.ts +118 -0
  218. package/dist/primitives/dialog/Dialog.js +121 -0
  219. package/dist/primitives/dialog/index.d.ts +1 -0
  220. package/dist/primitives/dialog/index.js +2 -0
  221. package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
  222. package/dist/primitives/disclosure/Disclosure.js +52 -0
  223. package/dist/primitives/disclosure/index.d.ts +1 -0
  224. package/dist/primitives/disclosure/index.js +2 -0
  225. package/dist/primitives/field/Checks.d.ts +57 -0
  226. package/dist/primitives/field/Checks.js +72 -0
  227. package/dist/primitives/field/DropZone.d.ts +42 -0
  228. package/dist/primitives/field/DropZone.js +52 -0
  229. package/dist/primitives/field/Field.d.ts +166 -0
  230. package/dist/primitives/field/Field.js +240 -0
  231. package/dist/primitives/field/Suggest.d.ts +48 -0
  232. package/dist/primitives/field/Suggest.js +43 -0
  233. package/dist/primitives/field/aria.d.ts +61 -0
  234. package/dist/primitives/field/aria.js +70 -0
  235. package/dist/primitives/field/context.d.ts +30 -0
  236. package/dist/primitives/field/context.js +13 -0
  237. package/dist/primitives/field/index.d.ts +7 -0
  238. package/dist/primitives/field/index.js +11 -0
  239. package/dist/primitives/field/layout.d.ts +24 -0
  240. package/dist/primitives/field/layout.js +10 -0
  241. package/dist/primitives/field/mode.d.ts +9 -0
  242. package/dist/primitives/field/mode.js +13 -0
  243. package/dist/primitives/hint/Hint.d.ts +31 -0
  244. package/dist/primitives/hint/Hint.js +42 -0
  245. package/dist/primitives/hint/index.d.ts +1 -0
  246. package/dist/primitives/hint/index.js +1 -0
  247. package/dist/primitives/index.d.ts +19 -0
  248. package/dist/primitives/index.js +20 -0
  249. package/dist/primitives/menu/Menu.d.ts +100 -0
  250. package/dist/primitives/menu/Menu.js +59 -0
  251. package/dist/primitives/menu/index.d.ts +1 -0
  252. package/dist/primitives/menu/index.js +2 -0
  253. package/dist/primitives/notice/Callout.d.ts +49 -0
  254. package/dist/primitives/notice/Callout.js +28 -0
  255. package/dist/primitives/notice/Notice.d.ts +45 -0
  256. package/dist/primitives/notice/Notice.js +61 -0
  257. package/dist/primitives/notice/Toast.d.ts +55 -0
  258. package/dist/primitives/notice/Toast.js +142 -0
  259. package/dist/primitives/notice/index.d.ts +3 -0
  260. package/dist/primitives/notice/index.js +5 -0
  261. package/dist/primitives/overlay/Popover.d.ts +6 -0
  262. package/dist/primitives/overlay/Popover.js +27 -0
  263. package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
  264. package/dist/primitives/overlay/PopoverTrigger.js +38 -0
  265. package/dist/primitives/overlay/Tooltip.d.ts +38 -0
  266. package/dist/primitives/overlay/Tooltip.js +74 -0
  267. package/dist/primitives/overlay/index.d.ts +3 -0
  268. package/dist/primitives/overlay/index.js +5 -0
  269. package/dist/primitives/picker/Picker.d.ts +48 -0
  270. package/dist/primitives/picker/Picker.js +150 -0
  271. package/dist/primitives/picker/index.d.ts +1 -0
  272. package/dist/primitives/picker/index.js +2 -0
  273. package/dist/primitives/surface/Peek.d.ts +60 -0
  274. package/dist/primitives/surface/Peek.js +71 -0
  275. package/dist/primitives/surface/index.d.ts +1 -0
  276. package/dist/primitives/surface/index.js +2 -0
  277. package/dist/primitives/table/CellEditor.d.ts +14 -0
  278. package/dist/primitives/table/CellEditor.js +83 -0
  279. package/dist/primitives/table/DataTable.d.ts +135 -0
  280. package/dist/primitives/table/DataTable.js +244 -0
  281. package/dist/primitives/table/EditableTable.d.ts +96 -0
  282. package/dist/primitives/table/EditableTable.js +423 -0
  283. package/dist/primitives/table/PivotTable.d.ts +14 -0
  284. package/dist/primitives/table/PivotTable.js +0 -0
  285. package/dist/primitives/table/ReportTable.d.ts +38 -0
  286. package/dist/primitives/table/ReportTable.js +106 -0
  287. package/dist/primitives/table/RowActions.d.ts +16 -0
  288. package/dist/primitives/table/RowActions.js +28 -0
  289. package/dist/primitives/table/SheetTable.d.ts +70 -0
  290. package/dist/primitives/table/SheetTable.js +197 -0
  291. package/dist/primitives/table/Table.d.ts +106 -0
  292. package/dist/primitives/table/Table.js +116 -0
  293. package/dist/primitives/table/index.d.ts +7 -0
  294. package/dist/primitives/table/index.js +9 -0
  295. package/dist/primitives/tabs/Tabs.d.ts +91 -0
  296. package/dist/primitives/tabs/Tabs.js +56 -0
  297. package/dist/primitives/tabs/index.d.ts +1 -0
  298. package/dist/primitives/tabs/index.js +1 -0
  299. package/dist/primitives/tag/Tag.d.ts +36 -0
  300. package/dist/primitives/tag/Tag.js +68 -0
  301. package/dist/primitives/tag/Tags.d.ts +57 -0
  302. package/dist/primitives/tag/Tags.js +104 -0
  303. package/dist/primitives/tag/TagsField.d.ts +32 -0
  304. package/dist/primitives/tag/TagsField.js +182 -0
  305. package/dist/primitives/tag/choices.d.ts +38 -0
  306. package/dist/primitives/tag/choices.js +26 -0
  307. package/dist/primitives/tag/index.d.ts +4 -0
  308. package/dist/primitives/tag/index.js +8 -0
  309. package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
  310. package/dist/primitives/toolbar/Toolbar.js +36 -0
  311. package/dist/primitives/toolbar/index.d.ts +1 -0
  312. package/dist/primitives/toolbar/index.js +2 -0
  313. package/dist/primitives/value/Copyable.d.ts +37 -0
  314. package/dist/primitives/value/Copyable.js +50 -0
  315. package/dist/primitives/value/Fecha.d.ts +12 -0
  316. package/dist/primitives/value/Fecha.js +80 -0
  317. package/dist/primitives/value/Meter.d.ts +29 -0
  318. package/dist/primitives/value/Meter.js +46 -0
  319. package/dist/primitives/value/Metric.d.ts +42 -0
  320. package/dist/primitives/value/Metric.js +38 -0
  321. package/dist/primitives/value/Person.d.ts +90 -0
  322. package/dist/primitives/value/Person.js +208 -0
  323. package/dist/primitives/value/Relation.d.ts +69 -0
  324. package/dist/primitives/value/Relation.js +57 -0
  325. package/dist/primitives/value/Totals.d.ts +26 -0
  326. package/dist/primitives/value/Totals.js +4 -0
  327. package/dist/primitives/value/Value.d.ts +77 -0
  328. package/dist/primitives/value/Value.js +119 -0
  329. package/dist/primitives/value/index.d.ts +8 -0
  330. package/dist/primitives/value/index.js +9 -0
  331. package/dist/tokens.css +1025 -0
  332. package/dist/views/index.d.ts +2 -0
  333. package/dist/views/index.js +3 -0
  334. package/dist/views/list/List.d.ts +113 -0
  335. package/dist/views/list/List.js +97 -0
  336. package/dist/views/list/ListView.d.ts +6 -0
  337. package/dist/views/list/ListView.js +269 -0
  338. package/dist/views/list/columns.d.ts +18 -0
  339. package/dist/views/list/columns.js +27 -0
  340. package/dist/views/list/derive.d.ts +76 -0
  341. package/dist/views/list/derive.js +181 -0
  342. package/dist/views/list/index.d.ts +2 -0
  343. package/dist/views/list/index.js +4 -0
  344. package/dist/views/list/presentations.d.ts +39 -0
  345. package/dist/views/list/presentations.js +103 -0
  346. package/dist/views/list/types.d.ts +272 -0
  347. package/dist/views/list/types.js +1 -0
  348. package/dist/views/list/useNarrowList.d.ts +17 -0
  349. package/dist/views/list/useNarrowList.js +32 -0
  350. package/dist/views/record/Record.d.ts +126 -0
  351. package/dist/views/record/Record.js +141 -0
  352. package/dist/views/record/index.d.ts +1 -0
  353. package/dist/views/record/index.js +2 -0
  354. package/package.json +109 -4
@@ -0,0 +1,105 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { byDensity, useDensity } from "../../foundations/density.js";
5
+ import { SurfaceProvider, useSurface } from "../../foundations/surface.js";
6
+ import { Line } from "../../primitives/block/Line.js";
7
+ import { X } from "lucide-react";
8
+ /** THE BAR — two rows, two questions.
9
+ *
10
+ * ┌──────────┬──────────────────────────────┬──────────────┐
11
+ * │ crumbs │ 🔍 search… ⚙3 │ 1–8 of 187 ‹ › │
12
+ * ├──────────┼──────────────────────────────┼──────────────┤
13
+ * │ [New] │ Facturas · 180 · 7 · 8 │ [columns] [▤][▦] │
14
+ * └──────────┴──────────────────────────────┴──────────────┘
15
+ * WHERE / HOW MUCH I SEE HOW I LOOK
16
+ *
17
+ * Pagination answers how much of the filtered set is on screen, so it stays
18
+ * with the search. Columns and the presentation switcher answer how the set
19
+ * is looked at, so they sit with the actions — not next to the page range.
20
+ * Putting the four of them on the search line is how the field and «1–80»
21
+ * painted over each other the moment the window shrank.
22
+ *
23
+ * ── Why it is two lines tall ────────────────────────────────────────
24
+ *
25
+ * Because the three things that go on the left do not fit in one: a
26
+ * comfortable button is 34 px tall, a list title may have to wrap onto two
27
+ * lines, and below it goes a sentence saying what the list is made of. Forcing
28
+ * it into a single line means shrinking the button —the defect we already
29
+ * fixed— or truncating the title, which is worse: a list that cannot be named
30
+ * in full is indistinguishable from the one next to it.
31
+ *
32
+ * ── The selection transforms the bar, it does not add one ───────────
33
+ *
34
+ * When rows get selected, the identity zone turns into «3 elegidas ✕» plus the
35
+ * actions that apply. **In the same place and without pushing anything.** A
36
+ * bar that appears shifts the list downwards right when somebody is ticking
37
+ * checkboxes, and the row they were about to tick is no longer where it was.
38
+ *
39
+ * The actions on the left do NOT go away: «Nuevo» stays available with three
40
+ * rows selected, because selecting is not entering a mode. */
41
+ export function ListingBar({ actions, title, description, counters, selection, search, pagination, views, crumbs, surface, }) {
42
+ const selecting = selection !== undefined && selection.count > 0;
43
+ const d = useDensity();
44
+ const copy = useUiCopy();
45
+ const activeSurface = useSurface(surface);
46
+ const isChrome = activeSurface === "chrome";
47
+ /* In compact the title and the description are NOT drawn, and it is not for
48
+ lack of room: in a preview or on a phone, the surface header already says
49
+ what I am looking at. Repeating it here burns the only line there is for
50
+ the search box — which is what is actually needed. */
51
+ const withIdentity = d === "comfortable";
52
+ /* Two rows, and the split is the same one as in a record:
53
+ 1 · WHERE I AM and HOW MANY THERE ARE — crumbs, search, page range.
54
+ 2 · WHAT I CAN DO and HOW I LOOK — actions, identity, columns / views.
55
+
56
+ Views used to sit next to the pagination. On a narrow window the search
57
+ field and «1–80 / 211» painted over each other; columns and the switcher
58
+ were the extra weight on a line that only needed how MANY. */
59
+ const center = selecting ? (_jsxs("div", { className: "flex min-w-0 items-center gap-x-2", children: [_jsxs("span", { className: `inline-flex h-(--size-control-md) shrink-0 items-center gap-1.5 rounded-control pr-1 pl-2.5 text-meta font-medium ${isChrome ? "bg-chrome-ink/14 text-chrome-ink" : "bg-ink/8 text-ink"}`, children: [_jsx("span", { className: "tabular-nums", children: selection?.count.toLocaleString(copy.locale) }), copy.selection.chosen, _jsx("button", { type: "button", onClick: selection?.onClear, "aria-label": copy.selection.clear, title: copy.selection.clear, className: `inline-flex size-5 items-center justify-center rounded-pill transition-colors duration-(--duration-instant) ${isChrome ? "hover:bg-chrome-ink/20" : "hover:bg-primary/20"}`, children: _jsx(X, { className: "size-3", strokeWidth: 2.5 }) })] }), selection?.actions === undefined ? null : (_jsx("div", { className: "flex min-w-0 items-center gap-1.5", children: selection.actions }))] })) : search === undefined ? null : (
60
+ /* Selecting replaces the search box: what gets selected is over what is
61
+ ALREADY filtered, so searching there would change the set underneath the
62
+ selection. */
63
+ _jsx("div", { className: "w-full min-w-0", children: search }));
64
+ /* El campo de búsqueda va en el CENTRO de la línea, y centrado contra la
65
+ línea: pide `--container-search` y los dos lados se reparten lo que sobra
66
+ en mitades iguales. Antes tomaba el resto, así que un rastro largo a la
67
+ izquierda lo corría —y dos listas seguidas dibujaban su buscador en dos
68
+ lugares distintos—. Ver `primitives/block/Line.tsx`. */
69
+ const topRow = (_jsx(Line, { asks: "var(--container-crumb)", centerAsks: center === null ? "auto" : "var(--container-search)", gap: byDensity(d, "gap-x-4 gap-y-2", "gap-x-2 gap-y-1.5"), align: "items-start", start: crumbs,
70
+ /* `overflow-x-clip` y NO `overflow-hidden`: lo que hay que recortar es el
71
+ ancho —que el campo no pinte encima de «1–80»—, y `hidden` en un eje
72
+ fuerza `auto` en el otro, así que también recortaba hacia ABAJO. El
73
+ panel de filtros del `SearchBar` cuelga con `absolute top-full`, o sea
74
+ justo afuera de esta caja: apretar el embudo abría un panel que nadie
75
+ veía. `clip` es el único valor que deja `visible` en el otro eje. */
76
+ center: center === null ? null : (_jsx("div", { className: "flex w-full min-w-0 items-center overflow-x-clip overflow-y-visible", children: center })), end: pagination }));
77
+ const bottomRow = actions === undefined && !withIdentity && views === undefined ? null : (
78
+ /* On a narrow list (a phone, a drawer) the name and its counters take the whole first
79
+ line and the action and the controls go below: squeezed between them, the
80
+ description wrapped one word per line. */
81
+ _jsxs("div", { className: `flex min-w-0 flex-wrap items-center gap-y-2 ${byDensity(d, "gap-x-4", "gap-x-2")}`, children: [actions === undefined ? null : (_jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: actions })), !withIdentity ? null : (_jsxs("div", { className: "flex min-w-0 flex-1 flex-col @max-2xl:order-first @max-2xl:basis-full", children: [title === undefined ? null : (
82
+ /* No `truncate`: the title may wrap onto two lines. A list that
83
+ cannot be named in full is indistinguishable from the one next
84
+ to it. */
85
+ /* `title` and semibold: the name of the list is the entry point of the
86
+ screen. At `lead` it was smaller than the metrics under it, so the
87
+ largest thing on a list page was a number (measured: 1.46:1 over the
88
+ body, below the ~1.5 an eye needs to find where to start). */
89
+ _jsx("h2", { className: `text-title leading-tight font-semibold ${isChrome ? "text-chrome-ink" : "text-ink"}`, children: title })), description === undefined && counters === undefined ? null : (_jsxs("div", { className: "mt-0.5 flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-0.5", children: [counters?.map((c) => (_jsxs("span", { className: "flex items-baseline gap-1", children: [_jsx("span", { className: `text-meta font-medium tabular-nums ${isChrome ? "text-chrome-ink" : "text-ink"}`, children: typeof c.value === "number"
90
+ ? c.value.toLocaleString(copy.locale)
91
+ : c.value }), _jsx("span", { className: `text-micro ${isChrome ? "text-chrome-muted" : "text-muted"}`, children: c.label })] }, c.label))), description === undefined ? null : (_jsx("span", { className: `min-w-0 text-micro ${isChrome ? "text-chrome-muted" : "text-muted"}`, children: description }))] }))] })), views === undefined ? null : (_jsx("div", { className: "ml-auto flex shrink-0 items-center gap-2", children: views }))] }));
92
+ const bar = (_jsxs("div", { className: `flex min-w-0 flex-col ${byDensity(d, "gap-2", "gap-1.5")}`, children: [!isChrome && bottomRow !== null ? (_jsx("div", { className: "border-b border-line pb-2", children: topRow })) : (topRow), bottomRow] }));
93
+ /* On the canvas the bar is a light control band: white, a hairline below, the same
94
+ padding as the dark one — a list inside an application whose bar is already dark keeps
95
+ ONE dark band per screen. Without its own band the bar sat flush on the page and its
96
+ controls had nothing to belong to. */
97
+ if (!isChrome) {
98
+ return (_jsx(SurfaceProvider, { value: "canvas", children: _jsx("div", { className: `flex min-w-0 flex-col border-b border-line bg-surface ${byDensity(d, "px-3.5 py-2.5", "px-2.5 py-2")}`, children: bar }) }));
99
+ }
100
+ /* On the chrome the bar IS the band: the background reaches the edges and
101
+ everything inside inherits the surface, so there is no way to forget to
102
+ pass it — which is how the status bar ended up with the canvas palette on
103
+ top of the navy. */
104
+ return (_jsx(SurfaceProvider, { value: "chrome", children: _jsx("div", { className: `flex min-w-0 flex-col bg-chrome ${byDensity(d, "px-3.5 py-2.5", "px-2.5 py-2")}`, children: bar }) }));
105
+ }
@@ -0,0 +1,36 @@
1
+ import type { Surface } from "../../foundations/types.js";
2
+ /** How many of how many, and how I move to the next page.
3
+ *
4
+ * **It lives ONCE and it is mounted in two places.** The normal destination is
5
+ * the search bar: you search, you narrow down and you paginate on the same
6
+ * line, without looking down to the footer to discover there are another
7
+ * hundred and sixty. The footer (`ListingFooter`) existed only for the lists
8
+ * that do NOT have a search box — there is no narrowing line there to put it on.
9
+ *
10
+ * Two implementations of one pagination diverge at the first fix, so this is
11
+ * the only one and both places receive it.
12
+ *
13
+ * ── The range, not the page number ──────────────────────────────────
14
+ *
15
+ * «1–25 de 187» answers three questions —where I am, how much I see, how much
16
+ * there is— and «page 1 of 8» answers a single one, in units that depend on a
17
+ * page size the user did not choose.
18
+ *
19
+ * ── Why the page size is visible ────────────────────────────────────
20
+ *
21
+ * Because it is the difference between scrolling eight times and scrolling
22
+ * once. In a tool where somebody compares a hundred and eighty invoices,
23
+ * leaving it fixed at 25 forces paginating over something that is not a
24
+ * product decision. */
25
+ export declare function Pagination({ from, to, total, pageSize, pageSizes, onPageSizeChange, onPrev, onNext, surface, }: {
26
+ from: number;
27
+ to: number;
28
+ total: number;
29
+ pageSize?: number;
30
+ pageSizes?: readonly number[];
31
+ onPageSizeChange?: (next: number) => void;
32
+ onPrev?: () => void;
33
+ onNext?: () => void;
34
+ /** By default it inherits it from the surface that wraps it. */
35
+ surface?: Surface;
36
+ }): import("react").JSX.Element;
@@ -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,106 @@
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, }: {
22
+ icon?: LucideIcon;
23
+ name: string;
24
+ onOpen?: () => void;
25
+ onPeek?: () => void;
26
+ tags?: ReactNode;
27
+ /** The secondary data, already formatted. In mono: it aligns across rows. */
28
+ meta?: string;
29
+ selected?: boolean;
30
+ onSelectedChange?: (next: boolean) => void;
31
+ actions?: ReactNode;
32
+ /** The `⋯` menu. **Without this the button is not drawn**: there used to be a
33
+ * fixed one that opened nothing — a control that exists and does nothing
34
+ * teaches people not to press it, and after that nobody presses the one that
35
+ * does work. */
36
+ more?: readonly MenuGroup[];
37
+ onMoreSelect?: (id: string) => void;
38
+ /** What happens on open, when «as a panel» is not true: «open the order that
39
+ * produced it». It is the `title` and the accessible name. */
40
+ openHint?: string;
41
+ /** What the resource PRODUCED, hanging below. Context: the indent and the
42
+ * rule are put there by the system and not by the view, because the
43
+ * relationship «this came out of that» has to look the same in the four
44
+ * lists that need it. */
45
+ children?: ReactNode;
46
+ }): import("react").JSX.Element;
47
+ /**
48
+ * EL ENCABEZADO DE LA PRESENTACIÓN DE FILAS.
49
+ *
50
+ * Context: la tabla y la planilla dicen qué es cada columna; la lista de filas
51
+ * no decía nada. Ochenta renglones con «Ferretería Sur · 13 sept · Bs 12.480» a la
52
+ * derecha y una ficha al final, y había que deducir qué era cada número — o
53
+ * cambiar a la tabla para averiguarlo, que es cambiar de presentación para leer
54
+ * la misma pantalla.
55
+ *
56
+ * **No son columnas: son las ZONAS de la fila, rotuladas.** Una fila tiene un
57
+ * nombre que se estira, un `meta` que es UNA cadena ya formateada, y una
58
+ * columna de fichas de ancho fijo. Rotularlas como si fueran tres columnas
59
+ * alineadas sería mentir sobre lo que hay debajo: el `meta` de una fila puede
60
+ * decir tres cosas y el de otra dos.
61
+ *
62
+ * Por eso calca la caja de `Row` —mismo padding, mismos huecos, mismos
63
+ * espaciadores de casilla, ícono y acciones— en vez de declarar un ancho
64
+ * propio. Si `Row` cambia su geometría, esto la sigue.
65
+ */
66
+ export declare function RowHeader({ name, meta, tags, withSelection, withIcon, withActions, }: {
67
+ name: string;
68
+ meta?: string;
69
+ tags?: string;
70
+ /** Los espaciadores de la izquierda. Los pone quien dibuja las filas, porque
71
+ * es el único que sabe si esta lista elige y si sus filas tienen ícono. */
72
+ withSelection?: boolean;
73
+ withIcon?: boolean;
74
+ /** La zona de acciones de la derecha ocupa su lugar aunque esté invisible
75
+ * —así nada se corre al pasar el puntero—, así que el rótulo de las fichas
76
+ * queda desalineado si acá no se reserva lo mismo. */
77
+ withActions?: boolean;
78
+ }): import("react").JSX.Element;
79
+ /**
80
+ * The header of a group inside a list.
81
+ *
82
+ * Context: grouping existed ONLY inside `DataTable`, so changing presentation
83
+ * —from table to rows— lost the grouping. Grouping belongs to the SET and not
84
+ * to the presentation; that is why it lives here.
85
+ *
86
+ * **`field` is drawn, and it is not decoration.** With one level nobody needed
87
+ * it: there was a single axis and everyone knew what it was. With several,
88
+ * «2026-09» does not say whether it is when the thing was registered or when it
89
+ * ran, and two consecutive headers can read as siblings when one is inside the
90
+ * other. The field names the cut; the indent says how deep it is.
91
+ */
92
+ export declare function GroupHeader({ field, label, count, detail, depth, open, onToggle, }: {
93
+ /** By which field this pile was cut. Without it a value is ambiguous as soon
94
+ * as there is more than one level. */
95
+ field?: string;
96
+ label: string;
97
+ count?: number;
98
+ /** What the pile ADDS UP TO, already written: «412 facturas · Bs 1.204.933». A
99
+ * folded pile that only says how many there are hides the answer instead of
100
+ * summarising it. */
101
+ detail?: string;
102
+ /** How deep the pile is. `0` is the first level. */
103
+ depth?: number;
104
+ open?: boolean;
105
+ onToggle?: () => void;
106
+ }): import("react").JSX.Element;
@@ -0,0 +1,122 @@
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 { ChevronRight, MoreHorizontal, SquareArrowOutUpRight } from "lucide-react";
9
+ /* ---------------------------------------------------------------- *
10
+ * THE RICH ROW — the other density
11
+ * ---------------------------------------------------------------- */
12
+ /** A record in two lines: big name on top, metadata below.
13
+ *
14
+ * ── The name opens, the body peeks ──────────────────────────────────
15
+ *
16
+ * Two different targets and zero waiting. The name is a LINK and opens with
17
+ * one click; the rest of the row does a preview. It used to open with a double
18
+ * click, and to be able to tell it apart from the single one ALL clicks had to
19
+ * be delayed by 220 ms — a name painted as a link discovers itself, «try
20
+ * double clicking» is written nowhere.
21
+ *
22
+ * The gesture is invisible if nothing says so, so on hover `⇱ abrir` appears:
23
+ * it is learned in one hover and never has to be explained.
24
+ *
25
+ * ── The metadata is in plain sight ──────────────────────────────────
26
+ *
27
+ * «some metadata that might help is not visible» — and it was literal: to know
28
+ * whether `FAC/2026/0118` was paid or overdue you had to open it. */
29
+ export function Row({ icon: Icon, name, onOpen, onPeek, tags, meta, selected, onSelectedChange, actions, more, onMoreSelect, openHint, children, }) {
30
+ const d = useDensity();
31
+ const copy = useUiCopy();
32
+ const openAs = openHint ?? copy.shell.openAsPanel;
33
+ const row = (
34
+ /* A `div` and not an `li`: whoever composes decides whether this is a list,
35
+ a table or a branch of a tree. And the zone that peeks is a `div` with
36
+ `onClick` and not a `button` — the actions are buttons too, and a button
37
+ inside another one is invalid HTML: the browser takes it apart and the
38
+ clicks get lost. */
39
+ _jsx("div", { className: "border-b border-line px-1 py-0.5 last:border-b-0", children: _jsxs("div", { "data-slot": "row",
40
+ /* The hairline stays on the shell, full width: eighty similar names with
41
+ no rule between them read as a paragraph. The wash is the inner row,
42
+ inset and rounded, so the hover is a mark and not a stripe. The inner
43
+ padding is shorter by exactly that inset, so the text does not move. */
44
+ 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")}`,
45
+ /* `onPeek` con `onOpen` de reserva: quien sólo declara `onOpen` —lo
46
+ natural si nunca oyó hablar de la vista previa— tiene que poder abrir
47
+ igual haciendo click en cualquier parte de la fila, no sólo en el
48
+ nombre. Con la vista previa apagada (`PeekLayer.tsx`) esto es hoy el
49
+ caso de todos: un click en la fila abre, sin más. */
50
+ 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 : (
51
+ /* Wrapped or not, the metadata is still there —it is what saves
52
+ having to open a set to know whether it is a thousand rows or a
53
+ million— and it truncates instead of wrapping into itself: three
54
+ lines per row in a 480 px drawer means three rows fit on screen. */
55
+ _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", children: [actions, onOpen === undefined ? null : (_jsx("button", { type: "button", onClick: (e) => {
56
+ e.stopPropagation();
57
+ onOpen();
58
+ }, 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 }) }) }))] })] }) }));
59
+ if (children === undefined)
60
+ return row;
61
+ /* What the resource produced, hanging below. The indent reaches up to where
62
+ the name starts —not up to the edge— so the rule falls exactly below the
63
+ icon and the relationship reads without reading: this came out of that.
64
+
65
+ The child is NOT another `Row`: an invoice line does not open, does not get
66
+ selected and has no actions of its own. Treating it as a row would make it promise
67
+ all of that. */
68
+ 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 })] }));
69
+ }
70
+ /**
71
+ * EL ENCABEZADO DE LA PRESENTACIÓN DE FILAS.
72
+ *
73
+ * Context: la tabla y la planilla dicen qué es cada columna; la lista de filas
74
+ * no decía nada. Ochenta renglones con «Ferretería Sur · 13 sept · Bs 12.480» a la
75
+ * derecha y una ficha al final, y había que deducir qué era cada número — o
76
+ * cambiar a la tabla para averiguarlo, que es cambiar de presentación para leer
77
+ * la misma pantalla.
78
+ *
79
+ * **No son columnas: son las ZONAS de la fila, rotuladas.** Una fila tiene un
80
+ * nombre que se estira, un `meta` que es UNA cadena ya formateada, y una
81
+ * columna de fichas de ancho fijo. Rotularlas como si fueran tres columnas
82
+ * alineadas sería mentir sobre lo que hay debajo: el `meta` de una fila puede
83
+ * decir tres cosas y el de otra dos.
84
+ *
85
+ * Por eso calca la caja de `Row` —mismo padding, mismos huecos, mismos
86
+ * espaciadores de casilla, ícono y acciones— en vez de declarar un ancho
87
+ * propio. Si `Row` cambia su geometría, esto la sigue.
88
+ */
89
+ export function RowHeader({ name, meta, tags, withSelection = false, withIcon = false, withActions = false, }) {
90
+ const d = useDensity();
91
+ const rotulo = "truncate text-micro font-medium text-muted";
92
+ return (_jsxs("div", { "data-slot": "row-header",
93
+ /* Se pega debajo de la barra de la lista, como el encabezado de la tabla
94
+ y el de la planilla. Ver `system/sticky.tsx`. */
95
+ 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 }))] }));
96
+ }
97
+ /**
98
+ * The header of a group inside a list.
99
+ *
100
+ * Context: grouping existed ONLY inside `DataTable`, so changing presentation
101
+ * —from table to rows— lost the grouping. Grouping belongs to the SET and not
102
+ * to the presentation; that is why it lives here.
103
+ *
104
+ * **`field` is drawn, and it is not decoration.** With one level nobody needed
105
+ * it: there was a single axis and everyone knew what it was. With several,
106
+ * «2026-09» does not say whether it is when the thing was registered or when it
107
+ * ran, and two consecutive headers can read as siblings when one is inside the
108
+ * other. The field names the cut; the indent says how deep it is.
109
+ */
110
+ export function GroupHeader({ field, label, count, detail, depth = 0, open = true, onToggle, }) {
111
+ const d = useDensity();
112
+ const copy = useUiCopy();
113
+ 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 }))] }));
114
+ /* La sangría va en el relleno de la izquierda y no en un margen: el
115
+ encabezado tiene fondo y borde inferior, y un margen los cortaría antes del
116
+ borde de la lista — la banda dejaría de leerse como una franja. El hover
117
+ no reemplaza ese fondo: es una marca metida adentro, redondeada. */
118
+ 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")}`;
119
+ const indent = depth === 0 ? undefined : { paddingLeft: `calc(var(--spacing-indent) * ${depth})` };
120
+ 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" }));
121
+ 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] }));
122
+ }
@@ -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;