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,113 @@
|
|
|
1
|
+
# ShareDialog
|
|
2
|
+
|
|
3
|
+
Who can see a record, and how to let more people in. An invite field
|
|
4
|
+
that takes several emails at once (with an access level), the people who
|
|
5
|
+
already have access (each with a menu: can view, can edit, remove),
|
|
6
|
+
sharing switches ("Allow comments"), and a link with a copy button that
|
|
7
|
+
turns to a check. Each section shows when you give its data or callback.
|
|
8
|
+
`ShareDialog` puts it in the kit's `Sheet` (a dialog; a bottom sheet on a
|
|
9
|
+
phone); `SharePanel` is the same body inline, for a settings page.
|
|
10
|
+
|
|
11
|
+
Every change goes out through an async callback that returns `{ ok: true }`
|
|
12
|
+
or `{ ok: false, error }`. A role change or a switch shows at once and goes
|
|
13
|
+
back when the save fails, with the reason on screen.
|
|
14
|
+
|
|
15
|
+
```jsx
|
|
16
|
+
import { ShareDialog, SharePanel } from "../components/blocks/share-dialog"; // a Makersclaw app ("./blocks/share-dialog" from components/)
|
|
17
|
+
import { ShareDialog, SharePanel } from "@/components/makersclaw/blocks/share-dialog"; // a shadcn project
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
A client component: render it from a `"use client"` component.
|
|
21
|
+
|
|
22
|
+
## The data
|
|
23
|
+
|
|
24
|
+
`people` is who has access now: `[{ id?, name?, email, src?, role, roleLabel?, locked?, pending? }]`.
|
|
25
|
+
|
|
26
|
+
- `role` is one of `roles`' values. A role outside them (an owner) shows
|
|
27
|
+
as a badge, with `roleLabel` as its words.
|
|
28
|
+
- `locked: true` — no menu for this person (the owner, yourself).
|
|
29
|
+
- `pending: true` — invited, not joined yet: "Invite sent" or an
|
|
30
|
+
"Invited" badge.
|
|
31
|
+
- `src` — a photo; the kit's `Avatar` shows initials without one.
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
Both components take these. Everything is optional; a section shows only
|
|
36
|
+
when its data or callback is there.
|
|
37
|
+
|
|
38
|
+
- `people` — the list above. `[]` shows `empty`; leave it out to hide the
|
|
39
|
+
section.
|
|
40
|
+
- `roles` — the access levels, in order: `[{ value, label }]`. Default
|
|
41
|
+
`[{ value: "view", label: "Can view" }, { value: "edit", label: "Can edit" }]`.
|
|
42
|
+
- `onInvite(emails, role)` — shows the invite field. Async, `{ ok, error }`.
|
|
43
|
+
The field splits on commas, spaces and semicolons, drops duplicates and
|
|
44
|
+
people who already have access, and refuses anything that is not an
|
|
45
|
+
email before it calls you. Add the invited people to `people` yourself
|
|
46
|
+
(with `pending: true`). With two or more `roles`, a picker beside the
|
|
47
|
+
field chooses the role.
|
|
48
|
+
- `inviteRole` — every invite gets this role, and no picker shows.
|
|
49
|
+
- `onRoleChange(person, role)` — each person's access becomes a menu.
|
|
50
|
+
Async, `{ ok, error }`. Update `people` on success.
|
|
51
|
+
- `onRemove(person)` — "Remove access" in that menu. Async. Take the
|
|
52
|
+
person out of `people` on success.
|
|
53
|
+
- `link` — a URL to share: a read-only field with a copy button, then a
|
|
54
|
+
"Copy link" button. Copying uses the browser's clipboard. A blocked
|
|
55
|
+
clipboard shows a message.
|
|
56
|
+
- `onCopy(link)` — called after a copy (analytics, "link created"). Async
|
|
57
|
+
and optional; a failure shows.
|
|
58
|
+
- `previewHref` or `onPreview` — a "Preview" button beside "Copy link".
|
|
59
|
+
- `settings` — switches: `[{ id, label, description?, icon?, checked, disabled? }]`.
|
|
60
|
+
- `onSettingChange(id, next)` — async, `{ ok, error }`. The switch goes
|
|
61
|
+
back when it fails.
|
|
62
|
+
- `youId` — the `id` (or email) of the person looking: "(you)" after their name.
|
|
63
|
+
- `inviteLabel` · `invitePlaceholder` · `peopleLabel` · `linkLabel` ·
|
|
64
|
+
`empty` — the words. Defaults: "Invite by email", "Emails, separated by
|
|
65
|
+
commas", "People with access", "Share link", "Only you have access so far."
|
|
66
|
+
- `className` — on the panel.
|
|
67
|
+
|
|
68
|
+
`ShareDialog` also takes:
|
|
69
|
+
|
|
70
|
+
- `open` · `onClose()` — controlled. Esc, a press outside and the ✕ close it.
|
|
71
|
+
- `title` — default "Share". `description` — a line under it.
|
|
72
|
+
- `footer` — buttons under the body (a "Done").
|
|
73
|
+
|
|
74
|
+
## Example
|
|
75
|
+
|
|
76
|
+
```jsx
|
|
77
|
+
"use client";
|
|
78
|
+
import { useState } from "react";
|
|
79
|
+
import { Button } from "../components/ui";
|
|
80
|
+
import { ShareDialog } from "../components/blocks/share-dialog";
|
|
81
|
+
|
|
82
|
+
export function ShareProject({ project, members, invite, setRole, remove }) {
|
|
83
|
+
const [open, setOpen] = useState(false);
|
|
84
|
+
return (
|
|
85
|
+
<>
|
|
86
|
+
<Button icon="share" onClick={() => setOpen(true)}>Share</Button>
|
|
87
|
+
<ShareDialog
|
|
88
|
+
open={open}
|
|
89
|
+
onClose={() => setOpen(false)}
|
|
90
|
+
title={`Share “${project.name}”`}
|
|
91
|
+
people={members}
|
|
92
|
+
onInvite={(emails, role) => invite(project.id, emails, role)} // server actions returning { ok, error }
|
|
93
|
+
onRoleChange={(person, role) => setRole(project.id, person.id, role)}
|
|
94
|
+
onRemove={(person) => remove(project.id, person.id)}
|
|
95
|
+
link={`https://app.example.com/p/${project.id}`}
|
|
96
|
+
/>
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## What it will not do
|
|
103
|
+
|
|
104
|
+
- Send the invite email or create the access row: `onInvite` does.
|
|
105
|
+
- Search your directory as you type. The field takes addresses; for a
|
|
106
|
+
people picker, use the kit's `Combobox`.
|
|
107
|
+
- Make the link public or private by itself. Put that switch in `settings`
|
|
108
|
+
and change `link` from your callback.
|
|
109
|
+
- Show a person's access per page or field. It has one role per person.
|
|
110
|
+
|
|
111
|
+
## Credits
|
|
112
|
+
|
|
113
|
+
Ported from blocks.so (https://blocks.so, dialogs 08 and 09, MIT) and Creative Tim UI (https://github.com/creativetimofficial/ui, modals-03, MIT), rewritten on the Makersclaw kit.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "share-dialog",
|
|
3
|
+
"title": "ShareDialog",
|
|
4
|
+
"description": "Who can see a record and how to let more people in: invite by email (comma separated, with an access level), each person with their access in a menu (can view, can edit, remove), sharing switches, and a link with a copy button that turns to a check. A dialog, or the same body inline (SharePanel). Every change is an async callback; a failure shows and the old value comes back.",
|
|
5
|
+
"useWhen": "A record, project, document or workspace can be shared: an \"Invite\" or \"Share\" button, a members list with roles, a public link to copy.",
|
|
6
|
+
"tags": ["share", "invite", "members", "collaborators", "access", "permissions", "roles", "team", "link", "copy link", "dialog", "modal"],
|
|
7
|
+
"exports": ["ShareDialog", "SharePanel"],
|
|
8
|
+
"files": ["index.js", "share-dialog.jsx"],
|
|
9
|
+
"requires": []
|
|
10
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// ShareDialog, every variant — the gallery's and the smoke test's
|
|
4
|
+
// surface. Not installed into apps: `makersclaw add share-dialog` lays
|
|
5
|
+
// index.js and share-dialog.jsx only. People live in memory; turn on
|
|
6
|
+
// "Fail the next save" to watch a change go back with its reason.
|
|
7
|
+
import { useState } from "react";
|
|
8
|
+
import { Button, Card, Field, Select, Switch } from "../components/ui";
|
|
9
|
+
import { ShareDialog, SharePanel } from "../components/blocks/share-dialog";
|
|
10
|
+
|
|
11
|
+
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
12
|
+
|
|
13
|
+
const TEAM = [
|
|
14
|
+
{ id: "u1", name: "Amara Okafor", email: "amara@harbourline.co", role: "owner", roleLabel: "Owner", locked: true },
|
|
15
|
+
{ id: "u2", name: "Jonas Lindqvist", email: "jonas@harbourline.co", role: "edit" },
|
|
16
|
+
{ id: "u3", name: "Priya Raman", email: "priya@harbourline.co", role: "view" },
|
|
17
|
+
{ id: "u4", name: "Mateo Alvarez", email: "mateo@fieldnotes.studio", role: "view" },
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
const MEMBERS = [
|
|
21
|
+
{ id: "m1", name: "Amara Okafor", email: "amara@harbourline.co", role: "member" },
|
|
22
|
+
{ id: "m2", name: "Jonas Lindqvist", email: "jonas@harbourline.co", role: "member" },
|
|
23
|
+
{ id: "m3", name: "Priya Raman", email: "priya@harbourline.co", role: "admin" },
|
|
24
|
+
{ id: "m4", name: "Wen Zhao", email: "wen@harbourline.co", role: "member" },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
const WORKSPACE_ROLES = [
|
|
28
|
+
{ value: "member", label: "Member" },
|
|
29
|
+
{ value: "admin", label: "Admin" },
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const VARIANTS = [
|
|
33
|
+
{ key: "invite", label: "Invite members — roles as badges" },
|
|
34
|
+
{ key: "link", label: "Share & collaborate — link and switch" },
|
|
35
|
+
{ key: "full", label: "Share with new members — access menus" },
|
|
36
|
+
{ key: "empty", label: "Nobody else yet — inline panel" },
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
export default function ShareDialogDemo() {
|
|
40
|
+
const [key, setKey] = useState("full");
|
|
41
|
+
const [open, setOpen] = useState(false);
|
|
42
|
+
const [fail, setFail] = useState(false);
|
|
43
|
+
const [team, setTeam] = useState(TEAM);
|
|
44
|
+
const [members, setMembers] = useState(MEMBERS);
|
|
45
|
+
const [solo, setSolo] = useState([]);
|
|
46
|
+
const [comments, setComments] = useState(true);
|
|
47
|
+
const [log, setLog] = useState(null);
|
|
48
|
+
|
|
49
|
+
// One fake server: waits, then fails once when asked to.
|
|
50
|
+
async function save(what, apply) {
|
|
51
|
+
await wait(600);
|
|
52
|
+
if (fail) {
|
|
53
|
+
setFail(false);
|
|
54
|
+
return { ok: false, error: "The server did not answer. Try again." };
|
|
55
|
+
}
|
|
56
|
+
apply?.();
|
|
57
|
+
setLog(what);
|
|
58
|
+
return { ok: true };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const invite = (list, setList) => (emails, role) =>
|
|
62
|
+
save(`invited ${emails.join(", ")} as ${role}`, () =>
|
|
63
|
+
setList((xs) => [...xs, ...emails.map((email) => ({ id: email, email, role, pending: true }))]),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const variantProps = {
|
|
67
|
+
invite: {
|
|
68
|
+
title: "Invite members",
|
|
69
|
+
description: "Add people to the Harbourline workspace. Check your company's policy before inviting anyone outside it.",
|
|
70
|
+
people: members,
|
|
71
|
+
roles: WORKSPACE_ROLES,
|
|
72
|
+
inviteRole: "member",
|
|
73
|
+
onInvite: invite(members, setMembers),
|
|
74
|
+
invitePlaceholder: "name@company.com",
|
|
75
|
+
inviteLabel: "Email",
|
|
76
|
+
peopleLabel: "People with existing access",
|
|
77
|
+
},
|
|
78
|
+
link: {
|
|
79
|
+
title: "Share & collaborate",
|
|
80
|
+
description: "Anyone with the link can open the Q4 launch plan.",
|
|
81
|
+
link: "https://harbourline.app/p/q4-launch-plan?share=8fk2",
|
|
82
|
+
previewHref: "#preview",
|
|
83
|
+
onCopy: (l) => save(`copied ${l}`),
|
|
84
|
+
settings: [{ id: "comments", label: "Allow comments and suggestions", icon: "message-circle", checked: comments }],
|
|
85
|
+
onSettingChange: (id, next) => save(`${id} → ${next ? "on" : "off"}`, () => setComments(next)),
|
|
86
|
+
},
|
|
87
|
+
full: {
|
|
88
|
+
title: "Share with new members",
|
|
89
|
+
description: "Give teammates and outside collaborators access to the Q4 launch plan.",
|
|
90
|
+
people: team,
|
|
91
|
+
youId: "u1",
|
|
92
|
+
onInvite: invite(team, setTeam),
|
|
93
|
+
onRoleChange: (p, role) => save(`${p.email} → ${role}`, () => setTeam((xs) => xs.map((x) => (x.id === p.id ? { ...x, role } : x)))),
|
|
94
|
+
onRemove: (p) => save(`removed ${p.email}`, () => setTeam((xs) => xs.filter((x) => x.id !== p.id))),
|
|
95
|
+
link: "https://harbourline.app/p/q4-launch-plan",
|
|
96
|
+
onCopy: (l) => save(`copied ${l}`),
|
|
97
|
+
},
|
|
98
|
+
empty: {
|
|
99
|
+
people: solo,
|
|
100
|
+
onInvite: invite(solo, setSolo),
|
|
101
|
+
onRoleChange: (p, role) => save(`${p.email} → ${role}`, () => setSolo((xs) => xs.map((x) => (x.id === p.id ? { ...x, role } : x)))),
|
|
102
|
+
onRemove: (p) => save(`removed ${p.email}`, () => setSolo((xs) => xs.filter((x) => x.id !== p.id))),
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
const props = variantProps[key];
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<div className="grid gap-6">
|
|
109
|
+
<div className="flex flex-wrap items-end gap-x-6 gap-y-2">
|
|
110
|
+
<Field id="share-variant" label="Variant" className="w-full sm:w-80">
|
|
111
|
+
<Select id="share-variant" value={key} onChange={(e) => setKey(e.target.value)} options={VARIANTS.map((v) => ({ value: v.key, label: v.label }))} />
|
|
112
|
+
</Field>
|
|
113
|
+
<Switch id="share-fail" label="Fail the next save" checked={fail} onChange={(e) => setFail(e.target.checked)} />
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
{key === "empty" ? (
|
|
117
|
+
<Card title="Sharing" description="Nobody else can see the Q4 launch plan yet." className="max-w-xl">
|
|
118
|
+
<SharePanel key={key} {...props} />
|
|
119
|
+
</Card>
|
|
120
|
+
) : (
|
|
121
|
+
<>
|
|
122
|
+
<div>
|
|
123
|
+
<Button variant="primary" icon={key === "invite" ? "user-plus" : "share"} onClick={() => setOpen(true)}>
|
|
124
|
+
{key === "invite" ? "Invite members" : "Share"}
|
|
125
|
+
</Button>
|
|
126
|
+
</div>
|
|
127
|
+
<ShareDialog key={key} open={open} onClose={() => setOpen(false)} {...props} />
|
|
128
|
+
{/* The same body inline, so the page shows it without opening the dialog. */}
|
|
129
|
+
<Card title={props.title} description={props.description} className="max-w-lg">
|
|
130
|
+
<SharePanel {...props} />
|
|
131
|
+
</Card>
|
|
132
|
+
</>
|
|
133
|
+
)}
|
|
134
|
+
|
|
135
|
+
<p className="min-h-5 break-all text-xs text-muted-foreground" aria-live="polite">
|
|
136
|
+
{log ? `Saved — ${log}` : "Invite, change access or copy the link to see what the callbacks receive."}
|
|
137
|
+
</p>
|
|
138
|
+
</div>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useEffect, useId, useRef, useState } from "react";
|
|
4
|
+
import { Avatar, Badge, Button, Icon, InputGroup, Item, Menu, Notice, Select, Sheet, Switch, cx } from "../../ui";
|
|
5
|
+
|
|
6
|
+
// ShareDialog — who can see a thing, and how to let more people in:
|
|
7
|
+
// invite by email, each person's access, a link to copy, a few sharing
|
|
8
|
+
// switches. SharePanel is the same body without the dialog, for a
|
|
9
|
+
// settings page. Every change goes out through an async callback that
|
|
10
|
+
// returns { ok } or { ok: false, error }; the dialog shows the failure
|
|
11
|
+
// and puts the old value back. See README.md beside this file.
|
|
12
|
+
|
|
13
|
+
const DEFAULT_ROLES = [
|
|
14
|
+
{ value: "view", label: "Can view" },
|
|
15
|
+
{ value: "edit", label: "Can edit" },
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
const EMAIL = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
19
|
+
|
|
20
|
+
function failure(result) {
|
|
21
|
+
return result && result.ok === false ? result.error || "That did not save. Try again." : null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function attempt(fn) {
|
|
25
|
+
try {
|
|
26
|
+
return failure(await fn());
|
|
27
|
+
} catch (e) {
|
|
28
|
+
return e?.message || String(e);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** "a@x.com, b@y.com; c@z.com" → { good: [...], bad: [...] }, deduped. */
|
|
33
|
+
function parseEmails(text) {
|
|
34
|
+
const all = String(text)
|
|
35
|
+
.split(/[\s,;]+/)
|
|
36
|
+
.map((s) => s.trim())
|
|
37
|
+
.filter(Boolean);
|
|
38
|
+
const seen = new Set();
|
|
39
|
+
const good = [];
|
|
40
|
+
const bad = [];
|
|
41
|
+
for (const e of all) {
|
|
42
|
+
const k = e.toLowerCase();
|
|
43
|
+
if (seen.has(k)) continue;
|
|
44
|
+
seen.add(k);
|
|
45
|
+
(EMAIL.test(e) ? good : bad).push(e);
|
|
46
|
+
}
|
|
47
|
+
return { good, bad };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function InviteRow({ onInvite, roles, inviteRole, placeholder, people, label }) {
|
|
51
|
+
const id = useId();
|
|
52
|
+
const [text, setText] = useState("");
|
|
53
|
+
const [role, setRole] = useState(inviteRole ?? roles[0]?.value);
|
|
54
|
+
const [busy, setBusy] = useState(false);
|
|
55
|
+
const [error, setError] = useState(null);
|
|
56
|
+
const [note, setNote] = useState(null);
|
|
57
|
+
|
|
58
|
+
async function submit(e) {
|
|
59
|
+
e.preventDefault();
|
|
60
|
+
if (busy) return;
|
|
61
|
+
setNote(null);
|
|
62
|
+
const { good, bad } = parseEmails(text);
|
|
63
|
+
if (!good.length && !bad.length) {
|
|
64
|
+
setError("Type at least one email address.");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (bad.length) {
|
|
68
|
+
setError(`${bad.join(", ")} ${bad.length === 1 ? "is not an email address" : "are not email addresses"}.`);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const have = new Set((people ?? []).map((p) => String(p.email ?? "").toLowerCase()));
|
|
72
|
+
const fresh = good.filter((e) => !have.has(e.toLowerCase()));
|
|
73
|
+
if (!fresh.length) {
|
|
74
|
+
setError(good.length === 1 ? `${good[0]} already has access.` : "Everyone listed already has access.");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
setError(null);
|
|
78
|
+
setBusy(true);
|
|
79
|
+
const failed = await attempt(() => onInvite(fresh, role));
|
|
80
|
+
setBusy(false);
|
|
81
|
+
if (failed) {
|
|
82
|
+
setError(failed);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
setText("");
|
|
86
|
+
setNote(fresh.length === 1 ? `Invited ${fresh[0]}.` : `Invited ${fresh.length} people.`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const picker =
|
|
90
|
+
roles.length > 1 && !inviteRole ? (
|
|
91
|
+
<Select
|
|
92
|
+
aria-label="Access for the people you invite"
|
|
93
|
+
value={role}
|
|
94
|
+
onChange={(e) => setRole(e.target.value)}
|
|
95
|
+
options={roles}
|
|
96
|
+
disabled={busy}
|
|
97
|
+
className="sm:w-36"
|
|
98
|
+
/>
|
|
99
|
+
) : null;
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<form onSubmit={submit} className="grid gap-2" noValidate>
|
|
103
|
+
<label htmlFor={id} className="text-sm font-medium text-foreground">
|
|
104
|
+
{label}
|
|
105
|
+
</label>
|
|
106
|
+
<div className="flex flex-col gap-2 sm:flex-row">
|
|
107
|
+
<div className="min-w-0 flex-1">
|
|
108
|
+
<InputGroup
|
|
109
|
+
id={id}
|
|
110
|
+
icon="user-plus"
|
|
111
|
+
type="text"
|
|
112
|
+
inputMode="email"
|
|
113
|
+
autoComplete="off"
|
|
114
|
+
placeholder={placeholder}
|
|
115
|
+
value={text}
|
|
116
|
+
onChange={(e) => {
|
|
117
|
+
setText(e.target.value);
|
|
118
|
+
if (error) setError(null);
|
|
119
|
+
}}
|
|
120
|
+
invalid={Boolean(error)}
|
|
121
|
+
aria-describedby={error ? `${id}-error` : note ? `${id}-note` : undefined}
|
|
122
|
+
disabled={busy}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
{picker}
|
|
126
|
+
<Button type="submit" variant="primary" loading={busy}>
|
|
127
|
+
Invite
|
|
128
|
+
</Button>
|
|
129
|
+
</div>
|
|
130
|
+
{error ? (
|
|
131
|
+
<p id={`${id}-error`} role="alert" className="text-xs text-destructive">
|
|
132
|
+
{error}
|
|
133
|
+
</p>
|
|
134
|
+
) : note ? (
|
|
135
|
+
<p id={`${id}-note`} role="status" className="text-xs text-muted-foreground">
|
|
136
|
+
{note}
|
|
137
|
+
</p>
|
|
138
|
+
) : null}
|
|
139
|
+
</form>
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function PersonRow({ person, roles, onRoleChange, onRemove, onError, you }) {
|
|
144
|
+
const [role, setRole] = useState(person.role);
|
|
145
|
+
const [busy, setBusy] = useState(false);
|
|
146
|
+
useEffect(() => setRole(person.role), [person.role]);
|
|
147
|
+
|
|
148
|
+
const known = roles.find((r) => r.value === role);
|
|
149
|
+
const editable = !person.locked && known && (onRoleChange || onRemove);
|
|
150
|
+
|
|
151
|
+
async function change(next) {
|
|
152
|
+
if (next === role || busy) return;
|
|
153
|
+
const before = role;
|
|
154
|
+
setRole(next); // shows at once; goes back if the save fails
|
|
155
|
+
setBusy(true);
|
|
156
|
+
const failed = await attempt(() => onRoleChange(person, next));
|
|
157
|
+
setBusy(false);
|
|
158
|
+
if (failed) {
|
|
159
|
+
setRole(before);
|
|
160
|
+
onError(`${person.name ?? person.email}: ${failed}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function remove() {
|
|
165
|
+
setBusy(true);
|
|
166
|
+
const failed = await attempt(() => onRemove(person));
|
|
167
|
+
setBusy(false);
|
|
168
|
+
if (failed) onError(`${person.name ?? person.email} still has access: ${failed}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const roleLabel = known?.label ?? person.roleLabel ?? String(role ?? "");
|
|
172
|
+
|
|
173
|
+
const items = editable
|
|
174
|
+
? [
|
|
175
|
+
onRoleChange && { type: "radio", label: "Access", value: role, onValueChange: change, options: roles },
|
|
176
|
+
onRoleChange && onRemove && { type: "separator" },
|
|
177
|
+
onRemove && { label: "Remove access", icon: "trash", tone: "danger", onSelect: remove },
|
|
178
|
+
]
|
|
179
|
+
: [];
|
|
180
|
+
|
|
181
|
+
const meta = editable ? (
|
|
182
|
+
<Menu
|
|
183
|
+
label={`${roleLabel}, access for ${person.name ?? person.email}`}
|
|
184
|
+
trigger={
|
|
185
|
+
<span className="inline-flex items-center gap-1 text-sm">
|
|
186
|
+
{busy ? <span className="text-muted-foreground">Saving…</span> : roleLabel}
|
|
187
|
+
<Icon name="chevron-down" size={14} className="text-muted-foreground" />
|
|
188
|
+
</span>
|
|
189
|
+
}
|
|
190
|
+
items={items}
|
|
191
|
+
/>
|
|
192
|
+
) : (
|
|
193
|
+
<Badge variant="outline">{roleLabel}</Badge>
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<li>
|
|
198
|
+
<Item
|
|
199
|
+
size="sm"
|
|
200
|
+
media={<Avatar name={person.name ?? person.email} src={person.src} size="sm" />}
|
|
201
|
+
title={
|
|
202
|
+
<>
|
|
203
|
+
{person.name ?? person.email}
|
|
204
|
+
{you ? <span className="font-normal text-muted-foreground"> (you)</span> : null}
|
|
205
|
+
</>
|
|
206
|
+
}
|
|
207
|
+
description={person.name ? person.email : person.pending ? "Invite sent" : undefined}
|
|
208
|
+
meta={person.pending && person.name ? <Badge variant="soft">Invited</Badge> : undefined}
|
|
209
|
+
actions={meta}
|
|
210
|
+
className={cx(busy && "opacity-70")}
|
|
211
|
+
/>
|
|
212
|
+
</li>
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function LinkRow({ link, onCopy, onPreview, previewHref, label }) {
|
|
217
|
+
const id = useId();
|
|
218
|
+
const [copied, setCopied] = useState(false);
|
|
219
|
+
const [error, setError] = useState(null);
|
|
220
|
+
const timer = useRef(null);
|
|
221
|
+
useEffect(() => () => clearTimeout(timer.current), []);
|
|
222
|
+
|
|
223
|
+
async function copy() {
|
|
224
|
+
setError(null);
|
|
225
|
+
let failed = null;
|
|
226
|
+
try {
|
|
227
|
+
if (!navigator.clipboard?.writeText) throw new Error("This browser blocks copying. Select the link and copy it by hand.");
|
|
228
|
+
await navigator.clipboard.writeText(link);
|
|
229
|
+
failed = await attempt(() => onCopy?.(link));
|
|
230
|
+
} catch (e) {
|
|
231
|
+
failed = e?.message || "Copying failed. Select the link and copy it by hand.";
|
|
232
|
+
}
|
|
233
|
+
if (failed) {
|
|
234
|
+
setError(failed);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
setCopied(true);
|
|
238
|
+
clearTimeout(timer.current);
|
|
239
|
+
timer.current = setTimeout(() => setCopied(false), 1600);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div className="grid gap-2">
|
|
244
|
+
<label htmlFor={id} className="text-sm font-medium text-foreground">
|
|
245
|
+
{label}
|
|
246
|
+
</label>
|
|
247
|
+
<InputGroup
|
|
248
|
+
id={id}
|
|
249
|
+
icon="link"
|
|
250
|
+
readOnly
|
|
251
|
+
value={link}
|
|
252
|
+
onFocus={(e) => e.target.select()}
|
|
253
|
+
action={{ label: copied ? "Copied" : "Copy link", icon: copied ? "check" : "copy", onClick: copy }}
|
|
254
|
+
/>
|
|
255
|
+
<p role="status" className={cx("text-xs", error ? "text-destructive" : "text-muted-foreground", !error && !copied && "sr-only")}>
|
|
256
|
+
{error ?? (copied ? "Link copied." : "")}
|
|
257
|
+
</p>
|
|
258
|
+
<div className="flex flex-col gap-2 sm:flex-row">
|
|
259
|
+
<Button variant="secondary" icon={copied ? "check" : "copy"} onClick={copy} className="sm:flex-1">
|
|
260
|
+
{copied ? "Copied" : "Copy link"}
|
|
261
|
+
</Button>
|
|
262
|
+
{previewHref || onPreview ? (
|
|
263
|
+
<Button variant="secondary" icon="external" href={previewHref} onClick={onPreview} target={previewHref ? "_blank" : undefined} rel={previewHref ? "noreferrer" : undefined} className="sm:flex-1">
|
|
264
|
+
Preview
|
|
265
|
+
</Button>
|
|
266
|
+
) : null}
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function SettingRow({ setting, onSettingChange, onError }) {
|
|
273
|
+
const id = useId();
|
|
274
|
+
const [on, setOn] = useState(Boolean(setting.checked));
|
|
275
|
+
const [busy, setBusy] = useState(false);
|
|
276
|
+
useEffect(() => setOn(Boolean(setting.checked)), [setting.checked]);
|
|
277
|
+
|
|
278
|
+
async function toggle(e) {
|
|
279
|
+
const next = e.target.checked;
|
|
280
|
+
setOn(next);
|
|
281
|
+
setBusy(true);
|
|
282
|
+
const failed = await attempt(() => onSettingChange?.(setting.id, next));
|
|
283
|
+
setBusy(false);
|
|
284
|
+
if (failed) {
|
|
285
|
+
setOn(!next);
|
|
286
|
+
onError(`${setting.label}: ${failed}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return (
|
|
291
|
+
<div className="flex items-center justify-between gap-4 rounded-md border border-border px-3">
|
|
292
|
+
<div className="min-w-0 py-2">
|
|
293
|
+
<label htmlFor={id} className="flex items-center gap-2 text-sm font-medium">
|
|
294
|
+
{setting.icon ? <Icon name={setting.icon} size={16} className="shrink-0 text-muted-foreground" /> : null}
|
|
295
|
+
{setting.label}
|
|
296
|
+
</label>
|
|
297
|
+
{setting.description ? <p className="text-xs text-muted-foreground">{setting.description}</p> : null}
|
|
298
|
+
</div>
|
|
299
|
+
<Switch id={id} checked={on} onChange={toggle} disabled={busy || setting.disabled} />
|
|
300
|
+
</div>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/** The body: invite, people, settings, link — each shows when its data or
|
|
305
|
+
* callback is given. */
|
|
306
|
+
export function SharePanel({
|
|
307
|
+
people,
|
|
308
|
+
roles = DEFAULT_ROLES,
|
|
309
|
+
onInvite,
|
|
310
|
+
inviteRole,
|
|
311
|
+
onRoleChange,
|
|
312
|
+
onRemove,
|
|
313
|
+
link,
|
|
314
|
+
onCopy,
|
|
315
|
+
onPreview,
|
|
316
|
+
previewHref,
|
|
317
|
+
settings,
|
|
318
|
+
onSettingChange,
|
|
319
|
+
youId,
|
|
320
|
+
inviteLabel = "Invite by email",
|
|
321
|
+
invitePlaceholder = "Emails, separated by commas",
|
|
322
|
+
peopleLabel = "People with access",
|
|
323
|
+
linkLabel = "Share link",
|
|
324
|
+
empty = "Only you have access so far.",
|
|
325
|
+
className,
|
|
326
|
+
}) {
|
|
327
|
+
const [error, setError] = useState(null);
|
|
328
|
+
const list = people ?? null;
|
|
329
|
+
|
|
330
|
+
return (
|
|
331
|
+
<div className={cx("grid gap-5", className)}>
|
|
332
|
+
{onInvite ? <InviteRow onInvite={onInvite} roles={roles} inviteRole={inviteRole} placeholder={invitePlaceholder} people={list} label={inviteLabel} /> : null}
|
|
333
|
+
|
|
334
|
+
{settings?.length ? (
|
|
335
|
+
<div className="grid gap-2">
|
|
336
|
+
{settings.map((s) => (
|
|
337
|
+
<SettingRow key={s.id} setting={s} onSettingChange={onSettingChange} onError={setError} />
|
|
338
|
+
))}
|
|
339
|
+
</div>
|
|
340
|
+
) : null}
|
|
341
|
+
|
|
342
|
+
{list ? (
|
|
343
|
+
<section className="grid gap-1" aria-label={peopleLabel}>
|
|
344
|
+
<h3 className="text-sm font-medium">{peopleLabel}</h3>
|
|
345
|
+
{list.length ? (
|
|
346
|
+
<ul className="grid divide-y divide-border">
|
|
347
|
+
{list.map((p) => (
|
|
348
|
+
<PersonRow
|
|
349
|
+
key={p.id ?? p.email}
|
|
350
|
+
person={p}
|
|
351
|
+
roles={roles}
|
|
352
|
+
onRoleChange={onRoleChange}
|
|
353
|
+
onRemove={onRemove}
|
|
354
|
+
onError={setError}
|
|
355
|
+
you={youId != null && (p.id ?? p.email) === youId}
|
|
356
|
+
/>
|
|
357
|
+
))}
|
|
358
|
+
</ul>
|
|
359
|
+
) : (
|
|
360
|
+
<p className="py-3 text-sm text-muted-foreground">{empty}</p>
|
|
361
|
+
)}
|
|
362
|
+
</section>
|
|
363
|
+
) : null}
|
|
364
|
+
|
|
365
|
+
{error ? (
|
|
366
|
+
<Notice tone="danger" onDismiss={() => setError(null)}>
|
|
367
|
+
{error}
|
|
368
|
+
</Notice>
|
|
369
|
+
) : null}
|
|
370
|
+
|
|
371
|
+
{link ? <LinkRow link={link} onCopy={onCopy} onPreview={onPreview} previewHref={previewHref} label={linkLabel} /> : null}
|
|
372
|
+
</div>
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/** SharePanel in the kit's Sheet: a dialog on a wide window, a bottom
|
|
377
|
+
* sheet on a phone. Controlled: `open` + `onClose`. */
|
|
378
|
+
export function ShareDialog({ open, onClose, title = "Share", description, footer, className, ...panel }) {
|
|
379
|
+
return (
|
|
380
|
+
<Sheet open={open} onClose={onClose} side="center" title={title} description={description} footer={footer} className={className}>
|
|
381
|
+
{open ? <SharePanel {...panel} /> : null}
|
|
382
|
+
</Sheet>
|
|
383
|
+
);
|
|
384
|
+
}
|