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.
- package/THIRD-PARTY-NOTICES.md +180 -0
- package/package.json +3 -2
- package/r/annotate.json +62 -0
- package/r/approval-request.json +52 -0
- package/r/audio-player.json +74 -0
- package/r/auth-form.json +55 -0
- package/r/board.json +10 -6
- package/r/calendar-view.json +105 -0
- package/r/cart.json +62 -0
- package/r/chart.json +83 -0
- package/r/chat.json +95 -0
- package/r/code-editor.json +94 -0
- package/r/comment-box.json +64 -0
- package/r/contact-section.json +53 -0
- package/r/core.json +205 -8
- package/r/csv-import.json +63 -0
- package/r/data-table.json +69 -0
- package/r/diagram.json +74 -0
- package/r/doc-editor.json +82 -0
- package/r/doc-page.json +61 -0
- package/r/email-render.json +73 -0
- package/r/faq.json +50 -0
- package/r/file-upload.json +54 -0
- package/r/flow-canvas.json +93 -0
- package/r/footer.json +52 -0
- package/r/gantt.json +70 -0
- package/r/job-panel.json +12 -7
- package/r/json-editor.json +66 -0
- package/r/map.json +89 -0
- package/r/menubar.json +52 -0
- package/r/mind-map.json +67 -0
- package/r/navigation-menu.json +52 -0
- package/r/order-detail.json +54 -0
- package/r/org-chart.json +66 -0
- package/r/page-builder.json +105 -0
- package/r/payment-methods.json +51 -0
- package/r/pdf-document.json +79 -0
- package/r/pdf-viewer.json +88 -0
- package/r/product-detail.json +76 -0
- package/r/product-grid.json +65 -0
- package/r/record-form.json +67 -0
- package/r/record-notes.json +55 -0
- package/r/registry.json +3411 -29
- package/r/search-dialog.json +51 -0
- package/r/settings-list.json +66 -0
- package/r/setup-checklist.json +51 -0
- package/r/share-dialog.json +52 -0
- package/r/sidebar.json +69 -0
- package/r/signature-pad.json +72 -0
- package/r/sketch-canvas.json +99 -0
- package/r/spreadsheet.json +90 -0
- package/r/stat-cards.json +66 -0
- package/r/task-list.json +63 -0
- package/r/task-model.json +98 -0
- package/r/testimonials.json +52 -0
- package/r/transaction-list.json +53 -0
- package/r/whiteboard.json +66 -0
- package/registry/blocks/annotate/README.md +179 -0
- package/registry/blocks/annotate/annotate-data.js +250 -0
- package/registry/blocks/annotate/annotate.jsx +646 -0
- package/registry/blocks/annotate/block.json +10 -0
- package/registry/blocks/annotate/demo.jsx +135 -0
- package/registry/blocks/annotate/index.js +15 -0
- package/registry/blocks/approval-request/README.md +116 -0
- package/registry/blocks/approval-request/approval-request.jsx +386 -0
- package/registry/blocks/approval-request/block.json +10 -0
- package/registry/blocks/approval-request/demo.jsx +127 -0
- package/registry/blocks/approval-request/index.js +4 -0
- package/registry/blocks/audio-player/README.md +167 -0
- package/registry/blocks/audio-player/audio-player.jsx +623 -0
- package/registry/blocks/audio-player/block.json +14 -0
- package/registry/blocks/audio-player/colors.js +109 -0
- package/registry/blocks/audio-player/demo.jsx +209 -0
- package/registry/blocks/audio-player/index.js +6 -0
- package/registry/blocks/audio-player/time.js +67 -0
- package/registry/blocks/audio-player/wavesurfer.js +9 -0
- package/registry/blocks/auth-form/README.md +126 -0
- package/registry/blocks/auth-form/auth-form.jsx +568 -0
- package/registry/blocks/auth-form/block.json +10 -0
- package/registry/blocks/auth-form/demo.jsx +131 -0
- package/registry/blocks/auth-form/index.js +3 -0
- package/registry/blocks/board/README.md +55 -5
- package/registry/blocks/board/block.json +2 -2
- package/registry/blocks/board/board.jsx +230 -86
- package/registry/blocks/board/demo.jsx +103 -24
- package/registry/blocks/calendar-view/README.md +180 -0
- package/registry/blocks/calendar-view/agenda.jsx +147 -0
- package/registry/blocks/calendar-view/block.json +76 -0
- package/registry/blocks/calendar-view/calendar-model.js +442 -0
- package/registry/blocks/calendar-view/calendar.jsx +627 -0
- package/registry/blocks/calendar-view/demo.jsx +195 -0
- package/registry/blocks/calendar-view/draft.jsx +49 -0
- package/registry/blocks/calendar-view/index.js +39 -0
- package/registry/blocks/calendar-view/month.jsx +346 -0
- package/registry/blocks/calendar-view/paint.js +69 -0
- package/registry/blocks/calendar-view/time-grid.jsx +527 -0
- package/registry/blocks/calendar-view/tray.jsx +87 -0
- package/registry/blocks/cart/README.md +146 -0
- package/registry/blocks/cart/block.json +10 -0
- package/registry/blocks/cart/cart.jsx +597 -0
- package/registry/blocks/cart/demo.jsx +98 -0
- package/registry/blocks/cart/index.js +4 -0
- package/registry/blocks/cart/money.js +28 -0
- package/registry/blocks/chart/README.md +316 -0
- package/registry/blocks/chart/block.json +10 -0
- package/registry/blocks/chart/cartesian.jsx +642 -0
- package/registry/blocks/chart/core.jsx +262 -0
- package/registry/blocks/chart/demo.jsx +357 -0
- package/registry/blocks/chart/heatmap.jsx +336 -0
- package/registry/blocks/chart/index.js +8 -0
- package/registry/blocks/chart/pie.jsx +245 -0
- package/registry/blocks/chart/sparkline.jsx +88 -0
- package/registry/blocks/chat/README.md +273 -0
- package/registry/blocks/chat/attachment.jsx +95 -0
- package/registry/blocks/chat/block.json +10 -0
- package/registry/blocks/chat/chat.jsx +132 -0
- package/registry/blocks/chat/composer.jsx +364 -0
- package/registry/blocks/chat/demo.jsx +578 -0
- package/registry/blocks/chat/header.jsx +34 -0
- package/registry/blocks/chat/index.js +9 -0
- package/registry/blocks/chat/message.jsx +450 -0
- package/registry/blocks/chat/scroller.jsx +247 -0
- package/registry/blocks/chat/text.jsx +104 -0
- package/registry/blocks/code-editor/README.md +210 -0
- package/registry/blocks/code-editor/block.json +22 -0
- package/registry/blocks/code-editor/code-editor.jsx +534 -0
- package/registry/blocks/code-editor/codemirror.js +38 -0
- package/registry/blocks/code-editor/demo.jsx +205 -0
- package/registry/blocks/code-editor/index.js +8 -0
- package/registry/blocks/code-editor/json.js +317 -0
- package/registry/blocks/code-editor/sql.js +136 -0
- package/registry/blocks/code-editor/text.js +101 -0
- package/registry/blocks/code-editor/theme.js +186 -0
- package/registry/blocks/comment-box/README.md +123 -0
- package/registry/blocks/comment-box/block.json +10 -0
- package/registry/blocks/comment-box/comment-body.jsx +217 -0
- package/registry/blocks/comment-box/comment-box.jsx +198 -0
- package/registry/blocks/comment-box/comment-time.jsx +77 -0
- package/registry/blocks/comment-box/demo.jsx +141 -0
- package/registry/blocks/comment-box/index.js +8 -0
- package/registry/blocks/contact-section/README.md +152 -0
- package/registry/blocks/contact-section/block.json +11 -0
- package/registry/blocks/contact-section/contact-section.jsx +509 -0
- package/registry/blocks/contact-section/demo.jsx +93 -0
- package/registry/blocks/contact-section/index.js +4 -0
- package/registry/blocks/csv-import/README.md +133 -0
- package/registry/blocks/csv-import/block.json +13 -0
- package/registry/blocks/csv-import/csv-import.jsx +479 -0
- package/registry/blocks/csv-import/demo.jsx +106 -0
- package/registry/blocks/csv-import/import.js +200 -0
- package/registry/blocks/csv-import/index.js +7 -0
- package/registry/blocks/data-table/README.md +386 -0
- package/registry/blocks/data-table/block.json +10 -0
- package/registry/blocks/data-table/data-table.jsx +1164 -0
- package/registry/blocks/data-table/demo.jsx +588 -0
- package/registry/blocks/data-table/index.js +5 -0
- package/registry/blocks/data-table/model.js +169 -0
- package/registry/blocks/diagram/README.md +108 -0
- package/registry/blocks/diagram/block.json +13 -0
- package/registry/blocks/diagram/demo.jsx +113 -0
- package/registry/blocks/diagram/diagram.jsx +481 -0
- package/registry/blocks/diagram/index.js +7 -0
- package/registry/blocks/diagram/mermaid.js +52 -0
- package/registry/blocks/diagram/palette.js +313 -0
- package/registry/blocks/diagram/source.js +98 -0
- package/registry/blocks/doc-editor/README.md +215 -0
- package/registry/blocks/doc-editor/block.json +15 -0
- package/registry/blocks/doc-editor/demo.jsx +225 -0
- package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
- package/registry/blocks/doc-editor/doc.js +309 -0
- package/registry/blocks/doc-editor/index.js +7 -0
- package/registry/blocks/doc-editor/schema.jsx +56 -0
- package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
- package/registry/blocks/doc-editor/theme.js +82 -0
- package/registry/blocks/doc-page/README.md +119 -0
- package/registry/blocks/doc-page/autosave.js +104 -0
- package/registry/blocks/doc-page/block.json +10 -0
- package/registry/blocks/doc-page/demo.jsx +137 -0
- package/registry/blocks/doc-page/doc-page.jsx +276 -0
- package/registry/blocks/doc-page/index.js +4 -0
- package/registry/blocks/email-render/README.md +151 -0
- package/registry/blocks/email-render/block.json +14 -0
- package/registry/blocks/email-render/demo.jsx +153 -0
- package/registry/blocks/email-render/doc-email.jsx +210 -0
- package/registry/blocks/email-render/email.jsx +221 -0
- package/registry/blocks/email-render/index.js +10 -0
- package/registry/blocks/email-render/render.js +50 -0
- package/registry/blocks/email-render/theme.js +215 -0
- package/registry/blocks/faq/README.md +101 -0
- package/registry/blocks/faq/block.json +11 -0
- package/registry/blocks/faq/demo.jsx +89 -0
- package/registry/blocks/faq/faq.jsx +220 -0
- package/registry/blocks/faq/index.js +4 -0
- package/registry/blocks/file-upload/README.md +138 -0
- package/registry/blocks/file-upload/block.json +10 -0
- package/registry/blocks/file-upload/demo.jsx +204 -0
- package/registry/blocks/file-upload/file-upload.jsx +635 -0
- package/registry/blocks/file-upload/index.js +4 -0
- package/registry/blocks/flow-canvas/README.md +420 -0
- package/registry/blocks/flow-canvas/block.json +13 -0
- package/registry/blocks/flow-canvas/board.js +819 -0
- package/registry/blocks/flow-canvas/chrome.jsx +649 -0
- package/registry/blocks/flow-canvas/context.jsx +35 -0
- package/registry/blocks/flow-canvas/demo.jsx +249 -0
- package/registry/blocks/flow-canvas/edges.jsx +107 -0
- package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
- package/registry/blocks/flow-canvas/index.js +41 -0
- package/registry/blocks/flow-canvas/nodes.jsx +392 -0
- package/registry/blocks/flow-canvas/theme.js +174 -0
- package/registry/blocks/footer/README.md +133 -0
- package/registry/blocks/footer/block.json +11 -0
- package/registry/blocks/footer/demo.jsx +119 -0
- package/registry/blocks/footer/footer.jsx +406 -0
- package/registry/blocks/footer/index.js +4 -0
- package/registry/blocks/gantt/README.md +220 -0
- package/registry/blocks/gantt/block.json +10 -0
- package/registry/blocks/gantt/demo.jsx +148 -0
- package/registry/blocks/gantt/gantt.jsx +923 -0
- package/registry/blocks/gantt/index.js +7 -0
- package/registry/blocks/gantt/rows.jsx +468 -0
- package/registry/blocks/gantt/schedule.js +278 -0
- package/registry/blocks/job-panel/README.md +80 -0
- package/registry/blocks/job-panel/block.json +3 -3
- package/registry/blocks/job-panel/demo.jsx +230 -3
- package/registry/blocks/job-panel/job-panel.jsx +169 -3
- package/registry/blocks/json-editor/README.md +133 -0
- package/registry/blocks/json-editor/block.json +10 -0
- package/registry/blocks/json-editor/demo.jsx +92 -0
- package/registry/blocks/json-editor/index.js +7 -0
- package/registry/blocks/json-editor/json-editor.jsx +305 -0
- package/registry/blocks/json-editor/json-tree.jsx +393 -0
- package/registry/blocks/json-editor/tree.js +225 -0
- package/registry/blocks/map/README.md +221 -0
- package/registry/blocks/map/block.json +14 -0
- package/registry/blocks/map/demo.jsx +201 -0
- package/registry/blocks/map/geo.js +198 -0
- package/registry/blocks/map/index.js +8 -0
- package/registry/blocks/map/list.jsx +128 -0
- package/registry/blocks/map/map-view.jsx +715 -0
- package/registry/blocks/map/maplibre.js +30 -0
- package/registry/blocks/map/marks.jsx +146 -0
- package/registry/blocks/map/theme.js +142 -0
- package/registry/blocks/menubar/README.md +109 -0
- package/registry/blocks/menubar/block.json +10 -0
- package/registry/blocks/menubar/demo.jsx +128 -0
- package/registry/blocks/menubar/index.js +4 -0
- package/registry/blocks/menubar/menubar.jsx +199 -0
- package/registry/blocks/mind-map/README.md +206 -0
- package/registry/blocks/mind-map/block.json +10 -0
- package/registry/blocks/mind-map/demo.jsx +103 -0
- package/registry/blocks/mind-map/index.js +31 -0
- package/registry/blocks/mind-map/mind-map.jsx +1125 -0
- package/registry/blocks/mind-map/topic.jsx +143 -0
- package/registry/blocks/mind-map/tree.js +645 -0
- package/registry/blocks/navigation-menu/README.md +124 -0
- package/registry/blocks/navigation-menu/block.json +10 -0
- package/registry/blocks/navigation-menu/demo.jsx +84 -0
- package/registry/blocks/navigation-menu/index.js +4 -0
- package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
- package/registry/blocks/order-detail/README.md +109 -0
- package/registry/blocks/order-detail/block.json +10 -0
- package/registry/blocks/order-detail/demo.jsx +88 -0
- package/registry/blocks/order-detail/index.js +4 -0
- package/registry/blocks/order-detail/order-detail.jsx +354 -0
- package/registry/blocks/org-chart/README.md +145 -0
- package/registry/blocks/org-chart/block.json +10 -0
- package/registry/blocks/org-chart/demo.jsx +94 -0
- package/registry/blocks/org-chart/index.js +7 -0
- package/registry/blocks/org-chart/org-chart.jsx +631 -0
- package/registry/blocks/org-chart/org.js +245 -0
- package/registry/blocks/org-chart/person.jsx +96 -0
- package/registry/blocks/page-builder/README.md +257 -0
- package/registry/blocks/page-builder/block.json +14 -0
- package/registry/blocks/page-builder/demo.jsx +145 -0
- package/registry/blocks/page-builder/grapes.js +18 -0
- package/registry/blocks/page-builder/index.js +11 -0
- package/registry/blocks/page-builder/page-builder.jsx +704 -0
- package/registry/blocks/page-builder/page-view.jsx +33 -0
- package/registry/blocks/page-builder/page.js +118 -0
- package/registry/blocks/page-builder/panels.jsx +588 -0
- package/registry/blocks/page-builder/sections.js +209 -0
- package/registry/blocks/page-builder/theme.js +87 -0
- package/registry/blocks/page-builder/thumbs.jsx +189 -0
- package/registry/blocks/page-builder/tokens.js +329 -0
- package/registry/blocks/payment-methods/README.md +104 -0
- package/registry/blocks/payment-methods/block.json +10 -0
- package/registry/blocks/payment-methods/demo.jsx +71 -0
- package/registry/blocks/payment-methods/index.js +4 -0
- package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
- package/registry/blocks/pdf-document/README.md +213 -0
- package/registry/blocks/pdf-document/block.json +13 -0
- package/registry/blocks/pdf-document/demo.jsx +136 -0
- package/registry/blocks/pdf-document/index.js +33 -0
- package/registry/blocks/pdf-document/money.js +83 -0
- package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
- package/registry/blocks/pdf-document/primitives.jsx +271 -0
- package/registry/blocks/pdf-document/quote.jsx +179 -0
- package/registry/blocks/pdf-document/theme.js +193 -0
- package/registry/blocks/pdf-viewer/README.md +191 -0
- package/registry/blocks/pdf-viewer/block.json +14 -0
- package/registry/blocks/pdf-viewer/demo.jsx +252 -0
- package/registry/blocks/pdf-viewer/helpers.js +270 -0
- package/registry/blocks/pdf-viewer/index.js +8 -0
- package/registry/blocks/pdf-viewer/notes.jsx +195 -0
- package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
- package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
- package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
- package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
- package/registry/blocks/product-detail/README.md +217 -0
- package/registry/blocks/product-detail/block.json +10 -0
- package/registry/blocks/product-detail/demo.jsx +233 -0
- package/registry/blocks/product-detail/index.js +7 -0
- package/registry/blocks/product-detail/price.jsx +115 -0
- package/registry/blocks/product-detail/product-detail.jsx +348 -0
- package/registry/blocks/product-detail/product-media.jsx +102 -0
- package/registry/blocks/product-detail/product-options.jsx +144 -0
- package/registry/blocks/product-grid/README.md +162 -0
- package/registry/blocks/product-grid/block.json +10 -0
- package/registry/blocks/product-grid/demo.jsx +255 -0
- package/registry/blocks/product-grid/index.js +5 -0
- package/registry/blocks/product-grid/product-card.jsx +232 -0
- package/registry/blocks/product-grid/product-grid.jsx +235 -0
- package/registry/blocks/record-form/README.md +196 -0
- package/registry/blocks/record-form/block.json +10 -0
- package/registry/blocks/record-form/demo.jsx +525 -0
- package/registry/blocks/record-form/index.js +4 -0
- package/registry/blocks/record-form/record-fields.jsx +457 -0
- package/registry/blocks/record-form/record-form.jsx +415 -0
- package/registry/blocks/record-form/rules.js +81 -0
- package/registry/blocks/record-notes/README.md +139 -0
- package/registry/blocks/record-notes/block.json +10 -0
- package/registry/blocks/record-notes/demo.jsx +179 -0
- package/registry/blocks/record-notes/index.js +4 -0
- package/registry/blocks/record-notes/record-notes.jsx +266 -0
- package/registry/blocks/search-dialog/README.md +108 -0
- package/registry/blocks/search-dialog/block.json +10 -0
- package/registry/blocks/search-dialog/demo.jsx +116 -0
- package/registry/blocks/search-dialog/index.js +3 -0
- package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
- package/registry/blocks/settings-list/README.md +171 -0
- package/registry/blocks/settings-list/block.json +10 -0
- package/registry/blocks/settings-list/demo.jsx +333 -0
- package/registry/blocks/settings-list/index.js +4 -0
- package/registry/blocks/settings-list/row-state.jsx +93 -0
- package/registry/blocks/settings-list/settings-list.jsx +349 -0
- package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
- package/registry/blocks/setup-checklist/README.md +142 -0
- package/registry/blocks/setup-checklist/block.json +10 -0
- package/registry/blocks/setup-checklist/demo.jsx +159 -0
- package/registry/blocks/setup-checklist/index.js +4 -0
- package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
- package/registry/blocks/share-dialog/README.md +113 -0
- package/registry/blocks/share-dialog/block.json +10 -0
- package/registry/blocks/share-dialog/demo.jsx +140 -0
- package/registry/blocks/share-dialog/index.js +3 -0
- package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
- package/registry/blocks/sidebar/README.md +190 -0
- package/registry/blocks/sidebar/block.json +43 -0
- package/registry/blocks/sidebar/demo.jsx +577 -0
- package/registry/blocks/sidebar/index.js +4 -0
- package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
- package/registry/blocks/sidebar/sidebar.jsx +864 -0
- package/registry/blocks/signature-pad/README.md +137 -0
- package/registry/blocks/signature-pad/block.json +13 -0
- package/registry/blocks/signature-pad/colors.js +115 -0
- package/registry/blocks/signature-pad/demo.jsx +94 -0
- package/registry/blocks/signature-pad/index.js +6 -0
- package/registry/blocks/signature-pad/pad.js +7 -0
- package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
- package/registry/blocks/signature-pad/signature.js +68 -0
- package/registry/blocks/sketch-canvas/README.md +280 -0
- package/registry/blocks/sketch-canvas/block.json +13 -0
- package/registry/blocks/sketch-canvas/demo.jsx +159 -0
- package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
- package/registry/blocks/sketch-canvas/index.js +9 -0
- package/registry/blocks/sketch-canvas/palette.js +226 -0
- package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
- package/registry/blocks/sketch-canvas/sketch.js +179 -0
- package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
- package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
- package/registry/blocks/sketch-canvas/stamps.js +104 -0
- package/registry/blocks/sketch-canvas/theme.js +131 -0
- package/registry/blocks/spreadsheet/README.md +307 -0
- package/registry/blocks/spreadsheet/block.json +13 -0
- package/registry/blocks/spreadsheet/cells.jsx +455 -0
- package/registry/blocks/spreadsheet/chrome.jsx +325 -0
- package/registry/blocks/spreadsheet/demo.jsx +177 -0
- package/registry/blocks/spreadsheet/grid.js +387 -0
- package/registry/blocks/spreadsheet/index.js +34 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
- package/registry/blocks/spreadsheet/types.js +473 -0
- package/registry/blocks/stat-cards/README.md +173 -0
- package/registry/blocks/stat-cards/block.json +10 -0
- package/registry/blocks/stat-cards/demo.jsx +257 -0
- package/registry/blocks/stat-cards/index.js +4 -0
- package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
- package/registry/blocks/stat-cards/usage.jsx +243 -0
- package/registry/blocks/task-list/README.md +176 -0
- package/registry/blocks/task-list/block.json +10 -0
- package/registry/blocks/task-list/demo.jsx +134 -0
- package/registry/blocks/task-list/index.js +5 -0
- package/registry/blocks/task-list/task-list.jsx +907 -0
- package/registry/blocks/task-model/README.md +455 -0
- package/registry/blocks/task-model/block.json +167 -0
- package/registry/blocks/task-model/changes.js +89 -0
- package/registry/blocks/task-model/dates.js +371 -0
- package/registry/blocks/task-model/demo.jsx +215 -0
- package/registry/blocks/task-model/dnd.jsx +240 -0
- package/registry/blocks/task-model/index.js +97 -0
- package/registry/blocks/task-model/model.js +919 -0
- package/registry/blocks/task-model/parts.jsx +449 -0
- package/registry/blocks/task-model/toolbar.jsx +271 -0
- package/registry/blocks/testimonials/README.md +139 -0
- package/registry/blocks/testimonials/block.json +11 -0
- package/registry/blocks/testimonials/demo.jsx +137 -0
- package/registry/blocks/testimonials/index.js +4 -0
- package/registry/blocks/testimonials/testimonials.jsx +430 -0
- package/registry/blocks/transaction-list/README.md +126 -0
- package/registry/blocks/transaction-list/block.json +10 -0
- package/registry/blocks/transaction-list/demo.jsx +79 -0
- package/registry/blocks/transaction-list/index.js +4 -0
- package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
- package/registry/blocks/whiteboard/README.md +179 -0
- package/registry/blocks/whiteboard/block.json +10 -0
- package/registry/blocks/whiteboard/demo.jsx +169 -0
- package/registry/blocks/whiteboard/index.js +6 -0
- package/registry/blocks/whiteboard/templates.js +150 -0
- package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
- package/src/cli.mjs +19 -6
- package/src/install.mjs +18 -0
- package/src/registry.mjs +38 -4
- package/src/shadcn.mjs +14 -1
- package/templates/next/files/README.md +145 -11
- package/templates/next/files/app/globals.css +55 -4
- package/templates/next/files/check/run.mjs +3 -3
- package/templates/next/files/components/ui/avatar.jsx +141 -0
- package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
- package/templates/next/files/components/ui/button-group.jsx +70 -0
- package/templates/next/files/components/ui/button.jsx +132 -12
- package/templates/next/files/components/ui/calendar.jsx +609 -0
- package/templates/next/files/components/ui/carousel.jsx +281 -0
- package/templates/next/files/components/ui/collapsible.jsx +208 -0
- package/templates/next/files/components/ui/combobox.jsx +608 -0
- package/templates/next/files/components/ui/command.jsx +577 -0
- package/templates/next/files/components/ui/context-menu.jsx +191 -0
- package/templates/next/files/components/ui/date-picker.jsx +360 -0
- package/templates/next/files/components/ui/distribution.jsx +117 -0
- package/templates/next/files/components/ui/feedback.jsx +143 -0
- package/templates/next/files/components/ui/field.jsx +115 -3
- package/templates/next/files/components/ui/file-input.jsx +234 -0
- package/templates/next/files/components/ui/icon.jsx +130 -8
- package/templates/next/files/components/ui/image.jsx +143 -0
- package/templates/next/files/components/ui/index.js +47 -2
- package/templates/next/files/components/ui/input-group.jsx +213 -0
- package/templates/next/files/components/ui/input-otp.jsx +170 -0
- package/templates/next/files/components/ui/item.jsx +206 -0
- package/templates/next/files/components/ui/menu.jsx +454 -82
- package/templates/next/files/components/ui/notice.jsx +126 -14
- package/templates/next/files/components/ui/pagination.jsx +156 -0
- package/templates/next/files/components/ui/popover.jsx +284 -0
- package/templates/next/files/components/ui/radio.jsx +193 -0
- package/templates/next/files/components/ui/rating.jsx +131 -0
- package/templates/next/files/components/ui/resizable.jsx +338 -0
- package/templates/next/files/components/ui/scroll-area.jsx +123 -0
- package/templates/next/files/components/ui/separator.jsx +119 -0
- package/templates/next/files/components/ui/sheet.jsx +96 -11
- package/templates/next/files/components/ui/slider.jsx +174 -0
- package/templates/next/files/components/ui/stat.jsx +574 -0
- package/templates/next/files/components/ui/steps.jsx +338 -0
- package/templates/next/files/components/ui/swatch.jsx +33 -0
- package/templates/next/files/components/ui/table.jsx +130 -0
- package/templates/next/files/components/ui/toast.jsx +440 -0
- package/templates/next/files/components/ui/toggle.jsx +195 -0
- package/templates/next/files/components/ui/tooltip.jsx +270 -0
- package/templates/next/template.json +264 -8
|
@@ -0,0 +1,577 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { isValidElement, useCallback, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
|
+
import { cx } from "./cx";
|
|
5
|
+
import { Icon } from "./icon";
|
|
6
|
+
import { KbdGroup } from "./separator";
|
|
7
|
+
import { Spinner } from "./feedback";
|
|
8
|
+
import { Sheet } from "./sheet";
|
|
9
|
+
|
|
10
|
+
// Command — a searchable list of actions: type to narrow, arrows to move,
|
|
11
|
+
// Enter to run. The inline list of a "jump to…" panel, and (CommandDialog)
|
|
12
|
+
// the ⌘K palette. No command package: the list is plain data, the filter
|
|
13
|
+
// is commandScore below, and the ARIA is the combobox + listbox pattern —
|
|
14
|
+
// focus stays in the input, aria-activedescendant names the active option,
|
|
15
|
+
// so a screen reader follows the highlight while the caret stays put.
|
|
16
|
+
//
|
|
17
|
+
// items [{ value?, label, description?, icon?, shortcut?, keywords?,
|
|
18
|
+
// group?, disabled?, onSelect? }] — `value` defaults to the
|
|
19
|
+
// label and must be unique; `icon` a glyph name from ./icon or
|
|
20
|
+
// a node (an Avatar); `shortcut` "⌘N" or ["⌘", "N"] (a hint
|
|
21
|
+
// only, shown from md up: bind the key yourself); `keywords`
|
|
22
|
+
// are matched but never shown; `group` is the heading items
|
|
23
|
+
// sit under, in the order groups first appear
|
|
24
|
+
// onSelect (item) — after the item's own onSelect
|
|
25
|
+
// label what a screen reader calls the input and the list
|
|
26
|
+
// placeholder the input's; empty — shown when nothing matches (a string
|
|
27
|
+
// or a node)
|
|
28
|
+
// query, defaultQuery, onQueryChange(query) — the typed text,
|
|
29
|
+
// controlled or not
|
|
30
|
+
// onSearch async (query, { signal }) → items: results come from you
|
|
31
|
+
// (a server search), debounced; the spinner shows while it
|
|
32
|
+
// runs, a stale answer is dropped. `items` show until the
|
|
33
|
+
// first answer
|
|
34
|
+
// loading true while you fetch `items` yourself
|
|
35
|
+
// filter false when `items` arrive already filtered
|
|
36
|
+
// autoFocus focus the input on mount
|
|
37
|
+
//
|
|
38
|
+
// Keys: ↑ ↓ move (disabled items are skipped), Home · End jump to the
|
|
39
|
+
// first · last, PageUp · PageDown by eight, Enter runs the active item. The
|
|
40
|
+
// first match is always active, so typing then Enter runs the best hit.
|
|
41
|
+
//
|
|
42
|
+
// CommandDialog — the same list in the kit's Sheet (center; a bottom sheet
|
|
43
|
+
// on a phone). `open` + `onOpenChange` or uncontrolled; `hotkey="k"` binds
|
|
44
|
+
// ⌘K / Ctrl+K to open and close it. Running an item closes it.
|
|
45
|
+
//
|
|
46
|
+
// commandScore(text, query) → 0…1 (0 = no match) and commandFilter(items,
|
|
47
|
+
// query) → the matching items, best first within their group, are exported
|
|
48
|
+
// for the parts built on this (Combobox) and for a list of your own.
|
|
49
|
+
//
|
|
50
|
+
// The one rule: the list is data. Never map items into your own markup —
|
|
51
|
+
// the ids, roles and keys here are what make it work for everyone.
|
|
52
|
+
|
|
53
|
+
// ─── matching ──────────────────────────────────────────────────────────
|
|
54
|
+
|
|
55
|
+
const fold = (s) =>
|
|
56
|
+
String(s ?? "")
|
|
57
|
+
.normalize("NFD")
|
|
58
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
59
|
+
.toLowerCase();
|
|
60
|
+
|
|
61
|
+
const BOUNDARY = /[\s\-_/.,:;()@&+]/;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* How well `text` matches `query`, 0 (not at all) to 1 (exactly). Accents
|
|
65
|
+
* and case are ignored. Best to worst: equal, a prefix, a substring at a
|
|
66
|
+
* word start, a substring inside a word, then the query's letters in order
|
|
67
|
+
* ("nyc" in "New York City"), scored by how many land on word starts.
|
|
68
|
+
*/
|
|
69
|
+
export function commandScore(text, query) {
|
|
70
|
+
const t = fold(text);
|
|
71
|
+
const q = fold(query).trim();
|
|
72
|
+
if (!q) return 1;
|
|
73
|
+
if (!t) return 0;
|
|
74
|
+
if (t === q) return 1;
|
|
75
|
+
if (t.startsWith(q)) return 0.9;
|
|
76
|
+
const at = t.indexOf(q);
|
|
77
|
+
if (at > 0) return (BOUNDARY.test(t[at - 1]) ? 0.8 : 0.6) - Math.min(at, 40) * 0.002;
|
|
78
|
+
// The letters in order.
|
|
79
|
+
let from = 0;
|
|
80
|
+
let prev = -2;
|
|
81
|
+
let points = 0;
|
|
82
|
+
let count = 0;
|
|
83
|
+
for (const ch of q) {
|
|
84
|
+
if (ch === " ") continue;
|
|
85
|
+
const found = t.indexOf(ch, from);
|
|
86
|
+
if (found < 0) return 0;
|
|
87
|
+
points += found === 0 || BOUNDARY.test(t[found - 1]) ? 1 : found === prev + 1 ? 0.6 : 0.2;
|
|
88
|
+
count++;
|
|
89
|
+
prev = found;
|
|
90
|
+
from = found + 1;
|
|
91
|
+
}
|
|
92
|
+
return 0.1 + 0.3 * (points / count);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// An item's best field: the label counts most, keywords nearly as much,
|
|
96
|
+
// the description least.
|
|
97
|
+
function scoreItem(item, q) {
|
|
98
|
+
const best = (query) =>
|
|
99
|
+
Math.max(
|
|
100
|
+
commandScore(item.label, query),
|
|
101
|
+
...(item.keywords ?? []).map((k) => commandScore(k, query) * 0.95),
|
|
102
|
+
item.description && typeof item.description === "string" ? commandScore(item.description, query) * 0.7 : 0,
|
|
103
|
+
);
|
|
104
|
+
const whole = best(q);
|
|
105
|
+
if (whole > 0) return whole;
|
|
106
|
+
// Words in any order ("lovelace ada"): every word must match somewhere.
|
|
107
|
+
const words = fold(q).split(/\s+/).filter(Boolean);
|
|
108
|
+
if (words.length < 2) return 0;
|
|
109
|
+
let sum = 0;
|
|
110
|
+
for (const w of words) {
|
|
111
|
+
const s = best(w);
|
|
112
|
+
if (!s) return 0;
|
|
113
|
+
sum += s;
|
|
114
|
+
}
|
|
115
|
+
return (sum / words.length) * 0.9;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** Every item with a `value` (the label when it has none). */
|
|
119
|
+
export function withValues(items) {
|
|
120
|
+
return (items ?? []).filter(Boolean).map((it) => (it.value === undefined ? { ...it, value: String(it.label) } : { ...it, value: String(it.value) }));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The items that match `query`, best first within their group; the groups
|
|
125
|
+
* keep the order they first appear in. An empty query returns them all,
|
|
126
|
+
* in order.
|
|
127
|
+
*/
|
|
128
|
+
export function commandFilter(items, query) {
|
|
129
|
+
const all = withValues(items);
|
|
130
|
+
if (!fold(query).trim()) return all;
|
|
131
|
+
const order = [];
|
|
132
|
+
const byGroup = new Map();
|
|
133
|
+
all.forEach((item, i) => {
|
|
134
|
+
const score = scoreItem(item, query);
|
|
135
|
+
if (!score) return;
|
|
136
|
+
const g = item.group ?? "";
|
|
137
|
+
if (!byGroup.has(g)) {
|
|
138
|
+
byGroup.set(g, []);
|
|
139
|
+
order.push(g);
|
|
140
|
+
}
|
|
141
|
+
byGroup.get(g).push({ item, score, i });
|
|
142
|
+
});
|
|
143
|
+
return order.flatMap((g) => byGroup.get(g).sort((a, b) => b.score - a.score || a.i - b.i).map((x) => x.item));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ─── pieces shared with Combobox ───────────────────────────────────────
|
|
147
|
+
|
|
148
|
+
/** The option's DOM id: stable for a list id and an index. */
|
|
149
|
+
export const optionId = (listId, i) => `${listId}-o${i}`;
|
|
150
|
+
|
|
151
|
+
/** The active value as the list shows it: the asked one while it is
|
|
152
|
+
* visible and enabled, else the first enabled item. */
|
|
153
|
+
export function resolveActive(items, active) {
|
|
154
|
+
if (active != null && items.some((it) => it.value === active && !it.disabled)) return active;
|
|
155
|
+
return items.find((it) => !it.disabled)?.value ?? null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The list keys. Returns true (and prevents the default) when it handled
|
|
160
|
+
* the key: ↑ ↓ Home End PageUp PageDown move, Enter picks.
|
|
161
|
+
*/
|
|
162
|
+
export function listKeyDown(e, items, active, setActive, onPick) {
|
|
163
|
+
const live = items.filter((it) => !it.disabled);
|
|
164
|
+
if (!live.length) return false;
|
|
165
|
+
const i = live.findIndex((it) => it.value === active);
|
|
166
|
+
const last = live.length - 1;
|
|
167
|
+
let next;
|
|
168
|
+
switch (e.key) {
|
|
169
|
+
case "ArrowDown":
|
|
170
|
+
next = i < 0 ? 0 : Math.min(i + 1, last);
|
|
171
|
+
break;
|
|
172
|
+
case "ArrowUp":
|
|
173
|
+
next = i < 0 ? last : Math.max(i - 1, 0);
|
|
174
|
+
break;
|
|
175
|
+
case "Home":
|
|
176
|
+
next = 0;
|
|
177
|
+
break;
|
|
178
|
+
case "End":
|
|
179
|
+
next = last;
|
|
180
|
+
break;
|
|
181
|
+
case "PageDown":
|
|
182
|
+
next = Math.min((i < 0 ? 0 : i) + 8, last);
|
|
183
|
+
break;
|
|
184
|
+
case "PageUp":
|
|
185
|
+
next = Math.max((i < 0 ? 0 : i) - 8, 0);
|
|
186
|
+
break;
|
|
187
|
+
case "Enter":
|
|
188
|
+
// An IME composing a word owns Enter.
|
|
189
|
+
if (i < 0 || e.nativeEvent?.isComposing) return false;
|
|
190
|
+
e.preventDefault();
|
|
191
|
+
onPick(live[i]);
|
|
192
|
+
return true;
|
|
193
|
+
default:
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
setActive(live[next].value);
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Results from an async `onSearch(query, { signal })`, debounced by
|
|
203
|
+
* `delay` ms (none for the empty query). A newer query aborts the older
|
|
204
|
+
* one; a failed search keeps the last results and sets `error`.
|
|
205
|
+
*/
|
|
206
|
+
export function useAsyncSearch(onSearch, query, { enabled = true, delay = 200 } = {}) {
|
|
207
|
+
const fn = useRef(onSearch);
|
|
208
|
+
fn.current = onSearch;
|
|
209
|
+
const has = Boolean(onSearch);
|
|
210
|
+
const [state, setState] = useState({ items: null, loading: false, error: null });
|
|
211
|
+
useEffect(() => {
|
|
212
|
+
if (!has || !enabled) return;
|
|
213
|
+
const ctl = new AbortController();
|
|
214
|
+
setState((s) => (s.loading ? s : { ...s, loading: true }));
|
|
215
|
+
const timer = setTimeout(async () => {
|
|
216
|
+
try {
|
|
217
|
+
const items = await fn.current(query, { signal: ctl.signal });
|
|
218
|
+
if (!ctl.signal.aborted) setState({ items: items ?? [], loading: false, error: null });
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (!ctl.signal.aborted) setState((s) => ({ items: s.items ?? [], loading: false, error }));
|
|
221
|
+
}
|
|
222
|
+
}, query.trim() ? delay : 0);
|
|
223
|
+
return () => {
|
|
224
|
+
clearTimeout(timer);
|
|
225
|
+
ctl.abort();
|
|
226
|
+
};
|
|
227
|
+
}, [has, enabled, query, delay]);
|
|
228
|
+
return state;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const PHONE = "(max-width: 639.98px)";
|
|
232
|
+
const subscribePhone = (cb) => {
|
|
233
|
+
const m = window.matchMedia(PHONE);
|
|
234
|
+
m.addEventListener("change", cb);
|
|
235
|
+
return () => m.removeEventListener("change", cb);
|
|
236
|
+
};
|
|
237
|
+
/** True under the kit's `sm` breakpoint (640px), where Sheet turns every
|
|
238
|
+
* overlay into a bottom sheet. False on the server. */
|
|
239
|
+
export function useIsPhone() {
|
|
240
|
+
return useSyncExternalStore(
|
|
241
|
+
subscribePhone,
|
|
242
|
+
() => window.matchMedia(PHONE).matches,
|
|
243
|
+
() => false,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/** An item's icon: a glyph name, or a node (an Avatar) as it is. */
|
|
248
|
+
export function ItemIcon({ icon, className }) {
|
|
249
|
+
if (!icon) return null;
|
|
250
|
+
if (typeof icon === "string") return <Icon name={icon} className={cx("shrink-0 text-muted-foreground", className)} />;
|
|
251
|
+
return isValidElement(icon) ? <span className={cx("flex shrink-0 items-center", className)}>{icon}</span> : null;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* The search bar at the top of a Command or a Combobox panel: a glyph, the
|
|
255
|
+
* input (role=combobox, pointing at `listId`), a spinner while `busy`.
|
|
256
|
+
* Every other prop goes to the <input>.
|
|
257
|
+
*/
|
|
258
|
+
export function CommandInput({ listId, activeId, busy, inputRef, className, ...rest }) {
|
|
259
|
+
return (
|
|
260
|
+
<div className={cx("flex items-center gap-2 border-b border-border px-3", className)}>
|
|
261
|
+
<Icon name="search" className="shrink-0 text-muted-foreground" />
|
|
262
|
+
<input
|
|
263
|
+
ref={inputRef}
|
|
264
|
+
type="text"
|
|
265
|
+
role="combobox"
|
|
266
|
+
aria-expanded="true"
|
|
267
|
+
aria-controls={listId}
|
|
268
|
+
aria-activedescendant={activeId ?? undefined}
|
|
269
|
+
aria-autocomplete="list"
|
|
270
|
+
autoComplete="off"
|
|
271
|
+
autoCorrect="off"
|
|
272
|
+
spellCheck={false}
|
|
273
|
+
// 16px on a phone, or iOS zooms the page when it takes focus.
|
|
274
|
+
className="min-h-11 min-w-0 flex-1 bg-transparent py-2 text-base text-foreground outline-none placeholder:text-muted-foreground sm:text-sm"
|
|
275
|
+
{...rest}
|
|
276
|
+
/>
|
|
277
|
+
{busy ? <Spinner size="sm" label="Searching" className="text-muted-foreground" /> : null}
|
|
278
|
+
</div>
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* The listbox: groups under their headings, options with icon, label,
|
|
284
|
+
* description and shortcut; the empty, loading and error lines around it.
|
|
285
|
+
*
|
|
286
|
+
* id, label the listbox's id and name
|
|
287
|
+
* items already filtered, in the order shown
|
|
288
|
+
* active the active value; onActive(value) on hover
|
|
289
|
+
* onPick(item) click, or Enter through listKeyDown
|
|
290
|
+
* selected a Set of chosen values (a Combobox): each option shows a
|
|
291
|
+
* check (`multiple`: a box at the start) and aria-selected
|
|
292
|
+
* says chosen; without it aria-selected marks the active
|
|
293
|
+
* className the scroll box's; its max height (default max-h-72)
|
|
294
|
+
* focusable the listbox takes focus itself (aria-activedescendant on
|
|
295
|
+
* it, `onKeyDown` for the keys) — when there is no input
|
|
296
|
+
*/
|
|
297
|
+
export function CommandList({
|
|
298
|
+
id,
|
|
299
|
+
label,
|
|
300
|
+
items,
|
|
301
|
+
active,
|
|
302
|
+
onActive,
|
|
303
|
+
onPick,
|
|
304
|
+
selected,
|
|
305
|
+
multiple = false,
|
|
306
|
+
empty = "No results.",
|
|
307
|
+
loading = false,
|
|
308
|
+
error,
|
|
309
|
+
focusable = false,
|
|
310
|
+
onKeyDown,
|
|
311
|
+
listRef,
|
|
312
|
+
className,
|
|
313
|
+
}) {
|
|
314
|
+
const box = useRef(null);
|
|
315
|
+
const activeIndex = items.findIndex((it) => it.value === active);
|
|
316
|
+
|
|
317
|
+
// Keep the active option in view as the keys move it.
|
|
318
|
+
useEffect(() => {
|
|
319
|
+
if (activeIndex < 0) return;
|
|
320
|
+
document.getElementById(optionId(id, activeIndex))?.scrollIntoView({ block: "nearest" });
|
|
321
|
+
}, [id, activeIndex]);
|
|
322
|
+
|
|
323
|
+
// Consecutive items with one group share a heading.
|
|
324
|
+
const sections = [];
|
|
325
|
+
items.forEach((item, i) => {
|
|
326
|
+
const g = item.group ?? "";
|
|
327
|
+
const tail = sections[sections.length - 1];
|
|
328
|
+
if (tail && tail.group === g) tail.rows.push([item, i]);
|
|
329
|
+
else sections.push({ group: g, rows: [[item, i]] });
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
function option([item, i]) {
|
|
333
|
+
const isActive = item.value === active;
|
|
334
|
+
const chosen = selected ? selected.has(item.value) : false;
|
|
335
|
+
const keys = item.shortcut ? (Array.isArray(item.shortcut) ? item.shortcut : [item.shortcut]) : null;
|
|
336
|
+
return (
|
|
337
|
+
<div
|
|
338
|
+
key={item.value}
|
|
339
|
+
id={optionId(id, i)}
|
|
340
|
+
role="option"
|
|
341
|
+
aria-selected={selected ? chosen : isActive}
|
|
342
|
+
aria-disabled={item.disabled || undefined}
|
|
343
|
+
data-active={isActive || undefined}
|
|
344
|
+
// The input keeps focus; a press on an option must not take it.
|
|
345
|
+
onMouseDown={(e) => e.preventDefault()}
|
|
346
|
+
onMouseMove={() => {
|
|
347
|
+
if (!item.disabled && !isActive) onActive?.(item.value);
|
|
348
|
+
}}
|
|
349
|
+
onClick={() => {
|
|
350
|
+
if (!item.disabled) onPick?.(item);
|
|
351
|
+
}}
|
|
352
|
+
className={cx(
|
|
353
|
+
"flex min-h-11 cursor-pointer select-none items-center gap-2 rounded-sm px-2 py-1.5 text-sm text-popover-foreground md:min-h-9",
|
|
354
|
+
"data-[active]:bg-accent data-[active]:text-accent-foreground",
|
|
355
|
+
"aria-disabled:cursor-not-allowed aria-disabled:opacity-50",
|
|
356
|
+
)}
|
|
357
|
+
>
|
|
358
|
+
{selected && multiple ? (
|
|
359
|
+
<span
|
|
360
|
+
aria-hidden="true"
|
|
361
|
+
className={cx(
|
|
362
|
+
"grid size-4 shrink-0 place-items-center rounded-sm border",
|
|
363
|
+
chosen ? "border-primary bg-primary text-primary-foreground" : "border-input bg-background",
|
|
364
|
+
)}
|
|
365
|
+
>
|
|
366
|
+
{chosen ? <Icon name="check" size={12} /> : null}
|
|
367
|
+
</span>
|
|
368
|
+
) : null}
|
|
369
|
+
<ItemIcon icon={item.icon} />
|
|
370
|
+
<span className="min-w-0 flex-1">
|
|
371
|
+
<span className="block truncate">{item.label}</span>
|
|
372
|
+
{item.description ? <span className="block truncate text-xs text-muted-foreground">{item.description}</span> : null}
|
|
373
|
+
</span>
|
|
374
|
+
{keys ? (
|
|
375
|
+
<span className="hidden shrink-0 md:inline-flex" aria-hidden="true">
|
|
376
|
+
<KbdGroup keys={keys} />
|
|
377
|
+
</span>
|
|
378
|
+
) : null}
|
|
379
|
+
{selected && !multiple ? (
|
|
380
|
+
<Icon name="check" className={cx("shrink-0 text-foreground", !chosen && "invisible")} />
|
|
381
|
+
) : null}
|
|
382
|
+
</div>
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const status = error ? "Search failed. Try again." : loading ? null : items.length === 0 ? empty : null;
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<div className={cx("overflow-y-auto overscroll-contain", className ?? "max-h-72")}>
|
|
390
|
+
{loading && items.length === 0 ? (
|
|
391
|
+
<div role="status" className="flex items-center justify-center gap-2 px-3 py-6 text-sm text-muted-foreground">
|
|
392
|
+
<Spinner size="sm" label={null} />
|
|
393
|
+
Searching…
|
|
394
|
+
</div>
|
|
395
|
+
) : null}
|
|
396
|
+
{status && !(loading && items.length === 0) ? (
|
|
397
|
+
<div role="status" className="px-3 py-6 text-center text-sm text-muted-foreground">
|
|
398
|
+
{status}
|
|
399
|
+
</div>
|
|
400
|
+
) : null}
|
|
401
|
+
<div
|
|
402
|
+
ref={(el) => {
|
|
403
|
+
box.current = el;
|
|
404
|
+
if (listRef) listRef.current = el;
|
|
405
|
+
}}
|
|
406
|
+
id={id}
|
|
407
|
+
role="listbox"
|
|
408
|
+
aria-label={label}
|
|
409
|
+
aria-multiselectable={multiple || undefined}
|
|
410
|
+
tabIndex={focusable ? 0 : undefined}
|
|
411
|
+
aria-activedescendant={focusable && activeIndex >= 0 ? optionId(id, activeIndex) : undefined}
|
|
412
|
+
onKeyDown={onKeyDown}
|
|
413
|
+
className={cx("scroll-py-1 outline-none", items.length ? "p-1" : "hidden")}
|
|
414
|
+
>
|
|
415
|
+
{sections.map((s, n) =>
|
|
416
|
+
s.group ? (
|
|
417
|
+
<div
|
|
418
|
+
key={`${s.group}-${n}`}
|
|
419
|
+
role="group"
|
|
420
|
+
aria-labelledby={`${id}-g${n}`}
|
|
421
|
+
className={cx(n > 0 && "mt-1 border-t border-border pt-1")}
|
|
422
|
+
>
|
|
423
|
+
<div id={`${id}-g${n}`} className="px-2 pb-1 pt-1.5 text-xs font-medium text-muted-foreground">
|
|
424
|
+
{s.group}
|
|
425
|
+
</div>
|
|
426
|
+
{s.rows.map(option)}
|
|
427
|
+
</div>
|
|
428
|
+
) : (
|
|
429
|
+
<div key={`-${n}`} role="presentation" className={cx(n > 0 && "mt-1 border-t border-border pt-1")}>
|
|
430
|
+
{s.rows.map(option)}
|
|
431
|
+
</div>
|
|
432
|
+
),
|
|
433
|
+
)}
|
|
434
|
+
</div>
|
|
435
|
+
</div>
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// ─── Command ───────────────────────────────────────────────────────────
|
|
440
|
+
|
|
441
|
+
export function Command({
|
|
442
|
+
items,
|
|
443
|
+
onSelect,
|
|
444
|
+
label = "Commands",
|
|
445
|
+
placeholder = "Type a command or search…",
|
|
446
|
+
empty = "No results.",
|
|
447
|
+
query,
|
|
448
|
+
defaultQuery = "",
|
|
449
|
+
onQueryChange,
|
|
450
|
+
onSearch,
|
|
451
|
+
loading = false,
|
|
452
|
+
filter = true,
|
|
453
|
+
autoFocus = false,
|
|
454
|
+
className,
|
|
455
|
+
listClassName,
|
|
456
|
+
bare = false,
|
|
457
|
+
}) {
|
|
458
|
+
const listId = `${useId().replace(/[^\w-]/g, "")}-list`;
|
|
459
|
+
const input = useRef(null);
|
|
460
|
+
const [own, setOwn] = useState(defaultQuery);
|
|
461
|
+
const text = query ?? own;
|
|
462
|
+
const [active, setActive] = useState(null);
|
|
463
|
+
const search = useAsyncSearch(onSearch, text);
|
|
464
|
+
|
|
465
|
+
const source = onSearch ? search.items ?? items : items;
|
|
466
|
+
const visible = useMemo(
|
|
467
|
+
() => (filter && !onSearch ? commandFilter(source, text) : withValues(source)),
|
|
468
|
+
[source, text, filter, onSearch],
|
|
469
|
+
);
|
|
470
|
+
const current = resolveActive(visible, active);
|
|
471
|
+
const activeIndex = visible.findIndex((it) => it.value === current);
|
|
472
|
+
|
|
473
|
+
useEffect(() => {
|
|
474
|
+
if (autoFocus) input.current?.focus({ preventScroll: true });
|
|
475
|
+
}, [autoFocus]);
|
|
476
|
+
|
|
477
|
+
function setText(v) {
|
|
478
|
+
if (query === undefined) setOwn(v);
|
|
479
|
+
onQueryChange?.(v);
|
|
480
|
+
// A new query starts from its best match.
|
|
481
|
+
setActive(null);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function pick(item) {
|
|
485
|
+
item.onSelect?.(item);
|
|
486
|
+
onSelect?.(item);
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
const busy = loading || search.loading;
|
|
490
|
+
|
|
491
|
+
return (
|
|
492
|
+
<div
|
|
493
|
+
className={cx(
|
|
494
|
+
"flex min-w-0 flex-col overflow-hidden bg-popover text-popover-foreground",
|
|
495
|
+
!bare && "rounded-md border border-border has-[input:focus-visible]:ring-2 has-[input:focus-visible]:ring-ring",
|
|
496
|
+
className,
|
|
497
|
+
)}
|
|
498
|
+
>
|
|
499
|
+
<CommandInput
|
|
500
|
+
inputRef={input}
|
|
501
|
+
listId={listId}
|
|
502
|
+
activeId={activeIndex >= 0 ? optionId(listId, activeIndex) : null}
|
|
503
|
+
busy={busy && visible.length > 0}
|
|
504
|
+
aria-label={label}
|
|
505
|
+
placeholder={placeholder}
|
|
506
|
+
value={text}
|
|
507
|
+
onChange={(e) => setText(e.target.value)}
|
|
508
|
+
onKeyDown={(e) => listKeyDown(e, visible, current, setActive, pick)}
|
|
509
|
+
/>
|
|
510
|
+
<CommandList
|
|
511
|
+
id={listId}
|
|
512
|
+
label={label}
|
|
513
|
+
items={visible}
|
|
514
|
+
active={current}
|
|
515
|
+
onActive={setActive}
|
|
516
|
+
onPick={pick}
|
|
517
|
+
empty={empty}
|
|
518
|
+
loading={busy}
|
|
519
|
+
error={search.error}
|
|
520
|
+
className={listClassName ?? "max-h-80"}
|
|
521
|
+
/>
|
|
522
|
+
</div>
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export function CommandDialog({ open, defaultOpen = false, onOpenChange, hotkey, label = "Command menu", onSelect, className, ...command }) {
|
|
527
|
+
const controlled = open !== undefined;
|
|
528
|
+
const [own, setOwn] = useState(defaultOpen);
|
|
529
|
+
const isOpen = controlled ? open : own;
|
|
530
|
+
const openRef = useRef(isOpen);
|
|
531
|
+
openRef.current = isOpen;
|
|
532
|
+
|
|
533
|
+
const change = useCallback(
|
|
534
|
+
(next) => {
|
|
535
|
+
if (next === openRef.current) return;
|
|
536
|
+
openRef.current = next;
|
|
537
|
+
if (!controlled) setOwn(next);
|
|
538
|
+
onOpenChange?.(next);
|
|
539
|
+
},
|
|
540
|
+
[controlled, onOpenChange],
|
|
541
|
+
);
|
|
542
|
+
const changeRef = useRef(change);
|
|
543
|
+
changeRef.current = change;
|
|
544
|
+
|
|
545
|
+
// ⌘K / Ctrl+K opens it, and closes it again.
|
|
546
|
+
useEffect(() => {
|
|
547
|
+
if (!hotkey) return;
|
|
548
|
+
function onKey(e) {
|
|
549
|
+
if (e.key?.toLowerCase() !== String(hotkey).toLowerCase() || !(e.metaKey || e.ctrlKey) || e.altKey || e.shiftKey) return;
|
|
550
|
+
e.preventDefault();
|
|
551
|
+
changeRef.current(!openRef.current);
|
|
552
|
+
}
|
|
553
|
+
window.addEventListener("keydown", onKey);
|
|
554
|
+
return () => window.removeEventListener("keydown", onKey);
|
|
555
|
+
}, [hotkey]);
|
|
556
|
+
|
|
557
|
+
// A bare Sheet: no header (Esc and a press outside close it), a flush
|
|
558
|
+
// body so the input sits at the top edge, the dialog named by `label`.
|
|
559
|
+
return (
|
|
560
|
+
<Sheet bare label={label} open={isOpen} onClose={() => change(false)} side="center" className={cx("sm:mt-[12dvh]", className)}>
|
|
561
|
+
{/* Mounted only while open, so each opening starts empty. */}
|
|
562
|
+
{isOpen ? (
|
|
563
|
+
<Command
|
|
564
|
+
bare
|
|
565
|
+
label={label}
|
|
566
|
+
autoFocus={isOpen}
|
|
567
|
+
listClassName="max-h-[min(24rem,60dvh)]"
|
|
568
|
+
onSelect={(item) => {
|
|
569
|
+
change(false);
|
|
570
|
+
onSelect?.(item);
|
|
571
|
+
}}
|
|
572
|
+
{...command}
|
|
573
|
+
/>
|
|
574
|
+
) : null}
|
|
575
|
+
</Sheet>
|
|
576
|
+
);
|
|
577
|
+
}
|