makersclaw 0.2.0 → 0.3.1

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 (475) hide show
  1. package/THIRD-PARTY-NOTICES.md +180 -0
  2. package/package.json +3 -2
  3. package/r/annotate.json +62 -0
  4. package/r/approval-request.json +52 -0
  5. package/r/audio-player.json +74 -0
  6. package/r/auth-form.json +55 -0
  7. package/r/board.json +10 -6
  8. package/r/calendar-view.json +105 -0
  9. package/r/cart.json +62 -0
  10. package/r/chart.json +83 -0
  11. package/r/chat.json +95 -0
  12. package/r/code-editor.json +94 -0
  13. package/r/comment-box.json +64 -0
  14. package/r/contact-section.json +53 -0
  15. package/r/core.json +205 -8
  16. package/r/csv-import.json +63 -0
  17. package/r/data-table.json +69 -0
  18. package/r/diagram.json +74 -0
  19. package/r/doc-editor.json +82 -0
  20. package/r/doc-page.json +61 -0
  21. package/r/email-render.json +73 -0
  22. package/r/faq.json +50 -0
  23. package/r/file-upload.json +54 -0
  24. package/r/flow-canvas.json +93 -0
  25. package/r/footer.json +52 -0
  26. package/r/gantt.json +70 -0
  27. package/r/job-panel.json +12 -7
  28. package/r/json-editor.json +66 -0
  29. package/r/map.json +89 -0
  30. package/r/menubar.json +52 -0
  31. package/r/mind-map.json +67 -0
  32. package/r/navigation-menu.json +52 -0
  33. package/r/order-detail.json +54 -0
  34. package/r/org-chart.json +66 -0
  35. package/r/page-builder.json +105 -0
  36. package/r/payment-methods.json +51 -0
  37. package/r/pdf-document.json +79 -0
  38. package/r/pdf-viewer.json +88 -0
  39. package/r/product-detail.json +76 -0
  40. package/r/product-grid.json +65 -0
  41. package/r/record-form.json +67 -0
  42. package/r/record-notes.json +55 -0
  43. package/r/registry.json +3411 -29
  44. package/r/search-dialog.json +51 -0
  45. package/r/settings-list.json +66 -0
  46. package/r/setup-checklist.json +51 -0
  47. package/r/share-dialog.json +52 -0
  48. package/r/sidebar.json +69 -0
  49. package/r/signature-pad.json +72 -0
  50. package/r/sketch-canvas.json +99 -0
  51. package/r/spreadsheet.json +90 -0
  52. package/r/stat-cards.json +66 -0
  53. package/r/task-list.json +63 -0
  54. package/r/task-model.json +98 -0
  55. package/r/testimonials.json +52 -0
  56. package/r/transaction-list.json +53 -0
  57. package/r/whiteboard.json +66 -0
  58. package/registry/blocks/annotate/README.md +179 -0
  59. package/registry/blocks/annotate/annotate-data.js +250 -0
  60. package/registry/blocks/annotate/annotate.jsx +646 -0
  61. package/registry/blocks/annotate/block.json +10 -0
  62. package/registry/blocks/annotate/demo.jsx +135 -0
  63. package/registry/blocks/annotate/index.js +15 -0
  64. package/registry/blocks/approval-request/README.md +116 -0
  65. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  66. package/registry/blocks/approval-request/block.json +10 -0
  67. package/registry/blocks/approval-request/demo.jsx +127 -0
  68. package/registry/blocks/approval-request/index.js +4 -0
  69. package/registry/blocks/audio-player/README.md +167 -0
  70. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  71. package/registry/blocks/audio-player/block.json +14 -0
  72. package/registry/blocks/audio-player/colors.js +109 -0
  73. package/registry/blocks/audio-player/demo.jsx +209 -0
  74. package/registry/blocks/audio-player/index.js +6 -0
  75. package/registry/blocks/audio-player/time.js +67 -0
  76. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  77. package/registry/blocks/auth-form/README.md +126 -0
  78. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  79. package/registry/blocks/auth-form/block.json +10 -0
  80. package/registry/blocks/auth-form/demo.jsx +131 -0
  81. package/registry/blocks/auth-form/index.js +3 -0
  82. package/registry/blocks/board/README.md +55 -5
  83. package/registry/blocks/board/block.json +2 -2
  84. package/registry/blocks/board/board.jsx +230 -86
  85. package/registry/blocks/board/demo.jsx +103 -24
  86. package/registry/blocks/calendar-view/README.md +180 -0
  87. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  88. package/registry/blocks/calendar-view/block.json +76 -0
  89. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  90. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  91. package/registry/blocks/calendar-view/demo.jsx +195 -0
  92. package/registry/blocks/calendar-view/draft.jsx +49 -0
  93. package/registry/blocks/calendar-view/index.js +39 -0
  94. package/registry/blocks/calendar-view/month.jsx +346 -0
  95. package/registry/blocks/calendar-view/paint.js +69 -0
  96. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  97. package/registry/blocks/calendar-view/tray.jsx +87 -0
  98. package/registry/blocks/cart/README.md +146 -0
  99. package/registry/blocks/cart/block.json +10 -0
  100. package/registry/blocks/cart/cart.jsx +597 -0
  101. package/registry/blocks/cart/demo.jsx +98 -0
  102. package/registry/blocks/cart/index.js +4 -0
  103. package/registry/blocks/cart/money.js +28 -0
  104. package/registry/blocks/chart/README.md +316 -0
  105. package/registry/blocks/chart/block.json +10 -0
  106. package/registry/blocks/chart/cartesian.jsx +642 -0
  107. package/registry/blocks/chart/core.jsx +262 -0
  108. package/registry/blocks/chart/demo.jsx +357 -0
  109. package/registry/blocks/chart/heatmap.jsx +336 -0
  110. package/registry/blocks/chart/index.js +8 -0
  111. package/registry/blocks/chart/pie.jsx +245 -0
  112. package/registry/blocks/chart/sparkline.jsx +88 -0
  113. package/registry/blocks/chat/README.md +273 -0
  114. package/registry/blocks/chat/attachment.jsx +95 -0
  115. package/registry/blocks/chat/block.json +10 -0
  116. package/registry/blocks/chat/chat.jsx +132 -0
  117. package/registry/blocks/chat/composer.jsx +364 -0
  118. package/registry/blocks/chat/demo.jsx +578 -0
  119. package/registry/blocks/chat/header.jsx +34 -0
  120. package/registry/blocks/chat/index.js +9 -0
  121. package/registry/blocks/chat/message.jsx +450 -0
  122. package/registry/blocks/chat/scroller.jsx +247 -0
  123. package/registry/blocks/chat/text.jsx +104 -0
  124. package/registry/blocks/code-editor/README.md +210 -0
  125. package/registry/blocks/code-editor/block.json +22 -0
  126. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  127. package/registry/blocks/code-editor/codemirror.js +38 -0
  128. package/registry/blocks/code-editor/demo.jsx +205 -0
  129. package/registry/blocks/code-editor/index.js +8 -0
  130. package/registry/blocks/code-editor/json.js +317 -0
  131. package/registry/blocks/code-editor/sql.js +136 -0
  132. package/registry/blocks/code-editor/text.js +101 -0
  133. package/registry/blocks/code-editor/theme.js +186 -0
  134. package/registry/blocks/comment-box/README.md +123 -0
  135. package/registry/blocks/comment-box/block.json +10 -0
  136. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  137. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  138. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  139. package/registry/blocks/comment-box/demo.jsx +141 -0
  140. package/registry/blocks/comment-box/index.js +8 -0
  141. package/registry/blocks/contact-section/README.md +152 -0
  142. package/registry/blocks/contact-section/block.json +11 -0
  143. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  144. package/registry/blocks/contact-section/demo.jsx +93 -0
  145. package/registry/blocks/contact-section/index.js +4 -0
  146. package/registry/blocks/csv-import/README.md +133 -0
  147. package/registry/blocks/csv-import/block.json +13 -0
  148. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  149. package/registry/blocks/csv-import/demo.jsx +106 -0
  150. package/registry/blocks/csv-import/import.js +200 -0
  151. package/registry/blocks/csv-import/index.js +7 -0
  152. package/registry/blocks/data-table/README.md +386 -0
  153. package/registry/blocks/data-table/block.json +10 -0
  154. package/registry/blocks/data-table/data-table.jsx +1164 -0
  155. package/registry/blocks/data-table/demo.jsx +588 -0
  156. package/registry/blocks/data-table/index.js +5 -0
  157. package/registry/blocks/data-table/model.js +169 -0
  158. package/registry/blocks/diagram/README.md +108 -0
  159. package/registry/blocks/diagram/block.json +13 -0
  160. package/registry/blocks/diagram/demo.jsx +113 -0
  161. package/registry/blocks/diagram/diagram.jsx +481 -0
  162. package/registry/blocks/diagram/index.js +7 -0
  163. package/registry/blocks/diagram/mermaid.js +52 -0
  164. package/registry/blocks/diagram/palette.js +313 -0
  165. package/registry/blocks/diagram/source.js +98 -0
  166. package/registry/blocks/doc-editor/README.md +215 -0
  167. package/registry/blocks/doc-editor/block.json +15 -0
  168. package/registry/blocks/doc-editor/demo.jsx +225 -0
  169. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  170. package/registry/blocks/doc-editor/doc.js +309 -0
  171. package/registry/blocks/doc-editor/index.js +7 -0
  172. package/registry/blocks/doc-editor/schema.jsx +56 -0
  173. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  174. package/registry/blocks/doc-editor/theme.js +82 -0
  175. package/registry/blocks/doc-page/README.md +119 -0
  176. package/registry/blocks/doc-page/autosave.js +104 -0
  177. package/registry/blocks/doc-page/block.json +10 -0
  178. package/registry/blocks/doc-page/demo.jsx +137 -0
  179. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  180. package/registry/blocks/doc-page/index.js +4 -0
  181. package/registry/blocks/email-render/README.md +151 -0
  182. package/registry/blocks/email-render/block.json +14 -0
  183. package/registry/blocks/email-render/demo.jsx +153 -0
  184. package/registry/blocks/email-render/doc-email.jsx +210 -0
  185. package/registry/blocks/email-render/email.jsx +221 -0
  186. package/registry/blocks/email-render/index.js +10 -0
  187. package/registry/blocks/email-render/render.js +50 -0
  188. package/registry/blocks/email-render/theme.js +215 -0
  189. package/registry/blocks/faq/README.md +101 -0
  190. package/registry/blocks/faq/block.json +11 -0
  191. package/registry/blocks/faq/demo.jsx +89 -0
  192. package/registry/blocks/faq/faq.jsx +220 -0
  193. package/registry/blocks/faq/index.js +4 -0
  194. package/registry/blocks/file-upload/README.md +138 -0
  195. package/registry/blocks/file-upload/block.json +10 -0
  196. package/registry/blocks/file-upload/demo.jsx +204 -0
  197. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  198. package/registry/blocks/file-upload/index.js +4 -0
  199. package/registry/blocks/flow-canvas/README.md +420 -0
  200. package/registry/blocks/flow-canvas/block.json +13 -0
  201. package/registry/blocks/flow-canvas/board.js +819 -0
  202. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  203. package/registry/blocks/flow-canvas/context.jsx +35 -0
  204. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  205. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  206. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  207. package/registry/blocks/flow-canvas/index.js +41 -0
  208. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  209. package/registry/blocks/flow-canvas/theme.js +174 -0
  210. package/registry/blocks/footer/README.md +133 -0
  211. package/registry/blocks/footer/block.json +11 -0
  212. package/registry/blocks/footer/demo.jsx +119 -0
  213. package/registry/blocks/footer/footer.jsx +406 -0
  214. package/registry/blocks/footer/index.js +4 -0
  215. package/registry/blocks/gantt/README.md +220 -0
  216. package/registry/blocks/gantt/block.json +10 -0
  217. package/registry/blocks/gantt/demo.jsx +148 -0
  218. package/registry/blocks/gantt/gantt.jsx +923 -0
  219. package/registry/blocks/gantt/index.js +7 -0
  220. package/registry/blocks/gantt/rows.jsx +468 -0
  221. package/registry/blocks/gantt/schedule.js +278 -0
  222. package/registry/blocks/job-panel/README.md +80 -0
  223. package/registry/blocks/job-panel/block.json +3 -3
  224. package/registry/blocks/job-panel/demo.jsx +230 -3
  225. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  226. package/registry/blocks/json-editor/README.md +133 -0
  227. package/registry/blocks/json-editor/block.json +10 -0
  228. package/registry/blocks/json-editor/demo.jsx +92 -0
  229. package/registry/blocks/json-editor/index.js +7 -0
  230. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  231. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  232. package/registry/blocks/json-editor/tree.js +225 -0
  233. package/registry/blocks/map/README.md +221 -0
  234. package/registry/blocks/map/block.json +14 -0
  235. package/registry/blocks/map/demo.jsx +201 -0
  236. package/registry/blocks/map/geo.js +198 -0
  237. package/registry/blocks/map/index.js +8 -0
  238. package/registry/blocks/map/list.jsx +128 -0
  239. package/registry/blocks/map/map-view.jsx +715 -0
  240. package/registry/blocks/map/maplibre.js +30 -0
  241. package/registry/blocks/map/marks.jsx +146 -0
  242. package/registry/blocks/map/theme.js +142 -0
  243. package/registry/blocks/menubar/README.md +109 -0
  244. package/registry/blocks/menubar/block.json +10 -0
  245. package/registry/blocks/menubar/demo.jsx +128 -0
  246. package/registry/blocks/menubar/index.js +4 -0
  247. package/registry/blocks/menubar/menubar.jsx +199 -0
  248. package/registry/blocks/mind-map/README.md +206 -0
  249. package/registry/blocks/mind-map/block.json +10 -0
  250. package/registry/blocks/mind-map/demo.jsx +103 -0
  251. package/registry/blocks/mind-map/index.js +31 -0
  252. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  253. package/registry/blocks/mind-map/topic.jsx +143 -0
  254. package/registry/blocks/mind-map/tree.js +645 -0
  255. package/registry/blocks/navigation-menu/README.md +124 -0
  256. package/registry/blocks/navigation-menu/block.json +10 -0
  257. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  258. package/registry/blocks/navigation-menu/index.js +4 -0
  259. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  260. package/registry/blocks/order-detail/README.md +109 -0
  261. package/registry/blocks/order-detail/block.json +10 -0
  262. package/registry/blocks/order-detail/demo.jsx +88 -0
  263. package/registry/blocks/order-detail/index.js +4 -0
  264. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  265. package/registry/blocks/org-chart/README.md +145 -0
  266. package/registry/blocks/org-chart/block.json +10 -0
  267. package/registry/blocks/org-chart/demo.jsx +94 -0
  268. package/registry/blocks/org-chart/index.js +7 -0
  269. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  270. package/registry/blocks/org-chart/org.js +245 -0
  271. package/registry/blocks/org-chart/person.jsx +96 -0
  272. package/registry/blocks/page-builder/README.md +257 -0
  273. package/registry/blocks/page-builder/block.json +14 -0
  274. package/registry/blocks/page-builder/demo.jsx +145 -0
  275. package/registry/blocks/page-builder/grapes.js +18 -0
  276. package/registry/blocks/page-builder/index.js +11 -0
  277. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  278. package/registry/blocks/page-builder/page-view.jsx +33 -0
  279. package/registry/blocks/page-builder/page.js +118 -0
  280. package/registry/blocks/page-builder/panels.jsx +588 -0
  281. package/registry/blocks/page-builder/sections.js +209 -0
  282. package/registry/blocks/page-builder/theme.js +87 -0
  283. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  284. package/registry/blocks/page-builder/tokens.js +329 -0
  285. package/registry/blocks/payment-methods/README.md +104 -0
  286. package/registry/blocks/payment-methods/block.json +10 -0
  287. package/registry/blocks/payment-methods/demo.jsx +71 -0
  288. package/registry/blocks/payment-methods/index.js +4 -0
  289. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  290. package/registry/blocks/pdf-document/README.md +213 -0
  291. package/registry/blocks/pdf-document/block.json +13 -0
  292. package/registry/blocks/pdf-document/demo.jsx +136 -0
  293. package/registry/blocks/pdf-document/index.js +33 -0
  294. package/registry/blocks/pdf-document/money.js +83 -0
  295. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  296. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  297. package/registry/blocks/pdf-document/quote.jsx +179 -0
  298. package/registry/blocks/pdf-document/theme.js +193 -0
  299. package/registry/blocks/pdf-viewer/README.md +191 -0
  300. package/registry/blocks/pdf-viewer/block.json +14 -0
  301. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  302. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  303. package/registry/blocks/pdf-viewer/index.js +8 -0
  304. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  305. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  306. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  307. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  308. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  309. package/registry/blocks/product-detail/README.md +217 -0
  310. package/registry/blocks/product-detail/block.json +10 -0
  311. package/registry/blocks/product-detail/demo.jsx +233 -0
  312. package/registry/blocks/product-detail/index.js +7 -0
  313. package/registry/blocks/product-detail/price.jsx +115 -0
  314. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  315. package/registry/blocks/product-detail/product-media.jsx +102 -0
  316. package/registry/blocks/product-detail/product-options.jsx +144 -0
  317. package/registry/blocks/product-grid/README.md +162 -0
  318. package/registry/blocks/product-grid/block.json +10 -0
  319. package/registry/blocks/product-grid/demo.jsx +255 -0
  320. package/registry/blocks/product-grid/index.js +5 -0
  321. package/registry/blocks/product-grid/product-card.jsx +232 -0
  322. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  323. package/registry/blocks/record-form/README.md +196 -0
  324. package/registry/blocks/record-form/block.json +10 -0
  325. package/registry/blocks/record-form/demo.jsx +525 -0
  326. package/registry/blocks/record-form/index.js +4 -0
  327. package/registry/blocks/record-form/record-fields.jsx +457 -0
  328. package/registry/blocks/record-form/record-form.jsx +415 -0
  329. package/registry/blocks/record-form/rules.js +81 -0
  330. package/registry/blocks/record-notes/README.md +139 -0
  331. package/registry/blocks/record-notes/block.json +10 -0
  332. package/registry/blocks/record-notes/demo.jsx +179 -0
  333. package/registry/blocks/record-notes/index.js +4 -0
  334. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  335. package/registry/blocks/search-dialog/README.md +108 -0
  336. package/registry/blocks/search-dialog/block.json +10 -0
  337. package/registry/blocks/search-dialog/demo.jsx +116 -0
  338. package/registry/blocks/search-dialog/index.js +3 -0
  339. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  340. package/registry/blocks/settings-list/README.md +171 -0
  341. package/registry/blocks/settings-list/block.json +10 -0
  342. package/registry/blocks/settings-list/demo.jsx +333 -0
  343. package/registry/blocks/settings-list/index.js +4 -0
  344. package/registry/blocks/settings-list/row-state.jsx +93 -0
  345. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  346. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  347. package/registry/blocks/setup-checklist/README.md +142 -0
  348. package/registry/blocks/setup-checklist/block.json +10 -0
  349. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  350. package/registry/blocks/setup-checklist/index.js +4 -0
  351. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  352. package/registry/blocks/share-dialog/README.md +113 -0
  353. package/registry/blocks/share-dialog/block.json +10 -0
  354. package/registry/blocks/share-dialog/demo.jsx +140 -0
  355. package/registry/blocks/share-dialog/index.js +3 -0
  356. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  357. package/registry/blocks/sidebar/README.md +190 -0
  358. package/registry/blocks/sidebar/block.json +43 -0
  359. package/registry/blocks/sidebar/demo.jsx +577 -0
  360. package/registry/blocks/sidebar/index.js +4 -0
  361. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  362. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  363. package/registry/blocks/signature-pad/README.md +137 -0
  364. package/registry/blocks/signature-pad/block.json +13 -0
  365. package/registry/blocks/signature-pad/colors.js +115 -0
  366. package/registry/blocks/signature-pad/demo.jsx +94 -0
  367. package/registry/blocks/signature-pad/index.js +6 -0
  368. package/registry/blocks/signature-pad/pad.js +7 -0
  369. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  370. package/registry/blocks/signature-pad/signature.js +68 -0
  371. package/registry/blocks/sketch-canvas/README.md +280 -0
  372. package/registry/blocks/sketch-canvas/block.json +13 -0
  373. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  374. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  375. package/registry/blocks/sketch-canvas/index.js +9 -0
  376. package/registry/blocks/sketch-canvas/palette.js +226 -0
  377. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  378. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  379. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  380. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  381. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  382. package/registry/blocks/sketch-canvas/theme.js +131 -0
  383. package/registry/blocks/spreadsheet/README.md +307 -0
  384. package/registry/blocks/spreadsheet/block.json +13 -0
  385. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  386. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  387. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  388. package/registry/blocks/spreadsheet/grid.js +387 -0
  389. package/registry/blocks/spreadsheet/index.js +34 -0
  390. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  391. package/registry/blocks/spreadsheet/types.js +473 -0
  392. package/registry/blocks/stat-cards/README.md +173 -0
  393. package/registry/blocks/stat-cards/block.json +10 -0
  394. package/registry/blocks/stat-cards/demo.jsx +257 -0
  395. package/registry/blocks/stat-cards/index.js +4 -0
  396. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  397. package/registry/blocks/stat-cards/usage.jsx +243 -0
  398. package/registry/blocks/task-list/README.md +176 -0
  399. package/registry/blocks/task-list/block.json +10 -0
  400. package/registry/blocks/task-list/demo.jsx +134 -0
  401. package/registry/blocks/task-list/index.js +5 -0
  402. package/registry/blocks/task-list/task-list.jsx +907 -0
  403. package/registry/blocks/task-model/README.md +455 -0
  404. package/registry/blocks/task-model/block.json +167 -0
  405. package/registry/blocks/task-model/changes.js +89 -0
  406. package/registry/blocks/task-model/dates.js +371 -0
  407. package/registry/blocks/task-model/demo.jsx +215 -0
  408. package/registry/blocks/task-model/dnd.jsx +240 -0
  409. package/registry/blocks/task-model/index.js +97 -0
  410. package/registry/blocks/task-model/model.js +919 -0
  411. package/registry/blocks/task-model/parts.jsx +449 -0
  412. package/registry/blocks/task-model/toolbar.jsx +271 -0
  413. package/registry/blocks/testimonials/README.md +139 -0
  414. package/registry/blocks/testimonials/block.json +11 -0
  415. package/registry/blocks/testimonials/demo.jsx +137 -0
  416. package/registry/blocks/testimonials/index.js +4 -0
  417. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  418. package/registry/blocks/transaction-list/README.md +126 -0
  419. package/registry/blocks/transaction-list/block.json +10 -0
  420. package/registry/blocks/transaction-list/demo.jsx +79 -0
  421. package/registry/blocks/transaction-list/index.js +4 -0
  422. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  423. package/registry/blocks/whiteboard/README.md +179 -0
  424. package/registry/blocks/whiteboard/block.json +10 -0
  425. package/registry/blocks/whiteboard/demo.jsx +169 -0
  426. package/registry/blocks/whiteboard/index.js +6 -0
  427. package/registry/blocks/whiteboard/templates.js +150 -0
  428. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  429. package/src/cli.mjs +19 -6
  430. package/src/install.mjs +18 -0
  431. package/src/registry.mjs +38 -4
  432. package/src/shadcn.mjs +14 -1
  433. package/templates/next/files/README.md +145 -11
  434. package/templates/next/files/app/globals.css +55 -4
  435. package/templates/next/files/check/run.mjs +3 -3
  436. package/templates/next/files/components/ui/avatar.jsx +141 -0
  437. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  438. package/templates/next/files/components/ui/button-group.jsx +70 -0
  439. package/templates/next/files/components/ui/button.jsx +132 -12
  440. package/templates/next/files/components/ui/calendar.jsx +609 -0
  441. package/templates/next/files/components/ui/carousel.jsx +281 -0
  442. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  443. package/templates/next/files/components/ui/combobox.jsx +608 -0
  444. package/templates/next/files/components/ui/command.jsx +577 -0
  445. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  446. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  447. package/templates/next/files/components/ui/distribution.jsx +117 -0
  448. package/templates/next/files/components/ui/feedback.jsx +143 -0
  449. package/templates/next/files/components/ui/field.jsx +115 -3
  450. package/templates/next/files/components/ui/file-input.jsx +234 -0
  451. package/templates/next/files/components/ui/icon.jsx +130 -8
  452. package/templates/next/files/components/ui/image.jsx +143 -0
  453. package/templates/next/files/components/ui/index.js +47 -2
  454. package/templates/next/files/components/ui/input-group.jsx +213 -0
  455. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  456. package/templates/next/files/components/ui/item.jsx +206 -0
  457. package/templates/next/files/components/ui/menu.jsx +454 -82
  458. package/templates/next/files/components/ui/notice.jsx +126 -14
  459. package/templates/next/files/components/ui/pagination.jsx +156 -0
  460. package/templates/next/files/components/ui/popover.jsx +284 -0
  461. package/templates/next/files/components/ui/radio.jsx +193 -0
  462. package/templates/next/files/components/ui/rating.jsx +131 -0
  463. package/templates/next/files/components/ui/resizable.jsx +338 -0
  464. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  465. package/templates/next/files/components/ui/separator.jsx +119 -0
  466. package/templates/next/files/components/ui/sheet.jsx +96 -11
  467. package/templates/next/files/components/ui/slider.jsx +174 -0
  468. package/templates/next/files/components/ui/stat.jsx +574 -0
  469. package/templates/next/files/components/ui/steps.jsx +338 -0
  470. package/templates/next/files/components/ui/swatch.jsx +33 -0
  471. package/templates/next/files/components/ui/table.jsx +130 -0
  472. package/templates/next/files/components/ui/toast.jsx +440 -0
  473. package/templates/next/files/components/ui/toggle.jsx +195 -0
  474. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  475. package/templates/next/template.json +264 -8
@@ -0,0 +1,232 @@
1
+ "use client";
2
+
3
+ import { useEffect, useState } from "react";
4
+ import { Badge, Button, Image, Kicker, Rating, Toggle, cx } from "../../ui";
5
+ import { Price, cartLine } from "../product-detail";
6
+
7
+ // One product as a card in the grid, and one promotion as a picture tile.
8
+ // The whole card opens the product (its name is the real link or button,
9
+ // stretched over the card); the heart and the add button sit above that
10
+ // target and click on their own.
11
+
12
+ async function attempt(fn, ...args) {
13
+ try {
14
+ const r = await fn(...args);
15
+ return r && typeof r === "object" ? r : { ok: Boolean(r) };
16
+ } catch (e) {
17
+ return { ok: false, error: e?.message };
18
+ }
19
+ }
20
+
21
+ const STRETCH = "after:absolute after:inset-0";
22
+
23
+ function Title({ href, onOpen, children, className }) {
24
+ if (href) {
25
+ return (
26
+ <Button variant="link" size="inherit" tone="muted" href={href} data-stretch className={cx(STRETCH, className)}>
27
+ {children}
28
+ </Button>
29
+ );
30
+ }
31
+ if (onOpen) {
32
+ return (
33
+ <Button variant="link" size="inherit" tone="muted" onClick={onOpen} data-stretch className={cx("text-left", STRETCH, className)}>
34
+ {children}
35
+ </Button>
36
+ );
37
+ }
38
+ return <span className={className}>{children}</span>;
39
+ }
40
+
41
+ /**
42
+ * A product card: picture, badge, heart, brand, name, rating, price and a
43
+ * quick add. A product with options cannot be added blind: its button
44
+ * says "Choose options" and opens it.
45
+ */
46
+ export function ProductCard({ product, onOpen, onAddToCart, onFavorite, favorite = false, currency = "USD", locale, addLabel = "Add to cart", className }) {
47
+ const [fav, setFav] = useState(favorite);
48
+ const [status, setStatus] = useState({ state: "idle" });
49
+ useEffect(() => setFav(favorite), [favorite]);
50
+ useEffect(() => {
51
+ if (status.state !== "added") return;
52
+ const t = setTimeout(() => setStatus({ state: "idle" }), 2500);
53
+ return () => clearTimeout(t);
54
+ }, [status.state]);
55
+
56
+ const open = onOpen ? () => onOpen(product) : undefined;
57
+ const cur = product.currency ?? currency;
58
+ const needsChoice = (product.options ?? []).some((o) => (o.values ?? []).length > 1);
59
+ const soldOut = product.available === false;
60
+ const picture = product.image ?? (typeof product.images?.[0] === "object" ? product.images[0]?.src : product.images?.[0]);
61
+
62
+ async function add() {
63
+ setStatus({ state: "busy" });
64
+ const r = await attempt(onAddToCart, cartLine(product, {}, 1, cur));
65
+ setStatus(r.ok ? { state: "added" } : { state: "failed", error: r.error || "Not added. Try again." });
66
+ }
67
+
68
+ async function toggle(next) {
69
+ setFav(next);
70
+ const r = await attempt(onFavorite, product, next);
71
+ if (!r.ok) {
72
+ setFav(!next);
73
+ setStatus({ state: "failed", error: r.error || (next ? "Not saved." : "Not removed.") });
74
+ }
75
+ }
76
+
77
+ return (
78
+ <article
79
+ className={cx(
80
+ "relative flex h-full min-w-0 flex-col overflow-hidden rounded-lg border border-border bg-card text-card-foreground",
81
+ "has-[a[data-stretch]:focus-visible,button[data-stretch]:focus-visible]:ring-2 has-[a[data-stretch]:focus-visible,button[data-stretch]:focus-visible]:ring-ring",
82
+ (product.href || onOpen) && "transition-shadow hover:shadow-md",
83
+ className,
84
+ )}
85
+ >
86
+ <div className="relative">
87
+ <Image src={picture} alt="" ratio="square" rounded="none" />
88
+ {product.badge ? (
89
+ <Badge variant="soft" className="absolute start-2 top-2">
90
+ {product.badge}
91
+ </Badge>
92
+ ) : null}
93
+ {onFavorite ? (
94
+ <Toggle
95
+ icon="heart"
96
+ label={fav ? `Remove ${product.name} from favourites` : `Save ${product.name} to favourites`}
97
+ pressed={fav}
98
+ onPressedChange={toggle}
99
+ className="absolute end-2 top-2 z-10"
100
+ />
101
+ ) : null}
102
+ </div>
103
+ <div className="flex flex-1 flex-col gap-1.5 border-t border-border p-3 sm:p-4">
104
+ {product.brand ? <Kicker className="truncate">{product.brand}</Kicker> : null}
105
+ <h3 className="line-clamp-2 text-sm font-medium leading-snug">
106
+ <Title href={product.href} onOpen={open}>
107
+ {product.name}
108
+ </Title>
109
+ </h3>
110
+ {typeof product.rating === "number" ? (
111
+ <p className="flex items-center gap-1.5 text-xs text-muted-foreground">
112
+ <Rating value={product.rating} size="sm" />
113
+ {typeof product.reviews === "number" ? <span data-nums>({product.reviews.toLocaleString(locale)})</span> : null}
114
+ </p>
115
+ ) : null}
116
+ <Price price={product.price} compareAt={product.compareAt} currency={cur} locale={locale} size="md" className="mt-auto pt-1" />
117
+ {onAddToCart ? (
118
+ <div className="relative z-10 mt-2 grid gap-1.5">
119
+ {soldOut ? (
120
+ <Button disabled className="w-full">
121
+ Sold out
122
+ </Button>
123
+ ) : needsChoice ? (
124
+ <Button className="w-full" onClick={open} disabled={!open} href={open ? undefined : product.href}>
125
+ Choose options
126
+ </Button>
127
+ ) : (
128
+ <Button variant="primary" icon="shopping-cart" className="w-full" loading={status.state === "busy"} onClick={add}>
129
+ {status.state === "added" ? "Added" : addLabel}
130
+ </Button>
131
+ )}
132
+ <p aria-live="polite" className="text-xs empty:hidden">
133
+ {status.state === "failed" ? <span className="text-destructive">{status.error}</span> : null}
134
+ {status.state === "added" ? <span className="sr-only">{product.name} added to your cart.</span> : null}
135
+ </p>
136
+ </div>
137
+ ) : null}
138
+ </div>
139
+ </article>
140
+ );
141
+ }
142
+
143
+ /**
144
+ * A promotion: a tall picture with a kicker, a title and an action on a
145
+ * panel over its foot. The panel is the theme's background, so the words
146
+ * read on any picture in any theme.
147
+ */
148
+ export function PromoTile({ item, large = false, onOpen, className }) {
149
+ const action = item.action ?? (item.href || onOpen ? { label: item.actionLabel ?? "Shop now", href: item.href } : null);
150
+ const open = action?.onClick ?? (onOpen ? () => onOpen(item) : undefined);
151
+ return (
152
+ <article
153
+ className={cx(
154
+ "relative flex min-w-0 overflow-hidden rounded-lg border border-border bg-muted",
155
+ "has-[a[data-stretch]:focus-visible,button[data-stretch]:focus-visible]:ring-2 has-[a[data-stretch]:focus-visible,button[data-stretch]:focus-visible]:ring-ring",
156
+ large ? "min-h-80 lg:min-h-full" : "min-h-80",
157
+ className,
158
+ )}
159
+ >
160
+ <FeatureMedia item={item} />
161
+ <div className={cx("relative mt-auto w-full p-3", large && "sm:p-5")}>
162
+ <div className="grid gap-1 rounded-md bg-background/90 p-4 text-foreground shadow-sm backdrop-blur-sm">
163
+ {item.kicker ? <Kicker>{item.kicker}</Kicker> : null}
164
+ <h3 className={cx("font-semibold leading-tight", large ? "text-xl sm:text-2xl" : "text-lg")}>{item.title ?? item.name}</h3>
165
+ {item.body ? <p className="text-sm text-muted-foreground">{item.body}</p> : null}
166
+ {action ? (
167
+ <p className="mt-1 text-sm">
168
+ {action.href ? (
169
+ <Button variant="link" href={action.href} data-stretch className={STRETCH}>
170
+ {action.label}
171
+ </Button>
172
+ ) : open ? (
173
+ <Button variant="link" onClick={open} data-stretch className={STRETCH}>
174
+ {action.label}
175
+ </Button>
176
+ ) : null}
177
+ </p>
178
+ ) : null}
179
+ </div>
180
+ </div>
181
+ </article>
182
+ );
183
+ }
184
+
185
+ // The tile's picture, or a muted looping video for a feature. The video
186
+ // never plays for a reader who asks for reduced motion, and has a pause.
187
+ function FeatureMedia({ item }) {
188
+ const [paused, setPaused] = useState(false);
189
+ const [calm, setCalm] = useState(true);
190
+ useEffect(() => {
191
+ if (!item.video || typeof matchMedia !== "function") return;
192
+ const mq = matchMedia("(prefers-reduced-motion: reduce)");
193
+ setCalm(mq.matches);
194
+ const on = () => setCalm(mq.matches);
195
+ mq.addEventListener("change", on);
196
+ return () => mq.removeEventListener("change", on);
197
+ }, [item.video]);
198
+
199
+ if (item.video) {
200
+ const still = calm || paused;
201
+ return (
202
+ <>
203
+ {/* A picture of motion, not content: muted, no controls of its own. */}
204
+ <video
205
+ key={still ? "still" : "moving"}
206
+ src={item.video}
207
+ poster={item.poster ?? item.image}
208
+ muted
209
+ loop
210
+ playsInline
211
+ autoPlay={!still}
212
+ aria-hidden="true"
213
+ className="absolute inset-0 size-full object-cover"
214
+ />
215
+ {!calm ? (
216
+ <Toggle
217
+ icon={paused ? "play" : "pause"}
218
+ label={paused ? "Play the video" : "Pause the video"}
219
+ pressed={paused}
220
+ onPressedChange={setPaused}
221
+ className="absolute start-3 top-3 z-10"
222
+ />
223
+ ) : null}
224
+ </>
225
+ );
226
+ }
227
+ return (
228
+ <div className="absolute inset-0">
229
+ <Image src={item.image} alt={item.alt ?? ""} ratio="square" rounded="none" className="size-full" />
230
+ </div>
231
+ );
232
+ }
@@ -0,0 +1,235 @@
1
+ "use client";
2
+
3
+ import { isValidElement, useId, useMemo, useState } from "react";
4
+ import { Button, Empty, Notice, Select, Skeleton, cx } from "../../ui";
5
+ import { ProductCard, PromoTile } from "./product-card";
6
+
7
+ // Products (or promotions) as cards in a grid, with facet filters, a sort
8
+ // and a count over it, and an optional lead tile that takes a double
9
+ // cell. Filtering and sorting happen here over `products`, or — with
10
+ // `manual` — in your loader, the block only reporting what was picked.
11
+
12
+ const SORTS = {
13
+ featured: { label: "Featured", compare: null },
14
+ "price-asc": { label: "Price: low to high", compare: (a, b) => (a.price ?? 0) - (b.price ?? 0) },
15
+ "price-desc": { label: "Price: high to low", compare: (a, b) => (b.price ?? 0) - (a.price ?? 0) },
16
+ rating: { label: "Top rated", compare: (a, b) => (b.rating ?? 0) - (a.rating ?? 0) },
17
+ newest: {
18
+ label: "Newest",
19
+ compare: (a, b) => String(b.createdAt ?? "").localeCompare(String(a.createdAt ?? "")),
20
+ },
21
+ };
22
+ const DEFAULT_SORTS = ["featured", "price-asc", "price-desc", "rating"];
23
+
24
+ function sortsOf(sort) {
25
+ if (!sort) return [];
26
+ const list = Array.isArray(sort) ? sort : DEFAULT_SORTS;
27
+ return list
28
+ .map((s) => (typeof s === "string" ? { value: s, ...SORTS[s] } : { ...SORTS[s.value], ...s }))
29
+ .filter((s) => s.value && s.label);
30
+ }
31
+
32
+ const optionsOf = (filter) =>
33
+ (filter.options ?? []).filter((o) => o != null).map((o) => (typeof o === "object" ? o : { value: String(o), label: String(o) }));
34
+
35
+ /** Does `product` carry `value` under `key`? Looked for in the product's
36
+ * own field (a string or a list), in `facets[key]`, and in the values of
37
+ * its option with that key — so a size filter matches the sizes a
38
+ * product is sold in. */
39
+ export function productMatches(product, key, value) {
40
+ const found = [];
41
+ const push = (v) => (Array.isArray(v) ? found.push(...v) : v != null && found.push(v));
42
+ push(product?.[key]);
43
+ push(product?.facets?.[key]);
44
+ const option = product?.options?.find((o) => o.key === key);
45
+ for (const v of option?.values ?? []) if (!(typeof v === "object" && v?.disabled)) push(typeof v === "object" ? v.value : v);
46
+ return found.some((v) => String(typeof v === "object" ? v.value : v) === String(value));
47
+ }
48
+
49
+ const COLS = {
50
+ product: {
51
+ 2: "grid-cols-2",
52
+ 3: "grid-cols-2 lg:grid-cols-3",
53
+ 4: "grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
54
+ },
55
+ promo: {
56
+ 2: "grid-cols-1 sm:grid-cols-2",
57
+ 3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
58
+ 4: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-4",
59
+ },
60
+ };
61
+
62
+ function LoadingCards({ count, promo, cols }) {
63
+ return (
64
+ <div aria-busy="true" aria-label="Loading products" className={cx("grid gap-3 sm:gap-4", cols)}>
65
+ {Array.from({ length: count }, (_, i) =>
66
+ promo ? (
67
+ <Skeleton key={i} shape="rect" className="h-80 w-full" />
68
+ ) : (
69
+ <div key={i} className="grid gap-2 overflow-hidden rounded-lg border border-border bg-card pb-4">
70
+ <Skeleton shape="rect" className="aspect-square w-full" />
71
+ <Skeleton className="mx-4 w-1/3" />
72
+ <Skeleton className="mx-4 w-3/4" />
73
+ <Skeleton className="mx-4 w-1/4" />
74
+ </div>
75
+ ),
76
+ )}
77
+ </div>
78
+ );
79
+ }
80
+
81
+ export function ProductGrid({
82
+ products,
83
+ card = "product",
84
+ columns,
85
+ filters,
86
+ sort,
87
+ defaultSort,
88
+ manual = false,
89
+ total,
90
+ onQueryChange,
91
+ feature,
92
+ onOpen,
93
+ onAddToCart,
94
+ onFavorite,
95
+ favorites,
96
+ currency = "USD",
97
+ locale,
98
+ addLabel,
99
+ countLabel = (n) => `${n.toLocaleString(locale)} ${n === 1 ? "product" : "products"}`,
100
+ loading = false,
101
+ error,
102
+ onRetry,
103
+ emptyTitle = "No products yet",
104
+ emptyBody,
105
+ label = "Products",
106
+ className,
107
+ }) {
108
+ const id = useId();
109
+ const promo = card === "promo";
110
+ const sorts = sortsOf(sort);
111
+ const facetList = (filters ?? []).filter(Boolean);
112
+ const [picked, setPicked] = useState(() => Object.fromEntries(facetList.map((f) => [f.key, f.value ?? ""])));
113
+ const [order, setOrder] = useState(defaultSort ?? sorts[0]?.value ?? "featured");
114
+ const favSet = new Set(favorites ?? []);
115
+ const active = Object.values(picked).some(Boolean);
116
+
117
+ const shown = useMemo(() => {
118
+ let list = (products ?? []).filter(Boolean);
119
+ if (manual) return list;
120
+ for (const [key, value] of Object.entries(picked)) if (value) list = list.filter((p) => productMatches(p, key, value));
121
+ const cmp = sorts.find((s) => s.value === order)?.compare;
122
+ return cmp ? [...list].sort(cmp) : list;
123
+ // eslint-disable-next-line react-hooks/exhaustive-deps
124
+ }, [products, picked, order, manual, sort]);
125
+
126
+ function change(nextPicked, nextOrder) {
127
+ setPicked(nextPicked);
128
+ setOrder(nextOrder);
129
+ onQueryChange?.({ filters: Object.fromEntries(Object.entries(nextPicked).filter(([, v]) => v)), sort: nextOrder });
130
+ }
131
+ const clear = () => change(Object.fromEntries(facetList.map((f) => [f.key, ""])), order);
132
+
133
+ const n = columns ?? (promo ? 3 : 4);
134
+ const cols = (COLS[promo ? "promo" : "product"][n] ?? COLS.product[4]);
135
+ const count = manual && typeof total === "number" ? total : shown.length;
136
+
137
+ const toolbar =
138
+ facetList.length || sorts.length ? (
139
+ <div className="flex flex-wrap items-end justify-between gap-3">
140
+ {facetList.length ? (
141
+ <div role="group" aria-label="Filters" className="grid w-full grid-cols-2 gap-2 sm:flex sm:w-auto sm:flex-wrap">
142
+ {facetList.map((f) => (
143
+ <div key={f.key} className="min-w-0 sm:w-40">
144
+ <Select aria-label={f.label} value={picked[f.key] ?? ""} onChange={(e) => change({ ...picked, [f.key]: e.target.value }, order)}>
145
+ <option value="">{f.all ?? `All ${String(f.label).toLowerCase()}`}</option>
146
+ {optionsOf(f).map((o) => (
147
+ <option key={o.value} value={o.value}>
148
+ {o.label}
149
+ </option>
150
+ ))}
151
+ </Select>
152
+ </div>
153
+ ))}
154
+ {active ? (
155
+ <Button variant="quiet" icon="x" onClick={clear}>
156
+ Clear
157
+ </Button>
158
+ ) : null}
159
+ </div>
160
+ ) : null}
161
+ <div className="flex w-full items-center justify-between gap-3 sm:ml-auto sm:w-auto">
162
+ <p id={`${id}-count`} data-nums aria-live="polite" className="text-sm text-muted-foreground whitespace-nowrap">
163
+ {loading ? " " : countLabel(count)}
164
+ </p>
165
+ {sorts.length ? (
166
+ <div className="w-44 shrink-0">
167
+ <Select
168
+ aria-label="Sort by"
169
+ value={order}
170
+ onChange={(e) => change(picked, e.target.value)}
171
+ options={sorts.map((s) => ({ value: s.value, label: s.label }))}
172
+ />
173
+ </div>
174
+ ) : null}
175
+ </div>
176
+ </div>
177
+ ) : null;
178
+
179
+ let body;
180
+ if (loading) body = <LoadingCards count={promo ? n : n * 2} promo={promo} cols={cols} />;
181
+ else if (error) {
182
+ body = (
183
+ <Notice tone="danger" title="The products did not load" action={onRetry ? <Button size="sm" onClick={onRetry}>Try again</Button> : null}>
184
+ {typeof error === "string" ? error : null}
185
+ </Notice>
186
+ );
187
+ } else if (shown.length === 0 && !feature) {
188
+ body = active ? (
189
+ <Empty
190
+ icon="filter"
191
+ title="Nothing matches these filters"
192
+ body="Take one off, or clear them all."
193
+ action={<Button onClick={clear}>Clear filters</Button>}
194
+ />
195
+ ) : (
196
+ <Empty icon="package" title={emptyTitle} body={emptyBody} />
197
+ );
198
+ } else {
199
+ const lead = feature ? (
200
+ <li key="feature" className={cx("min-w-0", promo ? "sm:col-span-2 sm:row-span-2" : "col-span-2 lg:row-span-2")}>
201
+ {isValidElement(feature) ? feature : <PromoTile item={feature} large onOpen={feature.onOpen} className="h-full" />}
202
+ </li>
203
+ ) : null;
204
+ body = (
205
+ <ul aria-describedby={toolbar ? `${id}-count` : undefined} className={cx("grid gap-3 sm:gap-4", cols)}>
206
+ {lead}
207
+ {shown.map((p, i) => (
208
+ <li key={p.id ?? i} className="min-w-0">
209
+ {promo ? (
210
+ <PromoTile item={p} onOpen={onOpen} className="h-full" />
211
+ ) : (
212
+ <ProductCard
213
+ product={p}
214
+ onOpen={onOpen}
215
+ onAddToCart={onAddToCart}
216
+ onFavorite={onFavorite}
217
+ favorite={favSet.has(p.id)}
218
+ currency={currency}
219
+ locale={locale}
220
+ addLabel={addLabel}
221
+ />
222
+ )}
223
+ </li>
224
+ ))}
225
+ </ul>
226
+ );
227
+ }
228
+
229
+ return (
230
+ <section aria-label={label} className={cx("grid min-w-0 gap-4", className)}>
231
+ {toolbar}
232
+ {body}
233
+ </section>
234
+ );
235
+ }
@@ -0,0 +1,196 @@
1
+ # RecordForm
2
+
3
+ One record's fields, described as data and drawn with the kit: a label, a
4
+ control, help under it, an error in its place. The fields are grouped into
5
+ titled sections. The form holds the draft, checks each field when you
6
+ leave it (and again as you fix it), and saves the whole record through
7
+ `onSave`. A save that fails keeps every edit on screen and says why — over
8
+ the form, and under each field the server refused.
9
+
10
+ ```jsx
11
+ import { RecordForm } from "../components/blocks/record-form"; // a Makersclaw app ("./blocks/record-form" from components/)
12
+ import { RecordForm } from "@/components/makersclaw/blocks/record-form"; // a shadcn project
13
+ ```
14
+
15
+ It is a client component: render it from a `"use client"` component, and
16
+ load the record in the server surface (README §4).
17
+
18
+ ## The data
19
+
20
+ `values` is the record: `{ name: "…", price: 189, tags: ["gift"] }`. Each
21
+ field reads and writes `values[field.name]`.
22
+
23
+ A field:
24
+
25
+ - `name` — the key in `values` (required, unique in the form).
26
+ - `label` — what it is called.
27
+ - `type` — how it is entered (default `"text"`):
28
+ - `text` · `email` · `url` · `tel` · `number` — an Input. `icon`,
29
+ `prefix` ("$") or `suffix` ("kg") make it an InputGroup. A number is
30
+ sent as a number (empty → `null`).
31
+ - `password` — a PasswordInput with show/hide.
32
+ - `textarea` — `rows` (4).
33
+ - `select` — a native Select over `options`; `placeholder` is the empty
34
+ first choice.
35
+ - `combobox` — a searchable Combobox over `options` (with `description`
36
+ per option); `multiple: true` holds an array.
37
+ - `tags` — a Combobox that holds an array and takes new words.
38
+ - `radio` — a RadioGroup; `orientation: "horizontal"` for a short row.
39
+ - `choice-cards` — the RadioGroup's card look: each option
40
+ `{ value, label, description?, price?, per?, badge? }` — a plan, a
41
+ package, a mode.
42
+ - `checkbox` — one box that must be ticked when `required` ("I agree…");
43
+ `description` under its label. Holds `true`/`false`.
44
+ - `checkboxes` — several boxes over `options`; holds an array of values.
45
+ - `switch` — an on/off with `description`; holds `true`/`false`.
46
+ - `date` — a DatePicker; the value is `"YYYY-MM-DD"`. `min`, `max`,
47
+ `captionLayout: "dropdown"` for a birth date.
48
+ - `time` — a TimeInput; `"HH:MM"`.
49
+ - `rating` — Rating stars as an input; `max` (5). Holds a whole number.
50
+ - `photo` — an Avatar with Upload and Remove. `nameFrom(values)` gives
51
+ the initials shown with no picture. Holds a URL string, or
52
+ `{ src, file, name }` after a pick: upload `file` in `onSave`.
53
+ - `images` — a row of thumbnails, each removable, and Add while under
54
+ `maxItems`. Holds an array of URL strings or `{ src, file, name }`.
55
+ - `static` — the value as text, not editable ("Verified"); `format(value,
56
+ values)` to word it.
57
+ - `required` — it must hold something (a ticked box, a chosen option).
58
+ - `options` — for the choosing types: `[{ value, label, description?,
59
+ disabled? }]` or plain strings.
60
+ - `help` — the muted line under it (hidden while an error shows).
61
+ - `placeholder`, `autoComplete`, `min`, `max`, `step`, `disabled`,
62
+ `readOnly` — as the control's own.
63
+ - `width` — `"full"` spans both columns; `"half"` keeps a wide type in one.
64
+ Text areas, choices, boxes, switches, a photo and images are full by
65
+ default.
66
+ - `when(values)` — shown only while it returns true; a hidden field is not
67
+ checked and not sent.
68
+ - `defaultValue` — used when `values` has none.
69
+
70
+ Validation, first broken rule wins, one message per field:
71
+
72
+ - `required` (`requiredMessage` to word it);
73
+ - the type's own shape: an email, a full `https://` address, a number
74
+ within `min`/`max`;
75
+ - `minLength`, `maxLength`;
76
+ - `pattern` — a regular expression the whole text must match, with
77
+ `patternMessage`;
78
+ - `matches` — the name of another field this one must equal (a confirm),
79
+ with `matchMessage`;
80
+ - `maxItems` — for arrays (tags, images, checkboxes);
81
+ - `validate(value, values)` — return a message, or nothing when fine.
82
+
83
+ ## Props
84
+
85
+ - `fields` — the fields, one untitled section. Or:
86
+ - `sections` — `[{ title?, description?, columns?, fields }]`: titled
87
+ groups in order.
88
+ - `values` — the record as it stands. Pass a different record and the form
89
+ starts over from it (and forgets unsaved edits).
90
+ - `onSave(values, { changed })` — save the record. `values` is every
91
+ shown field, text trimmed, numbers as numbers; `changed` names the keys
92
+ that differ from `values`. Return `{ ok: true }`, or `{ ok: false,
93
+ error?, errors? }` where `errors` is `{ fieldName: "message" }` — each
94
+ shows under its field and the first one is focused. A throw counts as
95
+ failed. In a Makersclaw app it is one line over `updateRow`:
96
+ `onSave={(v) => updateRow({ source: "public.products", id: p.id, patch: v })}`.
97
+ - `container` — `"card"` (default: a Card with `title` and `description`)
98
+ · `"page"` (no box: a page heading, then the sections) · `"dialog"` (a
99
+ centred Sheet; on a phone a bottom sheet). A dialog needs `open` and
100
+ `onClose`; it closes on a good save, and opens on the record as it
101
+ stands, never on last time's edits.
102
+ - `layout` — `"stacked"` (default: each section's title over its fields)
103
+ or `"split"` (the title and description on the left, the fields on the
104
+ right, a rule between sections, from 768px up). A dialog is always
105
+ stacked.
106
+ - `columns` — `2` (default: two columns from 640px) or `1`. A dialog is
107
+ one column.
108
+ - `title`, `description` — the card's, the page's, or the dialog's.
109
+ - `onCancel()` — shows Cancel; it drops the edits first. Without it, a
110
+ Discard button shows while there are edits.
111
+ - `onDelete()` — shows a danger button at the start of the footer (not in
112
+ a dialog) that asks first. Return `{ ok }` like `onSave`.
113
+ - `confirmDelete` — the ask: `{ title?, body?, confirmLabel?,
114
+ consequences?, requireText?, acknowledge? }` (the kit Confirm's).
115
+ - `deleteLabel` — `"Delete"`.
116
+ - `onChange(values)` — every edit, for a live preview beside the form.
117
+ - `submitLabel` (`"Save changes"`) · `cancelLabel` (`"Cancel"`) ·
118
+ `savedLabel` (`"Saved"`, shown for three seconds after a good save).
119
+ - `requireChanges` — `true` (default): Save waits for an edit. Pass
120
+ `false` for a form that creates a record.
121
+ - `loading` — skeleton fields while the record loads.
122
+ - `error` — the record could not be loaded: a Notice in place of the
123
+ fields; `onRetry` adds "Try again".
124
+ - `emptyLabel` — shown when there are no fields.
125
+ - `className` — layout around it (width, margin). Never colour.
126
+
127
+ ## Example
128
+
129
+ ```jsx
130
+ // components/ProductEditor.jsx — client
131
+ "use client";
132
+ import { updateRow, deleteRow } from "../lib/actions";
133
+ import { RecordForm } from "./blocks/record-form";
134
+
135
+ const FIELDS = [
136
+ { name: "name", label: "Product name", required: true },
137
+ { name: "price", label: "Price", type: "number", prefix: "$", min: 0, required: true },
138
+ { name: "category", label: "Category", type: "select", options: ["Outerwear", "Tops", "Footwear"] },
139
+ { name: "launch", label: "Launch date", type: "date" },
140
+ { name: "details", label: "Details", type: "textarea" },
141
+ ];
142
+
143
+ export default function ProductEditor({ product }) {
144
+ return (
145
+ <RecordForm
146
+ title="Edit product"
147
+ fields={FIELDS}
148
+ values={product}
149
+ onSave={(values) => updateRow({ source: "public.products", id: product.id, patch: values })}
150
+ onDelete={() => deleteRow({ source: "public.products", id: product.id })}
151
+ deleteLabel="Remove product"
152
+ />
153
+ );
154
+ }
155
+ ```
156
+
157
+ In a dialog, from a list:
158
+
159
+ ```jsx
160
+ const [editing, setEditing] = useState(null);
161
+
162
+ <RecordForm
163
+ container="dialog"
164
+ open={Boolean(editing)}
165
+ onClose={() => setEditing(null)}
166
+ title="Edit member"
167
+ values={editing ?? {}}
168
+ sections={[
169
+ { title: "Profile", fields: [{ name: "photo", label: "Photo", type: "photo" }, { name: "name", label: "Name", required: true }] },
170
+ { title: "Access", fields: [{ name: "role", label: "Role", type: "radio", options: ["Admin", "Member", "Viewer"] }] },
171
+ ]}
172
+ onSave={async (values) => {
173
+ const res = await updateRow({ source: "public.members", id: editing.id, patch: values });
174
+ return res.ok ? res : { ok: false, errors: res.error?.includes("email") ? { email: "Already in use." } : undefined, error: res.error };
175
+ }}
176
+ />
177
+ ```
178
+
179
+ ## What it will not do
180
+
181
+ - Upload files: a picked photo or image arrives in `onSave` as
182
+ `{ src, file }` — upload `file`, store the URL.
183
+ - Save field by field: it saves the record with one button. Settings that
184
+ each save when flipped are `settings-list`.
185
+ - Sign in, sign up or change a password with rules beside it: that is
186
+ `auth-form`.
187
+ - Lay out a field by hand: a field it cannot draw is your own form in
188
+ `components/`, built from the kit's `Field` and controls.
189
+
190
+ A form it cannot be is your own component in `components/`, built from the
191
+ kit — not an edit to this block. Its files are fixed: `npm run check`
192
+ refuses a change, and `makersclaw add record-form` restores them.
193
+
194
+ ## Credits
195
+
196
+ Shapes from Creative Tim UI (https://github.com/creativetimofficial/ui, MIT) and blocks.so (https://github.com/ephraimduncan/blocks, MIT); this is a rewrite on the Makersclaw kit.
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "record-form",
3
+ "title": "RecordForm",
4
+ "description": "Edit or create one record from a list of fields given as data (type, label, required, options, help, validation), grouped into titled sections — text, email, number, password, select, combobox, tags, radio, choice cards with prices, checkboxes, switch, date, time, rating, a photo and a row of images. It checks as you type, saves through an async onSave that answers { ok } or per-field errors shown under each field, and sits in a card, on a page (sections split title-left) or in a dialog.",
5
+ "useWhen": "A screen edits one record's fields: a profile, a product, a team member, account details, a sign-up or application form, a settings page of fields with one Save. Not for a list of records (data-table), for toggles that each save on their own (settings-list), or for sign-in (auth-form).",
6
+ "tags": ["form", "record", "edit", "create", "fields", "sections", "validation", "save", "profile", "product", "dialog", "settings", "crud", "choice", "plan"],
7
+ "exports": ["RecordForm"],
8
+ "files": ["index.js", "record-form.jsx", "record-fields.jsx", "rules.js"],
9
+ "requires": []
10
+ }