makersclaw 0.1.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 (478) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +41 -1
  3. package/THIRD-PARTY-NOTICES.md +180 -0
  4. package/package.json +15 -8
  5. package/r/annotate.json +62 -0
  6. package/r/approval-request.json +52 -0
  7. package/r/audio-player.json +74 -0
  8. package/r/auth-form.json +55 -0
  9. package/r/board.json +55 -0
  10. package/r/calendar-view.json +105 -0
  11. package/r/cart.json +62 -0
  12. package/r/chart.json +83 -0
  13. package/r/chat.json +95 -0
  14. package/r/code-editor.json +94 -0
  15. package/r/comment-box.json +64 -0
  16. package/r/contact-section.json +53 -0
  17. package/r/core.json +341 -0
  18. package/r/csv-import.json +63 -0
  19. package/r/data-table.json +69 -0
  20. package/r/diagram.json +74 -0
  21. package/r/doc-editor.json +82 -0
  22. package/r/doc-page.json +61 -0
  23. package/r/email-render.json +73 -0
  24. package/r/faq.json +50 -0
  25. package/r/file-upload.json +54 -0
  26. package/r/flow-canvas.json +93 -0
  27. package/r/footer.json +52 -0
  28. package/r/gantt.json +70 -0
  29. package/r/job-panel.json +56 -0
  30. package/r/json-editor.json +66 -0
  31. package/r/map.json +89 -0
  32. package/r/menubar.json +52 -0
  33. package/r/mind-map.json +67 -0
  34. package/r/navigation-menu.json +52 -0
  35. package/r/order-detail.json +54 -0
  36. package/r/org-chart.json +66 -0
  37. package/r/page-builder.json +105 -0
  38. package/r/payment-methods.json +51 -0
  39. package/r/pdf-document.json +79 -0
  40. package/r/pdf-viewer.json +88 -0
  41. package/r/product-detail.json +76 -0
  42. package/r/product-grid.json +65 -0
  43. package/r/record-form.json +67 -0
  44. package/r/record-notes.json +55 -0
  45. package/r/registry.json +3612 -0
  46. package/r/search-dialog.json +51 -0
  47. package/r/settings-list.json +66 -0
  48. package/r/setup-checklist.json +51 -0
  49. package/r/share-dialog.json +52 -0
  50. package/r/sidebar.json +69 -0
  51. package/r/signature-pad.json +72 -0
  52. package/r/sketch-canvas.json +99 -0
  53. package/r/spreadsheet.json +90 -0
  54. package/r/stat-cards.json +66 -0
  55. package/r/task-list.json +63 -0
  56. package/r/task-model.json +98 -0
  57. package/r/testimonials.json +52 -0
  58. package/r/transaction-list.json +53 -0
  59. package/r/whiteboard.json +66 -0
  60. package/registry/blocks/annotate/README.md +179 -0
  61. package/registry/blocks/annotate/annotate-data.js +250 -0
  62. package/registry/blocks/annotate/annotate.jsx +646 -0
  63. package/registry/blocks/annotate/block.json +10 -0
  64. package/registry/blocks/annotate/demo.jsx +135 -0
  65. package/registry/blocks/annotate/index.js +15 -0
  66. package/registry/blocks/approval-request/README.md +116 -0
  67. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  68. package/registry/blocks/approval-request/block.json +10 -0
  69. package/registry/blocks/approval-request/demo.jsx +127 -0
  70. package/registry/blocks/approval-request/index.js +4 -0
  71. package/registry/blocks/audio-player/README.md +167 -0
  72. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  73. package/registry/blocks/audio-player/block.json +14 -0
  74. package/registry/blocks/audio-player/colors.js +109 -0
  75. package/registry/blocks/audio-player/demo.jsx +209 -0
  76. package/registry/blocks/audio-player/index.js +6 -0
  77. package/registry/blocks/audio-player/time.js +67 -0
  78. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  79. package/registry/blocks/auth-form/README.md +126 -0
  80. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  81. package/registry/blocks/auth-form/block.json +10 -0
  82. package/registry/blocks/auth-form/demo.jsx +131 -0
  83. package/registry/blocks/auth-form/index.js +3 -0
  84. package/registry/blocks/board/README.md +64 -13
  85. package/registry/blocks/board/block.json +2 -2
  86. package/registry/blocks/board/board.jsx +237 -94
  87. package/registry/blocks/board/demo.jsx +103 -24
  88. package/registry/blocks/calendar-view/README.md +180 -0
  89. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  90. package/registry/blocks/calendar-view/block.json +76 -0
  91. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  92. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  93. package/registry/blocks/calendar-view/demo.jsx +195 -0
  94. package/registry/blocks/calendar-view/draft.jsx +49 -0
  95. package/registry/blocks/calendar-view/index.js +39 -0
  96. package/registry/blocks/calendar-view/month.jsx +346 -0
  97. package/registry/blocks/calendar-view/paint.js +69 -0
  98. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  99. package/registry/blocks/calendar-view/tray.jsx +87 -0
  100. package/registry/blocks/cart/README.md +146 -0
  101. package/registry/blocks/cart/block.json +10 -0
  102. package/registry/blocks/cart/cart.jsx +597 -0
  103. package/registry/blocks/cart/demo.jsx +98 -0
  104. package/registry/blocks/cart/index.js +4 -0
  105. package/registry/blocks/cart/money.js +28 -0
  106. package/registry/blocks/chart/README.md +316 -0
  107. package/registry/blocks/chart/block.json +10 -0
  108. package/registry/blocks/chart/cartesian.jsx +642 -0
  109. package/registry/blocks/chart/core.jsx +262 -0
  110. package/registry/blocks/chart/demo.jsx +357 -0
  111. package/registry/blocks/chart/heatmap.jsx +336 -0
  112. package/registry/blocks/chart/index.js +8 -0
  113. package/registry/blocks/chart/pie.jsx +245 -0
  114. package/registry/blocks/chart/sparkline.jsx +88 -0
  115. package/registry/blocks/chat/README.md +273 -0
  116. package/registry/blocks/chat/attachment.jsx +95 -0
  117. package/registry/blocks/chat/block.json +10 -0
  118. package/registry/blocks/chat/chat.jsx +132 -0
  119. package/registry/blocks/chat/composer.jsx +364 -0
  120. package/registry/blocks/chat/demo.jsx +578 -0
  121. package/registry/blocks/chat/header.jsx +34 -0
  122. package/registry/blocks/chat/index.js +9 -0
  123. package/registry/blocks/chat/message.jsx +450 -0
  124. package/registry/blocks/chat/scroller.jsx +247 -0
  125. package/registry/blocks/chat/text.jsx +104 -0
  126. package/registry/blocks/code-editor/README.md +210 -0
  127. package/registry/blocks/code-editor/block.json +22 -0
  128. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  129. package/registry/blocks/code-editor/codemirror.js +38 -0
  130. package/registry/blocks/code-editor/demo.jsx +205 -0
  131. package/registry/blocks/code-editor/index.js +8 -0
  132. package/registry/blocks/code-editor/json.js +317 -0
  133. package/registry/blocks/code-editor/sql.js +136 -0
  134. package/registry/blocks/code-editor/text.js +101 -0
  135. package/registry/blocks/code-editor/theme.js +186 -0
  136. package/registry/blocks/comment-box/README.md +123 -0
  137. package/registry/blocks/comment-box/block.json +10 -0
  138. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  139. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  140. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  141. package/registry/blocks/comment-box/demo.jsx +141 -0
  142. package/registry/blocks/comment-box/index.js +8 -0
  143. package/registry/blocks/contact-section/README.md +152 -0
  144. package/registry/blocks/contact-section/block.json +11 -0
  145. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  146. package/registry/blocks/contact-section/demo.jsx +93 -0
  147. package/registry/blocks/contact-section/index.js +4 -0
  148. package/registry/blocks/csv-import/README.md +133 -0
  149. package/registry/blocks/csv-import/block.json +13 -0
  150. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  151. package/registry/blocks/csv-import/demo.jsx +106 -0
  152. package/registry/blocks/csv-import/import.js +200 -0
  153. package/registry/blocks/csv-import/index.js +7 -0
  154. package/registry/blocks/data-table/README.md +386 -0
  155. package/registry/blocks/data-table/block.json +10 -0
  156. package/registry/blocks/data-table/data-table.jsx +1164 -0
  157. package/registry/blocks/data-table/demo.jsx +588 -0
  158. package/registry/blocks/data-table/index.js +5 -0
  159. package/registry/blocks/data-table/model.js +169 -0
  160. package/registry/blocks/diagram/README.md +108 -0
  161. package/registry/blocks/diagram/block.json +13 -0
  162. package/registry/blocks/diagram/demo.jsx +113 -0
  163. package/registry/blocks/diagram/diagram.jsx +481 -0
  164. package/registry/blocks/diagram/index.js +7 -0
  165. package/registry/blocks/diagram/mermaid.js +52 -0
  166. package/registry/blocks/diagram/palette.js +313 -0
  167. package/registry/blocks/diagram/source.js +98 -0
  168. package/registry/blocks/doc-editor/README.md +215 -0
  169. package/registry/blocks/doc-editor/block.json +15 -0
  170. package/registry/blocks/doc-editor/demo.jsx +225 -0
  171. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  172. package/registry/blocks/doc-editor/doc.js +309 -0
  173. package/registry/blocks/doc-editor/index.js +7 -0
  174. package/registry/blocks/doc-editor/schema.jsx +56 -0
  175. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  176. package/registry/blocks/doc-editor/theme.js +82 -0
  177. package/registry/blocks/doc-page/README.md +119 -0
  178. package/registry/blocks/doc-page/autosave.js +104 -0
  179. package/registry/blocks/doc-page/block.json +10 -0
  180. package/registry/blocks/doc-page/demo.jsx +137 -0
  181. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  182. package/registry/blocks/doc-page/index.js +4 -0
  183. package/registry/blocks/email-render/README.md +151 -0
  184. package/registry/blocks/email-render/block.json +14 -0
  185. package/registry/blocks/email-render/demo.jsx +153 -0
  186. package/registry/blocks/email-render/doc-email.jsx +210 -0
  187. package/registry/blocks/email-render/email.jsx +221 -0
  188. package/registry/blocks/email-render/index.js +10 -0
  189. package/registry/blocks/email-render/render.js +50 -0
  190. package/registry/blocks/email-render/theme.js +215 -0
  191. package/registry/blocks/faq/README.md +101 -0
  192. package/registry/blocks/faq/block.json +11 -0
  193. package/registry/blocks/faq/demo.jsx +89 -0
  194. package/registry/blocks/faq/faq.jsx +220 -0
  195. package/registry/blocks/faq/index.js +4 -0
  196. package/registry/blocks/file-upload/README.md +138 -0
  197. package/registry/blocks/file-upload/block.json +10 -0
  198. package/registry/blocks/file-upload/demo.jsx +204 -0
  199. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  200. package/registry/blocks/file-upload/index.js +4 -0
  201. package/registry/blocks/flow-canvas/README.md +420 -0
  202. package/registry/blocks/flow-canvas/block.json +13 -0
  203. package/registry/blocks/flow-canvas/board.js +819 -0
  204. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  205. package/registry/blocks/flow-canvas/context.jsx +35 -0
  206. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  207. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  208. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  209. package/registry/blocks/flow-canvas/index.js +41 -0
  210. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  211. package/registry/blocks/flow-canvas/theme.js +174 -0
  212. package/registry/blocks/footer/README.md +133 -0
  213. package/registry/blocks/footer/block.json +11 -0
  214. package/registry/blocks/footer/demo.jsx +119 -0
  215. package/registry/blocks/footer/footer.jsx +406 -0
  216. package/registry/blocks/footer/index.js +4 -0
  217. package/registry/blocks/gantt/README.md +220 -0
  218. package/registry/blocks/gantt/block.json +10 -0
  219. package/registry/blocks/gantt/demo.jsx +148 -0
  220. package/registry/blocks/gantt/gantt.jsx +923 -0
  221. package/registry/blocks/gantt/index.js +7 -0
  222. package/registry/blocks/gantt/rows.jsx +468 -0
  223. package/registry/blocks/gantt/schedule.js +278 -0
  224. package/registry/blocks/job-panel/README.md +86 -2
  225. package/registry/blocks/job-panel/block.json +3 -3
  226. package/registry/blocks/job-panel/demo.jsx +230 -3
  227. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  228. package/registry/blocks/json-editor/README.md +133 -0
  229. package/registry/blocks/json-editor/block.json +10 -0
  230. package/registry/blocks/json-editor/demo.jsx +92 -0
  231. package/registry/blocks/json-editor/index.js +7 -0
  232. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  233. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  234. package/registry/blocks/json-editor/tree.js +225 -0
  235. package/registry/blocks/map/README.md +221 -0
  236. package/registry/blocks/map/block.json +14 -0
  237. package/registry/blocks/map/demo.jsx +201 -0
  238. package/registry/blocks/map/geo.js +198 -0
  239. package/registry/blocks/map/index.js +8 -0
  240. package/registry/blocks/map/list.jsx +128 -0
  241. package/registry/blocks/map/map-view.jsx +715 -0
  242. package/registry/blocks/map/maplibre.js +30 -0
  243. package/registry/blocks/map/marks.jsx +146 -0
  244. package/registry/blocks/map/theme.js +142 -0
  245. package/registry/blocks/menubar/README.md +109 -0
  246. package/registry/blocks/menubar/block.json +10 -0
  247. package/registry/blocks/menubar/demo.jsx +128 -0
  248. package/registry/blocks/menubar/index.js +4 -0
  249. package/registry/blocks/menubar/menubar.jsx +199 -0
  250. package/registry/blocks/mind-map/README.md +206 -0
  251. package/registry/blocks/mind-map/block.json +10 -0
  252. package/registry/blocks/mind-map/demo.jsx +103 -0
  253. package/registry/blocks/mind-map/index.js +31 -0
  254. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  255. package/registry/blocks/mind-map/topic.jsx +143 -0
  256. package/registry/blocks/mind-map/tree.js +645 -0
  257. package/registry/blocks/navigation-menu/README.md +124 -0
  258. package/registry/blocks/navigation-menu/block.json +10 -0
  259. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  260. package/registry/blocks/navigation-menu/index.js +4 -0
  261. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  262. package/registry/blocks/order-detail/README.md +109 -0
  263. package/registry/blocks/order-detail/block.json +10 -0
  264. package/registry/blocks/order-detail/demo.jsx +88 -0
  265. package/registry/blocks/order-detail/index.js +4 -0
  266. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  267. package/registry/blocks/org-chart/README.md +145 -0
  268. package/registry/blocks/org-chart/block.json +10 -0
  269. package/registry/blocks/org-chart/demo.jsx +94 -0
  270. package/registry/blocks/org-chart/index.js +7 -0
  271. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  272. package/registry/blocks/org-chart/org.js +245 -0
  273. package/registry/blocks/org-chart/person.jsx +96 -0
  274. package/registry/blocks/page-builder/README.md +257 -0
  275. package/registry/blocks/page-builder/block.json +14 -0
  276. package/registry/blocks/page-builder/demo.jsx +145 -0
  277. package/registry/blocks/page-builder/grapes.js +18 -0
  278. package/registry/blocks/page-builder/index.js +11 -0
  279. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  280. package/registry/blocks/page-builder/page-view.jsx +33 -0
  281. package/registry/blocks/page-builder/page.js +118 -0
  282. package/registry/blocks/page-builder/panels.jsx +588 -0
  283. package/registry/blocks/page-builder/sections.js +209 -0
  284. package/registry/blocks/page-builder/theme.js +87 -0
  285. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  286. package/registry/blocks/page-builder/tokens.js +329 -0
  287. package/registry/blocks/payment-methods/README.md +104 -0
  288. package/registry/blocks/payment-methods/block.json +10 -0
  289. package/registry/blocks/payment-methods/demo.jsx +71 -0
  290. package/registry/blocks/payment-methods/index.js +4 -0
  291. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  292. package/registry/blocks/pdf-document/README.md +213 -0
  293. package/registry/blocks/pdf-document/block.json +13 -0
  294. package/registry/blocks/pdf-document/demo.jsx +136 -0
  295. package/registry/blocks/pdf-document/index.js +33 -0
  296. package/registry/blocks/pdf-document/money.js +83 -0
  297. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  298. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  299. package/registry/blocks/pdf-document/quote.jsx +179 -0
  300. package/registry/blocks/pdf-document/theme.js +193 -0
  301. package/registry/blocks/pdf-viewer/README.md +191 -0
  302. package/registry/blocks/pdf-viewer/block.json +14 -0
  303. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  304. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  305. package/registry/blocks/pdf-viewer/index.js +8 -0
  306. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  307. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  308. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  309. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  310. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  311. package/registry/blocks/product-detail/README.md +217 -0
  312. package/registry/blocks/product-detail/block.json +10 -0
  313. package/registry/blocks/product-detail/demo.jsx +233 -0
  314. package/registry/blocks/product-detail/index.js +7 -0
  315. package/registry/blocks/product-detail/price.jsx +115 -0
  316. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  317. package/registry/blocks/product-detail/product-media.jsx +102 -0
  318. package/registry/blocks/product-detail/product-options.jsx +144 -0
  319. package/registry/blocks/product-grid/README.md +162 -0
  320. package/registry/blocks/product-grid/block.json +10 -0
  321. package/registry/blocks/product-grid/demo.jsx +255 -0
  322. package/registry/blocks/product-grid/index.js +5 -0
  323. package/registry/blocks/product-grid/product-card.jsx +232 -0
  324. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  325. package/registry/blocks/record-form/README.md +196 -0
  326. package/registry/blocks/record-form/block.json +10 -0
  327. package/registry/blocks/record-form/demo.jsx +525 -0
  328. package/registry/blocks/record-form/index.js +4 -0
  329. package/registry/blocks/record-form/record-fields.jsx +457 -0
  330. package/registry/blocks/record-form/record-form.jsx +415 -0
  331. package/registry/blocks/record-form/rules.js +81 -0
  332. package/registry/blocks/record-notes/README.md +139 -0
  333. package/registry/blocks/record-notes/block.json +10 -0
  334. package/registry/blocks/record-notes/demo.jsx +179 -0
  335. package/registry/blocks/record-notes/index.js +4 -0
  336. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  337. package/registry/blocks/search-dialog/README.md +108 -0
  338. package/registry/blocks/search-dialog/block.json +10 -0
  339. package/registry/blocks/search-dialog/demo.jsx +116 -0
  340. package/registry/blocks/search-dialog/index.js +3 -0
  341. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  342. package/registry/blocks/settings-list/README.md +171 -0
  343. package/registry/blocks/settings-list/block.json +10 -0
  344. package/registry/blocks/settings-list/demo.jsx +333 -0
  345. package/registry/blocks/settings-list/index.js +4 -0
  346. package/registry/blocks/settings-list/row-state.jsx +93 -0
  347. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  348. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  349. package/registry/blocks/setup-checklist/README.md +142 -0
  350. package/registry/blocks/setup-checklist/block.json +10 -0
  351. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  352. package/registry/blocks/setup-checklist/index.js +4 -0
  353. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  354. package/registry/blocks/share-dialog/README.md +113 -0
  355. package/registry/blocks/share-dialog/block.json +10 -0
  356. package/registry/blocks/share-dialog/demo.jsx +140 -0
  357. package/registry/blocks/share-dialog/index.js +3 -0
  358. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  359. package/registry/blocks/sidebar/README.md +190 -0
  360. package/registry/blocks/sidebar/block.json +43 -0
  361. package/registry/blocks/sidebar/demo.jsx +577 -0
  362. package/registry/blocks/sidebar/index.js +4 -0
  363. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  364. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  365. package/registry/blocks/signature-pad/README.md +137 -0
  366. package/registry/blocks/signature-pad/block.json +13 -0
  367. package/registry/blocks/signature-pad/colors.js +115 -0
  368. package/registry/blocks/signature-pad/demo.jsx +94 -0
  369. package/registry/blocks/signature-pad/index.js +6 -0
  370. package/registry/blocks/signature-pad/pad.js +7 -0
  371. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  372. package/registry/blocks/signature-pad/signature.js +68 -0
  373. package/registry/blocks/sketch-canvas/README.md +280 -0
  374. package/registry/blocks/sketch-canvas/block.json +13 -0
  375. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  376. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  377. package/registry/blocks/sketch-canvas/index.js +9 -0
  378. package/registry/blocks/sketch-canvas/palette.js +226 -0
  379. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  380. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  381. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  382. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  383. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  384. package/registry/blocks/sketch-canvas/theme.js +131 -0
  385. package/registry/blocks/spreadsheet/README.md +307 -0
  386. package/registry/blocks/spreadsheet/block.json +13 -0
  387. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  388. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  389. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  390. package/registry/blocks/spreadsheet/grid.js +387 -0
  391. package/registry/blocks/spreadsheet/index.js +34 -0
  392. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  393. package/registry/blocks/spreadsheet/types.js +473 -0
  394. package/registry/blocks/stat-cards/README.md +173 -0
  395. package/registry/blocks/stat-cards/block.json +10 -0
  396. package/registry/blocks/stat-cards/demo.jsx +257 -0
  397. package/registry/blocks/stat-cards/index.js +4 -0
  398. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  399. package/registry/blocks/stat-cards/usage.jsx +243 -0
  400. package/registry/blocks/task-list/README.md +176 -0
  401. package/registry/blocks/task-list/block.json +10 -0
  402. package/registry/blocks/task-list/demo.jsx +134 -0
  403. package/registry/blocks/task-list/index.js +5 -0
  404. package/registry/blocks/task-list/task-list.jsx +907 -0
  405. package/registry/blocks/task-model/README.md +455 -0
  406. package/registry/blocks/task-model/block.json +167 -0
  407. package/registry/blocks/task-model/changes.js +89 -0
  408. package/registry/blocks/task-model/dates.js +371 -0
  409. package/registry/blocks/task-model/demo.jsx +215 -0
  410. package/registry/blocks/task-model/dnd.jsx +240 -0
  411. package/registry/blocks/task-model/index.js +97 -0
  412. package/registry/blocks/task-model/model.js +919 -0
  413. package/registry/blocks/task-model/parts.jsx +449 -0
  414. package/registry/blocks/task-model/toolbar.jsx +271 -0
  415. package/registry/blocks/testimonials/README.md +139 -0
  416. package/registry/blocks/testimonials/block.json +11 -0
  417. package/registry/blocks/testimonials/demo.jsx +137 -0
  418. package/registry/blocks/testimonials/index.js +4 -0
  419. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  420. package/registry/blocks/transaction-list/README.md +126 -0
  421. package/registry/blocks/transaction-list/block.json +10 -0
  422. package/registry/blocks/transaction-list/demo.jsx +79 -0
  423. package/registry/blocks/transaction-list/index.js +4 -0
  424. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  425. package/registry/blocks/whiteboard/README.md +179 -0
  426. package/registry/blocks/whiteboard/block.json +10 -0
  427. package/registry/blocks/whiteboard/demo.jsx +169 -0
  428. package/registry/blocks/whiteboard/index.js +6 -0
  429. package/registry/blocks/whiteboard/templates.js +150 -0
  430. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  431. package/src/install.mjs +18 -0
  432. package/src/shadcn.mjs +171 -0
  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/parts/fixture.jsx +1 -1
  437. package/templates/next/files/components/parts/sample.js +15 -0
  438. package/templates/next/files/components/ui/avatar.jsx +141 -0
  439. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  440. package/templates/next/files/components/ui/button-group.jsx +70 -0
  441. package/templates/next/files/components/ui/button.jsx +132 -12
  442. package/templates/next/files/components/ui/calendar.jsx +609 -0
  443. package/templates/next/files/components/ui/carousel.jsx +281 -0
  444. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  445. package/templates/next/files/components/ui/combobox.jsx +608 -0
  446. package/templates/next/files/components/ui/command.jsx +577 -0
  447. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  448. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  449. package/templates/next/files/components/ui/distribution.jsx +117 -0
  450. package/templates/next/files/components/ui/feedback.jsx +143 -0
  451. package/templates/next/files/components/ui/field.jsx +115 -3
  452. package/templates/next/files/components/ui/file-input.jsx +234 -0
  453. package/templates/next/files/components/ui/icon.jsx +130 -8
  454. package/templates/next/files/components/ui/image.jsx +143 -0
  455. package/templates/next/files/components/ui/index.js +47 -2
  456. package/templates/next/files/components/ui/input-group.jsx +213 -0
  457. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  458. package/templates/next/files/components/ui/item.jsx +206 -0
  459. package/templates/next/files/components/ui/menu.jsx +454 -82
  460. package/templates/next/files/components/ui/notice.jsx +126 -14
  461. package/templates/next/files/components/ui/pagination.jsx +156 -0
  462. package/templates/next/files/components/ui/popover.jsx +284 -0
  463. package/templates/next/files/components/ui/radio.jsx +193 -0
  464. package/templates/next/files/components/ui/rating.jsx +131 -0
  465. package/templates/next/files/components/ui/resizable.jsx +338 -0
  466. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  467. package/templates/next/files/components/ui/separator.jsx +119 -0
  468. package/templates/next/files/components/ui/sheet.jsx +96 -11
  469. package/templates/next/files/components/ui/slider.jsx +174 -0
  470. package/templates/next/files/components/ui/stat.jsx +574 -0
  471. package/templates/next/files/components/ui/steps.jsx +338 -0
  472. package/templates/next/files/components/ui/swatch.jsx +33 -0
  473. package/templates/next/files/components/ui/table.jsx +130 -0
  474. package/templates/next/files/components/ui/toast.jsx +440 -0
  475. package/templates/next/files/components/ui/toggle.jsx +195 -0
  476. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  477. package/templates/next/files/lib/sample.js +4 -12
  478. package/templates/next/template.json +264 -8
@@ -0,0 +1,325 @@
1
+ "use client";
2
+
3
+ // The panels around the grid: filter, sort, fields, add a field, the
4
+ // keyboard sheet. Each edits one part of the view and hands it back.
5
+ import { useState } from "react";
6
+ import { Avatar, Button, Combobox, Field, Icon, IconButton, Input, KbdGroup, Select, Switch, cx } from "../../ui";
7
+ import { COLUMN_TYPES, parseNumberText, typeOf } from "./types";
8
+
9
+ let seq = 0;
10
+ export const uid = (p) => `${p}-${Date.now().toString(36)}${(seq++).toString(36)}`;
11
+
12
+ /** What "ascending" and "descending" mean for a type, in words. */
13
+ export function sortWords(type) {
14
+ switch (type.name) {
15
+ case "number":
16
+ case "currency":
17
+ case "rating":
18
+ return { asc: "1 → 9", desc: "9 → 1" };
19
+ case "date":
20
+ return { asc: "Oldest first", desc: "Newest first" };
21
+ case "checkbox":
22
+ return { asc: "Checked first", desc: "Unchecked first" };
23
+ case "select":
24
+ case "multi-select":
25
+ return { asc: "First → last", desc: "Last → first" };
26
+ default:
27
+ return { asc: "A → Z", desc: "Z → A" };
28
+ }
29
+ }
30
+
31
+ // ── filter ──────────────────────────────────────────────────────────────
32
+
33
+ function NumberValue({ value, onChange }) {
34
+ const [text, setText] = useState(value == null || value === "" ? "" : String(value));
35
+ const n = parseNumberText(text);
36
+ return (
37
+ <Input
38
+ inputMode="decimal"
39
+ placeholder="Number"
40
+ aria-label="Value"
41
+ value={text}
42
+ invalid={text !== "" && Number.isNaN(n)}
43
+ onChange={(e) => {
44
+ setText(e.target.value);
45
+ const next = parseNumberText(e.target.value);
46
+ onChange(next === null ? "" : Number.isNaN(next) ? "" : next);
47
+ }}
48
+ />
49
+ );
50
+ }
51
+
52
+ function FilterValue({ op, column, value, onChange, ctx }) {
53
+ switch (op?.arg) {
54
+ case "text":
55
+ return <Input placeholder="Text" aria-label="Value" value={value ?? ""} onChange={(e) => onChange(e.target.value)} />;
56
+ case "number":
57
+ return <NumberValue key={column.key + op.id} value={value} onChange={onChange} />;
58
+ case "date":
59
+ return <Input type="date" aria-label="Value" value={value ?? ""} onChange={(e) => onChange(e.target.value)} />;
60
+ case "options":
61
+ return (
62
+ <Combobox
63
+ multiple
64
+ label="Values"
65
+ placeholder="Pick options"
66
+ options={(column.options ?? []).map((o) => ({ value: o.value, label: o.label }))}
67
+ value={Array.isArray(value) ? value : []}
68
+ onValueChange={(v) => onChange(v)}
69
+ />
70
+ );
71
+ case "person":
72
+ return (
73
+ <Combobox
74
+ multiple
75
+ label="People"
76
+ placeholder="Pick people"
77
+ options={(ctx.people ?? []).map((p) => ({ value: p.id, label: p.name, icon: <Avatar name={p.name} src={p.src} size="xs" /> }))}
78
+ value={Array.isArray(value) ? value : []}
79
+ onValueChange={(v) => onChange(v)}
80
+ />
81
+ );
82
+ default:
83
+ return <span aria-hidden="true" />;
84
+ }
85
+ }
86
+
87
+ export function FilterPanel({ columns, filters, onChange, ctx, custom }) {
88
+ const byKey = new Map(columns.map((c) => [c.key, c]));
89
+ function set(id, patch) {
90
+ onChange(filters.map((f) => (f.id === id ? { ...f, ...patch } : f)));
91
+ }
92
+ function add() {
93
+ const col = columns[0];
94
+ if (!col) return;
95
+ onChange([...filters, { id: uid("f"), key: col.key, op: typeOf(col, custom).allFilters[0].id, value: "" }]);
96
+ }
97
+ return (
98
+ <div className="grid w-[38rem] max-w-full gap-3">
99
+ <p className="text-sm font-medium">Filter rows</p>
100
+ {filters.length === 0 ? (
101
+ <p className="text-sm text-muted-foreground">Nothing filtered. Add a condition to show only the rows you need.</p>
102
+ ) : (
103
+ <ul className="grid gap-2">
104
+ {filters.map((f, i) => {
105
+ const col = byKey.get(f.key) ?? columns[0];
106
+ const t = typeOf(col, custom);
107
+ const op = t.allFilters.find((o) => o.id === f.op) ?? t.allFilters[0];
108
+ return (
109
+ <li
110
+ key={f.id}
111
+ className="grid gap-2 max-sm:rounded-md max-sm:border max-sm:border-border max-sm:p-2 sm:grid-cols-[3rem_minmax(0,1fr)_minmax(0,9rem)_minmax(0,1fr)_auto] sm:items-center"
112
+ >
113
+ <div className="flex items-center justify-between sm:contents">
114
+ <span className="text-xs text-muted-foreground">{i === 0 ? "Where" : "and"}</span>
115
+ <IconButton icon="x" size="sm" label="Remove this condition" className="sm:hidden" onClick={() => onChange(filters.filter((x) => x.id !== f.id))} />
116
+ </div>
117
+ <Select
118
+ aria-label="Field"
119
+ value={col.key}
120
+ options={columns.map((c) => ({ value: c.key, label: c.name }))}
121
+ onChange={(e) => {
122
+ const next = byKey.get(e.target.value);
123
+ set(f.id, { key: next.key, op: typeOf(next, custom).allFilters[0].id, value: "" });
124
+ }}
125
+ />
126
+ <Select
127
+ aria-label="Condition"
128
+ value={op.id}
129
+ options={t.allFilters.map((o) => ({ value: o.id, label: o.label }))}
130
+ onChange={(e) => set(f.id, { op: e.target.value, value: t.allFilters.find((o) => o.id === e.target.value)?.arg === op.arg ? f.value : "" })}
131
+ />
132
+ <div className="min-w-0">
133
+ <FilterValue key={f.key + op.arg} op={op} column={col} value={f.value} ctx={ctx} onChange={(value) => set(f.id, { value })} />
134
+ </div>
135
+ <IconButton icon="x" size="sm" label="Remove this condition" className="max-sm:hidden" onClick={() => onChange(filters.filter((x) => x.id !== f.id))} />
136
+ </li>
137
+ );
138
+ })}
139
+ </ul>
140
+ )}
141
+ <div className="flex items-center justify-between gap-2">
142
+ <Button variant="quiet" size="sm" icon="plus" onClick={add} disabled={!columns.length}>
143
+ Add condition
144
+ </Button>
145
+ {filters.length ? (
146
+ <Button variant="quiet" size="sm" onClick={() => onChange([])}>
147
+ Clear all
148
+ </Button>
149
+ ) : null}
150
+ </div>
151
+ </div>
152
+ );
153
+ }
154
+
155
+ // ── sort ────────────────────────────────────────────────────────────────
156
+
157
+ export function SortPanel({ columns, sort, onChange, custom }) {
158
+ const used = new Set(sort.map((s) => s.key));
159
+ const free = columns.filter((c) => !used.has(c.key));
160
+ return (
161
+ <div className="grid w-[26rem] max-w-full gap-3">
162
+ <p className="text-sm font-medium">Sort rows</p>
163
+ {sort.length === 0 ? (
164
+ <p className="text-sm text-muted-foreground">Rows show in the order they were added. Pick a field to sort by.</p>
165
+ ) : (
166
+ <ol className="grid gap-2">
167
+ {sort.map((s, i) => {
168
+ const col = columns.find((c) => c.key === s.key);
169
+ if (!col) return null;
170
+ const words = sortWords(typeOf(col, custom));
171
+ return (
172
+ <li key={s.key} className="grid grid-cols-[3rem_minmax(0,1fr)_minmax(0,9rem)_auto] items-center gap-2">
173
+ <span className="text-xs text-muted-foreground">{i === 0 ? "Sort by" : "then"}</span>
174
+ <Select
175
+ aria-label="Field"
176
+ value={s.key}
177
+ options={[col, ...free].map((c) => ({ value: c.key, label: c.name }))}
178
+ onChange={(e) => onChange(sort.map((x, j) => (j === i ? { ...x, key: e.target.value } : x)))}
179
+ />
180
+ <Select
181
+ aria-label="Direction"
182
+ value={s.dir}
183
+ options={[
184
+ { value: "asc", label: words.asc },
185
+ { value: "desc", label: words.desc },
186
+ ]}
187
+ onChange={(e) => onChange(sort.map((x, j) => (j === i ? { ...x, dir: e.target.value } : x)))}
188
+ />
189
+ <IconButton icon="x" size="sm" label="Remove this sort" onClick={() => onChange(sort.filter((_, j) => j !== i))} />
190
+ </li>
191
+ );
192
+ })}
193
+ </ol>
194
+ )}
195
+ <div className="flex items-center justify-between gap-2">
196
+ <Button variant="quiet" size="sm" icon="plus" disabled={!free.length} onClick={() => onChange([...sort, { key: free[0].key, dir: "asc" }])}>
197
+ {sort.length ? "Add another sort" : "Add a sort"}
198
+ </Button>
199
+ {sort.length ? (
200
+ <Button variant="quiet" size="sm" onClick={() => onChange([])}>
201
+ Clear
202
+ </Button>
203
+ ) : null}
204
+ </div>
205
+ </div>
206
+ );
207
+ }
208
+
209
+ // ── fields (show / hide) ────────────────────────────────────────────────
210
+
211
+ export function FieldsPanel({ columns, hidden, onChange, custom }) {
212
+ const off = new Set(hidden);
213
+ const [q, setQ] = useState("");
214
+ const shown = columns.filter((c) => c.name.toLowerCase().includes(q.trim().toLowerCase()));
215
+ const visibleCount = columns.filter((c) => !off.has(c.key)).length;
216
+ return (
217
+ <div className="grid w-72 max-w-full gap-2">
218
+ {columns.length > 8 ? <Input placeholder="Find a field" aria-label="Find a field" value={q} onChange={(e) => setQ(e.target.value)} /> : null}
219
+ <ul className="grid max-h-80 overflow-y-auto">
220
+ {shown.map((c) => {
221
+ const t = typeOf(c, custom);
222
+ const on = !off.has(c.key);
223
+ return (
224
+ <li key={c.key} className="flex items-center gap-2">
225
+ <Icon name={t.icon} size={14} className="shrink-0 text-muted-foreground" />
226
+ <Switch
227
+ id={`field-${c.key}`}
228
+ checked={on}
229
+ disabled={on && visibleCount <= 1}
230
+ onChange={() => onChange(on ? [...hidden, c.key] : hidden.filter((k) => k !== c.key))}
231
+ label={c.name}
232
+ className="min-w-0 flex-1 flex-row-reverse justify-between"
233
+ />
234
+ </li>
235
+ );
236
+ })}
237
+ </ul>
238
+ <div className="flex items-center justify-between gap-2 border-t border-border pt-2">
239
+ <Button variant="quiet" size="sm" onClick={() => onChange(columns.slice(1).map((c) => c.key))} disabled={visibleCount <= 1}>
240
+ Hide all
241
+ </Button>
242
+ <Button variant="quiet" size="sm" onClick={() => onChange([])} disabled={!hidden.length}>
243
+ Show all
244
+ </Button>
245
+ </div>
246
+ </div>
247
+ );
248
+ }
249
+
250
+ // ── add a field ─────────────────────────────────────────────────────────
251
+
252
+ export const TYPE_ORDER = ["text", "number", "currency", "date", "select", "multi-select", "person", "checkbox", "url", "rating"];
253
+
254
+ export function AddFieldPanel({ onAdd, names, custom }) {
255
+ const [name, setName] = useState("");
256
+ const [type, setType] = useState("text");
257
+ const kinds = [...TYPE_ORDER, ...Object.keys(custom ?? {}).filter((k) => !TYPE_ORDER.includes(k))];
258
+ const clash = names.some((n) => n.toLowerCase() === name.trim().toLowerCase());
259
+ function submit(e) {
260
+ e.preventDefault();
261
+ if (!name.trim() || clash) return;
262
+ onAdd({ name: name.trim(), type });
263
+ setName("");
264
+ setType("text");
265
+ }
266
+ return (
267
+ <form className="grid w-80 max-w-full gap-3" onSubmit={submit}>
268
+ <Field id="new-field-name" label="Field name" error={clash ? "A field already has that name" : undefined}>
269
+ <Input id="new-field-name" placeholder="Renewal date" value={name} onChange={(e) => setName(e.target.value)} autoComplete="off" />
270
+ </Field>
271
+ <div className="grid gap-1.5">
272
+ <p className="text-sm font-medium" id="new-field-type">
273
+ Type
274
+ </p>
275
+ <div role="radiogroup" aria-labelledby="new-field-type" className="grid grid-cols-2 gap-1">
276
+ {kinds.map((k) => {
277
+ const t = typeOf({ type: k }, custom);
278
+ const on = type === k;
279
+ return (
280
+ <Button key={k} variant={on ? "secondary" : "quiet"} size="sm" align="start" icon={t.icon} role="radio" aria-checked={on} onClick={() => setType(k)} className={cx("font-normal", on && "ring-1 ring-primary")}>
281
+ {t.label}
282
+ </Button>
283
+ );
284
+ })}
285
+ </div>
286
+ </div>
287
+ <Button type="submit" variant="primary" size="sm" disabled={!name.trim() || clash}>
288
+ Add field
289
+ </Button>
290
+ </form>
291
+ );
292
+ }
293
+
294
+ // ── keys ────────────────────────────────────────────────────────────────
295
+
296
+ const KEYS = [
297
+ [["↑", "↓", "←", "→"], "Move"],
298
+ [["⇧", "↑"], "Select more"],
299
+ [["↵"], "Edit (or start typing)"],
300
+ [["Esc"], "Cancel an edit"],
301
+ [["⌫"], "Clear cells"],
302
+ [["⌘", "C"], "Copy · ⌘V paste"],
303
+ [["⌘", "D"], "Fill down"],
304
+ [["⌘", "Z"], "Undo · ⇧⌘Z redo"],
305
+ [["Space"], "Tick a box · open a row"],
306
+ [["⇧", "Space"], "Select the row"],
307
+ ];
308
+
309
+ export function ShortcutsPanel() {
310
+ return (
311
+ <div className="grid w-72 gap-2">
312
+ <p className="text-sm font-medium">Keyboard</p>
313
+ <ul className="grid gap-1.5">
314
+ {KEYS.map(([keys, what]) => (
315
+ <li key={what} className="flex items-center justify-between gap-3 text-sm">
316
+ <span className="text-muted-foreground">{what}</span>
317
+ <KbdGroup keys={keys} />
318
+ </li>
319
+ ))}
320
+ </ul>
321
+ </div>
322
+ );
323
+ }
324
+
325
+ export { COLUMN_TYPES };
@@ -0,0 +1,177 @@
1
+ "use client";
2
+
3
+ // The Spreadsheet over a sales pipeline in memory — the gallery's and the
4
+ // smoke test's surface. Not installed into apps: `makersclaw add
5
+ // spreadsheet` lays index.js and the block's files only.
6
+ import { useMemo, useState } from "react";
7
+ import { Card, Sheet, Switch, ToggleGroup } from "../components/ui";
8
+ import { Spreadsheet } from "../components/blocks/spreadsheet";
9
+
10
+ const TEAM = [
11
+ { id: "u-maya", name: "Maya Okafor", email: "maya@northpeak.io" },
12
+ { id: "u-leon", name: "Leon Brandt", email: "leon@northpeak.io" },
13
+ { id: "u-priya", name: "Priya Raman", email: "priya@northpeak.io" },
14
+ { id: "u-tomas", name: "Tomás Ferreira", email: "tomas@northpeak.io" },
15
+ { id: "u-hana", name: "Hana Sato", email: "hana@northpeak.io" },
16
+ ];
17
+
18
+ const STAGES = [
19
+ { value: "lead", label: "Lead", hue: 5 },
20
+ { value: "qualified", label: "Qualified", hue: 2 },
21
+ { value: "proposal", label: "Proposal", hue: 3 },
22
+ { value: "negotiation", label: "Negotiation", hue: 4 },
23
+ { value: "won", label: "Won", hue: 1 },
24
+ { value: "lost", label: "Lost", hue: 5 },
25
+ ];
26
+
27
+ const SEGMENTS = [
28
+ { value: "saas", label: "SaaS", hue: 1 },
29
+ { value: "fintech", label: "Fintech", hue: 2 },
30
+ { value: "health", label: "Health", hue: 3 },
31
+ { value: "retail", label: "Retail", hue: 4 },
32
+ { value: "enterprise", label: "Enterprise", hue: 5 },
33
+ ];
34
+
35
+ const PIPELINE_COLUMNS = [
36
+ { key: "company", name: "Company", type: "text", width: 200, required: true },
37
+ { key: "stage", name: "Stage", type: "select", options: STAGES },
38
+ { key: "value", name: "Deal value", type: "currency", currency: "USD", precision: 0 },
39
+ { key: "owner", name: "Owner", type: "person" },
40
+ { key: "close", name: "Close date", type: "date" },
41
+ { key: "segments", name: "Segments", type: "multi-select", options: SEGMENTS },
42
+ { key: "seats", name: "Seats", type: "number", width: 96 },
43
+ { key: "fit", name: "Fit", type: "rating", max: 5, width: 120 },
44
+ { key: "contacted", name: "Contacted", type: "checkbox" },
45
+ { key: "website", name: "Website", type: "url" },
46
+ ];
47
+
48
+ const FIRST = ["North", "Blue", "Silver", "Bright", "Iron", "Maple", "Harbor", "Summit", "Cedar", "Atlas", "Nova", "Pine", "River", "Stone", "Lumen", "Orbit", "Vista", "Juniper"];
49
+ const SECOND = ["Labs", "Health", "Logistics", "Foods", "Studio", "Capital", "Robotics", "Media", "Dental", "Freight", "Analytics", "Apparel", "Energy", "Legal", "Clinics", "Software"];
50
+
51
+ // The same rows every render: a small seeded generator, so the gallery
52
+ // render does not change between runs.
53
+ function makeDeals(n) {
54
+ let seed = 11;
55
+ const rnd = () => {
56
+ seed = (seed * 16807) % 2147483647;
57
+ return seed / 2147483647;
58
+ };
59
+ const pick = (list) => list[Math.floor(rnd() * list.length)];
60
+ return Array.from({ length: n }, (_, i) => {
61
+ const company = `${pick(FIRST)} ${pick(SECOND)}${i >= FIRST.length * 4 ? ` ${Math.floor(i / 97) + 2}` : ""}`;
62
+ const slug = company.toLowerCase().replace(/[^a-z0-9]+/g, "");
63
+ const stage = pick(STAGES).value;
64
+ const d = new Date(Date.UTC(2026, 8, 1) + Math.floor(rnd() * 150) * 86400000).toISOString().slice(0, 10);
65
+ const segs = SEGMENTS.filter(() => rnd() < 0.28).map((s) => s.value);
66
+ return {
67
+ id: `deal-${1000 + i}`,
68
+ company,
69
+ stage,
70
+ value: Math.round(2 + rnd() * 120) * 500,
71
+ owner: pick(TEAM).id,
72
+ close: rnd() < 0.08 ? null : d,
73
+ segments: segs,
74
+ seats: Math.round(5 + rnd() * 400),
75
+ fit: Math.floor(rnd() * 6),
76
+ contacted: rnd() < 0.55,
77
+ website: rnd() < 0.1 ? "" : `https://${slug}.com`,
78
+ };
79
+ });
80
+ }
81
+
82
+ const MANY = makeDeals(10000);
83
+ let made = 0;
84
+ const FEW = makeDeals(60);
85
+
86
+ export default function SpreadsheetDemo() {
87
+ const [size, setSize] = useState("many");
88
+ const [failing, setFailing] = useState(false);
89
+ const [data, setData] = useState({ many: MANY, few: FEW, empty: [] });
90
+ const [columns, setColumns] = useState(PIPELINE_COLUMNS);
91
+ const [open, setOpen] = useState(null);
92
+ const rows = size === "loading" ? [] : data[size];
93
+
94
+ const save = (fn) => async (...args) => {
95
+ await new Promise((r) => setTimeout(r, 250));
96
+ if (failing) return { ok: false, error: "The server said no (the demo's “Saves fail” switch is on)." };
97
+ fn(...args);
98
+ return { ok: true };
99
+ };
100
+ const onChange = save((changes) =>
101
+ setData((d) => {
102
+ const by = new Map();
103
+ for (const c of changes) by.set(c.rowId, { ...(by.get(c.rowId) ?? {}), [c.key]: c.value });
104
+ return { ...d, [size]: d[size].map((r) => (by.has(r.id) ? { ...r, ...by.get(r.id) } : r)) };
105
+ }),
106
+ );
107
+ const onAddRows = save((added, { index }) =>
108
+ setData((d) => {
109
+ const list = d[size];
110
+ const at = index ?? list.length;
111
+ return { ...d, [size]: [...list.slice(0, at), ...added, ...list.slice(at)] };
112
+ }),
113
+ );
114
+ const onDeleteRows = save((ids) => setData((d) => ({ ...d, [size]: d[size].filter((r) => !ids.includes(r.id)) })));
115
+ const onColumnsChange = save((next) => setColumns(next));
116
+
117
+ const openRow = useMemo(() => (open ? rows.find((r) => r.id === open) : null), [open, rows]);
118
+
119
+ return (
120
+ <div className="grid gap-4">
121
+ <div className="flex flex-wrap items-center justify-between gap-3">
122
+ <div>
123
+ <h2 className="text-lg font-semibold">Pipeline</h2>
124
+ <p className="text-sm text-muted-foreground">Click a cell and type. Drag the corner to fill, paste from Sheets, right-click for more.</p>
125
+ </div>
126
+ <div className="flex flex-wrap items-center gap-3">
127
+ <ToggleGroup
128
+ size="sm"
129
+ aria-label="Rows"
130
+ value={size}
131
+ onChange={setSize}
132
+ items={[
133
+ { value: "many", label: "10,000 rows" },
134
+ { value: "few", label: "60 rows" },
135
+ { value: "empty", label: "Empty" },
136
+ { value: "loading", label: "Loading" },
137
+ ]}
138
+ />
139
+ <Switch id="sheet-fail" label="Saves fail" checked={failing} onChange={(e) => setFailing(e.target.checked)} />
140
+ </div>
141
+ </div>
142
+ <Spreadsheet
143
+ key={size}
144
+ label="Pipeline"
145
+ noun="deal"
146
+ rows={rows}
147
+ columns={columns}
148
+ people={TEAM}
149
+ loading={size === "loading"}
150
+ today="2026-09-23"
151
+ locale="en-US"
152
+ defaultView={{ frozen: 1, widths: {}, sort: [], filters: [] }}
153
+ onChange={onChange}
154
+ onAddRows={onAddRows}
155
+ onDeleteRows={onDeleteRows}
156
+ onColumnsChange={onColumnsChange}
157
+ onExpandRow={(row) => setOpen(row.id)}
158
+ createRow={() => ({ id: `deal-new-${++made}`, stage: "lead", contacted: false, segments: [] })}
159
+ height={560}
160
+ />
161
+ <Sheet open={Boolean(openRow)} onClose={() => setOpen(null)} title={openRow?.company || "Untitled deal"} description="The row, opened: base-view draws a record here.">
162
+ {openRow ? (
163
+ <Card padding="sm">
164
+ <dl className="grid grid-cols-[8rem_1fr] gap-x-3 gap-y-2 text-sm">
165
+ {columns.map((c) => (
166
+ <div key={c.key} className="contents">
167
+ <dt className="text-muted-foreground">{c.name}</dt>
168
+ <dd className="truncate">{Array.isArray(openRow[c.key]) ? openRow[c.key].join(", ") : String(openRow[c.key] ?? "—")}</dd>
169
+ </div>
170
+ ))}
171
+ </dl>
172
+ </Card>
173
+ ) : null}
174
+ </Sheet>
175
+ </div>
176
+ );
177
+ }