@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,98 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { useSurface } from "../../foundations/surface.js";
5
+ import { focusRing, tv } from "../../foundations/variants.js";
6
+ import { Menu } from "../../primitives/menu/Menu.js";
7
+ import { ChevronRight, MoreHorizontal } from "lucide-react";
8
+ import { Breadcrumb as AriaBreadcrumb, Breadcrumbs, Link } from "react-aria-components";
9
+ /** The three tones of a trail, per surface: where I came from, where I am, and the
10
+ * chevron between them. The chevron is the faintest thing in the row on purpose —
11
+ * it is punctuation, and punctuation louder than the words makes the words hard to
12
+ * read. */
13
+ const crumbStyles = tv({
14
+ extend: focusRing,
15
+ base: "block min-w-0 rounded-mark text-meta transition-colors duration-(--duration-instant)",
16
+ variants: {
17
+ surface: { canvas: "", chrome: "outline-chrome-link" },
18
+ kind: {
19
+ link: "cursor-pointer hover:underline hover:underline-offset-4",
20
+ inert: "cursor-default",
21
+ current: "cursor-default font-medium",
22
+ },
23
+ },
24
+ compoundVariants: [
25
+ { surface: "canvas", kind: ["link", "inert"], class: "text-muted" },
26
+ { surface: "canvas", kind: "link", class: "hover:text-ink" },
27
+ { surface: "canvas", kind: "current", class: "text-ink" },
28
+ { surface: "chrome", kind: ["link", "inert"], class: "text-chrome-muted" },
29
+ { surface: "chrome", kind: "link", class: "hover:text-chrome-ink" },
30
+ { surface: "chrome", kind: "current", class: "text-chrome-ink" },
31
+ ],
32
+ });
33
+ /**
34
+ * Where I came from, without eating the line.
35
+ *
36
+ * Built on React Aria's `Breadcrumbs`: the LAST crumb is the page I am on, so it
37
+ * gets `aria-current="page"` and stops being pressable — React Aria decides that
38
+ * from its position, not from whether the caller forgot an `onClick`. The others
39
+ * are `Link`s, so they press with Enter, show the system focus ring only from the
40
+ * keyboard, and read as links to a screen reader.
41
+ *
42
+ * **The hierarchy is in the TONE, not in a color.** Ancestors are quiet
43
+ * (`text-muted`), the current page is ink and medium weight: the eye lands on where
44
+ * I am and reads backwards. Painting the ancestors in the link blue made the whole
45
+ * row the same weight — four blue words and no «here».
46
+ *
47
+ * Two things keep it inside its place, and they are two because they are two
48
+ * different problems:
49
+ *
50
+ * 1. **Each crumb asks for `--container-crumb` and no more.** A name in this
51
+ * product reaches fifty characters, so one crumb is enough to eat the band even
52
+ * when the trail has three. The rest is cut with «…» and the full name stays in
53
+ * the `title`.
54
+ * 2. **With more than `folded` crumbs, the MIDDLE ones fold** into a «…» menu.
55
+ * By COUNT, not by width: a six-crumb trail is long on any screen. The first
56
+ * —the root, where you go back to— and the last —where I am— never fold, and
57
+ * what folded is still navigable, in order.
58
+ *
59
+ * Folding and truncating are not the same and that is why both exist: one removes
60
+ * CRUMBS and the other removes LETTERS.
61
+ */
62
+ export function Breadcrumb({ crumbs, surface: surfaceProp, folded = 3, }) {
63
+ const copy = useUiCopy();
64
+ const surface = useSurface(surfaceProp);
65
+ const edge = surface === "chrome" ? "text-chrome-muted/50" : "text-line-strong";
66
+ /* The first and the last always; the middle goes to the menu when there are too many. */
67
+ const folds = crumbs.length > folded;
68
+ const hidden = folds ? crumbs.slice(1, -1) : [];
69
+ const shown = folds
70
+ ? [crumbs[0], null, crumbs[crumbs.length - 1]]
71
+ : crumbs;
72
+ return (_jsx("nav", { "aria-label": copy.record.location, className: "flex min-w-0", children: _jsx(Breadcrumbs, { "aria-label": copy.record.location, className: "flex min-w-0 flex-wrap items-center gap-1", children: shown.map((crumb, i) => {
73
+ const key = crumb === null ? "folded" : `${i}-${crumb.label}`;
74
+ const last = i === shown.length - 1;
75
+ return (_jsxs(AriaBreadcrumb, { id: key, className: "flex min-w-0 items-center gap-1", children: [i === 0 ? null : (_jsx(ChevronRight, { className: `size-(--size-icon-sm) shrink-0 ${edge}`, strokeWidth: 2, "aria-hidden": true })), crumb === null ? (_jsx(Menu, { label: copy.record.location, title: copy.record.location, icon: MoreHorizontal, iconOnly: true, chevron: false, variant: "text", size: "sm", surface: surface === "chrome" ? "chrome" : "canvas", align: "start", groups: [
76
+ {
77
+ options: hidden.map((one, at) => ({
78
+ id: `crumb-${at}`,
79
+ label: one.label,
80
+ disabled: one.onClick === undefined,
81
+ onSelect: one.onClick,
82
+ })),
83
+ },
84
+ ] })) : (_jsx(Link
85
+ /* React Aria already disables the last one; a middle crumb with
86
+ nowhere to go is disabled too, instead of a link that does
87
+ nothing. */
88
+ , {
89
+ /* React Aria already disables the last one; a middle crumb with
90
+ nowhere to go is disabled too, instead of a link that does
91
+ nothing. */
92
+ isDisabled: last ? undefined : crumb.onClick === undefined, onPress: crumb.onClick, className: ({ isFocusVisible }) => crumbStyles({
93
+ surface,
94
+ isFocusVisible,
95
+ kind: last ? "current" : crumb.onClick === undefined ? "inert" : "link",
96
+ }), children: _jsx("span", { className: "block max-w-(--container-crumb) truncate", title: crumb.label, children: crumb.label }) }))] }, key));
97
+ }) }) }));
98
+ }
@@ -0,0 +1,26 @@
1
+ export interface DirtyState {
2
+ onSave: () => void;
3
+ onDiscard: () => void;
4
+ /** It is saving. The button turns off: pressing it twice sends it twice, and
5
+ * when what gets saved is a payment, that matters. */
6
+ saving?: boolean;
7
+ /** What gets saved, when it is not obvious: «guardar la factura», «guardar la
8
+ * regla». */
9
+ label?: string;
10
+ /** Why it CANNOT be saved yet. Present = the button is off and the reason is
11
+ * in its `title` — a button that is off with no reason is a wall. */
12
+ blocked?: string;
13
+ }
14
+ /** Save and discard, in the rail and in place of the action buttons.
15
+ *
16
+ * Context: discard goes to the LEFT of save and with the quieter look. It is
17
+ * the same rule as `FormActions`, and here it matters more: the rail is where
18
+ * the hand is after editing.
19
+ *
20
+ * The «sin guardar» warning is text and not just a color: whoever is leaving
21
+ * the screen has to be able to read what is going on, not deduce it from a
22
+ * dot. */
23
+ export declare function DirtyActions({ onSave, onDiscard, saving, label, blocked, }: DirtyState): import("react").JSX.Element;
24
+ /** The one action a view leads with: the element written with `role="primary"` — the
25
+ * same prop that paints it gold and that `make smoke` counts, so there is no second way to
26
+ * say «this is the main one» that could disagree with the first. */
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { bySurface, useSurface } from "../../foundations/surface.js";
5
+ import { Button } from "../../primitives/button/Button.js";
6
+ import { Save, Undo2 } from "lucide-react";
7
+ /** Save and discard, in the rail and in place of the action buttons.
8
+ *
9
+ * Context: discard goes to the LEFT of save and with the quieter look. It is
10
+ * the same rule as `FormActions`, and here it matters more: the rail is where
11
+ * the hand is after editing.
12
+ *
13
+ * The «sin guardar» warning is text and not just a color: whoever is leaving
14
+ * the screen has to be able to read what is going on, not deduce it from a
15
+ * dot. */
16
+ export function DirtyActions({ onSave, onDiscard, saving = false, label, blocked, }) {
17
+ const copy = useUiCopy();
18
+ /* It follows the rail it sits on instead of assuming the dark one: on a light rail the
19
+ chrome palette was white text on white. */
20
+ const surface = useSurface();
21
+ return (_jsxs(_Fragment, { children: [_jsx("span", { className: `inline-flex h-(--size-control-md) shrink-0 items-center gap-1.5 rounded-control px-2.5 text-meta font-medium ${bySurface(surface, "bg-warn/12 text-warn", "bg-chrome-warn/15 text-chrome-warn")}`, children: copy.record.unsaved }), _jsx(Button, { surface: surface, onClick: onDiscard, disabled: saving, icon: Undo2, children: copy.record.discard }), _jsx("span", { ...(blocked === undefined ? {} : { title: blocked }), children: _jsx(Button, { role: "primary", surface: surface, onClick: onSave, disabled: saving || blocked !== undefined, icon: Save, children: saving ? copy.record.saving : (label ?? copy.record.save) }) })] }));
22
+ }
@@ -0,0 +1,18 @@
1
+ import type { Surface } from "../../foundations/types.js";
2
+ /** Where I am inside the set I came from, and how I move to the next one.
3
+ *
4
+ * **It turns a record into a position.** Without this, opening the third of a
5
+ * list of eighty and wanting to see the fourth forces going back, finding
6
+ * where you were and opening again — three actions for what is one arrow.
7
+ *
8
+ * It goes far from the actions on purpose: moving between records is not doing
9
+ * something to this record. Stuck next to «Eliminar», one extra click on the
10
+ * wrong arrow is a destructive action. */
11
+ export declare function Pager({ index, total, onPrev, onNext, surface: surfaceProp, }: {
12
+ /** 1-based: what gets shown is «12 de 80», not «11». */
13
+ index: number;
14
+ total: number;
15
+ onPrev?: () => void;
16
+ onNext?: () => void;
17
+ surface?: Surface;
18
+ }): import("react").JSX.Element;
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { useSurface } from "../../foundations/surface.js";
5
+ import { ChevronLeft, ChevronRight } from "lucide-react";
6
+ /* ---------------------------------------------------------------- *
7
+ * THE PAGER
8
+ * ---------------------------------------------------------------- */
9
+ /** Where I am inside the set I came from, and how I move to the next one.
10
+ *
11
+ * **It turns a record into a position.** Without this, opening the third of a
12
+ * list of eighty and wanting to see the fourth forces going back, finding
13
+ * where you were and opening again — three actions for what is one arrow.
14
+ *
15
+ * It goes far from the actions on purpose: moving between records is not doing
16
+ * something to this record. Stuck next to «Eliminar», one extra click on the
17
+ * wrong arrow is a destructive action. */
18
+ export function Pager({ index, total, onPrev, onNext, surface: surfaceProp, }) {
19
+ const copy = useUiCopy();
20
+ const surface = useSurface(surfaceProp);
21
+ const isChrome = surface === "chrome";
22
+ const faint = isChrome ? "text-chrome-muted" : "text-muted";
23
+ const hover = isChrome
24
+ ? "not-disabled:hover:bg-chrome-ink/10 not-disabled:hover:text-chrome-ink"
25
+ : "not-disabled:hover:bg-ink/6 not-disabled:hover:text-ink";
26
+ /* On the chrome the ring is the chrome's link cyan, like the TopBar's: the
27
+ primary navy on a navy band is a ring nobody sees. */
28
+ const ring = isChrome
29
+ ? "focus-visible:outline-chrome-link"
30
+ : "focus-visible:outline-primary";
31
+ 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 ${ring} ${faint} ${hover}`;
32
+ return (_jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsxs("span", { className: `text-micro tabular-nums ${faint}`, children: [index, " / ", total.toLocaleString(copy.locale)] }), _jsx("button", { type: "button", onClick: onPrev, disabled: index <= 1, "aria-label": copy.record.prev, title: copy.record.prev, className: arrow, children: _jsx(ChevronLeft, { className: "size-(--size-icon-md)", strokeWidth: 2 }) }), _jsx("button", { type: "button", onClick: onNext, disabled: index >= total, "aria-label": copy.record.next, title: copy.record.next, className: arrow, children: _jsx(ChevronRight, { className: "size-(--size-icon-md)", strokeWidth: 2 }) })] }));
33
+ }
@@ -0,0 +1,75 @@
1
+ import { type Crumb } from "./Breadcrumb.js";
2
+ import { type DirtyState } from "./DirtyActions.js";
3
+ import type { Surface, Tone } from "../../foundations/types.js";
4
+ import type { LucideIcon } from "lucide-react";
5
+ import type { ReactNode } from "react";
6
+ export type { Surface };
7
+ export declare function RecordHeader({ crumbs, pager, actions, status, kicker, icon: Icon, title, subtitle, tag, figure, railSurface, smart, aspects, dirty, activity, activityLayout, blocking, indicator, children, }: {
8
+ crumbs?: readonly Crumb[];
9
+ pager?: ReactNode;
10
+ /** The action buttons. At most three visible + a `Menu` with `iconOnly`, and
11
+ * the rule for choosing which three: the ones that PRODUCE something new,
12
+ * never the navigation ones. They take the rail's surface from context: write
13
+ * them without `surface`, light by default, dark on `Record.Rail surface="chrome"`. */
14
+ actions?: ReactNode;
15
+ /** Everything around the record: its notices, conversation and history — an
16
+ * `Activity`. Never inside the sheet: it is about the record, not part of the form. */
17
+ activity?: ReactNode;
18
+ /**
19
+ * Where the activity goes, as the record's width allows (THE PAGE, below).
20
+ *
21
+ * auto a column at the RIGHT of the sheet, joined to it, when the page is
22
+ * `--container-beside` wide, and a card BELOW the sheet when it is not.
23
+ * **The default.**
24
+ * below always below the sheet, at its width.
25
+ *
26
+ * It is said by the RECORD and not guessed from the child: the flex direction is set by
27
+ * this container, and a parent that inspects what it was handed to decide how to lay
28
+ * itself out breaks the next time someone wraps the panel in something else.
29
+ */
30
+ activityLayout?: "auto" | "below";
31
+ /** `StatusBar` (where is it at?), the activity's `Timeline` (what happened to get
32
+ * here?), or both: they are different questions and many records only have one. */
33
+ status?: ReactNode;
34
+ /** The category, small and above the name: «factura de cliente». It makes a
35
+ * name like `NF-02/2026/00137` mean something without deducing it from the
36
+ * format. */
37
+ kicker?: string;
38
+ icon?: LucideIcon;
39
+ title: ReactNode;
40
+ /** The metadata that only DESCRIBES. In mono: the machine wrote it. */
41
+ subtitle?: ReactNode;
42
+ tag?: {
43
+ label: string;
44
+ tone?: Tone;
45
+ };
46
+ /** The record's key figure, as large as the title, on the right. See `RecordIdentity`. */
47
+ figure?: {
48
+ label: string;
49
+ value: ReactNode;
50
+ };
51
+ /** The rail's band. `canvas` (the default) is a light control bar with a hairline below;
52
+ * `chrome` is the dark band, an explicit choice for a record that owns the screen's
53
+ * only dark band (no dark application bar above it). */
54
+ railSurface?: Surface;
55
+ /** The counters that TAKE you somewhere else. They go in the sheet and next
56
+ * to the title — they belong to the record, not to my position in front of
57
+ * it. */
58
+ smart?: ReactNode;
59
+ aspects?: ReactNode;
60
+ /** There are unsaved changes. Its presence REPLACES the action buttons with
61
+ * save/discard, and it is not an aesthetic decision: running «Registrar pago» on
62
+ * an edited and unsaved invoice runs with the old values, and whoever pressed
63
+ * it has no way to know. As long as there are changes, the only thing that
64
+ * can be done is confirm them or throw them away. */
65
+ dirty?: DirtyState;
66
+ /** What is WRONG with the record — `RecordBlocking`. Always visible, outside the sheet:
67
+ * between the rail and the paper, never among the fields (see `RecordNotices`). */
68
+ blocking?: ReactNode;
69
+ /** The rest of the record's notices, as one indicator — `RecordNoticeIndicator`. In the
70
+ * rail next to the counters; without a rail, first in the record, next to them. */
71
+ indicator?: ReactNode;
72
+ /** The record body. It sits IN the paper with the title, so groups, tabs
73
+ * and the page are one white sheet on the ground — Odoo's `<sheet>`. */
74
+ children?: ReactNode;
75
+ }): import("react").JSX.Element;
@@ -0,0 +1,104 @@
1
+ "use client";
2
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { Breadcrumb } from "./Breadcrumb.js";
4
+ import { DirtyActions, } from "./DirtyActions.js";
5
+ import { isPrimary, ThumbBar, thumbPlan } from "./ThumbBar.js";
6
+ import { byDensity, useDensity } from "../../foundations/density.js";
7
+ import { SurfaceProvider } from "../../foundations/surface.js";
8
+ import { Line } from "../../primitives/block/Line.js";
9
+ import { Tag } from "../../primitives/tag/Tag.js";
10
+ export function RecordHeader({ crumbs, pager, actions, status, kicker, icon: Icon, title, subtitle, tag, figure, railSurface = "canvas", smart, aspects, dirty, activity, activityLayout = "auto", blocking, indicator, children, }) {
11
+ const d = useDensity();
12
+ const hasRail = crumbs !== undefined ||
13
+ pager !== undefined ||
14
+ actions !== undefined ||
15
+ status !== undefined ||
16
+ dirty !== undefined;
17
+ /* In compact the rail goes away ENTIRELY, and it is not for room: in a
18
+ preview, the breadcrumbs and the pager belong to the surface that opened it
19
+ —the list behind is still there— and the actions are already in the
20
+ drawer's header. A rail inside another rail is two bars saying where I am.
21
+
22
+ The state does survive: it moves to the sheet, below the title. It is the
23
+ only thing in the rail that belongs to the RECORD and not to my position in
24
+ front of it. */
25
+ const withRail = hasRail && d === "comfortable";
26
+ /* The activity column at the RIGHT of the sheet when the page is wide (THE PAGE). */
27
+ const beside = activity !== undefined && activityLayout === "auto";
28
+ /* The thumb bar's content (see THE THUMB BAR in `ThumbBar`): dirty first, else the primary. */
29
+ const { rail: railActions, primaries, mode: thumb, } = thumbPlan({ withRail, actions, dirty });
30
+ /* In compact the TITLE is not drawn either, for the same reason as in
31
+ `Listing`: the surface header already says it. Drawing it here showed it
32
+ twice in the same 480 px drawer — and the name of a customer in this product
33
+ reaches 50 characters, so the repetition eats two whole lines.
34
+
35
+ What does NOT go away: the `tag`, the state, the counters and the aspects.
36
+ They are what the drawer header does not say and the only way to see what
37
+ hangs off it without pinning the panel. */
38
+ const withIdentity = d === "comfortable";
39
+ /* The paper is Odoo's `<sheet>`: white on the ground, hairline, not a
40
+ shadow. It exists on the PAGE, where the chrome rail is a real band
41
+ (crumbs, pager, actions). A drawer is compact so `withRail` is already
42
+ false. A dialog is comfortable but has no crumbs or actions — and it IS
43
+ already a card. Status alone is not a page. */
44
+ const paper = withRail &&
45
+ (crumbs !== undefined ||
46
+ pager !== undefined ||
47
+ actions !== undefined ||
48
+ dirty !== undefined);
49
+ const identity = (_jsxs("div", { className: `flex flex-col ${byDensity(d, "gap-2.5 px-4 pt-3.5", "gap-2 px-0 pt-0")}`, children: [withRail || (smart === undefined && indicator === undefined) ? null : (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-2", children: [smart, indicator] })), _jsxs("div", { className: "flex min-w-0 flex-wrap items-start gap-x-5 gap-y-2", children: [_jsxs("div", { className: "flex min-w-0 items-start gap-2.5", children: [Icon === undefined || d === "compact" ? null : (_jsx("span", { className: "mt-1 flex size-9 shrink-0 items-center justify-center rounded-box bg-ink/5 text-muted", children: _jsx(Icon, { className: "size-(--size-icon-lg)", strokeWidth: 1.75 }) })), _jsxs("div", { className: "flex min-w-0 flex-col", children: [kicker === undefined || !withIdentity ? null : (_jsx("span", { className: "text-micro font-medium text-muted", children: kicker })), _jsxs("div", { className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1", children: [!withIdentity ? null : (
50
+ /* `display`, not `title`: the record's name is the entry point of the
51
+ screen. At the title size (19 px over a 13 px body, 1.46:1) nothing on
52
+ the sheet stood out and the eye had nowhere to start. */
53
+ _jsx("h1", { className: "min-w-0 truncate text-display font-semibold text-ink", children: title })), tag === undefined ? null : _jsx(Tag, { tone: tag.tone, children: tag.label })] }), subtitle === undefined || !withIdentity ? null : (_jsx("p", { className: "mt-0.5 truncate text-lead text-ink", children: subtitle }))] })] }), figure === undefined || !withIdentity ? null : (_jsxs("div", { className: "ml-auto flex shrink-0 flex-col items-end", children: [_jsx("span", { className: "text-micro font-medium text-muted", children: figure.label }), _jsx("span", { className: "text-display font-semibold tabular-nums text-ink", children: figure.value })] }))] }), withRail || status === undefined ? null : (_jsx("div", { className: "flex min-w-0 flex-wrap items-center gap-1.5", children: status })), aspects === undefined ? null : (
54
+ /* The line is the sheet's mouth. In comfortable it walks to the
55
+ edges; the current tab sits on it and opens into the page below. */
56
+ _jsx("div", { className: `border-b border-line-strong ${byDensity(d, "-mx-4 px-4", "")}`, children: aspects }))] }));
57
+ return (_jsxs(_Fragment, { children: [withRail ? (_jsx(SurfaceProvider, { value: railSurface, children: _jsx("div", { className: "@container/record", children: _jsxs("div", { className: `flex flex-col gap-2 px-3.5 py-2.5 ${railSurface === "chrome" ? "bg-chrome" : "border-b border-line bg-surface"}`, children: [_jsx(Line, { start: crumbs === undefined || crumbs.length === 0 ? null : (_jsx(Breadcrumb, { crumbs: crumbs, surface: railSurface })), center:
58
+ /* The notices' indicator rides with the counters: both are about the
59
+ record and both open something; neither is my position in front of
60
+ it. The line wraps on a phone, so it is never pushed out of reach. */
61
+ smart === undefined && indicator === undefined ? undefined : (_jsxs("div", { className: "flex min-w-0 flex-wrap items-center justify-center gap-2", children: [smart, indicator] })), end: pager }), actions === undefined && status === undefined && dirty === undefined ? null : (
62
+ /* Row 2 — WHAT I CAN DO and WHERE IT IS AT: actions stuck to the
63
+ left edge, the history and the state stuck to the right one. No
64
+ `flex-wrap`: wrapping split the row in two and the state ended up
65
+ below the actions, which is another hierarchy. */
66
+ _jsxs("div", { className: "flex min-w-0 items-start gap-x-4", children: [dirty !== undefined ? (_jsx("div", { className: "flex shrink-0 items-center gap-1.5 @max-handheld/record:hidden", children: _jsx(DirtyActions, { ...dirty }) })) : actions === undefined ? null : (_jsx("div", { className: "flex shrink-0 items-center gap-1.5", children: primaries.length === 0
67
+ ? actions
68
+ : railActions.map((node) => isPrimary(node) ? (_jsx("span", { "data-thumb-moves": "", className: "contents @max-handheld/record:hidden", children: node }, node.key)) : (node)) })), status === undefined ? null : (_jsx("div", { className: "ml-auto flex min-w-0 flex-col items-end gap-1.5", children: status }))] }))] }) }) })) : null, paper ? (
69
+ /* ==================================================================== *
70
+ * THE PAGE — [sheet | Actividad], on the ground.
71
+ *
72
+ * Two columns, joined, placed together and centred: the sheet
73
+ * (≤ --container-sheet, 72rem) and, at its RIGHT, the record's ACTIVITY
74
+ * (--container-activity, 22rem) — no strip of ground between them, so the page
75
+ * reads left to right as one paper: the record, then what it has. The pair is at
76
+ * most --container-record (94rem) wide; on a 1680 px desk both fit whole.
77
+ *
78
+ * A note of a field or a row is not drawn in the column: its mark (`Flag`) stays
79
+ * by its label or at its row — WHERE — and the activity lists it with the record's
80
+ * notices above the timeline — WHAT —, taking you to it on press. Cards aligned in
81
+ * the column were tried and dropped: they and the history fought for one column,
82
+ * and the history is what everybody reads.
83
+ *
84
+ * As the page narrows: below --container-beside (76rem) the activity goes BELOW
85
+ * the sheet as a card at the sheet's width.
86
+ *
87
+ * Folded, the column is a slim strip with its counts; the pair re-centres and the
88
+ * sheet keeps its width. A container query, not the window: with four panels open
89
+ * each is 380 px on a 1,600 screen.
90
+ *
91
+ * The sheet clips with `overflow-clip`, not `overflow-hidden`: hidden would make it
92
+ * a scroll container and the line table header would stick inside it, not under
93
+ * the application bar.
94
+ * ==================================================================== */
95
+ _jsx("div", { className: "bg-ground px-3 pt-3 pb-3", children: _jsx("div", { className: "@container/page", children: _jsxs("div", { className: `mx-auto flex min-w-0 flex-col gap-3 ${beside
96
+ ? "max-w-(--container-record) @beside/page:flex-row @beside/page:items-stretch @beside/page:justify-center @beside/page:gap-0"
97
+ : "max-w-(--container-sheet)"}`, children: [_jsxs("div", { className: `flex min-w-0 flex-1 flex-col gap-3 ${beside ? "mx-auto w-full max-w-(--container-sheet) @beside/page:mx-0" : ""}`, children: [blocking, _jsxs("div", { className: `flex min-w-0 flex-col overflow-clip rounded-box border border-line bg-surface ${beside ? "@beside/page:rounded-r-none" : ""}`, children: [identity, children] }), activity !== undefined && activityLayout === "below" ? activity : null] }), beside ? (
98
+ /* After the sheet: the record first, what is around it second — in the
99
+ reading order and on screen. */
100
+ _jsx("div", { className: "mx-auto w-full max-w-(--container-sheet) min-w-0 @beside/page:mx-0 @beside/page:flex @beside/page:w-auto @beside/page:max-w-none @beside/page:shrink-0", children: activity })) : null] }) }) })) : (
101
+ /* No paper (a dialog, a drawer, a `Drill` context): one surface, no ground beside
102
+ it — the band first, the record, and the activity below. */
103
+ _jsxs("div", { className: "flex min-w-0 flex-1 flex-col bg-surface", children: [blocking === undefined ? null : (_jsx("div", { className: byDensity(d, "px-4 pt-3.5", "pb-2"), children: blocking })), identity, children, activity === undefined ? null : (_jsx("div", { className: byDensity(d, "px-4 pb-4", "pb-2"), children: activity }))] })), thumb === null ? null : _jsx(ThumbBar, { mode: thumb, dirty: dirty, primaries: primaries })] }));
104
+ }
@@ -0,0 +1,37 @@
1
+ import { type CalloutProps } from "../../primitives/notice/Callout.js";
2
+ import { type ReactNode } from "react";
3
+ export type RecordNoticeSpec = Omit<CalloutProps, "dismissLabel">;
4
+ /** The notices in the order they are drawn: by urgency, written order inside a tone. */
5
+ export declare function orderNotices<T extends {
6
+ tone?: CalloutProps["tone"];
7
+ }>(notices: readonly T[]): T[];
8
+ /** Blocking (`bad`) and the rest, each already in urgency order. */
9
+ export declare function splitNotices(notices: readonly RecordNoticeSpec[]): {
10
+ blocking: RecordNoticeSpec[];
11
+ rest: RecordNoticeSpec[];
12
+ };
13
+ /** The band of what is wrong: the operation error first, then the `bad` notices. */
14
+ export declare function RecordBlocking({ notices, error, errorHeading, }: {
15
+ notices: readonly RecordNoticeSpec[];
16
+ /** The sentence the backend wrote about the last operation. Drawn first, whole. */
17
+ error?: string | null;
18
+ errorHeading?: string;
19
+ }): ReactNode;
20
+ /** The indicator in the rail: the count of the record's notices, toned by the most urgent,
21
+ * that opens their list on press. Nothing is drawn without notices. */
22
+ export declare function RecordNoticeIndicator({ notices, anchored, reveal, }: {
23
+ /** Not blocking, already in urgency order. */
24
+ notices: readonly RecordNoticeSpec[];
25
+ /** The notes of fields and rows: counted too when there is a panel that lists them —
26
+ * without one they are only the marks by their fields, and the popover leaves them out. */
27
+ anchored?: readonly {
28
+ tone?: "bad" | "warn" | "info" | "good";
29
+ }[];
30
+ /** The record has an ACTIVITY panel, and the notices live there: the indicator goes to
31
+ * them (opens the panel, scrolls to its notices, moves the focus) instead of floating
32
+ * a second copy in a popover. `target` is the id of that section. */
33
+ reveal?: {
34
+ target: string;
35
+ onReveal: () => void;
36
+ };
37
+ }): ReactNode;
@@ -0,0 +1,75 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { fillCopy, useUiCopy } from "../../foundations/copy.js";
4
+ import { useSurface } from "../../foundations/surface.js";
5
+ import { focusRing, tv } from "../../foundations/variants.js";
6
+ import { Callout } from "../../primitives/notice/Callout.js";
7
+ import { Failed } from "../../primitives/notice/Notice.js";
8
+ import { Popover } from "../../primitives/overlay/Popover.js";
9
+ import { tagTone } from "../../primitives/tag/Tag.js";
10
+ import { CircleCheck, Info, TriangleAlert } from "lucide-react";
11
+ import { useState } from "react";
12
+ import { Button, Dialog, DialogTrigger, Heading } from "react-aria-components";
13
+ const URGENCY = {
14
+ bad: 0,
15
+ warn: 1,
16
+ info: 2,
17
+ good: 3,
18
+ };
19
+ /** The notices in the order they are drawn: by urgency, written order inside a tone. */
20
+ export function orderNotices(notices) {
21
+ return notices
22
+ .map((notice, at) => ({ notice, at }))
23
+ .sort((a, b) => URGENCY[a.notice.tone ?? "info"] - URGENCY[b.notice.tone ?? "info"] || a.at - b.at)
24
+ .map(({ notice }) => notice);
25
+ }
26
+ /** Blocking (`bad`) and the rest, each already in urgency order. */
27
+ export function splitNotices(notices) {
28
+ const ordered = orderNotices(notices);
29
+ return {
30
+ blocking: ordered.filter((n) => n.tone === "bad"),
31
+ rest: ordered.filter((n) => n.tone !== "bad"),
32
+ };
33
+ }
34
+ /** The band of what is wrong: the operation error first, then the `bad` notices. */
35
+ export function RecordBlocking({ notices, error, errorHeading, }) {
36
+ const copy = useUiCopy();
37
+ const hasError = error !== undefined && error !== null;
38
+ if (notices.length === 0 && !hasError)
39
+ return null;
40
+ return (_jsxs("div", { role: "group", "aria-label": copy.record.blocking, "data-record-blocking": "", className: "flex min-w-0 flex-col gap-1.5", children: [hasError ? (_jsx(Failed, { heading: errorHeading ?? copy.failed.operation, sentence: error })) : null, notices.map(({ children, ...notice }, i) => (_jsx(Callout, { ...notice, dismissLabel: copy.shell.closeNotice, children: children }, i)))] }));
41
+ }
42
+ const ICON = {
43
+ warn: TriangleAlert,
44
+ info: Info,
45
+ good: CircleCheck,
46
+ };
47
+ const chip = tv({
48
+ extend: focusRing,
49
+ base: "inline-flex h-(--size-control-sm) shrink-0 cursor-default items-center gap-1.5 rounded-pill px-2.5 text-micro font-medium whitespace-nowrap transition-[filter] duration-(--duration-instant) hover:brightness-95 aria-expanded:brightness-95",
50
+ });
51
+ /** The indicator in the rail: the count of the record's notices, toned by the most urgent,
52
+ * that opens their list on press. Nothing is drawn without notices. */
53
+ export function RecordNoticeIndicator({ notices, anchored = [], reveal, }) {
54
+ const copy = useUiCopy();
55
+ const surface = useSurface();
56
+ const [open, setOpen] = useState(false);
57
+ const all = reveal === undefined ? notices : [...notices, ...anchored];
58
+ if (all.length === 0)
59
+ return null;
60
+ /* The most urgent of all of them gives the chip its tone; a field's `bad` note warns —
61
+ what blocks the record is the blocking band, not this chip. */
62
+ const order = ["bad", "warn", "info", "good"];
63
+ const worst = order.find((t) => all.some((n) => (n.tone ?? "info") === t)) ?? "info";
64
+ const tone = (worst === "bad" ? "warn" : worst);
65
+ const Icon = ICON[tone];
66
+ const count = fillCopy(all.length === 1 ? copy.record.noticeOne : copy.record.noticeMany, {
67
+ count: all.length.toLocaleString(copy.locale),
68
+ });
69
+ const face = (_jsxs(_Fragment, { children: [_jsx(Icon, { className: "size-(--size-icon-sm) shrink-0", strokeWidth: 2.25, "aria-hidden": true }), count] }));
70
+ const skin = tagTone(tone, surface === "chrome" ? "chrome" : "canvas");
71
+ if (reveal !== undefined) {
72
+ return (_jsx(Button, { "data-record-notices": "", "aria-controls": reveal.target, onPress: reveal.onReveal, className: ({ isFocusVisible }) => `${chip({ isFocusVisible })} ${skin}`, children: face }));
73
+ }
74
+ return (_jsxs(DialogTrigger, { isOpen: open, onOpenChange: setOpen, children: [_jsx(Button, { "data-record-notices": "", className: ({ isFocusVisible }) => `${chip({ isFocusVisible })} ${skin}`, children: face }), _jsx(Popover, { placement: "bottom", children: _jsxs(Dialog, { "aria-label": copy.record.notices, className: "flex max-h-[inherit] w-(--container-toast) flex-col gap-2 overflow-auto p-3 outline-0 max-sm:w-auto", children: [_jsx(Heading, { slot: "title", className: "text-meta font-semibold text-ink", children: copy.record.notices }), notices.map(({ children, ...notice }, i) => (_jsx(Callout, { ...notice, dismissLabel: copy.shell.closeNotice, children: children }, i)))] }) })] }));
75
+ }
@@ -0,0 +1,3 @@
1
+ export declare function RecordSkeleton({ label }: {
2
+ label: string;
3
+ }): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { byDensity, useDensity } from "../../foundations/density.js";
4
+ /* ==================================================================== *
5
+ * LA FICHA, MIENTRAS LLEGA.
6
+ *
7
+ * Factura y cliente contestaban la espera con `Empty`: «Cargando…» en el
8
+ * medio de la página. Eso es la frase de «no hay nada», y el registro sí
9
+ * está — todavía no llegó. La silueta es la de la ficha: el riel, el nombre,
10
+ * los cuatro hechos, las solapas y el cuerpo. Cuando el registro aparece,
11
+ * cae en el mismo lugar.
12
+ *
13
+ * Sin brillo. Un pulso ya quiere decir «este estado está vivo».
14
+ * ==================================================================== */
15
+ const HECHOS = ["w-16", "w-12", "w-20", "w-24"];
16
+ const SOLAPAS = ["w-14", "w-16", "w-20"];
17
+ export function RecordSkeleton({ label }) {
18
+ const d = useDensity();
19
+ const page = d === "comfortable";
20
+ return (_jsxs("article", { "aria-busy": "true", className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "sr-only", children: label }), page ? (_jsxs("div", { "aria-hidden": true, className: "flex items-center gap-3 bg-chrome px-3.5 py-2.5", children: [_jsx("span", { className: "h-3 w-24 rounded-control bg-chrome-ink/15" }), _jsx("span", { className: "h-3 w-40 rounded-control bg-chrome-ink/10" }), _jsx("span", { className: "ml-auto h-(--size-control-sm) w-28 rounded-control bg-chrome-ink/15" })] })) : null, _jsx("div", { "aria-hidden": true, className: page ? "border border-line bg-surface" : "bg-surface", children: _jsxs("div", { className: `flex flex-col ${byDensity(d, "gap-3 px-4 pt-3.5 pb-4", "gap-2 py-2")}`, children: [_jsx("span", { className: "h-5 w-72 max-w-full rounded-control bg-ink/8" }), _jsx("div", { className: "flex flex-wrap gap-x-6 gap-y-2", children: HECHOS.map((width) => (_jsxs("span", { className: "flex flex-col gap-1", children: [_jsx("span", { className: "h-2.5 w-10 rounded-control bg-ink/5" }), _jsx("span", { className: `h-3.5 rounded-control bg-ink/8 ${width}` })] }, width))) }), _jsx("div", { className: "flex gap-4 border-b border-line pb-2", children: SOLAPAS.map((width, i) => (_jsx("span", { className: `h-3 rounded-control ${i === 0 ? "bg-ink/10" : "bg-ink/5"} ${width}` }, width))) }), _jsx("span", { className: "h-24 rounded-box bg-ink/5" })] }) })] }));
21
+ }
@@ -0,0 +1,35 @@
1
+ import type { Surface } from "../../foundations/types.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ import { type ReactNode } from "react";
4
+ /** What hangs off this record, with its number, and can be pressed.
5
+ *
6
+ * It answers TWO questions with a single control: *is there any?* and *how
7
+ * many?*. Without this you have to open it to discover there is nothing — the
8
+ * most expensive click in any interface.
9
+ *
10
+ * **`—` is not `0`.** «I don't know yet» and «there is none» are two different
11
+ * answers, and confusing them makes somebody decide on data that has not
12
+ * arrived yet. */
13
+ export declare function SmartButton({ icon: Icon, count, label, onClick, disabled, surface, layout, }: {
14
+ icon: LucideIcon;
15
+ count?: number;
16
+ label: string;
17
+ onClick?: () => void;
18
+ disabled?: boolean;
19
+ /** By default it inherits it from the surface that wraps it. */
20
+ surface?: Surface;
21
+ /**
22
+ * `stacked` — the figure on top and the label below. For the sheet, where
23
+ * there is height.
24
+ * `inline` — everything on one line. For the rail.
25
+ *
26
+ * Context: in the rail, a two-line box next to breadcrumbs and a one-line
27
+ * pager imposes its height on the whole row, and the other two end up
28
+ * floating in the middle of a band that is twice as tall as them. That was
29
+ * «something looks off».
30
+ */
31
+ layout?: "stacked" | "inline";
32
+ }): import("react").JSX.Element;
33
+ export declare function SmartButtons({ children }: {
34
+ children: ReactNode;
35
+ }): import("react").JSX.Element;
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { bySurface, useSurface } from "../../foundations/surface.js";
5
+ import { Children, createContext, useContext } from "react";
6
+ /* ---------------------------------------------------------------- *
7
+ * THE SMART BUTTONS
8
+ * ---------------------------------------------------------------- */
9
+ /** On the chrome the counters share one bar. Each button reads that and drops
10
+ * its own frame, the way a stage drops its own arrow. */
11
+ const SmartGroup = createContext(false);
12
+ /** What hangs off this record, with its number, and can be pressed.
13
+ *
14
+ * It answers TWO questions with a single control: *is there any?* and *how
15
+ * many?*. Without this you have to open it to discover there is nothing — the
16
+ * most expensive click in any interface.
17
+ *
18
+ * **`—` is not `0`.** «I don't know yet» and «there is none» are two different
19
+ * answers, and confusing them makes somebody decide on data that has not
20
+ * arrived yet. */
21
+ export function SmartButton({ icon: Icon, count, label, onClick, disabled = false, surface, layout = "stacked", }) {
22
+ const copy = useUiCopy();
23
+ const empty = count === 0;
24
+ const activeSurface = useSurface(surface);
25
+ const isInline = layout === "inline";
26
+ const grouped = useContext(SmartGroup);
27
+ return (_jsxs("button", { type: "button", onClick: onClick, disabled: disabled || onClick === undefined, className: `flex shrink-0 items-center gap-2 text-left transition-[background-color,border-color,box-shadow] duration-(--duration-instant) disabled:cursor-default disabled:opacity-60 focus-visible:outline-2 focus-visible:outline-primary ${grouped
28
+ ? `not-disabled:hover:bg-chrome-ink/10 focus-visible:-outline-offset-2 ${isInline ? "h-(--size-control-sm) px-2.5" : "px-2.5 py-1"}`
29
+ : `rounded-control border focus-visible:outline-offset-2 ${isInline ? "h-(--size-control-sm) px-2" : "px-2.5 py-1"} ${bySurface(activeSurface, "border-ink/12 bg-surface shadow-raised not-disabled:hover:border-primary/40 not-disabled:hover:bg-primary/4", "border-chrome-ink/12 bg-chrome-ink/6 not-disabled:hover:border-chrome-link/40 not-disabled:hover:bg-chrome-ink/12")}`}`, children: [_jsx(Icon, { className: `size-(--size-icon-md) shrink-0 ${bySurface(activeSurface, empty ? "text-muted/50" : "text-muted", empty ? "text-chrome-muted/50" : "text-chrome-muted")}`, strokeWidth: 1.75 }), _jsxs("span", { className: `min-w-0 leading-tight ${isInline ? "flex items-baseline gap-1.5" : ""}`, children: [_jsx("span", { className: `text-meta font-medium tabular-nums ${isInline ? "" : "block"} ${bySurface(activeSurface, empty ? "text-muted" : "text-ink", empty ? "text-chrome-muted" : "text-chrome-ink")}`, children: count === undefined ? "—" : count.toLocaleString(copy.locale) }), _jsx("span", { className: `text-micro ${isInline ? "" : "block"} ${bySurface(activeSurface, "text-muted", "text-chrome-muted")}`, children: label })] })] }));
30
+ }
31
+ export function SmartButtons({ children }) {
32
+ const surface = useSurface();
33
+ if (surface !== "chrome") {
34
+ return _jsx("div", { className: "flex flex-wrap items-stretch gap-1.5", children: children });
35
+ }
36
+ const items = Children.toArray(children);
37
+ return (_jsx(SmartGroup.Provider, { value: true, children: _jsx("div", { className: "flex min-w-0 items-stretch overflow-hidden rounded-control border border-chrome-ink/15 bg-chrome-ink/8", children: items.map((child, i) => (_jsx("div", { className: i === 0 ? "flex min-w-0" : "flex min-w-0 border-l border-chrome-ink/20", children: child }, i))) }) }));
38
+ }