@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,169 @@
1
+ import { hasValue, hasChildren, isPseudo, isCollectionGroup, isEmbeddedGroup } from '@coffer-org/sdk/fields';
2
+ export const rendererRegistry = {};
3
+ export function registerRenderer(r) {
4
+ if (!r.kinds.length)
5
+ throw new Error('[render] renderer without kinds');
6
+ const stored = {
7
+ kinds: r.kinds,
8
+ layout: r.layout,
9
+ childWidth: r.childWidth,
10
+ box: r.box,
11
+ Page: r.Page,
12
+ Inline: r.Inline,
13
+ Value: r.Value,
14
+ pickerSize: r.pickerSize,
15
+ Picker: r.Picker,
16
+ editor: r.editor,
17
+ keys: r.keys,
18
+ };
19
+ for (const kind of r.kinds) {
20
+ if (rendererRegistry[kind])
21
+ throw new Error(`[render] kind '${kind}' already registered`);
22
+ rendererRegistry[kind] = stored;
23
+ }
24
+ }
25
+ export function resolveRenderer(field) {
26
+ return rendererRegistry[field.kind];
27
+ }
28
+ export function elementKind(el) {
29
+ if (hasValue(el) && !hasChildren(el))
30
+ return el.type.kind;
31
+ if (hasChildren(el))
32
+ return el.kind ?? (isCollectionGroup(el) ? 'collection' : isEmbeddedGroup(el) ? 'embedded' : 'group');
33
+ if (isPseudo(el)) {
34
+ if (el.el === 'button')
35
+ return 'button';
36
+ if (el.el === 'divider')
37
+ return 'divider';
38
+ if (el.el === 'info')
39
+ return 'info';
40
+ }
41
+ throw new Error('[render] unknown layout element');
42
+ }
43
+ export function fieldLayout(field) {
44
+ const layout = resolveRenderer(field)?.layout;
45
+ if (typeof layout === 'function')
46
+ return layout(field);
47
+ return layout ?? 'scalar';
48
+ }
49
+ export const multiBlock = (field) => field.hints['multi'] === true || field.hints['multiple'] === true ? 'block' : 'scalar';
50
+ export function resolveFieldSpan(field) {
51
+ const explicit = field.hints?.['span'];
52
+ if (typeof explicit === 'number' && explicit >= 1 && explicit <= 12) {
53
+ return Math.round(explicit);
54
+ }
55
+ if (typeof explicit === 'string') {
56
+ if (explicit === 'full')
57
+ return 12;
58
+ if (explicit === 'half')
59
+ return 6;
60
+ if (explicit === 'third')
61
+ return 4;
62
+ if (explicit === 'quarter')
63
+ return 3;
64
+ const parsed = parseInt(explicit, 10);
65
+ if (!Number.isNaN(parsed) && parsed >= 1 && parsed <= 12)
66
+ return parsed;
67
+ }
68
+ if (field.hints['multiple'] === true || field.hints['multi'] === true) {
69
+ if (field.kind === 'color' || field.kind === 'country')
70
+ return 6;
71
+ return 12;
72
+ }
73
+ const layout = fieldLayout(field);
74
+ if (layout === 'block')
75
+ return 12;
76
+ switch (field.kind) {
77
+ case 'boolean':
78
+ case 'tristate':
79
+ case 'triState':
80
+ case 'rating':
81
+ case 'percent':
82
+ case 'year':
83
+ case 'color':
84
+ case 'colorname':
85
+ case 'unit':
86
+ case 'amount':
87
+ case 'status':
88
+ case 'score':
89
+ case 'tag':
90
+ return 3;
91
+ case 'email':
92
+ case 'tel':
93
+ case 'url':
94
+ case 'password':
95
+ case 'measured':
96
+ case 'money':
97
+ case 'period':
98
+ case 'numberRange':
99
+ case 'realRange':
100
+ case 'dimensions':
101
+ case 'weight':
102
+ case 'cidr':
103
+ case 'file':
104
+ case 'document':
105
+ case 'audio':
106
+ case 'video':
107
+ case 'image':
108
+ case 'avatar':
109
+ case 'poster':
110
+ case 'cover':
111
+ case 'media':
112
+ case 'tags':
113
+ case 'keyValue':
114
+ case 'key_value':
115
+ return 6;
116
+ case 'markdown':
117
+ case 'textarea':
118
+ case 'snippet':
119
+ case 'source':
120
+ case 'json':
121
+ case 'embed':
122
+ case 'table':
123
+ case 'sheet':
124
+ case 'calendar':
125
+ case 'journal':
126
+ case 'manifest':
127
+ case 'prose':
128
+ case 'idcard':
129
+ case 'bento':
130
+ case 'grid':
131
+ case 'split':
132
+ return 12;
133
+ case 'text':
134
+ case 'string':
135
+ case 'int':
136
+ case 'number':
137
+ case 'real':
138
+ case 'date':
139
+ case 'time':
140
+ case 'datetime':
141
+ case 'reminder':
142
+ case 'select':
143
+ case 'country':
144
+ case 'slug':
145
+ case 'link':
146
+ case 'localDir':
147
+ case 'localFile':
148
+ case 'i18n':
149
+ case 'smalltext':
150
+ case 'ip':
151
+ case 'mac':
152
+ case 'imei':
153
+ case 'hostname':
154
+ case 'dataSize':
155
+ case 'ram':
156
+ case 'diskSize':
157
+ default:
158
+ return 4;
159
+ }
160
+ }
161
+ const templateRegistry = {};
162
+ export function registerTemplate(name, importer) {
163
+ if (templateRegistry[name])
164
+ throw new Error(`[template] '${name}' already registered`);
165
+ templateRegistry[name] = importer;
166
+ }
167
+ export function resolveTemplate(name) {
168
+ return templateRegistry[name];
169
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, 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, SlotScope, useSlots } from "../slot.js";
6
+ function SectionFields() {
7
+ return (_jsx(_Fragment, { children: useSlots().map((s) => (_jsx(Slot, { name: s.name }, s.name))) }));
8
+ }
9
+ function AccordionPage({ el }) {
10
+ const g = el;
11
+ const { t } = useTranslation();
12
+ const sections = g.view?.sections ?? [];
13
+ return (_jsx("div", { className: "flex min-w-0 flex-col gap-2", children: sections.map((section) => (_jsxs("details", { className: "rounded-lg border border-border", open: section.open, children: [_jsxs("summary", { className: "flex cursor-pointer items-center gap-1.5 px-3 py-2 font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring", children: [section.icon && _jsx(Icon, { name: section.icon, size: 14, className: "shrink-0" }), t(section.label)] }), _jsx("div", { className: "space-y-3 px-3 pb-3", children: _jsx(SlotScope, { name: section.name, children: _jsx(SectionFields, {}) }) })] }, section.name))) }));
14
+ }
15
+ registerRenderer({ kinds: ['accordion'], layout: 'block', editor: 'custom', Page: AccordionPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
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: ['aside'],
6
+ layout: 'block',
7
+ editor: 'custom',
8
+ Page: ({ el }) => {
9
+ const g = el;
10
+ const main = g.view?.main ?? [];
11
+ const side = g.view?.aside ?? [];
12
+ return (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-[minmax(0,1fr)_14rem]", children: [_jsx("div", { className: "min-w-0 space-y-3", children: main.map((n) => (_jsx(Slot, { name: n }, n))) }), _jsx("aside", { className: "min-w-0 space-y-2 text-sm text-muted sm:border-l sm:border-border sm:pl-4", children: side.map((n) => (_jsx(Slot, { name: n }, n))) })] }));
13
+ },
14
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
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 } from "../slot.js";
5
+ import { BlockShell } from "./block-shell.js";
6
+ function BalancePage({ el }) {
7
+ const g = el;
8
+ const sourceName = g.view?.source;
9
+ const { t } = useTranslation();
10
+ const caption = g.view?.caption;
11
+ const body = (_jsxs("div", { className: "flex flex-col gap-1", "data-testid": "balance", children: [_jsx(Slot, { name: sourceName, render: (_handle, defaultDisplay) => (_jsx("span", { className: "text-3xl font-semibold tabular-nums", "data-testid": "balance-figure", children: defaultDisplay })) }), caption && (_jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", "data-testid": "balance-caption", children: t(caption) }))] }));
12
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
13
+ }
14
+ registerRenderer({ kinds: ['balance'], layout: 'block', editor: 'custom', Page: BalancePage });
@@ -0,0 +1 @@
1
+ export declare const tileClass = "rounded-lg border border-border bg-surface p-3 min-w-0";
@@ -0,0 +1,24 @@
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
+ export const tileClass = 'rounded-lg border border-border bg-surface p-3 min-w-0';
6
+ const SPAN = {
7
+ '2x1': 'sm:col-span-2',
8
+ '1x2': 'sm:row-span-2',
9
+ '2x2': 'sm:col-span-2 sm:row-span-2',
10
+ wide: 'col-span-full',
11
+ };
12
+ function BentoPage({ el }) {
13
+ const g = el;
14
+ const spans = g.view?.spans ?? {};
15
+ const slots = useSlots();
16
+ const body = (_jsx("div", { className: "grid grid-cols-1 sm:grid-cols-3 auto-rows-auto gap-3", children: slots.map((s, i) => {
17
+ if (s.handle.hidden)
18
+ return null;
19
+ const span = spans[s.name];
20
+ return (_jsx("div", { className: `${tileClass} ${span ? (SPAN[span] ?? '') : ''}`, "data-testid": `bento-tile-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
21
+ }) }));
22
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
23
+ }
24
+ registerRenderer({ kinds: ['bento'], layout: 'block', editor: 'custom', Page: BentoPage });
@@ -0,0 +1,15 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ import type { SlotHandle } from '../slot.tsx';
3
+ import type { TFn } from '../../types.ts';
4
+ export type NamedSlot = {
5
+ name: string;
6
+ handle: SlotHandle;
7
+ };
8
+ export declare function findSlot(slots: readonly NamedSlot[], name: string | undefined): NamedSlot | undefined;
9
+ export declare function findSlots(slots: readonly NamedSlot[], names: readonly string[]): NamedSlot[];
10
+ export declare function BlockShell({ label, icon, children, }: {
11
+ label?: string;
12
+ icon?: string;
13
+ children: ReactNode;
14
+ }): ReactElement;
15
+ export declare function fieldLabel(handle: SlotHandle, t: TFn): string | undefined;
@@ -0,0 +1,20 @@
1
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useTranslation } from 'react-i18next';
3
+ import { isNamedField } from '@coffer-org/sdk/fields';
4
+ import { SectionShell } from "../../layout.js";
5
+ export function findSlot(slots, name) {
6
+ return name !== undefined ? slots.find((s) => s.name === name) : undefined;
7
+ }
8
+ export function findSlots(slots, names) {
9
+ return names.map((n) => findSlot(slots, n)).filter((s) => s !== undefined);
10
+ }
11
+ export function BlockShell({ label, icon, children, }) {
12
+ const { t } = useTranslation();
13
+ if (!label)
14
+ return _jsx(_Fragment, { children: children });
15
+ return (_jsx(SectionShell, { label: t(label), icon: icon, children: children }));
16
+ }
17
+ export function fieldLabel(handle, t) {
18
+ const key = handle.field.label ?? (isNamedField(handle.field) ? handle.field.type.label : undefined);
19
+ return key ? t(key) : undefined;
20
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,142 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { ChevronLeft, ChevronRight, Trash2, Plus } from 'lucide-react';
5
+ import { isNamedField, isStorageGroup } from '@coffer-org/sdk/fields';
6
+ import { registerRenderer } from "../../registry.js";
7
+ import { Popover, PopoverTrigger, PopoverContent, PopoverArrow } from "../../components/ui/popover.js";
8
+ import { Button } from "../../components/ui/button.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 { GroupField } from "../widgets/group-field.js";
14
+ import { useSlot } from "../slot.js";
15
+ import { ChecklistContent } from "../widgets/checklist-content.js";
16
+ import { useFieldErrors } from "../field-errors.js";
17
+ import { partField } from "../use-collection-rows.js";
18
+ function parseDay(v) {
19
+ const s = typeof v === 'string' ? v : typeof v === 'number' ? String(v) : '';
20
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(s);
21
+ if (!m)
22
+ return undefined;
23
+ return { year: Number(m[1]), month: Number(m[2]) - 1, day: Number(m[3]) };
24
+ }
25
+ function DayCell({ day, month, year, matchingRows, dateKey, dateFld, restFields, recordCtx, errorFor, onUpdateRow, onRemoveRow, onAddRow, rowTools, editing, }) {
26
+ const { t } = useTranslation();
27
+ const [open, setOpen] = useState(false);
28
+ if (day == null) {
29
+ return _jsx("div", { className: "aspect-square" });
30
+ }
31
+ const firstMatch = matchingRows[0];
32
+ const isMarked = firstMatch !== undefined;
33
+ const dateStr = `${year}-${String(month + 1).padStart(2, '0')}-${String(day).padStart(2, '0')}`;
34
+ const fullDateLabel = new Date(year, month, day).toLocaleDateString(undefined, {
35
+ day: 'numeric',
36
+ month: 'long',
37
+ year: 'numeric',
38
+ });
39
+ const primaryError = matchingRows
40
+ .map(({ idx }) => errorFor(idx, dateKey) || restFields.map((f) => errorFor(idx, f.key)).find(Boolean))
41
+ .find(Boolean);
42
+ const cellButton = (_jsxs("button", { type: "button", role: "button", className: `relative flex aspect-square w-full items-center justify-center rounded-md text-xs font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${primaryError
43
+ ? 'border border-danger/60 bg-danger/10 text-danger font-semibold'
44
+ : isMarked
45
+ ? 'bg-accent/20 font-semibold text-accent hover:bg-accent/30 shadow-xs'
46
+ : 'text-text hover:bg-muted/20'}`, "data-testid": firstMatch ? `calendar-date-${firstMatch.idx}` : undefined, "aria-label": dateStr, children: [day, isMarked && (_jsx("span", { "data-testid": `calendar-marked-${day}`, className: `absolute bottom-1 h-1 w-1 rounded-full ${primaryError ? 'bg-danger' : 'bg-accent'}` })), primaryError && _jsx("span", { className: "sr-only text-danger", children: primaryError })] }));
47
+ if (!rowTools) {
48
+ return (_jsx("div", { "data-testid": firstMatch ? `calendar-entry-${firstMatch.idx}` : undefined, className: "w-full", children: cellButton }));
49
+ }
50
+ return (_jsx("div", { "data-testid": firstMatch ? `calendar-entry-${firstMatch.idx}` : undefined, className: "w-full", children: _jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsx(PopoverTrigger, { asChild: true, children: cellButton }), _jsxs(PopoverContent, { align: "center", className: "w-72 p-3 space-y-3 shadow-lg", sideOffset: 6, children: [_jsx(PopoverArrow, {}), _jsxs("div", { className: "flex items-center justify-between border-b border-border/60 pb-2", children: [_jsx("span", { className: "text-xs font-semibold text-text", children: fullDateLabel }), _jsx("span", { className: "text-[11px] font-mono text-muted", children: dateStr })] }), isMarked ? (_jsx("div", { className: "space-y-3", children: matchingRows.map(({ row, idx }) => {
51
+ const err = errorFor(idx, dateKey);
52
+ return (_jsxs("div", { className: "space-y-2 border-b border-border/40 pb-2 last:border-b-0 last:pb-0", children: [err && _jsx("div", { className: "text-xs text-danger font-medium", children: err }), restFields.length > 0 && (_jsxs("div", { className: "space-y-1", children: [_jsx(ChecklistContent, { fields: restFields, row: row, editing: editing, onChange: (key, v) => onUpdateRow(idx, key, v), recordCtx: recordCtx, error: (key) => errorFor(idx, key) }), restFields.map((f) => {
53
+ const fieldErr = errorFor(idx, f.key);
54
+ return fieldErr ? (_jsx("div", { className: "text-xs text-danger font-medium", children: fieldErr }, f.key)) : null;
55
+ })] })), _jsx("div", { className: "flex items-center justify-between pt-1", children: _jsxs("button", { type: "button", "aria-label": "remove", onClick: () => {
56
+ onRemoveRow(idx);
57
+ setOpen(false);
58
+ }, className: "inline-flex items-center gap-1 text-xs text-danger hover:text-danger/80 transition-colors", children: [_jsx(Trash2, { size: 12 }), " ", t('actions.delete', 'Delete')] }) })] }, idx));
59
+ }) })) : (_jsx("div", { className: "space-y-2 pt-1", children: _jsxs(Button, { size: "sm", className: "w-full h-8 text-xs flex items-center justify-center gap-1.5", onClick: () => {
60
+ onAddRow(dateStr);
61
+ setOpen(false);
62
+ }, children: [_jsx(Plus, { size: 13 }), " ", t('actions.add', 'Add')] }) }))] })] }) }));
63
+ }
64
+ function MonthGrid({ rows, dateKey, dateFld, restFields, recordCtx, errorFor, onUpdateRow, onRemoveRow, onAddRow, rowTools, editing, }) {
65
+ const [viewDate, setViewDate] = useState(() => {
66
+ for (const r of rows) {
67
+ const d = parseDay(r[dateKey]);
68
+ if (d)
69
+ return new Date(d.year, d.month, 1);
70
+ }
71
+ return new Date();
72
+ });
73
+ const month = viewDate.getMonth();
74
+ const year = viewDate.getFullYear();
75
+ const prevMonth = () => setViewDate(new Date(year, month - 1, 1));
76
+ const nextMonth = () => setViewDate(new Date(year, month + 1, 1));
77
+ const first = new Date(year, month, 1);
78
+ const daysInMonth = new Date(year, month + 1, 0).getDate();
79
+ const lead = first.getDay();
80
+ const cells = [
81
+ ...Array.from({ length: lead }, () => null),
82
+ ...Array.from({ length: daysInMonth }, (_, i) => i + 1),
83
+ ];
84
+ while (cells.length % 7 !== 0)
85
+ cells.push(null);
86
+ const weekdays = ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'];
87
+ const monthName = new Date(year, month, 1).toLocaleDateString(undefined, { month: 'long', year: 'numeric' });
88
+ const rowsByDay = new Map();
89
+ const unassignedRows = [];
90
+ rows.forEach((row, idx) => {
91
+ const d = parseDay(row[dateKey]);
92
+ if (d && d.year === year && d.month === month) {
93
+ const list = rowsByDay.get(d.day) ?? [];
94
+ list.push({ row, idx });
95
+ rowsByDay.set(d.day, list);
96
+ }
97
+ else if (!d) {
98
+ unassignedRows.push({ row, idx });
99
+ }
100
+ });
101
+ return (_jsxs("div", { className: "w-full max-w-[280px] rounded-lg border border-border bg-surface p-3", "data-testid": "calendar-grid", children: [_jsxs("div", { className: "mb-2 flex items-center justify-between", children: [_jsx("span", { className: "text-xs font-semibold text-text capitalize", children: monthName }), _jsxs("div", { className: "flex items-center gap-0.5", children: [_jsx("button", { type: "button", onClick: prevMonth, "aria-label": "Previous month", className: "rounded p-1 text-muted hover:bg-muted/20 hover:text-text transition-colors", children: _jsx(ChevronLeft, { size: 13 }) }), _jsx("button", { type: "button", onClick: nextMonth, "aria-label": "Next month", className: "rounded p-1 text-muted hover:bg-muted/20 hover:text-text transition-colors", children: _jsx(ChevronRight, { size: 13 }) })] })] }), _jsx("div", { className: "mb-1 grid grid-cols-7 gap-1 text-center text-[10px] font-medium text-muted", children: weekdays.map((wd, i) => (_jsx("div", { className: "py-0.5", children: wd }, i))) }), _jsx("div", { className: "grid grid-cols-7 gap-1", children: cells.map((day, i) => (_jsx(DayCell, { day: day, month: month, year: year, matchingRows: day != null ? (rowsByDay.get(day) ?? []) : [], dateKey: dateKey, dateFld: dateFld, restFields: restFields, recordCtx: recordCtx, errorFor: errorFor, onUpdateRow: onUpdateRow, onRemoveRow: onRemoveRow, onAddRow: onAddRow, rowTools: rowTools, editing: editing }, i))) }), unassignedRows.length > 0 && (_jsx("div", { className: "mt-3 space-y-1.5 border-t border-border pt-2", children: unassignedRows.map(({ idx }) => {
102
+ const err = errorFor(idx, dateKey);
103
+ return (_jsxs("div", { "data-testid": `calendar-entry-${idx}`, className: "flex items-center justify-between text-xs", children: [_jsx("div", { "data-testid": `calendar-date-${idx}`, className: "text-danger font-medium", children: err || 'Required' }), rowTools && _jsx(RemoveBtn, { onClick: () => onRemoveRow(idx) })] }, idx));
104
+ }) }))] }));
105
+ }
106
+ function CalendarPage({ el, value, recordCtx, mode }) {
107
+ const { t } = useTranslation();
108
+ const depth = useGroupDepth();
109
+ const errorAt = useFieldErrors();
110
+ const { write } = useSlot();
111
+ const group = el;
112
+ const dateKey = group.view?.['date'];
113
+ const dateFld = partField(group, dateKey);
114
+ const g = useGroupEdit(mode, value, (v) => write(v));
115
+ const rows = Array.isArray(g.value) ? g.value : [];
116
+ const editing = g.mode !== 'view';
117
+ const live = useHybridScope() && mode === 'view';
118
+ const rowTools = editing || live;
119
+ if (!dateFld || !dateKey) {
120
+ return (_jsx(GroupField, { group: { ...group, kind: undefined }, value: value, onChange: (v) => void write(v), recordCtx: recordCtx, mode: mode }));
121
+ }
122
+ const restFields = group.fields
123
+ .filter((f) => isNamedField(f) && f.key !== dateKey)
124
+ .map((f) => ({ key: f.key, type: f.type }));
125
+ const errorFor = (idx, key) => isStorageGroup(group) ? errorAt([group.key, idx, key]) : undefined;
126
+ const updateRow = (idx, key, v) => {
127
+ const source = rows[idx];
128
+ const next = { ...source, [key]: v };
129
+ g.onChange(rows.map((r, i) => (i === idx ? next : r)));
130
+ };
131
+ const removeRow = (idx) => {
132
+ g.onChange(rows.filter((_, i) => i !== idx));
133
+ };
134
+ const addRow = (dateStr) => {
135
+ g.onChange([...rows, { [dateKey]: dateStr }]);
136
+ };
137
+ const content = (_jsx(MonthGrid, { rows: rows, dateKey: dateKey, dateFld: dateFld, restFields: restFields, recordCtx: recordCtx, errorFor: errorFor, onUpdateRow: updateRow, onRemoveRow: removeRow, onAddRow: addRow, rowTools: rowTools, editing: editing }));
138
+ if (mode !== 'view')
139
+ return content;
140
+ return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-1 min-w-0`, "data-testid": "calendar", 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 })] }));
141
+ }
142
+ registerRenderer({ kinds: ['calendar'], layout: 'block', Page: CalendarPage, editor: 'custom' });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
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 TONE = {
6
+ note: 'border-l-4 border-accent bg-accent/5',
7
+ warn: 'border-l-4 border-warning bg-warning/10 font-medium',
8
+ danger: 'border-l-[6px] border-danger bg-danger/10 font-semibold',
9
+ };
10
+ function CalloutPage({ el }) {
11
+ const g = el;
12
+ const tone = g.view?.tone ?? 'note';
13
+ const slots = useSlots();
14
+ const body = (_jsx("div", { className: `space-y-2 rounded-lg px-4 py-3 ${TONE[tone] ?? TONE.note}`, "data-testid": "callout", "data-tone": tone, children: slots.map((s, i) => {
15
+ if (s.handle.hidden)
16
+ return null;
17
+ return (_jsx("div", { className: "min-w-0", "data-testid": `callout-item-${i}`, children: _jsx(Slot, { name: s.name }) }, s.name));
18
+ }) }));
19
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
20
+ }
21
+ registerRenderer({ kinds: ['callout'], layout: 'block', editor: 'custom', Page: CalloutPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,46 @@
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 { groupScopeClass, useGroupDepth, GroupDepthProvider } from "../chrome.js";
5
+ import { useGroupEdit } from "../widgets/group-edit.js";
6
+ import { GroupField } from "../widgets/group-field.js";
7
+ import { FieldSlot } from "../dispatch.js";
8
+ import { useSlot } from "../slot.js";
9
+ import { toSeries, sparkline, seriesDelta } from "./series.js";
10
+ import { partField } from "../use-collection-rows.js";
11
+ const BOX = { width: 100, height: 28, pad: 2 };
12
+ function fmtDelta(n) {
13
+ const rounded = Math.round(n * 100) / 100;
14
+ return `${rounded > 0 ? '+' : ''}${rounded}`;
15
+ }
16
+ function ChartPage({ el, value, recordCtx, mode }) {
17
+ const { t } = useTranslation();
18
+ const depth = useGroupDepth();
19
+ const { write } = useSlot();
20
+ const group = el;
21
+ const valueKey = group.view?.['value'];
22
+ const dateKey = group.view?.['date'];
23
+ const showDelta = Boolean(group.view?.['delta']);
24
+ const valueFld = partField(group, valueKey);
25
+ const dateFld = partField(group, dateKey);
26
+ const g = useGroupEdit(mode, value, (v) => write(v));
27
+ const rows = Array.isArray(g.value) ? g.value : [];
28
+ const editing = g.mode !== 'view';
29
+ if (!valueKey || !dateKey || !valueFld || !dateFld) {
30
+ return (_jsx(GroupField, { group: { ...group, kind: undefined }, value: value, onChange: (v) => void write(v), recordCtx: recordCtx, mode: mode }));
31
+ }
32
+ const points = toSeries(rows, valueKey, dateKey);
33
+ const spark = sparkline(points, BOX);
34
+ const last = points[points.length - 1];
35
+ const delta = showDelta ? seriesDelta(points) : undefined;
36
+ const editor = (_jsx(GroupField, { group: { ...group, kind: undefined }, value: g.value, onChange: (v) => g.onChange(v), recordCtx: recordCtx, mode: "edit" }));
37
+ const reading = (p) => (_jsx(FieldSlot, { el: valueFld, field: valueFld.type, value: p.row?.[valueKey] ?? p.value, mode: "view", bare: true, recordCtx: recordCtx }));
38
+ const lowest = points.length ? points.reduce((a, b) => (b.value < a.value ? b : a)) : undefined;
39
+ const highest = points.length ? points.reduce((a, b) => (b.value > a.value ? b : a)) : undefined;
40
+ const chart = last === undefined ? (_jsx("p", { className: "py-1 text-sm text-muted", "data-testid": "chart-empty", children: t('form.noSeries', 'No readings yet') })) : (_jsxs("div", { className: "flex flex-col gap-1.5", "data-testid": "chart-body", children: [_jsxs("div", { className: "flex items-baseline gap-2", children: [_jsx("div", { className: "min-w-0 text-2xl font-semibold tabular-nums text-text", "data-testid": "chart-last", children: reading(last) }), delta !== undefined && (_jsx("span", { className: `text-sm font-medium tabular-nums ${delta > 0 ? 'text-accent' : delta < 0 ? 'text-danger' : 'text-muted'}`, "data-testid": "chart-delta", children: fmtDelta(delta) }))] }), _jsx("svg", { className: "h-12 w-full text-accent", viewBox: `0 0 ${BOX.width} ${BOX.height}`, preserveAspectRatio: "none", "aria-hidden": "true", "data-testid": "chart-spark", children: _jsx("path", { d: spark.path, "data-testid": "chart-path", fill: "none", stroke: "currentColor", strokeWidth: 1.5, strokeLinecap: "round", strokeLinejoin: "round", vectorEffect: "non-scaling-stroke" }) }), _jsxs("div", { className: "flex items-baseline gap-1 text-[10px] tabular-nums text-muted", "data-testid": "chart-range", children: [lowest && reading(lowest), _jsx("span", { "aria-hidden": "true", children: "\u2013" }), highest && reading(highest)] })] }));
41
+ const content = editing ? editor : chart;
42
+ if (mode !== 'view')
43
+ return content;
44
+ return (_jsxs("section", { className: `${groupScopeClass(depth)} flex flex-col gap-1 min-w-0`, "data-testid": "chart", 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) }), 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 })] }));
45
+ }
46
+ registerRenderer({ kinds: ['chart'], layout: 'block', Page: ChartPage, editor: 'custom' });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,16 @@
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 } from "../slot.js";
5
+ import { BlockShell } from "./block-shell.js";
6
+ function ComparePage({ el }) {
7
+ const g = el;
8
+ const { t } = useTranslation();
9
+ const left = g.view?.left ?? [];
10
+ const right = g.view?.right ?? [];
11
+ const leftLabel = g.view?.leftLabel;
12
+ const rightLabel = g.view?.rightLabel;
13
+ const body = (_jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", "data-testid": "compare", children: [_jsxs("div", { className: "min-w-0 space-y-3", "data-testid": "compare-left", children: [leftLabel && _jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", children: t(leftLabel) }), left.map((n) => (_jsx(Slot, { name: n }, n)))] }), _jsxs("div", { className: "min-w-0 space-y-3 sm:border-l sm:border-border sm:pl-4", "data-testid": "compare-right", children: [rightLabel && _jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", children: t(rightLabel) }), right.map((n) => (_jsx(Slot, { name: n }, n)))] })] }));
14
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
15
+ }
16
+ registerRenderer({ kinds: ['compare'], layout: 'block', editor: 'custom', Page: ComparePage });
@@ -0,0 +1 @@
1
+ export declare function countdownDays(value: unknown, today: string): number | null;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ArrowUp, ArrowDown, Minus } from 'lucide-react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { registerRenderer } from "../../registry.js";
5
+ import { Slot } from "../slot.js";
6
+ import { BlockShell } from "./block-shell.js";
7
+ const MS_PER_DAY = 86_400_000;
8
+ export function countdownDays(value, today) {
9
+ const s = typeof value === 'string' ? value.slice(0, 10) : '';
10
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(s))
11
+ return null;
12
+ const t = Date.parse(s);
13
+ const t0 = Date.parse(today);
14
+ if (Number.isNaN(t) || Number.isNaN(t0))
15
+ return null;
16
+ return Math.round((t - t0) / MS_PER_DAY);
17
+ }
18
+ function todayISO() {
19
+ return new Date().toISOString().slice(0, 10);
20
+ }
21
+ function CountdownPage({ el }) {
22
+ const g = el;
23
+ const sourceName = g.view?.source;
24
+ const { t } = useTranslation();
25
+ const caption = g.view?.caption;
26
+ const body = (_jsxs("div", { className: "flex flex-col gap-1", "data-testid": "countdown", children: [_jsx(Slot, { name: sourceName, render: (handle, defaultDisplay) => {
27
+ const days = countdownDays(handle.value, todayISO());
28
+ if (days === null)
29
+ return defaultDisplay;
30
+ const direction = days > 0 ? 'remaining' : days < 0 ? 'elapsed' : 'today';
31
+ const Ico = days > 0 ? ArrowUp : days < 0 ? ArrowDown : Minus;
32
+ return (_jsxs("span", { className: "inline-flex items-baseline gap-3", "data-testid": "countdown-figure", "data-direction": direction, children: [_jsxs("span", { className: "inline-flex items-baseline gap-1.5", children: [_jsx(Ico, { size: 16, className: "shrink-0 self-center text-muted", "aria-hidden": "true" }), _jsx("span", { className: "text-3xl font-bold tabular-nums", children: Math.abs(days) })] }), _jsx("span", { className: "text-sm text-muted", "data-testid": "countdown-date", children: defaultDisplay })] }));
33
+ } }), caption && (_jsx("div", { className: "text-xs font-medium uppercase tracking-wide text-muted", "data-testid": "countdown-caption", children: t(caption) }))] }));
34
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: body }));
35
+ }
36
+ registerRenderer({ kinds: ['countdown'], layout: 'block', editor: 'custom', Page: CountdownPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Link } from 'react-router-dom';
3
+ import { getShelf } from "../../schema-registry.js";
4
+ import { RecordInline } from "../../components/RecordInline.js";
5
+ import { useRecordsByIds } from "../../lib/use-record-labels.js";
6
+ import { Dash, shortId } from "../shared.js";
7
+ import { registerRenderer } from "../../registry.js";
8
+ import { Slot } from "../slot.js";
9
+ import { BlockShell } from "./block-shell.js";
10
+ import { relationIds } from "./relation-source.js";
11
+ function DeckCards({ field, value }) {
12
+ const library = field.relation?.library ?? '';
13
+ const shelf = field.relation?.shelf ?? '';
14
+ const ids = relationIds(field, value);
15
+ const recordMap = useRecordsByIds(library, shelf, ids);
16
+ const m = getShelf(library, shelf);
17
+ if (!ids.length)
18
+ return _jsx(Dash, {});
19
+ return (_jsx("div", { className: "overflow-x-auto", "data-testid": "deck", children: _jsx("div", { className: "flex gap-3 pb-1", children: ids.map((id) => {
20
+ const row = recordMap.get(id);
21
+ return (_jsx("div", { className: "flex w-40 shrink-0 flex-col gap-2 rounded-lg border border-border p-3", "data-testid": "deck-card", children: _jsx(Link, { to: `/${library}/${shelf}/${id}`, className: "min-w-0 hover:text-accent", children: m && row ? _jsx(RecordInline, { shelf: m, row: row }) : shortId(id) }) }, id));
22
+ }) }) }));
23
+ }
24
+ function DeckPage({ el }) {
25
+ const g = el;
26
+ const sourceName = g.view?.source;
27
+ return (_jsx(BlockShell, { label: g.label, icon: g.icon, children: _jsx(Slot, { name: sourceName, render: (handle) => _jsx(DeckCards, { field: handle.field.type, value: handle.value }) }) }));
28
+ }
29
+ registerRenderer({ kinds: ['deck'], layout: 'block', editor: 'custom', Page: DeckPage });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,13 @@
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 EpigraphPage({ el }) {
6
+ const g = el;
7
+ const sourceName = g.view?.source;
8
+ const attributionName = g.view?.attribution;
9
+ const slots = useSlots();
10
+ const attribution = findSlot(slots, attributionName);
11
+ return (_jsxs("blockquote", { className: "border-l-2 border-accent pl-4", "data-testid": "epigraph", children: [_jsx("div", { className: "min-w-0 text-lg leading-snug italic", "data-testid": "epigraph-source", children: _jsx(Slot, { name: sourceName }) }), attribution && !attribution.handle.hidden && (_jsx("div", { className: "mt-2 min-w-0 text-sm text-muted before:mr-1 before:content-['\u2014']", "data-testid": "epigraph-attribution", children: _jsx(Slot, { name: attributionName }) }))] }));
12
+ }
13
+ registerRenderer({ kinds: ['epigraph'], layout: 'block', editor: 'custom', Page: EpigraphPage });
@@ -0,0 +1 @@
1
+ export {};