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,69 @@
1
+ // How an item is coloured on the calendar — tokens only. A timed block
2
+ // and an all-day bar wear a tint with a solid leading edge; a timed item
3
+ // in a month cell is a dot. The colour comes from the task's project
4
+ // (chart-1…5), or its status's tone with colorBy="status"; one without
5
+ // either wears the theme's primary.
6
+
7
+ const HUE_TINT = {
8
+ 1: "bg-chart-1/15 hover:bg-chart-1/25 border-s-chart-1",
9
+ 2: "bg-chart-2/15 hover:bg-chart-2/25 border-s-chart-2",
10
+ 3: "bg-chart-3/15 hover:bg-chart-3/25 border-s-chart-3",
11
+ 4: "bg-chart-4/15 hover:bg-chart-4/25 border-s-chart-4",
12
+ 5: "bg-chart-5/15 hover:bg-chart-5/25 border-s-chart-5",
13
+ };
14
+
15
+ const HUE_DOT = { 1: "bg-chart-1", 2: "bg-chart-2", 3: "bg-chart-3", 4: "bg-chart-4", 5: "bg-chart-5" };
16
+
17
+ const TONE_TINT = {
18
+ neutral: "bg-muted hover:bg-accent border-s-muted-foreground",
19
+ primary: "bg-primary/10 hover:bg-primary/15 border-s-primary",
20
+ info: "bg-info/15 hover:bg-info/25 border-s-info",
21
+ success: "bg-success/15 hover:bg-success/25 border-s-success",
22
+ warning: "bg-warning/15 hover:bg-warning/25 border-s-warning",
23
+ danger: "bg-destructive/10 hover:bg-destructive/20 border-s-destructive",
24
+ };
25
+
26
+ const TONE_DOT = {
27
+ neutral: "bg-muted-foreground",
28
+ primary: "bg-primary",
29
+ info: "bg-info",
30
+ success: "bg-success",
31
+ warning: "bg-warning",
32
+ danger: "bg-destructive",
33
+ };
34
+
35
+ const DEFAULT = { tint: TONE_TINT.primary, dot: TONE_DOT.primary };
36
+
37
+ /** { tint, dot } class strings for a task. */
38
+ export function paintOf(task, ctx, colorBy = "project") {
39
+ if (colorBy === "status") {
40
+ const s = ctx.statusById.get(String(task.status));
41
+ if (s?.hue && HUE_TINT[s.hue]) return { tint: HUE_TINT[s.hue], dot: HUE_DOT[s.hue] };
42
+ const tone = s?.tone ?? "neutral";
43
+ return { tint: TONE_TINT[tone] ?? TONE_TINT.neutral, dot: TONE_DOT[tone] ?? TONE_DOT.neutral };
44
+ }
45
+ if (colorBy === "project") {
46
+ const p = ctx.projectById.get(String(task.project));
47
+ if (p?.hue && HUE_TINT[p.hue]) return { tint: HUE_TINT[p.hue], dot: HUE_DOT[p.hue] };
48
+ }
49
+ return DEFAULT;
50
+ }
51
+
52
+ /** A drag handle's props with our keys first: `onKey(e)` returns true
53
+ * when it handled the key, and dnd-kit's own keyboard pickup never sees
54
+ * it (Enter opens, Delete deletes, the arrows nudge a timed block). */
55
+ export function withKeys(handle, onKey) {
56
+ return {
57
+ ...handle,
58
+ onKeyDown: (e) => {
59
+ if (onKey?.(e) === true) return;
60
+ handle?.onKeyDown?.(e);
61
+ },
62
+ };
63
+ }
64
+
65
+ /** Whether a key event came from a field the founder is typing in. */
66
+ export function isTyping(e) {
67
+ const t = e.target;
68
+ return Boolean(t && (t.isContentEditable || /^(INPUT|TEXTAREA|SELECT)$/.test(t.tagName)));
69
+ }
@@ -0,0 +1,527 @@
1
+ "use client";
2
+
3
+ import { useEffect, useMemo, useRef, useState } from "react";
4
+ import { cx } from "../../ui";
5
+ import { dayOf, formatDate, isDone, isWeekend, minutesOf, useDraggableTask, useDropTarget } from "../task-model";
6
+ import { DAY_MINUTES, eventTimeLabel, hourLabel, layoutLanes, layoutTimed, minutesLabel, snapMinutes } from "./calendar-model";
7
+ import { DraftInput } from "./draft";
8
+ import { paintOf, withKeys } from "./paint";
9
+
10
+ // The week and the day: a strip of all-day items on top, then the hours
11
+ // as a scrolling grid with one column per day. Timed tasks are tinted
12
+ // blocks placed by their times; overlapping ones share the column side by
13
+ // side. Drag a block to move it (it snaps to the quarter hour and to the
14
+ // day columns as it goes), drag its bottom edge to change how long it
15
+ // runs, press on an empty stretch and drag to make a new one (a click
16
+ // makes an hour). A line marks now. The available-time layer draws closed
17
+ // hours muted and open slots as dashed boxes.
18
+ //
19
+ // Keyboard: a focused block moves with the arrow keys (up/down 15
20
+ // minutes, left/right a day), Shift+up/down changes its length, Enter
21
+ // opens it, Delete removes it (with Undo).
22
+
23
+ const GUTTER = "w-14 shrink-0";
24
+
25
+ export function TimeGrid({ days, events, api, hourHeight = 48, height = "40rem", closed, slots, renderSlot, onSlotClick, selectedSlot, slotLabel, now }) {
26
+ const { ctx, locale } = api;
27
+ const n = days.length;
28
+ const cols = { gridTemplateColumns: `repeat(${n}, minmax(0, 1fr))` };
29
+ const scrollRef = useRef(null);
30
+ const colsRef = useRef(null);
31
+ const colWidth = () => (colsRef.current?.getBoundingClientRect().width ?? 700) / n;
32
+ const timed = useMemo(() => events.filter((e) => e.timed), [events]);
33
+ const allDay = useMemo(() => events.filter((e) => !e.timed), [events]);
34
+ const nowDay = dayOf(now);
35
+ const nowMin = now ? minutesOf(now) : null;
36
+
37
+ // Open the grid near what matters: now, else the day's first item, else 8 AM.
38
+ const first = timed.length ? Math.min(...timed.map((e) => e.from)) : null;
39
+ const focusMin = nowDay && days.includes(nowDay) && nowMin !== null ? nowMin - 90 : first !== null ? first - 30 : 8 * 60;
40
+ const scrollKey = `${days[0]}:${n}`;
41
+ const scrolled = useRef(null);
42
+ useEffect(() => {
43
+ if (scrolled.current === scrollKey || !scrollRef.current) return;
44
+ scrolled.current = scrollKey;
45
+ scrollRef.current.scrollTop = Math.max(0, (focusMin / 60) * hourHeight);
46
+ }, [scrollKey, focusMin, hourHeight]);
47
+
48
+ return (
49
+ <div className="overflow-hidden rounded-lg border border-border bg-card">
50
+ <div className="flex overflow-y-hidden border-b border-border [scrollbar-gutter:stable]">
51
+ <div className={GUTTER} />
52
+ <div className="grid flex-1" style={cols}>
53
+ {days.map((day) => (
54
+ <DayHead key={day} day={day} api={api} single={n === 1} />
55
+ ))}
56
+ </div>
57
+ </div>
58
+ <AllDayStrip days={days} events={allDay} api={api} cols={cols} />
59
+ <div ref={scrollRef} className="relative overflow-y-auto overscroll-contain [scrollbar-gutter:stable]" style={{ height }}>
60
+ <div className="relative flex" style={{ height: 24 * hourHeight }}>
61
+ <div className={cx(GUTTER, "relative")} aria-hidden="true">
62
+ {Array.from({ length: 23 }, (_, i) => i + 1).map((h) => (
63
+ <span key={h} data-nums className="absolute end-2 -translate-y-1/2 text-2xs text-muted-foreground" style={{ top: h * hourHeight }}>
64
+ {hourLabel(h, { locale })}
65
+ </span>
66
+ ))}
67
+ </div>
68
+ <div ref={colsRef} className="relative grid flex-1" style={cols}>
69
+ <div aria-hidden="true" className="pointer-events-none absolute inset-0">
70
+ {Array.from({ length: 23 }, (_, i) => i + 1).map((h) => (
71
+ <div key={h} className="absolute inset-x-0 border-t border-border" style={{ top: h * hourHeight }} />
72
+ ))}
73
+ </div>
74
+ {days.map((day, i) => (
75
+ <DayColumn
76
+ key={day}
77
+ day={day}
78
+ index={i}
79
+ days={days}
80
+ events={timed.filter((e) => e.start === day)}
81
+ api={api}
82
+ hourHeight={hourHeight}
83
+ colWidth={colWidth}
84
+ closed={closed ? closed(day) : null}
85
+ slots={slots?.filter((s) => s.day === day)}
86
+ renderSlot={renderSlot}
87
+ onSlotClick={onSlotClick}
88
+ selectedSlot={selectedSlot}
89
+ slotLabel={slotLabel}
90
+ nowMin={day === nowDay ? nowMin : null}
91
+ weekend={isWeekend(day, ctx.weekend)}
92
+ />
93
+ ))}
94
+ </div>
95
+ </div>
96
+ </div>
97
+ </div>
98
+ );
99
+ }
100
+
101
+ function DayHead({ day, api, single }) {
102
+ const { ctx, locale } = api;
103
+ const today = day === ctx.today;
104
+ return (
105
+ <div className={cx("flex min-w-0 items-center justify-center border-s border-border py-2 first:border-s-0", single && "justify-start ps-3")}>
106
+ {/* raw: the heading is the way into that day (a button in the header
107
+ row); a kit Button would pad it as an action. */}
108
+ <button
109
+ type="button"
110
+ onClick={() => api.openDay(day)}
111
+ disabled={single}
112
+ aria-label={`${formatDate(day, { weekday: "long", month: "long", day: "numeric" }, { locale })}. Open the day`}
113
+ aria-current={today ? "date" : undefined}
114
+ className="group/head inline-flex min-w-0 items-center gap-1.5 rounded-md px-1.5 py-1 outline-none transition-colors duration-150 hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-default disabled:hover:bg-transparent"
115
+ >
116
+ <span className={cx("text-xs", today ? "font-medium text-primary" : "text-muted-foreground")}>{formatDate(day, { weekday: "short" }, { locale })}</span>
117
+ <span
118
+ data-nums
119
+ className={cx("inline-flex size-7 items-center justify-center rounded-full text-sm font-semibold", today ? "bg-primary text-primary-foreground" : "text-foreground")}
120
+ >
121
+ {Number(day.slice(8, 10))}
122
+ </span>
123
+ </button>
124
+ </div>
125
+ );
126
+ }
127
+
128
+ // ---- the all-day strip ----------------------------------------------------
129
+
130
+ const STRIP_LANES = 3;
131
+
132
+ function AllDayStrip({ days, events, api, cols }) {
133
+ const [open, setOpen] = useState(false);
134
+ const rowRef = useRef(null);
135
+ const layout = useMemo(() => layoutLanes(events, days), [events, days]);
136
+ const over = layout.lanes > STRIP_LANES && !open;
137
+ const lanes = over ? STRIP_LANES - 1 : layout.lanes;
138
+ const hiddenCount = layout.items.filter((it) => it.lane >= lanes).length;
139
+ const cellWidth = () => (rowRef.current?.getBoundingClientRect().width ?? 700) / days.length;
140
+ const drafting = api.draft?.allDay && days.includes(api.draft.day);
141
+ const rows = Math.max(lanes + (over ? 1 : 0) + (drafting ? 1 : 0), 1);
142
+ return (
143
+ <div className="flex overflow-y-hidden border-b border-border [scrollbar-gutter:stable]">
144
+ <div className={cx(GUTTER, "flex items-start justify-end pe-2 pt-1.5 text-2xs text-muted-foreground")}>All day</div>
145
+ <div ref={rowRef} className="relative grid flex-1" style={{ ...cols, minHeight: rows * 24 + 8 }}>
146
+ {days.map((day) => (
147
+ <StripCell key={day} day={day} api={api} />
148
+ ))}
149
+ <div className="pointer-events-none absolute inset-x-0 top-1 grid gap-y-0.5" style={{ ...cols, gridAutoRows: "22px" }}>
150
+ {layout.items
151
+ .filter((it) => it.lane < lanes)
152
+ .map((it) => (
153
+ <StripItem key={it.event.id} item={it} days={days} api={api} cellWidth={cellWidth} />
154
+ ))}
155
+ {over ? (
156
+ // raw: a line of small text sized to a lane that opens the strip;
157
+ // a kit Button is a 36px action.
158
+ <button
159
+ type="button"
160
+ onClick={() => setOpen(true)}
161
+ style={{ gridColumn: `1 / span ${days.length}`, gridRow: lanes + 1 }}
162
+ className="pointer-events-auto mx-1 justify-self-start rounded-sm px-1.5 text-xs font-medium text-muted-foreground outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
163
+ >
164
+ {hiddenCount} more
165
+ </button>
166
+ ) : null}
167
+ {drafting ? (
168
+ <div
169
+ style={{ gridColumn: `${days.indexOf(api.draft.day) + 1}`, gridRow: lanes + (over ? 2 : 1) }}
170
+ className="pointer-events-auto mx-1 flex items-center rounded-sm bg-primary/10 px-1.5 ring-1 ring-primary"
171
+ >
172
+ <DraftInput label={`New all-day task on ${formatDate(api.draft.day, { weekday: "long", month: "long", day: "numeric" })}`} onSubmit={api.saveDraft} onCancel={api.cancelDraft} />
173
+ </div>
174
+ ) : null}
175
+ </div>
176
+ </div>
177
+ </div>
178
+ );
179
+ }
180
+
181
+ function StripCell({ day, api }) {
182
+ const label = `${formatDate(day, { weekday: "long", month: "long", day: "numeric" }, { locale: api.locale })}, all day`;
183
+ const drop = useDropTarget(`strip:${day}`, { kind: "cell", day, label });
184
+ return (
185
+ <div
186
+ ref={drop.setNodeRef}
187
+ onClick={(e) => {
188
+ if (api.editable && e.target === e.currentTarget) api.startDraft({ day, allDay: true });
189
+ }}
190
+ className={cx("border-s border-border transition-colors duration-150 first:border-s-0", drop.isOver && "bg-accent")}
191
+ />
192
+ );
193
+ }
194
+
195
+ function StripItem({ item, days, api, cellWidth }) {
196
+ const { event, col, span, lane, clippedStart, clippedEnd } = item;
197
+ const { ctx, editable } = api;
198
+ const task = event.task;
199
+ const done = isDone(task, ctx);
200
+ const paint = paintOf(task, ctx, api.colorBy);
201
+ const title = task.title || "Untitled";
202
+ const d = useDraggableTask(`strip:${event.id}`, { kind: "span", task, event }, { disabled: !editable });
203
+ const canResize = editable && !clippedEnd;
204
+ const r = useDraggableTask(`sx:${event.id}`, { kind: "resize-x", task, event, cellWidth }, { disabled: !canResize });
205
+ const grow = r.isDragging && r.transform ? Math.round(r.transform.x / cellWidth()) : 0;
206
+ const shownSpan = Math.max(1, Math.min(days.length - col, span + Math.max(grow, 1 - span)));
207
+ const keys = withKeys(d.handle, (e) => {
208
+ if (e.key === "Enter") return true;
209
+ if ((e.key === "Delete" || e.key === "Backspace") && editable) {
210
+ e.preventDefault();
211
+ api.remove(task);
212
+ return true;
213
+ }
214
+ if (editable && e.shiftKey && (e.key === "ArrowRight" || e.key === "ArrowLeft")) {
215
+ e.preventDefault();
216
+ api.extend(event, e.key === "ArrowRight" ? 1 : -1);
217
+ return true;
218
+ }
219
+ return false;
220
+ });
221
+ return (
222
+ // raw: an all-day bar is a drag source that also opens; a kit
223
+ // Button would centre and pad it as an action.
224
+ <button
225
+ type="button"
226
+ ref={d.setNodeRef}
227
+ data-cal-id={event.id}
228
+ {...(editable ? keys : {})}
229
+ onPointerDown={(e) => {
230
+ if (!editable) return;
231
+ const rect = e.currentTarget.getBoundingClientRect();
232
+ const w = rect.width / shownSpan;
233
+ api.setGrab(days[col + Math.max(0, Math.min(shownSpan - 1, Math.floor((e.clientX - rect.left) / w)))]);
234
+ keys.onPointerDown?.(e);
235
+ }}
236
+ onClick={api.open ? () => api.open(task) : undefined}
237
+ aria-label={`${title}, ${event.start === event.end ? "all day" : `${formatDate(event.start, { month: "short", day: "numeric" })} to ${formatDate(event.end, { month: "short", day: "numeric" })}`}${done ? ", done" : ""}`}
238
+ title={title}
239
+ style={{ gridColumn: `${col + 1} / span ${shownSpan}`, gridRow: lane + 1 }}
240
+ className={cx(
241
+ "pointer-events-auto relative flex min-w-0 items-center rounded-sm border-s-[3px] px-1.5 text-left text-xs font-medium text-foreground outline-none transition-[opacity,background-color] duration-150 focus-visible:ring-2 focus-visible:ring-ring",
242
+ paint.tint,
243
+ clippedStart ? "ms-0 rounded-s-none border-s-0 ps-2" : "ms-1",
244
+ clippedEnd ? "me-0 rounded-e-none" : "me-1",
245
+ editable ? "cursor-grab active:cursor-grabbing" : api.open ? "cursor-pointer" : "cursor-default",
246
+ d.isDragging && "opacity-40",
247
+ r.isDragging && "ring-2 ring-primary",
248
+ done && "text-muted-foreground",
249
+ )}
250
+ >
251
+ <span className={cx("min-w-0 flex-1 truncate", done && "line-through")}>{title}</span>
252
+ {canResize ? (
253
+ <span
254
+ ref={r.setNodeRef}
255
+ {...r.handle}
256
+ role="presentation"
257
+ tabIndex={-1}
258
+ aria-hidden="true"
259
+ onClick={(e) => e.stopPropagation()}
260
+ className="absolute inset-y-0 end-0 w-2 cursor-ew-resize rounded-e-sm hover:bg-primary/30"
261
+ />
262
+ ) : null}
263
+ </button>
264
+ );
265
+ }
266
+
267
+ // ---- one day's column of hours ------------------------------------------
268
+
269
+ function DayColumn({ day, index, days, events, api, hourHeight, colWidth, closed, slots, renderSlot, onSlotClick, selectedSlot, slotLabel, nowMin, weekend }) {
270
+ const { editable, locale } = api;
271
+ const ppm = hourHeight / 60;
272
+ const ref = useRef(null);
273
+ const label = formatDate(day, { weekday: "long", month: "long", day: "numeric" }, { locale });
274
+ const drop = useDropTarget(`col:${day}`, { kind: "column", day, label, top: () => ref.current?.getBoundingClientRect().top ?? 0 });
275
+ const layout = useMemo(() => layoutTimed(events), [events]);
276
+ const [hover, setHover] = useState(null); // minutes under the mouse
277
+ const [making, setMaking] = useState(null); // { a, b } while press-dragging a new block
278
+ const lastPointer = useRef("mouse");
279
+ const drafting = api.draft && !api.draft.allDay && api.draft.day === day ? api.draft : null;
280
+
281
+ const minuteAt = (clientY) => {
282
+ const top = ref.current?.getBoundingClientRect().top ?? 0;
283
+ return Math.max(0, Math.min(DAY_MINUTES - 15, (clientY - top) / ppm));
284
+ };
285
+ const onBg = (e) => e.target === e.currentTarget || e.target.dataset?.calBg !== undefined;
286
+
287
+ return (
288
+ <div
289
+ ref={(el) => {
290
+ ref.current = el;
291
+ drop.setNodeRef(el);
292
+ }}
293
+ data-cal-bg=""
294
+ className={cx(
295
+ "relative border-s border-border transition-colors duration-150 first:border-s-0",
296
+ weekend && "bg-muted/20",
297
+ drop.isOver && "bg-accent/50",
298
+ )}
299
+ onPointerDown={(e) => {
300
+ lastPointer.current = e.pointerType;
301
+ if (!editable || e.pointerType !== "mouse" || e.button !== 0 || !onBg(e)) return;
302
+ const m = snapMinutes(minuteAt(e.clientY) - 7, 15);
303
+ setMaking({ a: m, b: m + 15 });
304
+ e.currentTarget.setPointerCapture?.(e.pointerId);
305
+ }}
306
+ onPointerMove={(e) => {
307
+ if (e.pointerType !== "mouse") return;
308
+ const m = minuteAt(e.clientY);
309
+ if (making) {
310
+ const b = snapMinutes(m, 15);
311
+ setMaking((x) => (x && (b !== x.b ? { ...x, b: Math.max(b, x.a + 15) } : x)));
312
+ } else if (editable && onBg(e)) {
313
+ const h = Math.floor(m / 30) * 30;
314
+ if (h !== hover) setHover(h);
315
+ } else if (hover !== null) setHover(null);
316
+ }}
317
+ onPointerLeave={() => setHover(null)}
318
+ onPointerUp={(e) => {
319
+ if (!making) return;
320
+ e.currentTarget.releasePointerCapture?.(e.pointerId);
321
+ const { a, b } = making;
322
+ setMaking(null);
323
+ setHover(null);
324
+ // A click (no real drag) makes the default length from the half hour.
325
+ if (b - a <= 15) {
326
+ const from = Math.floor(a / 30) * 30;
327
+ api.startDraft({ day, from, to: Math.min(DAY_MINUTES, from + api.defaultDuration) });
328
+ } else api.startDraft({ day, from: a, to: b });
329
+ }}
330
+ onClick={(e) => {
331
+ // A tap on a touch screen: an hour from the half hour tapped.
332
+ if (!editable || lastPointer.current === "mouse" || !onBg(e)) return;
333
+ const from = Math.floor(minuteAt(e.clientY) / 30) * 30;
334
+ api.startDraft({ day, from, to: Math.min(DAY_MINUTES, from + api.defaultDuration) });
335
+ }}
336
+ >
337
+ {closed?.map(([a, b]) => (
338
+ <div key={a} data-cal-bg="" aria-hidden="true" className="absolute inset-x-0 bg-muted/60" style={{ top: a * ppm, height: (b - a) * ppm }} />
339
+ ))}
340
+ {slots?.map((s) => (
341
+ <Slot key={s.id} slot={s} ppm={ppm} renderSlot={renderSlot} onSlotClick={onSlotClick} selected={selectedSlot != null && (selectedSlot === s.id || selectedSlot === s.start)} label={slotLabel} locale={locale} />
342
+ ))}
343
+ {hover !== null && !making && !drafting ? (
344
+ <div
345
+ aria-hidden="true"
346
+ className="pointer-events-none absolute inset-x-1 flex items-start rounded-md border border-dashed border-primary/40 px-1.5 pt-0.5 text-2xs font-medium text-primary"
347
+ style={{ top: hover * ppm + 1, height: 30 * ppm - 2 }}
348
+ >
349
+ + {minutesLabel(hover, { locale })}
350
+ </div>
351
+ ) : null}
352
+ {events.map((e) => (
353
+ <EventBlock key={e.id} event={e} pos={layout.get(e.id) ?? { col: 0, cols: 1, span: 1 }} ppm={ppm} api={api} index={index} days={days} colWidth={colWidth} />
354
+ ))}
355
+ {making ? (
356
+ <div aria-hidden="true" className="pointer-events-none absolute inset-x-1 z-10 rounded-md bg-primary/15 px-1.5 py-0.5 text-2xs font-medium text-primary ring-1 ring-primary" style={{ top: making.a * ppm, height: (making.b - making.a) * ppm }}>
357
+ {minutesLabel(making.a, { locale })} – {minutesLabel(making.b, { locale })}
358
+ </div>
359
+ ) : null}
360
+ {drafting ? (
361
+ <div
362
+ className="absolute inset-x-1 z-20 flex flex-col gap-0.5 rounded-md bg-card px-1.5 py-1 shadow-md ring-1 ring-primary"
363
+ style={{ top: drafting.from * ppm, height: Math.max((drafting.to - drafting.from) * ppm, 40) }}
364
+ >
365
+ <DraftInput label={`New task on ${label}, ${minutesLabel(drafting.from, { locale })} to ${minutesLabel(drafting.to, { locale })}`} onSubmit={api.saveDraft} onCancel={api.cancelDraft} />
366
+ <span data-nums className="text-2xs text-muted-foreground">
367
+ {minutesLabel(drafting.from, { locale })} – {minutesLabel(drafting.to, { locale })}
368
+ </span>
369
+ </div>
370
+ ) : null}
371
+ {nowMin !== null ? (
372
+ <div aria-hidden="true" className="pointer-events-none absolute inset-x-0 z-10 flex items-center" style={{ top: nowMin * ppm - 1 }}>
373
+ <span className="-ms-1 size-2.5 shrink-0 rounded-full bg-primary ring-2 ring-card" />
374
+ <span className="h-0.5 flex-1 bg-primary" />
375
+ </div>
376
+ ) : null}
377
+ </div>
378
+ );
379
+ }
380
+
381
+ function Slot({ slot, ppm, renderSlot, onSlotClick, selected, label = "Open", locale }) {
382
+ const style = { top: slot.from * ppm + 1, height: Math.max((slot.to - slot.from) * ppm - 2, 16) };
383
+ const words = `${label}, ${minutesLabel(slot.from, { locale })} to ${minutesLabel(slot.to, { locale })}`;
384
+ const face = renderSlot ? (
385
+ renderSlot(slot, { selected })
386
+ ) : (
387
+ <span data-nums className="truncate">
388
+ {minutesLabel(slot.from, { locale })}
389
+ </span>
390
+ );
391
+ const cls = cx(
392
+ "absolute inset-x-1 flex items-start overflow-hidden rounded-md border border-dashed px-1.5 pt-0.5 text-left text-2xs font-medium transition-colors duration-150",
393
+ selected ? "border-solid border-primary bg-primary text-primary-foreground" : "border-primary/50 bg-primary/5 text-primary",
394
+ );
395
+ if (!onSlotClick) {
396
+ return (
397
+ <div className={cx(cls, "pointer-events-none")} style={style} aria-label={words}>
398
+ {face}
399
+ </div>
400
+ );
401
+ }
402
+ return (
403
+ // raw: an open slot is a box on the grid placed by its times; a kit
404
+ // Button would pad and centre it as an action.
405
+ <button
406
+ type="button"
407
+ onClick={(e) => {
408
+ e.stopPropagation();
409
+ onSlotClick(slot);
410
+ }}
411
+ onPointerDown={(e) => e.stopPropagation()}
412
+ aria-label={words}
413
+ aria-pressed={selected}
414
+ style={style}
415
+ className={cx(cls, "z-[1] outline-none hover:bg-primary/15 focus-visible:ring-2 focus-visible:ring-ring", selected && "hover:bg-primary")}
416
+ >
417
+ {face}
418
+ </button>
419
+ );
420
+ }
421
+
422
+ function EventBlock({ event, pos, ppm, api, index, days, colWidth }) {
423
+ const { ctx, editable, locale } = api;
424
+ const task = event.task;
425
+ const done = isDone(task, ctx);
426
+ const paint = paintOf(task, ctx, api.colorBy);
427
+ const title = task.title || "Untitled";
428
+ const d = useDraggableTask(`ev:${event.id}`, { kind: "timed", task, event, colIndex: index, colWidth }, { disabled: !editable });
429
+ const canResize = editable && !event.deadline;
430
+ const r = useDraggableTask(`ry:${event.id}`, { kind: "resize-y", task, event }, { disabled: !canResize });
431
+
432
+ // While dragged, the block itself shows where it will land: snapped to
433
+ // the quarter hour and to a day column.
434
+ let from = event.from;
435
+ let to = event.to;
436
+ let dx = 0;
437
+ if (d.isDragging && d.transform) {
438
+ const w = colWidth();
439
+ const k = Math.max(-index, Math.min(days.length - 1 - index, Math.round(d.transform.x / w)));
440
+ dx = k * w;
441
+ const dm = Math.max(-event.from, Math.min(DAY_MINUTES - event.to, snapMinutes(d.transform.y / ppm, api.snap)));
442
+ from += dm;
443
+ to += dm;
444
+ }
445
+ if (r.isDragging && r.transform) to = Math.max(from + 15, Math.min(DAY_MINUTES, to + snapMinutes(r.transform.y / ppm, api.snap)));
446
+ const h = Math.max((to - from) * ppm, 18);
447
+ const tall = h >= 38;
448
+ const shown = { ...event, from, to };
449
+ const time = eventTimeLabel(shown, { locale });
450
+ const moving = d.isDragging || r.isDragging;
451
+
452
+ const keys = withKeys(d.handle, (e) => {
453
+ if (e.key === "Enter" || e.key === " ") return true;
454
+ if (!editable) return false;
455
+ if (e.key === "Delete" || e.key === "Backspace") {
456
+ e.preventDefault();
457
+ api.remove(task);
458
+ return true;
459
+ }
460
+ const step = { ArrowUp: -1, ArrowDown: 1, ArrowLeft: -1, ArrowRight: 1 }[e.key];
461
+ if (!step) return false;
462
+ e.preventDefault();
463
+ if (e.key === "ArrowLeft" || e.key === "ArrowRight") api.nudge(event, { dd: step });
464
+ else if (e.shiftKey) api.nudge(event, { dr: step * 15 });
465
+ else api.nudge(event, { dm: step * 15 });
466
+ return true;
467
+ });
468
+
469
+ return (
470
+ // raw: a timed block is a drag source placed by its times that also
471
+ // opens; a kit Button would centre and pad it as an action.
472
+ <button
473
+ type="button"
474
+ ref={d.setNodeRef}
475
+ data-cal-id={event.id}
476
+ {...(editable ? keys : { onKeyDown: keys.onKeyDown })}
477
+ onClick={api.open ? () => api.open(task) : undefined}
478
+ aria-label={`${title}, ${time}${done ? ", done" : ""}`}
479
+ aria-keyshortcuts={editable ? "ArrowUp ArrowDown ArrowLeft ArrowRight Shift+ArrowUp Shift+ArrowDown Enter Delete" : undefined}
480
+ title={`${title}\n${time}`}
481
+ style={{
482
+ top: from * ppm + 1,
483
+ height: h - 2,
484
+ left: `calc(${(pos.col / pos.cols) * 100}% + 2px)`,
485
+ width: `calc(${(pos.span / pos.cols) * 100}% - ${pos.col + pos.span < pos.cols ? 2 : 6}px)`,
486
+ transform: dx ? `translateX(${dx}px)` : undefined,
487
+ }}
488
+ className={cx(
489
+ "absolute flex flex-col overflow-hidden rounded-md border-s-[3px] px-1.5 text-left text-xs leading-tight text-foreground outline-none transition-[box-shadow,opacity,background-color] duration-150 focus-visible:z-20 focus-visible:ring-2 focus-visible:ring-ring",
490
+ paint.tint,
491
+ tall ? "py-1" : "justify-center py-0",
492
+ pos.cols > 1 && !moving && "ring-1 ring-card",
493
+ editable ? "cursor-grab active:cursor-grabbing" : api.open ? "cursor-pointer" : "cursor-default",
494
+ moving && "z-30 shadow-lg ring-1 ring-primary/40",
495
+ event.preview && "z-20 ring-2 ring-primary",
496
+ done && "text-muted-foreground",
497
+ )}
498
+ >
499
+ {tall ? (
500
+ <>
501
+ <span className={cx("line-clamp-2 font-medium", done && "line-through")}>{title}</span>
502
+ <span data-nums className="mt-0.5 truncate text-2xs text-muted-foreground">
503
+ {time}
504
+ </span>
505
+ </>
506
+ ) : (
507
+ <span className="flex min-w-0 items-baseline gap-1.5">
508
+ <span className={cx("truncate font-medium", done && "line-through")}>{title}</span>
509
+ <span data-nums className="shrink-0 text-2xs text-muted-foreground">
510
+ {minutesLabel(from, { locale })}
511
+ </span>
512
+ </span>
513
+ )}
514
+ {canResize ? (
515
+ <span
516
+ ref={r.setNodeRef}
517
+ {...r.handle}
518
+ role="presentation"
519
+ tabIndex={-1}
520
+ aria-hidden="true"
521
+ onClick={(e) => e.stopPropagation()}
522
+ className="absolute inset-x-0 bottom-0 h-2 cursor-ns-resize after:absolute after:inset-x-1/3 after:bottom-0.5 after:h-0.5 after:rounded-full after:bg-foreground/30 after:opacity-0 after:transition-opacity hover:after:opacity-100"
523
+ />
524
+ ) : null}
525
+ </button>
526
+ );
527
+ }
@@ -0,0 +1,87 @@
1
+ "use client";
2
+
3
+ import { Icon, cx } from "../../ui";
4
+ import { DueLabel, TaskChip, useDraggableTask, useDropTarget } from "../task-model";
5
+ import { withKeys } from "./paint";
6
+
7
+ // The unscheduled tray: open tasks with no date yet. Beside a wide
8
+ // calendar it is a column to drag from (onto a day, onto an hour) and to
9
+ // drop on (which takes the date off). On a phone, where there is nothing
10
+ // to drag onto, each task has a date picker instead.
11
+
12
+ export function UnscheduledTray({ tasks, api, mode = "panel", className }) {
13
+ const drop = useDropTarget("tray", { kind: "tray", label: "Unscheduled" }, { disabled: !api.editable || mode !== "panel" });
14
+ const dragging = drop.active && drop.active.data?.kind !== "tray";
15
+ return (
16
+ <section
17
+ ref={drop.setNodeRef}
18
+ aria-label="Unscheduled tasks"
19
+ className={cx(
20
+ "flex min-h-0 flex-col gap-2 transition-colors duration-150",
21
+ mode === "panel" && "rounded-lg border border-border bg-card p-3",
22
+ mode === "panel" && dragging && "border-dashed border-primary",
23
+ drop.isOver && "bg-accent",
24
+ className,
25
+ )}
26
+ >
27
+ {mode === "panel" ? (
28
+ <header className="flex items-baseline justify-between gap-2">
29
+ <h3 className="text-sm font-semibold">Unscheduled</h3>
30
+ <span data-nums className="text-xs text-muted-foreground">
31
+ {tasks.length}
32
+ </span>
33
+ </header>
34
+ ) : null}
35
+ {dragging ? (
36
+ <p className="text-xs text-muted-foreground">Drop here to take the date off.</p>
37
+ ) : tasks.length && api.editable && mode === "panel" ? (
38
+ <p className="text-xs text-muted-foreground">Drag a task onto a day or an hour to schedule it.</p>
39
+ ) : null}
40
+ {tasks.length ? (
41
+ <ul className="grid gap-1.5">
42
+ {tasks.map((t) => (mode === "panel" ? <TrayItem key={t.id} task={t} api={api} /> : <SheetItem key={t.id} task={t} api={api} />))}
43
+ </ul>
44
+ ) : (
45
+ <div className="grid place-items-center gap-1.5 rounded-md border border-dashed border-border px-3 py-6 text-center">
46
+ <Icon name="circle-check" size={18} className="text-muted-foreground" />
47
+ <p className="text-sm font-medium">Everything has a date</p>
48
+ <p className="text-xs text-muted-foreground">Tasks without one wait here.</p>
49
+ </div>
50
+ )}
51
+ </section>
52
+ );
53
+ }
54
+
55
+ function TrayItem({ task, api }) {
56
+ const d = useDraggableTask(`tray:${task.id}`, { kind: "tray", task }, { disabled: !api.editable });
57
+ const keys = withKeys(d.handle, (e) => e.key === "Enter");
58
+ return (
59
+ <li ref={d.setNodeRef} data-cal-id={task.id}>
60
+ <TaskChip
61
+ task={task}
62
+ ctx={api.ctx}
63
+ onOpen={api.open ?? undefined}
64
+ dragging={d.isDragging}
65
+ dragProps={api.editable ? keys : undefined}
66
+ className={cx("min-h-9", api.editable && "cursor-grab active:cursor-grabbing")}
67
+ />
68
+ </li>
69
+ );
70
+ }
71
+
72
+ function SheetItem({ task, api }) {
73
+ return (
74
+ <li className="group/row flex min-h-11 items-center gap-2">
75
+ <TaskChip task={task} ctx={api.ctx} onOpen={api.open ?? undefined} className="min-h-9 flex-1" />
76
+ {api.editable ? (
77
+ <DueLabel
78
+ value={null}
79
+ ctx={api.ctx}
80
+ quiet={false}
81
+ placeholder="Schedule"
82
+ onChange={(day) => day && api.update(task, { due: day }, `Scheduled “${task.title}”`)}
83
+ />
84
+ ) : null}
85
+ </li>
86
+ );
87
+ }