@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,25 @@
1
+ import { createContext, useContext } from 'react';
2
+ export const FieldErrorsContext = createContext(() => undefined);
3
+ export const useFieldErrors = () => useContext(FieldErrorsContext);
4
+ export const pathKey = (path) => path.join('.');
5
+ export function makeIssueText(t) {
6
+ return (code, params) => code === 'pattern' && params?.['messageKey'] ? t(String(params['messageKey'])) : t(`validation.${code}`, params);
7
+ }
8
+ export function mapIssues(issues, text) {
9
+ const out = {};
10
+ for (const i of issues) {
11
+ const k = pathKey(i.path?.length ? i.path : [i.field]);
12
+ if (!out[k])
13
+ out[k] = text(i.code, i.params);
14
+ }
15
+ return out;
16
+ }
17
+ export function clearPrefix(errors, key) {
18
+ const out = {};
19
+ for (const [k, v] of Object.entries(errors)) {
20
+ if (k === key || k.startsWith(key + '.'))
21
+ continue;
22
+ out[k] = v;
23
+ }
24
+ return out;
25
+ }
@@ -0,0 +1,20 @@
1
+ import type { ReactNode } from 'react';
2
+ import type { BoxGeom, FieldMode } from '../types.ts';
3
+ import type { FieldMeta, OptionItem } from '@coffer-org/sdk/fields';
4
+ export declare function focusInPopup(related: EventTarget | null): boolean;
5
+ type FieldFrameProps = {
6
+ field: FieldMeta;
7
+ value: unknown;
8
+ label?: string;
9
+ required?: boolean;
10
+ error?: string;
11
+ mode: FieldMode;
12
+ geom: BoxGeom;
13
+ view: (value: unknown, commit?: (v: unknown) => void | Promise<string | undefined>) => ReactNode;
14
+ edit: (value: unknown, onChange: (v: unknown) => void) => ReactNode;
15
+ bare?: boolean;
16
+ pickerOptions?: OptionItem[];
17
+ width?: string;
18
+ };
19
+ export declare function FieldFrame(props: FieldFrameProps): import("react").JSX.Element;
20
+ export {};
@@ -0,0 +1,78 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useFieldEdit, normalizeValue } from "./use-field-edit.js";
3
+ import { FieldLabel, FieldBox, FieldError, EditToggle, SaveCancel } from "./chrome.js";
4
+ import { useInFlow } from "./widgets/flow-row.js";
5
+ import { editorMode } from "./editor-mode.js";
6
+ import { useFieldAddress } from "./field-address.js";
7
+ import { useHybridScope } from "./hybrid-scope.js";
8
+ import { useFieldWidth } from "./field-width.js";
9
+ import { PopoverField } from "./popover-field.js";
10
+ import { useSlotOptional, useSlotRender } from "./slot.js";
11
+ const POPUP_SEL = '[data-slot="combobox-content"],[data-slot="popover-content"],[role="listbox"],[role="dialog"],[role="menu"],[data-scope="date-picker"][data-part="content"]';
12
+ export function focusInPopup(related) {
13
+ const el = related instanceof Element ? related : typeof document !== 'undefined' ? document.activeElement : null;
14
+ return el instanceof Element && el.closest(POPUP_SEL) !== null;
15
+ }
16
+ export function FieldFrame(props) {
17
+ return _jsx(FieldFrameBody, { ...props });
18
+ }
19
+ function FieldFrameBody(props) {
20
+ const { field, value, label, required, error, mode, geom, view, edit, bare, pickerOptions, width: ownWidth = 'w-64', } = props;
21
+ const controlled = mode !== 'view';
22
+ const slot = useSlotOptional();
23
+ const renderOverride = useSlotRender();
24
+ const write = slot?.writable ? slot.write : undefined;
25
+ const fe = useFieldEdit(field, value, write);
26
+ const inFlow = useInFlow();
27
+ const address = useFieldAddress();
28
+ const onRecordPage = useHybridScope();
29
+ const width = useFieldWidth() ?? ownWidth;
30
+ const emphasis = field.hints?.['emphasis'];
31
+ const noLabel = field.hints?.['noLabel'] === true || label === undefined;
32
+ const display = (val, commit) => {
33
+ const base = view(val, commit);
34
+ return renderOverride && slot ? renderOverride(slot, base) : base;
35
+ };
36
+ const editMode = !bare && !controlled && !field.derived && onRecordPage && address.key !== undefined && Boolean(write)
37
+ ? editorMode(field)
38
+ : null;
39
+ if (editMode !== null && editMode !== 'custom')
40
+ return (_jsx(PopoverField, { field: field, value: value, label: label, required: required, error: editMode === 'inplace' ? (slot?.error ?? error) : error, geom: geom, view: display, edit: edit, pickerOptions: pickerOptions, onCommit: write, width: width }));
41
+ const content = (editing, val, setVal, commit) => (editing ? edit(normalizeValue(field, val), setVal) : display(val, commit));
42
+ if (bare)
43
+ return (_jsxs(_Fragment, { children: [content(controlled, value, (v) => write?.(v), write), _jsx(FieldError, { error: slot?.error ?? error })] }));
44
+ if (controlled) {
45
+ if (label === undefined) {
46
+ return _jsx("div", { className: "min-w-0 flex-1", children: content(true, value, (v) => write?.(v)) });
47
+ }
48
+ return (_jsxs("div", { className: `group/field ${width}`, children: [_jsx(FieldLabel, { label: label, required: required }), content(true, value, (v) => write?.(v)), _jsx(FieldError, { error: slot?.error ?? error })] }));
49
+ }
50
+ if (field.hints['noEditControl'] === true) {
51
+ const box = (_jsx(FieldBox, { geom: geom, field: field, children: display(value, write) }));
52
+ if (label === undefined)
53
+ return box;
54
+ return (_jsxs("div", { className: `group/field ${width}`, children: [_jsx(FieldLabel, { label: label, required: required }), box, _jsx(FieldError, { error: slot?.error ?? error })] }));
55
+ }
56
+ const editLayer = (_jsx("div", { className: "min-w-0", onKeyDown: (e) => {
57
+ if (e.key === 'Escape') {
58
+ e.stopPropagation();
59
+ fe.cancel();
60
+ }
61
+ }, children: content(true, fe.draft, fe.setDraft) }));
62
+ const viewLayer = (_jsx(FieldBox, { geom: geom, field: field, children: content(false, value, () => { }, write) }));
63
+ const zone = inFlow ? (_jsxs("div", { className: "grid min-w-0", children: [_jsx("div", { className: "col-start-1 row-start-1 min-w-0", "aria-hidden": !fe.editing, inert: !fe.editing ? true : undefined, style: { visibility: fe.editing ? 'visible' : 'hidden' }, children: editLayer }), _jsx("div", { className: "col-start-1 row-start-1 min-w-0", style: { visibility: fe.editing ? 'hidden' : 'visible' }, children: viewLayer })] })) : fe.editing ? (editLayer) : (viewLayer);
64
+ const pencilOverlay = write && !fe.editing ? _jsx(EditToggle, { variant: "overlay", onEdit: fe.beginEdit }) : null;
65
+ const editControls = write && fe.editing ? (_jsx("div", { className: "absolute right-1 top-0 z-10 flex gap-1", children: _jsx(SaveCancel, { size: "xs", busy: fe.busy, onSave: () => fe.commit(), onCancel: fe.cancel }) })) : null;
66
+ if (emphasis === 'hero') {
67
+ const heroAction = write ? (fe.editing ? (_jsx(SaveCancel, { size: "sm", busy: fe.busy, onSave: () => fe.commit(), onCancel: fe.cancel })) : (_jsx(EditToggle, { onEdit: fe.beginEdit }))) : null;
68
+ return (_jsxs("div", { className: "group/field flex items-center gap-2 min-w-0", children: [_jsx("div", { className: "text-lg font-semibold text-text min-w-0", children: fe.editing ? editLayer : content(false, value, () => { }, write) }), heroAction, _jsx(FieldError, { error: slot?.error ?? error })] }));
69
+ }
70
+ if (emphasis === 'muted') {
71
+ const mutedAction = write ? (fe.editing ? (_jsx(SaveCancel, { size: "xs", busy: fe.busy, onSave: () => fe.commit(), onCancel: fe.cancel })) : (_jsx(EditToggle, { onEdit: fe.beginEdit }))) : null;
72
+ return (_jsxs("div", { className: "group/field grid grid-cols-[110px_1fr] items-center gap-x-3 text-xs text-muted min-w-0", children: [_jsxs("span", { className: "flex items-center gap-1.5", children: [label, mutedAction] }), _jsx("span", { className: "min-w-0", children: fe.editing ? editLayer : content(false, value, () => { }, write) }), _jsx(FieldError, { error: slot?.error ?? error })] }));
73
+ }
74
+ if (noLabel) {
75
+ return (_jsxs("div", { className: "group/field relative min-w-0 pr-8", children: [zone, pencilOverlay, editControls, _jsx(FieldError, { error: slot?.error ?? error })] }));
76
+ }
77
+ return (_jsxs("div", { className: `group/field relative ${width}`, children: [_jsx(FieldLabel, { label: label, required: required, reserveAction: Boolean(write) }), zone, pencilOverlay, editControls, _jsx(FieldError, { error: slot?.error ?? error })] }));
78
+ }
@@ -0,0 +1,8 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { FieldMeta } from '@coffer-org/sdk/fields';
3
+ export declare function ParentValuesProvider({ values, children, }: {
4
+ values: Record<string, unknown> | undefined;
5
+ children: ReactNode;
6
+ }): import("react").JSX.Element;
7
+ export declare const useParentValues: () => Record<string, unknown> | undefined;
8
+ export declare function withScopeValues(field: FieldMeta, self: Record<string, unknown> | undefined, parent?: Record<string, unknown> | undefined): FieldMeta;
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from 'react';
3
+ const SCOPES = [
4
+ ['unitFrom', 'unitValue'],
5
+ ['currencyFrom', 'currencyValue'],
6
+ ];
7
+ const ParentValues = createContext(undefined);
8
+ export function ParentValuesProvider({ values, children, }) {
9
+ return _jsx(ParentValues.Provider, { value: values, children: children });
10
+ }
11
+ export const useParentValues = () => useContext(ParentValues);
12
+ export function withScopeValues(field, self, parent) {
13
+ let out = field;
14
+ for (const [from, into] of SCOPES) {
15
+ const scope = String(field.hints[from] ?? '');
16
+ if (!scope)
17
+ continue;
18
+ const parentKey = scope.startsWith('parent:') ? scope.slice('parent:'.length) : null;
19
+ const v = parentKey ? parent?.[parentKey] : scope.includes(':') ? undefined : self?.[scope];
20
+ if (v == null || v === '')
21
+ continue;
22
+ out = { ...out, hints: { ...out.hints, [into]: String(v) } };
23
+ }
24
+ return out;
25
+ }
@@ -0,0 +1,7 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function FieldWidthProvider({ width, children }: {
3
+ width: string;
4
+ children: ReactNode;
5
+ }): import("react").JSX.Element;
6
+ export declare function useFieldWidth(): string | undefined;
7
+ export declare function withChildWidth(width: string | undefined, node: ReactNode): ReactNode;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from 'react';
3
+ const FieldWidthContext = createContext(undefined);
4
+ export function FieldWidthProvider({ width, children }) {
5
+ return _jsx(FieldWidthContext.Provider, { value: width, children: children });
6
+ }
7
+ export function useFieldWidth() {
8
+ return useContext(FieldWidthContext);
9
+ }
10
+ export function withChildWidth(width, node) {
11
+ return width ? _jsx(FieldWidthProvider, { width: width, children: node }) : node;
12
+ }
@@ -0,0 +1,12 @@
1
+ import type { FieldMode, RecordCtx } from '../types.ts';
2
+ export interface FormBridge {
3
+ mode: FieldMode;
4
+ recordCtx: RecordCtx;
5
+ getValue(key: string): unknown;
6
+ setValue(key: string, v: unknown): void | Promise<string | undefined>;
7
+ isHidden(key: string): boolean;
8
+ requiredOf(key: string): boolean;
9
+ error(key: string): string | undefined;
10
+ touch(key: string): void;
11
+ values: Record<string, unknown>;
12
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { type ReactNode } from 'react';
2
+ export declare function HybridScope({ children }: {
3
+ children: ReactNode;
4
+ }): import("react").JSX.Element;
5
+ export declare function useHybridScope(): boolean;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext } from 'react';
3
+ const HybridScopeContext = createContext(false);
4
+ export function HybridScope({ children }) {
5
+ return _jsx(HybridScopeContext.Provider, { value: true, children: children });
6
+ }
7
+ export function useHybridScope() {
8
+ return useContext(HybridScopeContext);
9
+ }
@@ -0,0 +1,64 @@
1
+ import type { FieldInlineProps } from '../types.ts';
2
+ export type FileRec = {
3
+ name: string;
4
+ mime?: string;
5
+ size?: number;
6
+ };
7
+ export declare function isVideoName(name: string): boolean;
8
+ export declare function Thumb({ name }: {
9
+ name: string;
10
+ }): import("react").JSX.Element;
11
+ export declare function parseFileRecs(value: unknown): FileRec[];
12
+ export type LightboxEdit = {
13
+ accept: string;
14
+ busy: boolean;
15
+ onReplace: (index: number, file: File) => void;
16
+ onDelete: (index: number) => void;
17
+ replaceLabel: string;
18
+ deleteLabel: string;
19
+ };
20
+ export declare function Lightbox({ images, index, onClose, onIndex, edit, }: {
21
+ images: string[];
22
+ index: number;
23
+ onClose: () => void;
24
+ onIndex: (i: number) => void;
25
+ edit?: LightboxEdit;
26
+ }): import("react").JSX.Element;
27
+ export declare function GalleryView({ value, poster }: {
28
+ value: unknown;
29
+ poster?: boolean;
30
+ }): import("react").JSX.Element;
31
+ export declare function AvatarView({ value }: {
32
+ value: unknown;
33
+ }): import("react").JSX.Element;
34
+ export declare function AvatarInline({ value }: FieldInlineProps): import("react").JSX.Element;
35
+ export declare function CoverView({ value, banner }: {
36
+ value: unknown;
37
+ banner?: boolean;
38
+ }): import("react").JSX.Element;
39
+ export declare function ImageWidget({ value, onChange, accept, poster, width, }: {
40
+ value: unknown;
41
+ onChange: (v: FileRec | '') => void;
42
+ accept?: string;
43
+ poster?: boolean;
44
+ width?: number;
45
+ }): import("react").JSX.Element;
46
+ export declare function ImagesWidget({ value, onChange, accept, poster, }: {
47
+ value: unknown;
48
+ onChange: (v: FileRec[] | '') => void;
49
+ accept?: string;
50
+ poster?: boolean;
51
+ }): import("react").JSX.Element;
52
+ export declare function AvatarWidget({ value, onChange, accept, multiple, }: {
53
+ value: unknown;
54
+ onChange: (v: FileRec | FileRec[] | '') => void;
55
+ accept?: string;
56
+ multiple?: boolean;
57
+ }): import("react").JSX.Element;
58
+ export declare function CoverWidget({ value, onChange, accept, multiple, }: {
59
+ value: unknown;
60
+ onChange: (v: FileRec[] | '') => void;
61
+ accept?: string;
62
+ multiple?: boolean;
63
+ }): import("react").JSX.Element;
64
+ export declare function ImageInline({ value, height }: FieldInlineProps): import("react").JSX.Element;
@@ -0,0 +1,322 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { useTranslation } from 'react-i18next';
4
+ import { ChevronLeft, ChevronRight, Loader2, Play, RefreshCw, Trash2, Upload, X } from 'lucide-react';
5
+ import { mediaUrl } from "../host-services.js";
6
+ import { confirmDialog } from "../components/ui/confirm.js";
7
+ import { Dash, UploadError, UploadProgress } from "./shared.js";
8
+ import { useFileUpload } from "./use-file-upload.js";
9
+ function confirmDeletePhoto(t) {
10
+ return confirmDialog({
11
+ title: t('actions.confirmDeletePhoto'),
12
+ confirmLabel: t('actions.delete'),
13
+ variant: 'destructive',
14
+ });
15
+ }
16
+ const VIDEO_EXT = /\.(mp4|webm|ogg|mov|m4v)$/i;
17
+ export function isVideoName(name) {
18
+ return VIDEO_EXT.test(name);
19
+ }
20
+ export function Thumb({ name }) {
21
+ if (isVideoName(name)) {
22
+ return (_jsxs(_Fragment, { children: [_jsx("video", { src: mediaUrl(name), muted: true, playsInline: true, preload: "metadata", className: "h-full w-full object-cover" }), _jsx("span", { className: "pointer-events-none absolute inset-0 flex items-center justify-center", children: _jsx(Play, { size: 28, className: "text-white/90 drop-shadow", fill: "currentColor" }) })] }));
23
+ }
24
+ return (_jsx("img", { src: mediaUrl(name), alt: "", loading: "lazy", className: "h-full w-full object-cover transition-opacity hover:opacity-90" }));
25
+ }
26
+ export function parseFileRecs(value) {
27
+ const isRec = (x) => !!x && typeof x === 'object' && typeof x.name === 'string';
28
+ if (Array.isArray(value))
29
+ return value.filter(isRec);
30
+ if (isRec(value))
31
+ return [value];
32
+ try {
33
+ const p = JSON.parse(String(value ?? ''));
34
+ const arr = Array.isArray(p) ? p : p ? [p] : [];
35
+ return arr.filter(isRec);
36
+ }
37
+ catch {
38
+ return [];
39
+ }
40
+ }
41
+ export function Lightbox({ images, index, onClose, onIndex, edit, }) {
42
+ const many = images.length > 1;
43
+ const prev = () => onIndex((index - 1 + images.length) % images.length);
44
+ const next = () => onIndex((index + 1) % images.length);
45
+ useEffect(() => {
46
+ const fn = (e) => {
47
+ if (e.key === 'Escape')
48
+ onClose();
49
+ else if (e.key === 'ArrowLeft' && many)
50
+ prev();
51
+ else if (e.key === 'ArrowRight' && many)
52
+ next();
53
+ };
54
+ document.addEventListener('keydown', fn);
55
+ document.body.style.overflow = 'hidden';
56
+ return () => {
57
+ document.removeEventListener('keydown', fn);
58
+ document.body.style.overflow = '';
59
+ };
60
+ }, [index, images.length]);
61
+ return (_jsxs("div", { className: "fixed inset-0 z-[100] bg-black/85 backdrop-blur-sm flex items-center justify-center", onClick: onClose, children: [edit && (_jsxs("div", { className: "absolute top-4 left-1/2 flex -translate-x-1/2 items-center gap-2", onClick: (e) => e.stopPropagation(), children: [_jsxs("label", { className: `flex cursor-pointer items-center gap-2 rounded-full bg-white/10 px-3 py-2 text-sm text-white transition-colors hover:bg-white/20 ${edit.busy ? 'pointer-events-none opacity-60' : ''}`, children: [edit.busy ? _jsx(Loader2, { size: 16, className: "animate-spin" }) : _jsx(RefreshCw, { size: 16 }), _jsx("span", { children: edit.replaceLabel }), _jsx("input", { type: "file", accept: edit.accept, className: "hidden", disabled: edit.busy, onChange: (e) => {
62
+ const f = e.target.files?.[0];
63
+ if (f)
64
+ edit.onReplace(index, f);
65
+ } })] }), _jsxs("button", { type: "button", disabled: edit.busy, className: "flex items-center gap-2 rounded-full bg-white/10 px-3 py-2 text-sm text-white transition-colors hover:bg-white/20 hover:text-red-400 disabled:opacity-60", onClick: () => edit.onDelete(index), children: [_jsx(Trash2, { size: 16 }), _jsx("span", { children: edit.deleteLabel })] })] })), _jsx("button", { className: "lightbox-btn absolute top-4 right-4", onClick: onClose, children: _jsx(X, { size: 22 }) }), many && (_jsx("button", { className: "lightbox-btn absolute left-3 md:left-6", onClick: (e) => {
66
+ e.stopPropagation();
67
+ prev();
68
+ }, children: _jsx(ChevronLeft, { size: 26 }) })), isVideoName(images[index]) ? (_jsx("video", { src: mediaUrl(images[index]), controls: true, autoPlay: true, className: "max-h-[90dvh] max-w-[92vw] rounded-lg shadow-2xl", onClick: (e) => e.stopPropagation() })) : (_jsx("img", { src: mediaUrl(images[index]), alt: "", className: "max-h-[90dvh] max-w-[92vw] object-contain rounded-lg shadow-2xl", onClick: (e) => e.stopPropagation() })), many && (_jsx("button", { className: "lightbox-btn absolute right-3 md:right-6", onClick: (e) => {
69
+ e.stopPropagation();
70
+ next();
71
+ }, children: _jsx(ChevronRight, { size: 26 }) })), many && (_jsxs("div", { className: "absolute bottom-5 left-1/2 -translate-x-1/2 text-white/70 text-sm tabular-nums", children: [index + 1, " / ", images.length] }))] }));
72
+ }
73
+ const SMALL_SLOTS = 4;
74
+ const MOSAIC = {
75
+ photo: { large: 'h-[12.5rem] w-[12.5rem]', small: 'h-24 w-24' },
76
+ poster: { large: 'h-[18.5rem] w-[12.5rem]', small: 'h-36 w-24' },
77
+ };
78
+ function MosaicTile({ rec, size, onClick, over }) {
79
+ return (_jsxs("button", { type: "button", onClick: onClick, className: `relative ${size} rounded-xl overflow-hidden border border-border hover:border-accent transition-colors cursor-zoom-in`, children: [_jsx(Thumb, { name: rec.name }), over != null && (_jsxs("span", { className: "pointer-events-none absolute inset-0 grid place-items-center bg-black/55 text-lg font-medium text-white tabular-nums", children: ["+", over] }))] }));
80
+ }
81
+ export function GalleryView({ value, poster }) {
82
+ const list = parseFileRecs(value);
83
+ const [open, setOpen] = useState(null);
84
+ if (!list.length)
85
+ return _jsx(Dash, {});
86
+ const names = list.map((r) => r.name);
87
+ const size = poster ? MOSAIC.poster : MOSAIC.photo;
88
+ const rest = list.slice(1, 1 + SMALL_SLOTS);
89
+ const hidden = list.length - 1 - rest.length;
90
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "flex flex-wrap items-start gap-2", children: [_jsx(MosaicTile, { rec: list[0], size: size.large, onClick: () => setOpen(0) }), rest.length > 0 && (_jsx("div", { className: "flex w-[12.5rem] flex-wrap gap-2", children: rest.map((rec, i) => (_jsx(MosaicTile, { rec: rec, size: size.small, onClick: () => setOpen(i + 1), over: hidden > 0 && i === rest.length - 1 ? hidden + 1 : undefined }, `${rec.name}-${i + 1}`))) }))] }), open !== null && _jsx(Lightbox, { images: names, index: open, onClose: () => setOpen(null), onIndex: setOpen })] }));
91
+ }
92
+ export function AvatarView({ value }) {
93
+ const list = parseFileRecs(value);
94
+ const [open, setOpen] = useState(null);
95
+ if (!list.length)
96
+ return _jsx(Dash, {});
97
+ const names = list.map((r) => r.name);
98
+ const single = list.length === 1;
99
+ return (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex flex-wrap items-center gap-2", children: single ? (_jsx("button", { type: "button", onClick: () => setOpen(0), className: "relative inline-block rounded-full cursor-zoom-in", children: _jsx("img", { src: mediaUrl(list[0].name), alt: "", loading: "lazy", className: "max-h-96 rounded-full border border-border object-cover hover:opacity-90 transition-opacity" }) }, `${list[0].name}-0`)) : (list.map((rec, i) => (_jsx("button", { type: "button", onClick: () => setOpen(i), className: "relative h-24 w-24 rounded-full overflow-hidden border border-border hover:border-accent transition-colors cursor-zoom-in", children: _jsx("img", { src: mediaUrl(rec.name), alt: "", loading: "lazy", className: "h-full w-full object-cover" }) }, `${rec.name}-${i}`)))) }), open !== null && _jsx(Lightbox, { images: names, index: open, onClose: () => setOpen(null), onIndex: setOpen })] }));
100
+ }
101
+ export function AvatarInline({ value }) {
102
+ const rec = parseFileRecs(value)[0];
103
+ if (!rec)
104
+ return _jsx("span", { className: "text-muted", children: "\u2014" });
105
+ return (_jsx("img", { src: mediaUrl(rec.name), alt: "", style: { height: 28, width: 28 }, className: "rounded-full object-cover" }));
106
+ }
107
+ export function CoverView({ value, banner }) {
108
+ const list = parseFileRecs(value);
109
+ const [idx, setIdx] = useState(0);
110
+ const [open, setOpen] = useState(false);
111
+ if (!list.length)
112
+ return _jsx(Dash, {});
113
+ const names = list.map((r) => r.name);
114
+ const many = list.length > 1;
115
+ const cur = Math.min(idx, list.length - 1);
116
+ const prev = () => setIdx((cur - 1 + list.length) % list.length);
117
+ const next = () => setIdx((cur + 1) % list.length);
118
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: banner
119
+ ? 'relative w-full overflow-hidden rounded-none'
120
+ : 'relative w-full overflow-hidden rounded-xl border border-border', children: [_jsx("button", { type: "button", onClick: (e) => {
121
+ e.stopPropagation();
122
+ setOpen(true);
123
+ }, className: banner ? 'block h-48 w-full cursor-zoom-in md:h-64' : 'block aspect-video max-h-96 w-full cursor-zoom-in', children: _jsx("img", { src: mediaUrl(names[cur]), alt: "", loading: "lazy", className: "h-full w-full object-cover" }) }), many && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "lightbox-btn absolute left-2 top-1/2 -translate-y-1/2", onClick: (e) => {
124
+ e.stopPropagation();
125
+ prev();
126
+ }, children: _jsx(ChevronLeft, { size: 22 }) }), _jsx("button", { type: "button", className: "lightbox-btn absolute right-2 top-1/2 -translate-y-1/2", onClick: (e) => {
127
+ e.stopPropagation();
128
+ next();
129
+ }, children: _jsx(ChevronRight, { size: 22 }) }), _jsxs("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-black/50 px-2 py-0.5 text-xs text-white tabular-nums", children: [cur + 1, " / ", list.length] })] }))] }), open && _jsx(Lightbox, { images: names, index: cur, onClose: () => setOpen(false), onIndex: setIdx })] }));
130
+ }
131
+ function UploadPlaceholder({ accept, busy, error, progress, onPick, onRetry, canRetry, className, round = false, }) {
132
+ const { t } = useTranslation();
133
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("label", { className: `flex cursor-pointer flex-col items-center justify-center gap-2 ${round ? 'rounded-full' : 'rounded-xl'} border-2 border-dashed border-border text-muted transition-colors hover:border-accent hover:text-accent ${busy ? 'pointer-events-none opacity-60' : ''} ${className ?? ''}`, children: [busy ? _jsx(Loader2, { size: 24, className: "animate-spin" }) : _jsx(Upload, { size: 24 }), _jsx("span", { className: "text-sm font-medium", children: t('actions.upload') }), _jsx("input", { type: "file", accept: accept, className: "hidden", disabled: busy, onChange: (e) => onPick(e.target.files?.[0] ?? null) })] }), busy && progress != null && _jsx(UploadProgress, { progress: progress }), error && _jsx(UploadError, { error: error, onRetry: onRetry, canRetry: canRetry })] }));
134
+ }
135
+ function UploadRow({ accept, busy, error, progress, onPick, onRetry, canRetry, className, }) {
136
+ const { t } = useTranslation();
137
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("label", { className: `flex h-9 cursor-pointer items-center gap-2 rounded-lg border border-dashed border-border px-3 text-sm text-muted transition-colors hover:border-accent hover:text-accent ${busy ? 'pointer-events-none opacity-60' : ''} ${className ?? 'w-fit'}`, children: [busy ? _jsx(Loader2, { size: 15, className: "animate-spin" }) : _jsx(Upload, { size: 15 }), _jsx("span", { className: "font-medium", children: t('actions.upload') }), _jsx("input", { type: "file", accept: accept, className: "hidden", disabled: busy, onChange: (e) => onPick(e.target.files?.[0] ?? null) })] }), busy && progress != null && _jsx(UploadProgress, { progress: progress }), error && _jsx(UploadError, { error: error, onRetry: onRetry, canRetry: canRetry })] }));
138
+ }
139
+ function galleryEdit({ t, accept, busy, upload, list, save, setOpen, single, }) {
140
+ return {
141
+ accept,
142
+ busy,
143
+ replaceLabel: t('actions.replace'),
144
+ deleteLabel: t(single ? 'actions.clear' : 'actions.delete'),
145
+ onReplace: (i, f) => void (async () => {
146
+ const r = await upload(f);
147
+ if (r)
148
+ save(list.map((x, j) => (j === i ? r : x)));
149
+ })(),
150
+ onDelete: (i) => void (async () => {
151
+ if (!(await confirmDeletePhoto(t)))
152
+ return;
153
+ const next = list.filter((_, j) => j !== i);
154
+ save(next);
155
+ setOpen(next.length ? Math.min(i, next.length - 1) : null);
156
+ })(),
157
+ };
158
+ }
159
+ export function ImageWidget({ value, onChange, accept = 'image/*', poster, width, }) {
160
+ const { t } = useTranslation();
161
+ const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
162
+ const [open, setOpen] = useState(null);
163
+ const rec = parseFileRecs(value)[0];
164
+ async function pick(file) {
165
+ if (!file)
166
+ return;
167
+ const r = await upload(file);
168
+ if (r)
169
+ onChange(r);
170
+ }
171
+ async function retryPick() {
172
+ const r = await retry();
173
+ if (r)
174
+ onChange(r);
175
+ }
176
+ if (rec) {
177
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("img", { src: mediaUrl(rec.name), alt: "", loading: "lazy", onClick: () => setOpen(0), ...(width != null ? { style: { '--img-w': `${width}px` } } : {}), className: width != null
178
+ ? `${poster ? 'aspect-[2/3]' : 'aspect-square'} w-full sm:w-(--img-w) self-start object-cover rounded-xl border border-border cursor-zoom-in hover:opacity-90 transition-opacity`
179
+ : poster
180
+ ? 'h-72 w-48 sm:h-96 sm:w-64 self-start object-cover rounded-xl border border-border cursor-zoom-in hover:opacity-90 transition-opacity'
181
+ : 'max-h-32 sm:max-h-40 self-start rounded-xl border border-border cursor-zoom-in hover:opacity-90 transition-opacity' }), error && _jsx(UploadError, { error: error, onRetry: () => void retryPick(), canRetry: canRetry }), open !== null && (_jsx(Lightbox, { images: [rec.name], index: 0, onClose: () => setOpen(null), onIndex: () => { }, edit: galleryEdit({
182
+ t,
183
+ accept,
184
+ busy,
185
+ upload,
186
+ list: [rec],
187
+ save: (next) => onChange(next[0] ?? ''),
188
+ setOpen,
189
+ single: true,
190
+ }) }))] }));
191
+ }
192
+ return (_jsx(UploadRow, { accept: accept, busy: busy, error: error, progress: progress, onPick: pick, onRetry: () => void retryPick(), canRetry: canRetry }));
193
+ }
194
+ export function ImagesWidget({ value, onChange, accept = 'image/*', poster, }) {
195
+ const { t } = useTranslation();
196
+ const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
197
+ const [open, setOpen] = useState(null);
198
+ const list = parseFileRecs(value);
199
+ const tile = poster ? 'h-72 w-48 sm:h-96 sm:w-64' : 'h-24 w-24';
200
+ function save(next) {
201
+ onChange(next.length ? next : '');
202
+ }
203
+ async function add(file) {
204
+ if (!file)
205
+ return;
206
+ const r = await upload(file);
207
+ if (r)
208
+ save([...list, r]);
209
+ }
210
+ async function retryAdd() {
211
+ const r = await retry();
212
+ if (r)
213
+ save([...list, r]);
214
+ }
215
+ const names = list.map((r) => r.name);
216
+ if (!list.length) {
217
+ return (_jsx(UploadRow, { accept: accept, busy: busy, error: error, progress: progress, onPick: add, onRetry: () => void retryAdd(), canRetry: canRetry }));
218
+ }
219
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-wrap gap-2", children: [list.map((rec, i) => (_jsx("button", { type: "button", onClick: () => setOpen(i), className: `relative ${tile} overflow-hidden rounded-xl border border-border cursor-zoom-in hover:border-accent transition-colors`, children: _jsx(Thumb, { name: rec.name }) }, `${rec.name}-${i}`))), _jsx(UploadPlaceholder, { accept: accept, busy: busy, error: error, progress: progress, onPick: add, onRetry: () => void retryAdd(), canRetry: canRetry, className: tile })] }), open !== null && (_jsx(Lightbox, { images: names, index: open, onClose: () => setOpen(null), onIndex: setOpen, edit: galleryEdit({ t, accept, busy, upload, list, save, setOpen }) }))] }));
220
+ }
221
+ export function AvatarWidget({ value, onChange, accept = 'image/*', multiple = false, }) {
222
+ const { t } = useTranslation();
223
+ const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
224
+ const [open, setOpen] = useState(null);
225
+ const list = parseFileRecs(value);
226
+ const save = (next) => onChange(next.length ? next : '');
227
+ const names = list.map((r) => r.name);
228
+ if (multiple) {
229
+ async function append(file) {
230
+ if (!file)
231
+ return;
232
+ const r = await upload(file);
233
+ if (r)
234
+ save([...list, r]);
235
+ }
236
+ async function retryAppend() {
237
+ const r = await retry();
238
+ if (r)
239
+ save([...list, r]);
240
+ }
241
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [list.map((rec, i) => (_jsx("button", { type: "button", onClick: () => setOpen(i), className: "relative h-24 w-24 shrink-0 overflow-hidden rounded-full border border-border cursor-zoom-in hover:border-accent transition-colors", children: _jsx("img", { src: mediaUrl(rec.name), alt: "", className: "h-full w-full object-cover" }) }, `${rec.name}-${i}`))), _jsx(UploadPlaceholder, { accept: accept, busy: busy, error: error, progress: progress, onPick: append, onRetry: () => void retryAppend(), canRetry: canRetry, className: "h-24 w-24", round: true })] }), open !== null && (_jsx(Lightbox, { images: names, index: open, onClose: () => setOpen(null), onIndex: setOpen, edit: galleryEdit({ t, accept, busy, upload, list, save, setOpen }) }))] }));
242
+ }
243
+ const rec = list[0];
244
+ async function pick(file) {
245
+ if (!file)
246
+ return;
247
+ const r = await upload(file);
248
+ if (r)
249
+ onChange(r);
250
+ }
251
+ async function retryPick() {
252
+ const r = await retry();
253
+ if (r)
254
+ onChange(r);
255
+ }
256
+ if (rec) {
257
+ return (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx("button", { type: "button", onClick: () => setOpen(0), className: "inline-block w-fit rounded-full cursor-zoom-in", children: _jsx("img", { src: mediaUrl(rec.name), alt: "", className: "h-28 w-28 sm:h-40 sm:w-40 rounded-full border border-border object-cover hover:opacity-90 transition-opacity" }) }), error && _jsx(UploadError, { error: error, onRetry: () => void retryPick(), canRetry: canRetry }), open !== null && (_jsx(Lightbox, { images: [rec.name], index: 0, onClose: () => setOpen(null), onIndex: () => { }, edit: galleryEdit({
258
+ t,
259
+ accept,
260
+ busy,
261
+ upload,
262
+ list: [rec],
263
+ save: (next) => onChange(next[0] ?? ''),
264
+ setOpen,
265
+ single: true,
266
+ }) }))] }));
267
+ }
268
+ return (_jsx(UploadPlaceholder, { accept: accept, busy: busy, error: error, progress: progress, onPick: pick, onRetry: () => void retryPick(), canRetry: canRetry, className: "h-28 w-28 sm:h-40 sm:w-40", round: true }));
269
+ }
270
+ export function CoverWidget({ value, onChange, accept = 'image/*', multiple = false, }) {
271
+ const { t } = useTranslation();
272
+ const { busy, error, progress, upload, retry, canRetry } = useFileUpload();
273
+ const [idx, setIdx] = useState(0);
274
+ const [open, setOpen] = useState(false);
275
+ const list = parseFileRecs(value);
276
+ const save = (next) => onChange(next.length ? next : '');
277
+ async function add(file) {
278
+ if (!file)
279
+ return;
280
+ const r = await upload(file);
281
+ if (r)
282
+ save(multiple ? [...list, r] : [r]);
283
+ }
284
+ async function retryAdd() {
285
+ const r = await retry();
286
+ if (r)
287
+ save(multiple ? [...list, r] : [r]);
288
+ }
289
+ if (!list.length) {
290
+ return (_jsx(UploadRow, { accept: accept, busy: busy, error: error, progress: progress, onPick: add, onRetry: () => void retryAdd(), canRetry: canRetry, className: "w-full" }));
291
+ }
292
+ const total = multiple ? list.length + 1 : list.length;
293
+ const many = total > 1;
294
+ const cur = Math.min(idx, total - 1);
295
+ const isUpload = multiple && cur === list.length;
296
+ const prev = () => setIdx((cur - 1 + total) % total);
297
+ const next = () => setIdx((cur + 1) % total);
298
+ return (_jsxs("div", { className: "group/cov relative w-full overflow-hidden rounded-xl border border-border", children: [isUpload ? (_jsxs("div", { className: "flex aspect-video max-h-96 w-full flex-col items-center justify-center gap-1 bg-elevated px-20", children: [_jsxs("label", { className: `flex w-full flex-1 cursor-pointer flex-col items-center justify-center gap-3 text-muted transition-colors hover:text-accent ${busy ? 'pointer-events-none opacity-60' : ''}`, children: [busy ? _jsx(Loader2, { size: 40, className: "animate-spin" }) : _jsx(Upload, { size: 40 }), _jsx("span", { className: "text-base font-medium", children: t('actions.upload') }), _jsx("input", { type: "file", accept: accept, className: "hidden", disabled: busy, onChange: (e) => add(e.target.files?.[0] ?? null) })] }), error && _jsx(UploadError, { error: error, onRetry: () => void retryAdd(), canRetry: canRetry })] })) : (_jsx("button", { type: "button", onClick: () => setOpen(true), className: "block w-full cursor-zoom-in", children: _jsx("img", { src: mediaUrl(list[cur].name), alt: "", className: "aspect-video max-h-96 w-full object-cover" }) })), many && (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "absolute left-2 top-1/2 z-10 flex h-9 w-9 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white backdrop-blur-sm transition-colors hover:bg-black/70", onClick: prev, children: _jsx(ChevronLeft, { size: 22 }) }), _jsx("button", { type: "button", className: "absolute right-2 top-1/2 z-10 flex h-9 w-9 -translate-y-1/2 items-center justify-center rounded-full bg-black/50 text-white backdrop-blur-sm transition-colors hover:bg-black/70", onClick: next, children: _jsx(ChevronRight, { size: 22 }) }), !isUpload && (_jsxs("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 rounded-full bg-black/50 px-2 py-0.5 text-xs text-white tabular-nums", children: [cur + 1, " / ", list.length] }))] })), open && !isUpload && (_jsx(Lightbox, { images: list.map((r) => r.name), index: cur, onClose: () => setOpen(false), onIndex: setIdx, edit: galleryEdit({
299
+ t,
300
+ accept,
301
+ busy,
302
+ upload,
303
+ list,
304
+ save,
305
+ setOpen: (i) => {
306
+ if (i === null)
307
+ setOpen(false);
308
+ else
309
+ setIdx(i);
310
+ },
311
+ single: !multiple,
312
+ }) }))] }));
313
+ }
314
+ export function ImageInline({ value, height }) {
315
+ const rec = parseFileRecs(value)[0];
316
+ if (!rec)
317
+ return _jsx("span", { className: "text-muted", children: "\u2014" });
318
+ const h = height ?? 32;
319
+ if (isVideoName(rec.name))
320
+ return (_jsx("video", { src: mediaUrl(rec.name), muted: true, playsInline: true, preload: "metadata", style: { height: h }, className: "object-contain rounded" }));
321
+ return _jsx("img", { src: mediaUrl(rec.name), alt: "", style: { height: h }, className: "object-contain rounded" });
322
+ }
@@ -0,0 +1,7 @@
1
+ import type { FieldMeta } from '@coffer-org/sdk/fields';
2
+ export type LiveKind = 'popover' | 'heavy';
3
+ export declare const LIVE_ENABLED: ReadonlySet<LiveKind>;
4
+ export declare const NON_FIELD_KINDS: ReadonlySet<string>;
5
+ export declare const BY_KIND: Readonly<Record<string, LiveKind>>;
6
+ export declare function defaultLiveKind(field: Pick<FieldMeta, 'kind' | 'prim' | 'hints'>): LiveKind;
7
+ export declare function liveKind(field: Pick<FieldMeta, 'kind' | 'prim' | 'hints'>): LiveKind | null;