makersclaw 0.2.0 → 0.3.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 (473) 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/install.mjs +18 -0
  430. package/src/shadcn.mjs +8 -0
  431. package/templates/next/files/README.md +145 -11
  432. package/templates/next/files/app/globals.css +55 -4
  433. package/templates/next/files/check/run.mjs +3 -3
  434. package/templates/next/files/components/ui/avatar.jsx +141 -0
  435. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  436. package/templates/next/files/components/ui/button-group.jsx +70 -0
  437. package/templates/next/files/components/ui/button.jsx +132 -12
  438. package/templates/next/files/components/ui/calendar.jsx +609 -0
  439. package/templates/next/files/components/ui/carousel.jsx +281 -0
  440. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  441. package/templates/next/files/components/ui/combobox.jsx +608 -0
  442. package/templates/next/files/components/ui/command.jsx +577 -0
  443. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  444. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  445. package/templates/next/files/components/ui/distribution.jsx +117 -0
  446. package/templates/next/files/components/ui/feedback.jsx +143 -0
  447. package/templates/next/files/components/ui/field.jsx +115 -3
  448. package/templates/next/files/components/ui/file-input.jsx +234 -0
  449. package/templates/next/files/components/ui/icon.jsx +130 -8
  450. package/templates/next/files/components/ui/image.jsx +143 -0
  451. package/templates/next/files/components/ui/index.js +47 -2
  452. package/templates/next/files/components/ui/input-group.jsx +213 -0
  453. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  454. package/templates/next/files/components/ui/item.jsx +206 -0
  455. package/templates/next/files/components/ui/menu.jsx +454 -82
  456. package/templates/next/files/components/ui/notice.jsx +126 -14
  457. package/templates/next/files/components/ui/pagination.jsx +156 -0
  458. package/templates/next/files/components/ui/popover.jsx +284 -0
  459. package/templates/next/files/components/ui/radio.jsx +193 -0
  460. package/templates/next/files/components/ui/rating.jsx +131 -0
  461. package/templates/next/files/components/ui/resizable.jsx +338 -0
  462. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  463. package/templates/next/files/components/ui/separator.jsx +119 -0
  464. package/templates/next/files/components/ui/sheet.jsx +96 -11
  465. package/templates/next/files/components/ui/slider.jsx +174 -0
  466. package/templates/next/files/components/ui/stat.jsx +574 -0
  467. package/templates/next/files/components/ui/steps.jsx +338 -0
  468. package/templates/next/files/components/ui/swatch.jsx +33 -0
  469. package/templates/next/files/components/ui/table.jsx +130 -0
  470. package/templates/next/files/components/ui/toast.jsx +440 -0
  471. package/templates/next/files/components/ui/toggle.jsx +195 -0
  472. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  473. package/templates/next/template.json +264 -8
@@ -0,0 +1,133 @@
1
+ # Footer
2
+
3
+ A public site's footer. It holds the brand and a line about it, link
4
+ columns (or one row of links), social buttons drawn with the kit's brand
5
+ marks, and the legal line with its links. Optional parts: a newsletter
6
+ sign-up (with a consent box), a call to action, a large brand statement
7
+ or quote, language and currency selects, and a version badge.
8
+
9
+ Three layouts:
10
+ - **columns** (default) — the brand, tagline and socials beside the link
11
+ columns (and an inline sign-up), then the legal row;
12
+ - **simple** — one row: brand, links, socials; then the legal row;
13
+ - **centred** — everything stacked in the middle.
14
+
15
+ The sign-up checks the email (and the consent, when asked) in place,
16
+ then calls `newsletter.onSubscribe(email, { consent })`, which is async
17
+ and returns `{ ok }`. While it runs the button spins. `{ ok: false,
18
+ error }` (or a throw) shows the error and keeps the email. `{ ok: true }`
19
+ shows "You're subscribed". It is a full-width section: it pads itself
20
+ and centres its content.
21
+
22
+ ```jsx
23
+ import { Footer } from "../components/blocks/footer"; // a Makersclaw app ("./blocks/footer" from components/)
24
+ import { Footer } from "@/components/makersclaw/blocks/footer"; // a shadcn project
25
+ ```
26
+
27
+ It is a client component: render it from a `"use client"` component (a
28
+ layout is the usual place).
29
+
30
+ ## The data
31
+
32
+ ```js
33
+ // a link, anywhere: columns, links, legalLinks
34
+ { label: "Pricing", href: "/pricing", external?: true } // external opens a new tab
35
+
36
+ // columns
37
+ [{ title: "Product", links: [link, …] }, …] // up to 5 across; 2 on a phone
38
+
39
+ // socials
40
+ [{ network: "x", href: "https://x.com/fieldline", label? }, …]
41
+ ```
42
+
43
+ `network` is one of `x`, `linkedin`, `github`, `youtube`, `instagram`,
44
+ `facebook`, `google`, `apple`, `microsoft`, `email`, `rss`. Anything
45
+ else needs an `icon` (a kit Icon name) and a `label`. Each is an icon
46
+ button, named to a screen reader ("X", "LinkedIn"), opening a new tab.
47
+
48
+ ## Props
49
+
50
+ - `layout` — `columns` (default) · `simple` · `centered`.
51
+ - `brand` — a node: your logo, linked home if you like.
52
+ - `tagline` — a line under the brand (columns and centred).
53
+ - `columns` — link columns (columns layout).
54
+ - `links` — one row of links (simple and centred; under the socials in
55
+ columns).
56
+ - `socials` — as above.
57
+ - `newsletter` — `{ onSubscribe, title?, body?, label?, placeholder?,
58
+ consent?, look?, doneTitle?, doneBody? }`. `onSubscribe(email, {
59
+ consent })` is async and returns `{ ok: true }` or `{ ok: false,
60
+ error? }`; without it there is no sign-up. `consent` is the checkbox's
61
+ label (a string or a node with a link), and must be ticked. `look`:
62
+ `inline` (default: a column beside the links) · `card` (a full-width
63
+ card under them). `label` is the button, default "Subscribe".
64
+ - `cta` — `{ title?, body?, actions: [{ label, href?, onClick?, icon?,
65
+ variant? }] }`: "Sign in", "Get the app". A band above the legal row;
66
+ the first action is primary unless it says otherwise.
67
+ - `statement` — a large line at the top: a string, or `{ text, by }` for
68
+ a quote.
69
+ - `locales` — `[{ id, label, options: [{ value, label }], value?,
70
+ defaultValue?, onChange? }]`: language, currency, country selects in
71
+ the legal row. Uncontrolled unless you pass `value`.
72
+ - `version` — "v4.2.0": a code badge in the legal row.
73
+ - `legal` — the copyright line: "© 2026 Fieldline Ltd.". Pass the year;
74
+ the block does not read the clock.
75
+ - `legalLinks` — privacy, terms, cookies.
76
+ - `onNavigate(link, event)` — called when any link or a `cta` action with
77
+ an `href` is followed, before navigation; `event.preventDefault()`
78
+ stops it.
79
+ - `label` — what a screen reader calls the footer and its link row.
80
+ Default "Site".
81
+ - `className` — the footer's spacing. Never colour.
82
+
83
+ ## Example
84
+
85
+ ```jsx
86
+ // components/SiteFooter.jsx
87
+ "use client";
88
+ import { Icon } from "./ui";
89
+ import { Footer } from "./blocks/footer";
90
+ import { subscribe } from "../lib/actions"; // your server action
91
+
92
+ export default function SiteFooter() {
93
+ return (
94
+ <Footer
95
+ brand={<span className="inline-flex items-center gap-2"><Icon name="zap" /> Fieldline</span>}
96
+ tagline="The pipeline small sales teams actually keep up to date."
97
+ columns={[
98
+ { title: "Product", links: [{ label: "Pricing", href: "/pricing" }, { label: "Changelog", href: "/changelog" }] },
99
+ { title: "Company", links: [{ label: "About", href: "/about" }, { label: "Careers", href: "/careers" }] },
100
+ ]}
101
+ socials={[{ network: "x", href: "https://x.com/fieldline" }, { network: "linkedin", href: "https://linkedin.com/company/fieldline" }]}
102
+ newsletter={{
103
+ body: "One email a month.",
104
+ consent: "I agree to the privacy policy.",
105
+ onSubscribe: async (email) => subscribe(email),
106
+ }}
107
+ legal="© 2026 Fieldline Ltd. All rights reserved."
108
+ legalLinks={[{ label: "Privacy", href: "/privacy" }, { label: "Terms", href: "/terms" }]}
109
+ />
110
+ );
111
+ }
112
+ ```
113
+
114
+ ## What it will not do
115
+
116
+ - Change the language or currency by itself: `locales[].onChange` is
117
+ yours.
118
+ - Stick to the bottom of a short page: that is your layout's
119
+ `min-h-dvh` flex column.
120
+ - Paint itself in the primary colour: the look is the workspace
121
+ theme's.
122
+ - Draw store badges: "Get the app" is two buttons with the Apple and
123
+ Google marks.
124
+
125
+ A footer it cannot be is your own component in `components/`, built
126
+ from the kit — not an edit to this block. Its files are fixed: `npm run
127
+ check` refuses a change, and `makersclaw add footer` restores them.
128
+
129
+ ## Credits
130
+
131
+ Layouts ported from Creative Tim UI's footer blocks
132
+ (https://github.com/creativetimofficial/ui, MIT), rewritten on the
133
+ Makersclaw kit.
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "footer",
3
+ "title": "Footer",
4
+ "description": "A public site's footer: brand and tagline, link columns or one row of links, social buttons in the kit's brand marks (X, LinkedIn, GitHub, YouTube, Instagram, Facebook), and the legal line with its links; optionally a newsletter sign-up with consent (validates, saves through an async onSubscribe returning { ok }, shows the failure), a call to action, a large brand statement, language and currency selects, and a version badge. Simple, columns or centred.",
5
+ "useWhen": "The bottom of every page of a public site: a marketing site, a docs hub, a shop, a customer portal. Not for an app's own status bar or a toolbar pinned to the bottom of a screen.",
6
+ "tags": ["footer", "site footer", "links", "social", "newsletter", "subscribe", "legal", "copyright", "language", "currency", "marketing", "landing page"],
7
+ "exports": ["Footer"],
8
+ "files": ["index.js", "footer.jsx"],
9
+ "requires": [],
10
+ "frame": "bleed"
11
+ }
@@ -0,0 +1,119 @@
1
+ "use client";
2
+
3
+ // The Footer in every variant — the gallery's and the smoke test's
4
+ // surface. Not installed into apps: `makersclaw add footer` lays index.js
5
+ // and footer.jsx only. Links are caught by onNavigate and only named
6
+ // here; pick whether the next newsletter sign-up succeeds or fails.
7
+ import { useState } from "react";
8
+ import { Field, Icon, Select, ToggleGroup } from "../components/ui";
9
+ import { Footer } from "../components/blocks/footer";
10
+
11
+ function Logo() {
12
+ return (
13
+ <span className="inline-flex items-center gap-2">
14
+ <Icon name="zap" size={20} className="text-primary" />
15
+ Fieldline
16
+ </span>
17
+ );
18
+ }
19
+
20
+ const COLUMNS = [
21
+ { title: "Product", links: [{ label: "Pipeline", href: "/product/pipeline" }, { label: "Automations", href: "/product/automations" }, { label: "Reports", href: "/product/reports" }, { label: "Pricing", href: "/pricing" }] },
22
+ { title: "Company", links: [{ label: "About", href: "/about" }, { label: "Careers", href: "/careers" }, { label: "Customers", href: "/customers" }, { label: "Press", href: "/press" }] },
23
+ { title: "Resources", links: [{ label: "Docs", href: "/docs" }, { label: "Guides", href: "/guides" }, { label: "Changelog", href: "/changelog" }, { label: "Status", href: "/status" }] },
24
+ ];
25
+ const MORE = [
26
+ { title: "Solutions", links: [{ label: "Founders", href: "/for/founders" }, { label: "Sales teams", href: "/for/sales" }, { label: "Agencies", href: "/for/agencies" }] },
27
+ { title: "Support", links: [{ label: "Help centre", href: "/help" }, { label: "Contact", href: "/contact" }, { label: "Security", href: "/security" }] },
28
+ ];
29
+ const ROW = [
30
+ { label: "Product", href: "/product" },
31
+ { label: "Pricing", href: "/pricing" },
32
+ { label: "Customers", href: "/customers" },
33
+ { label: "Docs", href: "/docs" },
34
+ { label: "Contact", href: "/contact" },
35
+ ];
36
+ const SOCIALS = [
37
+ { network: "x", href: "https://x.com" },
38
+ { network: "linkedin", href: "https://linkedin.com" },
39
+ { network: "github", href: "https://github.com" },
40
+ { network: "youtube", href: "https://youtube.com" },
41
+ { network: "instagram", href: "https://instagram.com" },
42
+ { network: "facebook", href: "https://facebook.com" },
43
+ ];
44
+ const LEGAL = "© 2026 Fieldline Ltd. All rights reserved.";
45
+ const LEGAL_LINKS = [
46
+ { label: "Privacy", href: "/privacy" },
47
+ { label: "Terms", href: "/terms" },
48
+ { label: "Cookies", href: "/cookies" },
49
+ ];
50
+ const TAGLINE = "The pipeline small sales teams actually keep up to date.";
51
+ const NEWS = { title: "Subscribe", body: "One email a month: what shipped, and what we learned selling it.", consent: "I agree to the terms and the privacy policy." };
52
+ const LANGUAGE = { id: "language", label: "Language", options: [{ value: "en", label: "English" }, { value: "de", label: "Deutsch" }, { value: "pt", label: "Português" }, { value: "fr", label: "Français" }] };
53
+ const CURRENCY = { id: "currency", label: "Currency", options: [{ value: "usd", label: "USD $" }, { value: "eur", label: "EUR €" }, { value: "gbp", label: "GBP £" }] };
54
+ const COUNTRY = { id: "country", label: "Country", options: [{ value: "gb", label: "United Kingdom" }, { value: "us", label: "United States" }, { value: "pt", label: "Portugal" }] };
55
+
56
+ const BASE = { brand: <Logo />, legal: LEGAL };
57
+
58
+ const VARIANTS = [
59
+ { id: "01", label: "01 · One row, links and socials", props: { ...BASE, layout: "simple", links: ROW, socials: SOCIALS.slice(0, 4) } },
60
+ { id: "02", label: "02 · Columns and a sign-up", props: { ...BASE, columns: COLUMNS, newsletter: NEWS, legalLinks: LEGAL_LINKS } },
61
+ { id: "03", label: "03 · A call to action", props: { ...BASE, layout: "simple", links: ROW, cta: { title: "Ready when you are", body: "Your workspace is one click away.", actions: [{ label: "Sign in", href: "/login", icon: "log-in" }] } } },
62
+ { id: "04", label: "04 · Sign-up in a card", props: { ...BASE, tagline: TAGLINE, columns: COLUMNS, socials: SOCIALS.slice(0, 3), newsletter: { ...NEWS, title: "Get the monthly letter", look: "card" } } },
63
+ { id: "05", label: "05 · Language and currency", props: { ...BASE, columns: COLUMNS, locales: [LANGUAGE, CURRENCY], legalLinks: LEGAL_LINKS } },
64
+ { id: "06", label: "06 · Brand and many columns", props: { ...BASE, tagline: TAGLINE, socials: SOCIALS.slice(0, 3), columns: [...COLUMNS, ...MORE], legalLinks: LEGAL_LINKS } },
65
+ { id: "07", label: "07 · Country picker", props: { ...BASE, layout: "simple", links: ROW.slice(0, 3), locales: [COUNTRY] } },
66
+ { id: "08", label: "08 · Version badge", props: { ...BASE, layout: "simple", version: "v4.2.0", legalLinks: LEGAL_LINKS } },
67
+ { id: "09", label: "09 · A notice and a button", props: { ...BASE, layout: "simple", cta: { body: "New: forecasts now show the deals behind every number.", actions: [{ label: "Read the release", href: "/changelog", variant: "secondary" }] } } },
68
+ { id: "10", label: "10 · Page links only", props: { legal: LEGAL, layout: "simple", links: ROW } },
69
+ { id: "11", label: "11 · A large statement", props: { ...BASE, statement: "Sell like a team of ten. Stay a team of three.", tagline: TAGLINE, columns: COLUMNS } },
70
+ { id: "12", label: "12 · Navigation links", props: { ...BASE, layout: "simple", links: [...ROW, { label: "Careers", href: "/careers" }, { label: "Status", href: "/status" }], legalLinks: LEGAL_LINKS } },
71
+ { id: "13", label: "13 · Centred", props: { ...BASE, layout: "centered", tagline: TAGLINE, links: ROW, socials: SOCIALS } },
72
+ { id: "14", label: "14 · Get the app", props: { ...BASE, tagline: TAGLINE, links: ROW, cta: { title: "Get the app", body: "Your pipeline on the train, with offline notes.", actions: [{ label: "App Store", href: "/apps/ios", icon: "apple", variant: "secondary" }, { label: "Google Play", href: "/apps/android", icon: "google", variant: "secondary" }] } } },
73
+ { id: "15", label: "15 · A quote above the columns", props: { ...BASE, statement: { text: "“We stopped losing deals in a spreadsheet.”", by: "Maya Okafor, Greenhaus" }, columns: COLUMNS, socials: SOCIALS.slice(0, 4) } },
74
+ ];
75
+
76
+ const OUTCOMES = [
77
+ { value: "ok", label: "Sign-up succeeds" },
78
+ { value: "fail", label: "Sign-up fails" },
79
+ ];
80
+
81
+ export default function FooterDemo() {
82
+ const [variant, setVariant] = useState("02");
83
+ const [outcome, setOutcome] = useState("ok");
84
+ const [followed, setFollowed] = useState(null);
85
+ const v = VARIANTS.find((x) => x.id === variant) ?? VARIANTS[0];
86
+ const props = { ...v.props };
87
+ if (props.newsletter) {
88
+ props.newsletter = {
89
+ ...props.newsletter,
90
+ onSubscribe: async () => {
91
+ await new Promise((r) => setTimeout(r, 900));
92
+ return outcome === "fail" ? { ok: false, error: "The list service did not answer. Try again in a minute." } : { ok: true };
93
+ },
94
+ };
95
+ }
96
+
97
+ return (
98
+ <div className="grid gap-2">
99
+ <div className="flex flex-wrap items-end gap-3 px-4 pt-4 sm:px-6">
100
+ <Field id="f-variant" label="Variant" className="w-full sm:w-80">
101
+ <Select id="f-variant" value={variant} onChange={(e) => setVariant(e.target.value)} options={VARIANTS.map((x) => ({ value: x.id, label: x.label }))} />
102
+ </Field>
103
+ <ToggleGroup aria-label="Next sign-up" items={OUTCOMES} value={outcome} onChange={setOutcome} />
104
+ </div>
105
+ <p className="px-4 text-sm text-muted-foreground sm:px-6">
106
+ Followed: <span className="font-medium text-foreground">{followed ?? "nothing yet"}</span>
107
+ </p>
108
+ <div className="h-24" aria-hidden="true" />
109
+ <Footer
110
+ key={variant}
111
+ {...props}
112
+ onNavigate={(link, e) => {
113
+ e.preventDefault();
114
+ setFollowed(link.href);
115
+ }}
116
+ />
117
+ </div>
118
+ );
119
+ }
@@ -0,0 +1,406 @@
1
+ "use client";
2
+
3
+ import { useId, useState } from "react";
4
+ import { Badge, Button, Checkbox, Field, IconButton, Input, Notice, Select, cx } from "../../ui";
5
+
6
+ // Footer — a public site's footer: the brand and a line about it, link
7
+ // columns (or one row of links), social buttons drawn with the kit's
8
+ // brand marks, and the legal line; optionally a newsletter sign-up (with
9
+ // consent), a call to action, a large brand statement, language or
10
+ // currency pickers, and a version badge. Three layouts: simple (one
11
+ // row), columns, and centred. The sign-up validates the email and saves
12
+ // through `newsletter.onSubscribe(email, extra)` — async, returning
13
+ // { ok } — showing the failure or a subscribed state.
14
+ //
15
+ // <Footer brand={<Logo />} columns={COLUMNS} socials={SOCIALS}
16
+ // legal="© 2026 Fieldline Ltd." />
17
+ //
18
+ // README.md beside this file lists every prop.
19
+
20
+ const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
21
+
22
+ // A network's name and its brand glyph.
23
+ const NETWORKS = {
24
+ x: { label: "X", icon: "x-logo" },
25
+ linkedin: { label: "LinkedIn", icon: "linkedin" },
26
+ instagram: { label: "Instagram", icon: "instagram" },
27
+ youtube: { label: "YouTube", icon: "youtube" },
28
+ facebook: { label: "Facebook", icon: "facebook" },
29
+ github: { label: "GitHub", icon: "github" },
30
+ google: { label: "Google", icon: "google" },
31
+ apple: { label: "Apple", icon: "apple" },
32
+ microsoft: { label: "Microsoft", icon: "microsoft" },
33
+ email: { label: "Email", icon: "mail" },
34
+ rss: { label: "RSS", icon: "globe" },
35
+ };
36
+
37
+ const COLUMNS = {
38
+ 1: "grid-cols-1",
39
+ 2: "grid-cols-2",
40
+ 3: "grid-cols-2 sm:grid-cols-3",
41
+ 4: "grid-cols-2 sm:grid-cols-4",
42
+ 5: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5",
43
+ };
44
+
45
+ function FooterLink({ link, onNavigate }) {
46
+ return (
47
+ <Button
48
+ variant="link"
49
+ tone="muted"
50
+ href={link.href}
51
+ onClick={(e) => onNavigate?.(link, e)}
52
+ target={link.external ? "_blank" : undefined}
53
+ rel={link.external ? "noreferrer" : undefined}
54
+ >
55
+ {link.label}
56
+ </Button>
57
+ );
58
+ }
59
+
60
+ function LinkRow({ links, onNavigate, center, label }) {
61
+ if (!links?.length) return null;
62
+ return (
63
+ <nav aria-label={label}>
64
+ <ul className={cx("flex flex-wrap gap-x-6 gap-y-3", center && "justify-center")}>
65
+ {links.map((l, i) => (
66
+ <li key={l.href ?? l.label ?? i}>
67
+ <FooterLink link={l} onNavigate={onNavigate} />
68
+ </li>
69
+ ))}
70
+ </ul>
71
+ </nav>
72
+ );
73
+ }
74
+
75
+ function Columns({ columns, onNavigate }) {
76
+ if (!columns?.length) return null;
77
+ const n = Math.min(columns.length, 5);
78
+ return (
79
+ <div className={cx("grid min-w-0 gap-x-6 gap-y-10", COLUMNS[n])}>
80
+ {columns.map((col, i) => (
81
+ <nav key={col.title ?? i} aria-label={col.title} className="min-w-0">
82
+ {col.title ? <h3 className="mb-4 text-sm font-semibold">{col.title}</h3> : null}
83
+ <ul className="grid gap-3">
84
+ {(col.links ?? []).map((l, k) => (
85
+ <li key={l.href ?? l.label ?? k} className="min-w-0">
86
+ <FooterLink link={l} onNavigate={onNavigate} />
87
+ </li>
88
+ ))}
89
+ </ul>
90
+ </nav>
91
+ ))}
92
+ </div>
93
+ );
94
+ }
95
+
96
+ function Socials({ socials, center }) {
97
+ if (!socials?.length) return null;
98
+ return (
99
+ <ul aria-label="Social links" className={cx("flex flex-wrap items-center gap-1", center && "justify-center")}>
100
+ {socials.map((s, i) => {
101
+ const net = NETWORKS[s.network] ?? { label: s.label ?? s.network, icon: s.icon ?? "link" };
102
+ return (
103
+ <li key={s.href ?? i}>
104
+ <IconButton
105
+ icon={s.icon ?? net.icon}
106
+ label={s.label ?? net.label}
107
+ href={s.href}
108
+ target="_blank"
109
+ rel="noreferrer"
110
+ />
111
+ </li>
112
+ );
113
+ })}
114
+ </ul>
115
+ );
116
+ }
117
+
118
+ function Brand({ brand, tagline, center }) {
119
+ if (!brand && !tagline) return null;
120
+ return (
121
+ <div className={cx("grid min-w-0 content-start gap-3", center && "justify-items-center text-center")}>
122
+ {brand ? <div className="text-lg font-semibold">{brand}</div> : null}
123
+ {tagline ? <p className="max-w-sm text-pretty text-sm text-muted-foreground">{tagline}</p> : null}
124
+ </div>
125
+ );
126
+ }
127
+
128
+ function Locales({ locales }) {
129
+ const [own, setOwn] = useState(() => Object.fromEntries((locales ?? []).map((l) => [l.id, l.defaultValue ?? l.options?.[0]?.value ?? ""])));
130
+ const uid = useId();
131
+ if (!locales?.length) return null;
132
+ return (
133
+ <div className="flex flex-wrap gap-3">
134
+ {locales.map((l) => {
135
+ const value = l.value ?? own[l.id];
136
+ const fid = `${uid}-${l.id}`;
137
+ return (
138
+ <Field key={l.id} id={fid} className="w-full min-w-0 sm:w-44">
139
+ <label htmlFor={fid} className="sr-only">
140
+ {l.label}
141
+ </label>
142
+ <Select
143
+ id={fid}
144
+ value={value}
145
+ options={l.options}
146
+ onChange={(e) => {
147
+ const v = e.target.value;
148
+ if (l.value === undefined) setOwn((prev) => ({ ...prev, [l.id]: v }));
149
+ l.onChange?.(v);
150
+ }}
151
+ />
152
+ </Field>
153
+ );
154
+ })}
155
+ </div>
156
+ );
157
+ }
158
+
159
+ function Newsletter({ newsletter, center }) {
160
+ const uid = useId();
161
+ const [email, setEmail] = useState("");
162
+ const [agreed, setAgreed] = useState(false);
163
+ const [error, setError] = useState(null);
164
+ const [status, setStatus] = useState("idle");
165
+ const [failure, setFailure] = useState(null);
166
+ const card = newsletter.look === "card";
167
+
168
+ async function submit(ev) {
169
+ ev.preventDefault();
170
+ if (status === "sending") return;
171
+ const value = email.trim();
172
+ let problem = null;
173
+ if (!value) problem = "Enter your email.";
174
+ else if (!EMAIL.test(value)) problem = "That email does not look right.";
175
+ else if (newsletter.consent && !agreed) problem = "Please agree before subscribing.";
176
+ setError(problem);
177
+ if (problem) {
178
+ document.getElementById(problem.startsWith("Please") ? `${uid}-consent` : `${uid}-email`)?.focus();
179
+ return;
180
+ }
181
+ setStatus("sending");
182
+ setFailure(null);
183
+ let res;
184
+ try {
185
+ res = await newsletter.onSubscribe(value, { consent: newsletter.consent ? agreed : undefined });
186
+ } catch (err) {
187
+ res = { ok: false, error: err?.message };
188
+ }
189
+ if (res?.ok) setStatus("done");
190
+ else {
191
+ setStatus("idle");
192
+ setFailure(res?.error || "You were not subscribed. Try again in a minute.");
193
+ }
194
+ }
195
+
196
+ return (
197
+ <div
198
+ className={cx(
199
+ "grid min-w-0 content-start gap-3",
200
+ card && "rounded-xl border border-border bg-card p-6 text-card-foreground sm:p-8 md:grid-cols-2 md:items-center md:gap-8",
201
+ center && !card && "justify-items-center text-center",
202
+ )}
203
+ >
204
+ <div className="grid gap-1">
205
+ <h3 className={cx("font-semibold", card ? "text-lg" : "text-sm")}>{newsletter.title ?? "Subscribe"}</h3>
206
+ {newsletter.body ? <p className="max-w-sm text-pretty text-sm text-muted-foreground">{newsletter.body}</p> : null}
207
+ </div>
208
+ {status === "done" ? (
209
+ <Notice tone="success" title={newsletter.doneTitle ?? "You're subscribed"}>
210
+ {newsletter.doneBody ?? `We'll write to ${email.trim()}.`}
211
+ </Notice>
212
+ ) : (
213
+ <form noValidate onSubmit={submit} className="grid w-full max-w-md gap-2" aria-busy={status === "sending" || undefined}>
214
+ <div className="flex flex-col gap-2 sm:flex-row sm:items-start">
215
+ <Field id={`${uid}-email`} className="min-w-0 flex-1" error={error && !error.startsWith("Please") ? error : undefined}>
216
+ <label htmlFor={`${uid}-email`} className="sr-only">
217
+ Email
218
+ </label>
219
+ <Input
220
+ id={`${uid}-email`}
221
+ type="email"
222
+ inputMode="email"
223
+ autoComplete="email"
224
+ placeholder={newsletter.placeholder ?? "you@company.com"}
225
+ value={email}
226
+ onChange={(e) => {
227
+ setEmail(e.target.value);
228
+ if (error) setError(null);
229
+ }}
230
+ invalid={!!error && !error.startsWith("Please")}
231
+ aria-describedby={error && !error.startsWith("Please") ? `${uid}-email-error` : undefined}
232
+ />
233
+ </Field>
234
+ <Button type="submit" variant="primary" loading={status === "sending"} className="shrink-0">
235
+ {newsletter.label ?? "Subscribe"}
236
+ </Button>
237
+ </div>
238
+ {newsletter.consent ? (
239
+ <div className="grid gap-1 text-left">
240
+ <Checkbox
241
+ id={`${uid}-consent`}
242
+ checked={agreed}
243
+ onChange={(e) => {
244
+ setAgreed(e.target.checked);
245
+ if (error) setError(null);
246
+ }}
247
+ aria-invalid={error?.startsWith("Please") ? true : undefined}
248
+ label={newsletter.consent}
249
+ className="text-muted-foreground"
250
+ />
251
+ {error?.startsWith("Please") ? (
252
+ <p role="alert" className="text-xs text-destructive">
253
+ {error}
254
+ </p>
255
+ ) : null}
256
+ </div>
257
+ ) : null}
258
+ {failure ? (
259
+ <Notice tone="danger" title="Not subscribed">
260
+ {failure}
261
+ </Notice>
262
+ ) : null}
263
+ </form>
264
+ )}
265
+ </div>
266
+ );
267
+ }
268
+
269
+ function Cta({ cta, center, onNavigate }) {
270
+ if (!cta) return null;
271
+ return (
272
+ <div className={cx("flex flex-col gap-4 md:flex-row md:items-center md:justify-between", center && "items-center text-center md:flex-col")}>
273
+ <div className="grid min-w-0 gap-1">
274
+ {cta.title ? <h3 className="text-balance text-2xl font-semibold tracking-tight">{cta.title}</h3> : null}
275
+ {cta.body ? <p className="max-w-xl text-pretty text-muted-foreground">{cta.body}</p> : null}
276
+ </div>
277
+ {cta.actions?.length ? (
278
+ <div className={cx("flex flex-wrap gap-3", center && "justify-center")}>
279
+ {cta.actions.map((a, i) => (
280
+ <Button
281
+ key={a.label ?? i}
282
+ variant={a.variant ?? (i === 0 ? "primary" : "secondary")}
283
+ icon={a.icon}
284
+ href={a.href}
285
+ onClick={(e) => {
286
+ a.onClick?.(e);
287
+ if (a.href) onNavigate?.(a, e);
288
+ }}
289
+ >
290
+ {a.label}
291
+ </Button>
292
+ ))}
293
+ </div>
294
+ ) : null}
295
+ </div>
296
+ );
297
+ }
298
+
299
+ function Statement({ statement, center }) {
300
+ if (!statement) return null;
301
+ const text = typeof statement === "string" ? statement : statement.text;
302
+ const by = typeof statement === "string" ? null : statement.by;
303
+ return (
304
+ <figure className={cx("grid gap-3 border-b border-border pb-10", center && "justify-items-center text-center")}>
305
+ <blockquote className="max-w-4xl text-balance text-3xl font-semibold tracking-tight sm:text-5xl">{text}</blockquote>
306
+ {by ? <figcaption className="text-muted-foreground">{by}</figcaption> : null}
307
+ </figure>
308
+ );
309
+ }
310
+
311
+ function Bottom({ legal, legalLinks, version, locales, onNavigate, center }) {
312
+ if (!legal && !legalLinks?.length && !version && !locales?.length) return null;
313
+ return (
314
+ <div
315
+ className={cx(
316
+ "flex flex-col gap-4 border-t border-border pt-8 text-sm md:flex-row md:items-center md:justify-between",
317
+ center && "items-center text-center md:flex-col",
318
+ )}
319
+ >
320
+ <div className={cx("flex min-w-0 flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:gap-x-6", center && "items-center sm:justify-center")}>
321
+ {legal ? <p className="text-muted-foreground">{legal}</p> : null}
322
+ <LinkRow links={legalLinks} onNavigate={onNavigate} center={center} label="Legal" />
323
+ </div>
324
+ <div className={cx("flex flex-wrap items-center gap-3", center && "justify-center")}>
325
+ {version ? <Badge variant="code">{version}</Badge> : null}
326
+ <Locales locales={locales} />
327
+ </div>
328
+ </div>
329
+ );
330
+ }
331
+
332
+ export function Footer({
333
+ layout = "columns",
334
+ brand,
335
+ tagline,
336
+ links,
337
+ columns,
338
+ socials,
339
+ newsletter,
340
+ cta,
341
+ statement,
342
+ locales,
343
+ version,
344
+ legal,
345
+ legalLinks,
346
+ onNavigate,
347
+ label = "Site",
348
+ className,
349
+ }) {
350
+ const centered = layout === "centered";
351
+ const news = newsletter?.onSubscribe ? newsletter : null;
352
+ const newsCard = news?.look === "card";
353
+
354
+ let main;
355
+ if (centered) {
356
+ main = (
357
+ <div className="grid justify-items-center gap-6 text-center">
358
+ <Brand brand={brand} tagline={tagline} center />
359
+ <LinkRow links={links} onNavigate={onNavigate} center label={label} />
360
+ <Socials socials={socials} center />
361
+ {news && !newsCard ? <Newsletter newsletter={news} center /> : null}
362
+ </div>
363
+ );
364
+ } else if (layout === "simple") {
365
+ main = (
366
+ <div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
367
+ <div className="flex min-w-0 flex-col gap-6 md:flex-row md:items-center md:gap-10">
368
+ {brand ? <div className="shrink-0 text-lg font-semibold">{brand}</div> : null}
369
+ <LinkRow links={links} onNavigate={onNavigate} label={label} />
370
+ </div>
371
+ <Socials socials={socials} />
372
+ </div>
373
+ );
374
+ } else {
375
+ main = (
376
+ <div className={cx("grid gap-12", news && !newsCard ? "lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)_minmax(0,2fr)]" : "lg:grid-cols-[minmax(0,2fr)_minmax(0,5fr)]")}>
377
+ <div className="grid content-start gap-5">
378
+ <Brand brand={brand} tagline={tagline} />
379
+ <Socials socials={socials} />
380
+ <LinkRow links={links} onNavigate={onNavigate} label={label} />
381
+ </div>
382
+ <Columns columns={columns} onNavigate={onNavigate} />
383
+ {news && !newsCard ? <Newsletter newsletter={news} /> : null}
384
+ </div>
385
+ );
386
+ }
387
+
388
+ return (
389
+ <footer aria-label={label} className={cx("border-t border-border px-4 pt-14 pb-8 sm:px-6 sm:pt-16", className)}>
390
+ <div className="mx-auto grid w-full max-w-6xl gap-10">
391
+ <Statement statement={statement} center={centered} />
392
+ {main}
393
+ {newsCard ? <Newsletter newsletter={news} /> : null}
394
+ <Cta cta={cta} center={centered} onNavigate={onNavigate} />
395
+ <Bottom
396
+ legal={legal}
397
+ legalLinks={legalLinks}
398
+ version={version}
399
+ locales={locales}
400
+ onNavigate={onNavigate}
401
+ center={centered}
402
+ />
403
+ </div>
404
+ </footer>
405
+ );
406
+ }
@@ -0,0 +1,4 @@
1
+ // The Footer block — `makersclaw add footer`. Fixed: compose it through
2
+ // its props (README.md beside this file); a footer it cannot be is your
3
+ // own component in components/.
4
+ export { Footer } from "./footer";