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,191 @@
1
+ "use client";
2
+
3
+ import { useEffect, useRef } from "react";
4
+ import { cx } from "./cx";
5
+ import { MenuPanel } from "./menu";
6
+ import { panelClass, usePopoverPosition } from "./popover";
7
+
8
+ // ContextMenu — the menu a right-click opens on a region: a row, a card,
9
+ // a canvas. The same `items` as Menu (actions, groups, checkboxes,
10
+ // radios, shortcuts, submenus) and the same keys, opened at the pointer
11
+ // and flipped to stay on screen. It opens on:
12
+ //
13
+ // a right-click (or Ctrl-click on a Mac) at the pointer
14
+ // a long press on touch under the finger
15
+ // Shift+F10 or the Menu key on a focused thing under that thing
16
+ //
17
+ // <ContextMenu label="Actions for Acme" items={[{ label: "Edit", onSelect }]}>
18
+ // <Row … />
19
+ // </ContextMenu>
20
+ //
21
+ // items as Menu's (./menu); none (or []) leaves the browser's own
22
+ // right-click menu alone
23
+ // label what a reader calls the menu (default "Actions")
24
+ // disabled true: the region behaves as if there were no menu
25
+ // className the region's (a <div> around children)
26
+ //
27
+ // The one rule: a context menu is a shortcut, never the only door. Every
28
+ // action in it is also somewhere a tap or a Tab can reach — a Menu on the
29
+ // row, a button in its page — because nobody discovers a right-click.
30
+ // Focus goes to the first item when it opens and back to what had it
31
+ // when it closes.
32
+ //
33
+ // The panel is a manual popover with its own light dismiss: an auto one
34
+ // would be closed by the very release of the button (or the finger) that
35
+ // opened it, since a right-click opens on press on a Mac.
36
+
37
+ const LONG_PRESS = 550; // ms; just past the OS's own, which wins where it fires
38
+
39
+ // A Menu open inside the region (a row's ⋯) keeps its own keys and clicks.
40
+ const inMenu = (e) => Boolean(e.target.closest?.('[role="menu"]'));
41
+
42
+ export function ContextMenu({ items, label = "Actions", disabled = false, className, children }) {
43
+ const pop = useRef(null);
44
+ const point = useRef(null);
45
+ const back = useRef(null);
46
+ const press = useRef({ timer: 0, x: 0, y: 0, at: 0 });
47
+ const opened = useRef(0);
48
+ const unwatch = useRef(null);
49
+ usePopoverPosition(point, pop, { side: "bottom", align: "start", offset: 2 });
50
+ const live = (items ?? []).filter(Boolean);
51
+ useEffect(() => () => unwatch.current?.(), []);
52
+
53
+ if (live.length === 0 || disabled) return <div className={className}>{children}</div>;
54
+
55
+ function hide() {
56
+ if (pop.current?.matches(":popover-open")) pop.current.hidePopover();
57
+ }
58
+
59
+ // Light dismiss: a press anywhere outside the panel (and its submenus,
60
+ // which sit inside it) closes it; so does Escape wherever focus is.
61
+ function watch() {
62
+ if (unwatch.current) return;
63
+ const down = (e) => {
64
+ if (!pop.current?.contains(e.target)) hide();
65
+ };
66
+ const key = (e) => {
67
+ if (e.key === "Escape" && !pop.current?.contains(e.target)) {
68
+ hide();
69
+ restore();
70
+ }
71
+ };
72
+ document.addEventListener("pointerdown", down, true);
73
+ document.addEventListener("keydown", key);
74
+ window.addEventListener("blur", hide);
75
+ unwatch.current = () => {
76
+ document.removeEventListener("pointerdown", down, true);
77
+ document.removeEventListener("keydown", key);
78
+ window.removeEventListener("blur", hide);
79
+ unwatch.current = null;
80
+ };
81
+ }
82
+
83
+ // `other` marks an open by a long press or a key, after which the
84
+ // browser's own contextmenu event (Android, Windows) is a duplicate.
85
+ function openAt(x, y, other = false) {
86
+ const p = pop.current;
87
+ const s = point.current;
88
+ if (!p || !s) return;
89
+ // The point is a fixed, empty box the panel is anchored to. Set it, then
90
+ // correct for a transformed ancestor that moves what "fixed" means;
91
+ // its width flips 0/1px so a measured placement sees a change.
92
+ s.style.left = `${x}px`;
93
+ s.style.top = `${y}px`;
94
+ const r = s.getBoundingClientRect();
95
+ s.style.left = `${2 * x - r.left}px`;
96
+ s.style.top = `${2 * y - r.top}px`;
97
+ s.style.width = s.style.width === "1px" ? "0px" : "1px";
98
+ if (!p.matches(":popover-open")) {
99
+ if (!back.current || !back.current.isConnected || p.contains(back.current)) back.current = document.activeElement;
100
+ } else p.hidePopover();
101
+ p.showPopover();
102
+ watch();
103
+ if (other) opened.current = Date.now();
104
+ p.querySelector('[role^="menuitem"]:not([disabled])')?.focus({ preventScroll: true });
105
+ }
106
+
107
+ function restore() {
108
+ const to = back.current;
109
+ back.current = null;
110
+ if (to && to !== document.body && to.isConnected) to.focus({ preventScroll: true });
111
+ }
112
+
113
+ function cancelPress() {
114
+ clearTimeout(press.current.timer);
115
+ press.current.timer = 0;
116
+ }
117
+
118
+ return (
119
+ <>
120
+ <div
121
+ className={cx("[-webkit-touch-callout:none]", className)}
122
+ onContextMenu={(e) => {
123
+ if (inMenu(e)) return;
124
+ e.preventDefault();
125
+ cancelPress();
126
+ // A long press or Shift+F10 opened it just now.
127
+ if (Date.now() - opened.current < 800) return;
128
+ openAt(e.clientX, e.clientY);
129
+ }}
130
+ onKeyDown={(e) => {
131
+ if ((e.key !== "ContextMenu" && !(e.shiftKey && e.key === "F10")) || inMenu(e)) return;
132
+ e.preventDefault();
133
+ const r = e.target.getBoundingClientRect();
134
+ openAt(r.left + Math.min(16, r.width / 2), r.bottom, true);
135
+ }}
136
+ onPointerDown={(e) => {
137
+ if (e.pointerType === "mouse" || inMenu(e)) return;
138
+ cancelPress();
139
+ const { clientX: x, clientY: y } = e;
140
+ press.current = {
141
+ x,
142
+ y,
143
+ at: 0,
144
+ timer: setTimeout(() => {
145
+ press.current.timer = 0;
146
+ press.current.at = Date.now();
147
+ openAt(x, y, true);
148
+ }, LONG_PRESS),
149
+ };
150
+ }}
151
+ onPointerMove={(e) => {
152
+ const p = press.current;
153
+ if (p.timer && Math.hypot(e.clientX - p.x, e.clientY - p.y) > 10) cancelPress();
154
+ }}
155
+ onPointerUp={cancelPress}
156
+ onPointerCancel={cancelPress}
157
+ onClickCapture={(e) => {
158
+ // The finger lifting after a long press is not a tap on what is under it.
159
+ if (Date.now() - press.current.at < 800) {
160
+ e.preventDefault();
161
+ e.stopPropagation();
162
+ }
163
+ }}
164
+ >
165
+ <span ref={point} aria-hidden="true" className="pointer-events-none fixed top-0 left-0 h-0 w-0" />
166
+ {children}
167
+ </div>
168
+ <MenuPanel
169
+ items={live}
170
+ label={label}
171
+ panelRef={pop}
172
+ popover="manual"
173
+ onToggle={(e) => {
174
+ // A submenu's toggle bubbles here; a close already followed by an
175
+ // open (a right-click elsewhere) is not a close.
176
+ if (e.target !== e.currentTarget || e.newState !== "closed" || pop.current?.matches(":popover-open")) return;
177
+ unwatch.current?.();
178
+ const a = document.activeElement;
179
+ if (!a || a === document.body || pop.current?.contains(a)) restore();
180
+ else back.current = null;
181
+ }}
182
+ onExit={() => {
183
+ hide();
184
+ restore();
185
+ }}
186
+ onClose={hide}
187
+ className={panelClass("auto", "min-w-44 max-h-[calc(100vh-1rem)] overflow-y-auto p-1")}
188
+ />
189
+ </>
190
+ );
191
+ }
@@ -0,0 +1,360 @@
1
+ "use client";
2
+
3
+ import { useState, useSyncExternalStore } from "react";
4
+ import { cx } from "./cx";
5
+ import { Icon } from "./icon";
6
+ import { Button, IconButton } from "./button";
7
+ import { Input } from "./field";
8
+ import { Popover } from "./popover";
9
+ import {
10
+ Calendar,
11
+ addDays,
12
+ addMonths,
13
+ endOfMonth,
14
+ formatDate,
15
+ parseDate,
16
+ plainSpaces,
17
+ startOfMonth,
18
+ useLocale,
19
+ useToday,
20
+ } from "./calendar";
21
+
22
+ // DatePicker — a field that reads like an input and opens a Calendar in a
23
+ // Popover: a due date, a report's span. The trigger shows the day in the
24
+ // reader's words ("Mar 12, 2026"); the value stays a "YYYY-MM-DD" string
25
+ // (calendar.jsx says why). Put it in <Field id> with the same `id`: the
26
+ // label points at the trigger.
27
+ //
28
+ // mode single (value "2026-03-12" | null) · range (value
29
+ // { from, to } | null)
30
+ // value, onChange(value) — controlled; or `defaultValue`
31
+ // placeholder "Pick a date" · "Pick a range"
32
+ // presets true (the defaults for the mode: Today, Tomorrow, In a
33
+ // week… · Today, Last 7 days, This month…) or your own
34
+ // [{ label, value }] — `value` a date, a { from, to }, or
35
+ // (today) → one. A list beside the calendar; a row above
36
+ // it on a phone
37
+ // clearable an × in the field once it holds a date (true)
38
+ // format Intl.DateTimeFormat options for the field ({ dateStyle:
39
+ // "medium" }); a range is written with formatRange
40
+ // disabled true: the whole field; a function (date) → true: those days
41
+ // min, max, numberOfMonths (range: 2 from 640px; always 1 on a phone),
42
+ // captionLayout, showWeekNumbers, weekStartsOn, locale, today — as
43
+ // Calendar
44
+ // name a hidden input for a form; a range submits two under it
45
+ // invalid the red edge — pair it with <Field error>
46
+ // size md (44px) · sm (36px)
47
+ // label what a screen reader calls the panel ("Choose a date")
48
+ // defaultOpen open on first render (a demo; a form never needs it)
49
+ //
50
+ // It closes when a day, a finished range or a preset is picked; the focus
51
+ // lands on the chosen day when it opens and back on the field when it
52
+ // closes.
53
+ //
54
+ // DateInput below is the typed way in: the browser's own date field
55
+ // (segments in the reader's order, digits only, never a free-text parse)
56
+ // with this Calendar one button away. Typing a date into a text box and
57
+ // parsing it is not robust across locales ("03/04" is two different
58
+ // days), which is why there is no text DateInput.
59
+
60
+ const FIELD =
61
+ "flex w-full items-center gap-2 rounded-md border border-input bg-background px-3 text-left text-sm text-foreground " +
62
+ "outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 " +
63
+ "aria-[invalid=true]:border-destructive";
64
+ const HEIGHT = { md: "min-h-11", sm: "min-h-9" };
65
+ // Presets: on a phone a scrolling row exactly as wide as the calendar (w-0
66
+ // plus a min width of its box: it never widens the panel); a column beside
67
+ // the calendar from 640px.
68
+ const PRESETS =
69
+ "-mx-3 flex w-0 min-w-[calc(100%+1.5rem)] gap-1 overflow-x-auto px-3 pb-1 " +
70
+ "sm:m-0 sm:w-36 sm:min-w-0 sm:flex-col sm:overflow-visible sm:border-r sm:border-border sm:p-0 sm:pr-3";
71
+
72
+ function useWide() {
73
+ return useSyncExternalStore(
74
+ (cb) => {
75
+ const q = window.matchMedia("(min-width: 640px)");
76
+ q.addEventListener("change", cb);
77
+ return () => q.removeEventListener("change", cb);
78
+ },
79
+ () => window.matchMedia("(min-width: 640px)").matches,
80
+ () => false,
81
+ );
82
+ }
83
+
84
+ /** The default presets. Each value is (today) → a date or a { from, to }. */
85
+ export const DATE_PRESETS = {
86
+ single: [
87
+ { label: "Today", value: (t) => t },
88
+ { label: "Tomorrow", value: (t) => addDays(t, 1) },
89
+ { label: "In a week", value: (t) => addDays(t, 7) },
90
+ { label: "In a month", value: (t) => addMonths(t, 1) },
91
+ ],
92
+ range: [
93
+ { label: "Today", value: (t) => ({ from: t, to: t }) },
94
+ { label: "Last 7 days", value: (t) => ({ from: addDays(t, -6), to: t }) },
95
+ { label: "Last 30 days", value: (t) => ({ from: addDays(t, -29), to: t }) },
96
+ { label: "This month", value: (t) => ({ from: startOfMonth(t), to: endOfMonth(t) }) },
97
+ { label: "Last month", value: (t) => ({ from: startOfMonth(addMonths(t, -1)), to: endOfMonth(addMonths(t, -1)) }) },
98
+ { label: "This year", value: (t) => ({ from: `${t.slice(0, 4)}-01-01`, to: `${t.slice(0, 4)}-12-31` }) },
99
+ ],
100
+ };
101
+
102
+ const same = (a, b) => (a && b && typeof a === "object" ? a.from === b.from && a.to === b.to : a === b);
103
+
104
+ /** The value in words: a day, or a span through formatRange. */
105
+ function words(mode, value, format, locale) {
106
+ if (mode !== "range") return value ? formatDate(value, format, locale) : "";
107
+ const { from, to } = value ?? {};
108
+ if (!from) return "";
109
+ if (!to) return `${formatDate(from, format, locale)} – …`;
110
+ const f = new Intl.DateTimeFormat(locale, { ...format, timeZone: "UTC" });
111
+ return plainSpaces(f.formatRange ? f.formatRange(parseDate(from), parseDate(to)) : `${f.format(parseDate(from))} – ${f.format(parseDate(to))}`);
112
+ }
113
+
114
+ export function DatePicker({
115
+ id,
116
+ mode = "single",
117
+ value: valueProp,
118
+ defaultValue = null,
119
+ onChange,
120
+ placeholder,
121
+ presets = false,
122
+ clearable = true,
123
+ format = { dateStyle: "medium" },
124
+ disabled,
125
+ min,
126
+ max,
127
+ numberOfMonths,
128
+ captionLayout,
129
+ showWeekNumbers,
130
+ weekStartsOn,
131
+ locale: localeProp,
132
+ today: todayProp,
133
+ name,
134
+ invalid,
135
+ size = "md",
136
+ align = "start",
137
+ label,
138
+ defaultOpen = false,
139
+ className,
140
+ }) {
141
+ const locale = useLocale(localeProp);
142
+ const today = useToday(todayProp);
143
+ const wide = useWide();
144
+ const [own, setOwn] = useState(defaultValue);
145
+ const value = valueProp !== undefined ? valueProp : own;
146
+ const [open, setOpen] = useState(defaultOpen);
147
+ const [month, setMonth] = useState(null);
148
+ const range = mode === "range";
149
+ const off = disabled === true;
150
+
151
+ const first = range ? value?.from : value;
152
+ const text = words(mode, value, format, locale);
153
+ const list = presets === true ? DATE_PRESETS[range ? "range" : "single"] : Array.isArray(presets) ? presets : [];
154
+ const shown = list.map((p) => ({ label: p.label, value: typeof p.value === "function" ? (today ? p.value(today) : null) : p.value }));
155
+
156
+ function set(v) {
157
+ if (valueProp === undefined) setOwn(v);
158
+ onChange?.(v);
159
+ }
160
+
161
+ // Each opening starts on the chosen day's month (or today's).
162
+ function onOpenChange(next) {
163
+ if (next) setMonth(startOfMonth(first) ?? startOfMonth(today));
164
+ setOpen(next);
165
+ }
166
+
167
+ function choose(v) {
168
+ set(v);
169
+ if (!range || (v?.from && v?.to)) setOpen(false);
170
+ }
171
+
172
+ const months = wide ? numberOfMonths ?? (range ? 2 : 1) : 1;
173
+ const showClear = clearable && !off && (range ? !!value?.from : !!value);
174
+
175
+ return (
176
+ <div className={cx("relative w-full", className)}>
177
+ <Popover
178
+ open={open}
179
+ onOpenChange={onOpenChange}
180
+ align={align}
181
+ width="auto"
182
+ className="p-3"
183
+ label={label ?? (range ? "Choose a date range" : "Choose a date")}
184
+ trigger={(props) => (
185
+ <button
186
+ {...props}
187
+ id={id}
188
+ type="button"
189
+ disabled={off}
190
+ aria-invalid={invalid ? true : undefined}
191
+ className={cx(FIELD, HEIGHT[size] ?? HEIGHT.md, showClear && "pr-11")}
192
+ >
193
+ <Icon name="calendar" className="shrink-0 text-muted-foreground" />
194
+ <span className={cx("min-w-0 flex-1 truncate", !text && "text-muted-foreground")}>
195
+ {text || placeholder || (range ? "Pick a range" : "Pick a date")}
196
+ </span>
197
+ </button>
198
+ )}
199
+ >
200
+ <div className="flex flex-col gap-3 sm:flex-row">
201
+ {shown.length ? (
202
+ <div
203
+ role="group"
204
+ aria-label="Presets"
205
+ className={PRESETS}
206
+ >
207
+ {shown.map((p) => {
208
+ const on = !!p.value && same(p.value, value);
209
+ return (
210
+ <Button
211
+ key={p.label}
212
+ size={wide ? "sm" : "md"}
213
+ variant={on ? "secondary" : "quiet"}
214
+ aria-pressed={on}
215
+ disabled={!p.value}
216
+ className="shrink-0 justify-start"
217
+ onClick={() => {
218
+ choose(p.value);
219
+ setMonth(startOfMonth(range ? p.value.from : p.value));
220
+ }}
221
+ >
222
+ {p.label}
223
+ </Button>
224
+ );
225
+ })}
226
+ </div>
227
+ ) : null}
228
+ <Calendar
229
+ mode={range ? "range" : "single"}
230
+ value={value}
231
+ onChange={choose}
232
+ month={month ?? undefined}
233
+ onMonthChange={(m) => setMonth(`${m}-01`)}
234
+ numberOfMonths={months}
235
+ min={min}
236
+ max={max}
237
+ disabled={typeof disabled === "function" ? disabled : undefined}
238
+ captionLayout={captionLayout}
239
+ showWeekNumbers={showWeekNumbers}
240
+ weekStartsOn={weekStartsOn}
241
+ locale={locale}
242
+ today={today ?? undefined}
243
+ autoFocus={open}
244
+ label={label ?? (range ? "Choose a date range" : "Choose a date")}
245
+ />
246
+ </div>
247
+ </Popover>
248
+ {showClear ? (
249
+ <IconButton
250
+ icon="x"
251
+ label="Clear date"
252
+ size="sm"
253
+ onClick={() => set(null)}
254
+ className={cx("absolute right-1", size === "sm" ? "top-0" : "top-1")}
255
+ />
256
+ ) : null}
257
+ {name && !range ? <input type="hidden" name={name} value={value ?? ""} /> : null}
258
+ {name && range ? (
259
+ <>
260
+ <input type="hidden" name={name} value={value?.from ?? ""} />
261
+ <input type="hidden" name={name} value={value?.to ?? ""} />
262
+ </>
263
+ ) : null}
264
+ </div>
265
+ );
266
+ }
267
+
268
+ /**
269
+ * A date typed or picked. The browser's own date field — segments in the
270
+ * reader's order, digits only, the value a "YYYY-MM-DD" string or "" while
271
+ * incomplete — with its built-in picker swapped for a button that opens
272
+ * this Calendar. onChange(date | null). Chrome and Safari hide their own
273
+ * picker icon; Firefox keeps its own beside ours.
274
+ *
275
+ * value, onChange(date | null), defaultValue, min, max, disabled
276
+ * (true, or a function for the Calendar's days), name, invalid, size,
277
+ * locale (the Calendar's; the field itself follows the browser), today
278
+ */
279
+ export function DateInput({
280
+ id,
281
+ value: valueProp,
282
+ defaultValue = null,
283
+ onChange,
284
+ min,
285
+ max,
286
+ disabled,
287
+ name,
288
+ invalid,
289
+ size = "md",
290
+ locale,
291
+ today,
292
+ weekStartsOn,
293
+ className,
294
+ ...rest
295
+ }) {
296
+ const [own, setOwn] = useState(defaultValue);
297
+ const value = valueProp !== undefined ? valueProp : own;
298
+ const [open, setOpen] = useState(false);
299
+ const [month, setMonth] = useState(null);
300
+ const off = disabled === true;
301
+ const out = !!value && ((min && value < min) || (max && value > max));
302
+
303
+ function set(v) {
304
+ if (valueProp === undefined) setOwn(v);
305
+ onChange?.(v);
306
+ }
307
+
308
+ return (
309
+ <div className={cx("relative w-full", className)}>
310
+ <Input
311
+ id={id}
312
+ type="date"
313
+ name={name}
314
+ value={value ?? ""}
315
+ min={min}
316
+ max={max}
317
+ disabled={off}
318
+ invalid={invalid || out}
319
+ onChange={(e) => set(e.target.value || null)}
320
+ className={cx(
321
+ "pr-12 [&::-webkit-calendar-picker-indicator]:hidden tabular-nums",
322
+ size === "sm" && "min-h-9",
323
+ )}
324
+ {...rest}
325
+ />
326
+ <span className={cx("absolute right-1", size === "sm" ? "top-0" : "top-1")}>
327
+ <Popover
328
+ open={open}
329
+ onOpenChange={(next) => {
330
+ if (next) setMonth(startOfMonth(value) ?? null);
331
+ setOpen(next);
332
+ }}
333
+ align="end"
334
+ width="auto"
335
+ className="p-3"
336
+ label="Choose a date"
337
+ trigger={(props) => <IconButton {...props} icon="calendar" label="Open calendar" size="sm" disabled={off} />}
338
+ >
339
+ <Calendar
340
+ value={value}
341
+ onChange={(v) => {
342
+ set(v);
343
+ setOpen(false);
344
+ }}
345
+ month={month ?? undefined}
346
+ onMonthChange={(m) => setMonth(`${m}-01`)}
347
+ min={min}
348
+ max={max}
349
+ disabled={typeof disabled === "function" ? disabled : undefined}
350
+ locale={locale}
351
+ today={today}
352
+ weekStartsOn={weekStartsOn}
353
+ autoFocus={open}
354
+ label="Choose a date"
355
+ />
356
+ </Popover>
357
+ </span>
358
+ </div>
359
+ );
360
+ }
@@ -0,0 +1,117 @@
1
+ import { cx } from "./cx";
2
+
3
+ // Parts of one whole as a single stacked bar: traffic by device, tickets
4
+ // by channel, spend by category. Server-safe (no hooks). Meter is one
5
+ // amount against a limit; this is N shares of a total, which always fill
6
+ // the bar — unless a `total` above their sum leaves a rest of track.
7
+ //
8
+ // Colour: each segment takes a categorical hue by default (segment i gets
9
+ // hue i+1, round 1..5, from --color-chart-N) — the parts are KINDS of a
10
+ // thing. A segment's status `tone` overrides it, for sentiment (positive ·
11
+ // neutral · negative feedback). Tone is status, hue is category.
12
+
13
+ // Literal class names, so Tailwind sees every one.
14
+ const HUE_FILLS = { 1: "bg-chart-1", 2: "bg-chart-2", 3: "bg-chart-3", 4: "bg-chart-4", 5: "bg-chart-5" };
15
+ const TONE_FILLS = {
16
+ neutral: "bg-muted-foreground",
17
+ success: "bg-success",
18
+ warning: "bg-warning",
19
+ danger: "bg-destructive",
20
+ info: "bg-info",
21
+ };
22
+ const SIZES = { thin: "h-1.5", md: "h-2.5" };
23
+
24
+ const EN = "en-US";
25
+ function shareText(pct) {
26
+ if (pct > 0 && pct < 1) return "<1%";
27
+ return `${Math.round(pct).toLocaleString(EN)}%`;
28
+ }
29
+
30
+ /**
31
+ * segments [{ label, value, tone?, hue? }] — value is a number in any
32
+ * unit; the bar shows each one's share of their total. A zero
33
+ * (or negative, or missing) value is skipped
34
+ * size thin (6px, a list row) · md (10px)
35
+ * legend a row under the bar: dot, label, share %
36
+ * label the bar's name, read to a screen reader with every
37
+ * segment's label and share: "Traffic by device: Desktop 62%, …"
38
+ * total the whole the shares are of, in the segments' unit: 100 GB of
39
+ * storage, a $1,000 budget. When the segments sum to less, the
40
+ * rest is drawn as unfilled track at the end and every share is
41
+ * of `total`. Left out (or not above the sum), the segments fill
42
+ * the bar, as before
43
+ * restLabel names that rest — "Free", "Remaining" — in the legend (a
44
+ * hollow dot) and to a screen reader; left out, the rest is
45
+ * track only
46
+ * formatValue (value) → string — the legend shows each value in it
47
+ * ("$450") instead of the share; a reader hears both
48
+ * formatLegend ({ label, value, pct, share, rest }) → the words after a
49
+ * legend label, whole ("$450 · 52%"); wins over formatValue.
50
+ * `rest` is true for the restLabel entry
51
+ *
52
+ * tone success · warning · danger · info · neutral — sentiment,
53
+ * overrides the hue
54
+ * hue 1..5 — pin a category's colour (so "Email" is the same hue
55
+ * in every chart); default the segment's place in the list
56
+ *
57
+ * An empty total (no positive values) draws the empty track and reads as
58
+ * "no data". Thin gaps separate the segments; the ends are round.
59
+ */
60
+ export function Distribution({ segments = [], size = "md", legend = false, label, total: whole, restLabel, formatValue, formatLegend, className }) {
61
+ const parts = segments
62
+ .map((s, i) => ({ ...s, i }))
63
+ .filter((s) => typeof s.value === "number" && Number.isFinite(s.value) && s.value > 0);
64
+ const sum = parts.reduce((n, s) => n + s.value, 0);
65
+ // A `total` above the sum leaves a rest: unfilled track, optionally named.
66
+ const rest = typeof whole === "number" && Number.isFinite(whole) && whole > sum ? whole - sum : 0;
67
+ const total = sum + rest;
68
+ const shown = parts.map((s) => {
69
+ const fill = TONE_FILLS[s.tone] ?? HUE_FILLS[s.hue] ?? HUE_FILLS[(s.i % 5) + 1];
70
+ const pct = total > 0 ? (s.value / total) * 100 : 0;
71
+ return { key: s.key ?? `${s.label}-${s.i}`, label: s.label, value: s.value, fill, pct, share: shareText(pct) };
72
+ });
73
+ const restPct = total > 0 ? (rest / total) * 100 : 0;
74
+ const restPart = rest > 0 ? { key: "__rest", label: restLabel, value: rest, pct: restPct, share: shareText(restPct), rest: true } : null;
75
+ const named = [...shown, ...(restPart && restLabel ? [restPart] : [])];
76
+ const words = (s) => (formatValue ? `${s.label} ${formatValue(s.value)}, ${s.share}` : `${s.label} ${s.share}`);
77
+ const said = shown.length
78
+ ? `${label ? `${label}: ` : ""}${named.map(words).join(", ")}`
79
+ : `${label ? `${label}: ` : ""}no data`;
80
+ const legendText = (s) =>
81
+ formatLegend ? formatLegend({ label: s.label, value: s.value, pct: s.pct, share: s.share, rest: Boolean(s.rest) }) : formatValue ? formatValue(s.value) : s.share;
82
+ const track = SIZES[size] ?? SIZES.md;
83
+
84
+ return (
85
+ <div className={cx("grid gap-2", className)}>
86
+ <div role="img" aria-label={said} className={cx("flex w-full gap-0.5 overflow-hidden rounded-full", track, !shown.length && "bg-accent")}>
87
+ {shown.map((s) => (
88
+ <span
89
+ key={s.key}
90
+ className={cx("h-full min-w-0.5 transition-[flex-grow] duration-300 ease-out motion-reduce:transition-none", s.fill)}
91
+ style={{ flexGrow: s.value, flexShrink: 1, flexBasis: 0 }}
92
+ />
93
+ ))}
94
+ {restPart ? <span className="h-full min-w-0.5 bg-accent" style={{ flexGrow: rest, flexShrink: 1, flexBasis: 0 }} /> : null}
95
+ </div>
96
+ {legend && shown.length ? (
97
+ <ul aria-hidden="true" className={cx("flex flex-wrap items-center gap-x-3 gap-y-1", size === "thin" ? "text-xs" : "text-sm")}>
98
+ {named.map((s) => (
99
+ <li key={s.key} className="inline-flex min-w-0 items-center gap-1.5 text-muted-foreground">
100
+ <span
101
+ className={cx(
102
+ "shrink-0 rounded-full",
103
+ size === "thin" ? "size-1.5" : "size-2",
104
+ s.rest ? "border border-muted-foreground bg-accent" : s.fill,
105
+ )}
106
+ />
107
+ <span className="min-w-0 truncate">{s.label}</span>
108
+ <span data-nums className="font-medium text-foreground">
109
+ {legendText(s)}
110
+ </span>
111
+ </li>
112
+ ))}
113
+ </ul>
114
+ ) : null}
115
+ </div>
116
+ );
117
+ }