@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,116 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { stickyTop, useScrollBox } from "../../foundations/sticky.js";
4
+ import { ArrowDown, ArrowUp, ChevronRight, ChevronsUpDown } from "lucide-react";
5
+ import { createContext, useContext, useId } from "react";
6
+ export function Table({ children,
7
+ /** With a height, the table scrolls on its own and the header sticks.
8
+ * Without a height, the page scrolls and the header goes away. */
9
+ height, }) {
10
+ const caja = useScrollBox(height === undefined ? {} : { height });
11
+ return (
12
+ /* Cuándo esta caja es un contenedor de scroll y cuándo no lo decide
13
+ `useScrollBox`: ahí vive el dilema entero y ahí vive su explicación. Las
14
+ tres tablas del sistema lo resolvían con tres copias de la misma
15
+ medición. */
16
+ _jsx("div", { ref: caja.ref, className: `min-w-0 ${caja.className}`, style: caja.style, children: _jsx("table", { className: "w-full border-separate border-spacing-0 px-1 text-left", children: children }) }));
17
+ }
18
+ export function THead({ children }) {
19
+ return (
20
+ /* Se pega DEBAJO de lo que ya esté pegado —la barra de la aplicación y la
21
+ de la lista— y no en `top: 0`, que lo metía atrás de las dos. Sin alto en
22
+ `Table` el que scrollea es la página, y ahí la diferencia es justamente
23
+ la que hacía desaparecer los nombres de columna. Ver `system/sticky.tsx`. */
24
+ _jsx("thead", { className: "sticky z-10 bg-ground", style: stickyTop, children: _jsx("tr", { children: children }) }));
25
+ }
26
+ /** A column. If it gets a `sortKey`, it can be sorted.
27
+ *
28
+ * The icon is ALWAYS there, dimmed when the column is not the sorted one: an
29
+ * indicator that only shows up once you sort does not say which columns are
30
+ * sortable, and then you have to try them one by one. */
31
+ export function Th({ children, sortKey, sort, onSort, numeric = false, align, width, }) {
32
+ const active = sortKey !== undefined && sort?.key === sortKey;
33
+ const dir = active ? sort.direction : undefined;
34
+ function nextSort() {
35
+ if (sortKey === undefined || onSort === undefined)
36
+ return;
37
+ // asc → desc → unsorted. The third click puts the table back the way it was.
38
+ if (!active)
39
+ onSort({ key: sortKey, direction: "asc" });
40
+ else if (dir === "asc")
41
+ onSort({ key: sortKey, direction: "desc" });
42
+ else
43
+ onSort(null);
44
+ }
45
+ const Icon = dir === "asc" ? ArrowUp : dir === "desc" ? ArrowDown : ChevronsUpDown;
46
+ return (_jsx("th", { scope: "col", style: width === undefined ? undefined : { width }, "aria-sort": active ? (dir === "asc" ? "ascending" : "descending") : "none", className: `border-b border-line-strong px-3 py-2 text-meta font-medium whitespace-nowrap text-muted ${(align ?? (numeric ? "right" : "left")) === "right" ? "text-right" : "text-left"}`, children: sortKey === undefined ? (children) : (_jsxs("button", { type: "button", onClick: nextSort, className: `inline-flex items-center gap-1 rounded-control transition-colors duration-(--duration-instant) hover:text-ink focus-visible:outline-2 focus-visible:outline-offset-1 focus-visible:outline-primary ${(align ?? (numeric ? "right" : "left")) === "right" ? "flex-row-reverse" : ""} ${active ? "text-ink" : ""}`, children: [children, _jsx(Icon, { className: `size-(--size-icon-sm) shrink-0 ${active ? "text-primary" : "text-line-strong"}`, strokeWidth: 2, "aria-hidden": true })] })) }));
47
+ }
48
+ /** The wash of a row lives on the cells, not on the `<tr>`. A background on the
49
+ * row paints edge to edge; on the cells the ends can round and the table's
50
+ * padding keeps the mark off the card. */
51
+ const RowMark = createContext(false);
52
+ const ROW_WASH = "group-hover/row:bg-ink/4";
53
+ const ROW_ON = "bg-primary/8";
54
+ function rowWash(selected) {
55
+ return selected ? ROW_ON : ROW_WASH;
56
+ }
57
+ export function Tr({ children, onClick, selected = false, }) {
58
+ return (_jsx(RowMark.Provider, { value: selected, children: _jsx("tr", { onClick: onClick, className: `group/row ${onClick === undefined ? "" : "cursor-pointer"}`, children: children }) }));
59
+ }
60
+ /**
61
+ * A row that opens its own detail underneath.
62
+ *
63
+ * Context: `DataTable` expands GROUPS —TanStack takes care of that— and that is
64
+ * not enough for the most common case in this product: a column that opens up
65
+ * to show its distribution. That is not a group, it is the detail of ONE row,
66
+ * and every view was writing it by hand with its own `colSpan` and its own
67
+ * chevron.
68
+ *
69
+ * The trigger is the first cell and not a column of chevrons: a whole column
70
+ * for a 12 px control is width that a twelve-column table needs.
71
+ */
72
+ export function TrDetail({ children, detail, open, onToggle, columns, label, selected = false, }) {
73
+ const id = useId();
74
+ return (_jsx(RowMark.Provider, { value: selected, children: _jsxs(_Fragment, { children: [_jsxs("tr", { onClick: onToggle, className: "group/row cursor-pointer", children: [_jsx("td", { className: `border-b border-line py-1.5 pr-0 pl-2.5 align-middle first:rounded-l-control ${rowWash(selected)}`, children: _jsx("button", { type: "button", onClick: (e) => {
75
+ e.stopPropagation();
76
+ onToggle();
77
+ }, "aria-expanded": open, "aria-controls": id, "aria-label": label, className: "inline-flex size-5 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(ChevronRight, { className: `size-(--size-icon-sm) transition-transform duration-(--duration-instant) ${open ? "rotate-90" : ""}`, strokeWidth: 2 }) }) }), children] }), _jsx("tr", { id: id, hidden: !open, children: _jsx("td", { colSpan: columns + 1, className: "border-b border-line bg-ground px-3 py-3", children: detail }) })] }) }));
78
+ }
79
+ /** The background of a cell, by tone. It replaces the row wash on that cell:
80
+ * a hovered row and a painted cell are two signals, and stacking them reads
81
+ * as a third color. */
82
+ export const CELL_TONE = {
83
+ neutral: "bg-ink/6",
84
+ info: "bg-info/12",
85
+ good: "bg-good/12",
86
+ warn: "bg-warn/15",
87
+ bad: "bg-bad/12",
88
+ accent: "bg-accent/20",
89
+ };
90
+ export function Td({ children, numeric = false, align,
91
+ /** A CODE — an id, a hash, a key — set in the monospaced face. Off by default: a
92
+ * number does not need it (a numeric cell gets tabular figures on its own), and a
93
+ * name in mono reads like a log, not like a report. */
94
+ mono = false,
95
+ /** Paints the whole cell. For what the eye has to find without reading: an
96
+ * imputed value, one out of range, a row that changed.
97
+ *
98
+ * Context: it is the CELL and not the text. Tinting the text says «this
99
+ * number is of another kind»; tinting the cell says «look here», which is
100
+ * something else — and in a table of forty rows by six columns, colored text
101
+ * gets lost. */
102
+ tone, title, }) {
103
+ const selected = useContext(RowMark);
104
+ return (_jsx("td", { title: title, className: `border-b border-line px-3 py-1.5 text-meta text-ink first:rounded-l-control last:rounded-r-control ${(align ?? (numeric ? "right" : "left")) === "right" ? "text-right" : "text-left"} ${mono ? "font-mono tabular-nums" : numeric ? "tabular-nums" : ""} ${tone === undefined ? rowWash(selected) : CELL_TONE[tone]}`, children: children }));
105
+ }
106
+ /** The selection checkbox, with its third state.
107
+ *
108
+ * «some» is not an ornament: without it, the header checkbox of a table with
109
+ * three of forty rows selected lies in both directions — empty says «none»
110
+ * and filled says «all». */
111
+ export function SelectBox({ checked, indeterminate = false, onChange, label, }) {
112
+ return (_jsx("input", { type: "checkbox", checked: checked, "aria-label": label, ref: (el) => {
113
+ if (el !== null)
114
+ el.indeterminate = indeterminate && !checked;
115
+ }, onChange: (e) => onChange?.(e.target.checked), onClick: (e) => e.stopPropagation(), className: "size-3.5 shrink-0 cursor-pointer accent-primary" }));
116
+ }
@@ -0,0 +1,7 @@
1
+ export { type Aggregate, type DataColumn, DataTable, type DataTableCreate, type GroupColumn, } from "./DataTable.js";
2
+ export { EditableTable, type EditAdd, type EditBands, type EditColumn, type EditLayout, sectionLines, type StackPlace, stackPlaces, type TabTarget, tabTarget, } from "./EditableTable.js";
3
+ export { PivotTable, type PivotTableProps, } from "./PivotTable.js";
4
+ export { type ReportColumn, type ReportRow, type ReportRowKind, ReportTable, } from "./ReportTable.js";
5
+ export { type RowAction, RowActions } from "./RowActions.js";
6
+ export { type SheetColumn, SheetTable } from "./SheetTable.js";
7
+ export { CELL_TONE, type SortDirection, type SortState, Table, Td, Th, THead, Tr, } from "./Table.js";
@@ -0,0 +1,9 @@
1
+ /* The presentation (`Table`) is ours and is used on its own; `DataTable` is the
2
+ wiring to TanStack for grouping and sorting. See the note in `DataTable.tsx`. */
3
+ export { DataTable, } from "./DataTable.js";
4
+ export { EditableTable, sectionLines, stackPlaces, tabTarget, } from "./EditableTable.js";
5
+ export { PivotTable, } from "./PivotTable.js";
6
+ export { ReportTable, } from "./ReportTable.js";
7
+ export { RowActions } from "./RowActions.js";
8
+ export { SheetTable } from "./SheetTable.js";
9
+ export { CELL_TONE, Table, Td, Th, THead, Tr, } from "./Table.js";
@@ -0,0 +1,91 @@
1
+ import type { Tone } from "../../foundations/types.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ /**
4
+ * UNA TIRA DE PESTAÑAS. La misma en todo el sistema.
5
+ *
6
+ * Context: esto vivía adentro del encabezado del registro y se llamaba
7
+ * `Aspects`, así que la actividad —que necesita exactamente lo mismo— no podía
8
+ * usarlo: un contexto no importa de otro contexto. El resultado era una tira de
9
+ * pestañas de segunda mano, con el activo marcado de otra manera, en la misma
10
+ * pantalla que la buena.
11
+ *
12
+ * Es una primitiva porque no sabe qué está separando: las caras de un registro,
13
+ * las dos fuentes de una historia, o lo que venga.
14
+ *
15
+ * ── Por qué el activo es una PESTAÑA de carpeta ─────────────────────
16
+ *
17
+ * Y no un subrayado. Un subrayado dice «esto está elegido»; una pestaña que sale
18
+ * del borde y tapa la línea dice «esto está ABIERTO y lo de abajo es su
19
+ * contenido», que es la relación real. La diferencia importa cuando hay tres y
20
+ * el contenido de abajo no cambia de forma al cambiar de una a otra.
21
+ *
22
+ * ── El contador apagado en cero ─────────────────────────────────────
23
+ *
24
+ * Un cero se dibuja, y apagado. Esconderlo obliga a abrir la pestaña para
25
+ * descubrir que está vacía; pintarlo como los demás la hace parecer que tiene
26
+ * algo. «No hay ninguna» es información y se dice.
27
+ *
28
+ * ── El contador y la NOTIFICACIÓN son dos cosas ─────────────────────
29
+ *
30
+ * El contador dice CUÁNTAS cosas hay detrás, y es neutro: «pagos 0» es una
31
+ * respuesta tan válida como «pagos 5». La notificación dice que algo de ahí
32
+ * PIDE que lo mires —falló, cambió, llegó— y por eso tiene color y por eso un
33
+ * cero no se dibuja: no hay nada que pedir.
34
+ *
35
+ * Confundirlos se ve así: una pestaña con «3» al lado y nadie sabe si son tres
36
+ * cosas o tres problemas. Son dos marcas porque son dos preguntas.
37
+ */
38
+ /**
39
+ * ALGO DE ESTA PESTAÑA PIDE ATENCIÓN.
40
+ *
41
+ * No es un conteo: un conteo dice cuántas cosas hay y esto dice que hay algo que
42
+ * mirar. Por eso tiene color, por eso no se dibuja en cero, y por eso puede
43
+ * convivir con el contador — «pagos 4 ⬤» es cuatro cosas, una de ellas
44
+ * rechazada.
45
+ */
46
+ export type TabNotice = {
47
+ /**
48
+ * Qué es lo que pide atención, en la voz del producto: «3 sin leer», «la
49
+ * factura no se pudo enviar».
50
+ *
51
+ * Obligatorio, y por la misma razón que en `StatusDot`: un punto de color sin
52
+ * su palabra al lado obliga a aprender una leyenda que no está escrita en
53
+ * ningún lado, y no le dice nada a quien no distingue el rojo del verde. Acá
54
+ * la marca acelera el barrido y la palabra informa — se lee al pasar por
55
+ * encima y la lee un lector de pantalla.
56
+ */
57
+ label: string;
58
+ /** Cuántas cosas lo piden. Sin esto es un punto, que es lo que se pone cuando
59
+ * llegó algo nuevo y contarlo no agrega nada. Un `0` no dibuja nada: no hay
60
+ * nada que pedir. */
61
+ count?: number;
62
+ /** De qué clase: `bad` algo falló, `warn` algo avisa, `accent` o `info` algo
63
+ * llegó. Sin esto, `bad` — que es el caso por el que esto existe. */
64
+ tone?: Tone;
65
+ };
66
+ export type TabItem = {
67
+ id: string;
68
+ label: string;
69
+ /** La marca de la pestaña. Con tres o más, el ícono es lo que se reconoce sin
70
+ * leer — y en una tira angosta es lo único que sobrevive al truncado. */
71
+ icon?: LucideIcon;
72
+ /** Cuántas cosas hay detrás. `undefined` = no se cuenta; `0` se dibuja
73
+ * apagado, que es distinto de no contar. */
74
+ count?: number;
75
+ /** Que algo de acá pide que lo mires. Aparte del contador: ver `TabNotice`. */
76
+ notice?: TabNotice;
77
+ };
78
+ /**
79
+ * The strip only: what each tab opens is drawn by whoever uses it (`Record`, `Activity`).
80
+ *
81
+ * Built on React Aria's `Tabs`, so the strip is a real tab list for the keyboard: the
82
+ * arrows move between tabs and select them, Home/End jump to the ends, and only the open
83
+ * tab is in the Tab order. The hand-written version had the roles and none of the keys.
84
+ */
85
+ export declare function Tabs({ tabs, current, onSelect, label, }: {
86
+ tabs: readonly TabItem[];
87
+ current: string;
88
+ onSelect?: (id: string) => void;
89
+ /** El rótulo accesible de la tira. Sin esto, «las caras del registro». */
90
+ label?: string;
91
+ }): import("react").JSX.Element;
@@ -0,0 +1,56 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useUiCopy } from "../../foundations/copy.js";
4
+ import { Tab, TabList, Tabs as AriaTabs } from "react-aria-components";
5
+ /* El color de la notificación. Estático y en una tabla porque Tailwind no puede
6
+ componer una clase con un pedazo variable: `bg-${tone}/12` no existe en la
7
+ hoja. */
8
+ const NOTICE_TONE = {
9
+ neutral: "bg-ink/8 text-muted",
10
+ info: "bg-info/12 text-info",
11
+ good: "bg-good/12 text-good",
12
+ warn: "bg-warn/14 text-warn",
13
+ bad: "bg-bad/12 text-bad",
14
+ accent: "bg-accent/14 text-accent",
15
+ };
16
+ const NOTICE_DOT = {
17
+ neutral: "bg-muted",
18
+ info: "bg-info",
19
+ good: "bg-good",
20
+ warn: "bg-warn",
21
+ bad: "bg-bad",
22
+ accent: "bg-accent",
23
+ };
24
+ /**
25
+ * The strip only: what each tab opens is drawn by whoever uses it (`Record`, `Activity`).
26
+ *
27
+ * Built on React Aria's `Tabs`, so the strip is a real tab list for the keyboard: the
28
+ * arrows move between tabs and select them, Home/End jump to the ends, and only the open
29
+ * tab is in the Tab order. The hand-written version had the roles and none of the keys.
30
+ */
31
+ export function Tabs({ tabs, current, onSelect, label, }) {
32
+ const copy = useUiCopy();
33
+ return (_jsx(AriaTabs, { selectedKey: current, onSelectionChange: (key) => onSelect?.(String(key)), children: _jsx(TabList, { "aria-label": label ?? copy.record.faces, className: "flex min-w-0 flex-wrap items-end gap-1", children: tabs.map((a) => {
34
+ const Icon = a.icon;
35
+ const empty = a.count === 0;
36
+ /* Un cero no pide nada, así que no se dibuja. Es la diferencia con el
37
+ contador, donde «0» es una respuesta. */
38
+ const notice = a.notice?.count === 0 ? undefined : a.notice;
39
+ return (_jsx(Tab, { id: a.id, ...(notice === undefined
40
+ ? {}
41
+ : { "aria-label": `${a.label} · ${notice.label}` }), className: ({ isSelected }) => `inline-flex shrink-0 cursor-default items-center px-3 py-2 text-meta outline-0 transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:-outline-offset-2 focus-visible:outline-primary ${isSelected
42
+ ? "-mb-px rounded-t-control border border-b-0 border-line-strong bg-surface font-medium text-ink"
43
+ : "border border-transparent text-muted hover:text-ink"}`, children: ({ isSelected }) => (
44
+ /* The `title` goes on the content and not on the tab: React Aria does not
45
+ forward it, and on the content the native tooltip still covers the
46
+ whole tab. */
47
+ _jsxs("span", { className: "inline-flex items-center gap-1.5", ...(notice === undefined ? {} : { title: notice.label }), children: [Icon === undefined ? null : (_jsx(Icon, { className: `size-(--size-icon-sm) shrink-0 ${isSelected ? "text-ink" : "text-muted"}`, strokeWidth: 2, "aria-hidden": true })), a.label, a.count === undefined ? null : (_jsx("span", { className: `rounded-pill px-1.5 text-micro tabular-nums ${empty
48
+ ? "bg-ink/5 text-muted/70"
49
+ : isSelected
50
+ ? "bg-ink/8 text-ink"
51
+ : "bg-ink/8 text-muted"}`, children: a.count.toLocaleString(copy.locale) })), notice === undefined ? null : notice.count === undefined ? (
52
+ /* Sin número, un punto. La palabra no se dibuja pero está: en el
53
+ `title` y en el nombre accesible de la pestaña. */
54
+ _jsx("span", { className: `size-1.5 shrink-0 rounded-pill ${NOTICE_DOT[notice.tone ?? "bad"]}`, "aria-hidden": true })) : (_jsx("span", { className: `rounded-pill px-1.5 text-micro font-medium tabular-nums ${NOTICE_TONE[notice.tone ?? "bad"]}`, "aria-hidden": true, children: notice.count.toLocaleString(copy.locale) }))] })) }, a.id));
55
+ }) }) }));
56
+ }
@@ -0,0 +1 @@
1
+ export { type TabItem, type TabNotice, Tabs } from "./Tabs.js";
@@ -0,0 +1 @@
1
+ export { Tabs } from "./Tabs.js";
@@ -0,0 +1,36 @@
1
+ import type { Tone } from "../../foundations/types.js";
2
+ import type { ReactNode } from "react";
3
+ export type { Tone };
4
+ /** The classes of a tone on a surface — the capsule's colors, shared with the chips of
5
+ * `Tags` so a classification reads the same as a value and as a removable chip. */
6
+ export declare function tagTone(tone: Tone, surface: "canvas" | "chrome"): string;
7
+ export declare function Tag({ children, tone, surface,
8
+ /** Context: a one-word classification sometimes needs a sentence that does not
9
+ * fit. The sentence goes into the `title` and not underneath, because the
10
+ * line below is taken up by the row's metadata. */
11
+ title, }: {
12
+ children: ReactNode;
13
+ tone?: Tone;
14
+ surface?: "canvas" | "chrome";
15
+ title?: string;
16
+ }): import("react").JSX.Element;
17
+ /** The status dot.
18
+ *
19
+ * **It never goes alone.** A colored dot without its word next to it forces
20
+ * you to learn a legend that is written down nowhere, and it says nothing to
21
+ * someone who cannot tell red from green. The dot speeds up the scanning of a
22
+ * column; the word is what informs. That is why `label` is mandatory and gets
23
+ * drawn — if it needs to be hidden, it is hidden with `aria` and not by
24
+ * deleting it.
25
+ *
26
+ * `pulse` for what is ALIVE: a bank statement still importing. It is the only animation in
27
+ * the system, and it exists because «importando» and «importado» differ by one
28
+ * letter and that is far too little for a state that changes on its own. */
29
+ export declare function StatusDot({ tone, label, pulse, surface, }: {
30
+ tone?: Tone;
31
+ /** ReactNode and not `string`: the label is visible text, and sometimes it
32
+ * carries a figure or a name already formatted by whoever composes. */
33
+ label: ReactNode;
34
+ pulse?: boolean;
35
+ surface?: "canvas" | "chrome";
36
+ }): import("react").JSX.Element;
@@ -0,0 +1,68 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /** Each tone on its two surfaces. The state does not change meaning when
3
+ * crossing from the canvas to the chrome, but its value does: `good` #2F6B3C
4
+ * gives 1.7:1 on #1C1A17 and the lightened one has to be used. */
5
+ const TAG_TONE = {
6
+ neutral: {
7
+ canvas: "border border-line-strong bg-ink/5 text-ink",
8
+ chrome: "border border-chrome-muted/30 bg-chrome-ink/10 text-chrome-ink",
9
+ },
10
+ info: {
11
+ canvas: "border border-info/25 bg-info/10 text-info",
12
+ chrome: "border border-chrome-link/30 bg-chrome-link/15 text-chrome-link",
13
+ },
14
+ good: {
15
+ canvas: "border border-good/25 bg-good/10 text-good",
16
+ chrome: "border border-chrome-good/30 bg-chrome-good/15 text-chrome-good",
17
+ },
18
+ warn: {
19
+ canvas: "border border-warn/30 bg-warn/10 text-warn",
20
+ chrome: "border border-chrome-warn/35 bg-chrome-warn/15 text-chrome-warn",
21
+ },
22
+ bad: {
23
+ canvas: "border border-bad/25 bg-bad/10 text-bad",
24
+ chrome: "border border-chrome-bad/35 bg-chrome-bad/15 text-chrome-bad",
25
+ },
26
+ accent: {
27
+ // The gold as TEXT only exists on the chrome (7.39:1). On the canvas it
28
+ // gives 2.43:1, so there this tag falls back to ink on a gold wash: the
29
+ // brand is visible and the word is readable.
30
+ canvas: "border border-accent/40 bg-accent/18 text-ink",
31
+ chrome: "border border-chrome-accent/40 bg-chrome-accent/15 text-chrome-accent",
32
+ },
33
+ };
34
+ /** The classes of a tone on a surface — the capsule's colors, shared with the chips of
35
+ * `Tags` so a classification reads the same as a value and as a removable chip. */
36
+ export function tagTone(tone, surface) {
37
+ return TAG_TONE[tone][surface];
38
+ }
39
+ export function Tag({ children, tone = "neutral", surface = "canvas",
40
+ /** Context: a one-word classification sometimes needs a sentence that does not
41
+ * fit. The sentence goes into the `title` and not underneath, because the
42
+ * line below is taken up by the row's metadata. */
43
+ title, }) {
44
+ return (_jsx("span", { title: title, className: `inline-flex shrink-0 items-center rounded-pill px-2 py-0.5 text-micro font-medium whitespace-nowrap ${TAG_TONE[tone][surface]}`, children: children }));
45
+ }
46
+ const DOT_TONE = {
47
+ neutral: { canvas: "bg-muted", chrome: "bg-chrome-muted" },
48
+ info: { canvas: "bg-info", chrome: "bg-chrome-link" },
49
+ good: { canvas: "bg-good", chrome: "bg-chrome-good" },
50
+ warn: { canvas: "bg-warn", chrome: "bg-chrome-warn" },
51
+ bad: { canvas: "bg-bad", chrome: "bg-chrome-bad" },
52
+ accent: { canvas: "bg-accent", chrome: "bg-chrome-accent" },
53
+ };
54
+ /** The status dot.
55
+ *
56
+ * **It never goes alone.** A colored dot without its word next to it forces
57
+ * you to learn a legend that is written down nowhere, and it says nothing to
58
+ * someone who cannot tell red from green. The dot speeds up the scanning of a
59
+ * column; the word is what informs. That is why `label` is mandatory and gets
60
+ * drawn — if it needs to be hidden, it is hidden with `aria` and not by
61
+ * deleting it.
62
+ *
63
+ * `pulse` for what is ALIVE: a bank statement still importing. It is the only animation in
64
+ * the system, and it exists because «importando» and «importado» differ by one
65
+ * letter and that is far too little for a state that changes on its own. */
66
+ export function StatusDot({ tone = "neutral", label, pulse = false, surface = "canvas", }) {
67
+ return (_jsxs("span", { className: "inline-flex shrink-0 items-center gap-1.5 whitespace-nowrap", children: [_jsxs("span", { className: "relative flex size-1.5 shrink-0", children: [pulse ? (_jsx("span", { className: `absolute inline-flex size-full animate-ping rounded-pill opacity-70 ${DOT_TONE[tone][surface]}` })) : null, _jsx("span", { className: `relative inline-flex size-1.5 rounded-pill ${DOT_TONE[tone][surface]}` })] }), _jsx("span", { className: `text-micro ${surface === "chrome" ? "text-chrome-muted" : "text-muted"}`, children: label })] }));
68
+ }
@@ -0,0 +1,57 @@
1
+ import type { Surface, Tone } from "../../foundations/types.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ export type TagItem = {
4
+ id: string;
5
+ label: string;
6
+ /** What clarifies it without having to read it whole: which table a field
7
+ * comes from, which symbol a comparison is. It goes muted and inside. */
8
+ note?: string;
9
+ icon?: LucideIcon;
10
+ /** Choose it. Without this the chip is a value and is not pressed. */
11
+ onSelect?: () => void;
12
+ /** Remove it, when removing it means something. It is a separate button
13
+ * inside the chip — and Backspace/Delete on the focused chip — so removing
14
+ * is not the same as choosing. */
15
+ onRemove?: () => void;
16
+ /** The accessible name of the × . React Aria already names it «Remove» +
17
+ * the chip; this replaces the verb when the chip needs a more exact one
18
+ * («quitar el filtro»). */
19
+ removeLabel?: string;
20
+ /** It is chosen right now. */
21
+ selected?: boolean;
22
+ /** It is MARKED by the keyboard of someone else — the search input that
23
+ * drives a list of suggestions — which is different from chosen: marked is
24
+ * «Enter takes this one», chosen is «this one is already set». */
25
+ active?: boolean;
26
+ /** What the chip MEANS, the same tones as `Tag`: a customer category «crédito vencido»
27
+ * in `bad`. Neutral by default — most tags classify, they do not warn. Chosen and
28
+ * marked chips keep their own paint: that state is what the strip is saying then. */
29
+ tone?: Tone;
30
+ title?: string;
31
+ };
32
+ export declare function Tags({ items, surface, label, labelledBy, stack, multiple, }: {
33
+ items: readonly TagItem[];
34
+ /** By default it inherits the surface that wraps it. */
35
+ surface?: Surface;
36
+ /** The accessible name of the strip. A strip of values is read as text and
37
+ * does not need one; a strip that can be pressed or removed is a control,
38
+ * and a control without a name is announced as «grid» and nothing else. */
39
+ label?: string;
40
+ /** The id of a visible label that names the strip — the label of the `Field` it lives
41
+ * in. It wins over `label`. */
42
+ labelledBy?: string;
43
+ /**
44
+ * One below the other, and not on a line that wraps.
45
+ *
46
+ * The difference is not aesthetic: a strip ACCOMPANIES —they are the parts of
47
+ * something being written, and are read together— and a list IS CHOSEN FROM,
48
+ * and there the eye goes down a column and so does the keyboard. Twelve
49
+ * attributes wrapped in three lines are twelve things to sweep in a zigzag to
50
+ * find one.
51
+ */
52
+ stack?: boolean;
53
+ /** When chips carry `selected`, more than one can be chosen at a time. By
54
+ * default the choice is ONE — a join, a grain, a period — and the strip is
55
+ * announced as single-select. */
56
+ multiple?: boolean;
57
+ }): import("react").JSX.Element | null;
@@ -0,0 +1,104 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { AriaLocale } from "../../foundations/aria.js";
4
+ import { bySurface, useSurface } from "../../foundations/surface.js";
5
+ import { cn, focusRing } from "../../foundations/variants.js";
6
+ import { tagTone } from "./Tag.js";
7
+ import { X } from "lucide-react";
8
+ import { Button as AriaButton, Tag as AriaTag, TagGroup, TagList, } from "react-aria-components";
9
+ export function Tags({ items, surface, label, labelledBy, stack = false, multiple = false, }) {
10
+ const sup = useSurface(surface);
11
+ if (items.length === 0)
12
+ return null;
13
+ const interactive = items.some((i) => i.onSelect !== undefined || i.onRemove !== undefined);
14
+ if (!interactive)
15
+ return _jsx(StillStrip, { items: items, surface: sup, label: label, stack: stack });
16
+ return (_jsx(AriaLocale, { children: _jsx(ChipGroup, { items: items, surface: sup, label: label, labelledBy: labelledBy, stack: stack, multiple: multiple }) }));
17
+ }
18
+ const STRIP = "flex min-w-0 flex-wrap items-center gap-1";
19
+ const STACK = "flex min-w-0 flex-col items-stretch gap-0.5";
20
+ /** A strip nobody presses: text in a row, a list only when it is named. */
21
+ function StillStrip({ items, surface, label, stack, }) {
22
+ const listed = label !== undefined;
23
+ return (_jsx("span", { className: stack ? STACK : STRIP, ...(listed ? { role: "list", "aria-label": label } : {}), children: items.map((item) => (_jsx("span", { ...(listed ? { role: "listitem" } : {}), ...(item.title === undefined ? {} : { title: item.title }), className: shell({ item, surface, stack, removable: false, pressable: false }), children: _jsx(Inside, { item: item }) }, item.id))) }));
24
+ }
25
+ function ChipGroup({ items, surface, label, labelledBy, stack, multiple, }) {
26
+ const byId = new Map(items.map((item) => [item.id, item]));
27
+ const removable = items.some((i) => i.onRemove !== undefined);
28
+ /* Selection is React Aria's only when the chips SAY whether they are chosen: then the
29
+ strip is announced as a selectable list and each chip as selected or not. Chips that
30
+ only act (`onSelect` without `selected`) are actions, and pressing one does not leave
31
+ anything «selected» behind. */
32
+ const selecting = items.some((i) => i.selected !== undefined && i.onSelect !== undefined);
33
+ const chosen = new Set(items.filter((i) => i.selected === true).map((i) => i.id));
34
+ /* The choice is the consumer's: the strip reports which chip was pressed and waits for the
35
+ next `selected`. In a single choice the chip that ARRIVES is the one pressed; pressing
36
+ the chosen one again reports that one, the way a radio reports the click on itself. */
37
+ const onSelectionChange = (next) => {
38
+ if (next === "all")
39
+ return;
40
+ const keys = new Set([...next].map(String));
41
+ const added = [...keys].filter((k) => !chosen.has(k));
42
+ const dropped = [...chosen].filter((k) => !keys.has(k));
43
+ const pressed = multiple ? [...added, ...dropped] : added.length > 0 ? added : dropped;
44
+ for (const key of pressed)
45
+ byId.get(key)?.onSelect?.();
46
+ };
47
+ /* Backspace on a chip removes it — and only the chips that can be removed: a chip without
48
+ `onRemove` in a strip that has some is still a value. */
49
+ const onRemove = (keys) => {
50
+ for (const key of keys)
51
+ byId.get(String(key))?.onRemove?.();
52
+ };
53
+ return (_jsx(TagGroup
54
+ /* The name falls back to the chips themselves: «cliente: Andina, estado: vencida» says
55
+ what the strip holds, which is better than an anonymous grid. */
56
+ , { ...(labelledBy === undefined
57
+ ? { "aria-label": label ?? items.map((i) => i.label).join(", ") }
58
+ : { "aria-labelledby": labelledBy }), ...(removable ? { onRemove } : {}), ...(selecting
59
+ ? {
60
+ selectionMode: multiple ? "multiple" : "single",
61
+ selectedKeys: chosen,
62
+ onSelectionChange,
63
+ }
64
+ : {}),
65
+ /* Escape belongs to whatever holds the strip — the popover it lives in closes with it —
66
+ and not to «clear the selection», which would erase the choice without a word. */
67
+ escapeKeyBehavior: "none", className: "min-w-0", children: _jsx(TagList, { items: items, className: stack ? STACK : STRIP, children: (item) => (_jsxs(AriaTag, { id: item.id, textValue: item.note === undefined ? item.label : `${item.label} ${item.note}`, ...(!selecting && item.onSelect !== undefined
68
+ ? { onAction: item.onSelect }
69
+ : {}), ...(item.title === undefined ? {} : { title: item.title }), className: ({ isFocusVisible, isHovered }) => cn(shell({
70
+ item,
71
+ surface,
72
+ stack,
73
+ removable: item.onRemove !== undefined,
74
+ pressable: item.onSelect !== undefined,
75
+ }), item.onSelect !== undefined && isHovered && item.active !== true
76
+ ? bySurface(surface, "border-ink/40", "border-chrome-ink/40")
77
+ : "", "outline-offset-1", focusRing({ isFocusVisible })), children: [_jsx(Inside, { item: item }), item.onRemove === undefined ? null : _jsx(Remove, { item: item, surface: surface })] })) }) }));
78
+ }
79
+ function paint(item, surface) {
80
+ if (item.active === true) {
81
+ return bySurface(surface, "border border-primary bg-primary text-surface", "border border-chrome-link bg-chrome-link text-chrome");
82
+ }
83
+ if (item.selected === true) {
84
+ return bySurface(surface, "border border-accent/40 bg-accent/18 text-ink", "border border-chrome-link/40 bg-chrome-link/25 text-chrome-ink");
85
+ }
86
+ if (item.tone !== undefined && item.tone !== "neutral")
87
+ return tagTone(item.tone, surface);
88
+ return bySurface(surface, "border border-line-strong bg-ink/5 text-ink", "border border-chrome-muted/30 bg-chrome-ink/12 text-chrome-ink");
89
+ }
90
+ /* Stacked, each chip takes its whole line: a column of chips of different widths does not
91
+ read as a list, it reads as a mess — and the blank on the right is what lets it be
92
+ pressed without aiming. */
93
+ function shell({ item, surface, stack, removable, pressable, }) {
94
+ return cn(stack ? "flex w-full justify-start" : "inline-flex shrink-0", "min-w-0 items-center gap-1 rounded-pill py-0.5 text-micro transition-colors duration-(--duration-instant)", paint(item, surface), removable ? "pr-0.5 pl-2" : "px-2", pressable ? "cursor-pointer" : "cursor-default");
95
+ }
96
+ function Inside({ item }) {
97
+ const Icon = item.icon;
98
+ return (_jsxs(_Fragment, { children: [Icon === undefined ? null : (_jsx(Icon, { className: "size-3 shrink-0 opacity-70", strokeWidth: 2, "aria-hidden": true })), _jsx("span", { className: "min-w-0 truncate", children: item.label }), item.note === undefined ? null : (_jsxs("span", { className: "shrink-0 opacity-60", children: ["\u00B7 ", item.note] }))] }));
99
+ }
100
+ /* The × is React Aria's `remove` slot: it removes THIS chip and never presses it, so
101
+ removing a pressable chip does not also choose it. */
102
+ function Remove({ item, surface }) {
103
+ return (_jsx(AriaButton, { slot: "remove", ...(item.removeLabel === undefined ? {} : { "aria-label": item.removeLabel }), className: ({ isFocusVisible }) => cn("inline-flex size-4 shrink-0 cursor-default items-center justify-center rounded-pill transition-colors duration-(--duration-instant)", bySurface(surface, "hover:bg-ink/15 pressed:bg-ink/20", "hover:bg-chrome-ink/25 pressed:bg-chrome-ink/30"), focusRing({ isFocusVisible })), children: _jsx(X, { className: "size-3", strokeWidth: 2.5, "aria-hidden": true }) }));
104
+ }
@@ -0,0 +1,35 @@
1
+ import { type FormMode } from "../field/mode.js";
2
+ import { type TagOption } from "./choices.js";
3
+ export type TagsFieldProps = {
4
+ /** The ids of the chosen tags, in the order they were added. */
5
+ value: readonly string[];
6
+ onChange?: (next: string[]) => void;
7
+ /** Every tag that can be put on the record — chosen ones included, for their labels. */
8
+ options: readonly TagOption[];
9
+ /**
10
+ * Create a tag from the typed text and return its new id (or a promise of it); the field
11
+ * then adds it. Without it there is no «create» row — the catalog is closed.
12
+ *
13
+ * Context: the id is returned and not invented here because the record is created by the
14
+ * backend (Odoo's `name_create`); the consumer also appends the new option to `options`.
15
+ */
16
+ onCreate?: (label: string) => string | Promise<string>;
17
+ /** The input's placeholder while no tag is chosen. Defaults to the copy table's «Add…». */
18
+ placeholder?: string;
19
+ disabled?: boolean;
20
+ id?: string;
21
+ describedBy?: string;
22
+ invalid?: boolean;
23
+ /** The name of the control when it is NOT inside a `Field` — a cell of an editable table.
24
+ * Inside a field, the field's label names it. */
25
+ "aria-label"?: string;
26
+ /** Forces the mode. By default it inherits the form's: in read mode it draws `Tags`. */
27
+ mode?: FormMode;
28
+ };
29
+ /**
30
+ * A many2many as removable chips plus an inline combobox that adds one more.
31
+ *
32
+ * Works inside `<Field label=…>`: the field's label names both the input and the strip of
33
+ * chips, and its hint and error describe the input. In read mode it is `Tags`.
34
+ */
35
+ export declare function TagsField({ mode, ...props }: TagsFieldProps): import("react").JSX.Element;