makersclaw 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +41 -1
- package/THIRD-PARTY-NOTICES.md +180 -0
- package/package.json +15 -8
- 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 +55 -0
- 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 +341 -0
- 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 +56 -0
- 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 +3612 -0
- 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 +64 -13
- package/registry/blocks/board/block.json +2 -2
- package/registry/blocks/board/board.jsx +237 -94
- 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 +86 -2
- 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/install.mjs +18 -0
- package/src/shadcn.mjs +171 -0
- 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/parts/fixture.jsx +1 -1
- package/templates/next/files/components/parts/sample.js +15 -0
- 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/files/lib/sample.js +4 -12
- package/templates/next/template.json +264 -8
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// The Faq in every variant — the gallery's and the smoke test's surface.
|
|
4
|
+
// Not installed into apps: `makersclaw add faq` lays index.js and
|
|
5
|
+
// faq.jsx only. Pick a variant and a state; the call to action only
|
|
6
|
+
// counts its presses here.
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Field, Select, ToggleGroup } from "../components/ui";
|
|
9
|
+
import { Faq } from "../components/blocks/faq";
|
|
10
|
+
|
|
11
|
+
const QUESTIONS = [
|
|
12
|
+
{
|
|
13
|
+
id: "trial",
|
|
14
|
+
icon: "clock",
|
|
15
|
+
question: "How long is the free trial?",
|
|
16
|
+
answer: "Fourteen days, with every feature on. We ask for a card only when you choose a plan, and nothing is charged before day fifteen.",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
id: "switch",
|
|
20
|
+
icon: "repeat",
|
|
21
|
+
question: "Can I move my data from another tool?",
|
|
22
|
+
answer: "Yes. Import a CSV from any spreadsheet, or connect your old CRM and we copy contacts, deals and notes across in one pass.",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "seats",
|
|
26
|
+
icon: "users",
|
|
27
|
+
question: "Do I pay per person?",
|
|
28
|
+
answer: "Plans include five seats. Each extra seat is $8 a month, billed only for the days it was in use.",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "cancel",
|
|
32
|
+
icon: "log-out",
|
|
33
|
+
question: "What happens if I cancel?",
|
|
34
|
+
answer: "Your workspace stays readable for 90 days, and you can export everything as CSV at any time. After that it is deleted for good.",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "security",
|
|
38
|
+
icon: "shield-check",
|
|
39
|
+
question: "Where is my data stored?",
|
|
40
|
+
answer: "In the EU (Frankfurt) by default, or the US on request. Everything is encrypted at rest and in transit, and backed up daily.",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "support",
|
|
44
|
+
icon: "life-buoy",
|
|
45
|
+
question: "How do I reach a person?",
|
|
46
|
+
answer: "Write to the chat in the corner or email support. A person replies within an hour on weekdays, 9 to 6 CET.",
|
|
47
|
+
},
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const CTA = { title: "Still have a question?", body: "Tell us what you are trying to do and we will reply within the hour.", label: "Send us a note", icon: "mail" };
|
|
51
|
+
|
|
52
|
+
const VARIANTS = [
|
|
53
|
+
{ id: "list-01", label: "Accordion · one open at a time", props: { layout: "accordion", description: "The things people ask before they sign up.", defaultOpen: "trial" } },
|
|
54
|
+
{ id: "grid-01", label: "Grid · two columns with icons", props: { layout: "grid", columns: 2, description: "Short answers to the usual questions." } },
|
|
55
|
+
{ id: "cards-icons-01", label: "Grid · icon cards and a request", props: { layout: "grid", description: "Short answers to the usual questions.", cta: CTA } },
|
|
56
|
+
{ id: "grid-cta-01", label: "Grid · plain cards and a CTA", props: { layout: "grid", description: "Short answers to the usual questions.", cta: CTA, plainIcons: true } },
|
|
57
|
+
{ id: "left-title-01", label: "Grid · title on the left", props: { layout: "grid", titlePosition: "start", look: "plain", description: "Short answers to the usual questions.", plainIcons: true } },
|
|
58
|
+
{ id: "simple-01", label: "List · every answer shown", props: { layout: "list", description: "The things people ask before they sign up.", plainIcons: true } },
|
|
59
|
+
{ id: "multi", label: "Accordion · several open, title on the left", props: { layout: "accordion", type: "multiple", titlePosition: "start", kicker: "Support", cta: CTA, plainIcons: true } },
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
const STATES = [
|
|
63
|
+
{ value: "filled", label: "Filled" },
|
|
64
|
+
{ value: "loading", label: "Loading" },
|
|
65
|
+
{ value: "empty", label: "Empty" },
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
export default function FaqDemo() {
|
|
69
|
+
const [variant, setVariant] = useState("list-01");
|
|
70
|
+
const [state, setState] = useState("filled");
|
|
71
|
+
const [asks, setAsks] = useState(0);
|
|
72
|
+
const v = VARIANTS.find((x) => x.id === variant) ?? VARIANTS[0];
|
|
73
|
+
const { plainIcons, ...props } = v.props;
|
|
74
|
+
const items = state === "empty" ? [] : plainIcons ? QUESTIONS.map(({ icon, ...q }) => q) : QUESTIONS;
|
|
75
|
+
const cta = props.cta ? { ...props.cta, onClick: () => setAsks((n) => n + 1) } : undefined;
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div className="grid gap-2">
|
|
79
|
+
<div className="flex flex-wrap items-end gap-3 px-4 pt-4 sm:px-6">
|
|
80
|
+
<Field id="faq-variant" label="Variant" className="w-full sm:w-80">
|
|
81
|
+
<Select id="faq-variant" value={variant} onChange={(e) => setVariant(e.target.value)} options={VARIANTS.map((x) => ({ value: x.id, label: x.label }))} />
|
|
82
|
+
</Field>
|
|
83
|
+
<ToggleGroup aria-label="State" items={STATES} value={state} onChange={setState} />
|
|
84
|
+
{asks ? <p className="pb-3 text-sm text-muted-foreground">Note asked for {asks}×</p> : null}
|
|
85
|
+
</div>
|
|
86
|
+
<Faq key={`${variant}-${state}`} {...props} cta={cta} items={items} loading={state === "loading"} />
|
|
87
|
+
</div>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { Accordion, Button, Empty, Icon, Kicker, Skeleton, cx } from "../../ui";
|
|
4
|
+
|
|
5
|
+
// Faq — a marketing section of questions and answers. Three layouts over
|
|
6
|
+
// the same rows: an accordion (one open at a time, or several), a grid
|
|
7
|
+
// of cards (optionally with an icon each), and a plain list with every
|
|
8
|
+
// answer shown. The title sits above or beside them, and an optional
|
|
9
|
+
// "need more help?" call to action closes the section.
|
|
10
|
+
//
|
|
11
|
+
// <Faq title="Questions, answered" items={faqs}
|
|
12
|
+
// cta={{ title: "Still stuck?", label: "Email support", href: "/contact" }} />
|
|
13
|
+
//
|
|
14
|
+
// README.md beside this file lists every prop.
|
|
15
|
+
|
|
16
|
+
const COLUMNS = {
|
|
17
|
+
1: "grid-cols-1",
|
|
18
|
+
2: "grid-cols-1 sm:grid-cols-2",
|
|
19
|
+
3: "grid-cols-1 sm:grid-cols-2 lg:grid-cols-3",
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const keyOf = (item, i) => String(item.id ?? i);
|
|
23
|
+
|
|
24
|
+
function Header({ kicker, title, description, center }) {
|
|
25
|
+
if (!kicker && !title && !description) return null;
|
|
26
|
+
return (
|
|
27
|
+
<div className={cx("grid content-start gap-3", center ? "mx-auto max-w-2xl justify-items-center text-center" : "max-w-xl")}>
|
|
28
|
+
{kicker ? <Kicker>{kicker}</Kicker> : null}
|
|
29
|
+
{title ? <h2 className="text-balance text-3xl font-semibold tracking-tight sm:text-4xl">{title}</h2> : null}
|
|
30
|
+
{description ? <p className="text-pretty text-base text-muted-foreground sm:text-lg">{description}</p> : null}
|
|
31
|
+
</div>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function Answer({ children }) {
|
|
36
|
+
return <div className="text-pretty text-base leading-relaxed text-muted-foreground [&_a]:text-primary [&_a]:underline [&_p+p]:mt-3">{children}</div>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function IconBadge({ name }) {
|
|
40
|
+
if (!name) return null;
|
|
41
|
+
return (
|
|
42
|
+
<span className="grid size-12 shrink-0 place-items-center rounded-lg bg-primary text-primary-foreground">
|
|
43
|
+
<Icon name={name} size={22} />
|
|
44
|
+
</span>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function Cta({ cta, center }) {
|
|
49
|
+
if (!cta) return null;
|
|
50
|
+
return (
|
|
51
|
+
<div className={cx("grid gap-3", center ? "justify-items-center text-center" : "justify-items-start")}>
|
|
52
|
+
{cta.title ? <h3 className="text-2xl font-semibold tracking-tight">{cta.title}</h3> : null}
|
|
53
|
+
{cta.body ? <p className="max-w-xl text-pretty text-base text-muted-foreground sm:text-lg">{cta.body}</p> : null}
|
|
54
|
+
{cta.label ? (
|
|
55
|
+
<Button variant="primary" href={cta.href} onClick={cta.onClick} icon={cta.icon} className="mt-2">
|
|
56
|
+
{cta.label}
|
|
57
|
+
</Button>
|
|
58
|
+
) : null}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function Loading({ layout, columns }) {
|
|
64
|
+
if (layout === "grid")
|
|
65
|
+
return (
|
|
66
|
+
<div aria-busy="true" aria-label="Loading questions" className={cx("grid gap-6", COLUMNS[columns] ?? COLUMNS[3])}>
|
|
67
|
+
{[0, 1, 2].map((i) => (
|
|
68
|
+
<div key={i} className="grid gap-3 rounded-lg border border-border bg-card p-6">
|
|
69
|
+
<Skeleton className="w-3/4" />
|
|
70
|
+
<Skeleton className="w-full" />
|
|
71
|
+
<Skeleton className="w-5/6" />
|
|
72
|
+
</div>
|
|
73
|
+
))}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
return (
|
|
77
|
+
<div aria-busy="true" aria-label="Loading questions" className="grid gap-5">
|
|
78
|
+
{[0, 1, 2, 3].map((i) => (
|
|
79
|
+
<div key={i} className="grid gap-3 border-b border-border pb-5">
|
|
80
|
+
<Skeleton className={i % 2 ? "w-1/2" : "w-2/3"} />
|
|
81
|
+
</div>
|
|
82
|
+
))}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function Grid({ items, columns, look }) {
|
|
88
|
+
return (
|
|
89
|
+
<div className={cx("grid gap-6", COLUMNS[columns] ?? COLUMNS[3])}>
|
|
90
|
+
{items.map((item, i) => (
|
|
91
|
+
<article
|
|
92
|
+
key={keyOf(item, i)}
|
|
93
|
+
id={item.anchor}
|
|
94
|
+
className={cx("grid min-w-0 content-start", look === "plain" ? "" : "rounded-lg border border-border bg-card text-card-foreground")}
|
|
95
|
+
>
|
|
96
|
+
<div className={cx("grid gap-5 border-b border-border pb-4", look === "plain" ? "" : "px-6 pt-6")}>
|
|
97
|
+
<IconBadge name={item.icon} />
|
|
98
|
+
<h3 className="text-pretty text-lg font-semibold leading-snug">{item.question}</h3>
|
|
99
|
+
</div>
|
|
100
|
+
<div className={cx("pt-4", look === "plain" ? "" : "px-6 pb-6")}>
|
|
101
|
+
<Answer>{item.answer}</Answer>
|
|
102
|
+
</div>
|
|
103
|
+
</article>
|
|
104
|
+
))}
|
|
105
|
+
</div>
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function List({ items }) {
|
|
110
|
+
return (
|
|
111
|
+
<dl className="grid gap-2">
|
|
112
|
+
{items.map((item, i) => (
|
|
113
|
+
<div key={keyOf(item, i)} id={item.anchor} className="grid min-w-0">
|
|
114
|
+
<dt className="flex items-center gap-3 border-b border-border py-5 text-lg font-semibold">
|
|
115
|
+
{item.icon ? <Icon name={item.icon} className="shrink-0 text-muted-foreground" /> : null}
|
|
116
|
+
<span className="min-w-0 text-pretty">{item.question}</span>
|
|
117
|
+
</dt>
|
|
118
|
+
<dd className="py-5">
|
|
119
|
+
<Answer>{item.answer}</Answer>
|
|
120
|
+
</dd>
|
|
121
|
+
</div>
|
|
122
|
+
))}
|
|
123
|
+
</dl>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function Folding({ items, type, defaultOpen, value, onOpenChange, label }) {
|
|
128
|
+
const multiple = type === "multiple";
|
|
129
|
+
return (
|
|
130
|
+
<Accordion
|
|
131
|
+
aria-label={label}
|
|
132
|
+
type={multiple ? "multiple" : "single"}
|
|
133
|
+
defaultValue={defaultOpen ?? (multiple ? [] : null)}
|
|
134
|
+
value={value}
|
|
135
|
+
onChange={onOpenChange}
|
|
136
|
+
items={items.map((item, i) => ({
|
|
137
|
+
key: keyOf(item, i),
|
|
138
|
+
title: (
|
|
139
|
+
<span className="flex items-center gap-3 py-2 text-base font-semibold sm:text-lg">
|
|
140
|
+
{item.icon ? <Icon name={item.icon} className="shrink-0 text-muted-foreground" /> : null}
|
|
141
|
+
<span className="min-w-0 text-pretty">{item.question}</span>
|
|
142
|
+
</span>
|
|
143
|
+
),
|
|
144
|
+
content: <Answer>{item.answer}</Answer>,
|
|
145
|
+
}))}
|
|
146
|
+
/>
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
export function Faq({
|
|
151
|
+
items,
|
|
152
|
+
layout = "accordion",
|
|
153
|
+
kicker,
|
|
154
|
+
title = "Frequently asked questions",
|
|
155
|
+
description,
|
|
156
|
+
titlePosition = "top",
|
|
157
|
+
columns,
|
|
158
|
+
look = "card",
|
|
159
|
+
type = "single",
|
|
160
|
+
defaultOpen,
|
|
161
|
+
open,
|
|
162
|
+
onOpenChange,
|
|
163
|
+
cta,
|
|
164
|
+
loading = false,
|
|
165
|
+
empty,
|
|
166
|
+
label,
|
|
167
|
+
className,
|
|
168
|
+
}) {
|
|
169
|
+
const list = (items ?? []).filter((it) => it && it.question);
|
|
170
|
+
const side = titlePosition === "start";
|
|
171
|
+
const cols = columns ?? (side ? 2 : 3);
|
|
172
|
+
const narrow = !side && layout !== "grid";
|
|
173
|
+
|
|
174
|
+
let body;
|
|
175
|
+
if (loading) body = <Loading layout={layout} columns={cols} />;
|
|
176
|
+
else if (list.length === 0)
|
|
177
|
+
body = (
|
|
178
|
+
<Empty
|
|
179
|
+
icon="message-circle"
|
|
180
|
+
title={empty?.title ?? "No questions yet"}
|
|
181
|
+
body={empty?.body ?? "Answers to what people ask most will show here."}
|
|
182
|
+
/>
|
|
183
|
+
);
|
|
184
|
+
else if (layout === "grid") body = <Grid items={list} columns={cols} look={look} />;
|
|
185
|
+
else if (layout === "list") body = <List items={list} />;
|
|
186
|
+
else
|
|
187
|
+
body = (
|
|
188
|
+
<Folding
|
|
189
|
+
items={list}
|
|
190
|
+
type={type}
|
|
191
|
+
defaultOpen={defaultOpen}
|
|
192
|
+
value={open}
|
|
193
|
+
onOpenChange={onOpenChange}
|
|
194
|
+
label={typeof title === "string" ? title : "Questions"}
|
|
195
|
+
/>
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const header = <Header kicker={kicker} title={title} description={description} center={!side} />;
|
|
199
|
+
return (
|
|
200
|
+
<section aria-label={label ?? (typeof title === "string" ? title : "Frequently asked questions")} className={cx("px-4 py-16 sm:px-6 sm:py-20", className)}>
|
|
201
|
+
<div className={cx("mx-auto grid w-full gap-12 sm:gap-16", narrow ? "max-w-3xl" : "max-w-6xl")}>
|
|
202
|
+
{side ? (
|
|
203
|
+
<div className="grid gap-10 lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)] lg:gap-12">
|
|
204
|
+
<div className="grid content-start gap-8">
|
|
205
|
+
{header}
|
|
206
|
+
<Cta cta={cta} />
|
|
207
|
+
</div>
|
|
208
|
+
<div className="min-w-0">{body}</div>
|
|
209
|
+
</div>
|
|
210
|
+
) : (
|
|
211
|
+
<>
|
|
212
|
+
{header}
|
|
213
|
+
<div className="min-w-0">{body}</div>
|
|
214
|
+
<Cta cta={cta} center />
|
|
215
|
+
</>
|
|
216
|
+
)}
|
|
217
|
+
</div>
|
|
218
|
+
</section>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# FileUpload
|
|
2
|
+
|
|
3
|
+
Files chosen or dropped, each one uploaded through your `onUpload` as
|
|
4
|
+
soon as it lands. Every file shows its bar while it runs (with Cancel),
|
|
5
|
+
the reason and a Retry when it fails, and a Remove once it is up. Files
|
|
6
|
+
the picker refuses (wrong type, too big, too many) are named in a notice.
|
|
7
|
+
The upload itself is yours: the block never fetches.
|
|
8
|
+
|
|
9
|
+
```jsx
|
|
10
|
+
import { FileUpload } from "../components/blocks/file-upload"; // a Makersclaw app ("./blocks/file-upload" from components/)
|
|
11
|
+
import { FileUpload } from "@/components/makersclaw/blocks/file-upload"; // a shadcn project
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
A client component. The picker is the kit's `FileDrop` (or `FileInput`
|
|
15
|
+
with `picker="button"`), so the browser's own file dialog opens and the
|
|
16
|
+
keyboard works: Tab to the zone, Enter or Space.
|
|
17
|
+
|
|
18
|
+
## The data
|
|
19
|
+
|
|
20
|
+
What the block tells you about a file (in `onChange` and `onRemove`):
|
|
21
|
+
`{ id, name, size, type, url?, ...whatever your onUpload returned }`.
|
|
22
|
+
Never the `File` and never the block's own state.
|
|
23
|
+
|
|
24
|
+
## Props
|
|
25
|
+
|
|
26
|
+
- `onUpload(file, { onProgress, signal })` — upload one `File`. Call
|
|
27
|
+
`onProgress(percent)` (0–100) as it goes; without it the bar sweeps.
|
|
28
|
+
Watch `signal` (an AbortSignal): it aborts when the person cancels or
|
|
29
|
+
replaces the file, and pass it to `fetch`. Return `{ ok: true, url?,
|
|
30
|
+
...anything }` or `{ ok: false, error }`; a throw counts as failed and
|
|
31
|
+
its message is shown. Without `onUpload` the block is a picker only:
|
|
32
|
+
files count as added at once.
|
|
33
|
+
- `variant` — how the files show, default `"list"`:
|
|
34
|
+
- `list` — the drop zone, then a card per file (name, size, state).
|
|
35
|
+
- `single` — one file at a time in a filled panel; a new pick replaces
|
|
36
|
+
it (and aborts its upload). An import, a contract.
|
|
37
|
+
- `sections` — a one-row drop zone, then the files grouped under
|
|
38
|
+
"Ready", "Uploading" (failed ones stay here with Retry) and
|
|
39
|
+
"Finished".
|
|
40
|
+
- `preview` — cards with the picture itself for images. `columns={2}`
|
|
41
|
+
lays them two across from 640px.
|
|
42
|
+
- `avatar` — one picture of a person or a company: the current one (or
|
|
43
|
+
the initials), Upload picture, Remove. Takes `value` (the current
|
|
44
|
+
URL) and `name`; `onChange(url | null)`.
|
|
45
|
+
- `onRemove(file)` — delete an uploaded file from storage; return
|
|
46
|
+
`{ ok }` like `onUpload`. The row shows a spinner, and on failure stays
|
|
47
|
+
with the reason. Without it Remove only drops the row. Removing a file
|
|
48
|
+
that is still queued, uploading or failed never calls it.
|
|
49
|
+
- `onChange(files)` — the uploaded files, every time that list changes.
|
|
50
|
+
Save their ids or URLs with the record here.
|
|
51
|
+
- `defaultFiles` — what shows at first: files already uploaded
|
|
52
|
+
(`[{ id, name, size, type, url? }]`). An entry that is `{ id?, file }`
|
|
53
|
+
(a `File` you got from a paste or a drop elsewhere on the page) starts
|
|
54
|
+
uploading on mount.
|
|
55
|
+
- `accept` — as the input's: `"image/*"`, `".csv,.xlsx"`. The picker
|
|
56
|
+
greys out the rest; a drop of the rest is refused and named.
|
|
57
|
+
- `multiple` — default `true` (`false` for `single` and `avatar`).
|
|
58
|
+
- `maxSize` — bytes, per file. Bigger files are refused and named.
|
|
59
|
+
- `maxFiles` — how many in all; the zone says so when it is full.
|
|
60
|
+
- `autoUpload` — default `true`. `false` holds new files as "Ready to
|
|
61
|
+
upload" with Clear and an "Upload N files" button, for a form that
|
|
62
|
+
sends its attachments on submit.
|
|
63
|
+
- `picker` — `"drop"` (default: the drop zone) · `"button"` (a
|
|
64
|
+
`FileInput` button and the hint beside it, for a tight form row).
|
|
65
|
+
- `label` — the zone's line (default "Drop files here or browse"); in
|
|
66
|
+
`avatar`, the name beside the picture ("Profile picture").
|
|
67
|
+
- `hint` — the muted line: default built from `accept` and `maxSize`
|
|
68
|
+
("Images · up to 4 MB").
|
|
69
|
+
- `buttonLabel` — `picker="button"`'s text, default "Add files".
|
|
70
|
+
- `uploadLabel` — the queue's submit text, or `avatar`'s button text.
|
|
71
|
+
- `columns` — `1` (default) or `2`, for `list` and `preview`.
|
|
72
|
+
- `disabled` — no picking, no retry, no remove.
|
|
73
|
+
- `aria-label` — the drop zone's name when `label` is not enough.
|
|
74
|
+
- `className` — layout around it. Never colour.
|
|
75
|
+
|
|
76
|
+
`formatSize(bytes)` ("3.1 MB") and `fileIcon(type, name)` (the kit icon
|
|
77
|
+
for a file) are exported too, for your own file rows.
|
|
78
|
+
|
|
79
|
+
## Example
|
|
80
|
+
|
|
81
|
+
```jsx
|
|
82
|
+
// components/DealFiles.jsx — client
|
|
83
|
+
"use client";
|
|
84
|
+
import { FileUpload } from "./blocks/file-upload";
|
|
85
|
+
|
|
86
|
+
// Your storage route answers a PUT with { id, url }. XMLHttpRequest, not
|
|
87
|
+
// fetch, because only it reports upload progress.
|
|
88
|
+
function put(file, { onProgress, signal }) {
|
|
89
|
+
return new Promise((resolve) => {
|
|
90
|
+
const xhr = new XMLHttpRequest();
|
|
91
|
+
xhr.open("PUT", `/api/files?name=${encodeURIComponent(file.name)}`);
|
|
92
|
+
xhr.upload.onprogress = (e) => e.lengthComputable && onProgress((e.loaded / e.total) * 100);
|
|
93
|
+
xhr.onload = () =>
|
|
94
|
+
xhr.status < 300 ? resolve({ ok: true, ...JSON.parse(xhr.responseText) }) : resolve({ ok: false, error: `Upload failed (${xhr.status}).` });
|
|
95
|
+
xhr.onerror = () => resolve({ ok: false, error: "The connection dropped. Try again." });
|
|
96
|
+
signal.addEventListener("abort", () => xhr.abort());
|
|
97
|
+
xhr.send(file);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export default function DealFiles({ deal, onSaveFiles }) {
|
|
102
|
+
return (
|
|
103
|
+
<FileUpload
|
|
104
|
+
defaultFiles={deal.files}
|
|
105
|
+
onUpload={put}
|
|
106
|
+
onRemove={(f) => fetch(`/api/files/${f.id}`, { method: "DELETE" }).then((r) => ({ ok: r.ok }))}
|
|
107
|
+
onChange={(files) => onSaveFiles(files.map((f) => ({ id: f.id, name: f.name, size: f.size, url: f.url })))}
|
|
108
|
+
maxSize={50 * 1024 * 1024}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
One spreadsheet to import, and a profile picture:
|
|
115
|
+
|
|
116
|
+
```jsx
|
|
117
|
+
<FileUpload variant="single" accept=".csv,.xlsx" maxSize={10 * 1024 * 1024} onUpload={put} onChange={([f]) => setImport(f ?? null)} />
|
|
118
|
+
|
|
119
|
+
<FileUpload variant="avatar" name={me.name} value={me.avatarUrl} onUpload={put} onChange={(url) => saveProfile({ avatar_url: url })} />
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## What it will not do
|
|
123
|
+
|
|
124
|
+
- Store anything: `onUpload` is yours (a storage bucket, a signed URL).
|
|
125
|
+
- Crop or resize a picture, or read a spreadsheet's rows: do that in
|
|
126
|
+
`onUpload` or after `onChange`.
|
|
127
|
+
- Reorder files, or keep a list you control from outside: `defaultFiles`
|
|
128
|
+
is where it starts; remount it (a `key`) to start again.
|
|
129
|
+
- Show a chat message's attachments: that is the chat block's composer.
|
|
130
|
+
|
|
131
|
+
A file picker it cannot be is your own component in `components/`,
|
|
132
|
+
built on the kit's `FileInput` or `FileDrop` — not an edit to this
|
|
133
|
+
block. Its files are fixed: `npm run check` refuses a change, and
|
|
134
|
+
`makersclaw add file-upload` restores them.
|
|
135
|
+
|
|
136
|
+
## Credits
|
|
137
|
+
|
|
138
|
+
Ported from blocks.so (https://blocks.so, file-upload 01, 03–06, MIT) and Creative Tim UI (https://github.com/creativetimofficial/ui, modals-05 and account-avatar-upload-01, MIT), rewritten on the Makersclaw kit.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "file-upload",
|
|
3
|
+
"title": "FileUpload",
|
|
4
|
+
"description": "Files chosen or dropped, each uploaded through your async onUpload(file, { onProgress, signal }) with a progress bar, cancel while it runs, a retry when it fails and a remove once done. Variants: a list of file cards, one file at a time, Uploading/Finished sections, image thumbnails (one or two columns), and a profile picture.",
|
|
5
|
+
"useWhen": "A form or page takes files the app stores: attachments on a record, a CSV to import, product photos, a logo, a profile picture. Not for showing files already stored with no way to add (a table or list), and not for a chat message's attachments (the chat block's composer has those).",
|
|
6
|
+
"tags": ["file", "upload", "dropzone", "drag", "drop", "attachment", "progress", "image", "photo", "avatar", "profile picture", "csv", "import", "retry"],
|
|
7
|
+
"exports": ["FileUpload", "formatSize", "fileIcon"],
|
|
8
|
+
"files": ["index.js", "file-upload.jsx"],
|
|
9
|
+
"requires": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// FileUpload with a pretend uploader in memory — the gallery's and the
|
|
4
|
+
// smoke test's surface. Not installed into apps: `makersclaw add
|
|
5
|
+
// file-upload` lays index.js and file-upload.jsx only.
|
|
6
|
+
import { useEffect, useRef, useState } from "react";
|
|
7
|
+
import { Button, Card, Field, Input, Notice, Select, Switch } from "../components/ui";
|
|
8
|
+
import { FileUpload } from "../components/blocks/file-upload";
|
|
9
|
+
|
|
10
|
+
const MB = 1024 * 1024;
|
|
11
|
+
|
|
12
|
+
const VARIANTS = [
|
|
13
|
+
{ value: "list", label: "List — file cards" },
|
|
14
|
+
{ value: "single", label: "Single — one file" },
|
|
15
|
+
{ value: "sections", label: "Sections — uploading / finished" },
|
|
16
|
+
{ value: "preview", label: "Preview — thumbnails" },
|
|
17
|
+
{ value: "grid", label: "Preview — two columns" },
|
|
18
|
+
{ value: "queue", label: "Button picker — upload on submit" },
|
|
19
|
+
{ value: "avatar", label: "Avatar — profile picture" },
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
// A grey placeholder picture, so the thumbnails have something to show: a
|
|
23
|
+
// real PNG (a landscape, or a round portrait), not SVG markup, which an
|
|
24
|
+
// app's check refuses in its code.
|
|
25
|
+
const LANDSCAPE_PNG = "iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAAAAABQyaazAAABnElEQVR42u3Y223EMAxE0em/j1uJ+0gteSKbYB3bkofryYL8H/EIkABKegkvNbCBDWxgAxvYwAY2sIENbGADs4DLrRKBy++KAy7LmFBX+/aEuty3I9T1vm2hAnybwgYe8W0JG9jABjawgQ38P8AeFp5/HsyfqPPfJPmvuvx3cf7PQn8e/SjCgUA0EB4h1DlfvVAnfeVCnfVVCw1AEoHwKKEMvlKhHL5KoSy+QqE8vjqhTL4yoQ9ICBAeK5TPNyncicnomxLuxeT0TQixAsEt3E/J6xsUcgGQqfUsQPAKMQPBKzyYkd93TIgZCF7h4YgqfLvCgYRKfDvCkUQVkPHVTgDBKcQOBKdwMKAy33rD4R2pzrfWcXxHKvTdd8QPBKNw5lAUAxlbaxwINuHkqVWx77snFUCwCacvvsp9H03nL77qfW9NqQByUT0NEDKEivMdA0KKUHm+A0DIESrQlw9kGwhJQkX6toCQJVSm708gpAkV6ssHsgYkHAiBQsX67oAQKVSu71OoYF8+kBuQcCDEChXt+wISXO9AwoFk1ytk7oEIFbJZugAAAABJRU5ErkJggg== iVBORw0KGgoAAAANSUhEUgAAAKAAAAB4CAAAAABQyaazAAAA5ElEQVR42u3awRGDMBBD0e2/HtXjWnJnEsLYoNWYrwI87wgr1QhPAQQIECBAgAABAgQIECBAgAAB9gN1TBRQ35MC1O8kAHWebqD+pxOoa2kDSs8Ly+FbEZaDt0Isk29aWC7frHBXoOQSls83Jyyjb0q4JVAyCncESk7hhkDJKtwPKHmFAN1AySwECBAgQIAAAQIECJBPfoD8uHM8ygJywOSI3g+M70nymybKxHVgfF+c37iP+M3CyF995O9mRvzy6AJx9fkXrN9OiHc8/ZIF5oMBCBAgQIAAAQIECBAgQIAAAQbnA5oGvOFZY0eXAAAAAElFTkSuQmCC";
|
|
26
|
+
const PORTRAIT_PNG = "";
|
|
27
|
+
function picture(name, shape) {
|
|
28
|
+
const bytes = Uint8Array.from(atob(shape === "circle" ? PORTRAIT_PNG : LANDSCAPE_PNG), (c) => c.charCodeAt(0));
|
|
29
|
+
return new File([bytes], name, { type: "image/png" });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function blob(name, bytes, type) {
|
|
33
|
+
return new File([new Uint8Array(bytes)], name, { type });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Files "handed over" on mount so every state shows without a drop: one
|
|
37
|
+
// of them fails once, so Retry has something to do.
|
|
38
|
+
const HANDED = {
|
|
39
|
+
list: () => [
|
|
40
|
+
blob("q3-pipeline.csv", 184_000, "text/csv"),
|
|
41
|
+
blob("vendor-contracts.zip", 6.2 * MB, "application/zip"),
|
|
42
|
+
blob("kickoff-notes.docx", 42_000, "application/vnd.openxmlformats-officedocument.wordprocessingml.document"),
|
|
43
|
+
],
|
|
44
|
+
single: () => [blob("customers-import.csv", 3.1 * MB, "text/csv")],
|
|
45
|
+
sections: () => [picture("storefront-hero.png"), picture("team-offsite.png", "circle"), picture("vendor-contracts.png")],
|
|
46
|
+
preview: () => [picture("storefront-hero.png"), picture("vendor-contracts.png", "circle")],
|
|
47
|
+
grid: () => [picture("shelf-oak-front.png"), picture("shelf-oak-side.png", "circle"), picture("vendor-contracts.png")],
|
|
48
|
+
queue: () => [],
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const DONE = {
|
|
52
|
+
list: [{ id: "d1", name: "brand-guidelines.pdf", size: 2.4 * MB, type: "application/pdf" }],
|
|
53
|
+
single: [],
|
|
54
|
+
sections: [{ id: "d2", name: "logo-v3-final.png", size: 88_000, type: "image/png" }],
|
|
55
|
+
preview: [],
|
|
56
|
+
grid: [],
|
|
57
|
+
queue: [],
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default function FileUploadDemo() {
|
|
61
|
+
const [variant, setVariant] = useState("list");
|
|
62
|
+
const [seed, setSeed] = useState(0);
|
|
63
|
+
const [failNext, setFailNext] = useState(false);
|
|
64
|
+
const [saved, setSaved] = useState(null);
|
|
65
|
+
const failOnce = useRef(new Set());
|
|
66
|
+
const failRef = useRef(failNext);
|
|
67
|
+
failRef.current = failNext;
|
|
68
|
+
|
|
69
|
+
// Hand the samples over once the page is in the browser (File is not
|
|
70
|
+
// on the server).
|
|
71
|
+
function rerun() {
|
|
72
|
+
failOnce.current = new Set(["vendor-contracts.zip", "vendor-contracts.png"]);
|
|
73
|
+
setSeed((s) => s + 1);
|
|
74
|
+
}
|
|
75
|
+
useEffect(rerun, [variant]);
|
|
76
|
+
|
|
77
|
+
function upload(file, { onProgress, signal }) {
|
|
78
|
+
const slow = file.size > MB ? 7 : 16;
|
|
79
|
+
return new Promise((resolve) => {
|
|
80
|
+
let p = 0;
|
|
81
|
+
const t = setInterval(() => {
|
|
82
|
+
if (signal.aborted) {
|
|
83
|
+
clearInterval(t);
|
|
84
|
+
resolve({ ok: false, error: "Cancelled." });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
p += slow + Math.random() * 8;
|
|
88
|
+
// Decided here, not at the start, so an upload that was aborted
|
|
89
|
+
// (a cancel, React's dev double mount) does not use the failure up.
|
|
90
|
+
if (p > 58 && (failOnce.current.has(file.name) || failRef.current)) {
|
|
91
|
+
clearInterval(t);
|
|
92
|
+
if (!failOnce.current.delete(file.name)) {
|
|
93
|
+
failRef.current = false;
|
|
94
|
+
setFailNext(false);
|
|
95
|
+
}
|
|
96
|
+
resolve({ ok: false, error: "The connection dropped at 60%. Try again." });
|
|
97
|
+
} else if (p >= 100) {
|
|
98
|
+
clearInterval(t);
|
|
99
|
+
onProgress(100);
|
|
100
|
+
resolve({ ok: true, id: `f_${Math.random().toString(36).slice(2, 8)}` });
|
|
101
|
+
} else onProgress(p);
|
|
102
|
+
}, 260);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function remove(file) {
|
|
107
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
108
|
+
return { ok: true, file };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const handed = seed > 0 ? (HANDED[variant]?.() ?? []).map((file, i) => ({ id: `s${seed}-${i}`, file })) : [];
|
|
112
|
+
const defaults = [...(DONE[variant] ?? []), ...handed];
|
|
113
|
+
const common = {
|
|
114
|
+
onUpload: upload,
|
|
115
|
+
onRemove: remove,
|
|
116
|
+
onChange: (files) => setSaved(files.length),
|
|
117
|
+
defaultFiles: defaults,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
let body;
|
|
121
|
+
if (variant === "list") {
|
|
122
|
+
body = (
|
|
123
|
+
<Card title="Share files with Northwind" description="Everything here goes to the client's folder." titleSize="lg">
|
|
124
|
+
<div className="grid gap-4">
|
|
125
|
+
<Field id="fu-folder" label="Folder name">
|
|
126
|
+
<Input id="fu-folder" defaultValue="Q3 planning" />
|
|
127
|
+
</Field>
|
|
128
|
+
<FileUpload {...common} maxSize={50 * MB} hint="Any file type · up to 50 MB each" />
|
|
129
|
+
</div>
|
|
130
|
+
</Card>
|
|
131
|
+
);
|
|
132
|
+
} else if (variant === "single") {
|
|
133
|
+
body = (
|
|
134
|
+
<Card title="Import customers" description="One spreadsheet, a row per customer." titleSize="lg">
|
|
135
|
+
<FileUpload {...common} variant="single" accept=".csv,.xlsx,.xls" maxSize={10 * MB} />
|
|
136
|
+
</Card>
|
|
137
|
+
);
|
|
138
|
+
} else if (variant === "sections") {
|
|
139
|
+
body = (
|
|
140
|
+
<div className="mx-auto w-full max-w-sm">
|
|
141
|
+
<FileUpload {...common} variant="sections" accept="image/png,image/jpeg,image/gif,image/svg+xml" maxSize={10 * MB} label="Drop images here or browse" />
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
} else if (variant === "preview" || variant === "grid") {
|
|
145
|
+
body = (
|
|
146
|
+
<Card
|
|
147
|
+
title={variant === "grid" ? "Product photos" : "Cover images"}
|
|
148
|
+
description={variant === "grid" ? "The first photo is the one buyers see." : "Shown on the project's page and in search."}
|
|
149
|
+
titleSize="lg"
|
|
150
|
+
>
|
|
151
|
+
<FileUpload {...common} variant="preview" columns={variant === "grid" ? 2 : 1} accept="image/*" maxSize={4 * MB} maxFiles={6} />
|
|
152
|
+
</Card>
|
|
153
|
+
);
|
|
154
|
+
} else if (variant === "queue") {
|
|
155
|
+
body = (
|
|
156
|
+
<Card title="Expense report" description="Attach the receipts, then send them in one go." titleSize="lg">
|
|
157
|
+
<div className="grid gap-4">
|
|
158
|
+
<Field id="fu-trip" label="Trip">
|
|
159
|
+
<Input id="fu-trip" defaultValue="Lisbon customer visits" />
|
|
160
|
+
</Field>
|
|
161
|
+
<FileUpload {...common} picker="button" buttonLabel="Attach receipts" autoUpload={false} accept="image/*,.pdf" maxSize={8 * MB} />
|
|
162
|
+
</div>
|
|
163
|
+
</Card>
|
|
164
|
+
);
|
|
165
|
+
} else {
|
|
166
|
+
body = (
|
|
167
|
+
<Card title="Your profile" titleSize="lg">
|
|
168
|
+
<div className="grid gap-4">
|
|
169
|
+
<FileUpload
|
|
170
|
+
variant="avatar"
|
|
171
|
+
name="Maya Okafor"
|
|
172
|
+
onUpload={upload}
|
|
173
|
+
onRemove={() => remove(null)}
|
|
174
|
+
maxSize={2 * MB}
|
|
175
|
+
hint="Square PNG or JPG, at least 400 × 400, up to 2 MB"
|
|
176
|
+
/>
|
|
177
|
+
<Notice title="A good picture">A clear, recent photo of your face, on a plain background, helps teammates find you.</Notice>
|
|
178
|
+
</div>
|
|
179
|
+
</Card>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return (
|
|
184
|
+
<div className="grid gap-4">
|
|
185
|
+
<div className="flex flex-wrap items-end gap-x-4 gap-y-2">
|
|
186
|
+
<Field id="fu-variant" label="Variant" className="w-full sm:w-72">
|
|
187
|
+
<Select id="fu-variant" value={variant} onChange={(e) => setVariant(e.target.value)} options={VARIANTS} />
|
|
188
|
+
</Field>
|
|
189
|
+
<Switch id="fu-fail" checked={failNext} onChange={(e) => setFailNext(e.target.checked)} label="Fail the next upload" />
|
|
190
|
+
<Button variant="quiet" size="sm" icon="refresh" onClick={rerun}>
|
|
191
|
+
Run the samples again
|
|
192
|
+
</Button>
|
|
193
|
+
</div>
|
|
194
|
+
<div key={`${variant}-${seed}`} className="mx-auto w-full max-w-xl">
|
|
195
|
+
{body}
|
|
196
|
+
</div>
|
|
197
|
+
{saved !== null && variant !== "avatar" ? (
|
|
198
|
+
<p className="text-center text-xs text-muted-foreground" aria-live="polite">
|
|
199
|
+
onChange: {saved} {saved === 1 ? "file" : "files"} uploaded
|
|
200
|
+
</p>
|
|
201
|
+
) : null}
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|