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
@@ -1,73 +1,457 @@
1
1
  "use client";
2
2
 
3
- import { useEffect, useId, useRef, useState } from "react";
3
+ import { createContext, useContext, useEffect, useId, useMemo, useRef, useState } from "react";
4
4
  import { cx } from "./cx";
5
5
  import { Icon } from "./icon";
6
+ import { KbdGroup } from "./separator";
7
+ import { panelClass, usePopoverPosition } from "./popover";
6
8
 
7
- // Menu — the "…" of a row, the kebab of a card. The Popover API does the
8
- // hard part (top layer, light dismiss, Escape); this positions it under
9
- // its button and moves focus with the arrow keys. `items` with nothing in
10
- // them render nothing: a menu is earned by having actions.
9
+ // Menu — the "…" of a row, the kebab of a card, a "View" or "Account"
10
+ // dropdown. The Popover API does the hard part (top layer, light dismiss,
11
+ // Escape); usePopoverPosition (./popover) keeps it beside its button,
12
+ // flipping at the window's edge and following scroll. The keys are the
13
+ // WAI-ARIA menu pattern: arrows, Home/End, a first letter jumps to the
14
+ // item it starts, ArrowRight/Left into and out of a submenu, Escape and
15
+ // Tab close it. `items` with nothing in them render nothing: a menu is
16
+ // earned by having actions.
11
17
  //
12
- // items [{ label, onSelect, icon?, tone?: "danger", disabled? }]
18
+ // items the entries, in order; any mix of
19
+ // { label, onSelect, icon?, tone?: "danger", disabled?,
20
+ // shortcut?: "⌘K", description? } an action (closes)
21
+ // { type: "checkbox", label, checked, onCheckedChange(next),
22
+ // icon?, shortcut?, description?, disabled? } stays open
23
+ // { type: "radio", label?, value, onValueChange(value),
24
+ // options: [{ value, label, description?, disabled? }] or
25
+ // ["a", "b"] } stays open
26
+ // { group: "Account", items: [...] } a labelled group
27
+ // { type: "label", label } a heading (a reader skips it;
28
+ // `group` is the one it names)
29
+ // { type: "separator" } a line
30
+ // { label, icon?, items: [...] } a submenu: to the side on a
31
+ // wide window, inline on a phone
32
+ // a falsy entry is skipped, so `cond && {…}` works
13
33
  // trigger a node to use as the button's content; default is the ⋯ icon
34
+ // label what a reader calls the menu, and the button: always the ⋯
35
+ // one's name (default "More"); with a `trigger`, the button's
36
+ // name only when given — else its visible text is. Begin it
37
+ // with that text ("Ada Lovelace, account menu"), so a voice
38
+ // user who says what they see still hits it
39
+ // side bottom (default) · top · right · left; flips when no room
40
+ // align end (default) · start · center, along that side
41
+ // size the button: sm (36px, the default — a row's or a card's ⋯) ·
42
+ // md (44px, a touch target: a toolbar, a menubar, a phone)
43
+ //
44
+ // A shortcut is only a hint: the menu draws it, the app binds it. The
45
+ // one rule: an action closes the menu, a choice (checkbox, radio) does
46
+ // not — it is a setting, and the next one is usually beside it.
47
+ //
48
+ // MenuPanel is the menu itself without its button — ContextMenu
49
+ // (./context-menu) opens the same `items` at the pointer.
50
+
51
+ const Root = createContext({ close() {}, narrow: false });
52
+ const Level = createContext(null);
53
+
54
+ const SYMBOL_KEYS = { "⌘": "Meta", "⌥": "Alt", "⇧": "Shift", "⌃": "Control", "↵": "Enter", "⏎": "Enter", "⌫": "Backspace", "⌦": "Delete", "⎋": "Escape", "⇥": "Tab", "↑": "ArrowUp", "↓": "ArrowDown", "←": "ArrowLeft", "→": "ArrowRight" };
55
+ const SYMBOLS = /[⌘⌥⇧⌃↵⏎⌫⌦⎋⇥↑↓←→]|[^⌘⌥⇧⌃↵⏎⌫⌦⎋⇥↑↓←→]+/g;
56
+
57
+ // "⌘K" → ["⌘", "K"]; "Ctrl+Shift+P" → ["Ctrl", "Shift", "P"]; an array as is.
58
+ function keysOf(shortcut) {
59
+ if (Array.isArray(shortcut)) return shortcut;
60
+ const s = String(shortcut).trim();
61
+ return /[+\s]/.test(s) && s.length > 1 ? s.split(/\s*\+\s*|\s+/).filter(Boolean) : s.match(SYMBOLS) ?? [];
62
+ }
63
+
64
+ // Only this menu's items — not a submenu's, which sit inside it.
65
+ function own(menu) {
66
+ if (!menu) return [];
67
+ return [...menu.querySelectorAll('[role^="menuitem"]:not([disabled])')].filter((n) => n.closest('[role="menu"]') === menu);
68
+ }
69
+
70
+ const nameOf = (n) => (n.dataset.text || n.textContent || "").trim().toLowerCase();
71
+
72
+ // A first letter moves to the next item it starts; letters typed quickly
73
+ // spell a longer prefix; the same letter again walks through its items.
74
+ function typeahead(nodes, key, memo) {
75
+ const now = Date.now();
76
+ memo.text = now - memo.at < 500 ? memo.text + key.toLowerCase() : key.toLowerCase();
77
+ memo.at = now;
78
+ const same = [...memo.text].every((c) => c === memo.text[0]);
79
+ const q = same ? memo.text[0] : memo.text;
80
+ const i = nodes.indexOf(document.activeElement);
81
+ const from = i < 0 ? 0 : i + (same ? 1 : 0);
82
+ return [...nodes.slice(from), ...nodes.slice(0, from)].find((n) => nameOf(n).startsWith(q));
83
+ }
84
+
85
+ function useNarrow() {
86
+ const [narrow, setNarrow] = useState(false);
87
+ useEffect(() => {
88
+ const mq = window.matchMedia("(max-width: 767.98px)");
89
+ const on = () => setNarrow(mq.matches);
90
+ on();
91
+ mq.addEventListener("change", on);
92
+ return () => mq.removeEventListener("change", on);
93
+ }, []);
94
+ return narrow;
95
+ }
96
+
97
+ const focus = (n) => n?.focus({ preventScroll: true });
98
+
99
+ const ITEM =
100
+ "flex w-full min-h-10 items-center gap-2 rounded-sm px-2 text-left text-sm outline-none hover:bg-accent focus-visible:bg-accent disabled:opacity-50";
101
+
102
+ /** The menu's list — role=menu, its keys and its items — for a part that
103
+ * opens it its own way (Menu under a button, ContextMenu at the pointer).
104
+ * `onExit` runs on Escape and Tab; `onClose` closes everything after an
105
+ * action. Pass `popover` and a `panelRef` to make it the floating panel. */
106
+ export function MenuPanel({ items, label, labelledBy, id, panelRef, popover, onToggle, onExit, onClose, onBack, className, style, onPointerEnter }) {
107
+ const root = useContext(Root);
108
+ const narrow = useNarrow();
109
+ const [openSub, setOpenSub] = useState(null);
110
+ const timer = useRef(0);
111
+ const typed = useRef({ text: "", at: 0 });
112
+ const live = (items ?? []).filter(Boolean);
113
+ // A level that holds choices keeps a gutter for their marks, so every
114
+ // label in it starts at the same line.
115
+ const gutter = live.some((it) => it.type === "checkbox" || it.type === "radio" || (it.group && (it.items ?? []).some((c) => c?.type === "checkbox" || c?.type === "radio")));
116
+
117
+ useEffect(() => () => clearTimeout(timer.current), []);
118
+
119
+ const level = useMemo(
120
+ () => ({
121
+ openSub,
122
+ setOpenSub,
123
+ gutter,
124
+ later(fn, ms) {
125
+ clearTimeout(timer.current);
126
+ timer.current = setTimeout(fn, ms);
127
+ },
128
+ cancel: () => clearTimeout(timer.current),
129
+ }),
130
+ [openSub, gutter],
131
+ );
132
+ const ctx = useMemo(() => (onClose ? { close: onClose, narrow } : { ...root, narrow }), [onClose, root, narrow]);
133
+
134
+ function onKeyDown(e) {
135
+ const menu = e.currentTarget;
136
+ const nodes = own(menu);
137
+ const i = nodes.indexOf(document.activeElement);
138
+ const k = e.key;
139
+ if (k === "ArrowDown") focus(nodes[(i + 1) % nodes.length]);
140
+ else if (k === "ArrowUp") focus(nodes[(i - 1 + nodes.length) % nodes.length]);
141
+ else if (k === "Home" || k === "PageUp") focus(nodes[0]);
142
+ else if (k === "End" || k === "PageDown") focus(nodes[nodes.length - 1]);
143
+ else if (k === "ArrowLeft" && onBack) onBack();
144
+ else if (k === "Escape") (onBack ?? onExit)?.();
145
+ else if (k === "Tab" && onExit) onExit();
146
+ else if (k.length === 1 && k !== " " && !e.ctrlKey && !e.metaKey && !e.altKey) focus(typeahead(nodes, k, typed.current));
147
+ else return; // Tab in a submenu reaches the top menu, which closes
148
+ e.preventDefault();
149
+ e.stopPropagation();
150
+ }
151
+
152
+ return (
153
+ <Root.Provider value={ctx}>
154
+ <Level.Provider value={level}>
155
+ <div
156
+ ref={panelRef}
157
+ id={id}
158
+ popover={popover}
159
+ role="menu"
160
+ aria-label={labelledBy ? undefined : label}
161
+ aria-labelledby={labelledBy}
162
+ aria-orientation="vertical"
163
+ tabIndex={-1}
164
+ onToggle={onToggle}
165
+ onKeyDown={onKeyDown}
166
+ onPointerEnter={onPointerEnter}
167
+ className={className}
168
+ style={style}
169
+ >
170
+ <Entries items={live} />
171
+ </div>
172
+ </Level.Provider>
173
+ </Root.Provider>
174
+ );
175
+ }
176
+
177
+ function Entries({ items }) {
178
+ return items.filter(Boolean).map((item, i) => <Entry key={item.key ?? i} item={item} />);
179
+ }
180
+
181
+ function Heading({ id, children }) {
182
+ const { gutter } = useContext(Level);
183
+ return (
184
+ <div id={id} role={id ? undefined : "none"} className={cx("px-2 py-1.5 text-xs font-medium text-muted-foreground", gutter && "pl-8")}>
185
+ {children}
186
+ </div>
187
+ );
188
+ }
189
+
190
+ function Entry({ item }) {
191
+ if (item.type === "separator") return <div role="separator" className="-mx-1 my-1 h-px bg-border" />;
192
+ if (item.type === "label") return <Heading>{item.label}</Heading>;
193
+ if (item.type === "checkbox") return <Choice item={item} kind="checkbox" checked={Boolean(item.checked)} onPick={() => item.onCheckedChange?.(!item.checked)} />;
194
+ if (item.type === "radio") return <RadioGroup item={item} />;
195
+ if (item.group !== undefined) return <Group item={item} />;
196
+ if (Array.isArray(item.items)) return <Submenu item={item} />;
197
+ return <Action item={item} />;
198
+ }
199
+
200
+ function Group({ item }) {
201
+ const id = useId();
202
+ const live = (item.items ?? []).filter(Boolean);
203
+ if (live.length === 0) return null;
204
+ return (
205
+ <div role="group" aria-labelledby={item.group ? id : undefined}>
206
+ {item.group ? <Heading id={id}>{item.group}</Heading> : null}
207
+ <Entries items={live} />
208
+ </div>
209
+ );
210
+ }
211
+
212
+ function RadioGroup({ item }) {
213
+ const id = useId();
214
+ const options = (item.options ?? []).filter((o) => o !== null && o !== undefined && o !== false).map((o) => (typeof o === "object" ? o : { value: o, label: String(o) }));
215
+ return (
216
+ <div role="group" aria-labelledby={item.label ? id : undefined}>
217
+ {item.label ? <Heading id={id}>{item.label}</Heading> : null}
218
+ {options.map((o) => (
219
+ <Choice key={String(o.value)} item={o} kind="radio" checked={o.value === item.value} onPick={() => o.value !== item.value && item.onValueChange?.(o.value)} />
220
+ ))}
221
+ </div>
222
+ );
223
+ }
14
224
 
15
- export function Menu({ items, label = "More", trigger, align = "end", className }) {
225
+ // One row's inside: the mark or icon, the label (and its description),
226
+ // the shortcut, a submenu's chevron.
227
+ function useRow(item) {
16
228
  const id = useId();
229
+ const { gutter } = useContext(Level);
230
+ const keys = item.shortcut ? keysOf(item.shortcut) : null;
231
+ const props = {
232
+ "data-text": typeof item.label === "string" ? item.label : undefined,
233
+ "aria-labelledby": item.description ? `${id}l` : undefined,
234
+ "aria-describedby": item.description ? `${id}d` : undefined,
235
+ "aria-keyshortcuts": keys ? keys.map((k) => SYMBOL_KEYS[k] ?? k).join("+") : undefined,
236
+ };
237
+ const body = (lead, tail) => (
238
+ <>
239
+ {lead ?? (item.icon ? <Icon name={item.icon} /> : gutter ? <span aria-hidden="true" className="size-4 shrink-0" /> : null)}
240
+ {item.description ? (
241
+ <span className="min-w-0 flex-1 py-1.5">
242
+ <span id={`${id}l`} className="block">
243
+ {item.label}
244
+ </span>
245
+ <span id={`${id}d`} className="block max-w-64 text-xs text-muted-foreground">
246
+ {item.description}
247
+ </span>
248
+ </span>
249
+ ) : (
250
+ <span className="min-w-0 flex-1">{item.label}</span>
251
+ )}
252
+ {keys ? (
253
+ <span aria-hidden="true" className="ml-auto shrink-0 pl-4">
254
+ <KbdGroup keys={keys} />
255
+ </span>
256
+ ) : null}
257
+ {tail}
258
+ </>
259
+ );
260
+ return { props, body };
261
+ }
262
+
263
+ // Hovering an item with a mouse takes the focus to it (so the keys go on
264
+ // from there) and closes a sibling's submenu a moment later.
265
+ function useHover() {
266
+ const level = useContext(Level);
267
+ const { narrow } = useContext(Root);
268
+ return (e) => {
269
+ if (e.pointerType !== "mouse" || narrow) return;
270
+ focus(e.currentTarget);
271
+ if (level.openSub) level.later(() => level.setOpenSub(null), 200);
272
+ else level.cancel();
273
+ };
274
+ }
275
+
276
+ function Action({ item }) {
277
+ const { close } = useContext(Root);
278
+ const hover = useHover();
279
+ const { props, body } = useRow(item);
280
+ return (
281
+ <button
282
+ type="button"
283
+ role="menuitem"
284
+ disabled={item.disabled}
285
+ {...props}
286
+ onPointerEnter={hover}
287
+ onClick={() => {
288
+ close();
289
+ item.onSelect?.();
290
+ }}
291
+ className={cx(ITEM, item.tone === "danger" && "text-destructive")}
292
+ >
293
+ {body()}
294
+ </button>
295
+ );
296
+ }
297
+
298
+ function Choice({ item, kind, checked, onPick }) {
299
+ const hover = useHover();
300
+ const { props, body } = useRow(item);
301
+ const mark = (
302
+ <span aria-hidden="true" className="grid size-4 shrink-0 place-items-center">
303
+ {checked ? kind === "checkbox" ? <Icon name="check" size={16} /> : <span className="size-2 rounded-full bg-current" /> : null}
304
+ </span>
305
+ );
306
+ return (
307
+ <button
308
+ type="button"
309
+ role={kind === "checkbox" ? "menuitemcheckbox" : "menuitemradio"}
310
+ aria-checked={checked}
311
+ disabled={item.disabled}
312
+ {...props}
313
+ onPointerEnter={hover}
314
+ onClick={onPick}
315
+ className={cx(ITEM, item.tone === "danger" && "text-destructive")}
316
+ >
317
+ {body(mark)}
318
+ </button>
319
+ );
320
+ }
321
+
322
+ const NONE = { current: null };
323
+
324
+ function Submenu({ item }) {
325
+ const key = useId();
326
+ const level = useContext(Level);
327
+ const { narrow } = useContext(Root);
17
328
  const btn = useRef(null);
18
- const pop = useRef(null);
19
- const [open, setOpen] = useState(false);
20
- const live = (items ?? []).filter(Boolean);
329
+ const sub = useRef(null);
330
+ const wantFocus = useRef(false);
331
+ const open = level.openSub === key;
332
+ const live = (item.items ?? []).filter(Boolean);
333
+ const { props, body } = useRow(item);
334
+ usePopoverPosition(btn, narrow ? NONE : sub, { side: "right", align: "start", offset: 2 });
21
335
 
22
- // A fixed-positioned popover does not follow its button; close it when
23
- // the page moves rather than leave it floating where the button was.
336
+ // The state drives the panel; a light dismiss comes back through onToggle.
24
337
  useEffect(() => {
25
- if (!open) return;
26
- const close = () => pop.current?.hidePopover();
27
- window.addEventListener("resize", close);
28
- window.addEventListener("scroll", close, true);
29
- return () => {
30
- window.removeEventListener("resize", close);
31
- window.removeEventListener("scroll", close, true);
32
- };
33
- }, [open]);
338
+ const p = sub.current;
339
+ if (p && !narrow) {
340
+ const shown = p.matches(":popover-open");
341
+ if (open && !shown) p.showPopover();
342
+ else if (!open && shown) p.hidePopover();
343
+ }
344
+ if (open && wantFocus.current) {
345
+ wantFocus.current = false;
346
+ focus(own(sub.current)[0]);
347
+ }
348
+ }, [open, narrow]);
34
349
 
35
350
  if (live.length === 0) return null;
36
351
 
37
- function place() {
38
- const b = btn.current?.getBoundingClientRect();
39
- const p = pop.current;
40
- if (!b || !p) return;
41
- p.style.position = "fixed";
42
- p.style.top = `${Math.round(b.bottom + 4)}px`;
43
- const width = p.offsetWidth || 200;
44
- const left = align === "end" ? b.right - width : b.left;
45
- p.style.left = `${Math.max(8, Math.min(left, window.innerWidth - width - 8))}px`;
352
+ function show(withFocus) {
353
+ wantFocus.current = withFocus;
354
+ level.cancel();
355
+ if (open && withFocus) focus(own(sub.current)[0]);
356
+ else level.setOpenSub(key);
357
+ }
358
+ function back() {
359
+ level.setOpenSub(null);
360
+ focus(btn.current);
46
361
  }
47
362
 
48
- function toggle() {
49
- const p = pop.current;
50
- if (!p) return;
51
- if (p.matches(":popover-open")) p.hidePopover();
52
- else {
53
- p.showPopover();
54
- place();
55
- p.querySelector('[role="menuitem"]:not([disabled])')?.focus();
363
+ return (
364
+ <div role="none">
365
+ <button
366
+ ref={btn}
367
+ type="button"
368
+ role="menuitem"
369
+ aria-haspopup="menu"
370
+ aria-expanded={open}
371
+ aria-controls={open ? `${key}m` : undefined}
372
+ id={`${key}t`}
373
+ disabled={item.disabled}
374
+ {...props}
375
+ onPointerEnter={(e) => {
376
+ if (e.pointerType !== "mouse" || narrow) return;
377
+ focus(e.currentTarget);
378
+ if (!open) level.later(() => level.setOpenSub(key), 100);
379
+ else level.cancel();
380
+ }}
381
+ onClick={(e) => {
382
+ // A key (Enter, Space) goes in; a click opens it, or closes it
383
+ // again on a phone, where the list unfolds in place.
384
+ if (e.detail === 0) show(true);
385
+ else if (open && narrow) level.setOpenSub(null);
386
+ else show(false);
387
+ }}
388
+ onKeyDown={(e) => {
389
+ if (e.key !== "ArrowRight") return;
390
+ e.preventDefault();
391
+ e.stopPropagation();
392
+ show(true);
393
+ }}
394
+ className={cx(ITEM, "aria-expanded:bg-accent")}
395
+ >
396
+ {body(null, <Icon name={narrow ? "chevron-down" : "chevron-right"} size={16} className={cx("ml-auto shrink-0 text-muted-foreground transition-transform motion-reduce:transition-none", narrow && open && "rotate-180")} />)}
397
+ </button>
398
+ {narrow ? (
399
+ open ? (
400
+ <MenuPanel id={`${key}m`} items={live} labelledBy={`${key}t`} panelRef={sub} onBack={back} className="ml-4 border-l border-border pl-1" />
401
+ ) : null
402
+ ) : (
403
+ <MenuPanel
404
+ id={`${key}m`}
405
+ items={live}
406
+ labelledBy={`${key}t`}
407
+ panelRef={sub}
408
+ popover="auto"
409
+ onBack={back}
410
+ onPointerEnter={() => level.cancel()}
411
+ onToggle={(e) => {
412
+ if (e.target === e.currentTarget && e.newState === "closed" && level.openSub === key) level.setOpenSub(null);
413
+ }}
414
+ className={panelClass("auto", "min-w-44 max-h-[calc(100vh-1rem)] overflow-y-auto p-1")}
415
+ />
416
+ )}
417
+ </div>
418
+ );
419
+ }
420
+
421
+ const TRIGGER_SIZES = {
422
+ sm: "min-h-9 min-w-9 px-2",
423
+ md: "min-h-11 min-w-11 px-3",
424
+ };
425
+
426
+ export function Menu({ items, label, trigger, align = "end", side = "bottom", size = "sm", className }) {
427
+ const name = label ?? "More";
428
+ const id = useId();
429
+ const btn = useRef(null);
430
+ const pop = useRef(null);
431
+ const start = useRef("first");
432
+ const [open, setOpen] = useState(false);
433
+ const live = (items ?? []).filter(Boolean);
434
+ usePopoverPosition(btn, pop, { side, align, offset: 4 });
435
+
436
+ if (live.length === 0) return null;
437
+
438
+ function onToggle(e) {
439
+ // React bubbles toggle: a submenu's opening is not this menu's.
440
+ if (e.target !== e.currentTarget) return;
441
+ const now = e.newState === "open";
442
+ setOpen(now);
443
+ if (now) {
444
+ const nodes = own(pop.current);
445
+ focus(start.current === "last" ? nodes[nodes.length - 1] : nodes[0]);
446
+ start.current = "first";
447
+ } else if (!document.activeElement || document.activeElement === document.body || pop.current?.contains(document.activeElement)) {
448
+ focus(btn.current);
56
449
  }
57
450
  }
58
451
 
59
- function onKeyDown(e) {
60
- const nodes = [...(pop.current?.querySelectorAll('[role="menuitem"]:not([disabled])') ?? [])];
61
- const i = nodes.indexOf(document.activeElement);
62
- if (e.key === "ArrowDown") nodes[(i + 1) % nodes.length]?.focus();
63
- else if (e.key === "ArrowUp") nodes[(i - 1 + nodes.length) % nodes.length]?.focus();
64
- else if (e.key === "Home") nodes[0]?.focus();
65
- else if (e.key === "End") nodes[nodes.length - 1]?.focus();
66
- else if (e.key === "Tab" || e.key === "Escape") {
67
- pop.current?.hidePopover();
68
- btn.current?.focus();
69
- } else return;
70
- e.preventDefault();
452
+ function exit() {
453
+ pop.current?.hidePopover();
454
+ focus(btn.current);
71
455
  }
72
456
 
73
457
  return (
@@ -75,49 +459,37 @@ export function Menu({ items, label = "More", trigger, align = "end", className
75
459
  <button
76
460
  ref={btn}
77
461
  type="button"
462
+ popoverTarget={id}
78
463
  aria-haspopup="menu"
79
464
  aria-expanded={open}
80
465
  aria-controls={id}
81
- aria-label={trigger ? undefined : label}
82
- title={trigger ? undefined : label}
83
- onClick={toggle}
466
+ aria-label={trigger ? label : name}
467
+ title={trigger ? undefined : name}
468
+ onKeyDown={(e) => {
469
+ if (e.key !== "ArrowDown" && e.key !== "ArrowUp") return;
470
+ e.preventDefault();
471
+ start.current = e.key === "ArrowUp" ? "last" : "first";
472
+ if (!pop.current?.matches(":popover-open")) pop.current?.showPopover();
473
+ }}
84
474
  className={cx(
85
- "inline-flex min-h-9 min-w-9 items-center justify-center gap-2 rounded-md px-2 text-sm text-foreground outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring",
475
+ "inline-flex items-center justify-center gap-2 rounded-md text-sm text-foreground outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring",
476
+ TRIGGER_SIZES[size] ?? TRIGGER_SIZES.sm,
86
477
  className,
87
478
  )}
88
479
  >
89
480
  {trigger ?? <Icon name="more" />}
90
481
  </button>
91
- <div
92
- ref={pop}
482
+ <MenuPanel
93
483
  id={id}
484
+ items={live}
485
+ label={name}
486
+ panelRef={pop}
94
487
  popover="auto"
95
- role="menu"
96
- aria-label={label}
97
- onToggle={(e) => setOpen(e.newState === "open")}
98
- onKeyDown={onKeyDown}
99
- className="min-w-44 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-lg"
100
- >
101
- {live.map((item, i) => (
102
- <button
103
- key={i}
104
- type="button"
105
- role="menuitem"
106
- disabled={item.disabled}
107
- onClick={() => {
108
- pop.current?.hidePopover();
109
- item.onSelect?.();
110
- }}
111
- className={cx(
112
- "flex w-full min-h-10 items-center gap-2 rounded-sm px-2 text-left text-sm outline-none hover:bg-accent focus-visible:bg-accent disabled:opacity-50",
113
- item.tone === "danger" && "text-destructive",
114
- )}
115
- >
116
- {item.icon ? <Icon name={item.icon} /> : null}
117
- {item.label}
118
- </button>
119
- ))}
120
- </div>
488
+ onToggle={onToggle}
489
+ onExit={exit}
490
+ onClose={() => pop.current?.hidePopover()}
491
+ className={panelClass("auto", "min-w-44 max-h-[calc(100vh-1rem)] overflow-y-auto p-1")}
492
+ />
121
493
  </>
122
494
  );
123
495
  }