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,133 @@
|
|
|
1
|
+
# Footer
|
|
2
|
+
|
|
3
|
+
A public site's footer. It holds the brand and a line about it, link
|
|
4
|
+
columns (or one row of links), social buttons drawn with the kit's brand
|
|
5
|
+
marks, and the legal line with its links. Optional parts: a newsletter
|
|
6
|
+
sign-up (with a consent box), a call to action, a large brand statement
|
|
7
|
+
or quote, language and currency selects, and a version badge.
|
|
8
|
+
|
|
9
|
+
Three layouts:
|
|
10
|
+
- **columns** (default) — the brand, tagline and socials beside the link
|
|
11
|
+
columns (and an inline sign-up), then the legal row;
|
|
12
|
+
- **simple** — one row: brand, links, socials; then the legal row;
|
|
13
|
+
- **centred** — everything stacked in the middle.
|
|
14
|
+
|
|
15
|
+
The sign-up checks the email (and the consent, when asked) in place,
|
|
16
|
+
then calls `newsletter.onSubscribe(email, { consent })`, which is async
|
|
17
|
+
and returns `{ ok }`. While it runs the button spins. `{ ok: false,
|
|
18
|
+
error }` (or a throw) shows the error and keeps the email. `{ ok: true }`
|
|
19
|
+
shows "You're subscribed". It is a full-width section: it pads itself
|
|
20
|
+
and centres its content.
|
|
21
|
+
|
|
22
|
+
```jsx
|
|
23
|
+
import { Footer } from "../components/blocks/footer"; // a Makersclaw app ("./blocks/footer" from components/)
|
|
24
|
+
import { Footer } from "@/components/makersclaw/blocks/footer"; // a shadcn project
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
It is a client component: render it from a `"use client"` component (a
|
|
28
|
+
layout is the usual place).
|
|
29
|
+
|
|
30
|
+
## The data
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// a link, anywhere: columns, links, legalLinks
|
|
34
|
+
{ label: "Pricing", href: "/pricing", external?: true } // external opens a new tab
|
|
35
|
+
|
|
36
|
+
// columns
|
|
37
|
+
[{ title: "Product", links: [link, …] }, …] // up to 5 across; 2 on a phone
|
|
38
|
+
|
|
39
|
+
// socials
|
|
40
|
+
[{ network: "x", href: "https://x.com/fieldline", label? }, …]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`network` is one of `x`, `linkedin`, `github`, `youtube`, `instagram`,
|
|
44
|
+
`facebook`, `google`, `apple`, `microsoft`, `email`, `rss`. Anything
|
|
45
|
+
else needs an `icon` (a kit Icon name) and a `label`. Each is an icon
|
|
46
|
+
button, named to a screen reader ("X", "LinkedIn"), opening a new tab.
|
|
47
|
+
|
|
48
|
+
## Props
|
|
49
|
+
|
|
50
|
+
- `layout` — `columns` (default) · `simple` · `centered`.
|
|
51
|
+
- `brand` — a node: your logo, linked home if you like.
|
|
52
|
+
- `tagline` — a line under the brand (columns and centred).
|
|
53
|
+
- `columns` — link columns (columns layout).
|
|
54
|
+
- `links` — one row of links (simple and centred; under the socials in
|
|
55
|
+
columns).
|
|
56
|
+
- `socials` — as above.
|
|
57
|
+
- `newsletter` — `{ onSubscribe, title?, body?, label?, placeholder?,
|
|
58
|
+
consent?, look?, doneTitle?, doneBody? }`. `onSubscribe(email, {
|
|
59
|
+
consent })` is async and returns `{ ok: true }` or `{ ok: false,
|
|
60
|
+
error? }`; without it there is no sign-up. `consent` is the checkbox's
|
|
61
|
+
label (a string or a node with a link), and must be ticked. `look`:
|
|
62
|
+
`inline` (default: a column beside the links) · `card` (a full-width
|
|
63
|
+
card under them). `label` is the button, default "Subscribe".
|
|
64
|
+
- `cta` — `{ title?, body?, actions: [{ label, href?, onClick?, icon?,
|
|
65
|
+
variant? }] }`: "Sign in", "Get the app". A band above the legal row;
|
|
66
|
+
the first action is primary unless it says otherwise.
|
|
67
|
+
- `statement` — a large line at the top: a string, or `{ text, by }` for
|
|
68
|
+
a quote.
|
|
69
|
+
- `locales` — `[{ id, label, options: [{ value, label }], value?,
|
|
70
|
+
defaultValue?, onChange? }]`: language, currency, country selects in
|
|
71
|
+
the legal row. Uncontrolled unless you pass `value`.
|
|
72
|
+
- `version` — "v4.2.0": a code badge in the legal row.
|
|
73
|
+
- `legal` — the copyright line: "© 2026 Fieldline Ltd.". Pass the year;
|
|
74
|
+
the block does not read the clock.
|
|
75
|
+
- `legalLinks` — privacy, terms, cookies.
|
|
76
|
+
- `onNavigate(link, event)` — called when any link or a `cta` action with
|
|
77
|
+
an `href` is followed, before navigation; `event.preventDefault()`
|
|
78
|
+
stops it.
|
|
79
|
+
- `label` — what a screen reader calls the footer and its link row.
|
|
80
|
+
Default "Site".
|
|
81
|
+
- `className` — the footer's spacing. Never colour.
|
|
82
|
+
|
|
83
|
+
## Example
|
|
84
|
+
|
|
85
|
+
```jsx
|
|
86
|
+
// components/SiteFooter.jsx
|
|
87
|
+
"use client";
|
|
88
|
+
import { Icon } from "./ui";
|
|
89
|
+
import { Footer } from "./blocks/footer";
|
|
90
|
+
import { subscribe } from "../lib/actions"; // your server action
|
|
91
|
+
|
|
92
|
+
export default function SiteFooter() {
|
|
93
|
+
return (
|
|
94
|
+
<Footer
|
|
95
|
+
brand={<span className="inline-flex items-center gap-2"><Icon name="zap" /> Fieldline</span>}
|
|
96
|
+
tagline="The pipeline small sales teams actually keep up to date."
|
|
97
|
+
columns={[
|
|
98
|
+
{ title: "Product", links: [{ label: "Pricing", href: "/pricing" }, { label: "Changelog", href: "/changelog" }] },
|
|
99
|
+
{ title: "Company", links: [{ label: "About", href: "/about" }, { label: "Careers", href: "/careers" }] },
|
|
100
|
+
]}
|
|
101
|
+
socials={[{ network: "x", href: "https://x.com/fieldline" }, { network: "linkedin", href: "https://linkedin.com/company/fieldline" }]}
|
|
102
|
+
newsletter={{
|
|
103
|
+
body: "One email a month.",
|
|
104
|
+
consent: "I agree to the privacy policy.",
|
|
105
|
+
onSubscribe: async (email) => subscribe(email),
|
|
106
|
+
}}
|
|
107
|
+
legal="© 2026 Fieldline Ltd. All rights reserved."
|
|
108
|
+
legalLinks={[{ label: "Privacy", href: "/privacy" }, { label: "Terms", href: "/terms" }]}
|
|
109
|
+
/>
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## What it will not do
|
|
115
|
+
|
|
116
|
+
- Change the language or currency by itself: `locales[].onChange` is
|
|
117
|
+
yours.
|
|
118
|
+
- Stick to the bottom of a short page: that is your layout's
|
|
119
|
+
`min-h-dvh` flex column.
|
|
120
|
+
- Paint itself in the primary colour: the look is the workspace
|
|
121
|
+
theme's.
|
|
122
|
+
- Draw store badges: "Get the app" is two buttons with the Apple and
|
|
123
|
+
Google marks.
|
|
124
|
+
|
|
125
|
+
A footer it cannot be is your own component in `components/`, built
|
|
126
|
+
from the kit — not an edit to this block. Its files are fixed: `npm run
|
|
127
|
+
check` refuses a change, and `makersclaw add footer` restores them.
|
|
128
|
+
|
|
129
|
+
## Credits
|
|
130
|
+
|
|
131
|
+
Layouts ported from Creative Tim UI's footer blocks
|
|
132
|
+
(https://github.com/creativetimofficial/ui, MIT), rewritten on the
|
|
133
|
+
Makersclaw kit.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "footer",
|
|
3
|
+
"title": "Footer",
|
|
4
|
+
"description": "A public site's footer: brand and tagline, link columns or one row of links, social buttons in the kit's brand marks (X, LinkedIn, GitHub, YouTube, Instagram, Facebook), and the legal line with its links; optionally a newsletter sign-up with consent (validates, saves through an async onSubscribe returning { ok }, shows the failure), a call to action, a large brand statement, language and currency selects, and a version badge. Simple, columns or centred.",
|
|
5
|
+
"useWhen": "The bottom of every page of a public site: a marketing site, a docs hub, a shop, a customer portal. Not for an app's own status bar or a toolbar pinned to the bottom of a screen.",
|
|
6
|
+
"tags": ["footer", "site footer", "links", "social", "newsletter", "subscribe", "legal", "copyright", "language", "currency", "marketing", "landing page"],
|
|
7
|
+
"exports": ["Footer"],
|
|
8
|
+
"files": ["index.js", "footer.jsx"],
|
|
9
|
+
"requires": [],
|
|
10
|
+
"frame": "bleed"
|
|
11
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// The Footer in every variant — the gallery's and the smoke test's
|
|
4
|
+
// surface. Not installed into apps: `makersclaw add footer` lays index.js
|
|
5
|
+
// and footer.jsx only. Links are caught by onNavigate and only named
|
|
6
|
+
// here; pick whether the next newsletter sign-up succeeds or fails.
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Field, Icon, Select, ToggleGroup } from "../components/ui";
|
|
9
|
+
import { Footer } from "../components/blocks/footer";
|
|
10
|
+
|
|
11
|
+
function Logo() {
|
|
12
|
+
return (
|
|
13
|
+
<span className="inline-flex items-center gap-2">
|
|
14
|
+
<Icon name="zap" size={20} className="text-primary" />
|
|
15
|
+
Fieldline
|
|
16
|
+
</span>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const COLUMNS = [
|
|
21
|
+
{ title: "Product", links: [{ label: "Pipeline", href: "/product/pipeline" }, { label: "Automations", href: "/product/automations" }, { label: "Reports", href: "/product/reports" }, { label: "Pricing", href: "/pricing" }] },
|
|
22
|
+
{ title: "Company", links: [{ label: "About", href: "/about" }, { label: "Careers", href: "/careers" }, { label: "Customers", href: "/customers" }, { label: "Press", href: "/press" }] },
|
|
23
|
+
{ title: "Resources", links: [{ label: "Docs", href: "/docs" }, { label: "Guides", href: "/guides" }, { label: "Changelog", href: "/changelog" }, { label: "Status", href: "/status" }] },
|
|
24
|
+
];
|
|
25
|
+
const MORE = [
|
|
26
|
+
{ title: "Solutions", links: [{ label: "Founders", href: "/for/founders" }, { label: "Sales teams", href: "/for/sales" }, { label: "Agencies", href: "/for/agencies" }] },
|
|
27
|
+
{ title: "Support", links: [{ label: "Help centre", href: "/help" }, { label: "Contact", href: "/contact" }, { label: "Security", href: "/security" }] },
|
|
28
|
+
];
|
|
29
|
+
const ROW = [
|
|
30
|
+
{ label: "Product", href: "/product" },
|
|
31
|
+
{ label: "Pricing", href: "/pricing" },
|
|
32
|
+
{ label: "Customers", href: "/customers" },
|
|
33
|
+
{ label: "Docs", href: "/docs" },
|
|
34
|
+
{ label: "Contact", href: "/contact" },
|
|
35
|
+
];
|
|
36
|
+
const SOCIALS = [
|
|
37
|
+
{ network: "x", href: "https://x.com" },
|
|
38
|
+
{ network: "linkedin", href: "https://linkedin.com" },
|
|
39
|
+
{ network: "github", href: "https://github.com" },
|
|
40
|
+
{ network: "youtube", href: "https://youtube.com" },
|
|
41
|
+
{ network: "instagram", href: "https://instagram.com" },
|
|
42
|
+
{ network: "facebook", href: "https://facebook.com" },
|
|
43
|
+
];
|
|
44
|
+
const LEGAL = "© 2026 Fieldline Ltd. All rights reserved.";
|
|
45
|
+
const LEGAL_LINKS = [
|
|
46
|
+
{ label: "Privacy", href: "/privacy" },
|
|
47
|
+
{ label: "Terms", href: "/terms" },
|
|
48
|
+
{ label: "Cookies", href: "/cookies" },
|
|
49
|
+
];
|
|
50
|
+
const TAGLINE = "The pipeline small sales teams actually keep up to date.";
|
|
51
|
+
const NEWS = { title: "Subscribe", body: "One email a month: what shipped, and what we learned selling it.", consent: "I agree to the terms and the privacy policy." };
|
|
52
|
+
const LANGUAGE = { id: "language", label: "Language", options: [{ value: "en", label: "English" }, { value: "de", label: "Deutsch" }, { value: "pt", label: "Português" }, { value: "fr", label: "Français" }] };
|
|
53
|
+
const CURRENCY = { id: "currency", label: "Currency", options: [{ value: "usd", label: "USD $" }, { value: "eur", label: "EUR €" }, { value: "gbp", label: "GBP £" }] };
|
|
54
|
+
const COUNTRY = { id: "country", label: "Country", options: [{ value: "gb", label: "United Kingdom" }, { value: "us", label: "United States" }, { value: "pt", label: "Portugal" }] };
|
|
55
|
+
|
|
56
|
+
const BASE = { brand: <Logo />, legal: LEGAL };
|
|
57
|
+
|
|
58
|
+
const VARIANTS = [
|
|
59
|
+
{ id: "01", label: "01 · One row, links and socials", props: { ...BASE, layout: "simple", links: ROW, socials: SOCIALS.slice(0, 4) } },
|
|
60
|
+
{ id: "02", label: "02 · Columns and a sign-up", props: { ...BASE, columns: COLUMNS, newsletter: NEWS, legalLinks: LEGAL_LINKS } },
|
|
61
|
+
{ id: "03", label: "03 · A call to action", props: { ...BASE, layout: "simple", links: ROW, cta: { title: "Ready when you are", body: "Your workspace is one click away.", actions: [{ label: "Sign in", href: "/login", icon: "log-in" }] } } },
|
|
62
|
+
{ id: "04", label: "04 · Sign-up in a card", props: { ...BASE, tagline: TAGLINE, columns: COLUMNS, socials: SOCIALS.slice(0, 3), newsletter: { ...NEWS, title: "Get the monthly letter", look: "card" } } },
|
|
63
|
+
{ id: "05", label: "05 · Language and currency", props: { ...BASE, columns: COLUMNS, locales: [LANGUAGE, CURRENCY], legalLinks: LEGAL_LINKS } },
|
|
64
|
+
{ id: "06", label: "06 · Brand and many columns", props: { ...BASE, tagline: TAGLINE, socials: SOCIALS.slice(0, 3), columns: [...COLUMNS, ...MORE], legalLinks: LEGAL_LINKS } },
|
|
65
|
+
{ id: "07", label: "07 · Country picker", props: { ...BASE, layout: "simple", links: ROW.slice(0, 3), locales: [COUNTRY] } },
|
|
66
|
+
{ id: "08", label: "08 · Version badge", props: { ...BASE, layout: "simple", version: "v4.2.0", legalLinks: LEGAL_LINKS } },
|
|
67
|
+
{ id: "09", label: "09 · A notice and a button", props: { ...BASE, layout: "simple", cta: { body: "New: forecasts now show the deals behind every number.", actions: [{ label: "Read the release", href: "/changelog", variant: "secondary" }] } } },
|
|
68
|
+
{ id: "10", label: "10 · Page links only", props: { legal: LEGAL, layout: "simple", links: ROW } },
|
|
69
|
+
{ id: "11", label: "11 · A large statement", props: { ...BASE, statement: "Sell like a team of ten. Stay a team of three.", tagline: TAGLINE, columns: COLUMNS } },
|
|
70
|
+
{ id: "12", label: "12 · Navigation links", props: { ...BASE, layout: "simple", links: [...ROW, { label: "Careers", href: "/careers" }, { label: "Status", href: "/status" }], legalLinks: LEGAL_LINKS } },
|
|
71
|
+
{ id: "13", label: "13 · Centred", props: { ...BASE, layout: "centered", tagline: TAGLINE, links: ROW, socials: SOCIALS } },
|
|
72
|
+
{ id: "14", label: "14 · Get the app", props: { ...BASE, tagline: TAGLINE, links: ROW, cta: { title: "Get the app", body: "Your pipeline on the train, with offline notes.", actions: [{ label: "App Store", href: "/apps/ios", icon: "apple", variant: "secondary" }, { label: "Google Play", href: "/apps/android", icon: "google", variant: "secondary" }] } } },
|
|
73
|
+
{ id: "15", label: "15 · A quote above the columns", props: { ...BASE, statement: { text: "“We stopped losing deals in a spreadsheet.”", by: "Maya Okafor, Greenhaus" }, columns: COLUMNS, socials: SOCIALS.slice(0, 4) } },
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const OUTCOMES = [
|
|
77
|
+
{ value: "ok", label: "Sign-up succeeds" },
|
|
78
|
+
{ value: "fail", label: "Sign-up fails" },
|
|
79
|
+
];
|
|
80
|
+
|
|
81
|
+
export default function FooterDemo() {
|
|
82
|
+
const [variant, setVariant] = useState("02");
|
|
83
|
+
const [outcome, setOutcome] = useState("ok");
|
|
84
|
+
const [followed, setFollowed] = useState(null);
|
|
85
|
+
const v = VARIANTS.find((x) => x.id === variant) ?? VARIANTS[0];
|
|
86
|
+
const props = { ...v.props };
|
|
87
|
+
if (props.newsletter) {
|
|
88
|
+
props.newsletter = {
|
|
89
|
+
...props.newsletter,
|
|
90
|
+
onSubscribe: async () => {
|
|
91
|
+
await new Promise((r) => setTimeout(r, 900));
|
|
92
|
+
return outcome === "fail" ? { ok: false, error: "The list service did not answer. Try again in a minute." } : { ok: true };
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<div className="grid gap-2">
|
|
99
|
+
<div className="flex flex-wrap items-end gap-3 px-4 pt-4 sm:px-6">
|
|
100
|
+
<Field id="f-variant" label="Variant" className="w-full sm:w-80">
|
|
101
|
+
<Select id="f-variant" value={variant} onChange={(e) => setVariant(e.target.value)} options={VARIANTS.map((x) => ({ value: x.id, label: x.label }))} />
|
|
102
|
+
</Field>
|
|
103
|
+
<ToggleGroup aria-label="Next sign-up" items={OUTCOMES} value={outcome} onChange={setOutcome} />
|
|
104
|
+
</div>
|
|
105
|
+
<p className="px-4 text-sm text-muted-foreground sm:px-6">
|
|
106
|
+
Followed: <span className="font-medium text-foreground">{followed ?? "nothing yet"}</span>
|
|
107
|
+
</p>
|
|
108
|
+
<div className="h-24" aria-hidden="true" />
|
|
109
|
+
<Footer
|
|
110
|
+
key={variant}
|
|
111
|
+
{...props}
|
|
112
|
+
onNavigate={(link, e) => {
|
|
113
|
+
e.preventDefault();
|
|
114
|
+
setFollowed(link.href);
|
|
115
|
+
}}
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useId, useState } from "react";
|
|
4
|
+
import { Badge, Button, Checkbox, Field, IconButton, Input, Notice, Select, cx } from "../../ui";
|
|
5
|
+
|
|
6
|
+
// Footer — a public site's footer: the brand and a line about it, link
|
|
7
|
+
// columns (or one row of links), social buttons drawn with the kit's
|
|
8
|
+
// brand marks, and the legal line; optionally a newsletter sign-up (with
|
|
9
|
+
// consent), a call to action, a large brand statement, language or
|
|
10
|
+
// currency pickers, and a version badge. Three layouts: simple (one
|
|
11
|
+
// row), columns, and centred. The sign-up validates the email and saves
|
|
12
|
+
// through `newsletter.onSubscribe(email, extra)` — async, returning
|
|
13
|
+
// { ok } — showing the failure or a subscribed state.
|
|
14
|
+
//
|
|
15
|
+
// <Footer brand={<Logo />} columns={COLUMNS} socials={SOCIALS}
|
|
16
|
+
// legal="© 2026 Fieldline Ltd." />
|
|
17
|
+
//
|
|
18
|
+
// README.md beside this file lists every prop.
|
|
19
|
+
|
|
20
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
21
|
+
|
|
22
|
+
// A network's name and its brand glyph.
|
|
23
|
+
const NETWORKS = {
|
|
24
|
+
x: { label: "X", icon: "x-logo" },
|
|
25
|
+
linkedin: { label: "LinkedIn", icon: "linkedin" },
|
|
26
|
+
instagram: { label: "Instagram", icon: "instagram" },
|
|
27
|
+
youtube: { label: "YouTube", icon: "youtube" },
|
|
28
|
+
facebook: { label: "Facebook", icon: "facebook" },
|
|
29
|
+
github: { label: "GitHub", icon: "github" },
|
|
30
|
+
google: { label: "Google", icon: "google" },
|
|
31
|
+
apple: { label: "Apple", icon: "apple" },
|
|
32
|
+
microsoft: { label: "Microsoft", icon: "microsoft" },
|
|
33
|
+
email: { label: "Email", icon: "mail" },
|
|
34
|
+
rss: { label: "RSS", icon: "globe" },
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const COLUMNS = {
|
|
38
|
+
1: "grid-cols-1",
|
|
39
|
+
2: "grid-cols-2",
|
|
40
|
+
3: "grid-cols-2 sm:grid-cols-3",
|
|
41
|
+
4: "grid-cols-2 sm:grid-cols-4",
|
|
42
|
+
5: "grid-cols-2 sm:grid-cols-3 lg:grid-cols-5",
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
function FooterLink({ link, onNavigate }) {
|
|
46
|
+
return (
|
|
47
|
+
<Button
|
|
48
|
+
variant="link"
|
|
49
|
+
tone="muted"
|
|
50
|
+
href={link.href}
|
|
51
|
+
onClick={(e) => onNavigate?.(link, e)}
|
|
52
|
+
target={link.external ? "_blank" : undefined}
|
|
53
|
+
rel={link.external ? "noreferrer" : undefined}
|
|
54
|
+
>
|
|
55
|
+
{link.label}
|
|
56
|
+
</Button>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function LinkRow({ links, onNavigate, center, label }) {
|
|
61
|
+
if (!links?.length) return null;
|
|
62
|
+
return (
|
|
63
|
+
<nav aria-label={label}>
|
|
64
|
+
<ul className={cx("flex flex-wrap gap-x-6 gap-y-3", center && "justify-center")}>
|
|
65
|
+
{links.map((l, i) => (
|
|
66
|
+
<li key={l.href ?? l.label ?? i}>
|
|
67
|
+
<FooterLink link={l} onNavigate={onNavigate} />
|
|
68
|
+
</li>
|
|
69
|
+
))}
|
|
70
|
+
</ul>
|
|
71
|
+
</nav>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function Columns({ columns, onNavigate }) {
|
|
76
|
+
if (!columns?.length) return null;
|
|
77
|
+
const n = Math.min(columns.length, 5);
|
|
78
|
+
return (
|
|
79
|
+
<div className={cx("grid min-w-0 gap-x-6 gap-y-10", COLUMNS[n])}>
|
|
80
|
+
{columns.map((col, i) => (
|
|
81
|
+
<nav key={col.title ?? i} aria-label={col.title} className="min-w-0">
|
|
82
|
+
{col.title ? <h3 className="mb-4 text-sm font-semibold">{col.title}</h3> : null}
|
|
83
|
+
<ul className="grid gap-3">
|
|
84
|
+
{(col.links ?? []).map((l, k) => (
|
|
85
|
+
<li key={l.href ?? l.label ?? k} className="min-w-0">
|
|
86
|
+
<FooterLink link={l} onNavigate={onNavigate} />
|
|
87
|
+
</li>
|
|
88
|
+
))}
|
|
89
|
+
</ul>
|
|
90
|
+
</nav>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function Socials({ socials, center }) {
|
|
97
|
+
if (!socials?.length) return null;
|
|
98
|
+
return (
|
|
99
|
+
<ul aria-label="Social links" className={cx("flex flex-wrap items-center gap-1", center && "justify-center")}>
|
|
100
|
+
{socials.map((s, i) => {
|
|
101
|
+
const net = NETWORKS[s.network] ?? { label: s.label ?? s.network, icon: s.icon ?? "link" };
|
|
102
|
+
return (
|
|
103
|
+
<li key={s.href ?? i}>
|
|
104
|
+
<IconButton
|
|
105
|
+
icon={s.icon ?? net.icon}
|
|
106
|
+
label={s.label ?? net.label}
|
|
107
|
+
href={s.href}
|
|
108
|
+
target="_blank"
|
|
109
|
+
rel="noreferrer"
|
|
110
|
+
/>
|
|
111
|
+
</li>
|
|
112
|
+
);
|
|
113
|
+
})}
|
|
114
|
+
</ul>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function Brand({ brand, tagline, center }) {
|
|
119
|
+
if (!brand && !tagline) return null;
|
|
120
|
+
return (
|
|
121
|
+
<div className={cx("grid min-w-0 content-start gap-3", center && "justify-items-center text-center")}>
|
|
122
|
+
{brand ? <div className="text-lg font-semibold">{brand}</div> : null}
|
|
123
|
+
{tagline ? <p className="max-w-sm text-pretty text-sm text-muted-foreground">{tagline}</p> : null}
|
|
124
|
+
</div>
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function Locales({ locales }) {
|
|
129
|
+
const [own, setOwn] = useState(() => Object.fromEntries((locales ?? []).map((l) => [l.id, l.defaultValue ?? l.options?.[0]?.value ?? ""])));
|
|
130
|
+
const uid = useId();
|
|
131
|
+
if (!locales?.length) return null;
|
|
132
|
+
return (
|
|
133
|
+
<div className="flex flex-wrap gap-3">
|
|
134
|
+
{locales.map((l) => {
|
|
135
|
+
const value = l.value ?? own[l.id];
|
|
136
|
+
const fid = `${uid}-${l.id}`;
|
|
137
|
+
return (
|
|
138
|
+
<Field key={l.id} id={fid} className="w-full min-w-0 sm:w-44">
|
|
139
|
+
<label htmlFor={fid} className="sr-only">
|
|
140
|
+
{l.label}
|
|
141
|
+
</label>
|
|
142
|
+
<Select
|
|
143
|
+
id={fid}
|
|
144
|
+
value={value}
|
|
145
|
+
options={l.options}
|
|
146
|
+
onChange={(e) => {
|
|
147
|
+
const v = e.target.value;
|
|
148
|
+
if (l.value === undefined) setOwn((prev) => ({ ...prev, [l.id]: v }));
|
|
149
|
+
l.onChange?.(v);
|
|
150
|
+
}}
|
|
151
|
+
/>
|
|
152
|
+
</Field>
|
|
153
|
+
);
|
|
154
|
+
})}
|
|
155
|
+
</div>
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function Newsletter({ newsletter, center }) {
|
|
160
|
+
const uid = useId();
|
|
161
|
+
const [email, setEmail] = useState("");
|
|
162
|
+
const [agreed, setAgreed] = useState(false);
|
|
163
|
+
const [error, setError] = useState(null);
|
|
164
|
+
const [status, setStatus] = useState("idle");
|
|
165
|
+
const [failure, setFailure] = useState(null);
|
|
166
|
+
const card = newsletter.look === "card";
|
|
167
|
+
|
|
168
|
+
async function submit(ev) {
|
|
169
|
+
ev.preventDefault();
|
|
170
|
+
if (status === "sending") return;
|
|
171
|
+
const value = email.trim();
|
|
172
|
+
let problem = null;
|
|
173
|
+
if (!value) problem = "Enter your email.";
|
|
174
|
+
else if (!EMAIL.test(value)) problem = "That email does not look right.";
|
|
175
|
+
else if (newsletter.consent && !agreed) problem = "Please agree before subscribing.";
|
|
176
|
+
setError(problem);
|
|
177
|
+
if (problem) {
|
|
178
|
+
document.getElementById(problem.startsWith("Please") ? `${uid}-consent` : `${uid}-email`)?.focus();
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
setStatus("sending");
|
|
182
|
+
setFailure(null);
|
|
183
|
+
let res;
|
|
184
|
+
try {
|
|
185
|
+
res = await newsletter.onSubscribe(value, { consent: newsletter.consent ? agreed : undefined });
|
|
186
|
+
} catch (err) {
|
|
187
|
+
res = { ok: false, error: err?.message };
|
|
188
|
+
}
|
|
189
|
+
if (res?.ok) setStatus("done");
|
|
190
|
+
else {
|
|
191
|
+
setStatus("idle");
|
|
192
|
+
setFailure(res?.error || "You were not subscribed. Try again in a minute.");
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<div
|
|
198
|
+
className={cx(
|
|
199
|
+
"grid min-w-0 content-start gap-3",
|
|
200
|
+
card && "rounded-xl border border-border bg-card p-6 text-card-foreground sm:p-8 md:grid-cols-2 md:items-center md:gap-8",
|
|
201
|
+
center && !card && "justify-items-center text-center",
|
|
202
|
+
)}
|
|
203
|
+
>
|
|
204
|
+
<div className="grid gap-1">
|
|
205
|
+
<h3 className={cx("font-semibold", card ? "text-lg" : "text-sm")}>{newsletter.title ?? "Subscribe"}</h3>
|
|
206
|
+
{newsletter.body ? <p className="max-w-sm text-pretty text-sm text-muted-foreground">{newsletter.body}</p> : null}
|
|
207
|
+
</div>
|
|
208
|
+
{status === "done" ? (
|
|
209
|
+
<Notice tone="success" title={newsletter.doneTitle ?? "You're subscribed"}>
|
|
210
|
+
{newsletter.doneBody ?? `We'll write to ${email.trim()}.`}
|
|
211
|
+
</Notice>
|
|
212
|
+
) : (
|
|
213
|
+
<form noValidate onSubmit={submit} className="grid w-full max-w-md gap-2" aria-busy={status === "sending" || undefined}>
|
|
214
|
+
<div className="flex flex-col gap-2 sm:flex-row sm:items-start">
|
|
215
|
+
<Field id={`${uid}-email`} className="min-w-0 flex-1" error={error && !error.startsWith("Please") ? error : undefined}>
|
|
216
|
+
<label htmlFor={`${uid}-email`} className="sr-only">
|
|
217
|
+
Email
|
|
218
|
+
</label>
|
|
219
|
+
<Input
|
|
220
|
+
id={`${uid}-email`}
|
|
221
|
+
type="email"
|
|
222
|
+
inputMode="email"
|
|
223
|
+
autoComplete="email"
|
|
224
|
+
placeholder={newsletter.placeholder ?? "you@company.com"}
|
|
225
|
+
value={email}
|
|
226
|
+
onChange={(e) => {
|
|
227
|
+
setEmail(e.target.value);
|
|
228
|
+
if (error) setError(null);
|
|
229
|
+
}}
|
|
230
|
+
invalid={!!error && !error.startsWith("Please")}
|
|
231
|
+
aria-describedby={error && !error.startsWith("Please") ? `${uid}-email-error` : undefined}
|
|
232
|
+
/>
|
|
233
|
+
</Field>
|
|
234
|
+
<Button type="submit" variant="primary" loading={status === "sending"} className="shrink-0">
|
|
235
|
+
{newsletter.label ?? "Subscribe"}
|
|
236
|
+
</Button>
|
|
237
|
+
</div>
|
|
238
|
+
{newsletter.consent ? (
|
|
239
|
+
<div className="grid gap-1 text-left">
|
|
240
|
+
<Checkbox
|
|
241
|
+
id={`${uid}-consent`}
|
|
242
|
+
checked={agreed}
|
|
243
|
+
onChange={(e) => {
|
|
244
|
+
setAgreed(e.target.checked);
|
|
245
|
+
if (error) setError(null);
|
|
246
|
+
}}
|
|
247
|
+
aria-invalid={error?.startsWith("Please") ? true : undefined}
|
|
248
|
+
label={newsletter.consent}
|
|
249
|
+
className="text-muted-foreground"
|
|
250
|
+
/>
|
|
251
|
+
{error?.startsWith("Please") ? (
|
|
252
|
+
<p role="alert" className="text-xs text-destructive">
|
|
253
|
+
{error}
|
|
254
|
+
</p>
|
|
255
|
+
) : null}
|
|
256
|
+
</div>
|
|
257
|
+
) : null}
|
|
258
|
+
{failure ? (
|
|
259
|
+
<Notice tone="danger" title="Not subscribed">
|
|
260
|
+
{failure}
|
|
261
|
+
</Notice>
|
|
262
|
+
) : null}
|
|
263
|
+
</form>
|
|
264
|
+
)}
|
|
265
|
+
</div>
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function Cta({ cta, center, onNavigate }) {
|
|
270
|
+
if (!cta) return null;
|
|
271
|
+
return (
|
|
272
|
+
<div className={cx("flex flex-col gap-4 md:flex-row md:items-center md:justify-between", center && "items-center text-center md:flex-col")}>
|
|
273
|
+
<div className="grid min-w-0 gap-1">
|
|
274
|
+
{cta.title ? <h3 className="text-balance text-2xl font-semibold tracking-tight">{cta.title}</h3> : null}
|
|
275
|
+
{cta.body ? <p className="max-w-xl text-pretty text-muted-foreground">{cta.body}</p> : null}
|
|
276
|
+
</div>
|
|
277
|
+
{cta.actions?.length ? (
|
|
278
|
+
<div className={cx("flex flex-wrap gap-3", center && "justify-center")}>
|
|
279
|
+
{cta.actions.map((a, i) => (
|
|
280
|
+
<Button
|
|
281
|
+
key={a.label ?? i}
|
|
282
|
+
variant={a.variant ?? (i === 0 ? "primary" : "secondary")}
|
|
283
|
+
icon={a.icon}
|
|
284
|
+
href={a.href}
|
|
285
|
+
onClick={(e) => {
|
|
286
|
+
a.onClick?.(e);
|
|
287
|
+
if (a.href) onNavigate?.(a, e);
|
|
288
|
+
}}
|
|
289
|
+
>
|
|
290
|
+
{a.label}
|
|
291
|
+
</Button>
|
|
292
|
+
))}
|
|
293
|
+
</div>
|
|
294
|
+
) : null}
|
|
295
|
+
</div>
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function Statement({ statement, center }) {
|
|
300
|
+
if (!statement) return null;
|
|
301
|
+
const text = typeof statement === "string" ? statement : statement.text;
|
|
302
|
+
const by = typeof statement === "string" ? null : statement.by;
|
|
303
|
+
return (
|
|
304
|
+
<figure className={cx("grid gap-3 border-b border-border pb-10", center && "justify-items-center text-center")}>
|
|
305
|
+
<blockquote className="max-w-4xl text-balance text-3xl font-semibold tracking-tight sm:text-5xl">{text}</blockquote>
|
|
306
|
+
{by ? <figcaption className="text-muted-foreground">{by}</figcaption> : null}
|
|
307
|
+
</figure>
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function Bottom({ legal, legalLinks, version, locales, onNavigate, center }) {
|
|
312
|
+
if (!legal && !legalLinks?.length && !version && !locales?.length) return null;
|
|
313
|
+
return (
|
|
314
|
+
<div
|
|
315
|
+
className={cx(
|
|
316
|
+
"flex flex-col gap-4 border-t border-border pt-8 text-sm md:flex-row md:items-center md:justify-between",
|
|
317
|
+
center && "items-center text-center md:flex-col",
|
|
318
|
+
)}
|
|
319
|
+
>
|
|
320
|
+
<div className={cx("flex min-w-0 flex-col gap-3 sm:flex-row sm:flex-wrap sm:items-center sm:gap-x-6", center && "items-center sm:justify-center")}>
|
|
321
|
+
{legal ? <p className="text-muted-foreground">{legal}</p> : null}
|
|
322
|
+
<LinkRow links={legalLinks} onNavigate={onNavigate} center={center} label="Legal" />
|
|
323
|
+
</div>
|
|
324
|
+
<div className={cx("flex flex-wrap items-center gap-3", center && "justify-center")}>
|
|
325
|
+
{version ? <Badge variant="code">{version}</Badge> : null}
|
|
326
|
+
<Locales locales={locales} />
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function Footer({
|
|
333
|
+
layout = "columns",
|
|
334
|
+
brand,
|
|
335
|
+
tagline,
|
|
336
|
+
links,
|
|
337
|
+
columns,
|
|
338
|
+
socials,
|
|
339
|
+
newsletter,
|
|
340
|
+
cta,
|
|
341
|
+
statement,
|
|
342
|
+
locales,
|
|
343
|
+
version,
|
|
344
|
+
legal,
|
|
345
|
+
legalLinks,
|
|
346
|
+
onNavigate,
|
|
347
|
+
label = "Site",
|
|
348
|
+
className,
|
|
349
|
+
}) {
|
|
350
|
+
const centered = layout === "centered";
|
|
351
|
+
const news = newsletter?.onSubscribe ? newsletter : null;
|
|
352
|
+
const newsCard = news?.look === "card";
|
|
353
|
+
|
|
354
|
+
let main;
|
|
355
|
+
if (centered) {
|
|
356
|
+
main = (
|
|
357
|
+
<div className="grid justify-items-center gap-6 text-center">
|
|
358
|
+
<Brand brand={brand} tagline={tagline} center />
|
|
359
|
+
<LinkRow links={links} onNavigate={onNavigate} center label={label} />
|
|
360
|
+
<Socials socials={socials} center />
|
|
361
|
+
{news && !newsCard ? <Newsletter newsletter={news} center /> : null}
|
|
362
|
+
</div>
|
|
363
|
+
);
|
|
364
|
+
} else if (layout === "simple") {
|
|
365
|
+
main = (
|
|
366
|
+
<div className="flex flex-col gap-6 md:flex-row md:items-center md:justify-between">
|
|
367
|
+
<div className="flex min-w-0 flex-col gap-6 md:flex-row md:items-center md:gap-10">
|
|
368
|
+
{brand ? <div className="shrink-0 text-lg font-semibold">{brand}</div> : null}
|
|
369
|
+
<LinkRow links={links} onNavigate={onNavigate} label={label} />
|
|
370
|
+
</div>
|
|
371
|
+
<Socials socials={socials} />
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
} else {
|
|
375
|
+
main = (
|
|
376
|
+
<div className={cx("grid gap-12", news && !newsCard ? "lg:grid-cols-[minmax(0,2fr)_minmax(0,3fr)_minmax(0,2fr)]" : "lg:grid-cols-[minmax(0,2fr)_minmax(0,5fr)]")}>
|
|
377
|
+
<div className="grid content-start gap-5">
|
|
378
|
+
<Brand brand={brand} tagline={tagline} />
|
|
379
|
+
<Socials socials={socials} />
|
|
380
|
+
<LinkRow links={links} onNavigate={onNavigate} label={label} />
|
|
381
|
+
</div>
|
|
382
|
+
<Columns columns={columns} onNavigate={onNavigate} />
|
|
383
|
+
{news && !newsCard ? <Newsletter newsletter={news} /> : null}
|
|
384
|
+
</div>
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
return (
|
|
389
|
+
<footer aria-label={label} className={cx("border-t border-border px-4 pt-14 pb-8 sm:px-6 sm:pt-16", className)}>
|
|
390
|
+
<div className="mx-auto grid w-full max-w-6xl gap-10">
|
|
391
|
+
<Statement statement={statement} center={centered} />
|
|
392
|
+
{main}
|
|
393
|
+
{newsCard ? <Newsletter newsletter={news} /> : null}
|
|
394
|
+
<Cta cta={cta} center={centered} onNavigate={onNavigate} />
|
|
395
|
+
<Bottom
|
|
396
|
+
legal={legal}
|
|
397
|
+
legalLinks={legalLinks}
|
|
398
|
+
version={version}
|
|
399
|
+
locales={locales}
|
|
400
|
+
onNavigate={onNavigate}
|
|
401
|
+
center={centered}
|
|
402
|
+
/>
|
|
403
|
+
</div>
|
|
404
|
+
</footer>
|
|
405
|
+
);
|
|
406
|
+
}
|