@sincpro/web-ui 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +157 -2
  3. package/dist/components/assist/AssistMark.d.ts +64 -0
  4. package/dist/components/assist/AssistMark.js +108 -0
  5. package/dist/components/assist/AssistPending.d.ts +24 -0
  6. package/dist/components/assist/AssistPending.js +26 -0
  7. package/dist/components/assist/AssistSuggestion.d.ts +42 -0
  8. package/dist/components/assist/AssistSuggestion.js +55 -0
  9. package/dist/components/assist/ChangeReview.d.ts +56 -0
  10. package/dist/components/assist/ChangeReview.js +121 -0
  11. package/dist/components/assist/assist.d.ts +77 -0
  12. package/dist/components/assist/assist.js +87 -0
  13. package/dist/components/assist/index.d.ts +5 -0
  14. package/dist/components/assist/index.js +7 -0
  15. package/dist/components/chatter/Chatter.d.ts +161 -0
  16. package/dist/components/chatter/Chatter.js +232 -0
  17. package/dist/components/chatter/Messages.d.ts +66 -0
  18. package/dist/components/chatter/Messages.js +43 -0
  19. package/dist/components/chatter/Timeline.d.ts +21 -0
  20. package/dist/components/chatter/Timeline.js +30 -0
  21. package/dist/components/chatter/index.d.ts +3 -0
  22. package/dist/components/chatter/index.js +4 -0
  23. package/dist/components/form/Form.d.ts +52 -0
  24. package/dist/components/form/Form.js +49 -0
  25. package/dist/components/form/Sheet.d.ts +43 -0
  26. package/dist/components/form/Sheet.js +83 -0
  27. package/dist/components/form/index.d.ts +2 -0
  28. package/dist/components/form/index.js +4 -0
  29. package/dist/components/frame/Chart.d.ts +13 -0
  30. package/dist/components/frame/Chart.js +24 -0
  31. package/dist/components/frame/FrameView.d.ts +33 -0
  32. package/dist/components/frame/FrameView.js +125 -0
  33. package/dist/components/frame/index.d.ts +3 -0
  34. package/dist/components/frame/index.js +3 -0
  35. package/dist/components/frame/model.d.ts +43 -0
  36. package/dist/components/frame/model.js +101 -0
  37. package/dist/components/index.d.ts +10 -0
  38. package/dist/components/index.js +11 -0
  39. package/dist/components/lines/LineItems.d.ts +74 -0
  40. package/dist/components/lines/LineItems.js +229 -0
  41. package/dist/components/lines/index.d.ts +1 -0
  42. package/dist/components/lines/index.js +2 -0
  43. package/dist/components/lines/lines.d.ts +23 -0
  44. package/dist/components/lines/lines.js +26 -0
  45. package/dist/components/listing/ColumnsMenu.d.ts +20 -0
  46. package/dist/components/listing/ColumnsMenu.js +46 -0
  47. package/dist/components/listing/GroupingLevels.d.ts +35 -0
  48. package/dist/components/listing/GroupingLevels.js +45 -0
  49. package/dist/components/listing/Listing.d.ts +27 -0
  50. package/dist/components/listing/Listing.js +86 -0
  51. package/dist/components/listing/ListingBar.d.ts +73 -0
  52. package/dist/components/listing/ListingBar.js +105 -0
  53. package/dist/components/listing/Pagination.d.ts +36 -0
  54. package/dist/components/listing/Pagination.js +69 -0
  55. package/dist/components/listing/RecordCard.d.ts +33 -0
  56. package/dist/components/listing/RecordCard.js +22 -0
  57. package/dist/components/listing/RecordLink.d.ts +7 -0
  58. package/dist/components/listing/RecordLink.js +22 -0
  59. package/dist/components/listing/Row.d.ts +106 -0
  60. package/dist/components/listing/Row.js +122 -0
  61. package/dist/components/listing/SortMenu.d.ts +62 -0
  62. package/dist/components/listing/SortMenu.js +105 -0
  63. package/dist/components/listing/ViewSwitcher.d.ts +24 -0
  64. package/dist/components/listing/ViewSwitcher.js +16 -0
  65. package/dist/components/listing/columns.d.ts +47 -0
  66. package/dist/components/listing/columns.js +72 -0
  67. package/dist/components/listing/grouping.d.ts +35 -0
  68. package/dist/components/listing/grouping.js +47 -0
  69. package/dist/components/listing/index.d.ts +17 -0
  70. package/dist/components/listing/index.js +20 -0
  71. package/dist/components/listing/modelColumns.d.ts +50 -0
  72. package/dist/components/listing/modelColumns.js +103 -0
  73. package/dist/components/listing/pages.d.ts +78 -0
  74. package/dist/components/listing/pages.js +135 -0
  75. package/dist/components/listing/selection.d.ts +21 -0
  76. package/dist/components/listing/selection.js +43 -0
  77. package/dist/components/listing/set.d.ts +48 -0
  78. package/dist/components/listing/set.js +24 -0
  79. package/dist/components/listing/tree.d.ts +28 -0
  80. package/dist/components/listing/tree.js +23 -0
  81. package/dist/components/navigation/CommandPalette.d.ts +40 -0
  82. package/dist/components/navigation/CommandPalette.js +114 -0
  83. package/dist/components/navigation/NavigationTree.d.ts +31 -0
  84. package/dist/components/navigation/NavigationTree.js +79 -0
  85. package/dist/components/navigation/commands.d.ts +30 -0
  86. package/dist/components/navigation/commands.js +30 -0
  87. package/dist/components/navigation/index.d.ts +3 -0
  88. package/dist/components/navigation/index.js +4 -0
  89. package/dist/components/record/Breadcrumb.d.ts +42 -0
  90. package/dist/components/record/Breadcrumb.js +98 -0
  91. package/dist/components/record/DirtyActions.d.ts +26 -0
  92. package/dist/components/record/DirtyActions.js +22 -0
  93. package/dist/components/record/Pager.d.ts +18 -0
  94. package/dist/components/record/Pager.js +33 -0
  95. package/dist/components/record/RecordHeader.d.ts +71 -0
  96. package/dist/components/record/RecordHeader.js +72 -0
  97. package/dist/components/record/RecordSkeleton.d.ts +3 -0
  98. package/dist/components/record/RecordSkeleton.js +21 -0
  99. package/dist/components/record/SmartButtons.d.ts +35 -0
  100. package/dist/components/record/SmartButtons.js +38 -0
  101. package/dist/components/record/StatusBar.d.ts +51 -0
  102. package/dist/components/record/StatusBar.js +168 -0
  103. package/dist/components/record/ThumbBar.d.ts +36 -0
  104. package/dist/components/record/ThumbBar.js +86 -0
  105. package/dist/components/record/index.d.ts +6 -0
  106. package/dist/components/record/index.js +8 -0
  107. package/dist/components/search/CriteriaBuilder.d.ts +58 -0
  108. package/dist/components/search/CriteriaBuilder.js +278 -0
  109. package/dist/components/search/CriteriaChips.d.ts +22 -0
  110. package/dist/components/search/CriteriaChips.js +64 -0
  111. package/dist/components/search/FilterBuilder.d.ts +142 -0
  112. package/dist/components/search/FilterBuilder.js +421 -0
  113. package/dist/components/search/JoinWord.d.ts +8 -0
  114. package/dist/components/search/JoinWord.js +47 -0
  115. package/dist/components/search/JsonEditor.d.ts +19 -0
  116. package/dist/components/search/JsonEditor.js +78 -0
  117. package/dist/components/search/SearchBar.d.ts +78 -0
  118. package/dist/components/search/SearchBar.js +387 -0
  119. package/dist/components/search/criteria.d.ts +116 -0
  120. package/dist/components/search/criteria.js +159 -0
  121. package/dist/components/search/datePeriods.d.ts +122 -0
  122. package/dist/components/search/datePeriods.js +134 -0
  123. package/dist/components/search/dateRanges.d.ts +54 -0
  124. package/dist/components/search/dateRanges.js +89 -0
  125. package/dist/components/search/index.d.ts +9 -0
  126. package/dist/components/search/index.js +20 -0
  127. package/dist/components/search/model.d.ts +54 -0
  128. package/dist/components/search/model.js +37 -0
  129. package/dist/components/search/query.d.ts +166 -0
  130. package/dist/components/search/query.js +436 -0
  131. package/dist/components/shell/AppFrame.d.ts +14 -0
  132. package/dist/components/shell/AppFrame.js +80 -0
  133. package/dist/components/shell/TopBar.d.ts +73 -0
  134. package/dist/components/shell/TopBar.js +54 -0
  135. package/dist/components/shell/headerSlot.d.ts +17 -0
  136. package/dist/components/shell/headerSlot.js +21 -0
  137. package/dist/components/shell/index.d.ts +2 -0
  138. package/dist/components/shell/index.js +3 -0
  139. package/dist/foundations/aria.d.ts +14 -0
  140. package/dist/foundations/aria.js +18 -0
  141. package/dist/foundations/calendar.d.ts +28 -0
  142. package/dist/foundations/calendar.js +183 -0
  143. package/dist/foundations/copy.d.ts +619 -0
  144. package/dist/foundations/copy.es.d.ts +2 -0
  145. package/dist/foundations/copy.es.js +377 -0
  146. package/dist/foundations/copy.js +472 -0
  147. package/dist/foundations/density.d.ts +16 -0
  148. package/dist/foundations/density.js +21 -0
  149. package/dist/foundations/grouping.d.ts +281 -0
  150. package/dist/foundations/grouping.js +0 -0
  151. package/dist/foundations/index.d.ts +14 -0
  152. package/dist/foundations/index.js +30 -0
  153. package/dist/foundations/layout.d.ts +9 -0
  154. package/dist/foundations/layout.js +9 -0
  155. package/dist/foundations/pivot.d.ts +222 -0
  156. package/dist/foundations/pivot.js +198 -0
  157. package/dist/foundations/pivotSheet.d.ts +61 -0
  158. package/dist/foundations/pivotSheet.js +0 -0
  159. package/dist/foundations/slots.d.ts +67 -0
  160. package/dist/foundations/slots.js +63 -0
  161. package/dist/foundations/sticky.d.ts +53 -0
  162. package/dist/foundations/sticky.js +150 -0
  163. package/dist/foundations/surface.d.ts +9 -0
  164. package/dist/foundations/surface.js +26 -0
  165. package/dist/foundations/theme.d.ts +42 -0
  166. package/dist/foundations/theme.js +88 -0
  167. package/dist/foundations/types.d.ts +23 -0
  168. package/dist/foundations/types.js +50 -0
  169. package/dist/foundations/variants.d.ts +19 -0
  170. package/dist/foundations/variants.js +48 -0
  171. package/dist/foundations/xlsx.d.ts +86 -0
  172. package/dist/foundations/xlsx.js +0 -0
  173. package/dist/index.d.ts +19 -0
  174. package/dist/index.js +19 -0
  175. package/dist/primitives/block/Block.d.ts +66 -0
  176. package/dist/primitives/block/Block.js +34 -0
  177. package/dist/primitives/block/Line.d.ts +18 -0
  178. package/dist/primitives/block/Line.js +45 -0
  179. package/dist/primitives/block/SectionGrid.d.ts +30 -0
  180. package/dist/primitives/block/SectionGrid.js +29 -0
  181. package/dist/primitives/block/Steps.d.ts +76 -0
  182. package/dist/primitives/block/Steps.js +43 -0
  183. package/dist/primitives/block/index.d.ts +4 -0
  184. package/dist/primitives/block/index.js +7 -0
  185. package/dist/primitives/button/Button.d.ts +137 -0
  186. package/dist/primitives/button/Button.js +234 -0
  187. package/dist/primitives/button/index.d.ts +1 -0
  188. package/dist/primitives/button/index.js +2 -0
  189. package/dist/primitives/cell/CellStack.d.ts +47 -0
  190. package/dist/primitives/cell/CellStack.js +33 -0
  191. package/dist/primitives/cell/index.d.ts +1 -0
  192. package/dist/primitives/cell/index.js +3 -0
  193. package/dist/primitives/choice/ComboBox.d.ts +16 -0
  194. package/dist/primitives/choice/ComboBox.js +52 -0
  195. package/dist/primitives/choice/RadioGroup.d.ts +18 -0
  196. package/dist/primitives/choice/RadioGroup.js +50 -0
  197. package/dist/primitives/choice/SearchField.d.ts +24 -0
  198. package/dist/primitives/choice/SearchField.js +27 -0
  199. package/dist/primitives/choice/SegmentedControl.d.ts +16 -0
  200. package/dist/primitives/choice/SegmentedControl.js +69 -0
  201. package/dist/primitives/choice/Slider.d.ts +16 -0
  202. package/dist/primitives/choice/Slider.js +46 -0
  203. package/dist/primitives/choice/Switch.d.ts +14 -0
  204. package/dist/primitives/choice/Switch.js +65 -0
  205. package/dist/primitives/choice/ThemeSwitcher.d.ts +10 -0
  206. package/dist/primitives/choice/ThemeSwitcher.js +25 -0
  207. package/dist/primitives/choice/index.d.ts +8 -0
  208. package/dist/primitives/choice/index.js +11 -0
  209. package/dist/primitives/choice/options.d.ts +28 -0
  210. package/dist/primitives/choice/options.js +6 -0
  211. package/dist/primitives/date/Calendar.d.ts +31 -0
  212. package/dist/primitives/date/Calendar.js +110 -0
  213. package/dist/primitives/date/DatePicker.d.ts +34 -0
  214. package/dist/primitives/date/DatePicker.js +99 -0
  215. package/dist/primitives/date/index.d.ts +2 -0
  216. package/dist/primitives/date/index.js +4 -0
  217. package/dist/primitives/dialog/Dialog.d.ts +118 -0
  218. package/dist/primitives/dialog/Dialog.js +121 -0
  219. package/dist/primitives/dialog/index.d.ts +1 -0
  220. package/dist/primitives/dialog/index.js +2 -0
  221. package/dist/primitives/disclosure/Disclosure.d.ts +36 -0
  222. package/dist/primitives/disclosure/Disclosure.js +52 -0
  223. package/dist/primitives/disclosure/index.d.ts +1 -0
  224. package/dist/primitives/disclosure/index.js +2 -0
  225. package/dist/primitives/field/Checks.d.ts +57 -0
  226. package/dist/primitives/field/Checks.js +72 -0
  227. package/dist/primitives/field/DropZone.d.ts +42 -0
  228. package/dist/primitives/field/DropZone.js +52 -0
  229. package/dist/primitives/field/Field.d.ts +166 -0
  230. package/dist/primitives/field/Field.js +240 -0
  231. package/dist/primitives/field/Suggest.d.ts +48 -0
  232. package/dist/primitives/field/Suggest.js +43 -0
  233. package/dist/primitives/field/aria.d.ts +61 -0
  234. package/dist/primitives/field/aria.js +70 -0
  235. package/dist/primitives/field/context.d.ts +30 -0
  236. package/dist/primitives/field/context.js +13 -0
  237. package/dist/primitives/field/index.d.ts +7 -0
  238. package/dist/primitives/field/index.js +11 -0
  239. package/dist/primitives/field/layout.d.ts +24 -0
  240. package/dist/primitives/field/layout.js +10 -0
  241. package/dist/primitives/field/mode.d.ts +9 -0
  242. package/dist/primitives/field/mode.js +13 -0
  243. package/dist/primitives/hint/Hint.d.ts +31 -0
  244. package/dist/primitives/hint/Hint.js +42 -0
  245. package/dist/primitives/hint/index.d.ts +1 -0
  246. package/dist/primitives/hint/index.js +1 -0
  247. package/dist/primitives/index.d.ts +19 -0
  248. package/dist/primitives/index.js +20 -0
  249. package/dist/primitives/menu/Menu.d.ts +100 -0
  250. package/dist/primitives/menu/Menu.js +59 -0
  251. package/dist/primitives/menu/index.d.ts +1 -0
  252. package/dist/primitives/menu/index.js +2 -0
  253. package/dist/primitives/notice/Callout.d.ts +49 -0
  254. package/dist/primitives/notice/Callout.js +28 -0
  255. package/dist/primitives/notice/Notice.d.ts +45 -0
  256. package/dist/primitives/notice/Notice.js +61 -0
  257. package/dist/primitives/notice/Toast.d.ts +55 -0
  258. package/dist/primitives/notice/Toast.js +142 -0
  259. package/dist/primitives/notice/index.d.ts +3 -0
  260. package/dist/primitives/notice/index.js +5 -0
  261. package/dist/primitives/overlay/Popover.d.ts +6 -0
  262. package/dist/primitives/overlay/Popover.js +27 -0
  263. package/dist/primitives/overlay/PopoverTrigger.d.ts +55 -0
  264. package/dist/primitives/overlay/PopoverTrigger.js +38 -0
  265. package/dist/primitives/overlay/Tooltip.d.ts +38 -0
  266. package/dist/primitives/overlay/Tooltip.js +74 -0
  267. package/dist/primitives/overlay/index.d.ts +3 -0
  268. package/dist/primitives/overlay/index.js +5 -0
  269. package/dist/primitives/picker/Picker.d.ts +48 -0
  270. package/dist/primitives/picker/Picker.js +150 -0
  271. package/dist/primitives/picker/index.d.ts +1 -0
  272. package/dist/primitives/picker/index.js +2 -0
  273. package/dist/primitives/surface/Peek.d.ts +60 -0
  274. package/dist/primitives/surface/Peek.js +71 -0
  275. package/dist/primitives/surface/index.d.ts +1 -0
  276. package/dist/primitives/surface/index.js +2 -0
  277. package/dist/primitives/table/CellEditor.d.ts +14 -0
  278. package/dist/primitives/table/CellEditor.js +83 -0
  279. package/dist/primitives/table/DataTable.d.ts +135 -0
  280. package/dist/primitives/table/DataTable.js +244 -0
  281. package/dist/primitives/table/EditableTable.d.ts +96 -0
  282. package/dist/primitives/table/EditableTable.js +423 -0
  283. package/dist/primitives/table/PivotTable.d.ts +14 -0
  284. package/dist/primitives/table/PivotTable.js +0 -0
  285. package/dist/primitives/table/ReportTable.d.ts +38 -0
  286. package/dist/primitives/table/ReportTable.js +106 -0
  287. package/dist/primitives/table/RowActions.d.ts +16 -0
  288. package/dist/primitives/table/RowActions.js +28 -0
  289. package/dist/primitives/table/SheetTable.d.ts +70 -0
  290. package/dist/primitives/table/SheetTable.js +197 -0
  291. package/dist/primitives/table/Table.d.ts +106 -0
  292. package/dist/primitives/table/Table.js +116 -0
  293. package/dist/primitives/table/index.d.ts +7 -0
  294. package/dist/primitives/table/index.js +9 -0
  295. package/dist/primitives/tabs/Tabs.d.ts +91 -0
  296. package/dist/primitives/tabs/Tabs.js +56 -0
  297. package/dist/primitives/tabs/index.d.ts +1 -0
  298. package/dist/primitives/tabs/index.js +1 -0
  299. package/dist/primitives/tag/Tag.d.ts +36 -0
  300. package/dist/primitives/tag/Tag.js +68 -0
  301. package/dist/primitives/tag/Tags.d.ts +57 -0
  302. package/dist/primitives/tag/Tags.js +104 -0
  303. package/dist/primitives/tag/TagsField.d.ts +32 -0
  304. package/dist/primitives/tag/TagsField.js +182 -0
  305. package/dist/primitives/tag/choices.d.ts +38 -0
  306. package/dist/primitives/tag/choices.js +26 -0
  307. package/dist/primitives/tag/index.d.ts +4 -0
  308. package/dist/primitives/tag/index.js +8 -0
  309. package/dist/primitives/toolbar/Toolbar.d.ts +13 -0
  310. package/dist/primitives/toolbar/Toolbar.js +36 -0
  311. package/dist/primitives/toolbar/index.d.ts +1 -0
  312. package/dist/primitives/toolbar/index.js +2 -0
  313. package/dist/primitives/value/Copyable.d.ts +37 -0
  314. package/dist/primitives/value/Copyable.js +50 -0
  315. package/dist/primitives/value/Fecha.d.ts +12 -0
  316. package/dist/primitives/value/Fecha.js +80 -0
  317. package/dist/primitives/value/Meter.d.ts +29 -0
  318. package/dist/primitives/value/Meter.js +46 -0
  319. package/dist/primitives/value/Metric.d.ts +42 -0
  320. package/dist/primitives/value/Metric.js +38 -0
  321. package/dist/primitives/value/Person.d.ts +90 -0
  322. package/dist/primitives/value/Person.js +208 -0
  323. package/dist/primitives/value/Relation.d.ts +69 -0
  324. package/dist/primitives/value/Relation.js +57 -0
  325. package/dist/primitives/value/Totals.d.ts +26 -0
  326. package/dist/primitives/value/Totals.js +4 -0
  327. package/dist/primitives/value/Value.d.ts +77 -0
  328. package/dist/primitives/value/Value.js +119 -0
  329. package/dist/primitives/value/index.d.ts +8 -0
  330. package/dist/primitives/value/index.js +9 -0
  331. package/dist/tokens.css +1025 -0
  332. package/dist/views/index.d.ts +2 -0
  333. package/dist/views/index.js +3 -0
  334. package/dist/views/list/List.d.ts +113 -0
  335. package/dist/views/list/List.js +97 -0
  336. package/dist/views/list/ListView.d.ts +6 -0
  337. package/dist/views/list/ListView.js +269 -0
  338. package/dist/views/list/columns.d.ts +18 -0
  339. package/dist/views/list/columns.js +27 -0
  340. package/dist/views/list/derive.d.ts +76 -0
  341. package/dist/views/list/derive.js +181 -0
  342. package/dist/views/list/index.d.ts +2 -0
  343. package/dist/views/list/index.js +4 -0
  344. package/dist/views/list/presentations.d.ts +39 -0
  345. package/dist/views/list/presentations.js +103 -0
  346. package/dist/views/list/types.d.ts +272 -0
  347. package/dist/views/list/types.js +1 -0
  348. package/dist/views/list/useNarrowList.d.ts +17 -0
  349. package/dist/views/list/useNarrowList.js +32 -0
  350. package/dist/views/record/Record.d.ts +126 -0
  351. package/dist/views/record/Record.js +141 -0
  352. package/dist/views/record/index.d.ts +1 -0
  353. package/dist/views/record/index.js +2 -0
  354. package/package.json +109 -4
@@ -0,0 +1,76 @@
1
+ import type { BlockStatus } from "./Block.js";
2
+ import { type LucideIcon } from "lucide-react";
3
+ import { type ReactNode } from "react";
4
+ /**
5
+ * Context: a procedure laid flat — every stage of one piece of work on a single
6
+ * page, each collapsed to its title until you need it.
7
+ *
8
+ * ── Why not a `Wizard` ──────────────────────────────────────────────
9
+ *
10
+ * A wizard shows one step and hides the rest, which is right when the steps are
11
+ * a single sitting and wrong when they are the work itself. This is for work
12
+ * that spans days: you come back to a half-done piece and the first thing you
13
+ * need is **where it stands** — which stages are settled, which are waiting on
14
+ * you, which cannot start yet — and a wizard answers that with one screen and a
15
+ * «back» button.
16
+ *
17
+ * ── Why collapsed and not a `Block` per stage ───────────────────────
18
+ *
19
+ * `Block` already carries the half of this that matters — a header that says
20
+ * «Required» or «(Incomplete)» so you can see what is missing WITHOUT opening
21
+ * anything. What it cannot do is *close*. Seven open blocks is a page nobody
22
+ * scrolls; seven closed ones with their state on the line is an index of the
23
+ * work, and that index IS the view.
24
+ *
25
+ * ── Locked is a sentence, never a disabled box ──────────────────────
26
+ *
27
+ * A stage that cannot start yet says **what would unlock it**, because «you
28
+ * cannot do this» without «do that first» is the failure mode that makes people
29
+ * click every section looking for the one that works.
30
+ */
31
+ export type StepProps = {
32
+ title: string;
33
+ /** The stage's own mark, to the left of the title — what the eye finds when
34
+ * scanning a closed stack. */
35
+ icon?: LucideIcon;
36
+ /** «Required», «(Incomplete)», «(Complete)» — whatever this stage's state is
37
+ * called by whoever composes; the kit does not know what complete means. */
38
+ status?: BlockStatus;
39
+ /** Present when the stage cannot be opened yet. The sentence says what would
40
+ * unlock it, and it is shown in place of the contents. */
41
+ locked?: string;
42
+ /** What can be done with this stage, on its header line. Hidden while closed:
43
+ * an action you cannot see the context of is an action you press by mistake. */
44
+ actions?: ReactNode;
45
+ open: boolean;
46
+ onToggle: () => void;
47
+ children: ReactNode;
48
+ };
49
+ /**
50
+ * One stage. Controlled on purpose: which stage is open is a fact about where
51
+ * the work stands, so the page owns it — completing one can open the next, and
52
+ * arriving at a half-done piece can open the one that is waiting.
53
+ */
54
+ export declare function Step({ title, icon: Icon, status, locked, actions, open, onToggle, children, }: StepProps): import("react").JSX.Element;
55
+ export type StepsProps = {
56
+ children: ReactNode;
57
+ };
58
+ /** The stack. One frame: the outer corners are the only rounded ones, and the
59
+ * stages share a hairline. A box around a box would be two frames arguing. */
60
+ export declare function Steps({ children }: StepsProps): import("react").JSX.Element;
61
+ /**
62
+ * Which stage is open, when the page has no better answer than «one at a time».
63
+ *
64
+ * Returns the current id and a toggle: pressing the open one closes it, so a
65
+ * fully collapsed stack —the index of the work— is always one click away.
66
+ *
67
+ * `openStep` exists for the rail: the button that says what to do next lives up
68
+ * there with the other actions, and pressing it has to open the stage down here
69
+ * that does it.
70
+ */
71
+ export declare function useSteps(initial?: string): {
72
+ open: string | undefined;
73
+ isOpen: (id: string) => boolean;
74
+ toggle: (id: string) => void;
75
+ openStep: (id: string) => void;
76
+ };
@@ -0,0 +1,43 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Tag } from "../tag/Tag.js";
4
+ import { ChevronDown, Lock } from "lucide-react";
5
+ import { useState } from "react";
6
+ /**
7
+ * One stage. Controlled on purpose: which stage is open is a fact about where
8
+ * the work stands, so the page owns it — completing one can open the next, and
9
+ * arriving at a half-done piece can open the one that is waiting.
10
+ */
11
+ export function Step({ title, icon: Icon, status, locked, actions, open, onToggle, children, }) {
12
+ const isLocked = locked !== undefined;
13
+ const expanded = open && !isLocked;
14
+ return (_jsxs("section", { className: "min-w-0 border-t border-line bg-surface first:border-t-0", children: [_jsx("h3", { className: "min-w-0", children: _jsxs("button", { type: "button", onClick: onToggle, "aria-expanded": expanded,
15
+ /* Open is a ground bar: the stage you are in is the only row with a
16
+ fill. Closed rows stay on the paper, so the stack still reads as
17
+ an index. */
18
+ className: `group/step relative flex w-full min-w-0 items-center px-3 py-2.5 text-left ${expanded ? "border-b border-line bg-ground" : ""} ${isLocked ? "cursor-default" : "cursor-pointer"}`, children: [isLocked ? null : (_jsx("span", { "aria-hidden": true, className: "pointer-events-none absolute inset-x-1 inset-y-0.5 rounded-control group-hover/step:bg-ground" })), _jsxs("span", { className: "relative z-10 flex min-w-0 flex-1 items-center gap-3", children: [Icon === undefined ? null : (_jsx(Icon, { className: `size-(--size-icon-md) shrink-0 ${isLocked ? "text-muted/60" : "text-muted"}`, "aria-hidden": true })), _jsx("span", { className: `min-w-0 truncate text-meta font-medium ${isLocked ? "text-muted" : "text-ink"}`, children: title }), isLocked ? (_jsx(Lock, { className: "size-(--size-icon-sm) shrink-0 text-muted/60", "aria-hidden": true })) : status === undefined ? null : (_jsx(Tag, { tone: status.tone ?? "neutral", children: status.label })), _jsx(ChevronDown, { className: `ml-auto size-(--size-icon-md) shrink-0 text-muted transition-transform ${expanded ? "rotate-180" : ""}`, "aria-hidden": true })] })] }) }), !expanded ? null : (_jsxs("div", { className: "px-3 py-3", children: [actions === undefined ? null : (_jsx("div", { className: "mb-3 flex flex-wrap items-center gap-1", children: actions })), children] })), !isLocked ? null : (_jsx("p", { className: "border-t border-line px-3 py-2 text-micro text-muted", children: locked }))] }));
19
+ }
20
+ /** The stack. One frame: the outer corners are the only rounded ones, and the
21
+ * stages share a hairline. A box around a box would be two frames arguing. */
22
+ export function Steps({ children }) {
23
+ return (_jsx("div", { className: "flex min-w-0 flex-col overflow-hidden rounded-box border border-line bg-surface", children: children }));
24
+ }
25
+ /**
26
+ * Which stage is open, when the page has no better answer than «one at a time».
27
+ *
28
+ * Returns the current id and a toggle: pressing the open one closes it, so a
29
+ * fully collapsed stack —the index of the work— is always one click away.
30
+ *
31
+ * `openStep` exists for the rail: the button that says what to do next lives up
32
+ * there with the other actions, and pressing it has to open the stage down here
33
+ * that does it.
34
+ */
35
+ export function useSteps(initial) {
36
+ const [open, setOpen] = useState(initial);
37
+ return {
38
+ open,
39
+ isOpen: (id) => open === id,
40
+ toggle: (id) => setOpen((current) => (current === id ? undefined : id)),
41
+ openStep: (id) => setOpen(id),
42
+ };
43
+ }
@@ -0,0 +1,4 @@
1
+ export { Block, type BlockProps, type BlockStatus, Rule, SectionHeader, } from "./Block.js";
2
+ export { FullWidth, SectionGrid } from "./SectionGrid.js";
3
+ export { Line } from "./Line.js";
4
+ export { Step, type StepProps, Steps, type StepsProps, useSteps, } from "./Steps.js";
@@ -0,0 +1,7 @@
1
+ /* The structure of a view: where each section starts and ends. */
2
+ export { Block, Rule, SectionHeader, } from "./Block.js";
3
+ export { FullWidth, SectionGrid } from "./SectionGrid.js";
4
+ /* La línea de tres zonas: lo que centra el centro contra la LÍNEA y no contra
5
+ lo que sobra. La usan las dos bandas — la del registro y la de la lista. */
6
+ export { Line } from "./Line.js";
7
+ export { Step, Steps, useSteps, } from "./Steps.js";
@@ -0,0 +1,137 @@
1
+ import type { Surface } from "../../foundations/types.js";
2
+ import type { LucideIcon } from "lucide-react";
3
+ import type { ComponentPropsWithRef, ReactNode } from "react";
4
+ export type ButtonRole = "primary" | "secondary" | "ghost" | "danger";
5
+ export type ButtonSize = "sm" | "md" | "lg";
6
+ export type { Surface };
7
+ /** The icon accompanies the label and never replaces it.
8
+ *
9
+ * Size `sm` (12px) on the small button and `md` (16px) on the medium one, with
10
+ * the stroke that belongs to each — see the iconography scale: what stays
11
+ * constant is the optical weight, not the number. */
12
+ /** The look of a button, without the button.
13
+ *
14
+ * Context: it exists because there are things that LOOK like a button and are
15
+ * not — a menu trigger is the case. Without this, each one copies the classes
16
+ * and by the third copy they no longer match: that is exactly what happened
17
+ * with «+ Crear», which was the only primary button in the product with a
18
+ * different height. */
19
+ export declare function buttonClasses(role?: ButtonRole, size?: ButtonSize, surface?: Surface): string;
20
+ /** What kind of thing a trigger opens. `menu` is the default because it is what the
21
+ * `expanded` prop always announced; a `PopoverTrigger` opens a `dialog`, a picker a
22
+ * `listbox`. */
23
+ export type ButtonPopup = "menu" | "dialog" | "listbox";
24
+ /** The native attributes a system button lets through.
25
+ *
26
+ * Context: React Aria's triggers (`TooltipTrigger`, `DialogTrigger`, a `Toolbar`) talk to
27
+ * the control by handing it a `ref` and DOM props — pointer and focus handlers,
28
+ * `aria-describedby`, `aria-expanded`. A button that drops them is a button the tooltip
29
+ * never learns was hovered. So everything native goes through, and `ref` is a plain prop
30
+ * (React 19).
31
+ *
32
+ * What stays out on purpose: `className` and `style` — the look is the role, the size and
33
+ * the surface, and a primitive that takes classes from outside stops being the one place
34
+ * where a button is decided; `role`, because here it is the button's ROLE in the hierarchy
35
+ * (`primary`…), not an ARIA role; and the attributes the props below already own. */
36
+ type NativeProps<E extends "button" | "a", Own extends string> = Omit<ComponentPropsWithRef<E>, "children" | "className" | "style" | "role" | Own>;
37
+ export type ButtonProps = NativeProps<"button", "type" | "disabled" | "title" | "aria-haspopup" | "aria-expanded"> & {
38
+ children: ReactNode;
39
+ role?: ButtonRole;
40
+ size?: ButtonSize;
41
+ surface?: Surface;
42
+ /** To the left of the label. Never alone: a button with only an icon is an
43
+ * `IconButton` and has its own piece, with its mandatory `aria-label`. */
44
+ icon?: LucideIcon;
45
+ disabled?: boolean;
46
+ title?: string;
47
+ /** `submit` for the button that sends the form it is in. `button` by default, so a
48
+ * button dropped inside a `<form>` never submits it by accident. */
49
+ type?: "button" | "submit";
50
+ /** Context: the button that opens a dropdown has to say so. Without
51
+ * `aria-expanded`, a screen reader announces it as an ordinary button and
52
+ * whoever uses it does not know there is something open underneath. */
53
+ expanded?: boolean;
54
+ /** What it opens, written to `aria-haspopup`. `menu` when absent. */
55
+ haspopup?: ButtonPopup;
56
+ };
57
+ export declare function Button({ children, role, size, surface: surfaceProp, icon: Icon, disabled, title, type, expanded, haspopup, ref, ...native }: ButtonProps): import("react").JSX.Element;
58
+ export type ButtonLinkProps = NativeProps<"a", "href" | "download" | "title"> & {
59
+ children: ReactNode;
60
+ href: string;
61
+ role?: ButtonRole;
62
+ size?: ButtonSize;
63
+ surface?: Surface;
64
+ icon?: LucideIcon;
65
+ /** The name it is saved with. `true` lets the server decide it. */
66
+ download?: string | boolean;
67
+ title?: string;
68
+ };
69
+ /**
70
+ * The same button, but it is a real link.
71
+ *
72
+ * Context: it exists because a `<button>` that navigates breaks middle-click,
73
+ * «open in a new tab» and «save as» — and downloading a file is exactly that.
74
+ * The look is shared entirely with `Button`: two different silhouettes for
75
+ * «download» and «synthesize» in the same bar is the problem this file exists
76
+ * in order not to have.
77
+ *
78
+ * It does not carry `inline-block`: a flex item gets blockified anyway, and with
79
+ * that class the `<a>`'s box measured its padding with zero content and the text
80
+ * was painted on top of its neighbor.
81
+ */
82
+ export declare function ButtonLink({ children, href, role, size, surface: surfaceProp, icon: Icon, download, title, ref, ...native }: ButtonLinkProps): import("react").JSX.Element;
83
+ /** The `IconButton` is SQUARE: its side is the height of its step. An icon
84
+ * button narrower than it is tall has less area than it appears to, and misses
85
+ * when you aim right at the edges. */
86
+ export type IconTone = "normal" | "action" | "danger";
87
+ export type IconButtonProps = NativeProps<"button", "type" | "disabled" | "title" | "aria-label" | "aria-haspopup" | "aria-expanded"> & {
88
+ icon: LucideIcon;
89
+ /** What is read aloud and shown in the tooltip. It is not optional: an icon
90
+ * alone does not say what it does, and whoever knows already knew what to
91
+ * press. */
92
+ label: string;
93
+ size?: ButtonSize;
94
+ surface?: Surface;
95
+ tone?: IconTone;
96
+ disabled?: boolean;
97
+ type?: "button" | "submit";
98
+ /** Opens a menu and says whether it is open. Without this, a screen reader
99
+ * announces «button» where there is a dropdown. */
100
+ expanded?: boolean;
101
+ /** What it opens, written to `aria-haspopup`. `menu` when absent. */
102
+ haspopup?: ButtonPopup;
103
+ /** The system's `Tooltip` with the `label`, on hover and focus. On by default; `false`
104
+ * falls back to the native `title`. */
105
+ tooltip?: boolean;
106
+ };
107
+ /** Only an icon, and that is why the label is mandatory even though it is not
108
+ * drawn.
109
+ *
110
+ * It exists because «download» was a grey 11 px `↓` character with no box: the
111
+ * most frequent action of a row was the hardest one to hit. Here it has its
112
+ * full control height and a background on hover — the target is the same as
113
+ * that of a button with text, only without the text. */
114
+ export declare function IconButton({ icon: Icon, label, size, surface: surfaceProp, tone, disabled, type, expanded, haspopup, tooltip, ref, ...native }: IconButtonProps): import("react").JSX.Element;
115
+ export type IconLinkProps = NativeProps<"a", "href" | "download" | "title" | "aria-label"> & {
116
+ icon: LucideIcon;
117
+ label: string;
118
+ href: string;
119
+ /** The name it is saved with. Present = the browser downloads the file
120
+ * instead of navigating. */
121
+ download?: string;
122
+ size?: ButtonSize;
123
+ surface?: Surface;
124
+ tone?: IconTone;
125
+ /** As in `IconButton`: the system's `Tooltip` by default, the native `title` with
126
+ * `false`. */
127
+ tooltip?: boolean;
128
+ };
129
+ /**
130
+ * The same icon, but it is a real link.
131
+ *
132
+ * Context: it exists for the same reason as `ButtonLink` — downloading a file or
133
+ * going to a view is NOT pressing a button. With a `<button>` you lose
134
+ * middle-click, «copy the address» and the browser's `download`, which is the
135
+ * only thing that keeps a 150 MB CSV from going through the tab's memory.
136
+ */
137
+ export declare function IconLink({ icon: Icon, label, href, download, size, surface: surfaceProp, tone, tooltip, ref, ...native }: IconLinkProps): import("react").JSX.Element;
@@ -0,0 +1,234 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ /* ==================================================================== *
4
+ * BUTTON — DO.
5
+ *
6
+ * The product had THREE contradictory primaries at the same time: a blue
7
+ * `+ Crear`, a golden `Registrar pago` and a blue `+ Importar facturas`. Three equal
8
+ * weights saying three different things, and therefore no hierarchy at all.
9
+ *
10
+ * The rule that fixes it is four roles and not one more:
11
+ *
12
+ * primary DO — solid gold, `ink` on top (6.98:1)
13
+ * secondary do without urgency — a box with an edge
14
+ * ghost goes along — no weight, only the color of «go»
15
+ * danger DESTROY — and it cannot be undone
16
+ *
17
+ * **Only one solid gold per surface.** `+ Crear` is the chrome's CTA,
18
+ * `Registrar pago` the panel's: they do not compete because they do not share a
19
+ * surface.
20
+ *
21
+ * ── Why a labeled button is a box, and an icon target is a circle ──
22
+ *
23
+ * `pill` is «what is a capsule and not a box» — a tag, a chip, a counter.
24
+ * A labeled button is the opposite: a box that gets pressed. A row of actions
25
+ * in capsules reads as a row of tags, and then what has to be pressed and
26
+ * what merely informs have the same silhouette. `radius/control` is the step
27
+ * for «what is touched and measures one line of text».
28
+ *
29
+ * An icon-only target has no line of text. It is a square hit area, and a
30
+ * square with an 8px corner still reads as a leftover box. The circle is the
31
+ * silhouette of that target (the same call Spectrum makes for icon-only
32
+ * buttons). It does not meet the tag: the tag has text, the circle does not.
33
+ *
34
+ * ── The weight of the type is hierarchy too ─────────────────────────
35
+ *
36
+ * The four roles do NOT carry the same weight, and that is deliberate. The
37
+ * primary goes in `semibold` because it is the only thing on the screen that
38
+ * has to be seen from far away; the rest in `medium`. Putting all four at the
39
+ * same weight forces color to do the whole job of hierarchy by itself — and
40
+ * then someone glancing at the screen out of the corner of their eye, or who
41
+ * cannot tell the gold from the red apart, sees four identical buttons.
42
+ *
43
+ * ── `surface`: the prop wins, the context is the default ────────────
44
+ *
45
+ * The same piece lives on both surfaces and does NOT behave the same: on the
46
+ * canvas, «ghost» is painted with the navigation blue (7.48:1); on the chrome
47
+ * that blue gives 1.4:1 and the lightened one has to be used. The band that is
48
+ * dark (`AppFrame`'s bar, a `Record.Rail surface="chrome"`) provides its surface
49
+ * through `SurfaceProvider`, so a button written inside it takes the right
50
+ * palette without remembering to say so — which is how a navy band ends up with
51
+ * canvas-colored buttons. The explicit prop still wins, so a one-off decision
52
+ * stays auditable by reading the JSX.
53
+ *
54
+ * ── What this file does NOT do ──────────────────────────────────────
55
+ *
56
+ * It does not know what an invoice is, it does not call anything, it has no
57
+ * state and it does not write a single value by hand: every height, every
58
+ * radius and every color comes from a token. If it needed any of those things,
59
+ * it would not be a primitive.
60
+ * ==================================================================== */
61
+ import { useSurface } from "../../foundations/surface.js";
62
+ import { Tooltip, TooltipTrigger, useInTooltipTrigger, } from "../overlay/Tooltip.js";
63
+ /** The height comes from `--size-control-*`; the padding and the type follow.
64
+ *
65
+ * sm a row, the header of a block
66
+ * md the working size — the action bar of a record
67
+ * lg a SINGLE, prominent action: a dialog's CTA, an empty state
68
+ *
69
+ * **Every button in the same row measures the same.** `lg` is not «the
70
+ * primary»: it is for when the button is alone. Mixing heights in a bar looks
71
+ * like an alignment mistake, not like hierarchy — and hierarchy is already
72
+ * done by the color and the weight, which work without changing the size of
73
+ * anything.
74
+ *
75
+ * The padding grows FASTER than the height (3 → 4 → 6). A tall, narrow button
76
+ * reads as a stretched icon; what gives it presence is the air on the sides of
77
+ * the label, not the height alone. */
78
+ const SIZE = {
79
+ sm: "h-(--size-control-sm) px-3 gap-1.5 text-meta",
80
+ md: "h-(--size-control-md) px-4 gap-2 text-body",
81
+ lg: "h-(--size-control-lg) px-6 gap-2 text-lead",
82
+ };
83
+ /** The weight per role. See the header note: it is not aesthetics, it is half
84
+ * of the hierarchy — the half that keeps working out of the corner of your
85
+ * eye. */
86
+ const WEIGHT = {
87
+ primary: "font-semibold",
88
+ secondary: "font-medium",
89
+ ghost: "font-medium",
90
+ danger: "font-semibold",
91
+ };
92
+ /** Each role on its two surfaces. The table exists so that adding a third
93
+ * surface is one column here and not an `if` scattered across the tree — and
94
+ * so that the day a combination is missing, the compiler says so. */
95
+ const ROLE = {
96
+ primary: {
97
+ // `btn-accent` is the brand gradient, use 3 of 4. Barely a glint of metal:
98
+ // a strong gradient turns a tool into a landing page.
99
+ canvas: "btn-accent border border-transparent hover:brightness-[1.04] active:brightness-95",
100
+ chrome: "btn-accent border border-transparent hover:brightness-[1.04] active:brightness-95",
101
+ },
102
+ secondary: {
103
+ // Quiet on purpose. The blue belongs to `ghost` (go) and the gold to
104
+ // `primary` (do). A secondary painted in the action color is a second
105
+ // primary, which is the defect this file exists to stop.
106
+ canvas: "border border-line-strong bg-ink/6 text-ink hover:bg-ink/10",
107
+ chrome: "border border-chrome-muted/30 bg-chrome-raised text-chrome-ink hover:bg-chrome-ink/10",
108
+ },
109
+ ghost: {
110
+ canvas: "border border-transparent text-primary hover:bg-primary/8",
111
+ chrome: "border border-transparent text-chrome-link hover:bg-chrome-ink/8",
112
+ },
113
+ danger: {
114
+ canvas: "border border-transparent bg-danger text-surface hover:bg-danger-hover",
115
+ chrome: "border border-transparent bg-danger text-surface hover:bg-danger-hover",
116
+ },
117
+ };
118
+ /** The icon accompanies the label and never replaces it.
119
+ *
120
+ * Size `sm` (12px) on the small button and `md` (16px) on the medium one, with
121
+ * the stroke that belongs to each — see the iconography scale: what stays
122
+ * constant is the optical weight, not the number. */
123
+ /** The look of a button, without the button.
124
+ *
125
+ * Context: it exists because there are things that LOOK like a button and are
126
+ * not — a menu trigger is the case. Without this, each one copies the classes
127
+ * and by the third copy they no longer match: that is exactly what happened
128
+ * with «+ Crear», which was the only primary button in the product with a
129
+ * different height. */
130
+ export function buttonClasses(role = "secondary", size = "md", surface = "canvas") {
131
+ return `inline-flex shrink-0 items-center justify-center rounded-control whitespace-nowrap transition-[background-color,border-color,filter,color] duration-(--duration-instant) ease-(--ease-out) disabled:pointer-events-none disabled:opacity-45 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary ${SIZE[size]} ${WEIGHT[role]} ${ROLE[role][surface]}`;
132
+ }
133
+ const ICON = {
134
+ sm: { size: "size-(--size-icon-sm)", stroke: 2 },
135
+ md: { size: "size-(--size-icon-md)", stroke: 1.75 },
136
+ lg: { size: "size-(--size-icon-lg)", stroke: 1.5 },
137
+ };
138
+ /** `aria-haspopup` + `aria-expanded`, only when the button opens something. Spread AFTER the
139
+ * native props, and only when it applies, so it never erases what a React Aria trigger
140
+ * already wrote. */
141
+ function popupAttrs(expanded, haspopup) {
142
+ if (expanded === undefined && haspopup === undefined)
143
+ return {};
144
+ return {
145
+ "aria-haspopup": haspopup ?? "menu",
146
+ ...(expanded === undefined ? {} : { "aria-expanded": expanded }),
147
+ };
148
+ }
149
+ export function Button({ children, role = "secondary", size = "md", surface: surfaceProp, icon: Icon, disabled = false, title, type = "button", expanded, haspopup, ref, ...native }) {
150
+ const surface = useSurface(surfaceProp);
151
+ const ic = ICON[size];
152
+ return (_jsxs("button", { ...native, ref: ref, type: type, title: title, disabled: disabled, ...popupAttrs(expanded, haspopup), "data-emphasis": role, className: buttonClasses(role, size, surface), children: [Icon === undefined ? null : (_jsx(Icon, { className: `${ic.size} shrink-0`, strokeWidth: ic.stroke, "aria-hidden": true })), children] }));
153
+ }
154
+ /**
155
+ * The same button, but it is a real link.
156
+ *
157
+ * Context: it exists because a `<button>` that navigates breaks middle-click,
158
+ * «open in a new tab» and «save as» — and downloading a file is exactly that.
159
+ * The look is shared entirely with `Button`: two different silhouettes for
160
+ * «download» and «synthesize» in the same bar is the problem this file exists
161
+ * in order not to have.
162
+ *
163
+ * It does not carry `inline-block`: a flex item gets blockified anyway, and with
164
+ * that class the `<a>`'s box measured its padding with zero content and the text
165
+ * was painted on top of its neighbor.
166
+ */
167
+ export function ButtonLink({ children, href, role = "secondary", size = "md", surface: surfaceProp, icon: Icon, download, title, ref, ...native }) {
168
+ const surface = useSurface(surfaceProp);
169
+ const ic = ICON[size];
170
+ return (_jsxs("a", { ...native, ref: ref, "data-emphasis": role, href: href, title: title, ...(download === undefined ? {} : { download }), className: `${buttonClasses(role, size, surface)} no-underline`, children: [Icon === undefined ? null : (_jsx(Icon, { className: `${ic.size} shrink-0`, strokeWidth: ic.stroke, "aria-hidden": true })), children] }));
171
+ }
172
+ /** Outside the component because the button and the link share it: the same
173
+ * icon has to look the same whether it leads to an action or to a file. */
174
+ const TONE = {
175
+ normal: {
176
+ canvas: "text-muted hover:bg-ink/6 hover:text-ink",
177
+ chrome: "text-chrome-muted hover:bg-chrome-ink/10 hover:text-chrome-ink",
178
+ },
179
+ action: {
180
+ canvas: "text-primary hover:bg-primary/8",
181
+ chrome: "text-chrome-link hover:bg-chrome-link/12",
182
+ },
183
+ danger: {
184
+ canvas: "text-danger hover:bg-danger/8",
185
+ chrome: "text-chrome-bad hover:bg-chrome-bad/12",
186
+ },
187
+ };
188
+ const BOX = "inline-flex shrink-0 items-center justify-center rounded-pill transition-colors duration-(--duration-instant) focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary";
189
+ const SQUARE = {
190
+ sm: "size-(--size-control-sm)",
191
+ md: "size-(--size-control-md)",
192
+ lg: "size-(--size-control-lg)",
193
+ };
194
+ /** The label of an icon, shown.
195
+ *
196
+ * Context: the native `title` was the only way to learn what an icon does, and it is the
197
+ * browser's tooltip — late, unstyled, never on keyboard focus. So an icon target draws the
198
+ * system's `Tooltip` with its `label` instead, on hover AND on focus, and drops the `title`
199
+ * so two tooltips never show at once. `false` brings the `title` back, for the rare icon
200
+ * whose name is already written next to it. Inside a `TooltipTrigger` the consumer's
201
+ * tooltip wins and this one steps aside. */
202
+ function Labelled({ label, tooltip, children, }) {
203
+ const wrapped = useInTooltipTrigger();
204
+ if (wrapped)
205
+ return children(undefined);
206
+ if (!tooltip)
207
+ return children(label);
208
+ return (_jsxs(TooltipTrigger, { children: [children(undefined), _jsx(Tooltip, { children: label })] }));
209
+ }
210
+ /** Only an icon, and that is why the label is mandatory even though it is not
211
+ * drawn.
212
+ *
213
+ * It exists because «download» was a grey 11 px `↓` character with no box: the
214
+ * most frequent action of a row was the hardest one to hit. Here it has its
215
+ * full control height and a background on hover — the target is the same as
216
+ * that of a button with text, only without the text. */
217
+ export function IconButton({ icon: Icon, label, size = "sm", surface: surfaceProp, tone = "normal", disabled = false, type = "button", expanded, haspopup, tooltip = true, ref, ...native }) {
218
+ const surface = useSurface(surfaceProp);
219
+ const ic = ICON[size];
220
+ return (_jsx(Labelled, { label: label, tooltip: tooltip, children: (title) => (_jsx("button", { ...native, ref: ref, type: type, title: title, "aria-label": label, disabled: disabled, ...popupAttrs(expanded, haspopup), className: `${BOX} disabled:pointer-events-none disabled:opacity-45 ${SQUARE[size]} ${TONE[tone][surface]}`, children: _jsx(Icon, { className: ic.size, strokeWidth: ic.stroke, "aria-hidden": true }) })) }));
221
+ }
222
+ /**
223
+ * The same icon, but it is a real link.
224
+ *
225
+ * Context: it exists for the same reason as `ButtonLink` — downloading a file or
226
+ * going to a view is NOT pressing a button. With a `<button>` you lose
227
+ * middle-click, «copy the address» and the browser's `download`, which is the
228
+ * only thing that keeps a 150 MB CSV from going through the tab's memory.
229
+ */
230
+ export function IconLink({ icon: Icon, label, href, download, size = "sm", surface: surfaceProp, tone = "action", tooltip = true, ref, ...native }) {
231
+ const surface = useSurface(surfaceProp);
232
+ const ic = ICON[size];
233
+ return (_jsx(Labelled, { label: label, tooltip: tooltip, children: (title) => (_jsx("a", { ...native, ref: ref, href: href, ...(download === undefined ? {} : { download }), title: title, "aria-label": label, className: `${BOX} ${SQUARE[size]} ${TONE[tone][surface]}`, children: _jsx(Icon, { className: ic.size, strokeWidth: ic.stroke, "aria-hidden": true }) })) }));
234
+ }
@@ -0,0 +1 @@
1
+ export { Button, buttonClasses, ButtonLink, type ButtonLinkProps, type ButtonPopup, type ButtonProps, type ButtonRole, type ButtonSize, IconButton, type IconButtonProps, IconLink, type IconLinkProps, type IconTone, } from "./Button.js";
@@ -0,0 +1,2 @@
1
+ /* DO. Four roles, three sizes, two surfaces. */
2
+ export { Button, buttonClasses, ButtonLink, IconButton, IconLink, } from "./Button.js";
@@ -0,0 +1,47 @@
1
+ import type { LucideIcon } from "lucide-react";
2
+ import type { ReactNode } from "react";
3
+ /**
4
+ * A thumbnail — the photo of a product, the logo of a customer — or, when there is none, its
5
+ * icon on a quiet tile. One size per context so a column of them lines up.
6
+ *
7
+ * Context: a list of forty products where only the name tells them apart makes people read
8
+ * every row; the picture is recognised without reading. The tile without a photo keeps the
9
+ * same size and place, so the rows that have one and the rows that do not still align.
10
+ */
11
+ export declare function Thumb({ src, alt, icon: Icon, size, }: {
12
+ /** The image. Without it, `icon` on a tile; without both, an empty tile. */
13
+ src?: string;
14
+ /** What the image shows, for whoever cannot see it. Required with `src`. */
15
+ alt?: string;
16
+ icon?: LucideIcon;
17
+ /** `sm` for a dense row (24 px), `md` for a table (32 px), `lg` for a card (48 px). */
18
+ size?: "sm" | "md" | "lg";
19
+ }): import("react").JSX.Element;
20
+ /**
21
+ * A cell with hierarchy: what the row IS on top, what tells it apart below — a product and its
22
+ * description, a customer and its NIT, an account and its code — with an optional picture.
23
+ *
24
+ * Context: a table cell used to hold one string. An ERP row is rarely one thing: a product line
25
+ * is «Soporte mensual» AND «plan estándar · SKU SOP-01», and putting them in two columns spends
26
+ * width on a column nobody sorts by, while squeezing them into one string loses the hierarchy.
27
+ * Two lines, the first in ink and the second small and quiet, read as one thing with a detail —
28
+ * the way the eye already reads a line of an invoice.
29
+ *
30
+ * It works in any cell: `DataTable` (a column's `cell`), `Table`/`Td`, the rows of a list, a
31
+ * card. The second line wraps or truncates (`wrap`); the first always truncates, with the full
32
+ * text on hover.
33
+ */
34
+ export declare function CellStack({ primary, secondary, media, wrap, }: {
35
+ /** What the row is — the name. In ink, one line. */
36
+ primary: ReactNode;
37
+ /** What tells it apart — a description, a code, a reference. Small and quiet. */
38
+ secondary?: ReactNode;
39
+ /** A picture or icon on the left: `{ src, alt }` or `{ icon }`. */
40
+ media?: {
41
+ src?: string;
42
+ alt?: string;
43
+ icon?: LucideIcon;
44
+ };
45
+ /** Let the second line wrap (a description) instead of truncating (a code). */
46
+ wrap?: boolean;
47
+ }): import("react").JSX.Element;
@@ -0,0 +1,33 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * A thumbnail — the photo of a product, the logo of a customer — or, when there is none, its
4
+ * icon on a quiet tile. One size per context so a column of them lines up.
5
+ *
6
+ * Context: a list of forty products where only the name tells them apart makes people read
7
+ * every row; the picture is recognised without reading. The tile without a photo keeps the
8
+ * same size and place, so the rows that have one and the rows that do not still align.
9
+ */
10
+ export function Thumb({ src, alt, icon: Icon, size = "md", }) {
11
+ const box = { sm: "size-6", md: "size-8", lg: "size-12" }[size];
12
+ if (src !== undefined) {
13
+ return (_jsx("img", { src: src, alt: alt ?? "", loading: "lazy", className: `${box} shrink-0 rounded-control bg-ink/5 object-cover` }));
14
+ }
15
+ return (_jsx("span", { "aria-hidden": true, className: `${box} flex shrink-0 items-center justify-center rounded-control bg-ink/5 text-muted`, children: Icon === undefined ? null : (_jsx(Icon, { className: size === "lg" ? "size-(--size-icon-lg)" : "size-(--size-icon-sm)", strokeWidth: 1.75 })) }));
16
+ }
17
+ /**
18
+ * A cell with hierarchy: what the row IS on top, what tells it apart below — a product and its
19
+ * description, a customer and its NIT, an account and its code — with an optional picture.
20
+ *
21
+ * Context: a table cell used to hold one string. An ERP row is rarely one thing: a product line
22
+ * is «Soporte mensual» AND «plan estándar · SKU SOP-01», and putting them in two columns spends
23
+ * width on a column nobody sorts by, while squeezing them into one string loses the hierarchy.
24
+ * Two lines, the first in ink and the second small and quiet, read as one thing with a detail —
25
+ * the way the eye already reads a line of an invoice.
26
+ *
27
+ * It works in any cell: `DataTable` (a column's `cell`), `Table`/`Td`, the rows of a list, a
28
+ * card. The second line wraps or truncates (`wrap`); the first always truncates, with the full
29
+ * text on hover.
30
+ */
31
+ export function CellStack({ primary, secondary, media, wrap = false, }) {
32
+ return (_jsxs("span", { className: "flex min-w-0 items-center gap-2.5", children: [media === undefined ? null : (_jsx(Thumb, { size: "md", ...(media.src === undefined ? {} : { src: media.src }), ...(media.alt === undefined ? {} : { alt: media.alt }), ...(media.icon === undefined ? {} : { icon: media.icon }) })), _jsxs("span", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate text-meta font-medium text-ink", ...(typeof primary === "string" ? { title: primary } : {}), children: primary }), secondary === undefined ? null : (_jsx("span", { className: `text-micro text-muted ${wrap ? "line-clamp-2" : "truncate"}`, ...(typeof secondary === "string" ? { title: secondary } : {}), children: secondary }))] })] }));
33
+ }
@@ -0,0 +1 @@
1
+ export { CellStack, Thumb } from "./CellStack.js";
@@ -0,0 +1,3 @@
1
+ /* LOS WIDGETS DE CELDA — lo que va adentro de una celda, una fila o una tarjeta con jerarquía:
2
+ lo principal en tinta, lo que lo distingue chico y callado, y la foto si la hay. */
3
+ export { CellStack, Thumb } from "./CellStack.js";
@@ -0,0 +1,16 @@
1
+ import { type ChoiceOption } from "./options.js";
2
+ import { type FormMode } from "../field/mode.js";
3
+ export type ComboBoxProps = {
4
+ /** The chosen option's `value`, or `null` when nothing is chosen. */
5
+ value: string | null;
6
+ onChange?: (next: string | null) => void;
7
+ options: readonly ChoiceOption[];
8
+ placeholder?: string;
9
+ disabled?: boolean;
10
+ id?: string;
11
+ describedBy?: string;
12
+ invalid?: boolean;
13
+ /** Forces the mode. By default it inherits the form's: in read mode it draws the label. */
14
+ mode?: FormMode;
15
+ };
16
+ export declare function ComboBox({ mode, ...props }: ComboBoxProps): import("react").JSX.Element;