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,608 @@
1
+ "use client";
2
+
3
+ import { useEffect, useId, useMemo, useRef, useState } from "react";
4
+ import { cx } from "./cx";
5
+ import { Icon } from "./icon";
6
+ import { Button } from "./button";
7
+ import { Sheet } from "./sheet";
8
+ import { panelClass, usePopoverPosition } from "./popover";
9
+ import {
10
+ CommandInput,
11
+ CommandList,
12
+ ItemIcon,
13
+ commandFilter,
14
+ listKeyDown,
15
+ optionId,
16
+ resolveActive,
17
+ useAsyncSearch,
18
+ useIsPhone,
19
+ withValues,
20
+ } from "./command";
21
+
22
+ // Combobox — pick one or many from a list you can search: a person, a
23
+ // company, a country, tags. A field-shaped trigger shows the choice; the
24
+ // panel beside it holds a search input and the options — Command's list and
25
+ // filter (./command), so the keys and the matching are the same everywhere.
26
+ // With `searchable={false}` it is the rich Select: the same trigger and
27
+ // options (icons, descriptions, groups) with no input, for a short list the
28
+ // native <select> cannot draw.
29
+ //
30
+ // options [{ value, label, description?, icon?, group?, keywords?,
31
+ // count?, disabled? }] — `icon` a glyph name or a node (an
32
+ // Avatar); `count` a number or short text at the option's
33
+ // right, muted ("15" beside a facet's value)
34
+ // value, defaultValue, onValueChange(value, option) — a value (null for
35
+ // none), or with `multiple` an array of values (and the
36
+ // options chosen)
37
+ // multiple chips in the trigger, each with a remove button; the
38
+ // panel stays open while you pick; Backspace in an empty
39
+ // search (or on the trigger) removes the last
40
+ // searchable true (default): a search input in the panel · false: the
41
+ // rich Select — focus stays on the trigger, arrows move,
42
+ // typing a letter jumps to the next option starting with it
43
+ // creatable offer `Create "typed"` when nothing matches exactly; true,
44
+ // or (text) => its label. onCreate(text) may return (or
45
+ // resolve) the new option; else it is { value: text, label: text }
46
+ // onSearch async (query, { signal }) → options, debounced; the list
47
+ // shows what it answers (a spinner meanwhile). A chosen
48
+ // option stays named after the results move on
49
+ // loading true while you fetch `options` yourself
50
+ // clearable an × that empties the choice
51
+ // disabled, invalid, name (hidden inputs, so a <form> posts the value)
52
+ // placeholder, searchPlaceholder, empty (shown when nothing matches)
53
+ // open, defaultOpen, onOpenChange(open) — the panel's state, controlled
54
+ // or not (default closed); light dismiss, Escape and a
55
+ // pick that closes report onOpenChange(false)
56
+ // label the name when it is not in a <Field> (a Field's label
57
+ // names it through `id`, and titles the phone sheet)
58
+ // side, align the panel's place, as Popover (default bottom · start);
59
+ // it is the trigger's width, 14rem at the least
60
+ // mobile sheet (default) · popover — on a phone (under 640px) the
61
+ // panel opens as a bottom Sheet: a popover under a field
62
+ // there is squeezed by the on-screen keyboard and half
63
+ // hidden; a sheet is the full width, within thumb reach,
64
+ // and its search sits above the keyboard. Multiple adds a
65
+ // Done button to it
66
+ //
67
+ // Every prop not named goes to the trigger button (aria-describedby for a
68
+ // Field's hint, onBlur…). In a Field, pass the same `id`:
69
+ //
70
+ // <Field id="owner" label="Owner"><Combobox id="owner" options={people} /></Field>
71
+ //
72
+ // ARIA: the trigger is role=combobox (its text is the value, the Field's
73
+ // label its name); the search input inside is a combobox too, pointing at
74
+ // the listbox with aria-activedescendant. Without a search, the trigger
75
+ // itself carries aria-activedescendant — the APG "select-only combobox".
76
+
77
+ const CREATE = "\u0000create";
78
+ const same = (a, b) => String(a).trim().toLowerCase() === String(b).trim().toLowerCase();
79
+
80
+ export function Combobox({
81
+ id,
82
+ options,
83
+ value,
84
+ defaultValue,
85
+ onValueChange,
86
+ multiple = false,
87
+ searchable = true,
88
+ creatable = false,
89
+ onCreate,
90
+ onSearch,
91
+ loading = false,
92
+ clearable = false,
93
+ disabled = false,
94
+ invalid = false,
95
+ name,
96
+ placeholder = "Select…",
97
+ searchPlaceholder = "Search…",
98
+ empty = "No results.",
99
+ label,
100
+ side = "bottom",
101
+ align = "start",
102
+ mobile = "sheet",
103
+ open,
104
+ defaultOpen = false,
105
+ onOpenChange,
106
+ className,
107
+ onKeyDown,
108
+ onKeyUp,
109
+ onBlur,
110
+ ...rest
111
+ }) {
112
+ const uid = useId().replace(/[^\w-]/g, "");
113
+ const panelId = `${uid}-panel`;
114
+ const listId = `${uid}-list`;
115
+ const wrap = useRef(null);
116
+ const btn = useRef(null);
117
+ const panel = useRef(null);
118
+ const input = useRef(null);
119
+ const list = useRef(null);
120
+ const sheetBody = useRef(null);
121
+ const phone = useIsPhone() && mobile === "sheet";
122
+
123
+ const [isOpen, setIsOpen] = useState(false);
124
+ const lastOpen = useRef(false);
125
+ // Every change of the panel's state passes here, so onOpenChange hears
126
+ // each one once, whoever made it (the trigger, a pick, light dismiss).
127
+ const setOpen = (now) => {
128
+ setIsOpen(now);
129
+ if (now !== lastOpen.current) {
130
+ lastOpen.current = now;
131
+ onOpenChange?.(now);
132
+ }
133
+ };
134
+ const [query, setQuery] = useState("");
135
+ const [active, setActive] = useState(null);
136
+ const [title, setTitle] = useState(label ?? placeholder);
137
+ const [width, setWidth] = useState(0);
138
+ const [created, setCreated] = useState([]);
139
+ const seed = useRef("");
140
+ const wasOpen = useRef(false);
141
+ const swallowSpace = useRef(false);
142
+ const typed = useRef({ text: "", at: 0 });
143
+
144
+ // The choice, as an array of string values either way.
145
+ const controlled = value !== undefined;
146
+ const [own, setOwn] = useState(defaultValue ?? (multiple ? [] : null));
147
+ const raw = controlled ? value : own;
148
+ const chosen = multiple ? (Array.isArray(raw) ? raw.map(String) : []) : raw == null || raw === "" ? [] : [String(raw)];
149
+ const key = chosen.join("\u0001");
150
+ const chosenSet = useMemo(() => new Set(key ? key.split("\u0001") : []), [key]);
151
+
152
+ // Every option seen, so a chosen one keeps its label (and icon) after a
153
+ // search replaced the list it came from.
154
+ const known = useRef(new Map());
155
+ const search = useAsyncSearch(onSearch, query, { enabled: isOpen });
156
+ const base = withValues(onSearch ? search.items ?? options : options);
157
+ const source = [...base, ...created.filter((c) => !base.some((o) => o.value === c.value))];
158
+ for (const o of source) known.current.set(o.value, o);
159
+ const optionOf = (v) => known.current.get(v) ?? { value: v, label: v };
160
+
161
+ let visible = searchable && !onSearch ? commandFilter(source, query) : source;
162
+ const text = query.trim();
163
+ if (creatable && text && !source.some((o) => same(o.label, text))) {
164
+ visible = [
165
+ ...visible,
166
+ { value: CREATE, label: typeof creatable === "function" ? creatable(text) : `Create "${text}"`, icon: "plus", create: true },
167
+ ];
168
+ }
169
+ const current = resolveActive(visible, active);
170
+ const activeIndex = visible.findIndex((o) => o.value === current);
171
+ const activeId = activeIndex >= 0 ? optionId(listId, activeIndex) : null;
172
+ const busy = loading || search.loading;
173
+ // The list draws a count at an option's right inside its label: the
174
+ // filter, the typeahead and the chips keep reading the plain label.
175
+ const shown = visible.some((o) => o.count != null && o.count !== "")
176
+ ? visible.map((o) =>
177
+ o.count == null || o.count === ""
178
+ ? o
179
+ : {
180
+ ...o,
181
+ label: (
182
+ <span className="flex items-center gap-2">
183
+ <span className="min-w-0 flex-1 truncate">{o.label}</span>
184
+ <span className="sr-only">, </span>
185
+ <span className="shrink-0 text-xs tabular-nums text-muted-foreground">{o.count}</span>
186
+ </span>
187
+ ),
188
+ },
189
+ )
190
+ : visible;
191
+
192
+ usePopoverPosition(wrap, panel, { side, align });
193
+
194
+ // The panel is the trigger's width, kept as the field resizes.
195
+ useEffect(() => {
196
+ const el = wrap.current;
197
+ if (!el) return;
198
+ const ro = new ResizeObserver(() => setWidth(el.offsetWidth));
199
+ ro.observe(el);
200
+ return () => ro.disconnect();
201
+ }, []);
202
+
203
+ // `open` drives the panel; `defaultOpen` opens it once, on mount.
204
+ const want = open ?? null;
205
+ useEffect(() => {
206
+ if (want === null) return;
207
+ if (want && !lastOpen.current) show();
208
+ else if (!want && lastOpen.current) hide();
209
+ }, [want]); // eslint-disable-line react-hooks/exhaustive-deps
210
+ useEffect(() => {
211
+ if (defaultOpen && open === undefined) show();
212
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
213
+
214
+ // Focus into the panel once it shows: the search, or (a phone sheet with
215
+ // no search) the list itself.
216
+ useEffect(() => {
217
+ if (!isOpen) return;
218
+ if (searchable) input.current?.focus({ preventScroll: true });
219
+ else if (phone) list.current?.focus({ preventScroll: true });
220
+ }, [isOpen, searchable, phone]);
221
+
222
+ // ─── open and close ────────────────────────────────────────────────
223
+
224
+ function fieldLabel() {
225
+ if (!id || typeof document === "undefined") return null;
226
+ const el = document.querySelector(`label[for="${CSS.escape(id)}"]`);
227
+ return el?.textContent?.replace(/\s*\*$/, "").trim() || null;
228
+ }
229
+
230
+ // Every opening starts here: the typed seed (or nothing), the choice
231
+ // active, the title read from the Field.
232
+ function begin() {
233
+ setQuery(searchable ? seed.current : "");
234
+ seed.current = "";
235
+ setActive(chosen[0] ?? null);
236
+ setTitle(label ?? fieldLabel() ?? placeholder);
237
+ }
238
+
239
+ function show() {
240
+ if (disabled) return;
241
+ // Read the width live: a `defaultOpen` shows from the first effect,
242
+ // before the hydrated render has learnt it is on a phone.
243
+ if (phone || (mobile === "sheet" && window.matchMedia("(max-width: 639.98px)").matches)) {
244
+ begin();
245
+ setOpen(true);
246
+ } else if (!panel.current?.matches(":popover-open")) {
247
+ panel.current?.showPopover();
248
+ }
249
+ }
250
+
251
+ function hide() {
252
+ if (phone) {
253
+ // Close the <dialog> itself: it hands focus back to the trigger.
254
+ const dialog = sheetBody.current?.closest("dialog");
255
+ if (dialog?.open) dialog.close();
256
+ else setOpen(false);
257
+ } else if (panel.current?.matches(":popover-open")) {
258
+ panel.current.hidePopover();
259
+ }
260
+ }
261
+
262
+ // The popover's own opens and closes (the trigger, light dismiss, Esc).
263
+ function onToggle(e) {
264
+ const now = e.newState === "open";
265
+ if (now) begin();
266
+ else {
267
+ const at = document.activeElement;
268
+ if (!at || at === document.body || panel.current?.contains(at)) btn.current?.focus({ preventScroll: true });
269
+ }
270
+ setOpen(now);
271
+ }
272
+
273
+ // ─── choosing ──────────────────────────────────────────────────────
274
+
275
+ function commit(next) {
276
+ if (multiple) {
277
+ if (!controlled) setOwn(next);
278
+ onValueChange?.(next, next.map(optionOf));
279
+ } else {
280
+ const v = next[0] ?? null;
281
+ if (!controlled) setOwn(v);
282
+ onValueChange?.(v, v == null ? null : optionOf(v));
283
+ }
284
+ }
285
+
286
+ function choose(v) {
287
+ if (multiple) {
288
+ commit(chosenSet.has(v) ? chosen.filter((x) => x !== v) : [...chosen, v]);
289
+ setActive(v);
290
+ } else {
291
+ commit([v]);
292
+ hide();
293
+ }
294
+ }
295
+
296
+ async function create(typedText) {
297
+ let made = null;
298
+ try {
299
+ made = await onCreate?.(typedText);
300
+ } catch {
301
+ return; // the caller refused it; its own error shows where it chose
302
+ }
303
+ const option = made && typeof made === "object" ? withValues([made])[0] : { value: typedText, label: typedText };
304
+ known.current.set(option.value, option);
305
+ setCreated((c) => [...c.filter((o) => o.value !== option.value), option]);
306
+ setQuery("");
307
+ if (multiple) {
308
+ commit([...chosen.filter((x) => x !== option.value), option.value]);
309
+ setActive(option.value);
310
+ } else {
311
+ commit([option.value]);
312
+ hide();
313
+ }
314
+ }
315
+
316
+ function pick(item) {
317
+ if (item.create) create(text);
318
+ else choose(item.value);
319
+ }
320
+
321
+ function remove(v) {
322
+ commit(chosen.filter((x) => x !== v));
323
+ btn.current?.focus({ preventScroll: true });
324
+ }
325
+
326
+ // ─── keys ──────────────────────────────────────────────────────────
327
+
328
+ const printable = (e) => e.key.length === 1 && e.key !== " " && !e.metaKey && !e.ctrlKey && !e.altKey;
329
+
330
+ // Select-only: a letter jumps to the next option starting with it;
331
+ // letters typed quickly spell a prefix; one letter repeated cycles.
332
+ function typeahead(ch) {
333
+ const now = Date.now();
334
+ const t = typed.current;
335
+ t.text = now - t.at > 700 ? ch : t.text + ch;
336
+ t.at = now;
337
+ const repeat = [...t.text].every((c) => c === t.text[0]);
338
+ const q = (repeat ? t.text[0] : t.text).toLowerCase();
339
+ const live = visible.filter((o) => !o.disabled);
340
+ const at = Math.max(0, live.findIndex((o) => o.value === current));
341
+ const from = repeat ? at + 1 : at;
342
+ const order = [...live.slice(from), ...live.slice(0, from)];
343
+ const hit = order.find((o) => String(o.label).toLowerCase().startsWith(q));
344
+ if (hit) setActive(hit.value);
345
+ }
346
+
347
+ function onTriggerKey(e) {
348
+ onKeyDown?.(e);
349
+ if (e.defaultPrevented || disabled) return;
350
+ const open = isOpen && !phone;
351
+ if (!open) {
352
+ if (e.key === "ArrowDown" || e.key === "ArrowUp") {
353
+ e.preventDefault();
354
+ show();
355
+ } else if (e.key === "Backspace" && multiple && chosen.length) {
356
+ e.preventDefault();
357
+ commit(chosen.slice(0, -1));
358
+ } else if (printable(e)) {
359
+ e.preventDefault();
360
+ if (searchable) seed.current = e.key;
361
+ show();
362
+ }
363
+ return;
364
+ }
365
+ // Open without a search: the keys work the list from the trigger.
366
+ if (e.key === "Tab") return hide();
367
+ if (e.key === " " || e.key === "Enter") {
368
+ e.preventDefault();
369
+ swallowSpace.current = e.key === " ";
370
+ const item = visible.find((o) => o.value === current);
371
+ if (item && !item.disabled) pick(item);
372
+ return;
373
+ }
374
+ if (listKeyDown(e, visible, current, setActive, pick)) return;
375
+ if (printable(e)) {
376
+ e.preventDefault();
377
+ typeahead(e.key);
378
+ }
379
+ }
380
+
381
+ // A Space that picked must not also click the trigger open again.
382
+ function onTriggerKeyUp(e) {
383
+ onKeyUp?.(e);
384
+ if (e.key === " " && swallowSpace.current) {
385
+ e.preventDefault();
386
+ swallowSpace.current = false;
387
+ }
388
+ }
389
+
390
+ function onSearchKey(e) {
391
+ if (listKeyDown(e, visible, current, setActive, pick)) return;
392
+ if (e.key === "Backspace" && multiple && !query && chosen.length) {
393
+ e.preventDefault();
394
+ commit(chosen.slice(0, -1));
395
+ }
396
+ }
397
+
398
+ // A phone sheet without a search: the focused listbox takes the keys.
399
+ function onListKey(e) {
400
+ if (e.key === " ") {
401
+ e.preventDefault();
402
+ const item = visible.find((o) => o.value === current);
403
+ if (item && !item.disabled) pick(item);
404
+ return;
405
+ }
406
+ if (listKeyDown(e, visible, current, setActive, pick)) return;
407
+ if (printable(e)) typeahead(e.key);
408
+ }
409
+
410
+ // Focus leaving for anything outside the field and its panel closes it.
411
+ function leaving(e) {
412
+ const to = e.relatedTarget;
413
+ if (!to || phone) return;
414
+ if (panel.current?.contains(to) || wrap.current?.contains(to)) return;
415
+ hide();
416
+ }
417
+
418
+ // ─── the trigger ───────────────────────────────────────────────────
419
+
420
+ const one = !multiple && chosen.length ? optionOf(chosen[0]) : null;
421
+ const showClear = clearable && chosen.length > 0 && !disabled;
422
+
423
+ const body = (
424
+ <>
425
+ {searchable ? (
426
+ <CommandInput
427
+ inputRef={input}
428
+ listId={listId}
429
+ activeId={activeId}
430
+ busy={busy && visible.length > 0}
431
+ aria-label={searchPlaceholder || `Search ${title}`}
432
+ placeholder={searchPlaceholder}
433
+ value={query}
434
+ onChange={(e) => {
435
+ setQuery(e.target.value);
436
+ setActive(null);
437
+ }}
438
+ onKeyDown={onSearchKey}
439
+ />
440
+ ) : null}
441
+ <CommandList
442
+ id={listId}
443
+ label={title}
444
+ items={shown}
445
+ active={current}
446
+ onActive={setActive}
447
+ onPick={pick}
448
+ selected={chosenSet}
449
+ multiple={multiple}
450
+ empty={empty}
451
+ loading={busy}
452
+ error={search.error}
453
+ focusable={phone && !searchable}
454
+ onKeyDown={phone && !searchable ? onListKey : undefined}
455
+ listRef={list}
456
+ // On a phone the list scrolls, not the sheet, so the search stays put.
457
+ className={phone ? "max-h-[55dvh]" : undefined}
458
+ />
459
+ </>
460
+ );
461
+
462
+ return (
463
+ <>
464
+ <div
465
+ ref={wrap}
466
+ onPointerDown={() => {
467
+ wasOpen.current = isOpen;
468
+ }}
469
+ // A press on the field (not on one of its buttons) opens it; a
470
+ // press while open was the light dismiss that closed it.
471
+ onClick={(e) => {
472
+ if (disabled || e.target.closest("button")) return;
473
+ btn.current?.focus({ preventScroll: true });
474
+ if (!wasOpen.current) show();
475
+ }}
476
+ className={cx(
477
+ "relative flex w-full min-h-11 cursor-pointer flex-wrap items-center gap-1 rounded-md border border-input bg-background pl-3 text-sm text-foreground",
478
+ "has-[[data-combobox-trigger]:focus-visible]:ring-2 has-[[data-combobox-trigger]:focus-visible]:ring-ring",
479
+ "has-[[data-combobox-trigger][aria-invalid=true]]:border-destructive",
480
+ showClear ? "pr-16" : "pr-9",
481
+ multiple && chosen.length > 0 && "py-1.5 pl-1.5",
482
+ disabled && "cursor-not-allowed opacity-50",
483
+ className,
484
+ )}
485
+ >
486
+ {multiple
487
+ ? chosen.map((v) => {
488
+ const o = optionOf(v);
489
+ return (
490
+ <span
491
+ key={v}
492
+ className="inline-flex min-h-7 max-w-full items-center gap-1 rounded-sm border border-border bg-secondary pl-2 pr-0.5 text-xs text-secondary-foreground"
493
+ >
494
+ {typeof o.icon === "string" ? <Icon name={o.icon} size={12} className="shrink-0 text-muted-foreground" /> : null}
495
+ <span className="truncate">{o.label}</span>
496
+ {disabled ? (
497
+ <span className="w-1.5" />
498
+ ) : (
499
+ <button
500
+ type="button"
501
+ aria-label={`Remove ${o.label}`}
502
+ onClick={() => remove(v)}
503
+ className="grid size-6 shrink-0 place-items-center rounded-sm text-muted-foreground outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
504
+ >
505
+ <Icon name="x" size={12} />
506
+ </button>
507
+ )}
508
+ </span>
509
+ );
510
+ })
511
+ : null}
512
+ <button
513
+ ref={btn}
514
+ id={id}
515
+ type="button"
516
+ role="combobox"
517
+ data-combobox-trigger=""
518
+ disabled={disabled}
519
+ aria-label={label}
520
+ aria-haspopup={searchable ? "dialog" : "listbox"}
521
+ aria-expanded={isOpen}
522
+ aria-controls={isOpen ? (searchable && !phone ? panelId : listId) : undefined}
523
+ aria-activedescendant={isOpen && !searchable && !phone ? activeId ?? undefined : undefined}
524
+ aria-invalid={invalid || undefined}
525
+ popoverTarget={phone ? undefined : panelId}
526
+ onClick={phone ? () => show() : undefined}
527
+ onKeyDown={onTriggerKey}
528
+ onKeyUp={onTriggerKeyUp}
529
+ onBlur={(e) => {
530
+ onBlur?.(e);
531
+ if (!searchable) leaving(e);
532
+ }}
533
+ className={cx(
534
+ "flex min-h-9 min-w-12 flex-1 items-center gap-2 self-stretch text-left outline-none disabled:cursor-not-allowed",
535
+ multiple && chosen.length > 0 && "pl-1",
536
+ )}
537
+ {...rest}
538
+ >
539
+ {multiple ? (
540
+ chosen.length ? (
541
+ <span className="sr-only">{chosen.map((v) => optionOf(v).label).join(", ")}</span>
542
+ ) : (
543
+ <span className="truncate text-muted-foreground">{placeholder}</span>
544
+ )
545
+ ) : one ? (
546
+ <>
547
+ <ItemIcon icon={one.icon} />
548
+ <span className="truncate">{one.label}</span>
549
+ </>
550
+ ) : (
551
+ <span className="truncate text-muted-foreground">{placeholder}</span>
552
+ )}
553
+ </button>
554
+ {showClear ? (
555
+ <button
556
+ type="button"
557
+ aria-label="Clear"
558
+ onClick={() => {
559
+ commit([]);
560
+ btn.current?.focus({ preventScroll: true });
561
+ }}
562
+ className="absolute right-8 top-1/2 grid size-7 -translate-y-1/2 place-items-center rounded-sm text-muted-foreground outline-none hover:bg-accent hover:text-foreground focus-visible:ring-2 focus-visible:ring-ring"
563
+ >
564
+ <Icon name="x" size={14} />
565
+ </button>
566
+ ) : null}
567
+ <Icon name="chevrons-up-down" className="pointer-events-none absolute right-3 top-1/2 -translate-y-1/2 text-muted-foreground" />
568
+ {name ? chosen.map((v) => <input key={v} type="hidden" name={name} value={v} />) : null}
569
+ </div>
570
+
571
+ {phone ? (
572
+ isOpen ? (
573
+ <Sheet
574
+ open
575
+ side="bottom"
576
+ title={title}
577
+ onClose={() => setOpen(false)}
578
+ footer={
579
+ multiple ? (
580
+ <Button variant="primary" onClick={hide} className="w-full">
581
+ Done{chosen.length ? ` · ${chosen.length} chosen` : ""}
582
+ </Button>
583
+ ) : null
584
+ }
585
+ >
586
+ <div ref={sheetBody} className="-mx-4 -my-4">
587
+ {body}
588
+ </div>
589
+ </Sheet>
590
+ ) : null
591
+ ) : (
592
+ <div
593
+ ref={panel}
594
+ id={panelId}
595
+ popover="auto"
596
+ role={searchable ? "dialog" : undefined}
597
+ aria-label={searchable ? title : undefined}
598
+ onToggle={onToggle}
599
+ onBlur={leaving}
600
+ className={panelClass("auto", "overflow-hidden p-0 text-sm")}
601
+ style={width ? { width: `max(${width}px, 14rem)` } : undefined}
602
+ >
603
+ {body}
604
+ </div>
605
+ )}
606
+ </>
607
+ );
608
+ }