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,302 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useState } from "react";
|
|
4
|
+
import { Badge, Button, Confirm, Empty, Icon, Notice, Skeleton, Tooltip, cx, todayString } from "../../ui";
|
|
5
|
+
|
|
6
|
+
// The payment methods a customer has saved, as cards: the brand, the last
|
|
7
|
+
// four digits, the expiry and which one is the default. Make default and
|
|
8
|
+
// Remove (behind a Confirm) go out through callbacks that return { ok }.
|
|
9
|
+
// Adding one is the app's: `onAdd` opens the payment provider (Paddle),
|
|
10
|
+
// which collects the card. The block never holds a card number.
|
|
11
|
+
|
|
12
|
+
// Server and browser may differ in locale, time zone and even the day:
|
|
13
|
+
// the first render uses fixed ones (en-US, UTC) so it matches the server's
|
|
14
|
+
// HTML, then the reader's own take over.
|
|
15
|
+
function useHydrated() {
|
|
16
|
+
const [hydrated, setHydrated] = useState(false);
|
|
17
|
+
useEffect(() => setHydrated(true), []);
|
|
18
|
+
return hydrated;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const BRANDS = {
|
|
22
|
+
visa: { name: "Visa", icon: "credit-card" },
|
|
23
|
+
mastercard: { name: "Mastercard", icon: "credit-card" },
|
|
24
|
+
amex: { name: "American Express", icon: "credit-card" },
|
|
25
|
+
discover: { name: "Discover", icon: "credit-card" },
|
|
26
|
+
jcb: { name: "JCB", icon: "credit-card" },
|
|
27
|
+
unionpay: { name: "UnionPay", icon: "credit-card" },
|
|
28
|
+
maestro: { name: "Maestro", icon: "credit-card" },
|
|
29
|
+
card: { name: "Card", icon: "credit-card" },
|
|
30
|
+
paypal: { name: "PayPal", icon: "credit-card" },
|
|
31
|
+
apple_pay: { name: "Apple Pay", icon: "apple" },
|
|
32
|
+
google_pay: { name: "Google Pay", icon: "google" },
|
|
33
|
+
bank: { name: "Bank account", icon: "building" },
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function brandOf(method) {
|
|
37
|
+
const key = String(method.brand ?? "card").toLowerCase().replace(/[\s-]+/g, "_");
|
|
38
|
+
const known = BRANDS[key];
|
|
39
|
+
return known ?? { name: method.brand || "Card", icon: "credit-card" };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The name a method is called by in buttons and dialogs: "Visa ending 4242".
|
|
43
|
+
function nameOf(method) {
|
|
44
|
+
if (method.label) return method.label;
|
|
45
|
+
const brand = brandOf(method).name;
|
|
46
|
+
return method.last4 ? `${brand} ending ${method.last4}` : brand;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// "expired" · "soon" (this month or next) · "ok" · null (no expiry).
|
|
50
|
+
function expiryState(method, today) {
|
|
51
|
+
const m = Number(method.expMonth);
|
|
52
|
+
let y = Number(method.expYear);
|
|
53
|
+
if (!m || !y) return null;
|
|
54
|
+
if (y < 100) y += 2000;
|
|
55
|
+
const [ty, tm] = today.split("-").map(Number);
|
|
56
|
+
const months = (y - ty) * 12 + (m - tm);
|
|
57
|
+
if (months < 0) return "expired";
|
|
58
|
+
if (months <= 1) return "soon";
|
|
59
|
+
return "ok";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const expiryText = (method) => {
|
|
63
|
+
const m = String(method.expMonth).padStart(2, "0");
|
|
64
|
+
const y = String(method.expYear).slice(-2);
|
|
65
|
+
return `${m}/${y}`;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
async function attempt(fn) {
|
|
69
|
+
try {
|
|
70
|
+
const res = await fn();
|
|
71
|
+
return res && res.ok === false ? res.error || "That did not save." : null;
|
|
72
|
+
} catch (e) {
|
|
73
|
+
return e?.message || "That did not save.";
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function MethodCard({ method, today, busy, error, onMakeDefault, onAskRemove, removable, lockedReason }) {
|
|
78
|
+
const brand = brandOf(method);
|
|
79
|
+
const expiry = expiryState(method, today);
|
|
80
|
+
const name = nameOf(method);
|
|
81
|
+
return (
|
|
82
|
+
<li
|
|
83
|
+
className={cx(
|
|
84
|
+
"relative grid gap-4 rounded-lg border bg-card p-4 text-card-foreground",
|
|
85
|
+
method.isDefault ? "border-primary/60" : "border-border",
|
|
86
|
+
)}
|
|
87
|
+
>
|
|
88
|
+
<div className="flex items-start gap-3">
|
|
89
|
+
<span className="grid h-11 w-14 shrink-0 place-items-center rounded-md border border-border bg-muted text-foreground">
|
|
90
|
+
<Icon name={brand.icon} size={22} />
|
|
91
|
+
</span>
|
|
92
|
+
<div className="grid min-w-0 flex-1 gap-0.5">
|
|
93
|
+
<p className="truncate font-medium">
|
|
94
|
+
{method.last4 ? (
|
|
95
|
+
<>
|
|
96
|
+
<span aria-hidden="true" className="tracking-widest text-muted-foreground">
|
|
97
|
+
••••{" "}
|
|
98
|
+
</span>
|
|
99
|
+
<span className="sr-only">{brand.name} ending </span>
|
|
100
|
+
<span className="tabular-nums">{method.last4}</span>
|
|
101
|
+
</>
|
|
102
|
+
) : (
|
|
103
|
+
method.label || brand.name
|
|
104
|
+
)}
|
|
105
|
+
</p>
|
|
106
|
+
<p className="flex flex-wrap items-center gap-x-2 gap-y-1 text-sm text-muted-foreground">
|
|
107
|
+
<span>{method.last4 ? brand.name : method.holder || method.email || ""}</span>
|
|
108
|
+
{method.expMonth && method.expYear ? (
|
|
109
|
+
<span className={cx(expiry === "expired" && "text-destructive")}>
|
|
110
|
+
{expiry === "expired" ? "Expired" : "Expires"} {expiryText(method)}
|
|
111
|
+
</span>
|
|
112
|
+
) : null}
|
|
113
|
+
</p>
|
|
114
|
+
{method.last4 && method.holder ? <p className="truncate text-xs text-muted-foreground">{method.holder}</p> : null}
|
|
115
|
+
</div>
|
|
116
|
+
<div className="flex shrink-0 flex-col items-end gap-1">
|
|
117
|
+
{method.isDefault ? (
|
|
118
|
+
<Badge tone="primary" variant="soft">
|
|
119
|
+
Default
|
|
120
|
+
</Badge>
|
|
121
|
+
) : null}
|
|
122
|
+
{expiry === "expired" ? (
|
|
123
|
+
<Badge tone="danger" variant="soft">
|
|
124
|
+
Expired
|
|
125
|
+
</Badge>
|
|
126
|
+
) : expiry === "soon" ? (
|
|
127
|
+
<Badge tone="warning" variant="soft">
|
|
128
|
+
Expires soon
|
|
129
|
+
</Badge>
|
|
130
|
+
) : null}
|
|
131
|
+
</div>
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
{onMakeDefault || onAskRemove ? (
|
|
135
|
+
<div className="flex flex-wrap items-center gap-2 border-t border-border pt-3">
|
|
136
|
+
{onMakeDefault && !method.isDefault ? (
|
|
137
|
+
<Button
|
|
138
|
+
size="sm"
|
|
139
|
+
variant="quiet"
|
|
140
|
+
icon="check"
|
|
141
|
+
onClick={() => onMakeDefault(method)}
|
|
142
|
+
loading={busy === "default"}
|
|
143
|
+
disabled={Boolean(busy) || expiry === "expired"}
|
|
144
|
+
aria-label={`Make ${name} the default`}
|
|
145
|
+
>
|
|
146
|
+
Make default
|
|
147
|
+
</Button>
|
|
148
|
+
) : null}
|
|
149
|
+
{onAskRemove ? (
|
|
150
|
+
<Tooltip label={removable ? "" : lockedReason} className="ml-auto">
|
|
151
|
+
<Button
|
|
152
|
+
size="sm"
|
|
153
|
+
variant="danger"
|
|
154
|
+
icon="trash"
|
|
155
|
+
onClick={() => onAskRemove(method)}
|
|
156
|
+
disabled={Boolean(busy) || !removable}
|
|
157
|
+
aria-label={`Remove ${name}`}
|
|
158
|
+
className="ml-auto"
|
|
159
|
+
>
|
|
160
|
+
Remove
|
|
161
|
+
</Button>
|
|
162
|
+
</Tooltip>
|
|
163
|
+
) : null}
|
|
164
|
+
</div>
|
|
165
|
+
) : null}
|
|
166
|
+
|
|
167
|
+
{error ? (
|
|
168
|
+
<p role="alert" className="text-xs text-destructive">
|
|
169
|
+
{error}
|
|
170
|
+
</p>
|
|
171
|
+
) : null}
|
|
172
|
+
</li>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function Loading() {
|
|
177
|
+
return (
|
|
178
|
+
<ul aria-busy="true" aria-label="Loading payment methods" className="grid gap-3 md:grid-cols-2">
|
|
179
|
+
{[0, 1].map((i) => (
|
|
180
|
+
<li key={i} className="flex gap-3 rounded-lg border border-border p-4">
|
|
181
|
+
<Skeleton shape="rect" className="h-11 w-14" />
|
|
182
|
+
<div className="grid flex-1 content-start gap-2">
|
|
183
|
+
<Skeleton className="w-1/2" />
|
|
184
|
+
<Skeleton className="w-1/3" />
|
|
185
|
+
</div>
|
|
186
|
+
</li>
|
|
187
|
+
))}
|
|
188
|
+
</ul>
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export function PaymentMethods({
|
|
193
|
+
methods,
|
|
194
|
+
onAdd,
|
|
195
|
+
onSetDefault,
|
|
196
|
+
onRemove,
|
|
197
|
+
loading = false,
|
|
198
|
+
error,
|
|
199
|
+
title = "Payment methods",
|
|
200
|
+
description = "The cards and wallets saved to your account. The default is charged first.",
|
|
201
|
+
addLabel = "Add payment method",
|
|
202
|
+
emptyTitle = "No saved payment methods",
|
|
203
|
+
emptyBody = "Add one and it is ready at checkout.",
|
|
204
|
+
today,
|
|
205
|
+
className,
|
|
206
|
+
}) {
|
|
207
|
+
const [busy, setBusy] = useState({});
|
|
208
|
+
const [errors, setErrors] = useState({});
|
|
209
|
+
const [removing, setRemoving] = useState(null);
|
|
210
|
+
const [removeBusy, setRemoveBusy] = useState(false);
|
|
211
|
+
const [adding, setAdding] = useState(false);
|
|
212
|
+
const [addError, setAddError] = useState(null);
|
|
213
|
+
const hydrated = useHydrated();
|
|
214
|
+
const now = today ?? (hydrated ? todayString() : new Date().toISOString().slice(0, 10));
|
|
215
|
+
const list = (methods ?? []).filter(Boolean);
|
|
216
|
+
const onlyOne = list.length <= 1;
|
|
217
|
+
|
|
218
|
+
const mark = (id, state) => setBusy((b) => ({ ...b, [id]: state }));
|
|
219
|
+
const fail = (id, message) => setErrors((e) => ({ ...e, [id]: message }));
|
|
220
|
+
|
|
221
|
+
async function makeDefault(method) {
|
|
222
|
+
mark(method.id, "default");
|
|
223
|
+
fail(method.id, null);
|
|
224
|
+
fail(method.id, await attempt(() => onSetDefault(method)));
|
|
225
|
+
mark(method.id, null);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function confirmRemove() {
|
|
229
|
+
const method = removing;
|
|
230
|
+
if (!method) return;
|
|
231
|
+
setRemoveBusy(true);
|
|
232
|
+
fail(method.id, null);
|
|
233
|
+
const failed = await attempt(() => onRemove(method));
|
|
234
|
+
setRemoveBusy(false);
|
|
235
|
+
setRemoving(null);
|
|
236
|
+
if (failed) fail(method.id, failed);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
async function add() {
|
|
240
|
+
setAdding(true);
|
|
241
|
+
setAddError(null);
|
|
242
|
+
setAddError(await attempt(() => onAdd()));
|
|
243
|
+
setAdding(false);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const addButton = onAdd ? (
|
|
247
|
+
<Button variant="primary" icon="plus" onClick={add} loading={adding}>
|
|
248
|
+
{addLabel}
|
|
249
|
+
</Button>
|
|
250
|
+
) : null;
|
|
251
|
+
|
|
252
|
+
let body;
|
|
253
|
+
if (loading) body = <Loading />;
|
|
254
|
+
else if (!list.length) body = <Empty icon="credit-card" title={emptyTitle} body={emptyBody} action={addButton} />;
|
|
255
|
+
else
|
|
256
|
+
body = (
|
|
257
|
+
<ul aria-label={title} className="grid gap-3 md:grid-cols-2">
|
|
258
|
+
{list.map((method) => (
|
|
259
|
+
<MethodCard
|
|
260
|
+
key={method.id}
|
|
261
|
+
method={method}
|
|
262
|
+
today={now}
|
|
263
|
+
busy={busy[method.id]}
|
|
264
|
+
error={errors[method.id]}
|
|
265
|
+
onMakeDefault={onSetDefault ? makeDefault : null}
|
|
266
|
+
onAskRemove={onRemove ? setRemoving : null}
|
|
267
|
+
removable={!method.isDefault || onlyOne}
|
|
268
|
+
lockedReason="Make another method the default before removing this one"
|
|
269
|
+
/>
|
|
270
|
+
))}
|
|
271
|
+
</ul>
|
|
272
|
+
);
|
|
273
|
+
|
|
274
|
+
return (
|
|
275
|
+
<section className={cx("grid gap-5", className)}>
|
|
276
|
+
<header className="flex flex-wrap items-start justify-between gap-3">
|
|
277
|
+
<div className="min-w-0 max-w-prose">
|
|
278
|
+
<h2 className="text-xl font-semibold">{title}</h2>
|
|
279
|
+
{description ? <p className="mt-1 text-sm text-muted-foreground">{description}</p> : null}
|
|
280
|
+
</div>
|
|
281
|
+
{!loading && list.length ? <div className="w-full sm:w-auto [&>button]:w-full">{addButton}</div> : null}
|
|
282
|
+
</header>
|
|
283
|
+
{error ? <Notice tone="danger">{error}</Notice> : null}
|
|
284
|
+
{addError ? (
|
|
285
|
+
<Notice tone="danger" onDismiss={() => setAddError(null)}>
|
|
286
|
+
{addError}
|
|
287
|
+
</Notice>
|
|
288
|
+
) : null}
|
|
289
|
+
{body}
|
|
290
|
+
<Confirm
|
|
291
|
+
open={Boolean(removing)}
|
|
292
|
+
onClose={() => (removeBusy ? null : setRemoving(null))}
|
|
293
|
+
onConfirm={confirmRemove}
|
|
294
|
+
tone="danger"
|
|
295
|
+
busy={removeBusy}
|
|
296
|
+
title={removing ? `Remove ${nameOf(removing)}?` : "Remove?"}
|
|
297
|
+
body="It will no longer be offered at checkout. You can add it again later."
|
|
298
|
+
confirmLabel="Remove"
|
|
299
|
+
/>
|
|
300
|
+
</section>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# PdfDocument
|
|
2
|
+
|
|
3
|
+
Make PDFs in the workspace theme, in the browser. A small set of document
|
|
4
|
+
parts — headings, text, label-over-value fields, tables whose header
|
|
5
|
+
repeats on every page, totals, callouts, badges, rows and columns — and
|
|
6
|
+
a ready quote / estimate / invoice layout built from them. `renderPdf`
|
|
7
|
+
turns a document into a `Blob` to download, preview, upload or attach;
|
|
8
|
+
`usePdf` keeps one fresh while a form changes (a live preview); and
|
|
9
|
+
`PdfDownloadButton` is the "Download PDF" beside it.
|
|
10
|
+
|
|
11
|
+
The look is a clean business document: white paper, near-black ink,
|
|
12
|
+
grey for secondary text, hairline rules, one accent — the theme's
|
|
13
|
+
primary — on a bar along the top edge and on the amount that matters.
|
|
14
|
+
Type is one face at a few sizes on a 4pt rhythm, sentence case, figures
|
|
15
|
+
aligned in their columns. It prints well in black and white.
|
|
16
|
+
|
|
17
|
+
It is [@react-pdf/renderer](https://github.com/diegomura/react-pdf)
|
|
18
|
+
(MIT) underneath: it lays the document out in JavaScript and writes the
|
|
19
|
+
PDF, no browser print dialog and no server. The blocks that print
|
|
20
|
+
(quote-builder, proposal-accept…) import this one, never
|
|
21
|
+
`@react-pdf/renderer`.
|
|
22
|
+
|
|
23
|
+
```jsx
|
|
24
|
+
import { QuoteDocument, renderPdf, PdfDownloadButton } from "../components/blocks/pdf-document"; // a Makersclaw app ("./blocks/pdf-document" from components/)
|
|
25
|
+
import { QuoteDocument, renderPdf, PdfDownloadButton } from "@/components/makersclaw/blocks/pdf-document"; // a shadcn project
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Render from a `"use client"` component. `quoteTotals`, `formatMoney`,
|
|
29
|
+
`formatQuantity`, `formatPdfDate`, `buildPdfTheme` are plain functions
|
|
30
|
+
and work on the server (use the same sums on the screen and in the PDF).
|
|
31
|
+
|
|
32
|
+
## Colours: why the theme is read at render time
|
|
33
|
+
|
|
34
|
+
A PDF cannot read CSS variables — every colour in it is a value written
|
|
35
|
+
into the file. So `renderPdf` reads the workspace theme from the page at
|
|
36
|
+
the moment it makes the PDF (a hidden probe resolves `--color-primary`
|
|
37
|
+
and the other tokens to plain colours) and passes them in. Change the
|
|
38
|
+
theme and the next PDF follows; a PDF already made keeps its colours.
|
|
39
|
+
|
|
40
|
+
Two schemes:
|
|
41
|
+
|
|
42
|
+
- `paper` (the default) — what gets printed, emailed or signed: white
|
|
43
|
+
paper and dark ink whatever the workspace looks like, with the theme's
|
|
44
|
+
primary as the accent. A dark workspace still sends a light invoice.
|
|
45
|
+
If the primary is too pale to read on white (a mint, a lemon), text
|
|
46
|
+
uses a darker shade of it (4.5:1 or more) while bars keep the colour.
|
|
47
|
+
- `theme` — the workspace's own background, ink and borders, for a PDF
|
|
48
|
+
meant to be read on screen in the brand's look.
|
|
49
|
+
|
|
50
|
+
Nothing in a document names a colour: parts take a `tone` (`default` ·
|
|
51
|
+
`muted` · `accent` · `success` · `warning` · `danger` · `info`).
|
|
52
|
+
|
|
53
|
+
Fonts: Helvetica, which every PDF reader has built in, so a PDF is small
|
|
54
|
+
and nothing is fetched. It draws Latin text; for other scripts, or
|
|
55
|
+
symbols like ₹, register a font that has them (`registerPdfFont`).
|
|
56
|
+
|
|
57
|
+
## QuoteDocument
|
|
58
|
+
|
|
59
|
+
A quote, an estimate or an invoice from data. The sender and the
|
|
60
|
+
document's name at the top, then who it is for, the dates and the amount
|
|
61
|
+
due; the lines; the sums; notes, terms and how to pay.
|
|
62
|
+
|
|
63
|
+
```jsx
|
|
64
|
+
<QuoteDocument
|
|
65
|
+
kind="invoice" // quote (default) · estimate · invoice
|
|
66
|
+
number="INV-2026-041"
|
|
67
|
+
issued="2026-09-23" // "YYYY-MM-DD" or a Date
|
|
68
|
+
until="2026-10-07" // valid until (quote) · due (invoice)
|
|
69
|
+
from={{ name: "Northwind Studio", lines: ["18 Harbour Street", "Bristol BS1 4QA"], email: "billing@northwind.studio", logo: "/logo.png" }}
|
|
70
|
+
to={{ name: "Priya Raman", company: "Juniper Health", lines: ["400 Market St"], email: "priya@juniper.health" }}
|
|
71
|
+
items={[{ description: "Design system", detail: "32 components", quantity: 1, unit: "", unitPrice: 9600, discountPercent: 0, taxable: true }]}
|
|
72
|
+
currency="GBP" locale="en-GB"
|
|
73
|
+
taxRate={20} taxLabel="VAT (20%)"
|
|
74
|
+
discount={{ percent: 5 }} // or { amount: 250, label: "Loyalty" }
|
|
75
|
+
paid={3000} // a deposit: prints Total, Paid, Balance due
|
|
76
|
+
status="Deposit received" statusTone="success" // an outlined badge under the number
|
|
77
|
+
notes="Thanks for the conversation…" // a tinted callout
|
|
78
|
+
terms="Payment is due within 14 days."
|
|
79
|
+
payment={["Northwind Studio Ltd", "Sort code 00-00-00"]} // "How to pay" (lines or a string)
|
|
80
|
+
/>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Every prop but the data you have is optional: `title` overrides the
|
|
84
|
+
document's name, `reference` adds a "Reference" field (a PO number),
|
|
85
|
+
`footer` replaces the footer line (default: sender, email, website),
|
|
86
|
+
`dateLocale` formats dates (default `en-GB`: 23 Sep 2026), `size` is
|
|
87
|
+
`"A4"` (default) or `"LETTER"`. Amounts are in the currency's main unit
|
|
88
|
+
(pounds, not pence); `quoteTotals` does the sums — each line rounded,
|
|
89
|
+
the discount before tax, tax only on `taxable` lines.
|
|
90
|
+
|
|
91
|
+
## The parts
|
|
92
|
+
|
|
93
|
+
Compose your own document from these; they only render inside a
|
|
94
|
+
`PdfDocument`, passed to `renderPdf` (never into the page).
|
|
95
|
+
|
|
96
|
+
- `PdfDocument` — `title`, `author`, `subject` (the file's properties),
|
|
97
|
+
`size` (`"A4"` · `"LETTER"` · `"LEGAL"` · `[w, h]` in points),
|
|
98
|
+
`landscape`, `margin` (`compact` 36pt · `normal` 48pt · `roomy` 64pt),
|
|
99
|
+
`accentBar` (default `true`), `footer` (a line at the foot of every
|
|
100
|
+
page, with "Page 2 of 3" at the right when there is more than one;
|
|
101
|
+
`null` for none). Content flows onto new pages by itself.
|
|
102
|
+
- `PdfStack` — a column with `gap` (pt, default 8); `break` starts it on
|
|
103
|
+
a new page; `wrap={false}` keeps it on one page.
|
|
104
|
+
- `PdfRow` — a row: `gap` (default 16), `justify` (`start` · `between` ·
|
|
105
|
+
`end` · `center`), `align` (`start` · `center` · `end` · `baseline`).
|
|
106
|
+
`PdfCol` — one column of it: `flex` (share, default 1; `0` = as wide as
|
|
107
|
+
its content) or `width` (pt).
|
|
108
|
+
- `PdfHeading` — `level` 1 (24pt, the document's name) · 2 (12pt, a
|
|
109
|
+
section) · 3 (bold body size); `tone`, `align`.
|
|
110
|
+
- `PdfText` — `size` (`xs` · `sm` · `md` · `lg` · `xl` · `xxl`), `tone`,
|
|
111
|
+
`bold`, `align`.
|
|
112
|
+
- `PdfLabel` — the small muted line above a value. `PdfField` — a label
|
|
113
|
+
over a value (`strong` sets the value large and bold).
|
|
114
|
+
- `PdfLines` — lines of an address: the first bold, the rest muted
|
|
115
|
+
(`first={false}`, `muted={false}` to change).
|
|
116
|
+
- `PdfTable` — `columns` `[{ key, label, flex? | width?, align?,
|
|
117
|
+
render?(row, i) }]`, `rows` (a row's `detail` prints as a muted second
|
|
118
|
+
line under the first column), `empty`. The header repeats on each page;
|
|
119
|
+
a row never splits.
|
|
120
|
+
- `PdfTotals` — `rows` `[{ label, value, strong?, tone? }]` flush right;
|
|
121
|
+
the `strong` row is larger, over a rule. `width` (default 220pt).
|
|
122
|
+
- `PdfCallout` — a tinted box with an accent edge (`tone="neutral"` for
|
|
123
|
+
grey), optional `title`.
|
|
124
|
+
- `PdfBadge` — an outlined pill: `tone` `accent` · `success` · `warning`
|
|
125
|
+
· `danger` · `neutral`.
|
|
126
|
+
- `PdfDivider` — a hairline; `space` above and below (pt), `strong` for
|
|
127
|
+
an ink rule.
|
|
128
|
+
- `PdfImage` (`src`: a URL the browser can fetch, or a data URL),
|
|
129
|
+
`PdfLink` (`src`), `PdfView`, `PdfRawText` — the library's own parts,
|
|
130
|
+
for anything else; style them with `usePdfTheme()` values.
|
|
131
|
+
- `usePdfTheme()` — inside a part: `{ paper, ink, muted, rule, band,
|
|
132
|
+
accent, accentInk, accentSoft, onAccent, success, warning, danger,
|
|
133
|
+
info, font, fontBold }`. `PDF_TYPE` is the size scale in points.
|
|
134
|
+
|
|
135
|
+
## Functions
|
|
136
|
+
|
|
137
|
+
- `renderPdf(doc, { theme?, root?, scheme? })` → `Promise<Blob>`
|
|
138
|
+
(`application/pdf`). `theme` from `resolvePdfTheme(root, { scheme })`
|
|
139
|
+
or `buildPdfTheme(slots, { scheme, font })`; by default it is read from
|
|
140
|
+
`<body>` (or `root`) now. Rejects when the document cannot be made (an
|
|
141
|
+
image that will not load, a font that will not fetch).
|
|
142
|
+
- `usePdf(build, deps, { delay = 400, enabled = true, ...renderOptions })`
|
|
143
|
+
→ `{ blob, url, loading, error, refresh }`. Re-renders `delay` ms after
|
|
144
|
+
`deps` settle; `url` is an object URL, revoked when replaced.
|
|
145
|
+
- `PdfDownloadButton` — `document` (an element, or a function returning
|
|
146
|
+
one, called on press), `fileName`, `label` ("Download PDF"), `icon`,
|
|
147
|
+
`variant`, `size`, `disabled`, `options` (for renderPdf). Busy while it
|
|
148
|
+
renders; a failure shows a toast and the button is ready again.
|
|
149
|
+
- `downloadBlob(blob, fileName)` — save any Blob as a file.
|
|
150
|
+
- `registerPdfFont({ family, regular, bold? })` → `{ family, bold }` —
|
|
151
|
+
a TTF/WOFF by URL; pass the result as `font` to `buildPdfTheme`.
|
|
152
|
+
- `quoteTotals({ items, discount?, taxRate?, paid? })` → `{ lines,
|
|
153
|
+
subtotal, discount, tax, total, paid, due }`; `lineAmount(item)`;
|
|
154
|
+
`round2(n)`.
|
|
155
|
+
- `formatMoney(n, currency = "USD", locale = "en-US")` ("£1,234.50"),
|
|
156
|
+
`formatQuantity(q, locale)` ("1.5"), `formatPdfDate(value, locale =
|
|
157
|
+
"en-GB")` ("23 Sep 2026") — in characters the PDF's face can draw.
|
|
158
|
+
|
|
159
|
+
## Example
|
|
160
|
+
|
|
161
|
+
A quote screen: the form, a live preview in the `pdf-viewer` block, and
|
|
162
|
+
the download.
|
|
163
|
+
|
|
164
|
+
```jsx
|
|
165
|
+
"use client";
|
|
166
|
+
import { PdfViewer } from "../components/blocks/pdf-viewer";
|
|
167
|
+
import { PdfDownloadButton, QuoteDocument, usePdf } from "../components/blocks/pdf-document";
|
|
168
|
+
|
|
169
|
+
export function QuotePreview({ quote }) {
|
|
170
|
+
const doc = () => <QuoteDocument {...quote} />;
|
|
171
|
+
const { blob, error } = usePdf(doc, [quote]);
|
|
172
|
+
return (
|
|
173
|
+
<div className="grid gap-3">
|
|
174
|
+
<div className="flex justify-end">
|
|
175
|
+
<PdfDownloadButton variant="primary" document={doc} fileName={`${quote.number}.pdf`} />
|
|
176
|
+
</div>
|
|
177
|
+
<PdfViewer file={blob} loading={!blob && !error} title={`Quote ${quote.number}`} thumbnails={false} />
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Your own document:
|
|
184
|
+
|
|
185
|
+
```jsx
|
|
186
|
+
const blob = await renderPdf(
|
|
187
|
+
<PdfDocument title="Statement" footer="Juniper Health · Statement for August">
|
|
188
|
+
<PdfHeading level={1}>Statement</PdfHeading>
|
|
189
|
+
<PdfText tone="muted">1–31 August 2026</PdfText>
|
|
190
|
+
<PdfDivider />
|
|
191
|
+
<PdfTable columns={[{ key: "date", label: "Date", width: 80 }, { key: "what", label: "Description", flex: 3 }, { key: "amount", label: "Amount", width: 90, align: "right" }]} rows={rows} />
|
|
192
|
+
<PdfTotals rows={[{ label: "Closing balance", value: formatMoney(balance), strong: true }]} />
|
|
193
|
+
</PdfDocument>,
|
|
194
|
+
);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## What it will not do
|
|
198
|
+
|
|
199
|
+
- Render the parts into the page: they are PDF parts, not HTML. Preview
|
|
200
|
+
a PDF with the `pdf-viewer` block.
|
|
201
|
+
- Edit or fill an existing PDF, or sign one (see `signature-pad`).
|
|
202
|
+
- Use the workspace's web font on its own: register one with
|
|
203
|
+
`registerPdfFont`, or the PDF uses Helvetica.
|
|
204
|
+
- Make PDFs on the server from this block (it is a client block that
|
|
205
|
+
reads the theme from the page); `quoteTotals` and the formatters do run
|
|
206
|
+
there.
|
|
207
|
+
- Write right-to-left text or hyphenate: words are never split at a line
|
|
208
|
+
end.
|
|
209
|
+
|
|
210
|
+
## Credits
|
|
211
|
+
|
|
212
|
+
Built on [@react-pdf/renderer](https://github.com/diegomura/react-pdf) by
|
|
213
|
+
Diego Muracciole (MIT), used unmodified. See THIRD-PARTY-NOTICES.md.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pdf-document",
|
|
3
|
+
"title": "PdfDocument",
|
|
4
|
+
"description": "Make PDFs in the workspace theme (@react-pdf/renderer): a small set of document primitives (headings, text, label/value fields, tables with repeating headers, totals, callouts, badges, rows and columns) and a clean ready-made quote / estimate / invoice layout, rendered in the browser to a Blob with renderPdf(doc). The theme's colours are read from the page when the PDF is made (a PDF cannot read CSS variables): white paper, near-black ink and the theme's primary as the one accent. Also a live-preview hook (usePdf), a Download PDF button, and the quote sums and money formatting as plain functions.",
|
|
5
|
+
"useWhen": "A screen produces a document someone downloads, prints, emails or signs: a quote, an invoice, an estimate, a receipt, a statement, a certificate, a one-page report. The quote-builder and proposal blocks print through it. Not for showing an existing PDF on screen (pdf-viewer) or for a web page people read in the app (doc-page).",
|
|
6
|
+
"tags": ["pdf", "document", "invoice", "quote", "estimate", "receipt", "print", "download", "export", "react-pdf", "billing", "proposal", "statement"],
|
|
7
|
+
"exports": ["renderPdf", "usePdf", "PdfDownloadButton", "downloadBlob", "registerPdfFont", "PdfDocument", "PdfStack", "PdfRow", "PdfCol", "PdfText", "PdfHeading", "PdfLabel", "PdfField", "PdfDivider", "PdfCallout", "PdfBadge", "PdfTable", "PdfTotals", "PdfLines", "PdfView", "PdfRawText", "PdfImage", "PdfLink", "PdfThemeProvider", "usePdfTheme", "PDF_TYPE", "QuoteDocument", "quoteTotals", "lineAmount", "round2", "formatMoney", "formatQuantity", "formatPdfDate", "resolvePdfTheme", "buildPdfTheme", "DEFAULT_PDF_THEME"],
|
|
8
|
+
"files": ["index.js", "pdf-document.jsx", "primitives.jsx", "quote.jsx", "money.js", "theme.js"],
|
|
9
|
+
"requires": [],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"@react-pdf/renderer": "4.9.0"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// PdfDocument — the gallery's and the smoke test's surface. Not installed
|
|
4
|
+
// into apps. A small quote editor: the fields on the left, the PDF on the
|
|
5
|
+
// right, made in the browser from QuoteDocument as you type (usePdf) and
|
|
6
|
+
// shown in the pdf-viewer block, with a Download PDF button. Switch the
|
|
7
|
+
// kind to Invoice and the same data prints as an invoice with a deposit.
|
|
8
|
+
// The colours are the workspace theme's, read when the PDF is made.
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { Button, Card, Field, IconButton, Input, Notice, Textarea, ToggleGroup } from "../components/ui";
|
|
11
|
+
import { PdfViewer } from "../components/blocks/pdf-viewer";
|
|
12
|
+
import { PdfDownloadButton, QuoteDocument, formatMoney, quoteTotals, usePdf } from "../components/blocks/pdf-document";
|
|
13
|
+
|
|
14
|
+
const STUDIO = {
|
|
15
|
+
name: "Northwind Studio",
|
|
16
|
+
lines: ["18 Harbour Street", "Bristol BS1 4QA, United Kingdom"],
|
|
17
|
+
email: "billing@northwind.studio",
|
|
18
|
+
website: "northwind.studio",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const START_ITEMS = [
|
|
22
|
+
{ id: "l1", description: "Discovery workshop", detail: "Two half-day sessions with your team", quantity: 2, unit: "days", unitPrice: 1400 },
|
|
23
|
+
{ id: "l2", description: "Design system", detail: "Tokens, 32 components, documentation", quantity: 1, unitPrice: 9600 },
|
|
24
|
+
{ id: "l3", description: "Landing page build", quantity: 3, unitPrice: 1850 },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
let nextId = 4;
|
|
28
|
+
|
|
29
|
+
export default function PdfDocumentDemo() {
|
|
30
|
+
const [kind, setKind] = useState("quote");
|
|
31
|
+
const [client, setClient] = useState({ name: "Priya Raman", company: "Juniper Health", email: "priya@juniper.health" });
|
|
32
|
+
const [items, setItems] = useState(START_ITEMS);
|
|
33
|
+
const [taxRate, setTaxRate] = useState("20");
|
|
34
|
+
const [notes, setNotes] = useState("Thank you for the conversation last week. This quote covers the first phase; we will scope the build together once discovery is done.");
|
|
35
|
+
|
|
36
|
+
const invoice = kind === "invoice";
|
|
37
|
+
const doc = {
|
|
38
|
+
kind,
|
|
39
|
+
number: invoice ? "INV-2026-041" : "Q-2026-118",
|
|
40
|
+
issued: "2026-09-23",
|
|
41
|
+
until: invoice ? "2026-10-07" : "2026-10-23",
|
|
42
|
+
from: STUDIO,
|
|
43
|
+
to: { name: client.name, company: client.company, email: client.email },
|
|
44
|
+
items,
|
|
45
|
+
currency: "GBP",
|
|
46
|
+
locale: "en-GB",
|
|
47
|
+
taxRate: Number(taxRate) || 0,
|
|
48
|
+
taxLabel: `VAT (${Number(taxRate) || 0}%)`,
|
|
49
|
+
paid: invoice ? 3000 : 0,
|
|
50
|
+
status: invoice ? "Deposit received" : null,
|
|
51
|
+
statusTone: "success",
|
|
52
|
+
notes,
|
|
53
|
+
terms: invoice ? "Payment is due within 14 days." : "This quote is valid for 30 days. Work starts on a signed quote and a 30% deposit.",
|
|
54
|
+
payment: invoice ? ["Northwind Studio Ltd", "Sort code 00-00-00", "Account 00000000"] : null,
|
|
55
|
+
};
|
|
56
|
+
const pdf = usePdf(() => <QuoteDocument {...doc} />, [JSON.stringify(doc)], { delay: 500 });
|
|
57
|
+
const sums = quoteTotals({ items, taxRate: doc.taxRate, paid: doc.paid });
|
|
58
|
+
|
|
59
|
+
const setItem = (id, patch) => setItems((list) => list.map((it) => (it.id === id ? { ...it, ...patch } : it)));
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div className="grid gap-4 lg:grid-cols-[minmax(0,400px)_minmax(0,1fr)] lg:items-start">
|
|
63
|
+
<Card title="Quote details" padding="md">
|
|
64
|
+
<div className="grid gap-4">
|
|
65
|
+
<ToggleGroup
|
|
66
|
+
aria-label="Kind of document"
|
|
67
|
+
className="w-full"
|
|
68
|
+
value={kind}
|
|
69
|
+
onChange={setKind}
|
|
70
|
+
items={[
|
|
71
|
+
{ value: "quote", label: "Quote" },
|
|
72
|
+
{ value: "invoice", label: "Invoice" },
|
|
73
|
+
]}
|
|
74
|
+
/>
|
|
75
|
+
<div className="grid gap-3 sm:grid-cols-2">
|
|
76
|
+
<Field id="q-name" label="Client">
|
|
77
|
+
<Input id="q-name" value={client.name} onChange={(e) => setClient({ ...client, name: e.target.value })} />
|
|
78
|
+
</Field>
|
|
79
|
+
<Field id="q-company" label="Company">
|
|
80
|
+
<Input id="q-company" value={client.company} onChange={(e) => setClient({ ...client, company: e.target.value })} />
|
|
81
|
+
</Field>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div className="grid gap-2">
|
|
85
|
+
<p className="text-sm font-medium">Line items</p>
|
|
86
|
+
<ul className="grid gap-2">
|
|
87
|
+
{items.map((it) => (
|
|
88
|
+
<li key={it.id} className="grid grid-cols-[minmax(0,1fr)_4rem_6rem_auto] items-center gap-2">
|
|
89
|
+
<Input aria-label="Description" value={it.description} placeholder="What is it?" onChange={(e) => setItem(it.id, { description: e.target.value })} />
|
|
90
|
+
<Input aria-label="Quantity" inputMode="decimal" className="text-right tabular-nums" value={String(it.quantity)} onChange={(e) => setItem(it.id, { quantity: e.target.value })} />
|
|
91
|
+
<Input aria-label="Unit price" inputMode="decimal" className="text-right tabular-nums" value={String(it.unitPrice)} onChange={(e) => setItem(it.id, { unitPrice: e.target.value })} />
|
|
92
|
+
<IconButton icon="trash" label={`Remove ${it.description || "line"}`} onClick={() => setItems((list) => list.filter((x) => x.id !== it.id))} />
|
|
93
|
+
</li>
|
|
94
|
+
))}
|
|
95
|
+
</ul>
|
|
96
|
+
<div className="flex items-center justify-between gap-2">
|
|
97
|
+
<Button variant="quiet" size="sm" icon="plus" onClick={() => setItems((list) => [...list, { id: `l${nextId++}`, description: "", quantity: 1, unitPrice: 0 }])}>
|
|
98
|
+
Add line
|
|
99
|
+
</Button>
|
|
100
|
+
<p className="text-sm text-muted-foreground tabular-nums">
|
|
101
|
+
Subtotal <span className="font-medium text-foreground">{formatMoney(sums.subtotal, "GBP", "en-GB")}</span>
|
|
102
|
+
</p>
|
|
103
|
+
</div>
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<Field id="q-tax" label="VAT rate" hint="Percent, on every line.">
|
|
107
|
+
<Input id="q-tax" inputMode="decimal" className="tabular-nums" value={taxRate} onChange={(e) => setTaxRate(e.target.value)} />
|
|
108
|
+
</Field>
|
|
109
|
+
<Field id="q-notes" label="Note to the client">
|
|
110
|
+
<Textarea id="q-notes" autoGrow minRows={2} maxRows={6} value={notes} onChange={(e) => setNotes(e.target.value)} />
|
|
111
|
+
</Field>
|
|
112
|
+
</div>
|
|
113
|
+
</Card>
|
|
114
|
+
|
|
115
|
+
<div className="grid min-w-0 gap-3">
|
|
116
|
+
<div className="flex flex-wrap items-center justify-between gap-3">
|
|
117
|
+
<div className="min-w-0">
|
|
118
|
+
<p className="text-sm font-medium">
|
|
119
|
+
{invoice ? "Invoice" : "Quote"} {doc.number}
|
|
120
|
+
</p>
|
|
121
|
+
<p className="text-sm text-muted-foreground tabular-nums">
|
|
122
|
+
{invoice ? "Balance due" : "Total"} {formatMoney(sums.due, "GBP", "en-GB")} · {pdf.loading ? "Updating preview…" : "Preview is up to date"}
|
|
123
|
+
</p>
|
|
124
|
+
</div>
|
|
125
|
+
<PdfDownloadButton variant="primary" document={() => <QuoteDocument {...doc} />} fileName={`${doc.number}.pdf`} />
|
|
126
|
+
</div>
|
|
127
|
+
{pdf.error ? (
|
|
128
|
+
<Notice tone="danger" title="The preview couldn't be made">
|
|
129
|
+
{String(pdf.error.message ?? pdf.error)}
|
|
130
|
+
</Notice>
|
|
131
|
+
) : null}
|
|
132
|
+
<PdfViewer file={pdf.blob} loading={!pdf.blob} title={`${invoice ? "Invoice" : "Quote"} ${doc.number}`} fileName={`${doc.number}.pdf`} thumbnails={false} height="760px" />
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|