@sincpro/web-ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -2
  3. package/dist/components/assist/AssistMark.d.ts +64 -0
  4. package/dist/components/assist/AssistMark.js +108 -0
  5. package/dist/components/assist/AssistPending.d.ts +24 -0
  6. package/dist/components/assist/AssistPending.js +26 -0
  7. package/dist/components/assist/AssistSuggestion.d.ts +42 -0
  8. package/dist/components/assist/AssistSuggestion.js +55 -0
  9. package/dist/components/assist/ChangeReview.d.ts +56 -0
  10. package/dist/components/assist/ChangeReview.js +121 -0
  11. package/dist/components/assist/assist.d.ts +77 -0
  12. package/dist/components/assist/assist.js +87 -0
  13. package/dist/components/assist/index.d.ts +5 -0
  14. package/dist/components/assist/index.js +7 -0
  15. package/dist/components/chatter/Chatter.d.ts +161 -0
  16. package/dist/components/chatter/Chatter.js +232 -0
  17. package/dist/components/chatter/Messages.d.ts +66 -0
  18. package/dist/components/chatter/Messages.js +43 -0
  19. package/dist/components/chatter/Timeline.d.ts +21 -0
  20. package/dist/components/chatter/Timeline.js +30 -0
  21. package/dist/components/chatter/index.d.ts +3 -0
  22. package/dist/components/chatter/index.js +4 -0
  23. package/dist/components/form/Form.d.ts +52 -0
  24. package/dist/components/form/Form.js +49 -0
  25. package/dist/components/form/Sheet.d.ts +43 -0
  26. package/dist/components/form/Sheet.js +83 -0
  27. package/dist/components/form/index.d.ts +2 -0
  28. package/dist/components/form/index.js +4 -0
  29. package/dist/components/frame/Chart.d.ts +13 -0
  30. package/dist/components/frame/Chart.js +24 -0
  31. package/dist/components/frame/FrameView.d.ts +33 -0
  32. package/dist/components/frame/FrameView.js +125 -0
  33. package/dist/components/frame/index.d.ts +3 -0
  34. package/dist/components/frame/index.js +3 -0
  35. package/dist/components/frame/model.d.ts +43 -0
  36. package/dist/components/frame/model.js +101 -0
  37. package/dist/components/index.d.ts +10 -0
  38. package/dist/components/index.js +11 -0
  39. package/dist/components/lines/LineItems.d.ts +74 -0
  40. package/dist/components/lines/LineItems.js +229 -0
  41. package/dist/components/lines/index.d.ts +1 -0
  42. package/dist/components/lines/index.js +2 -0
  43. package/dist/components/lines/lines.d.ts +23 -0
  44. package/dist/components/lines/lines.js +26 -0
  45. package/dist/components/listing/ColumnsMenu.d.ts +20 -0
  46. package/dist/components/listing/ColumnsMenu.js +46 -0
  47. package/dist/components/listing/GroupingLevels.d.ts +35 -0
  48. package/dist/components/listing/GroupingLevels.js +45 -0
  49. package/dist/components/listing/Listing.d.ts +27 -0
  50. package/dist/components/listing/Listing.js +86 -0
  51. package/dist/components/listing/ListingBar.d.ts +73 -0
  52. package/dist/components/listing/ListingBar.js +105 -0
  53. package/dist/components/listing/Pagination.d.ts +36 -0
  54. package/dist/components/listing/Pagination.js +69 -0
  55. package/dist/components/listing/RecordCard.d.ts +33 -0
  56. package/dist/components/listing/RecordCard.js +22 -0
  57. package/dist/components/listing/RecordLink.d.ts +7 -0
  58. package/dist/components/listing/RecordLink.js +22 -0
  59. package/dist/components/listing/Row.d.ts +106 -0
  60. package/dist/components/listing/Row.js +122 -0
  61. package/dist/components/listing/SortMenu.d.ts +62 -0
  62. package/dist/components/listing/SortMenu.js +105 -0
  63. package/dist/components/listing/ViewSwitcher.d.ts +24 -0
  64. package/dist/components/listing/ViewSwitcher.js +16 -0
  65. package/dist/components/listing/columns.d.ts +47 -0
  66. package/dist/components/listing/columns.js +72 -0
  67. package/dist/components/listing/grouping.d.ts +35 -0
  68. package/dist/components/listing/grouping.js +47 -0
  69. package/dist/components/listing/index.d.ts +17 -0
  70. package/dist/components/listing/index.js +20 -0
  71. package/dist/components/listing/modelColumns.d.ts +50 -0
  72. package/dist/components/listing/modelColumns.js +103 -0
  73. package/dist/components/listing/pages.d.ts +78 -0
  74. package/dist/components/listing/pages.js +135 -0
  75. package/dist/components/listing/selection.d.ts +21 -0
  76. package/dist/components/listing/selection.js +43 -0
  77. package/dist/components/listing/set.d.ts +48 -0
  78. package/dist/components/listing/set.js +24 -0
  79. package/dist/components/listing/tree.d.ts +28 -0
  80. package/dist/components/listing/tree.js +23 -0
  81. package/dist/components/navigation/CommandPalette.d.ts +40 -0
  82. package/dist/components/navigation/CommandPalette.js +114 -0
  83. package/dist/components/navigation/NavigationTree.d.ts +31 -0
  84. package/dist/components/navigation/NavigationTree.js +79 -0
  85. package/dist/components/navigation/commands.d.ts +30 -0
  86. package/dist/components/navigation/commands.js +30 -0
  87. package/dist/components/navigation/index.d.ts +3 -0
  88. package/dist/components/navigation/index.js +4 -0
  89. package/dist/components/record/Breadcrumb.d.ts +42 -0
  90. package/dist/components/record/Breadcrumb.js +98 -0
  91. package/dist/components/record/DirtyActions.d.ts +26 -0
  92. package/dist/components/record/DirtyActions.js +22 -0
  93. package/dist/components/record/Pager.d.ts +18 -0
  94. package/dist/components/record/Pager.js +33 -0
  95. package/dist/components/record/RecordHeader.d.ts +71 -0
  96. package/dist/components/record/RecordHeader.js +72 -0
  97. package/dist/components/record/RecordSkeleton.d.ts +3 -0
  98. package/dist/components/record/RecordSkeleton.js +21 -0
  99. package/dist/components/record/SmartButtons.d.ts +35 -0
  100. package/dist/components/record/SmartButtons.js +38 -0
  101. package/dist/components/record/StatusBar.d.ts +51 -0
  102. package/dist/components/record/StatusBar.js +168 -0
  103. package/dist/components/record/ThumbBar.d.ts +36 -0
  104. package/dist/components/record/ThumbBar.js +86 -0
  105. package/dist/components/record/index.d.ts +6 -0
  106. package/dist/components/record/index.js +8 -0
  107. package/dist/components/search/CriteriaBuilder.d.ts +58 -0
  108. package/dist/components/search/CriteriaBuilder.js +278 -0
  109. package/dist/components/search/CriteriaChips.d.ts +22 -0
  110. package/dist/components/search/CriteriaChips.js +64 -0
  111. package/dist/components/search/FilterBuilder.d.ts +142 -0
  112. package/dist/components/search/FilterBuilder.js +421 -0
  113. package/dist/components/search/JoinWord.d.ts +8 -0
  114. package/dist/components/search/JoinWord.js +47 -0
  115. package/dist/components/search/JsonEditor.d.ts +19 -0
  116. package/dist/components/search/JsonEditor.js +78 -0
  117. package/dist/components/search/SearchBar.d.ts +78 -0
  118. package/dist/components/search/SearchBar.js +387 -0
  119. package/dist/components/search/criteria.d.ts +116 -0
  120. package/dist/components/search/criteria.js +159 -0
  121. package/dist/components/search/datePeriods.d.ts +122 -0
  122. package/dist/components/search/datePeriods.js +134 -0
  123. package/dist/components/search/dateRanges.d.ts +54 -0
  124. package/dist/components/search/dateRanges.js +89 -0
  125. package/dist/components/search/index.d.ts +9 -0
  126. package/dist/components/search/index.js +20 -0
  127. package/dist/components/search/model.d.ts +54 -0
  128. package/dist/components/search/model.js +37 -0
  129. package/dist/components/search/query.d.ts +166 -0
  130. package/dist/components/search/query.js +436 -0
  131. package/dist/components/shell/AppFrame.d.ts +14 -0
  132. package/dist/components/shell/AppFrame.js +80 -0
  133. package/dist/components/shell/TopBar.d.ts +73 -0
  134. package/dist/components/shell/TopBar.js +54 -0
  135. package/dist/components/shell/headerSlot.d.ts +17 -0
  136. package/dist/components/shell/headerSlot.js +21 -0
  137. package/dist/components/shell/index.d.ts +2 -0
  138. package/dist/components/shell/index.js +3 -0
  139. package/dist/foundations/aria.d.ts +14 -0
  140. package/dist/foundations/aria.js +18 -0
  141. package/dist/foundations/calendar.d.ts +28 -0
  142. package/dist/foundations/calendar.js +183 -0
  143. package/dist/foundations/copy.d.ts +619 -0
  144. package/dist/foundations/copy.es.d.ts +2 -0
  145. package/dist/foundations/copy.es.js +377 -0
  146. package/dist/foundations/copy.js +472 -0
  147. package/dist/foundations/density.d.ts +16 -0
  148. package/dist/foundations/density.js +21 -0
  149. package/dist/foundations/grouping.d.ts +281 -0
  150. package/dist/foundations/grouping.js +0 -0
  151. package/dist/foundations/index.d.ts +14 -0
  152. package/dist/foundations/index.js +30 -0
  153. package/dist/foundations/layout.d.ts +9 -0
  154. package/dist/foundations/layout.js +9 -0
  155. package/dist/foundations/pivot.d.ts +222 -0
  156. package/dist/foundations/pivot.js +198 -0
  157. package/dist/foundations/pivotSheet.d.ts +61 -0
  158. package/dist/foundations/pivotSheet.js +0 -0
  159. package/dist/foundations/slots.d.ts +67 -0
  160. package/dist/foundations/slots.js +63 -0
  161. package/dist/foundations/sticky.d.ts +53 -0
  162. package/dist/foundations/sticky.js +150 -0
  163. package/dist/foundations/surface.d.ts +9 -0
  164. package/dist/foundations/surface.js +26 -0
  165. package/dist/foundations/theme.d.ts +42 -0
  166. package/dist/foundations/theme.js +88 -0
  167. package/dist/foundations/types.d.ts +23 -0
  168. package/dist/foundations/types.js +50 -0
  169. package/dist/foundations/variants.d.ts +19 -0
  170. package/dist/foundations/variants.js +48 -0
  171. package/dist/foundations/xlsx.d.ts +86 -0
  172. package/dist/foundations/xlsx.js +0 -0
  173. package/dist/index.d.ts +19 -0
  174. package/dist/index.js +19 -0
  175. package/dist/primitives/block/Block.d.ts +66 -0
  176. package/dist/primitives/block/Block.js +34 -0
  177. package/dist/primitives/block/Line.d.ts +18 -0
  178. package/dist/primitives/block/Line.js +45 -0
  179. package/dist/primitives/block/SectionGrid.d.ts +30 -0
  180. package/dist/primitives/block/SectionGrid.js +29 -0
  181. package/dist/primitives/block/Steps.d.ts +76 -0
  182. package/dist/primitives/block/Steps.js +43 -0
  183. package/dist/primitives/block/index.d.ts +4 -0
  184. package/dist/primitives/block/index.js +7 -0
  185. package/dist/primitives/button/Button.d.ts +137 -0
  186. package/dist/primitives/button/Button.js +234 -0
  187. package/dist/primitives/button/index.d.ts +1 -0
  188. package/dist/primitives/button/index.js +2 -0
  189. package/dist/primitives/cell/CellStack.d.ts +47 -0
  190. package/dist/primitives/cell/CellStack.js +33 -0
  191. package/dist/primitives/cell/index.d.ts +1 -0
  192. package/dist/primitives/cell/index.js +3 -0
  193. package/dist/primitives/choice/ComboBox.d.ts +16 -0
  194. package/dist/primitives/choice/ComboBox.js +52 -0
  195. package/dist/primitives/choice/RadioGroup.d.ts +18 -0
  196. package/dist/primitives/choice/RadioGroup.js +50 -0
  197. package/dist/primitives/choice/SearchField.d.ts +24 -0
  198. package/dist/primitives/choice/SearchField.js +27 -0
  199. package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
  200. package/dist/primitives/choice/SegmentedControl.js +69 -0
  201. package/dist/primitives/choice/Slider.d.ts +16 -0
  202. package/dist/primitives/choice/Slider.js +46 -0
  203. package/dist/primitives/choice/Switch.d.ts +14 -0
  204. package/dist/primitives/choice/Switch.js +65 -0
  205. package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
  206. package/dist/primitives/choice/ThemeSwitcher.js +25 -0
  207. package/dist/primitives/choice/index.d.ts +8 -0
  208. package/dist/primitives/choice/index.js +11 -0
  209. package/dist/primitives/choice/options.d.ts +28 -0
  210. package/dist/primitives/choice/options.js +6 -0
  211. package/dist/primitives/date/Calendar.d.ts +31 -0
  212. package/dist/primitives/date/Calendar.js +110 -0
  213. package/dist/primitives/date/DatePicker.d.ts +34 -0
  214. package/dist/primitives/date/DatePicker.js +99 -0
  215. package/dist/primitives/date/index.d.ts +2 -0
  216. package/dist/primitives/date/index.js +4 -0
  217. package/dist/primitives/dialog/Dialog.d.ts +118 -0
  218. package/dist/primitives/dialog/Dialog.js +121 -0
  219. package/dist/primitives/dialog/index.d.ts +1 -0
  220. package/dist/primitives/dialog/index.js +2 -0
  221. package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
  222. package/dist/primitives/disclosure/Disclosure.js +52 -0
  223. package/dist/primitives/disclosure/index.d.ts +1 -0
  224. package/dist/primitives/disclosure/index.js +2 -0
  225. package/dist/primitives/field/Checks.d.ts +57 -0
  226. package/dist/primitives/field/Checks.js +72 -0
  227. package/dist/primitives/field/DropZone.d.ts +42 -0
  228. package/dist/primitives/field/DropZone.js +52 -0
  229. package/dist/primitives/field/Field.d.ts +166 -0
  230. package/dist/primitives/field/Field.js +240 -0
  231. package/dist/primitives/field/Suggest.d.ts +48 -0
  232. package/dist/primitives/field/Suggest.js +43 -0
  233. package/dist/primitives/field/aria.d.ts +61 -0
  234. package/dist/primitives/field/aria.js +70 -0
  235. package/dist/primitives/field/context.d.ts +30 -0
  236. package/dist/primitives/field/context.js +13 -0
  237. package/dist/primitives/field/index.d.ts +7 -0
  238. package/dist/primitives/field/index.js +11 -0
  239. package/dist/primitives/field/layout.d.ts +24 -0
  240. package/dist/primitives/field/layout.js +10 -0
  241. package/dist/primitives/field/mode.d.ts +9 -0
  242. package/dist/primitives/field/mode.js +13 -0
  243. package/dist/primitives/hint/Hint.d.ts +31 -0
  244. package/dist/primitives/hint/Hint.js +42 -0
  245. package/dist/primitives/hint/index.d.ts +1 -0
  246. package/dist/primitives/hint/index.js +1 -0
  247. package/dist/primitives/index.d.ts +19 -0
  248. package/dist/primitives/index.js +20 -0
  249. package/dist/primitives/menu/Menu.d.ts +100 -0
  250. package/dist/primitives/menu/Menu.js +59 -0
  251. package/dist/primitives/menu/index.d.ts +1 -0
  252. package/dist/primitives/menu/index.js +2 -0
  253. package/dist/primitives/notice/Callout.d.ts +49 -0
  254. package/dist/primitives/notice/Callout.js +28 -0
  255. package/dist/primitives/notice/Notice.d.ts +45 -0
  256. package/dist/primitives/notice/Notice.js +61 -0
  257. package/dist/primitives/notice/Toast.d.ts +55 -0
  258. package/dist/primitives/notice/Toast.js +142 -0
  259. package/dist/primitives/notice/index.d.ts +3 -0
  260. package/dist/primitives/notice/index.js +5 -0
  261. package/dist/primitives/overlay/Popover.d.ts +6 -0
  262. package/dist/primitives/overlay/Popover.js +27 -0
  263. package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
  264. package/dist/primitives/overlay/PopoverTrigger.js +38 -0
  265. package/dist/primitives/overlay/Tooltip.d.ts +38 -0
  266. package/dist/primitives/overlay/Tooltip.js +74 -0
  267. package/dist/primitives/overlay/index.d.ts +3 -0
  268. package/dist/primitives/overlay/index.js +5 -0
  269. package/dist/primitives/picker/Picker.d.ts +48 -0
  270. package/dist/primitives/picker/Picker.js +150 -0
  271. package/dist/primitives/picker/index.d.ts +1 -0
  272. package/dist/primitives/picker/index.js +2 -0
  273. package/dist/primitives/surface/Peek.d.ts +60 -0
  274. package/dist/primitives/surface/Peek.js +71 -0
  275. package/dist/primitives/surface/index.d.ts +1 -0
  276. package/dist/primitives/surface/index.js +2 -0
  277. package/dist/primitives/table/CellEditor.d.ts +14 -0
  278. package/dist/primitives/table/CellEditor.js +83 -0
  279. package/dist/primitives/table/DataTable.d.ts +135 -0
  280. package/dist/primitives/table/DataTable.js +244 -0
  281. package/dist/primitives/table/EditableTable.d.ts +96 -0
  282. package/dist/primitives/table/EditableTable.js +423 -0
  283. package/dist/primitives/table/PivotTable.d.ts +14 -0
  284. package/dist/primitives/table/PivotTable.js +0 -0
  285. package/dist/primitives/table/ReportTable.d.ts +38 -0
  286. package/dist/primitives/table/ReportTable.js +106 -0
  287. package/dist/primitives/table/RowActions.d.ts +16 -0
  288. package/dist/primitives/table/RowActions.js +28 -0
  289. package/dist/primitives/table/SheetTable.d.ts +70 -0
  290. package/dist/primitives/table/SheetTable.js +197 -0
  291. package/dist/primitives/table/Table.d.ts +106 -0
  292. package/dist/primitives/table/Table.js +116 -0
  293. package/dist/primitives/table/index.d.ts +7 -0
  294. package/dist/primitives/table/index.js +9 -0
  295. package/dist/primitives/tabs/Tabs.d.ts +91 -0
  296. package/dist/primitives/tabs/Tabs.js +56 -0
  297. package/dist/primitives/tabs/index.d.ts +1 -0
  298. package/dist/primitives/tabs/index.js +1 -0
  299. package/dist/primitives/tag/Tag.d.ts +36 -0
  300. package/dist/primitives/tag/Tag.js +68 -0
  301. package/dist/primitives/tag/Tags.d.ts +57 -0
  302. package/dist/primitives/tag/Tags.js +104 -0
  303. package/dist/primitives/tag/TagsField.d.ts +32 -0
  304. package/dist/primitives/tag/TagsField.js +182 -0
  305. package/dist/primitives/tag/choices.d.ts +38 -0
  306. package/dist/primitives/tag/choices.js +26 -0
  307. package/dist/primitives/tag/index.d.ts +4 -0
  308. package/dist/primitives/tag/index.js +8 -0
  309. package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
  310. package/dist/primitives/toolbar/Toolbar.js +36 -0
  311. package/dist/primitives/toolbar/index.d.ts +1 -0
  312. package/dist/primitives/toolbar/index.js +2 -0
  313. package/dist/primitives/value/Copyable.d.ts +37 -0
  314. package/dist/primitives/value/Copyable.js +50 -0
  315. package/dist/primitives/value/Fecha.d.ts +12 -0
  316. package/dist/primitives/value/Fecha.js +80 -0
  317. package/dist/primitives/value/Meter.d.ts +29 -0
  318. package/dist/primitives/value/Meter.js +46 -0
  319. package/dist/primitives/value/Metric.d.ts +42 -0
  320. package/dist/primitives/value/Metric.js +38 -0
  321. package/dist/primitives/value/Person.d.ts +90 -0
  322. package/dist/primitives/value/Person.js +208 -0
  323. package/dist/primitives/value/Relation.d.ts +69 -0
  324. package/dist/primitives/value/Relation.js +57 -0
  325. package/dist/primitives/value/Totals.d.ts +26 -0
  326. package/dist/primitives/value/Totals.js +4 -0
  327. package/dist/primitives/value/Value.d.ts +77 -0
  328. package/dist/primitives/value/Value.js +119 -0
  329. package/dist/primitives/value/index.d.ts +8 -0
  330. package/dist/primitives/value/index.js +9 -0
  331. package/dist/tokens.css +1025 -0
  332. package/dist/views/index.d.ts +2 -0
  333. package/dist/views/index.js +3 -0
  334. package/dist/views/list/List.d.ts +113 -0
  335. package/dist/views/list/List.js +97 -0
  336. package/dist/views/list/ListView.d.ts +6 -0
  337. package/dist/views/list/ListView.js +269 -0
  338. package/dist/views/list/columns.d.ts +18 -0
  339. package/dist/views/list/columns.js +27 -0
  340. package/dist/views/list/derive.d.ts +76 -0
  341. package/dist/views/list/derive.js +181 -0
  342. package/dist/views/list/index.d.ts +2 -0
  343. package/dist/views/list/index.js +4 -0
  344. package/dist/views/list/presentations.d.ts +39 -0
  345. package/dist/views/list/presentations.js +103 -0
  346. package/dist/views/list/types.d.ts +272 -0
  347. package/dist/views/list/types.js +1 -0
  348. package/dist/views/list/useNarrowList.d.ts +17 -0
  349. package/dist/views/list/useNarrowList.js +32 -0
  350. package/dist/views/record/Record.d.ts +126 -0
  351. package/dist/views/record/Record.js +141 -0
  352. package/dist/views/record/index.d.ts +1 -0
  353. package/dist/views/record/index.js +2 -0
  354. package/package.json +109 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SINCPRO S.R.L
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,158 @@
1
- # Temporary Holding Version
1
+ # @sincpro/web-ui
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **A design system for enterprise web software** — ERPs, back offices, management tools: the
4
+ screens where people spend eight hours a day reading records, filtering lists and filling
5
+ forms. React + Tailwind v4, open source, by [Sincpro](https://github.com/Sincpro-SRL).
6
+
7
+ Most design systems are built for marketing sites and consumer apps, then stretched over
8
+ business software. This one starts from the other end: dense data, long sessions, keyboard
9
+ users, numbers that must be legible, and screens that are always about _records_.
10
+
11
+ ## The idea
12
+
13
+ **Every management screen is one of two things: one record, or N records.** That is the
14
+ whole product surface, and the system is organised around it in four levels — each one
15
+ defined by what it _knows_, and each one importing only from the levels below:
16
+
17
+ | Level | What it knows | Examples |
18
+ | --------------- | --------------------------------------- | ------------------------------------------------------- |
19
+ | **Foundations** | The decisions. No components. | color, typography, scales, motion, surfaces |
20
+ | **Primitives** | Nothing about the screen it is on. | `Button`, `Field`, `Menu`, `Tag`, `Table`, `Dialog` |
21
+ | **Components** | One question of the product, by family. | data tables, search and criteria, record parts, chatter |
22
+ | **Views** | The two macros, and nothing else. | `Record` (one) · `List` (N) |
23
+
24
+ If something does not fit one of the two views, it is not a view — it is a component in
25
+ disguise. And if something needs to know what an invoice, a dataset or a customer _is_, it
26
+ does not belong here: that is domain, and it lives in the application.
27
+
28
+ ### Principles
29
+
30
+ - **Tokens or nothing.** Every color, size and radius comes from `src/tokens.css`. No
31
+ arbitrary Tailwind values, no hand-written hex, no raw `px`, no raw Tailwind palette — a
32
+ test enforces it, and every exception is written down with its reason.
33
+ - **Contrast is measured, not guessed.** Each color token carries its WCAG ratio next to it,
34
+ and a test recomputes it: a number that drifted from its color fails the build.
35
+ - **Two surfaces.** The work happens on a warm light _canvas_; navigation lives on a dark
36
+ _chrome_. A piece is judged on the surface it will actually live on.
37
+ - **Density is a setting, not a fork.** The same components serve a dense ERP grid and a
38
+ roomy form (`DensityProvider`).
39
+ - **Language is data.** Interface copy is English by default and replaced as a whole table
40
+ (`installCopy(SPANISH_COPY)`, `UiCopyProvider`) — never hard-coded in a component.
41
+ - **Accessible by default.** Storybook runs the a11y checks as errors, not warnings.
42
+
43
+ ## Using it
44
+
45
+ ```bash
46
+ yarn add @sincpro/web-ui react react-dom tailwindcss @tanstack/react-table @tanstack/react-virtual
47
+ ```
48
+
49
+ Your bundler must run Tailwind v4 (`@tailwindcss/vite` or `@tailwindcss/postcss`). Then import
50
+ the tokens once — fonts, theme and Tailwind come with it:
51
+
52
+ ```css
53
+ @import "@sincpro/web-ui/tokens.css";
54
+ ```
55
+
56
+ ```tsx
57
+ import { Field, installCopy, SPANISH_COPY, TextInput } from "@sincpro/web-ui";
58
+
59
+ installCopy(SPANISH_COPY); // optional: the interface in Spanish
60
+
61
+ export function CompanyName({
62
+ value,
63
+ onChange,
64
+ }: {
65
+ value: string;
66
+ onChange: (v: string) => void;
67
+ }) {
68
+ return (
69
+ <Field label="Company name" required>
70
+ <TextInput value={value} onChange={onChange} />
71
+ </Field>
72
+ );
73
+ }
74
+ ```
75
+
76
+ Each level is also its own entry — `@sincpro/web-ui/foundations`, `/primitives`,
77
+ `/components`, `/views` — so what a level offers is one import away.
78
+
79
+ **React Server Components.** Every module that needs the browser (hooks, context, events)
80
+ starts with `"use client"`, and a test keeps it that way; pure logic (calendar, pivot,
81
+ criteria) stays callable from the server. The views are client components: compose
82
+ `Record` inside a client component of your own.
83
+
84
+ ### Theming (light / dark)
85
+
86
+ The palettes live in `tokens.css`: the same `--color-*` names, redefined for dark. Components
87
+ never read the theme — they use `bg-surface` / `text-ink` and the cascade resolves them — so
88
+ a screen needs no change to work in both.
89
+
90
+ - With nothing configured the stylesheet follows `prefers-color-scheme`.
91
+ - `data-theme="dark"` / `"light"` on **any** element forces that palette for everything under it
92
+ (a light invoice preview inside a dark app works). Portals live under `<html>`, so an app
93
+ switch writes it there.
94
+ - `ThemeProvider` + `useTheme()` do that write (`"system" | "light" | "dark"`; `"system"`
95
+ removes the attribute) and `ThemeSwitcher` is the tiny control. Remembering the choice is
96
+ the app's job (`onChange`).
97
+
98
+ ```tsx
99
+ <ThemeProvider
100
+ defaultValue="system"
101
+ onChange={(theme) => localStorage.setItem("theme", theme)}
102
+ >
103
+ <App /> {/* somewhere: <ThemeSwitcher /> */}
104
+ </ThemeProvider>
105
+ ```
106
+
107
+ Rules a new token must follow: define it in **both** palettes with its contrast on the same line
108
+ (`tests/tokens.test.ts` measures each theme against its own ground, surface and chrome, and fails
109
+ if a light token has no dark twin or the two dark copies drift); the gold stays the primary
110
+ action in both (`on-accent` keeps the text on it dark); never write a colour outside
111
+ `tokens.css`; use `bg-scrim` — not `bg-ink/30` — for a veil. In Storybook the toolbar has a
112
+ theme switch, or open a story with `&globals=theme:dark`.
113
+
114
+ ## Developing it
115
+
116
+ ```bash
117
+ make init # pre-commit + yarn install
118
+ make dev # Storybook on :6006 — the living documentation
119
+ make test # node --test: behaviour, tokens, contrast, client boundaries
120
+ make smoke # every story opened in headless Chrome: no console errors, no blanks
121
+ make build # dist/ (JS + .d.ts + tokens.css)
122
+ ```
123
+
124
+ Node 26 (the active LTS) or newer — `nvm use` reads `.nvmrc`. Every operation — local or CI —
125
+ goes through the `Makefile`; run `make` to list the targets.
126
+
127
+ ```text
128
+ src/
129
+ ├── tokens.css the source of every value: color, type, spacing, radii, motion
130
+ ├── index.ts the public surface: the four levels below
131
+ ├── foundations/ density, surface, copy, slots, calendar, pivot, xlsx
132
+ ├── primitives/ one folder per family: button, field, menu, table, tabs, …
133
+ ├── components/ one folder per family: listing, record, search, form, chatter, frame, shell
134
+ └── views/ list (N records) · record (one record)
135
+ stories/ Storybook, by level — and templates/: whole pages built from the public API
136
+ tests/ node --test over the TypeScript sources
137
+ ```
138
+
139
+ `make doctor` rejects an import that climbs a level (a primitive importing a component, a
140
+ component importing a view), so the layering holds without anyone remembering it.
141
+
142
+ ### Iterating with AI
143
+
144
+ This system is developed together with AI agents, and it is set up for it: the rules an
145
+ agent must follow live in [`AGENTS.md`](./AGENTS.md), the guards that catch what an agent
146
+ gets wrong (tokens, contrast, import direction, client boundaries, stories that blow up)
147
+ run in `make test`, `make doctor` and `make smoke`, and
148
+ while `make dev` runs, Storybook serves an MCP endpoint at `/mcp` so an agent reads the
149
+ stories that already exist instead of guessing them.
150
+
151
+ ## Contributing
152
+
153
+ See [CONTRIBUTING.md](./CONTRIBUTING.md). Issues and pull requests are welcome — especially
154
+ from people who build business software for a living.
155
+
156
+ ## License
157
+
158
+ [MIT](./LICENSE) © SINCPRO S.R.L.
@@ -0,0 +1,64 @@
1
+ import { type AssistOrigin, type AssistSource } from "./assist.js";
2
+ import type { ChatterEvent } from "../chatter/Chatter.js";
3
+ import type { ReactNode } from "react";
4
+ /** The robot and the assistant's name: the header of a suggestion, a review, a pending
5
+ * state. `children` replaces the name — «Asistente sugiere». */
6
+ export declare function AssistLabel({ children, id }: {
7
+ children?: ReactNode;
8
+ id?: string;
9
+ }): import("react").JSX.Element;
10
+ /**
11
+ * Where the assistant got it: «Fuente: orden de compra PO/2026/0042».
12
+ *
13
+ * Context: a value without its source is a value nobody can check, and an unchecked value
14
+ * from a machine is the one people stop trusting after the first mistake. The source is a
15
+ * LINK to the record it was read from — with `href` a real `<a>` (middle-click, copy), with
16
+ * `onOpen` a link the screen answers — so checking it is one press, not a search.
17
+ */
18
+ export declare function AssistSourceLink({ source }: {
19
+ source: AssistSource;
20
+ }): import("react").JSX.Element;
21
+ export type AssistMarkProps = {
22
+ /** When, from what, and who accepted it. Each one that is present is said in the card. */
23
+ origin?: AssistOrigin;
24
+ };
25
+ /**
26
+ * The small robot next to a value the assistant set — provenance, after the fact.
27
+ *
28
+ * Context: once a suggestion is accepted the value looks like any other, and a week later
29
+ * nobody remembers whether «30 días» was typed or proposed. The mark keeps that answer next
30
+ * to the value: its tooltip says «Lo completó el asistente», and pressing it opens WHO
31
+ * (the assistant, and the person who accepted), WHEN and FROM WHAT — with the source as a
32
+ * link. It is a popover and not a tooltip because the source is a link, and a tooltip can
33
+ * never be entered with the keyboard.
34
+ *
35
+ * Next to a field, write it in the field's `action`; next to a read value, use
36
+ * `AssistedValue`.
37
+ */
38
+ export declare function AssistMark({ origin }: AssistMarkProps): import("react").JSX.Element;
39
+ /** A read value with the assistant's mark after it: a `Record.Facts` value, a table cell. */
40
+ export declare function AssistedValue({ children, origin, }: {
41
+ /** The value, drawn the way it would be without the mark. */
42
+ children: ReactNode;
43
+ origin?: AssistOrigin;
44
+ }): import("react").JSX.Element;
45
+ /**
46
+ * The assistant's entry in a record's history: the chatter's event, with the robot as its
47
+ * node, the assistant (and who accepted) as its author, and the source as its detail.
48
+ *
49
+ * Context: provenance has two places. The mark answers «where did THIS value come from»
50
+ * while looking at it; the history answers «what has the assistant done on this record»,
51
+ * in the order it happened, next to what people did. Same mark in both, so the eye learns
52
+ * it once.
53
+ *
54
+ * Call it when the change happens (in the handler), not at module load: it reads the
55
+ * installed copy, like the formatters do.
56
+ */
57
+ export declare function assistEvent({ id, label, at, source, acceptedBy, }: {
58
+ id: string;
59
+ /** What happened, in the past: «completó 5 líneas desde la orden de compra». */
60
+ label: ReactNode;
61
+ at: Date | string | number;
62
+ source?: AssistSource;
63
+ acceptedBy?: string;
64
+ }): ChatterEvent;
@@ -0,0 +1,108 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { ASSIST_ICON, } from "./assist.js";
4
+ import { currentCopy, fillCopy, useUiCopy } from "../../foundations/copy.js";
5
+ import { cn, focusRing } from "../../foundations/variants.js";
6
+ import { PopoverTrigger } from "../../primitives/overlay/PopoverTrigger.js";
7
+ import { Tooltip, TooltipTrigger } from "../../primitives/overlay/Tooltip.js";
8
+ import { DateValue } from "../../primitives/value/Fecha.js";
9
+ import { Link } from "react-aria-components";
10
+ /* ==================================================================== *
11
+ * THE ASSISTANT'S MARK — how a screen says «this came from the assistant».
12
+ *
13
+ * Three pieces, one look:
14
+ *
15
+ * AssistLabel the robot and the word «Asistente», in the info tone:
16
+ * the header of anything the assistant proposes
17
+ * AssistSourceLink «Fuente: orden de compra PO/2026/0042», a real link
18
+ * AssistMark the small robot next to a value it SET, which opens
19
+ * who, when and from what
20
+ *
21
+ * ── Why the info tone, and never the gold ───────────────────────────
22
+ *
23
+ * The gold is the ONE main action of a screen (PRINCIPLES §2–3). The assistant
24
+ * is not an action, it is a source of information — and `info` already means
25
+ * «a fact that changes how you read what is below». It is also never the color
26
+ * alone: the robot and the word are always there, so a person who does not tell
27
+ * teal from navy still reads «Asistente».
28
+ *
29
+ * ── Why a robot and not sparkles ────────────────────────────────────
30
+ *
31
+ * Sparkles celebrate; a business screen states. «An automated assistant did
32
+ * this» is the fact a person needs to weigh a value, and the robot says exactly
33
+ * that. The icon lives in `ASSIST_ICON`, so changing it is one line.
34
+ * ==================================================================== */
35
+ /** The robot and the assistant's name: the header of a suggestion, a review, a pending
36
+ * state. `children` replaces the name — «Asistente sugiere». */
37
+ export function AssistLabel({ children, id }) {
38
+ const copy = useUiCopy();
39
+ const Icon = ASSIST_ICON;
40
+ return (_jsxs("span", { id: id, "data-assist": "label", className: "inline-flex shrink-0 items-center gap-1 text-micro font-medium text-info", children: [_jsx(Icon, { className: "size-(--size-icon-sm) shrink-0", strokeWidth: 2, "aria-hidden": true }), children ?? copy.assist.assistant] }));
41
+ }
42
+ /**
43
+ * Where the assistant got it: «Fuente: orden de compra PO/2026/0042».
44
+ *
45
+ * Context: a value without its source is a value nobody can check, and an unchecked value
46
+ * from a machine is the one people stop trusting after the first mistake. The source is a
47
+ * LINK to the record it was read from — with `href` a real `<a>` (middle-click, copy), with
48
+ * `onOpen` a link the screen answers — so checking it is one press, not a search.
49
+ */
50
+ export function AssistSourceLink({ source }) {
51
+ const copy = useUiCopy();
52
+ const goes = source.href !== undefined || source.onOpen !== undefined;
53
+ return (_jsxs("span", { className: "inline-flex max-w-full min-w-0 items-baseline gap-1 text-micro text-muted", children: [_jsxs("span", { className: "shrink-0", children: [copy.assist.source, ":"] }), goes ? (_jsx(Link, { ...(source.href === undefined ? {} : { href: source.href }), ...(source.onOpen === undefined ? {} : { onPress: source.onOpen }), className: ({ isFocusVisible }) => cn("min-w-0 cursor-pointer truncate rounded-mark text-primary underline-offset-2 hover:underline", focusRing({ isFocusVisible })), children: source.label })) : (_jsx("span", { className: "min-w-0 truncate text-ink", children: source.label }))] }));
54
+ }
55
+ /** The mark's own button. It forwards `ref` and spreads native props, so `PopoverTrigger`
56
+ * anchors to it and `TooltipTrigger` names it — the same contract as `IconButton`, in the
57
+ * assistant's tone instead of the three an icon button has. */
58
+ function MarkButton({ label, expanded, haspopup, ref, ...native }) {
59
+ const Icon = ASSIST_ICON;
60
+ return (_jsxs(TooltipTrigger, { children: [_jsx("button", { ...native, ref: ref, type: "button", "aria-label": label, ...(haspopup === undefined ? {} : { "aria-haspopup": haspopup }), ...(expanded === undefined ? {} : { "aria-expanded": expanded }), "data-assist": "mark", className: "inline-flex size-(--size-control-sm) shrink-0 items-center justify-center rounded-pill text-info transition-colors duration-(--duration-instant) hover:bg-info/10 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary", children: _jsx(Icon, { className: "size-(--size-icon-sm)", strokeWidth: 2, "aria-hidden": true }) }), _jsx(Tooltip, { children: label })] }));
61
+ }
62
+ /**
63
+ * The small robot next to a value the assistant set — provenance, after the fact.
64
+ *
65
+ * Context: once a suggestion is accepted the value looks like any other, and a week later
66
+ * nobody remembers whether «30 días» was typed or proposed. The mark keeps that answer next
67
+ * to the value: its tooltip says «Lo completó el asistente», and pressing it opens WHO
68
+ * (the assistant, and the person who accepted), WHEN and FROM WHAT — with the source as a
69
+ * link. It is a popover and not a tooltip because the source is a link, and a tooltip can
70
+ * never be entered with the keyboard.
71
+ *
72
+ * Next to a field, write it in the field's `action`; next to a read value, use
73
+ * `AssistedValue`.
74
+ */
75
+ export function AssistMark({ origin = {} }) {
76
+ const copy = useUiCopy();
77
+ return (_jsx(PopoverTrigger, { title: copy.assist.madeBy, placement: "bottom end", trigger: _jsx(MarkButton, { label: copy.assist.madeBy }), children: _jsxs("div", { className: "flex max-w-72 min-w-0 flex-col gap-1.5 text-micro text-muted", children: [origin.source === undefined ? null : _jsx(AssistSourceLink, { source: origin.source }), origin.at === undefined ? null : _jsx(DateValue, { when: origin.at, format: "exact" }), origin.acceptedBy === undefined ? null : (_jsx("span", { children: fillCopy(copy.assist.acceptedBy, { name: origin.acceptedBy }) }))] }) }));
78
+ }
79
+ /** A read value with the assistant's mark after it: a `Record.Facts` value, a table cell. */
80
+ export function AssistedValue({ children, origin, }) {
81
+ return (_jsxs("span", { className: "inline-flex max-w-full min-w-0 items-center gap-0.5", children: [_jsx("span", { className: "min-w-0 truncate", children: children }), _jsx(AssistMark, { ...(origin === undefined ? {} : { origin }) })] }));
82
+ }
83
+ /**
84
+ * The assistant's entry in a record's history: the chatter's event, with the robot as its
85
+ * node, the assistant (and who accepted) as its author, and the source as its detail.
86
+ *
87
+ * Context: provenance has two places. The mark answers «where did THIS value come from»
88
+ * while looking at it; the history answers «what has the assistant done on this record»,
89
+ * in the order it happened, next to what people did. Same mark in both, so the eye learns
90
+ * it once.
91
+ *
92
+ * Call it when the change happens (in the handler), not at module load: it reads the
93
+ * installed copy, like the formatters do.
94
+ */
95
+ export function assistEvent({ id, label, at, source, acceptedBy, }) {
96
+ const copy = currentCopy();
97
+ const by = acceptedBy === undefined
98
+ ? copy.assist.assistant
99
+ : `${copy.assist.assistant} · ${fillCopy(copy.assist.acceptedBy, { name: acceptedBy })}`;
100
+ return {
101
+ id,
102
+ label,
103
+ at,
104
+ by,
105
+ icon: ASSIST_ICON,
106
+ ...(source === undefined ? {} : { detail: _jsx(AssistSourceLink, { source: source }) }),
107
+ };
108
+ }
@@ -0,0 +1,24 @@
1
+ export type AssistPendingProps = {
2
+ /** What the assistant is doing, in the screen's words: «Leyendo la orden de compra
3
+ * PO/2026/0042…». The copy's «El asistente está trabajando…» when absent. */
4
+ label?: string;
5
+ /** Stops it. Present = a «Cancelar» next to the sentence. */
6
+ onCancel?: () => void;
7
+ /** Skeleton bars under the sentence, standing in for the proposal that is coming —
8
+ * `0` (the default) is the one-line form, for under a field. */
9
+ rows?: number;
10
+ };
11
+ /**
12
+ * The assistant is working on a suggestion, and the screen goes on.
13
+ *
14
+ * Context: the assistant is slower than a form, and a screen that locks while it thinks
15
+ * teaches people to wait for it — or to close it. So this BLOCKS NOTHING: it is a line in
16
+ * the place where the suggestion will appear (under a field, above the lines), the fields
17
+ * stay editable, and «Cancelar» stops it. What the person types in the meantime wins; the
18
+ * screen decides whether a suggestion that arrives late still makes sense.
19
+ *
20
+ * The sentence is a polite live region (`role="status"`), so a screen reader hears that
21
+ * the assistant started without being interrupted; the bars are silent. The spinner and
22
+ * the bars' pulse stop under reduced motion, by the global rule in `tokens.css`.
23
+ */
24
+ export declare function AssistPending({ label, onCancel, rows }: AssistPendingProps): import("react").JSX.Element;
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { AssistLabel } from "./AssistMark.js";
4
+ import { useUiCopy } from "../../foundations/copy.js";
5
+ import { Button } from "../../primitives/button/Button.js";
6
+ import { LoaderCircle } from "lucide-react";
7
+ /** The widths of the skeleton's bars: uneven, so it reads as lines of content and not as
8
+ * a striped box. */
9
+ const BAR = ["w-2/3", "w-1/2", "w-3/4", "w-2/5"];
10
+ /**
11
+ * The assistant is working on a suggestion, and the screen goes on.
12
+ *
13
+ * Context: the assistant is slower than a form, and a screen that locks while it thinks
14
+ * teaches people to wait for it — or to close it. So this BLOCKS NOTHING: it is a line in
15
+ * the place where the suggestion will appear (under a field, above the lines), the fields
16
+ * stay editable, and «Cancelar» stops it. What the person types in the meantime wins; the
17
+ * screen decides whether a suggestion that arrives late still makes sense.
18
+ *
19
+ * The sentence is a polite live region (`role="status"`), so a screen reader hears that
20
+ * the assistant started without being interrupted; the bars are silent. The spinner and
21
+ * the bars' pulse stop under reduced motion, by the global rule in `tokens.css`.
22
+ */
23
+ export function AssistPending({ label, onCancel, rows = 0 }) {
24
+ const copy = useUiCopy();
25
+ return (_jsxs("div", { "data-assist": "pending", "aria-busy": "true", className: "flex min-w-0 flex-col gap-2", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", children: [_jsx(LoaderCircle, { className: "size-(--size-icon-sm) shrink-0 animate-spin text-info", strokeWidth: 2, "aria-hidden": true }), _jsx(AssistLabel, {}), _jsx("span", { role: "status", className: "min-w-0 text-micro text-muted", children: label ?? copy.assist.working }), onCancel === undefined ? null : (_jsx(Button, { role: "ghost", size: "sm", onClick: onCancel, children: copy.assist.cancel }))] }), rows <= 0 ? null : (_jsx("span", { "aria-hidden": true, className: "flex flex-col gap-2 pl-5", children: Array.from({ length: rows }, (_, i) => (_jsx("span", { className: `h-3 rounded-control bg-ink/8 motion-safe:animate-pulse ${BAR[i % BAR.length]}` }, i))) }))] }));
26
+ }
@@ -0,0 +1,42 @@
1
+ import { type AssistApply, type AssistSource } from "./assist.js";
2
+ import { type ReactNode } from "react";
3
+ export type AssistSuggestionProps = {
4
+ /** The proposed value, drawn as it will read once applied: «30 días», «15/11/2026», a
5
+ * product with its code. */
6
+ value: ReactNode;
7
+ /** Where the assistant got it — a link to the record it read. */
8
+ source?: AssistSource;
9
+ /** Applies the value. Return the function that reverts it and the screen gets a
10
+ * «Deshacer» notice; the suggestion itself is the screen's to remove. */
11
+ onAccept: AssistApply;
12
+ /** «No, gracias»: the screen forgets the suggestion. */
13
+ onDismiss: () => void;
14
+ /** The field's name, for the notice after accepting. Inside a `Field` the accessible
15
+ * name already takes the field's label; the notice is read elsewhere, so it needs it. */
16
+ field?: string;
17
+ };
18
+ /**
19
+ * The assistant proposes a value for ONE field: the customer's payment term, the product of
20
+ * a scanned line, the due date. It goes right under the control it would change.
21
+ *
22
+ * Context: a suggestion has to be noticed and must not be mistaken for the value. So it is
23
+ * NOT written into the control — a pre-filled field reads as «someone already decided» and
24
+ * gets saved without a look. It sits under the control, in a dashed box (dashed: proposed,
25
+ * not yet real), with the robot and «Asistente sugiere», the value, and its source as a
26
+ * link. «Aceptar» applies it at once, and the notice carries «Deshacer» — undo over
27
+ * confirm, the system's rule. The × forgets it.
28
+ *
29
+ * Written inside a `Field`, after the control:
30
+ *
31
+ * <Field label="Plazo de pago">
32
+ * <NumberInput … />
33
+ * <AssistSuggestion value="30 días" source={…} onAccept={…} onDismiss={…} />
34
+ * </Field>
35
+ *
36
+ * It is a group named «Sugerencia del asistente» + the field's label, so a screen reader
37
+ * hears which field it is about before the value. Both buttons are plain buttons: Tab
38
+ * reaches them, Enter and Space press them. After pressing either, the focus goes back to
39
+ * the field's control — the suggestion that held it is gone, and the control is what the
40
+ * person was working on.
41
+ */
42
+ export declare function AssistSuggestion({ value, source, onAccept, onDismiss, field, }: AssistSuggestionProps): import("react").JSX.Element;
@@ -0,0 +1,55 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { acceptedMessage, applyWithUndo, } from "./assist.js";
4
+ import { AssistLabel, AssistSourceLink } from "./AssistMark.js";
5
+ import { useUiCopy } from "../../foundations/copy.js";
6
+ import { Button, IconButton } from "../../primitives/button/Button.js";
7
+ import { FieldIdsContext } from "../../primitives/field/context.js";
8
+ import { useToastIfAny } from "../../primitives/notice/Toast.js";
9
+ import { Check, X } from "lucide-react";
10
+ import { useContext, useId } from "react";
11
+ /**
12
+ * The assistant proposes a value for ONE field: the customer's payment term, the product of
13
+ * a scanned line, the due date. It goes right under the control it would change.
14
+ *
15
+ * Context: a suggestion has to be noticed and must not be mistaken for the value. So it is
16
+ * NOT written into the control — a pre-filled field reads as «someone already decided» and
17
+ * gets saved without a look. It sits under the control, in a dashed box (dashed: proposed,
18
+ * not yet real), with the robot and «Asistente sugiere», the value, and its source as a
19
+ * link. «Aceptar» applies it at once, and the notice carries «Deshacer» — undo over
20
+ * confirm, the system's rule. The × forgets it.
21
+ *
22
+ * Written inside a `Field`, after the control:
23
+ *
24
+ * <Field label="Plazo de pago">
25
+ * <NumberInput … />
26
+ * <AssistSuggestion value="30 días" source={…} onAccept={…} onDismiss={…} />
27
+ * </Field>
28
+ *
29
+ * It is a group named «Sugerencia del asistente» + the field's label, so a screen reader
30
+ * hears which field it is about before the value. Both buttons are plain buttons: Tab
31
+ * reaches them, Enter and Space press them. After pressing either, the focus goes back to
32
+ * the field's control — the suggestion that held it is gone, and the control is what the
33
+ * person was working on.
34
+ */
35
+ export function AssistSuggestion({ value, source, onAccept, onDismiss, field, }) {
36
+ const copy = useUiCopy();
37
+ const toasts = useToastIfAny();
38
+ const ids = useContext(FieldIdsContext);
39
+ const nameId = useId();
40
+ /* Back to the control once this box is gone. A frame later: the screen removes the
41
+ suggestion in the same update, and the control is only focusable after it. */
42
+ const refocus = () => {
43
+ const target = ids?.id;
44
+ if (target === undefined || typeof document === "undefined")
45
+ return;
46
+ requestAnimationFrame(() => document.getElementById(target)?.focus());
47
+ };
48
+ return (_jsxs("div", { role: "group", "aria-labelledby": ids === null ? nameId : `${nameId} ${ids.labelId}`, "data-assist": "suggestion", className: "flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5 rounded-control border border-dashed border-info/40 bg-info/5 py-1 pr-1 pl-2.5", children: [_jsx("span", { id: nameId, hidden: true, children: copy.assist.suggestion }), _jsxs("span", { className: "flex min-w-0 flex-1 flex-col gap-0.5 py-0.5", children: [_jsxs("span", { className: "flex min-w-0 flex-wrap items-baseline gap-x-1.5", children: [_jsxs(AssistLabel, { children: [copy.assist.assistant, " ", copy.assist.suggests] }), _jsx("span", { className: "min-w-0 text-meta font-semibold text-ink tabular-nums", children: value })] }), source === undefined ? null : _jsx(AssistSourceLink, { source: source })] }), _jsxs("span", { className: "flex shrink-0 items-center gap-0.5", children: [_jsx(Button, { role: "secondary", size: "sm", icon: Check, onClick: () => {
49
+ applyWithUndo(onAccept, toasts, acceptedMessage(copy, field), copy.assist.undo);
50
+ refocus();
51
+ }, children: copy.assist.accept }), _jsx(IconButton, { icon: X, label: copy.assist.dismiss, onClick: () => {
52
+ onDismiss();
53
+ refocus();
54
+ } })] })] }));
55
+ }
@@ -0,0 +1,56 @@
1
+ import { type AssistApply, type AssistSource, type ProposedChange } from "./assist.js";
2
+ import { type DialogContext } from "../../primitives/dialog/Dialog.js";
3
+ import { type ReactNode } from "react";
4
+ type GroupProps = {
5
+ /** «Campos», «Líneas». Without it the changes are listed without a heading. */
6
+ title?: string;
7
+ children?: ReactNode;
8
+ };
9
+ type ReviewProps = {
10
+ /** What the proposal is, as a heading. The copy's «Cambios propuestos» when absent. */
11
+ title?: string;
12
+ /** One sentence under the title: what the assistant read and what it would do. */
13
+ summary?: string;
14
+ /** Where the whole proposal comes from. A change may carry its own. */
15
+ source?: AssistSource;
16
+ /** Applies the chosen changes — their ids, in the listed order. Return the function that
17
+ * reverts them, and the screen gets «Deshacer». */
18
+ onApply: AssistApply<[ids: string[]]>;
19
+ /** Rejects the proposal. */
20
+ onDiscard: () => void;
21
+ /** The chosen ids, controlled. Uncontrolled from «all but the `unchosen` ones». */
22
+ chosen?: readonly string[];
23
+ onChosenChange?: (ids: string[]) => void;
24
+ /** `ChangeReview.Group` and `ChangeReview.Change`. */
25
+ children?: ReactNode;
26
+ };
27
+ export type ChangeReviewProps = ReviewProps & {
28
+ /** The role of «Aplicar». `primary` by default; `secondary` inside a record whose rail
29
+ * already shows the screen's one primary. */
30
+ emphasis?: "primary" | "secondary";
31
+ };
32
+ /** The review inside the page: a dashed card in the record it would change. */
33
+ export declare function ChangeReview(props: ChangeReviewProps): import("react").JSX.Element;
34
+ export declare namespace ChangeReview {
35
+ var Group: ((props: GroupProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"group">;
36
+ var Change: ((props: ProposedChange) => ReactNode) & import("../../foundations/slots.js").Slotted<"change">;
37
+ }
38
+ export type ChangeReviewDialogProps = ReviewProps & {
39
+ open: boolean;
40
+ /** Every way out ends here: ×, Escape, the scrim — «later», the proposal stays — and
41
+ * also after «Aplicar» and «Descartar». */
42
+ onClose: () => void;
43
+ /** What the dialog is about, in its header: «factura · FAC/2026/0120». */
44
+ context?: DialogContext;
45
+ };
46
+ /**
47
+ * The same review, in a `Dialog`: opened on demand — from a notice that says «el asistente
48
+ * encontró la orden de compra» — and a bottom sheet on a phone. «Aplicar» is the dialog's
49
+ * primary; closing without choosing keeps the proposal for later.
50
+ */
51
+ export declare function ChangeReviewDialog(props: ChangeReviewDialogProps): import("react").JSX.Element;
52
+ export declare namespace ChangeReviewDialog {
53
+ var Group: ((props: GroupProps) => ReactNode) & import("../../foundations/slots.js").Slotted<"group">;
54
+ var Change: ((props: ProposedChange) => ReactNode) & import("../../foundations/slots.js").Slotted<"change">;
55
+ }
56
+ export {};