makersclaw 0.2.0 → 0.3.1

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 (475) hide show
  1. package/THIRD-PARTY-NOTICES.md +180 -0
  2. package/package.json +3 -2
  3. package/r/annotate.json +62 -0
  4. package/r/approval-request.json +52 -0
  5. package/r/audio-player.json +74 -0
  6. package/r/auth-form.json +55 -0
  7. package/r/board.json +10 -6
  8. package/r/calendar-view.json +105 -0
  9. package/r/cart.json +62 -0
  10. package/r/chart.json +83 -0
  11. package/r/chat.json +95 -0
  12. package/r/code-editor.json +94 -0
  13. package/r/comment-box.json +64 -0
  14. package/r/contact-section.json +53 -0
  15. package/r/core.json +205 -8
  16. package/r/csv-import.json +63 -0
  17. package/r/data-table.json +69 -0
  18. package/r/diagram.json +74 -0
  19. package/r/doc-editor.json +82 -0
  20. package/r/doc-page.json +61 -0
  21. package/r/email-render.json +73 -0
  22. package/r/faq.json +50 -0
  23. package/r/file-upload.json +54 -0
  24. package/r/flow-canvas.json +93 -0
  25. package/r/footer.json +52 -0
  26. package/r/gantt.json +70 -0
  27. package/r/job-panel.json +12 -7
  28. package/r/json-editor.json +66 -0
  29. package/r/map.json +89 -0
  30. package/r/menubar.json +52 -0
  31. package/r/mind-map.json +67 -0
  32. package/r/navigation-menu.json +52 -0
  33. package/r/order-detail.json +54 -0
  34. package/r/org-chart.json +66 -0
  35. package/r/page-builder.json +105 -0
  36. package/r/payment-methods.json +51 -0
  37. package/r/pdf-document.json +79 -0
  38. package/r/pdf-viewer.json +88 -0
  39. package/r/product-detail.json +76 -0
  40. package/r/product-grid.json +65 -0
  41. package/r/record-form.json +67 -0
  42. package/r/record-notes.json +55 -0
  43. package/r/registry.json +3411 -29
  44. package/r/search-dialog.json +51 -0
  45. package/r/settings-list.json +66 -0
  46. package/r/setup-checklist.json +51 -0
  47. package/r/share-dialog.json +52 -0
  48. package/r/sidebar.json +69 -0
  49. package/r/signature-pad.json +72 -0
  50. package/r/sketch-canvas.json +99 -0
  51. package/r/spreadsheet.json +90 -0
  52. package/r/stat-cards.json +66 -0
  53. package/r/task-list.json +63 -0
  54. package/r/task-model.json +98 -0
  55. package/r/testimonials.json +52 -0
  56. package/r/transaction-list.json +53 -0
  57. package/r/whiteboard.json +66 -0
  58. package/registry/blocks/annotate/README.md +179 -0
  59. package/registry/blocks/annotate/annotate-data.js +250 -0
  60. package/registry/blocks/annotate/annotate.jsx +646 -0
  61. package/registry/blocks/annotate/block.json +10 -0
  62. package/registry/blocks/annotate/demo.jsx +135 -0
  63. package/registry/blocks/annotate/index.js +15 -0
  64. package/registry/blocks/approval-request/README.md +116 -0
  65. package/registry/blocks/approval-request/approval-request.jsx +386 -0
  66. package/registry/blocks/approval-request/block.json +10 -0
  67. package/registry/blocks/approval-request/demo.jsx +127 -0
  68. package/registry/blocks/approval-request/index.js +4 -0
  69. package/registry/blocks/audio-player/README.md +167 -0
  70. package/registry/blocks/audio-player/audio-player.jsx +623 -0
  71. package/registry/blocks/audio-player/block.json +14 -0
  72. package/registry/blocks/audio-player/colors.js +109 -0
  73. package/registry/blocks/audio-player/demo.jsx +209 -0
  74. package/registry/blocks/audio-player/index.js +6 -0
  75. package/registry/blocks/audio-player/time.js +67 -0
  76. package/registry/blocks/audio-player/wavesurfer.js +9 -0
  77. package/registry/blocks/auth-form/README.md +126 -0
  78. package/registry/blocks/auth-form/auth-form.jsx +568 -0
  79. package/registry/blocks/auth-form/block.json +10 -0
  80. package/registry/blocks/auth-form/demo.jsx +131 -0
  81. package/registry/blocks/auth-form/index.js +3 -0
  82. package/registry/blocks/board/README.md +55 -5
  83. package/registry/blocks/board/block.json +2 -2
  84. package/registry/blocks/board/board.jsx +230 -86
  85. package/registry/blocks/board/demo.jsx +103 -24
  86. package/registry/blocks/calendar-view/README.md +180 -0
  87. package/registry/blocks/calendar-view/agenda.jsx +147 -0
  88. package/registry/blocks/calendar-view/block.json +76 -0
  89. package/registry/blocks/calendar-view/calendar-model.js +442 -0
  90. package/registry/blocks/calendar-view/calendar.jsx +627 -0
  91. package/registry/blocks/calendar-view/demo.jsx +195 -0
  92. package/registry/blocks/calendar-view/draft.jsx +49 -0
  93. package/registry/blocks/calendar-view/index.js +39 -0
  94. package/registry/blocks/calendar-view/month.jsx +346 -0
  95. package/registry/blocks/calendar-view/paint.js +69 -0
  96. package/registry/blocks/calendar-view/time-grid.jsx +527 -0
  97. package/registry/blocks/calendar-view/tray.jsx +87 -0
  98. package/registry/blocks/cart/README.md +146 -0
  99. package/registry/blocks/cart/block.json +10 -0
  100. package/registry/blocks/cart/cart.jsx +597 -0
  101. package/registry/blocks/cart/demo.jsx +98 -0
  102. package/registry/blocks/cart/index.js +4 -0
  103. package/registry/blocks/cart/money.js +28 -0
  104. package/registry/blocks/chart/README.md +316 -0
  105. package/registry/blocks/chart/block.json +10 -0
  106. package/registry/blocks/chart/cartesian.jsx +642 -0
  107. package/registry/blocks/chart/core.jsx +262 -0
  108. package/registry/blocks/chart/demo.jsx +357 -0
  109. package/registry/blocks/chart/heatmap.jsx +336 -0
  110. package/registry/blocks/chart/index.js +8 -0
  111. package/registry/blocks/chart/pie.jsx +245 -0
  112. package/registry/blocks/chart/sparkline.jsx +88 -0
  113. package/registry/blocks/chat/README.md +273 -0
  114. package/registry/blocks/chat/attachment.jsx +95 -0
  115. package/registry/blocks/chat/block.json +10 -0
  116. package/registry/blocks/chat/chat.jsx +132 -0
  117. package/registry/blocks/chat/composer.jsx +364 -0
  118. package/registry/blocks/chat/demo.jsx +578 -0
  119. package/registry/blocks/chat/header.jsx +34 -0
  120. package/registry/blocks/chat/index.js +9 -0
  121. package/registry/blocks/chat/message.jsx +450 -0
  122. package/registry/blocks/chat/scroller.jsx +247 -0
  123. package/registry/blocks/chat/text.jsx +104 -0
  124. package/registry/blocks/code-editor/README.md +210 -0
  125. package/registry/blocks/code-editor/block.json +22 -0
  126. package/registry/blocks/code-editor/code-editor.jsx +534 -0
  127. package/registry/blocks/code-editor/codemirror.js +38 -0
  128. package/registry/blocks/code-editor/demo.jsx +205 -0
  129. package/registry/blocks/code-editor/index.js +8 -0
  130. package/registry/blocks/code-editor/json.js +317 -0
  131. package/registry/blocks/code-editor/sql.js +136 -0
  132. package/registry/blocks/code-editor/text.js +101 -0
  133. package/registry/blocks/code-editor/theme.js +186 -0
  134. package/registry/blocks/comment-box/README.md +123 -0
  135. package/registry/blocks/comment-box/block.json +10 -0
  136. package/registry/blocks/comment-box/comment-body.jsx +217 -0
  137. package/registry/blocks/comment-box/comment-box.jsx +198 -0
  138. package/registry/blocks/comment-box/comment-time.jsx +77 -0
  139. package/registry/blocks/comment-box/demo.jsx +141 -0
  140. package/registry/blocks/comment-box/index.js +8 -0
  141. package/registry/blocks/contact-section/README.md +152 -0
  142. package/registry/blocks/contact-section/block.json +11 -0
  143. package/registry/blocks/contact-section/contact-section.jsx +509 -0
  144. package/registry/blocks/contact-section/demo.jsx +93 -0
  145. package/registry/blocks/contact-section/index.js +4 -0
  146. package/registry/blocks/csv-import/README.md +133 -0
  147. package/registry/blocks/csv-import/block.json +13 -0
  148. package/registry/blocks/csv-import/csv-import.jsx +479 -0
  149. package/registry/blocks/csv-import/demo.jsx +106 -0
  150. package/registry/blocks/csv-import/import.js +200 -0
  151. package/registry/blocks/csv-import/index.js +7 -0
  152. package/registry/blocks/data-table/README.md +386 -0
  153. package/registry/blocks/data-table/block.json +10 -0
  154. package/registry/blocks/data-table/data-table.jsx +1164 -0
  155. package/registry/blocks/data-table/demo.jsx +588 -0
  156. package/registry/blocks/data-table/index.js +5 -0
  157. package/registry/blocks/data-table/model.js +169 -0
  158. package/registry/blocks/diagram/README.md +108 -0
  159. package/registry/blocks/diagram/block.json +13 -0
  160. package/registry/blocks/diagram/demo.jsx +113 -0
  161. package/registry/blocks/diagram/diagram.jsx +481 -0
  162. package/registry/blocks/diagram/index.js +7 -0
  163. package/registry/blocks/diagram/mermaid.js +52 -0
  164. package/registry/blocks/diagram/palette.js +313 -0
  165. package/registry/blocks/diagram/source.js +98 -0
  166. package/registry/blocks/doc-editor/README.md +215 -0
  167. package/registry/blocks/doc-editor/block.json +15 -0
  168. package/registry/blocks/doc-editor/demo.jsx +225 -0
  169. package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
  170. package/registry/blocks/doc-editor/doc.js +309 -0
  171. package/registry/blocks/doc-editor/index.js +7 -0
  172. package/registry/blocks/doc-editor/schema.jsx +56 -0
  173. package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
  174. package/registry/blocks/doc-editor/theme.js +82 -0
  175. package/registry/blocks/doc-page/README.md +119 -0
  176. package/registry/blocks/doc-page/autosave.js +104 -0
  177. package/registry/blocks/doc-page/block.json +10 -0
  178. package/registry/blocks/doc-page/demo.jsx +137 -0
  179. package/registry/blocks/doc-page/doc-page.jsx +276 -0
  180. package/registry/blocks/doc-page/index.js +4 -0
  181. package/registry/blocks/email-render/README.md +151 -0
  182. package/registry/blocks/email-render/block.json +14 -0
  183. package/registry/blocks/email-render/demo.jsx +153 -0
  184. package/registry/blocks/email-render/doc-email.jsx +210 -0
  185. package/registry/blocks/email-render/email.jsx +221 -0
  186. package/registry/blocks/email-render/index.js +10 -0
  187. package/registry/blocks/email-render/render.js +50 -0
  188. package/registry/blocks/email-render/theme.js +215 -0
  189. package/registry/blocks/faq/README.md +101 -0
  190. package/registry/blocks/faq/block.json +11 -0
  191. package/registry/blocks/faq/demo.jsx +89 -0
  192. package/registry/blocks/faq/faq.jsx +220 -0
  193. package/registry/blocks/faq/index.js +4 -0
  194. package/registry/blocks/file-upload/README.md +138 -0
  195. package/registry/blocks/file-upload/block.json +10 -0
  196. package/registry/blocks/file-upload/demo.jsx +204 -0
  197. package/registry/blocks/file-upload/file-upload.jsx +635 -0
  198. package/registry/blocks/file-upload/index.js +4 -0
  199. package/registry/blocks/flow-canvas/README.md +420 -0
  200. package/registry/blocks/flow-canvas/block.json +13 -0
  201. package/registry/blocks/flow-canvas/board.js +819 -0
  202. package/registry/blocks/flow-canvas/chrome.jsx +649 -0
  203. package/registry/blocks/flow-canvas/context.jsx +35 -0
  204. package/registry/blocks/flow-canvas/demo.jsx +249 -0
  205. package/registry/blocks/flow-canvas/edges.jsx +107 -0
  206. package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
  207. package/registry/blocks/flow-canvas/index.js +41 -0
  208. package/registry/blocks/flow-canvas/nodes.jsx +392 -0
  209. package/registry/blocks/flow-canvas/theme.js +174 -0
  210. package/registry/blocks/footer/README.md +133 -0
  211. package/registry/blocks/footer/block.json +11 -0
  212. package/registry/blocks/footer/demo.jsx +119 -0
  213. package/registry/blocks/footer/footer.jsx +406 -0
  214. package/registry/blocks/footer/index.js +4 -0
  215. package/registry/blocks/gantt/README.md +220 -0
  216. package/registry/blocks/gantt/block.json +10 -0
  217. package/registry/blocks/gantt/demo.jsx +148 -0
  218. package/registry/blocks/gantt/gantt.jsx +923 -0
  219. package/registry/blocks/gantt/index.js +7 -0
  220. package/registry/blocks/gantt/rows.jsx +468 -0
  221. package/registry/blocks/gantt/schedule.js +278 -0
  222. package/registry/blocks/job-panel/README.md +80 -0
  223. package/registry/blocks/job-panel/block.json +3 -3
  224. package/registry/blocks/job-panel/demo.jsx +230 -3
  225. package/registry/blocks/job-panel/job-panel.jsx +169 -3
  226. package/registry/blocks/json-editor/README.md +133 -0
  227. package/registry/blocks/json-editor/block.json +10 -0
  228. package/registry/blocks/json-editor/demo.jsx +92 -0
  229. package/registry/blocks/json-editor/index.js +7 -0
  230. package/registry/blocks/json-editor/json-editor.jsx +305 -0
  231. package/registry/blocks/json-editor/json-tree.jsx +393 -0
  232. package/registry/blocks/json-editor/tree.js +225 -0
  233. package/registry/blocks/map/README.md +221 -0
  234. package/registry/blocks/map/block.json +14 -0
  235. package/registry/blocks/map/demo.jsx +201 -0
  236. package/registry/blocks/map/geo.js +198 -0
  237. package/registry/blocks/map/index.js +8 -0
  238. package/registry/blocks/map/list.jsx +128 -0
  239. package/registry/blocks/map/map-view.jsx +715 -0
  240. package/registry/blocks/map/maplibre.js +30 -0
  241. package/registry/blocks/map/marks.jsx +146 -0
  242. package/registry/blocks/map/theme.js +142 -0
  243. package/registry/blocks/menubar/README.md +109 -0
  244. package/registry/blocks/menubar/block.json +10 -0
  245. package/registry/blocks/menubar/demo.jsx +128 -0
  246. package/registry/blocks/menubar/index.js +4 -0
  247. package/registry/blocks/menubar/menubar.jsx +199 -0
  248. package/registry/blocks/mind-map/README.md +206 -0
  249. package/registry/blocks/mind-map/block.json +10 -0
  250. package/registry/blocks/mind-map/demo.jsx +103 -0
  251. package/registry/blocks/mind-map/index.js +31 -0
  252. package/registry/blocks/mind-map/mind-map.jsx +1125 -0
  253. package/registry/blocks/mind-map/topic.jsx +143 -0
  254. package/registry/blocks/mind-map/tree.js +645 -0
  255. package/registry/blocks/navigation-menu/README.md +124 -0
  256. package/registry/blocks/navigation-menu/block.json +10 -0
  257. package/registry/blocks/navigation-menu/demo.jsx +84 -0
  258. package/registry/blocks/navigation-menu/index.js +4 -0
  259. package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
  260. package/registry/blocks/order-detail/README.md +109 -0
  261. package/registry/blocks/order-detail/block.json +10 -0
  262. package/registry/blocks/order-detail/demo.jsx +88 -0
  263. package/registry/blocks/order-detail/index.js +4 -0
  264. package/registry/blocks/order-detail/order-detail.jsx +354 -0
  265. package/registry/blocks/org-chart/README.md +145 -0
  266. package/registry/blocks/org-chart/block.json +10 -0
  267. package/registry/blocks/org-chart/demo.jsx +94 -0
  268. package/registry/blocks/org-chart/index.js +7 -0
  269. package/registry/blocks/org-chart/org-chart.jsx +631 -0
  270. package/registry/blocks/org-chart/org.js +245 -0
  271. package/registry/blocks/org-chart/person.jsx +96 -0
  272. package/registry/blocks/page-builder/README.md +257 -0
  273. package/registry/blocks/page-builder/block.json +14 -0
  274. package/registry/blocks/page-builder/demo.jsx +145 -0
  275. package/registry/blocks/page-builder/grapes.js +18 -0
  276. package/registry/blocks/page-builder/index.js +11 -0
  277. package/registry/blocks/page-builder/page-builder.jsx +704 -0
  278. package/registry/blocks/page-builder/page-view.jsx +33 -0
  279. package/registry/blocks/page-builder/page.js +118 -0
  280. package/registry/blocks/page-builder/panels.jsx +588 -0
  281. package/registry/blocks/page-builder/sections.js +209 -0
  282. package/registry/blocks/page-builder/theme.js +87 -0
  283. package/registry/blocks/page-builder/thumbs.jsx +189 -0
  284. package/registry/blocks/page-builder/tokens.js +329 -0
  285. package/registry/blocks/payment-methods/README.md +104 -0
  286. package/registry/blocks/payment-methods/block.json +10 -0
  287. package/registry/blocks/payment-methods/demo.jsx +71 -0
  288. package/registry/blocks/payment-methods/index.js +4 -0
  289. package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
  290. package/registry/blocks/pdf-document/README.md +213 -0
  291. package/registry/blocks/pdf-document/block.json +13 -0
  292. package/registry/blocks/pdf-document/demo.jsx +136 -0
  293. package/registry/blocks/pdf-document/index.js +33 -0
  294. package/registry/blocks/pdf-document/money.js +83 -0
  295. package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
  296. package/registry/blocks/pdf-document/primitives.jsx +271 -0
  297. package/registry/blocks/pdf-document/quote.jsx +179 -0
  298. package/registry/blocks/pdf-document/theme.js +193 -0
  299. package/registry/blocks/pdf-viewer/README.md +191 -0
  300. package/registry/blocks/pdf-viewer/block.json +14 -0
  301. package/registry/blocks/pdf-viewer/demo.jsx +252 -0
  302. package/registry/blocks/pdf-viewer/helpers.js +270 -0
  303. package/registry/blocks/pdf-viewer/index.js +8 -0
  304. package/registry/blocks/pdf-viewer/notes.jsx +195 -0
  305. package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
  306. package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
  307. package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
  308. package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
  309. package/registry/blocks/product-detail/README.md +217 -0
  310. package/registry/blocks/product-detail/block.json +10 -0
  311. package/registry/blocks/product-detail/demo.jsx +233 -0
  312. package/registry/blocks/product-detail/index.js +7 -0
  313. package/registry/blocks/product-detail/price.jsx +115 -0
  314. package/registry/blocks/product-detail/product-detail.jsx +348 -0
  315. package/registry/blocks/product-detail/product-media.jsx +102 -0
  316. package/registry/blocks/product-detail/product-options.jsx +144 -0
  317. package/registry/blocks/product-grid/README.md +162 -0
  318. package/registry/blocks/product-grid/block.json +10 -0
  319. package/registry/blocks/product-grid/demo.jsx +255 -0
  320. package/registry/blocks/product-grid/index.js +5 -0
  321. package/registry/blocks/product-grid/product-card.jsx +232 -0
  322. package/registry/blocks/product-grid/product-grid.jsx +235 -0
  323. package/registry/blocks/record-form/README.md +196 -0
  324. package/registry/blocks/record-form/block.json +10 -0
  325. package/registry/blocks/record-form/demo.jsx +525 -0
  326. package/registry/blocks/record-form/index.js +4 -0
  327. package/registry/blocks/record-form/record-fields.jsx +457 -0
  328. package/registry/blocks/record-form/record-form.jsx +415 -0
  329. package/registry/blocks/record-form/rules.js +81 -0
  330. package/registry/blocks/record-notes/README.md +139 -0
  331. package/registry/blocks/record-notes/block.json +10 -0
  332. package/registry/blocks/record-notes/demo.jsx +179 -0
  333. package/registry/blocks/record-notes/index.js +4 -0
  334. package/registry/blocks/record-notes/record-notes.jsx +266 -0
  335. package/registry/blocks/search-dialog/README.md +108 -0
  336. package/registry/blocks/search-dialog/block.json +10 -0
  337. package/registry/blocks/search-dialog/demo.jsx +116 -0
  338. package/registry/blocks/search-dialog/index.js +3 -0
  339. package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
  340. package/registry/blocks/settings-list/README.md +171 -0
  341. package/registry/blocks/settings-list/block.json +10 -0
  342. package/registry/blocks/settings-list/demo.jsx +333 -0
  343. package/registry/blocks/settings-list/index.js +4 -0
  344. package/registry/blocks/settings-list/row-state.jsx +93 -0
  345. package/registry/blocks/settings-list/settings-list.jsx +349 -0
  346. package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
  347. package/registry/blocks/setup-checklist/README.md +142 -0
  348. package/registry/blocks/setup-checklist/block.json +10 -0
  349. package/registry/blocks/setup-checklist/demo.jsx +159 -0
  350. package/registry/blocks/setup-checklist/index.js +4 -0
  351. package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
  352. package/registry/blocks/share-dialog/README.md +113 -0
  353. package/registry/blocks/share-dialog/block.json +10 -0
  354. package/registry/blocks/share-dialog/demo.jsx +140 -0
  355. package/registry/blocks/share-dialog/index.js +3 -0
  356. package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
  357. package/registry/blocks/sidebar/README.md +190 -0
  358. package/registry/blocks/sidebar/block.json +43 -0
  359. package/registry/blocks/sidebar/demo.jsx +577 -0
  360. package/registry/blocks/sidebar/index.js +4 -0
  361. package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
  362. package/registry/blocks/sidebar/sidebar.jsx +864 -0
  363. package/registry/blocks/signature-pad/README.md +137 -0
  364. package/registry/blocks/signature-pad/block.json +13 -0
  365. package/registry/blocks/signature-pad/colors.js +115 -0
  366. package/registry/blocks/signature-pad/demo.jsx +94 -0
  367. package/registry/blocks/signature-pad/index.js +6 -0
  368. package/registry/blocks/signature-pad/pad.js +7 -0
  369. package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
  370. package/registry/blocks/signature-pad/signature.js +68 -0
  371. package/registry/blocks/sketch-canvas/README.md +280 -0
  372. package/registry/blocks/sketch-canvas/block.json +13 -0
  373. package/registry/blocks/sketch-canvas/demo.jsx +159 -0
  374. package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
  375. package/registry/blocks/sketch-canvas/index.js +9 -0
  376. package/registry/blocks/sketch-canvas/palette.js +226 -0
  377. package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
  378. package/registry/blocks/sketch-canvas/sketch.js +179 -0
  379. package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
  380. package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
  381. package/registry/blocks/sketch-canvas/stamps.js +104 -0
  382. package/registry/blocks/sketch-canvas/theme.js +131 -0
  383. package/registry/blocks/spreadsheet/README.md +307 -0
  384. package/registry/blocks/spreadsheet/block.json +13 -0
  385. package/registry/blocks/spreadsheet/cells.jsx +455 -0
  386. package/registry/blocks/spreadsheet/chrome.jsx +325 -0
  387. package/registry/blocks/spreadsheet/demo.jsx +177 -0
  388. package/registry/blocks/spreadsheet/grid.js +387 -0
  389. package/registry/blocks/spreadsheet/index.js +34 -0
  390. package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
  391. package/registry/blocks/spreadsheet/types.js +473 -0
  392. package/registry/blocks/stat-cards/README.md +173 -0
  393. package/registry/blocks/stat-cards/block.json +10 -0
  394. package/registry/blocks/stat-cards/demo.jsx +257 -0
  395. package/registry/blocks/stat-cards/index.js +4 -0
  396. package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
  397. package/registry/blocks/stat-cards/usage.jsx +243 -0
  398. package/registry/blocks/task-list/README.md +176 -0
  399. package/registry/blocks/task-list/block.json +10 -0
  400. package/registry/blocks/task-list/demo.jsx +134 -0
  401. package/registry/blocks/task-list/index.js +5 -0
  402. package/registry/blocks/task-list/task-list.jsx +907 -0
  403. package/registry/blocks/task-model/README.md +455 -0
  404. package/registry/blocks/task-model/block.json +167 -0
  405. package/registry/blocks/task-model/changes.js +89 -0
  406. package/registry/blocks/task-model/dates.js +371 -0
  407. package/registry/blocks/task-model/demo.jsx +215 -0
  408. package/registry/blocks/task-model/dnd.jsx +240 -0
  409. package/registry/blocks/task-model/index.js +97 -0
  410. package/registry/blocks/task-model/model.js +919 -0
  411. package/registry/blocks/task-model/parts.jsx +449 -0
  412. package/registry/blocks/task-model/toolbar.jsx +271 -0
  413. package/registry/blocks/testimonials/README.md +139 -0
  414. package/registry/blocks/testimonials/block.json +11 -0
  415. package/registry/blocks/testimonials/demo.jsx +137 -0
  416. package/registry/blocks/testimonials/index.js +4 -0
  417. package/registry/blocks/testimonials/testimonials.jsx +430 -0
  418. package/registry/blocks/transaction-list/README.md +126 -0
  419. package/registry/blocks/transaction-list/block.json +10 -0
  420. package/registry/blocks/transaction-list/demo.jsx +79 -0
  421. package/registry/blocks/transaction-list/index.js +4 -0
  422. package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
  423. package/registry/blocks/whiteboard/README.md +179 -0
  424. package/registry/blocks/whiteboard/block.json +10 -0
  425. package/registry/blocks/whiteboard/demo.jsx +169 -0
  426. package/registry/blocks/whiteboard/index.js +6 -0
  427. package/registry/blocks/whiteboard/templates.js +150 -0
  428. package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
  429. package/src/cli.mjs +19 -6
  430. package/src/install.mjs +18 -0
  431. package/src/registry.mjs +38 -4
  432. package/src/shadcn.mjs +14 -1
  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/ui/avatar.jsx +141 -0
  437. package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
  438. package/templates/next/files/components/ui/button-group.jsx +70 -0
  439. package/templates/next/files/components/ui/button.jsx +132 -12
  440. package/templates/next/files/components/ui/calendar.jsx +609 -0
  441. package/templates/next/files/components/ui/carousel.jsx +281 -0
  442. package/templates/next/files/components/ui/collapsible.jsx +208 -0
  443. package/templates/next/files/components/ui/combobox.jsx +608 -0
  444. package/templates/next/files/components/ui/command.jsx +577 -0
  445. package/templates/next/files/components/ui/context-menu.jsx +191 -0
  446. package/templates/next/files/components/ui/date-picker.jsx +360 -0
  447. package/templates/next/files/components/ui/distribution.jsx +117 -0
  448. package/templates/next/files/components/ui/feedback.jsx +143 -0
  449. package/templates/next/files/components/ui/field.jsx +115 -3
  450. package/templates/next/files/components/ui/file-input.jsx +234 -0
  451. package/templates/next/files/components/ui/icon.jsx +130 -8
  452. package/templates/next/files/components/ui/image.jsx +143 -0
  453. package/templates/next/files/components/ui/index.js +47 -2
  454. package/templates/next/files/components/ui/input-group.jsx +213 -0
  455. package/templates/next/files/components/ui/input-otp.jsx +170 -0
  456. package/templates/next/files/components/ui/item.jsx +206 -0
  457. package/templates/next/files/components/ui/menu.jsx +454 -82
  458. package/templates/next/files/components/ui/notice.jsx +126 -14
  459. package/templates/next/files/components/ui/pagination.jsx +156 -0
  460. package/templates/next/files/components/ui/popover.jsx +284 -0
  461. package/templates/next/files/components/ui/radio.jsx +193 -0
  462. package/templates/next/files/components/ui/rating.jsx +131 -0
  463. package/templates/next/files/components/ui/resizable.jsx +338 -0
  464. package/templates/next/files/components/ui/scroll-area.jsx +123 -0
  465. package/templates/next/files/components/ui/separator.jsx +119 -0
  466. package/templates/next/files/components/ui/sheet.jsx +96 -11
  467. package/templates/next/files/components/ui/slider.jsx +174 -0
  468. package/templates/next/files/components/ui/stat.jsx +574 -0
  469. package/templates/next/files/components/ui/steps.jsx +338 -0
  470. package/templates/next/files/components/ui/swatch.jsx +33 -0
  471. package/templates/next/files/components/ui/table.jsx +130 -0
  472. package/templates/next/files/components/ui/toast.jsx +440 -0
  473. package/templates/next/files/components/ui/toggle.jsx +195 -0
  474. package/templates/next/files/components/ui/tooltip.jsx +270 -0
  475. package/templates/next/template.json +264 -8
@@ -1,5 +1,5 @@
1
1
  import { cx } from "./cx";
2
- import { Button } from "./button";
2
+ import { IconButton } from "./button";
3
3
  import { Icon } from "./icon";
4
4
 
5
5
  // Static parts: a card, a notice, a badge, a kicker, the empty state.
@@ -13,14 +13,36 @@ const TONES = {
13
13
  danger: { box: "border-destructive/50 bg-card text-card-foreground", icon: "warning" },
14
14
  };
15
15
 
16
- /** A raised panel with an optional heading row. */
17
- export function Card({ title, description, actions, className, children, as: Tag = "section" }) {
16
+ /** A raised panel with an optional heading row.
17
+ * padding md (default, 16px) · sm (12px, a dense rail) · none (the
18
+ * body runs to the border — a table, a list of rows; the
19
+ * heading row keeps its own 16px inset)
20
+ * titleSize md (default, 16px) · lg (20px, a page-level card's title)
21
+ * Pick these here, never with a className: cx does not merge classes,
22
+ * so a `p-0` laid over the default padding wins only by CSS order. */
23
+ const CARD_PADDING = { none: "", sm: "p-3", md: "p-4" };
24
+ const CARD_TITLE = { md: "text-base", lg: "text-xl" };
25
+
26
+ export function Card({
27
+ title,
28
+ description,
29
+ actions,
30
+ padding = "md",
31
+ titleSize = "md",
32
+ className,
33
+ children,
34
+ as: Tag = "section",
35
+ }) {
36
+ const pad = CARD_PADDING[padding] ?? CARD_PADDING.md;
37
+ const bare = padding === "none";
18
38
  return (
19
- <Tag className={cx("rounded-lg border border-border bg-card p-4 text-card-foreground", className)}>
39
+ <Tag className={cx("rounded-lg border border-border bg-card text-card-foreground", pad, bare && "overflow-hidden", className)}>
20
40
  {title || actions ? (
21
- <div className="mb-3 flex items-start justify-between gap-3">
41
+ <div className={cx("flex items-start justify-between gap-3", bare ? "px-4 pt-4 pb-3" : "mb-3")}>
22
42
  <div className="min-w-0">
23
- {title ? <h2 className="text-base font-semibold leading-tight">{title}</h2> : null}
43
+ {title ? (
44
+ <h2 className={cx("font-semibold leading-tight", CARD_TITLE[titleSize] ?? CARD_TITLE.md)}>{title}</h2>
45
+ ) : null}
24
46
  {description ? <p className="mt-0.5 text-sm text-muted-foreground">{description}</p> : null}
25
47
  </div>
26
48
  {actions ? <div className="flex shrink-0 gap-2">{actions}</div> : null}
@@ -45,25 +67,115 @@ export function Notice({ tone = "neutral", title, action, onDismiss, className,
45
67
  </div>
46
68
  {action ? <div className="shrink-0">{action}</div> : null}
47
69
  {onDismiss ? (
48
- <Button variant="quiet" size="sm" onClick={onDismiss} aria-label="Dismiss" className="-my-1 -mr-1 w-9 px-0">
49
- <Icon name="x" />
50
- </Button>
70
+ <IconButton icon="x" label="Dismiss" size="sm" onClick={onDismiss} className="-my-1 -mr-1" />
51
71
  ) : null}
52
72
  </div>
53
73
  );
54
74
  }
55
75
 
56
- /** A small label for a state or a count. */
57
- export function Badge({ tone = "neutral", className, children }) {
58
- const look = {
76
+ /** A small label for a state or a count.
77
+ * tone neutral · primary · success · warning · danger · info
78
+ * variant label (default: small caps, wide tracking; primary is a
79
+ * solid fill, every other tone a tint) · soft (the case as
80
+ * written, normal tracking, EVERY tone a tint — primary
81
+ * included: "Ready", "Daily over", "Feature") · outline (the
82
+ * case as written, a tone-coloured hairline on no fill) ·
83
+ * code (the mono face, the case as written, normal tracking
84
+ * — a version tag "v2.4.0", a commit "a3f8c2d", a path)
85
+ * hue 1..5 — a categorical colour, read from --color-chart-N,
86
+ * for KINDS of things (a DNS type, a platform, a content
87
+ * type). Tone is for status; hue is for category, and a
88
+ * tone given with it wins. With every variant: soft is the
89
+ * case as written on a tint of the hue with a hue dot and
90
+ * foreground text; label (and code) is filled with the hue
91
+ * and black or white text, whichever reads; outline is a
92
+ * hue hairline with foreground text. The text is never the
93
+ * hue itself: a chart colour is not made to be read as type
94
+ * Case is a variant, not its own prop: uppercase needs wide tracking and
95
+ * sentence case needs none, so the two travel together, and a `case`
96
+ * prop would have to be ignored by code. A soft primary lives on `soft`
97
+ * because `label` primary is a solid fill apps already rely on. The
98
+ * outline's ring is inset, so every variant is the same size in a row. */
99
+ const BADGE_LOOK = {
100
+ label: {
59
101
  neutral: "bg-muted text-muted-foreground",
60
102
  primary: "bg-primary text-primary-foreground",
61
103
  success: "bg-success/15 text-success",
62
104
  warning: "bg-warning/15 text-warning",
63
105
  danger: "bg-destructive/15 text-destructive",
64
106
  info: "bg-info/15 text-info",
65
- }[tone];
66
- return <span className={cx("inline-flex items-center rounded-sm px-1.5 py-0.5 text-2xs font-medium uppercase tracking-wide", look, className)}>{children}</span>;
107
+ },
108
+ soft: {
109
+ neutral: "bg-muted text-muted-foreground",
110
+ primary: "bg-primary/15 text-primary",
111
+ success: "bg-success/15 text-success",
112
+ warning: "bg-warning/15 text-warning",
113
+ danger: "bg-destructive/15 text-destructive",
114
+ info: "bg-info/15 text-info",
115
+ },
116
+ outline: {
117
+ neutral: "bg-transparent text-foreground ring-1 ring-inset ring-border",
118
+ primary: "bg-transparent text-primary ring-1 ring-inset ring-primary/50",
119
+ success: "bg-transparent text-success ring-1 ring-inset ring-success/50",
120
+ warning: "bg-transparent text-warning ring-1 ring-inset ring-warning/50",
121
+ danger: "bg-transparent text-destructive ring-1 ring-inset ring-destructive/50",
122
+ info: "bg-transparent text-info ring-1 ring-inset ring-info/50",
123
+ },
124
+ };
125
+ BADGE_LOOK.code = BADGE_LOOK.label;
126
+
127
+ const BADGE_FACE = {
128
+ label: "font-medium uppercase tracking-wide",
129
+ soft: "font-medium",
130
+ outline: "font-medium",
131
+ code: "font-mono font-normal",
132
+ };
133
+
134
+ // Categorical hues, literal so Tailwind sees every class. A filled hue's
135
+ // text is black or white, picked from the fill's relative luminance (the
136
+ // WCAG formula, on the clipped sRGB channels): what CSS contrast-color()
137
+ // does, which no token can. A chart colour sits mid-lightness (chart-2…5
138
+ // are clamped to OKLCH L 0.55–0.8, often past sRGB), where the theme's
139
+ // light text fails on a light theme and its dark text on a dark primary.
140
+ // Black below Y 0.179, white above: the better of the two is always
141
+ // 4.58:1 or more. The soft tint is light (8%) so foreground text keeps
142
+ // its contrast.
143
+ const HUE_LOOK = {
144
+ label: {
145
+ 1: "bg-chart-1 text-[color:color(from_var(--color-chart-1)_srgb_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1))]",
146
+ 2: "bg-chart-2 text-[color:color(from_var(--color-chart-2)_srgb_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1))]",
147
+ 3: "bg-chart-3 text-[color:color(from_var(--color-chart-3)_srgb_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1))]",
148
+ 4: "bg-chart-4 text-[color:color(from_var(--color-chart-4)_srgb_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1))]",
149
+ 5: "bg-chart-5 text-[color:color(from_var(--color-chart-5)_srgb_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1)_clamp(0,(0.179_-_(0.2126*pow(clamp(0,r,1),2.2)_+_0.7152*pow(clamp(0,g,1),2.2)_+_0.0722*pow(clamp(0,b,1),2.2)))*999,1))]",
150
+ },
151
+ soft: {
152
+ 1: "bg-chart-1/8 text-foreground",
153
+ 2: "bg-chart-2/8 text-foreground",
154
+ 3: "bg-chart-3/8 text-foreground",
155
+ 4: "bg-chart-4/8 text-foreground",
156
+ 5: "bg-chart-5/8 text-foreground",
157
+ },
158
+ outline: {
159
+ 1: "bg-transparent text-foreground ring-1 ring-inset ring-chart-1",
160
+ 2: "bg-transparent text-foreground ring-1 ring-inset ring-chart-2",
161
+ 3: "bg-transparent text-foreground ring-1 ring-inset ring-chart-3",
162
+ 4: "bg-transparent text-foreground ring-1 ring-inset ring-chart-4",
163
+ 5: "bg-transparent text-foreground ring-1 ring-inset ring-chart-5",
164
+ },
165
+ };
166
+ HUE_LOOK.code = HUE_LOOK.label;
167
+ const HUE_DOT = { 1: "bg-chart-1", 2: "bg-chart-2", 3: "bg-chart-3", 4: "bg-chart-4", 5: "bg-chart-5" };
168
+
169
+ export function Badge({ tone, hue, variant = "label", className, children }) {
170
+ const v = BADGE_FACE[variant] ? variant : "label";
171
+ const h = tone == null && HUE_DOT[hue] ? hue : null;
172
+ const look = h ? HUE_LOOK[v][h] : BADGE_LOOK[v][tone ?? "neutral"] ?? BADGE_LOOK[v].neutral;
173
+ return (
174
+ <span className={cx("inline-flex items-center rounded-sm px-1.5 py-0.5 text-2xs", h && v === "soft" && "gap-1", BADGE_FACE[v], look, className)}>
175
+ {h && v === "soft" ? <span aria-hidden="true" className={cx("size-1.5 shrink-0 rounded-full", HUE_DOT[h])} /> : null}
176
+ {children}
177
+ </span>
178
+ );
67
179
  }
68
180
 
69
181
  /** The small uppercase line above a heading. */
@@ -0,0 +1,156 @@
1
+ import { cx } from "./cx";
2
+ import { Button } from "./button";
3
+ import { Icon } from "./icon";
4
+ import { Select } from "./field";
5
+
6
+ // Pagination — move through a long list a page at a time. Numbers around
7
+ // the current page with "…" for the gaps, first and last always there,
8
+ // previous/next disabled at the ends. Two ways to move, pick one:
9
+ // `hrefFor(page)` makes every page a link (next/link, base path applied;
10
+ // works in a server surface, the URL is the state) or `onPageChange(page)`
11
+ // makes them buttons (a client surface holds the page).
12
+ //
13
+ // page the current page, 1-based
14
+ // pageCount how many pages there are
15
+ // siblings pages shown each side of the current one (default 1)
16
+ // compact always "Page 3 of 12" with previous/next; without it
17
+ // a phone gets that and a wider window the numbers
18
+ // pageSize with onPageSizeChange: a "Rows per page" Select
19
+ // pageSizes its choices (default 10 · 25 · 50 · 100)
20
+ // size md (44px targets) · sm (36px, for a dense table foot)
21
+ //
22
+ // One page and no page-size choice: nothing to move through, nothing drawn.
23
+
24
+ /** The slots to draw: page numbers and "gap". The row is the same width
25
+ * wherever the current page is, so the buttons never jump under a thumb. */
26
+ function slots(page, count, siblings) {
27
+ const width = siblings * 2 + 5; // first, gap, siblings, current, siblings, gap, last
28
+ if (count <= width) return Array.from({ length: count }, (_, i) => i + 1);
29
+ const range = (a, b) => Array.from({ length: b - a + 1 }, (_, i) => a + i);
30
+ const edge = siblings * 2 + 3; // what a run from either end holds before its gap
31
+ if (page <= edge - siblings) return [...range(1, edge), "gap", count];
32
+ if (page > count - (edge - siblings)) return [1, "gap", ...range(count - edge + 1, count)];
33
+ return [1, "gap", ...range(page - siblings, page + siblings), "gap", count];
34
+ }
35
+
36
+ export function Pagination({
37
+ page,
38
+ pageCount,
39
+ hrefFor,
40
+ onPageChange,
41
+ siblings = 1,
42
+ compact = false,
43
+ pageSize,
44
+ pageSizes = [10, 25, 50, 100],
45
+ onPageSizeChange,
46
+ size = "md",
47
+ label = "Pagination",
48
+ className,
49
+ }) {
50
+ const count = Math.max(1, Math.floor(pageCount || 1));
51
+ const current = Math.min(Math.max(1, Math.floor(page || 1)), count);
52
+ const sizer = Boolean(onPageSizeChange && pageSize);
53
+ if (count <= 1 && !sizer) return null;
54
+
55
+ const square = size === "sm" ? "min-w-9 px-2" : "min-w-11 px-2";
56
+
57
+ // One page control: a link when hrefFor is given, else a button. Past
58
+ // either end it is a disabled button, never a link to nowhere.
59
+ const go = (to, { children, current: isCurrent, className: extra, ...rest }) => {
60
+ const disabled = to < 1 || to > count;
61
+ const common = {
62
+ size,
63
+ variant: isCurrent ? "secondary" : "quiet",
64
+ className: extra,
65
+ "aria-current": isCurrent ? "page" : undefined,
66
+ ...rest,
67
+ };
68
+ if (disabled) return <Button {...common} disabled>{children}</Button>;
69
+ if (hrefFor) return <Button {...common} href={hrefFor(to)}>{children}</Button>;
70
+ return (
71
+ <Button {...common} onClick={isCurrent ? undefined : () => onPageChange?.(to)}>
72
+ {children}
73
+ </Button>
74
+ );
75
+ };
76
+
77
+ const prev = (wide) =>
78
+ go(current - 1, {
79
+ "aria-label": "Previous page",
80
+ className: wide ? "gap-1 pl-2 pr-3" : square,
81
+ children: (
82
+ <>
83
+ <Icon name="chevron-left" />
84
+ {wide ? <span>Previous</span> : null}
85
+ </>
86
+ ),
87
+ });
88
+ const next = (wide) =>
89
+ go(current + 1, {
90
+ "aria-label": "Next page",
91
+ className: wide ? "gap-1 pl-3 pr-2" : square,
92
+ children: (
93
+ <>
94
+ {wide ? <span>Next</span> : null}
95
+ <Icon name="chevron-right" />
96
+ </>
97
+ ),
98
+ });
99
+
100
+ const status = (
101
+ <span data-nums className="whitespace-nowrap px-2 text-sm text-muted-foreground">
102
+ Page <span className="font-medium text-foreground">{current}</span> of {count}
103
+ </span>
104
+ );
105
+
106
+ return (
107
+ <nav aria-label={label} className={cx("flex flex-wrap items-center justify-between gap-x-4 gap-y-2", className)}>
108
+ {sizer ? (
109
+ <label className="inline-flex items-center gap-2 text-sm text-muted-foreground">
110
+ <span className="whitespace-nowrap">Rows per page</span>
111
+ <span className="w-20">
112
+ <Select
113
+ value={String(pageSize)}
114
+ onChange={(e) => onPageSizeChange(Number(e.target.value))}
115
+ options={pageSizes.map((n) => ({ value: String(n), label: String(n) }))}
116
+ />
117
+ </span>
118
+ </label>
119
+ ) : null}
120
+
121
+ {count > 1 ? (
122
+ <>
123
+ {/* Compact: every width when asked, else only a phone's. */}
124
+ <div className={cx("flex items-center gap-1", sizer ? "ml-auto" : "mx-auto", !compact && "sm:hidden")}>
125
+ {prev(false)}
126
+ {status}
127
+ {next(false)}
128
+ </div>
129
+
130
+ {compact ? null : (
131
+ <ul className={cx("hidden items-center gap-1 sm:flex", sizer ? "ml-auto" : "mx-auto")}>
132
+ <li>{prev(true)}</li>
133
+ {slots(current, count, Math.max(0, siblings)).map((s, i) =>
134
+ s === "gap" ? (
135
+ <li key={`gap-${i}`} aria-hidden="true" className={cx("inline-flex items-center justify-center text-muted-foreground", square)}>
136
+ <Icon name="more" />
137
+ </li>
138
+ ) : (
139
+ <li key={s}>
140
+ {go(s, {
141
+ current: s === current,
142
+ "aria-label": `Page ${s}`,
143
+ className: square,
144
+ children: <span data-nums>{s}</span>,
145
+ })}
146
+ </li>
147
+ ),
148
+ )}
149
+ <li>{next(true)}</li>
150
+ </ul>
151
+ )}
152
+ </>
153
+ ) : null}
154
+ </nav>
155
+ );
156
+ }
@@ -0,0 +1,284 @@
1
+ "use client";
2
+
3
+ import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
4
+ import { cx } from "./cx";
5
+ import { Button } from "./button";
6
+
7
+ // Popover — a panel that floats beside the control that opened it: a
8
+ // filter form under its button, a short setting beside a row. The Popover
9
+ // API does the hard part (top layer, light dismiss, Escape, focus back to
10
+ // the trigger); CSS anchor positioning keeps it beside the trigger where
11
+ // the browser has it, and a measured fallback does where it does not.
12
+ // Controlled (`open` + `onOpenChange`) or not (`defaultOpen`).
13
+ //
14
+ // trigger a node → the content of a kit Button (`variant`, `size`
15
+ // apply); or (props) => element for a trigger of your own —
16
+ // spread `props` on a <button> (ref, popoverTarget, aria-*)
17
+ // side top · right · bottom (default) · left; flips to the
18
+ // opposite side when there is no room, and every open tries
19
+ // the asked side first again
20
+ // align start · center (default) · end, along that side (physical:
21
+ // start is the left or top edge)
22
+ // width sm (224px) · md (288px, default) · lg (384px) · auto (its
23
+ // content); never wider than the window less 16px
24
+ // className the panel's; a padding in it (p-0, p-2…) replaces the p-4
25
+ // label what a screen reader calls the panel (role=dialog);
26
+ // defaults to the trigger when it is a string
27
+ // open, defaultOpen, onOpenChange(open) — light dismiss and Escape
28
+ // report onOpenChange(false); the panel follows `open`
29
+ //
30
+ // Focus moves to the panel's first control when it opens and back to the
31
+ // trigger when it closes; Tab past the panel's end closes it.
32
+ //
33
+ // For the parts built on this (Tooltip, HoverCard, a combobox, a date
34
+ // picker): usePopoverPosition(anchorRef, panelRef, { side, align, offset })
35
+ // keeps any [popover] panel beside its anchor — flipping, following scroll
36
+ // and resize — and panelClass(width, className) is the panel's look.
37
+
38
+ const WIDTHS = { sm: "w-56", md: "w-72", lg: "w-96", auto: "w-max" };
39
+ const OPPOSITE = { top: "bottom", bottom: "top", left: "right", right: "left" };
40
+ const EDGE = 8; // the room kept between a panel and the window's edge
41
+
42
+ /** The floating panel's look: popover tokens, a border, a shadow, and a
43
+ * quick fade that `prefers-reduced-motion` drops. */
44
+ export function panelClass(width = "md", className) {
45
+ return cx(
46
+ WIDTHS[width] ?? WIDTHS.md,
47
+ "max-w-[calc(100vw-1rem)] rounded-md border border-border bg-popover text-popover-foreground shadow-lg outline-none",
48
+ "opacity-0 scale-95 transition-[opacity,scale,display,overlay] transition-discrete duration-150 open:opacity-100 open:scale-100",
49
+ "starting:open:opacity-0 starting:open:scale-95 motion-reduce:transition-none",
50
+ className,
51
+ );
52
+ }
53
+
54
+ // Anchor positioning with the fallbacks we name: Chrome 129+, Safari 26.
55
+ // Checked once; anything older takes the measured path.
56
+ let anchored;
57
+ function canAnchor() {
58
+ if (anchored === undefined) {
59
+ anchored =
60
+ typeof CSS !== "undefined" &&
61
+ CSS.supports("position-area", "bottom span-right") &&
62
+ CSS.supports("position-try-fallbacks", "top span-left, bottom span-all");
63
+ }
64
+ return anchored;
65
+ }
66
+
67
+ // Every place the panel may go, preferred first: the asked side with the
68
+ // asked alignment, then the other alignments, then the opposite side.
69
+ // `span-all` centres on the anchor with the whole window to spread into.
70
+ function areas(side, align) {
71
+ const vertical = side === "top" || side === "bottom";
72
+ const [start, end] = vertical ? ["span-right", "span-left"] : ["span-bottom", "span-top"];
73
+ const cross = { start, end, center: "span-all" };
74
+ const order = align === "start" ? ["start", "end", "center"] : align === "end" ? ["end", "start", "center"] : ["center", "start", "end"];
75
+ return [side, OPPOSITE[side]].flatMap((s) => order.map((a) => `${s} ${cross[a]}`));
76
+ }
77
+
78
+ // Set a panel beside its anchor; returns the undo.
79
+ function attach(a, p, name, side, align, offset) {
80
+ const vertical = side === "top" || side === "bottom";
81
+ p.style.inset = "auto";
82
+
83
+ if (canAnchor()) {
84
+ const [first, ...rest] = areas(side, align);
85
+ // An anchor may hold several panels (a tooltip and a popover): the
86
+ // names are a list, and each panel adds and removes only its own.
87
+ const names = () => a.style.getPropertyValue("anchor-name").split(",").map((n) => n.trim()).filter((n) => n.startsWith("--"));
88
+ a.style.setProperty("anchor-name", [...names().filter((n) => n !== name), name].join(", "));
89
+ p.style.setProperty("position-anchor", name);
90
+ p.style.setProperty("position-area", first);
91
+ p.style.setProperty("position-try-fallbacks", rest.join(", "));
92
+ p.style.setProperty("position-visibility", "anchors-visible");
93
+ // Both sides of the axis, so a flipped panel keeps its gap.
94
+ p.style.margin = vertical ? `${offset}px 0` : `0 ${offset}px`;
95
+ // Chrome remembers the last fallback that fitted while the panel is
96
+ // still drawn — and the close fade keeps it drawn — so a panel that
97
+ // once flipped reopens flipped while that side still fits. A changed
98
+ // list makes it forget: every open alternates between the list and
99
+ // the same list with its last place repeated (a no-op try), so each
100
+ // open starts again from the asked side.
101
+ let fresh = false;
102
+ const reprefer = (e) => {
103
+ if (e.newState !== "open") return;
104
+ fresh = !fresh;
105
+ p.style.setProperty("position-try-fallbacks", (fresh ? [...rest, rest[rest.length - 1]] : rest).join(", "));
106
+ };
107
+ p.addEventListener("beforetoggle", reprefer);
108
+ return () => {
109
+ p.removeEventListener("beforetoggle", reprefer);
110
+ const left = names().filter((n) => n !== name);
111
+ if (left.length) a.style.setProperty("anchor-name", left.join(", "));
112
+ else a.style.removeProperty("anchor-name");
113
+ };
114
+ }
115
+
116
+ let frame = 0;
117
+ function place() {
118
+ frame = 0;
119
+ if (!p.matches(":popover-open")) return;
120
+ const r = a.getBoundingClientRect();
121
+ const vw = document.documentElement.clientWidth;
122
+ const vh = document.documentElement.clientHeight;
123
+ // Measure at the origin, where nothing squeezes it.
124
+ p.style.top = "0px";
125
+ p.style.left = "0px";
126
+ const w = p.offsetWidth;
127
+ const h = p.offsetHeight;
128
+ const room = { top: r.top, bottom: vh - r.bottom, left: r.left, right: vw - r.right };
129
+ const need = (vertical ? h : w) + offset + EDGE;
130
+ const s = room[side] < need && room[OPPOSITE[side]] > room[side] ? OPPOSITE[side] : side;
131
+ let x;
132
+ let y;
133
+ if (vertical) {
134
+ y = s === "bottom" ? r.bottom + offset : r.top - offset - h;
135
+ x = align === "start" ? r.left : align === "end" ? r.right - w : r.left + (r.width - w) / 2;
136
+ } else {
137
+ x = s === "right" ? r.right + offset : r.left - offset - w;
138
+ y = align === "start" ? r.top : align === "end" ? r.bottom - h : r.top + (r.height - h) / 2;
139
+ }
140
+ // Shift onto the screen; where neither side had room this may cover
141
+ // the anchor, which beats a panel cut off at the window's edge.
142
+ x = Math.max(EDGE, Math.min(x, vw - w - EDGE));
143
+ y = Math.max(EDGE, Math.min(y, vh - h - EDGE));
144
+ p.style.top = `${Math.round(y)}px`;
145
+ p.style.left = `${Math.round(x)}px`;
146
+ p.dataset.side = s;
147
+ // An anchor scrolled out of the window takes its panel with it.
148
+ p.style.visibility = r.bottom < 0 || r.top > vh || r.right < 0 || r.left > vw ? "hidden" : "";
149
+ }
150
+ const queue = () => {
151
+ if (!frame) frame = requestAnimationFrame(place);
152
+ };
153
+ // A ResizeObserver reports before paint, so the panel is placed in
154
+ // the frame it appears — and again whenever either box changes size.
155
+ const ro = new ResizeObserver(place);
156
+ ro.observe(p);
157
+ ro.observe(a);
158
+ window.addEventListener("scroll", queue, true);
159
+ window.addEventListener("resize", queue);
160
+ return () => {
161
+ ro.disconnect();
162
+ cancelAnimationFrame(frame);
163
+ window.removeEventListener("scroll", queue, true);
164
+ window.removeEventListener("resize", queue);
165
+ };
166
+ }
167
+
168
+ /**
169
+ * Keep an open [popover] panel beside its anchor. CSS anchor positioning
170
+ * where the browser has it (it follows scroll natively and hides the
171
+ * panel when the anchor scrolls away); else it is measured — placed
172
+ * before the first paint, flipped to the opposite side when the asked one
173
+ * has no room, shifted along the other axis to stay on screen, and moved
174
+ * again on scroll, resize and a change in either element's size.
175
+ */
176
+ export function usePopoverPosition(anchorRef, panelRef, { side = "bottom", align = "center", offset = 6 } = {}) {
177
+ const name = `--pop-${useId().replace(/[^\w-]/g, "")}`;
178
+ const live = useRef({ key: null, off: null });
179
+
180
+ // No dependency list: a panel may mount after its anchor (a portal) or
181
+ // either element be replaced, so every commit looks, and sets up again
182
+ // only when an element or an option changed.
183
+ useLayoutEffect(() => {
184
+ const key = [anchorRef.current, panelRef.current, side, align, offset];
185
+ if (live.current.key?.every((v, i) => v === key[i])) return;
186
+ live.current.off?.();
187
+ const [a, p] = key;
188
+ live.current = { key, off: a && p ? attach(a, p, name, side, align, offset) : null };
189
+ });
190
+ useLayoutEffect(
191
+ () => () => {
192
+ live.current.off?.();
193
+ live.current = { key: null, off: null };
194
+ },
195
+ [],
196
+ );
197
+ }
198
+
199
+ const FOCUSABLE = 'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
200
+
201
+ export function Popover({
202
+ trigger,
203
+ label,
204
+ side = "bottom",
205
+ align = "center",
206
+ width = "md",
207
+ open,
208
+ defaultOpen = false,
209
+ onOpenChange,
210
+ variant = "secondary",
211
+ size = "md",
212
+ className,
213
+ children,
214
+ }) {
215
+ const id = useId();
216
+ const btn = useRef(null);
217
+ const pop = useRef(null);
218
+ const controlled = open !== undefined;
219
+ const [own, setOwn] = useState(defaultOpen);
220
+ const isOpen = controlled ? open : own;
221
+ const last = useRef(isOpen);
222
+ last.current = isOpen;
223
+ usePopoverPosition(btn, pop, { side, align });
224
+
225
+ // The state drives the top layer; the browser's own closes (light
226
+ // dismiss, Escape, the trigger again) come back through onToggle.
227
+ useEffect(() => {
228
+ const p = pop.current;
229
+ if (!p) return;
230
+ const shown = p.matches(":popover-open");
231
+ if (isOpen && !shown) p.showPopover();
232
+ else if (!isOpen && shown) p.hidePopover();
233
+ }, [isOpen]);
234
+
235
+ function onToggle(e) {
236
+ const now = e.newState === "open";
237
+ const p = pop.current;
238
+ if (now) (p?.querySelector(FOCUSABLE) ?? p)?.focus({ preventScroll: true });
239
+ else if (!document.activeElement || document.activeElement === document.body || p?.contains(document.activeElement)) {
240
+ btn.current?.focus({ preventScroll: true });
241
+ }
242
+ if (!controlled) setOwn(now);
243
+ if (now !== last.current) onOpenChange?.(now);
244
+ }
245
+
246
+ // Tab out of the panel's end (to anything but the trigger) closes it.
247
+ function onBlur(e) {
248
+ const to = e.relatedTarget;
249
+ if (to && !pop.current?.contains(to) && to !== btn.current) pop.current?.hidePopover();
250
+ }
251
+
252
+ const props = {
253
+ ref: btn,
254
+ popoverTarget: id,
255
+ "aria-haspopup": "dialog",
256
+ "aria-expanded": isOpen,
257
+ "aria-controls": id,
258
+ };
259
+
260
+ return (
261
+ <>
262
+ {typeof trigger === "function" ? (
263
+ trigger(props)
264
+ ) : (
265
+ <Button variant={variant} size={size} {...props}>
266
+ {trigger}
267
+ </Button>
268
+ )}
269
+ <div
270
+ ref={pop}
271
+ id={id}
272
+ popover="auto"
273
+ role="dialog"
274
+ aria-label={label ?? (typeof trigger === "string" ? trigger : undefined)}
275
+ tabIndex={-1}
276
+ onToggle={onToggle}
277
+ onBlur={onBlur}
278
+ className={panelClass(width, cx(!/(^|\s)p[xytrbl]?-/.test(className ?? "") && "p-4", "text-sm", className))}
279
+ >
280
+ {children}
281
+ </div>
282
+ </>
283
+ );
284
+ }