@coffer-org/web-ui 4.0.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 (387) hide show
  1. package/README.md +87 -0
  2. package/dist/Markdown.d.ts +4 -0
  3. package/dist/Markdown.js +13 -0
  4. package/dist/MarkdownEditor.d.ts +6 -0
  5. package/dist/MarkdownEditor.js +83 -0
  6. package/dist/actions/ActionButton.d.ts +7 -0
  7. package/dist/actions/ActionButton.js +54 -0
  8. package/dist/actions/PopupBody.d.ts +4 -0
  9. package/dist/actions/PopupBody.js +8 -0
  10. package/dist/actions/TemplateHost.d.ts +4 -0
  11. package/dist/actions/TemplateHost.js +36 -0
  12. package/dist/actions/registry.d.ts +20 -0
  13. package/dist/actions/registry.js +9 -0
  14. package/dist/components/ExtendReasonInfo.d.ts +6 -0
  15. package/dist/components/ExtendReasonInfo.js +12 -0
  16. package/dist/components/FavoriteButton.d.ts +8 -0
  17. package/dist/components/FavoriteButton.js +7 -0
  18. package/dist/components/Icon.d.ts +6 -0
  19. package/dist/components/Icon.js +12 -0
  20. package/dist/components/PageBody.d.ts +4 -0
  21. package/dist/components/PageBody.js +4 -0
  22. package/dist/components/RecordInline.d.ts +8 -0
  23. package/dist/components/RecordInline.js +29 -0
  24. package/dist/components/SaveStatus.d.ts +1 -0
  25. package/dist/components/SaveStatus.js +11 -0
  26. package/dist/components/Toolbar.d.ts +5 -0
  27. package/dist/components/Toolbar.js +4 -0
  28. package/dist/components/icons/color.d.ts +2 -0
  29. package/dist/components/icons/color.js +10 -0
  30. package/dist/components/icons/flag.d.ts +2 -0
  31. package/dist/components/icons/flag.js +6 -0
  32. package/dist/components/icons/lucide.d.ts +2 -0
  33. package/dist/components/icons/lucide.js +111 -0
  34. package/dist/components/icons/registry.d.ts +4 -0
  35. package/dist/components/icons/registry.js +22 -0
  36. package/dist/components/icons/simple.d.ts +2 -0
  37. package/dist/components/icons/simple.js +15 -0
  38. package/dist/components/icons/tabler.d.ts +2 -0
  39. package/dist/components/icons/tabler.js +11 -0
  40. package/dist/components/ui/FieldLabel.d.ts +9 -0
  41. package/dist/components/ui/FieldLabel.js +4 -0
  42. package/dist/components/ui/badge.d.ts +9 -0
  43. package/dist/components/ui/badge.js +19 -0
  44. package/dist/components/ui/button.d.ts +11 -0
  45. package/dist/components/ui/button.js +33 -0
  46. package/dist/components/ui/calendar.d.ts +8 -0
  47. package/dist/components/ui/calendar.js +72 -0
  48. package/dist/components/ui/card.d.ts +8 -0
  49. package/dist/components/ui/card.js +16 -0
  50. package/dist/components/ui/checkbox.d.ts +4 -0
  51. package/dist/components/ui/checkbox.js +8 -0
  52. package/dist/components/ui/chip-cls.d.ts +1 -0
  53. package/dist/components/ui/chip-cls.js +1 -0
  54. package/dist/components/ui/combobox.d.ts +24 -0
  55. package/dist/components/ui/combobox.js +58 -0
  56. package/dist/components/ui/confirm.d.ts +10 -0
  57. package/dist/components/ui/confirm.js +37 -0
  58. package/dist/components/ui/date-picker.d.ts +5 -0
  59. package/dist/components/ui/date-picker.js +16 -0
  60. package/dist/components/ui/dialog.d.ts +16 -0
  61. package/dist/components/ui/dialog.js +27 -0
  62. package/dist/components/ui/dropdown-menu.d.ts +15 -0
  63. package/dist/components/ui/dropdown-menu.js +18 -0
  64. package/dist/components/ui/input-group.d.ts +16 -0
  65. package/dist/components/ui/input-group.js +56 -0
  66. package/dist/components/ui/input.d.ts +3 -0
  67. package/dist/components/ui/input.js +6 -0
  68. package/dist/components/ui/label.d.ts +4 -0
  69. package/dist/components/ui/label.js +7 -0
  70. package/dist/components/ui/mask-input.d.ts +61 -0
  71. package/dist/components/ui/mask-input.js +1052 -0
  72. package/dist/components/ui/month-year-picker.d.ts +10 -0
  73. package/dist/components/ui/month-year-picker.js +46 -0
  74. package/dist/components/ui/option-icon.d.ts +15 -0
  75. package/dist/components/ui/option-icon.js +19 -0
  76. package/dist/components/ui/phone-input.d.ts +13 -0
  77. package/dist/components/ui/phone-input.js +53 -0
  78. package/dist/components/ui/popover.d.ts +11 -0
  79. package/dist/components/ui/popover.js +28 -0
  80. package/dist/components/ui/rating.d.ts +44 -0
  81. package/dist/components/ui/rating.js +501 -0
  82. package/dist/components/ui/scroll-area.d.ts +5 -0
  83. package/dist/components/ui/scroll-area.js +11 -0
  84. package/dist/components/ui/select-combobox.d.ts +25 -0
  85. package/dist/components/ui/select-combobox.js +36 -0
  86. package/dist/components/ui/select.d.ts +13 -0
  87. package/dist/components/ui/select.js +24 -0
  88. package/dist/components/ui/separator.d.ts +4 -0
  89. package/dist/components/ui/separator.js +7 -0
  90. package/dist/components/ui/switch.d.ts +4 -0
  91. package/dist/components/ui/switch.js +7 -0
  92. package/dist/components/ui/textarea.d.ts +3 -0
  93. package/dist/components/ui/textarea.js +6 -0
  94. package/dist/components/ui/time-picker.d.ts +84 -0
  95. package/dist/components/ui/time-picker.js +1258 -0
  96. package/dist/components/visually-hidden-input.d.ts +10 -0
  97. package/dist/components/visually-hidden-input.js +96 -0
  98. package/dist/hooks/use-as-ref.d.ts +3 -0
  99. package/dist/hooks/use-as-ref.js +10 -0
  100. package/dist/hooks/use-isomorphic-layout-effect.d.ts +3 -0
  101. package/dist/hooks/use-isomorphic-layout-effect.js +3 -0
  102. package/dist/hooks/use-lazy-ref.d.ts +3 -0
  103. package/dist/hooks/use-lazy-ref.js +9 -0
  104. package/dist/hooks/use-media-query.d.ts +3 -0
  105. package/dist/hooks/use-media-query.js +26 -0
  106. package/dist/host-services.d.ts +47 -0
  107. package/dist/host-services.js +35 -0
  108. package/dist/index.d.ts +70 -0
  109. package/dist/index.js +69 -0
  110. package/dist/layout.d.ts +29 -0
  111. package/dist/layout.js +323 -0
  112. package/dist/lib/cat-color.d.ts +1 -0
  113. package/dist/lib/cat-color.js +7 -0
  114. package/dist/lib/compose-refs.d.ts +5 -0
  115. package/dist/lib/compose-refs.js +38 -0
  116. package/dist/lib/date-format.d.ts +9 -0
  117. package/dist/lib/date-format.js +31 -0
  118. package/dist/lib/format.d.ts +14 -0
  119. package/dist/lib/format.js +34 -0
  120. package/dist/lib/json.d.ts +1 -0
  121. package/dist/lib/json.js +12 -0
  122. package/dist/lib/multi-value.d.ts +3 -0
  123. package/dist/lib/multi-value.js +22 -0
  124. package/dist/lib/record-option.d.ts +7 -0
  125. package/dist/lib/record-option.js +52 -0
  126. package/dist/lib/use-record-labels.d.ts +14 -0
  127. package/dist/lib/use-record-labels.js +130 -0
  128. package/dist/lib/utils.d.ts +2 -0
  129. package/dist/lib/utils.js +5 -0
  130. package/dist/list-view.d.ts +26 -0
  131. package/dist/list-view.js +11 -0
  132. package/dist/markdown/blocks.d.ts +10 -0
  133. package/dist/markdown/blocks.js +57 -0
  134. package/dist/markdown/extensions.d.ts +6 -0
  135. package/dist/markdown/extensions.js +25 -0
  136. package/dist/markdown/slash.d.ts +23 -0
  137. package/dist/markdown/slash.js +27 -0
  138. package/dist/markdown/toolbar.d.ts +13 -0
  139. package/dist/markdown/toolbar.js +119 -0
  140. package/dist/plugin-ui.d.ts +43 -0
  141. package/dist/plugin-ui.js +37 -0
  142. package/dist/registry.d.ts +26 -0
  143. package/dist/registry.js +169 -0
  144. package/dist/render/blocks/accordion.d.ts +1 -0
  145. package/dist/render/blocks/accordion.js +15 -0
  146. package/dist/render/blocks/aside.d.ts +1 -0
  147. package/dist/render/blocks/aside.js +14 -0
  148. package/dist/render/blocks/balance.d.ts +1 -0
  149. package/dist/render/blocks/balance.js +14 -0
  150. package/dist/render/blocks/bento.d.ts +1 -0
  151. package/dist/render/blocks/bento.js +24 -0
  152. package/dist/render/blocks/block-shell.d.ts +15 -0
  153. package/dist/render/blocks/block-shell.js +20 -0
  154. package/dist/render/blocks/calendar.d.ts +1 -0
  155. package/dist/render/blocks/calendar.js +142 -0
  156. package/dist/render/blocks/callout.d.ts +1 -0
  157. package/dist/render/blocks/callout.js +21 -0
  158. package/dist/render/blocks/chart.d.ts +1 -0
  159. package/dist/render/blocks/chart.js +46 -0
  160. package/dist/render/blocks/compare.d.ts +1 -0
  161. package/dist/render/blocks/compare.js +16 -0
  162. package/dist/render/blocks/countdown.d.ts +1 -0
  163. package/dist/render/blocks/countdown.js +36 -0
  164. package/dist/render/blocks/deck.d.ts +1 -0
  165. package/dist/render/blocks/deck.js +29 -0
  166. package/dist/render/blocks/epigraph.d.ts +1 -0
  167. package/dist/render/blocks/epigraph.js +13 -0
  168. package/dist/render/blocks/facets.d.ts +1 -0
  169. package/dist/render/blocks/facets.js +23 -0
  170. package/dist/render/blocks/figure.d.ts +1 -0
  171. package/dist/render/blocks/figure.js +13 -0
  172. package/dist/render/blocks/grid.d.ts +1 -0
  173. package/dist/render/blocks/grid.js +21 -0
  174. package/dist/render/blocks/heatmap.d.ts +1 -0
  175. package/dist/render/blocks/heatmap.js +135 -0
  176. package/dist/render/blocks/idcard.d.ts +1 -0
  177. package/dist/render/blocks/idcard.js +30 -0
  178. package/dist/render/blocks/identity.d.ts +1 -0
  179. package/dist/render/blocks/identity.js +46 -0
  180. package/dist/render/blocks/index.d.ts +40 -0
  181. package/dist/render/blocks/index.js +40 -0
  182. package/dist/render/blocks/journal.d.ts +1 -0
  183. package/dist/render/blocks/journal.js +50 -0
  184. package/dist/render/blocks/ledger.d.ts +1 -0
  185. package/dist/render/blocks/ledger.js +23 -0
  186. package/dist/render/blocks/manifest.d.ts +1 -0
  187. package/dist/render/blocks/manifest.js +50 -0
  188. package/dist/render/blocks/masthead.d.ts +1 -0
  189. package/dist/render/blocks/masthead.js +15 -0
  190. package/dist/render/blocks/meter.d.ts +1 -0
  191. package/dist/render/blocks/meter.js +32 -0
  192. package/dist/render/blocks/nutrition.d.ts +1 -0
  193. package/dist/render/blocks/nutrition.js +27 -0
  194. package/dist/render/blocks/people.d.ts +1 -0
  195. package/dist/render/blocks/people.js +35 -0
  196. package/dist/render/blocks/plaque.d.ts +1 -0
  197. package/dist/render/blocks/plaque.js +15 -0
  198. package/dist/render/blocks/properties.d.ts +1 -0
  199. package/dist/render/blocks/properties.js +22 -0
  200. package/dist/render/blocks/prose.d.ts +1 -0
  201. package/dist/render/blocks/prose.js +17 -0
  202. package/dist/render/blocks/receipt.d.ts +1 -0
  203. package/dist/render/blocks/receipt.js +23 -0
  204. package/dist/render/blocks/registry.d.ts +16 -0
  205. package/dist/render/blocks/registry.js +7 -0
  206. package/dist/render/blocks/relation-source.d.ts +2 -0
  207. package/dist/render/blocks/relation-source.js +6 -0
  208. package/dist/render/blocks/route.d.ts +1 -0
  209. package/dist/render/blocks/route.js +23 -0
  210. package/dist/render/blocks/score.d.ts +1 -0
  211. package/dist/render/blocks/score.js +15 -0
  212. package/dist/render/blocks/series.d.ts +23 -0
  213. package/dist/render/blocks/series.js +57 -0
  214. package/dist/render/blocks/specimen.d.ts +1 -0
  215. package/dist/render/blocks/specimen.js +19 -0
  216. package/dist/render/blocks/split.d.ts +1 -0
  217. package/dist/render/blocks/split.js +15 -0
  218. package/dist/render/blocks/spread.d.ts +1 -0
  219. package/dist/render/blocks/spread.js +15 -0
  220. package/dist/render/blocks/stack.d.ts +1 -0
  221. package/dist/render/blocks/stack.js +15 -0
  222. package/dist/render/blocks/stats.d.ts +1 -0
  223. package/dist/render/blocks/stats.js +23 -0
  224. package/dist/render/blocks/status.d.ts +1 -0
  225. package/dist/render/blocks/status.js +18 -0
  226. package/dist/render/blocks/table.d.ts +11 -0
  227. package/dist/render/blocks/table.js +136 -0
  228. package/dist/render/blocks/tabs.d.ts +1 -0
  229. package/dist/render/blocks/tabs.js +17 -0
  230. package/dist/render/blocks/terminal.d.ts +1 -0
  231. package/dist/render/blocks/terminal.js +18 -0
  232. package/dist/render/blocks/timeline.d.ts +1 -0
  233. package/dist/render/blocks/timeline.js +31 -0
  234. package/dist/render/box.d.ts +5 -0
  235. package/dist/render/box.js +23 -0
  236. package/dist/render/chrome.d.ts +64 -0
  237. package/dist/render/chrome.js +92 -0
  238. package/dist/render/core/amount.d.ts +1 -0
  239. package/dist/render/core/amount.js +19 -0
  240. package/dist/render/core/attachment.d.ts +29 -0
  241. package/dist/render/core/attachment.js +102 -0
  242. package/dist/render/core/choice.d.ts +1 -0
  243. package/dist/render/core/choice.js +40 -0
  244. package/dist/render/core/composite.d.ts +1 -0
  245. package/dist/render/core/composite.js +132 -0
  246. package/dist/render/core/datetime.d.ts +2 -0
  247. package/dist/render/core/datetime.js +215 -0
  248. package/dist/render/core/dimensions.d.ts +1 -0
  249. package/dist/render/core/dimensions.js +44 -0
  250. package/dist/render/core/embed.d.ts +11 -0
  251. package/dist/render/core/embed.js +99 -0
  252. package/dist/render/core/file.d.ts +12 -0
  253. package/dist/render/core/file.js +238 -0
  254. package/dist/render/core/illustrated.d.ts +39 -0
  255. package/dist/render/core/illustrated.js +113 -0
  256. package/dist/render/core/index.d.ts +23 -0
  257. package/dist/render/core/index.js +23 -0
  258. package/dist/render/core/layout-elements.d.ts +1 -0
  259. package/dist/render/core/layout-elements.js +78 -0
  260. package/dist/render/core/localpath.d.ts +6 -0
  261. package/dist/render/core/localpath.js +73 -0
  262. package/dist/render/core/money.d.ts +1 -0
  263. package/dist/render/core/money.js +39 -0
  264. package/dist/render/core/number.d.ts +17 -0
  265. package/dist/render/core/number.js +104 -0
  266. package/dist/render/core/period.d.ts +1 -0
  267. package/dist/render/core/period.js +60 -0
  268. package/dist/render/core/range.d.ts +1 -0
  269. package/dist/render/core/range.js +55 -0
  270. package/dist/render/core/reminder.d.ts +2 -0
  271. package/dist/render/core/reminder.js +56 -0
  272. package/dist/render/core/source.d.ts +3 -0
  273. package/dist/render/core/source.js +65 -0
  274. package/dist/render/core/stepper.d.ts +1 -0
  275. package/dist/render/core/stepper.js +81 -0
  276. package/dist/render/core/storage-group.d.ts +1 -0
  277. package/dist/render/core/storage-group.js +30 -0
  278. package/dist/render/core/text.d.ts +15 -0
  279. package/dist/render/core/text.js +370 -0
  280. package/dist/render/core/tristate.d.ts +1 -0
  281. package/dist/render/core/tristate.js +42 -0
  282. package/dist/render/core/weight.d.ts +4 -0
  283. package/dist/render/core/weight.js +38 -0
  284. package/dist/render/dispatch.d.ts +12 -0
  285. package/dist/render/dispatch.js +53 -0
  286. package/dist/render/display-override.d.ts +25 -0
  287. package/dist/render/display-override.js +23 -0
  288. package/dist/render/editor-mode.d.ts +4 -0
  289. package/dist/render/editor-mode.js +11 -0
  290. package/dist/render/field-address.d.ts +14 -0
  291. package/dist/render/field-address.js +10 -0
  292. package/dist/render/field-errors.d.ts +8 -0
  293. package/dist/render/field-errors.js +25 -0
  294. package/dist/render/field-frame.d.ts +20 -0
  295. package/dist/render/field-frame.js +78 -0
  296. package/dist/render/field-scope.d.ts +8 -0
  297. package/dist/render/field-scope.js +25 -0
  298. package/dist/render/field-width.d.ts +7 -0
  299. package/dist/render/field-width.js +12 -0
  300. package/dist/render/form-types.d.ts +12 -0
  301. package/dist/render/form-types.js +1 -0
  302. package/dist/render/hybrid-scope.d.ts +5 -0
  303. package/dist/render/hybrid-scope.js +9 -0
  304. package/dist/render/image.d.ts +64 -0
  305. package/dist/render/image.js +322 -0
  306. package/dist/render/live-kind.d.ts +7 -0
  307. package/dist/render/live-kind.js +104 -0
  308. package/dist/render/markdown-view.d.ts +5 -0
  309. package/dist/render/markdown-view.js +17 -0
  310. package/dist/render/multi.d.ts +10 -0
  311. package/dist/render/multi.js +26 -0
  312. package/dist/render/multiple-chips.d.ts +24 -0
  313. package/dist/render/multiple-chips.js +18 -0
  314. package/dist/render/option-list.d.ts +15 -0
  315. package/dist/render/option-list.js +47 -0
  316. package/dist/render/option-value.d.ts +20 -0
  317. package/dist/render/option-value.js +41 -0
  318. package/dist/render/picker-scrim.d.ts +7 -0
  319. package/dist/render/picker-scrim.js +32 -0
  320. package/dist/render/picker-surface.d.ts +5 -0
  321. package/dist/render/picker-surface.js +9 -0
  322. package/dist/render/popover-field.d.ts +16 -0
  323. package/dist/render/popover-field.js +153 -0
  324. package/dist/render/record-save-ctx.d.ts +11 -0
  325. package/dist/render/record-save-ctx.js +41 -0
  326. package/dist/render/shared.d.ts +92 -0
  327. package/dist/render/shared.js +182 -0
  328. package/dist/render/slot.d.ts +45 -0
  329. package/dist/render/slot.js +194 -0
  330. package/dist/render/sort-rows.d.ts +3 -0
  331. package/dist/render/sort-rows.js +38 -0
  332. package/dist/render/use-collection-rows.d.ts +10 -0
  333. package/dist/render/use-collection-rows.js +11 -0
  334. package/dist/render/use-field-edit.d.ts +28 -0
  335. package/dist/render/use-field-edit.js +50 -0
  336. package/dist/render/use-file-upload.d.ts +10 -0
  337. package/dist/render/use-file-upload.js +35 -0
  338. package/dist/render/use-form-bridge.d.ts +21 -0
  339. package/dist/render/use-form-bridge.js +53 -0
  340. package/dist/render/use-hybrid.d.ts +2 -0
  341. package/dist/render/use-hybrid.js +5 -0
  342. package/dist/render/widgets/attachment-group.d.ts +1 -0
  343. package/dist/render/widgets/attachment-group.js +35 -0
  344. package/dist/render/widgets/checklist-content.d.ts +15 -0
  345. package/dist/render/widgets/checklist-content.js +12 -0
  346. package/dist/render/widgets/checklist-group.d.ts +11 -0
  347. package/dist/render/widgets/checklist-group.js +47 -0
  348. package/dist/render/widgets/checklist-row.d.ts +14 -0
  349. package/dist/render/widgets/checklist-row.js +14 -0
  350. package/dist/render/widgets/display.d.ts +33 -0
  351. package/dist/render/widgets/display.js +97 -0
  352. package/dist/render/widgets/edit.d.ts +63 -0
  353. package/dist/render/widgets/edit.js +283 -0
  354. package/dist/render/widgets/fixed-collection.d.ts +11 -0
  355. package/dist/render/widgets/fixed-collection.js +34 -0
  356. package/dist/render/widgets/flow-row.d.ts +14 -0
  357. package/dist/render/widgets/flow-row.js +27 -0
  358. package/dist/render/widgets/group-edit.d.ts +9 -0
  359. package/dist/render/widgets/group-edit.js +23 -0
  360. package/dist/render/widgets/group-field.d.ts +9 -0
  361. package/dist/render/widgets/group-field.js +42 -0
  362. package/dist/render/widgets/group-widgets.d.ts +2 -0
  363. package/dist/render/widgets/group-widgets.js +41 -0
  364. package/dist/render/widgets/group.d.ts +26 -0
  365. package/dist/render/widgets/group.js +139 -0
  366. package/dist/render/widgets/illustrated-group.d.ts +1 -0
  367. package/dist/render/widgets/illustrated-group.js +41 -0
  368. package/dist/render/widgets/table-group.d.ts +11 -0
  369. package/dist/render/widgets/table-group.js +58 -0
  370. package/dist/render/widgets/two-cell-group.d.ts +21 -0
  371. package/dist/render/widgets/two-cell-group.js +42 -0
  372. package/dist/render/widgets/url-widget.d.ts +7 -0
  373. package/dist/render/widgets/url-widget.js +15 -0
  374. package/dist/render/widgets/weekday-grid.d.ts +13 -0
  375. package/dist/render/widgets/weekday-grid.js +47 -0
  376. package/dist/render/write.d.ts +5 -0
  377. package/dist/render/write.js +14 -0
  378. package/dist/schema-registry.d.ts +6 -0
  379. package/dist/schema-registry.js +25 -0
  380. package/dist/site-links.d.ts +13 -0
  381. package/dist/site-links.js +18 -0
  382. package/dist/strip-markdown.d.ts +1 -0
  383. package/dist/strip-markdown.js +11 -0
  384. package/dist/theme.css +189 -0
  385. package/dist/types.d.ts +84 -0
  386. package/dist/types.js +1 -0
  387. package/package.json +85 -0
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Link } from 'react-router-dom';
3
+ import { getShelf } from "../../schema-registry.js";
4
+ import { OptionIconView } from "../../components/ui/option-icon.js";
5
+ import { recordOption } from "../../lib/record-option.js";
6
+ import { useRecordsByIds } from "../../lib/use-record-labels.js";
7
+ import { Dash, shortId } from "../shared.js";
8
+ import { registerRenderer } from "../../registry.js";
9
+ import { Slot } from "../slot.js";
10
+ import { BlockShell } from "./block-shell.js";
11
+ import { relationIds } from "./relation-source.js";
12
+ function PeopleRoster({ field, value }) {
13
+ const library = field.relation?.library ?? '';
14
+ const shelf = field.relation?.shelf ?? '';
15
+ const ids = relationIds(field, value);
16
+ const recordMap = useRecordsByIds(library, shelf, ids);
17
+ const m = getShelf(library, shelf);
18
+ if (!ids.length)
19
+ return _jsx(Dash, {});
20
+ return (_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", "data-testid": "people", children: [_jsx("div", { className: "flex items-center", "data-testid": "people-avatars", children: ids.map((id, i) => {
21
+ const row = recordMap.get(id);
22
+ const { icon } = m && row ? recordOption(m, row) : { icon: undefined };
23
+ return (_jsx("span", { className: `rounded-full ring-2 ring-surface ${i > 0 ? '-ml-2.5' : ''}`, style: { zIndex: ids.length - i }, "data-testid": `people-avatar-${i}`, children: icon ? (_jsx(OptionIconView, { icon: icon, framed: true, size: "md" })) : (_jsx(OptionIconView, { icon: { kind: 'icon', value: m?.icon ?? 'lucide:user' }, framed: true, size: "md" })) }, id));
24
+ }) }), _jsx("div", { className: "flex flex-wrap items-center gap-x-1.5 text-sm", "data-testid": "people-names", children: ids.map((id) => {
25
+ const row = recordMap.get(id);
26
+ const { title } = m && row ? recordOption(m, row) : { title: shortId(id) };
27
+ return (_jsx(Link, { to: `/${library}/${shelf}/${id}`, className: "hover:text-accent hover:underline", children: title }, id));
28
+ }) })] }));
29
+ }
30
+ function PeoplePage({ el }) {
31
+ const g = el;
32
+ const sourceName = g.view?.source;
33
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: _jsx(Slot, { name: sourceName, render: (handle) => _jsx(PeopleRoster, { field: handle.field.type, value: handle.value }) }) }));
34
+ }
35
+ registerRenderer({ kinds: ['people'], layout: 'block', editor: 'custom', Page: PeoplePage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { BlockShell, findSlot, findSlots } from "./block-shell.js";
5
+ function PlaquePage({ el }) {
6
+ const g = el;
7
+ const titleName = g.view?.title;
8
+ const subtitleNames = g.view?.subtitle ?? [];
9
+ const slots = useSlots();
10
+ const title = findSlot(slots, titleName);
11
+ const subtitles = findSlots(slots, subtitleNames);
12
+ const body = (_jsxs("div", { className: "flex flex-col items-center gap-3 py-4 text-center", "data-testid": "plaque", children: [title && !title.handle.hidden && (_jsx("div", { className: "min-w-0 w-full border-b border-border pb-3", "data-testid": "plaque-item-0", children: _jsx(Slot, { name: titleName, render: (_handle, defaultDisplay) => (_jsx("div", { className: "text-base font-semibold text-text", "data-testid": "plaque-title", children: defaultDisplay })) }) })), subtitles.map((s, i) => s.handle.hidden ? null : (_jsx("div", { className: "min-w-0", "data-testid": `plaque-item-${i + 1}`, children: _jsx(Slot, { name: s.name, render: (_handle, defaultDisplay) => (_jsx("div", { className: "text-xs font-medium uppercase tracking-wider text-muted", "data-testid": "plaque-subtitle", children: defaultDisplay })) }) }, s.name)))] }));
13
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
14
+ }
15
+ registerRenderer({ kinds: ['plaque'], layout: 'block', editor: 'custom', Page: PlaquePage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { BlockShell } from "./block-shell.js";
5
+ const COLS = {
6
+ 2: 'grid-cols-1 @sm:grid-cols-2',
7
+ 3: 'grid-cols-1 @sm:grid-cols-2 @md:grid-cols-3',
8
+ 4: 'grid-cols-1 @sm:grid-cols-2 @md:grid-cols-4',
9
+ };
10
+ function PropertiesPage({ el }) {
11
+ const g = el;
12
+ const columns = g.view?.columns ?? 2;
13
+ const colClass = COLS[columns] ?? COLS[2];
14
+ const slots = useSlots();
15
+ const body = (_jsx("div", { className: `@container grid gap-x-4 gap-y-3.5 p-4 rounded-xl border border-border/60 bg-muted/15 ${colClass}`, children: slots.map((s, i) => {
16
+ if (s.handle.hidden)
17
+ return null;
18
+ return (_jsx("div", { className: "min-w-0", "data-testid": `properties-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
19
+ }) }));
20
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
21
+ }
22
+ registerRenderer({ kinds: ['properties'], layout: 'block', editor: 'custom', Page: PropertiesPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { BlockShell } from "./block-shell.js";
5
+ const DROP_CAP_CLS = 'first-letter:float-left first-letter:mr-2 first-letter:text-5xl first-letter:font-bold first-letter:leading-[0.8]';
6
+ function ProsePage({ el }) {
7
+ const g = el;
8
+ const dropCapName = g.view?.dropCap;
9
+ const slots = useSlots();
10
+ const body = (_jsx("div", { className: "max-w-[65ch] space-y-3 font-serif text-base leading-relaxed", "data-testid": "prose", children: slots.map((s, i) => {
11
+ if (s.handle.hidden)
12
+ return null;
13
+ return (_jsx("div", { className: `min-w-0 ${s.name === dropCapName ? DROP_CAP_CLS : ''}`, "data-testid": `prose-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
14
+ }) }));
15
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
16
+ }
17
+ registerRenderer({ kinds: ['prose'], layout: 'block', editor: 'custom', Page: ProsePage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { registerRenderer } from "../../registry.js";
4
+ import { Slot, useSlots } from "../slot.js";
5
+ import { BlockShell, fieldLabel, findSlot, findSlots } from "./block-shell.js";
6
+ function ReceiptPage({ el }) {
7
+ const g = el;
8
+ const { t } = useTranslation();
9
+ const lineNames = g.view?.lines ?? [];
10
+ const totalName = g.view?.total;
11
+ const slots = useSlots();
12
+ const lines = findSlots(slots, lineNames);
13
+ const total = findSlot(slots, totalName);
14
+ const body = (_jsxs("div", { className: "flex flex-col", "data-testid": "receipt", children: [_jsx("div", { className: "flex flex-col divide-y divide-dashed divide-border", children: lines.map((line, i) => line.handle.hidden ? null : (_jsx("div", { className: "min-w-0 py-1.5", "data-testid": `receipt-line-${i}`, children: _jsx(Slot, { name: line.name, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "flex w-full min-w-0 items-baseline justify-between gap-3", "data-testid": "receipt-line", children: [_jsx("span", { className: "min-w-0 text-muted", children: fieldLabel(handle, t) }), _jsx("span", { className: "shrink-0 text-right tabular-nums", children: defaultDisplay })] })) }) }, line.name))) }), total && !total.handle.hidden && (_jsx("div", { className: "mt-2 min-w-0 border-t-2 border-border pt-2", "data-testid": "receipt-total-row", children: _jsx(Slot, { name: totalName, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "flex w-full min-w-0 items-baseline justify-between gap-3 text-lg font-bold", "data-testid": "receipt-total", children: [_jsx("span", { className: "min-w-0", children: fieldLabel(handle, t) }), _jsx("span", { className: "shrink-0 text-right tabular-nums", children: defaultDisplay })] })) }) }))] }));
15
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
16
+ }
17
+ registerRenderer({
18
+ kinds: ['receipt'],
19
+ layout: 'block',
20
+ editor: 'custom',
21
+ Page: ReceiptPage,
22
+ childWidth: 'w-full',
23
+ });
@@ -0,0 +1,16 @@
1
+ import type { ReactNode, ReactElement } from 'react';
2
+ import type { ContainerNode } from '@coffer-org/sdk/fields';
3
+ import type { DisplayOverride } from '../display-override.tsx';
4
+ export type { DisplayOverride };
5
+ export interface BlockComponent {
6
+ (p: {
7
+ el: ContainerNode;
8
+ items: ReactNode[];
9
+ label?: string;
10
+ icon?: string;
11
+ }): ReactElement;
12
+ displays?: (el: ContainerNode) => readonly (DisplayOverride | undefined)[];
13
+ childWidth?: string;
14
+ }
15
+ export declare function registerBlock(kind: string, c: BlockComponent): void;
16
+ export declare function resolveBlock(kind: string | undefined): BlockComponent | undefined;
@@ -0,0 +1,7 @@
1
+ const blocks = new Map();
2
+ export function registerBlock(kind, c) {
3
+ blocks.set(kind, c);
4
+ }
5
+ export function resolveBlock(kind) {
6
+ return kind ? blocks.get(kind) : undefined;
7
+ }
@@ -0,0 +1,2 @@
1
+ import type { FieldMeta } from '@coffer-org/sdk/fields';
2
+ export declare function relationIds(field: FieldMeta, value: unknown): string[];
@@ -0,0 +1,6 @@
1
+ import { parseJSON } from "../../lib/json.js";
2
+ import { hasValue } from "../shared.js";
3
+ export function relationIds(field, value) {
4
+ const multi = Boolean(field.hints['multi']);
5
+ return multi ? parseJSON(value, []).map(String) : hasValue(value) ? [String(value)] : [];
6
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { findSlot } from "./block-shell.js";
5
+ function RoutePage({ el }) {
6
+ const g = el;
7
+ const fromName = g.view?.from;
8
+ const toName = g.view?.to;
9
+ const departName = g.view?.depart;
10
+ const arriveName = g.view?.arrive;
11
+ const durationName = g.view?.duration;
12
+ const slots = useSlots();
13
+ const from = findSlot(slots, fromName);
14
+ const to = findSlot(slots, toName);
15
+ const depart = findSlot(slots, departName);
16
+ const arrive = findSlot(slots, arriveName);
17
+ const duration = findSlot(slots, durationName);
18
+ const fromVisible = from !== undefined && !from.handle.hidden;
19
+ const toVisible = to !== undefined && !to.handle.hidden;
20
+ const durationVisible = duration !== undefined && !duration.handle.hidden;
21
+ return (_jsxs("div", { className: "flex items-start gap-3", "data-testid": "route", children: [fromVisible && (_jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-start gap-1", "data-testid": "route-from", children: [_jsx(Slot, { name: fromName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-xl font-bold", "data-testid": "route-endpoint-0", children: defaultDisplay })) }), depart && !depart.handle.hidden && (_jsx("span", { className: "min-w-0 text-xs text-muted", "data-testid": "route-depart", children: _jsx(Slot, { name: departName }) }))] })), (fromVisible || toVisible || durationVisible) && (_jsxs("div", { className: "relative flex min-w-[3rem] flex-1 shrink-0 flex-col items-center justify-center self-stretch", "data-testid": "route-axis", children: [_jsx("span", { className: "absolute inset-x-0 top-1/2 border-t border-dashed border-border", "aria-hidden": true }), durationVisible && (_jsx("span", { className: "relative z-10 bg-surface px-1.5 text-xs font-medium text-muted", "data-testid": "route-duration", children: _jsx(Slot, { name: durationName }) }))] })), toVisible && (_jsxs("div", { className: "flex min-w-0 flex-1 flex-col items-end gap-1 text-right", "data-testid": "route-to", children: [_jsx(Slot, { name: toName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-xl font-bold", "data-testid": "route-endpoint-1", children: defaultDisplay })) }), arrive && !arrive.handle.hidden && (_jsx("span", { className: "min-w-0 text-xs text-muted", "data-testid": "route-arrive", children: _jsx(Slot, { name: arriveName }) }))] }))] }));
22
+ }
23
+ registerRenderer({ kinds: ['route'], layout: 'block', editor: 'custom', Page: RoutePage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { BlockShell, findSlot } from "./block-shell.js";
5
+ function ScorePage({ el }) {
6
+ const g = el;
7
+ const sourceName = g.view?.source;
8
+ const verdictName = g.view?.verdict;
9
+ const max = g.view?.max;
10
+ const slots = useSlots();
11
+ const verdict = findSlot(slots, verdictName);
12
+ const body = (_jsxs("div", { className: "flex flex-wrap items-baseline gap-3", "data-testid": "score", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: sourceName, render: (_handle, defaultDisplay) => (_jsxs("span", { className: "inline-flex items-baseline gap-0.5 text-3xl font-bold tabular-nums", "data-testid": "score-figure", children: [defaultDisplay, max != null && _jsxs("span", { className: "text-lg font-normal text-muted", children: ["/", max] })] })) }) }), verdict && !verdict.handle.hidden && (_jsx("div", { className: "min-w-0 text-muted", "data-testid": "score-verdict", children: _jsx(Slot, { name: verdictName }) }))] }));
13
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
14
+ }
15
+ registerRenderer({ kinds: ['score'], layout: 'block', editor: 'custom', Page: ScorePage });
@@ -0,0 +1,23 @@
1
+ export interface SeriesPoint {
2
+ value: number;
3
+ t?: number;
4
+ row?: Record<string, unknown>;
5
+ }
6
+ export interface SeriesBox {
7
+ width: number;
8
+ height: number;
9
+ pad?: number;
10
+ }
11
+ export interface Sparkline {
12
+ path: string;
13
+ coords: {
14
+ x: number;
15
+ y: number;
16
+ }[];
17
+ min: number | undefined;
18
+ max: number | undefined;
19
+ }
20
+ export declare function parseSeriesDate(v: unknown): number | undefined;
21
+ export declare function toSeries(rows: Record<string, unknown>[], value: string, date: string): SeriesPoint[];
22
+ export declare function sparkline(input: SeriesPoint[], box: SeriesBox): Sparkline;
23
+ export declare function seriesDelta(points: SeriesPoint[]): number | undefined;
@@ -0,0 +1,57 @@
1
+ export function parseSeriesDate(v) {
2
+ const s = typeof v === 'string' ? v : typeof v === 'number' ? String(v) : '';
3
+ const m = /^(\d{4})-(\d{2})-(\d{2})(?:[T ](\d{2}):(\d{2})(?::(\d{2}))?)?/.exec(s);
4
+ if (!m)
5
+ return undefined;
6
+ return Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]), Number(m[4] ?? 0), Number(m[5] ?? 0), Number(m[6] ?? 0));
7
+ }
8
+ function toNumber(v) {
9
+ if (typeof v === 'number')
10
+ return Number.isFinite(v) ? v : undefined;
11
+ if (typeof v === 'string' && v.trim() !== '') {
12
+ const n = Number(v);
13
+ return Number.isFinite(n) ? n : undefined;
14
+ }
15
+ return undefined;
16
+ }
17
+ export function toSeries(rows, value, date) {
18
+ const points = [];
19
+ for (const row of rows) {
20
+ const v = toNumber(row[value]);
21
+ if (v === undefined)
22
+ continue;
23
+ points.push({ value: v, t: parseSeriesDate(row[date]), row });
24
+ }
25
+ if (points.length > 1 && points.every((p) => p.t !== undefined)) {
26
+ points.sort((a, b) => a.t - b.t);
27
+ }
28
+ return points;
29
+ }
30
+ function round(n) {
31
+ return Math.round(n * 100) / 100;
32
+ }
33
+ export function sparkline(input, box) {
34
+ const pad = box.pad ?? 0;
35
+ const innerW = Math.max(0, box.width - pad * 2);
36
+ const innerH = Math.max(0, box.height - pad * 2);
37
+ const points = input.filter((p) => Number.isFinite(p.value));
38
+ if (points.length === 0)
39
+ return { path: '', coords: [], min: undefined, max: undefined };
40
+ const values = points.map((p) => p.value);
41
+ const min = Math.min(...values);
42
+ const max = Math.max(...values);
43
+ const range = max - min;
44
+ const coords = points.map((p, i) => {
45
+ const fx = points.length > 1 ? i / (points.length - 1) : 0;
46
+ const fy = range > 0 ? (p.value - min) / range : 0.5;
47
+ return { x: round(pad + fx * innerW), y: round(pad + (1 - fy) * innerH) };
48
+ });
49
+ const move = coords.map((c, i) => `${i === 0 ? 'M' : 'L'} ${c.x} ${c.y}`).join(' ');
50
+ const path = coords.length === 1 ? `${move} L ${coords[0].x} ${coords[0].y}` : move;
51
+ return { path, coords, min, max };
52
+ }
53
+ export function seriesDelta(points) {
54
+ if (points.length < 2)
55
+ return undefined;
56
+ return points[points.length - 1].value - points[points.length - 2].value;
57
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { Icon } from "../../components/Icon.js";
4
+ import { registerRenderer } from "../../registry.js";
5
+ import { Slot, useSlots } from "../slot.js";
6
+ import { fieldLabel, findSlot, findSlots } from "./block-shell.js";
7
+ function SpecimenPage({ el }) {
8
+ const g = el;
9
+ const { t } = useTranslation();
10
+ const titleName = g.view?.title;
11
+ const subtitleName = g.view?.subtitle;
12
+ const conditionNames = g.view?.conditions ?? [];
13
+ const slots = useSlots();
14
+ const subtitle = findSlot(slots, subtitleName);
15
+ const conditions = findSlots(slots, conditionNames);
16
+ const visibleConditions = conditions.filter((c) => !c.handle.hidden);
17
+ return (_jsxs("div", { className: "space-y-2", "data-testid": "specimen", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: titleName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-xl font-medium italic", "data-testid": "specimen-title", children: defaultDisplay })) }) }), subtitle && !subtitle.handle.hidden && (_jsx("div", { className: "min-w-0 text-sm text-muted", "data-testid": "specimen-subtitle", children: _jsx(Slot, { name: subtitleName }) })), visibleConditions.length > 0 && (_jsx("div", { className: "flex flex-col gap-1", "data-testid": "specimen-conditions", children: conditions.map((c, i) => c.handle.hidden ? null : (_jsx("div", { "data-testid": `specimen-condition-${i}`, children: _jsx(Slot, { name: c.name, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "inline-flex min-w-0 items-baseline gap-1.5", children: [_jsx(Icon, { name: "lucide:circle", size: 8, className: "relative top-[0.15em] shrink-0 fill-current text-muted" }), _jsx("span", { className: "shrink-0 text-muted", children: fieldLabel(handle, t) }), _jsx("span", { className: "min-w-0 font-medium text-text", children: defaultDisplay })] })) }) }, c.name))) }))] }));
18
+ }
19
+ registerRenderer({ kinds: ['specimen'], layout: 'block', editor: 'custom', Page: SpecimenPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot } from "../slot.js";
4
+ registerRenderer({
5
+ kinds: ['split'],
6
+ layout: 'block',
7
+ editor: 'custom',
8
+ Page: ({ el }) => {
9
+ const g = el;
10
+ const [a, b] = g.view?.ratio ?? [1, 1];
11
+ const first = g.view?.first ?? [];
12
+ const second = g.view?.second ?? [];
13
+ return (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:[grid-template-columns:var(--split)]", style: { '--split': `${a}fr ${b}fr` }, children: [_jsx("div", { className: "min-w-0 space-y-3", children: first.map((n) => (_jsx(Slot, { name: n }, n))) }), _jsx("div", { className: "min-w-0 space-y-3", children: second.map((n) => (_jsx(Slot, { name: n }, n))) })] }));
14
+ },
15
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { findSlot, findSlots } from "./block-shell.js";
5
+ function SpreadPage({ el }) {
6
+ const g = el;
7
+ const visualName = g.view?.visual;
8
+ const quoteName = g.view?.quote;
9
+ const factNames = g.view?.facts ?? [];
10
+ const slots = useSlots();
11
+ const quote = findSlot(slots, quoteName);
12
+ const facts = findSlots(slots, factNames);
13
+ return (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: visualName }) }), _jsxs("div", { className: "flex min-w-0 flex-col gap-3", children: [quote && !quote.handle.hidden && (_jsx("blockquote", { className: "border-l-2 border-accent pl-3 italic text-muted", children: _jsx(Slot, { name: quoteName }) })), facts.map((f) => (_jsx(Slot, { name: f.name }, f.name)))] })] }));
14
+ }
15
+ registerRenderer({ kinds: ['spread'], layout: 'block', editor: 'custom', Page: SpreadPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { registerRenderer } from "../../registry.js";
3
+ import { Slot, useSlots } from "../slot.js";
4
+ import { BlockShell } from "./block-shell.js";
5
+ function StackPage({ el }) {
6
+ const g = el;
7
+ const slots = useSlots();
8
+ const body = (_jsx("div", { className: "flex flex-col gap-3 w-full", children: slots.map((s, i) => {
9
+ if (s.handle.hidden)
10
+ return null;
11
+ return (_jsx("div", { className: "w-full min-w-0", "data-testid": `stack-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
12
+ }) }));
13
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
14
+ }
15
+ registerRenderer({ kinds: ['stack'], layout: 'block', editor: 'custom', Page: StackPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { registerRenderer } from "../../registry.js";
4
+ import { Slot, useSlots } from "../slot.js";
5
+ import { BlockShell, fieldLabel } from "./block-shell.js";
6
+ function StatsPage({ el }) {
7
+ const g = el;
8
+ const { t } = useTranslation();
9
+ const slots = useSlots();
10
+ const body = (_jsx("div", { className: "flex flex-wrap gap-x-8 gap-y-4 sm:flex-nowrap sm:divide-x sm:divide-border py-1", "data-testid": "stats", children: slots.map((s, i) => {
11
+ if (s.handle.hidden)
12
+ return null;
13
+ return (_jsx("div", { className: "min-w-0 flex-1 sm:px-5 sm:first:pl-0", "data-testid": `stats-item-${i}`, children: _jsx(Slot, { name: s.name, ownLabel: true, render: (handle, defaultDisplay) => (_jsxs("span", { className: "flex min-w-0 flex-col items-start gap-0.5 py-1", "data-testid": "stats-figure", children: [_jsx("span", { className: "min-w-0 text-2xl font-semibold tabular-nums text-text", children: defaultDisplay }), _jsx("span", { className: "min-w-0 text-[11px] font-medium uppercase tracking-wider text-muted", children: fieldLabel(handle, t) })] })) }) }, s.name));
14
+ }) }));
15
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
16
+ }
17
+ registerRenderer({
18
+ kinds: ['stats'],
19
+ layout: 'block',
20
+ editor: 'custom',
21
+ Page: StatsPage,
22
+ childWidth: 'w-full',
23
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { optionChipProps } from "../option-value.js";
3
+ import { registerRenderer } from "../../registry.js";
4
+ import { Slot, useSlots } from "../slot.js";
5
+ import { BlockShell, findSlot } from "./block-shell.js";
6
+ function StatusPage({ el }) {
7
+ const g = el;
8
+ const sourceName = g.view?.source;
9
+ const sinceName = g.view?.since;
10
+ const slots = useSlots();
11
+ const since = findSlot(slots, sinceName);
12
+ const body = (_jsxs("div", { className: "flex flex-wrap items-center gap-3", "data-testid": "status", children: [_jsx("div", { className: "min-w-0", children: _jsx(Slot, { name: sourceName, render: (handle, defaultDisplay) => {
13
+ const { chipClassName, chipStyle } = optionChipProps(handle.field.type);
14
+ return (_jsx("span", { className: `inline-flex min-w-0 items-center gap-2 rounded-lg border px-3 py-1.5 font-medium ${chipClassName ? chipClassName() : ''}`, style: chipStyle ? chipStyle(handle.value) : undefined, "data-testid": "status-banner", children: defaultDisplay }));
15
+ } }) }), since && !since.handle.hidden && (_jsx("div", { className: "min-w-0 text-sm text-muted", "data-testid": "status-since", children: _jsx(Slot, { name: sinceName }) }))] }));
16
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
17
+ }
18
+ registerRenderer({ kinds: ['status'], layout: 'block', editor: 'custom', Page: StatusPage });
@@ -0,0 +1,11 @@
1
+ import type { ContainerNode, ValueNode } from '@coffer-org/sdk/fields';
2
+ import type { TableAggregate } from '@coffer-org/sdk/blocks';
3
+ import { type Row } from '../use-collection-rows.ts';
4
+ export type { Row };
5
+ export type Col = ValueNode & {
6
+ key: string;
7
+ };
8
+ export declare function columnsOf(el: ContainerNode): Col[];
9
+ export declare function warnUnknownKey(el: ContainerNode, option: string, key: string): void;
10
+ export declare function aggregate(kind: TableAggregate, f: Col, rows: Row[]): number | undefined;
11
+ export declare function totalValue(f: Col, rows: Row[], n: number): unknown;
@@ -0,0 +1,136 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { isNamedField, isStorageGroup } from '@coffer-org/sdk/fields';
5
+ import { cellValue, magnitudeSub, subValue, withSubValue } from '@coffer-org/sdk/shelf';
6
+ import { registerRenderer } from "../../registry.js";
7
+ import { Icon } from "../../components/Icon.js";
8
+ import { AddBtn } from "../shared.js";
9
+ import { RemoveBtn } from "../multiple-chips.js";
10
+ import { groupScopeClass, useGroupDepth, GroupDepthProvider } from "../chrome.js";
11
+ import { useGroupEdit } from "../widgets/group-edit.js";
12
+ import { useHybridScope } from "../hybrid-scope.js";
13
+ import { FieldSlot, fieldKeys } from "../dispatch.js";
14
+ import { useFieldErrors } from "../field-errors.js";
15
+ import { sortByField } from "../sort-rows.js";
16
+ import { useCollectionRows } from "../use-collection-rows.js";
17
+ import { useSlot } from "../slot.js";
18
+ const NO_VALUE = ' __table_none__';
19
+ export function columnsOf(el) {
20
+ return el.fields.filter((f) => isNamedField(f));
21
+ }
22
+ const warned = new Set();
23
+ export function warnUnknownKey(el, option, key) {
24
+ const id = `${el.key}:${option}:${key}`;
25
+ if (warned.has(id))
26
+ return;
27
+ warned.add(id);
28
+ console.warn(`[table] '${el.key}': ${option} names '${key}', which is not a column of the row; ignored.`);
29
+ }
30
+ function numericCell(f, row) {
31
+ const v = cellValue(f.type, f.key, row);
32
+ const sub = magnitudeSub(f.type);
33
+ const raw = sub ? subValue(v, sub) : v;
34
+ const n = raw == null || raw === '' ? NaN : Number(raw);
35
+ return Number.isFinite(n) ? n : undefined;
36
+ }
37
+ export function aggregate(kind, f, rows) {
38
+ if (kind === 'count')
39
+ return rows.length;
40
+ const nums = rows.map((r) => numericCell(f, r)).filter((n) => n !== undefined);
41
+ if (!nums.length)
42
+ return undefined;
43
+ if (kind === 'sum')
44
+ return nums.reduce((a, b) => a + b, 0);
45
+ if (kind === 'avg')
46
+ return nums.reduce((a, b) => a + b, 0) / nums.length;
47
+ if (kind === 'min')
48
+ return Math.min(...nums);
49
+ return Math.max(...nums);
50
+ }
51
+ export function totalValue(f, rows, n) {
52
+ const sub = magnitudeSub(f.type);
53
+ if (!sub)
54
+ return n;
55
+ const sample = rows.map((r) => cellValue(f.type, f.key, r)).find((v) => v && typeof v === 'object');
56
+ return withSubValue(sample, sub, n);
57
+ }
58
+ function cellNode(f, row, editing, live, onCommit, recordCtx, error) {
59
+ if (editing) {
60
+ return (_jsx(FieldSlot, { el: f, field: f.type, fieldKey: f.key, value: row[f.key] ?? '', onChange: onCommit, mode: "edit", bare: true, recordCtx: recordCtx, error: error }));
61
+ }
62
+ return (_jsx(FieldSlot, { el: f, field: f.type, fieldKey: live ? f.key : undefined, value: row[f.key] ?? '', mode: "view", bare: !live, recordCtx: recordCtx, onChange: live ? onCommit : undefined, error: error }));
63
+ }
64
+ function TablePage({ el, value, recordCtx, mode }) {
65
+ const { t } = useTranslation();
66
+ const depth = useGroupDepth();
67
+ const errorAt = useFieldErrors();
68
+ const { write } = useSlot();
69
+ const group = el;
70
+ const cols = columnsOf(group);
71
+ const declaredSort = group.view?.['sort'];
72
+ const declaredDir = group.view?.['dir'] ?? 'asc';
73
+ const colOf = (key) => cols.find((f) => f.key === key);
74
+ if (declaredSort && !colOf(declaredSort))
75
+ warnUnknownKey(group, 'sort', declaredSort);
76
+ const [sort, setSort] = useState(declaredSort && colOf(declaredSort) ? { key: declaredSort, dir: declaredDir } : null);
77
+ const cycle = (key) => setSort((s) => (s?.key !== key ? { key, dir: 'asc' } : s.dir === 'asc' ? { key, dir: 'desc' } : null));
78
+ const g = useGroupEdit(mode, value, (v) => write(v));
79
+ const rows = Array.isArray(g.value) ? g.value : [];
80
+ const editing = g.mode !== 'view';
81
+ const live = useHybridScope() && mode === 'view';
82
+ const keyCtx = useMemo(() => ({ t }), [t]);
83
+ const ordered = useMemo(() => {
84
+ if (editing || !sort)
85
+ return rows;
86
+ const f = colOf(sort.key);
87
+ if (!f)
88
+ return rows;
89
+ return sortByField(rows, sort.key, sort.dir, f.type, keyCtx);
90
+ }, [rows, sort, editing, keyCtx]);
91
+ const indexOf = useMemo(() => new Map(rows.map((r, i) => [r, i])), [rows]);
92
+ const groupKey = group.view?.['groupBy'];
93
+ const groupFld = colOf(groupKey);
94
+ if (groupKey && !groupFld)
95
+ warnUnknownKey(group, 'groupBy', groupKey);
96
+ const buckets = useMemo(() => {
97
+ if (editing || !groupKey || !groupFld)
98
+ return [{ key: null, sort: '', label: '', rows: ordered }];
99
+ const byValue = new Map();
100
+ const none = [];
101
+ for (const row of ordered) {
102
+ const k = fieldKeys(groupFld.type, cellValue(groupFld.type, groupKey, row), keyCtx)[0];
103
+ if (!k) {
104
+ none.push(row);
105
+ continue;
106
+ }
107
+ const b = byValue.get(k.value) ?? { key: k.value, sort: k.sort ?? k.value, label: k.label, rows: [] };
108
+ b.rows.push(row);
109
+ byValue.set(k.value, b);
110
+ }
111
+ const out = [...byValue.values()].sort((a, b) => a.sort.localeCompare(b.sort, undefined, { numeric: true, sensitivity: 'base' }));
112
+ if (none.length)
113
+ out.push({ key: NO_VALUE, sort: NO_VALUE, label: t('table.noValue'), rows: none });
114
+ return out;
115
+ }, [ordered, editing, groupKey, groupFld, keyCtx, t]);
116
+ const rowTools = editing || live;
117
+ const numbered = group.view?.['numbered'] === true;
118
+ const totals = group.view?.['totals'];
119
+ for (const key of Object.keys(totals ?? {}))
120
+ if (!colOf(key))
121
+ warnUnknownKey(group, 'totals', key);
122
+ const hasTotals = Boolean(totals && Object.keys(totals).some((k) => colOf(k)));
123
+ const totalRow = (bucketRows, testId) => (_jsxs("tr", { "data-testid": testId, className: "text-xs text-muted", children: [numbered && _jsx("td", { className: "border-b border-border py-1.5 pr-3" }), cols.map((f, i) => {
124
+ const kind = totals?.[f.key];
125
+ const n = kind && colOf(f.key) ? aggregate(kind, f, bucketRows) : undefined;
126
+ return (_jsxs("td", { className: "border-b border-border py-1.5 pr-4", children: [i === 0 && !totals?.[f.key] ? t('table.total') : null, n === undefined ? null : (_jsx(FieldSlot, { el: f, field: f.type, value: totalValue(f, bucketRows, n), mode: "view", bare: true, recordCtx: recordCtx }))] }, f.key));
127
+ }), rowTools && _jsx("td", { className: "border-b border-border" })] }));
128
+ const { rowId, updateRow, removeRow, addRow } = useCollectionRows(rows, (v) => g.onChange(v));
129
+ const errorFor = (idx, key) => isStorageGroup(group) ? errorAt([group.key, idx, key]) : undefined;
130
+ const bodyRow = (row, idx, seq) => (_jsxs("tr", { "data-testid": `table-row-${idx}`, children: [numbered && _jsx("td", { className: "border-b border-border py-2 pr-3 text-xs text-muted", children: seq }), cols.map((f) => (_jsx("td", { className: "border-b border-border py-2 pr-4 align-top", children: cellNode(f, row, editing, live, (v) => updateRow(idx, f.key, v), recordCtx, errorFor(idx, f.key)) }, f.key))), rowTools && (_jsx("td", { className: "border-b border-border py-2 align-top", children: _jsx(RemoveBtn, { className: "mt-0.5", onClick: () => removeRow(idx) }) }))] }, rowId(row)));
131
+ const content = (_jsxs("div", { className: "flex flex-col gap-2 min-w-0", children: [_jsx("div", { className: "overflow-auto max-h-[60vh]", children: _jsxs("table", { className: "w-full border-separate border-spacing-0 text-sm", "data-testid": "table-block", children: [_jsx("thead", { className: "eink-solid sticky top-0 z-10 bg-bg/95 backdrop-blur", children: _jsxs("tr", { children: [numbered && _jsx("th", { className: "border-b border-border py-1.5 pr-3" }), cols.map((f) => (_jsx("th", { className: "border-b border-border py-1.5 pr-4 text-left text-xs font-normal text-muted", children: _jsxs("button", { type: "button", className: "inline-flex items-center gap-1 focus-visible:outline-2 focus-visible:outline-accent", onClick: () => cycle(f.key), children: [t(f.type.label), !editing && sort?.key === f.key && (_jsx(Icon, { name: sort.dir === 'asc' ? 'lucide:arrow-up' : 'lucide:arrow-down', className: "size-3" }))] }) }, f.key))), rowTools && _jsx("th", { className: "border-b border-border" })] }) }), buckets.map((b) => (_jsxs("tbody", { className: "tabular-nums", children: [b.key !== null && (_jsx("tr", { "data-testid": `table-group-${b.key}`, children: _jsxs("th", { colSpan: cols.length + (numbered ? 1 : 0) + (rowTools ? 1 : 0), className: "border-b border-border pt-4 pb-1 text-left text-xs font-medium text-muted", children: [b.key === NO_VALUE || !groupFld ? (b.label) : (_jsx(FieldSlot, { el: groupFld, field: groupFld.type, value: b.rows[0]?.[groupKey ?? ''], mode: "view", bare: true, recordCtx: recordCtx })), ' ', _jsx("span", { className: "text-muted/70", children: b.rows.length })] }) })), b.rows.map((row, i) => bodyRow(row, indexOf.get(row) ?? -1, i + 1)), hasTotals && b.key !== null && totalRow(b.rows, `table-subtotal-${b.key}`)] }, b.key ?? 'all'))), hasTotals && _jsx("tfoot", { children: totalRow(rows, 'table-total') })] }) }), rowTools && _jsx(AddBtn, { onClick: addRow, children: t('form.addRow') })] }));
132
+ if (mode !== 'view')
133
+ return content;
134
+ return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-1 min-w-0`, "data-testid": "table", children: [group.label && (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-[13px] font-semibold text-accent", children: t(group.label) }), !live && g.actions && _jsx("span", { className: "ml-auto flex shrink-0 items-center gap-1.5", children: g.actions })] })), _jsx(GroupDepthProvider, { depth: depth + 1, children: content })] }));
135
+ }
136
+ registerRenderer({ kinds: ['table'], layout: 'block', Page: TablePage, editor: 'custom' });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { Icon } from "../../components/Icon.js";
5
+ import { registerRenderer } from "../../registry.js";
6
+ import { Slot, SlotScope, useSlots } from "../slot.js";
7
+ function TabFields() {
8
+ return (_jsx(_Fragment, { children: useSlots().map((s) => (_jsx(Slot, { name: s.name }, s.name))) }));
9
+ }
10
+ function TabsPage({ el }) {
11
+ const g = el;
12
+ const { t } = useTranslation();
13
+ const tabs = g.view?.tabs ?? [];
14
+ const [active, setActive] = useState(0);
15
+ return (_jsxs("div", { className: "min-w-0", children: [_jsx("div", { role: "tablist", className: "flex flex-nowrap gap-1 overflow-x-auto border-b border-border", children: tabs.map((tab, i) => (_jsxs("button", { type: "button", role: "tab", "aria-selected": i === active, onClick: () => setActive(i), className: `flex shrink-0 items-center gap-1.5 whitespace-nowrap border-b-2 px-3 py-2 text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${i === active ? 'border-accent text-accent' : 'border-transparent text-muted'}`, children: [tab.icon && _jsx(Icon, { name: tab.icon, size: 14, className: "shrink-0" }), t(tab.label)] }, tab.name))) }), tabs.map((tab, i) => (_jsx("div", { role: "tabpanel", hidden: i !== active, className: "min-w-0 space-y-3 pt-3", children: _jsx(SlotScope, { name: tab.name, children: _jsx(TabFields, {}) }) }, tab.name)))] }));
16
+ }
17
+ registerRenderer({ kinds: ['tabs'], layout: 'block', editor: 'custom', Page: TabsPage });
@@ -0,0 +1 @@
1
+ export {};