makersclaw 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/THIRD-PARTY-NOTICES.md +180 -0
- package/package.json +3 -2
- package/r/annotate.json +62 -0
- package/r/approval-request.json +52 -0
- package/r/audio-player.json +74 -0
- package/r/auth-form.json +55 -0
- package/r/board.json +10 -6
- package/r/calendar-view.json +105 -0
- package/r/cart.json +62 -0
- package/r/chart.json +83 -0
- package/r/chat.json +95 -0
- package/r/code-editor.json +94 -0
- package/r/comment-box.json +64 -0
- package/r/contact-section.json +53 -0
- package/r/core.json +205 -8
- package/r/csv-import.json +63 -0
- package/r/data-table.json +69 -0
- package/r/diagram.json +74 -0
- package/r/doc-editor.json +82 -0
- package/r/doc-page.json +61 -0
- package/r/email-render.json +73 -0
- package/r/faq.json +50 -0
- package/r/file-upload.json +54 -0
- package/r/flow-canvas.json +93 -0
- package/r/footer.json +52 -0
- package/r/gantt.json +70 -0
- package/r/job-panel.json +12 -7
- package/r/json-editor.json +66 -0
- package/r/map.json +89 -0
- package/r/menubar.json +52 -0
- package/r/mind-map.json +67 -0
- package/r/navigation-menu.json +52 -0
- package/r/order-detail.json +54 -0
- package/r/org-chart.json +66 -0
- package/r/page-builder.json +105 -0
- package/r/payment-methods.json +51 -0
- package/r/pdf-document.json +79 -0
- package/r/pdf-viewer.json +88 -0
- package/r/product-detail.json +76 -0
- package/r/product-grid.json +65 -0
- package/r/record-form.json +67 -0
- package/r/record-notes.json +55 -0
- package/r/registry.json +3411 -29
- package/r/search-dialog.json +51 -0
- package/r/settings-list.json +66 -0
- package/r/setup-checklist.json +51 -0
- package/r/share-dialog.json +52 -0
- package/r/sidebar.json +69 -0
- package/r/signature-pad.json +72 -0
- package/r/sketch-canvas.json +99 -0
- package/r/spreadsheet.json +90 -0
- package/r/stat-cards.json +66 -0
- package/r/task-list.json +63 -0
- package/r/task-model.json +98 -0
- package/r/testimonials.json +52 -0
- package/r/transaction-list.json +53 -0
- package/r/whiteboard.json +66 -0
- package/registry/blocks/annotate/README.md +179 -0
- package/registry/blocks/annotate/annotate-data.js +250 -0
- package/registry/blocks/annotate/annotate.jsx +646 -0
- package/registry/blocks/annotate/block.json +10 -0
- package/registry/blocks/annotate/demo.jsx +135 -0
- package/registry/blocks/annotate/index.js +15 -0
- package/registry/blocks/approval-request/README.md +116 -0
- package/registry/blocks/approval-request/approval-request.jsx +386 -0
- package/registry/blocks/approval-request/block.json +10 -0
- package/registry/blocks/approval-request/demo.jsx +127 -0
- package/registry/blocks/approval-request/index.js +4 -0
- package/registry/blocks/audio-player/README.md +167 -0
- package/registry/blocks/audio-player/audio-player.jsx +623 -0
- package/registry/blocks/audio-player/block.json +14 -0
- package/registry/blocks/audio-player/colors.js +109 -0
- package/registry/blocks/audio-player/demo.jsx +209 -0
- package/registry/blocks/audio-player/index.js +6 -0
- package/registry/blocks/audio-player/time.js +67 -0
- package/registry/blocks/audio-player/wavesurfer.js +9 -0
- package/registry/blocks/auth-form/README.md +126 -0
- package/registry/blocks/auth-form/auth-form.jsx +568 -0
- package/registry/blocks/auth-form/block.json +10 -0
- package/registry/blocks/auth-form/demo.jsx +131 -0
- package/registry/blocks/auth-form/index.js +3 -0
- package/registry/blocks/board/README.md +55 -5
- package/registry/blocks/board/block.json +2 -2
- package/registry/blocks/board/board.jsx +230 -86
- package/registry/blocks/board/demo.jsx +103 -24
- package/registry/blocks/calendar-view/README.md +180 -0
- package/registry/blocks/calendar-view/agenda.jsx +147 -0
- package/registry/blocks/calendar-view/block.json +76 -0
- package/registry/blocks/calendar-view/calendar-model.js +442 -0
- package/registry/blocks/calendar-view/calendar.jsx +627 -0
- package/registry/blocks/calendar-view/demo.jsx +195 -0
- package/registry/blocks/calendar-view/draft.jsx +49 -0
- package/registry/blocks/calendar-view/index.js +39 -0
- package/registry/blocks/calendar-view/month.jsx +346 -0
- package/registry/blocks/calendar-view/paint.js +69 -0
- package/registry/blocks/calendar-view/time-grid.jsx +527 -0
- package/registry/blocks/calendar-view/tray.jsx +87 -0
- package/registry/blocks/cart/README.md +146 -0
- package/registry/blocks/cart/block.json +10 -0
- package/registry/blocks/cart/cart.jsx +597 -0
- package/registry/blocks/cart/demo.jsx +98 -0
- package/registry/blocks/cart/index.js +4 -0
- package/registry/blocks/cart/money.js +28 -0
- package/registry/blocks/chart/README.md +316 -0
- package/registry/blocks/chart/block.json +10 -0
- package/registry/blocks/chart/cartesian.jsx +642 -0
- package/registry/blocks/chart/core.jsx +262 -0
- package/registry/blocks/chart/demo.jsx +357 -0
- package/registry/blocks/chart/heatmap.jsx +336 -0
- package/registry/blocks/chart/index.js +8 -0
- package/registry/blocks/chart/pie.jsx +245 -0
- package/registry/blocks/chart/sparkline.jsx +88 -0
- package/registry/blocks/chat/README.md +273 -0
- package/registry/blocks/chat/attachment.jsx +95 -0
- package/registry/blocks/chat/block.json +10 -0
- package/registry/blocks/chat/chat.jsx +132 -0
- package/registry/blocks/chat/composer.jsx +364 -0
- package/registry/blocks/chat/demo.jsx +578 -0
- package/registry/blocks/chat/header.jsx +34 -0
- package/registry/blocks/chat/index.js +9 -0
- package/registry/blocks/chat/message.jsx +450 -0
- package/registry/blocks/chat/scroller.jsx +247 -0
- package/registry/blocks/chat/text.jsx +104 -0
- package/registry/blocks/code-editor/README.md +210 -0
- package/registry/blocks/code-editor/block.json +22 -0
- package/registry/blocks/code-editor/code-editor.jsx +534 -0
- package/registry/blocks/code-editor/codemirror.js +38 -0
- package/registry/blocks/code-editor/demo.jsx +205 -0
- package/registry/blocks/code-editor/index.js +8 -0
- package/registry/blocks/code-editor/json.js +317 -0
- package/registry/blocks/code-editor/sql.js +136 -0
- package/registry/blocks/code-editor/text.js +101 -0
- package/registry/blocks/code-editor/theme.js +186 -0
- package/registry/blocks/comment-box/README.md +123 -0
- package/registry/blocks/comment-box/block.json +10 -0
- package/registry/blocks/comment-box/comment-body.jsx +217 -0
- package/registry/blocks/comment-box/comment-box.jsx +198 -0
- package/registry/blocks/comment-box/comment-time.jsx +77 -0
- package/registry/blocks/comment-box/demo.jsx +141 -0
- package/registry/blocks/comment-box/index.js +8 -0
- package/registry/blocks/contact-section/README.md +152 -0
- package/registry/blocks/contact-section/block.json +11 -0
- package/registry/blocks/contact-section/contact-section.jsx +509 -0
- package/registry/blocks/contact-section/demo.jsx +93 -0
- package/registry/blocks/contact-section/index.js +4 -0
- package/registry/blocks/csv-import/README.md +133 -0
- package/registry/blocks/csv-import/block.json +13 -0
- package/registry/blocks/csv-import/csv-import.jsx +479 -0
- package/registry/blocks/csv-import/demo.jsx +106 -0
- package/registry/blocks/csv-import/import.js +200 -0
- package/registry/blocks/csv-import/index.js +7 -0
- package/registry/blocks/data-table/README.md +386 -0
- package/registry/blocks/data-table/block.json +10 -0
- package/registry/blocks/data-table/data-table.jsx +1164 -0
- package/registry/blocks/data-table/demo.jsx +588 -0
- package/registry/blocks/data-table/index.js +5 -0
- package/registry/blocks/data-table/model.js +169 -0
- package/registry/blocks/diagram/README.md +108 -0
- package/registry/blocks/diagram/block.json +13 -0
- package/registry/blocks/diagram/demo.jsx +113 -0
- package/registry/blocks/diagram/diagram.jsx +481 -0
- package/registry/blocks/diagram/index.js +7 -0
- package/registry/blocks/diagram/mermaid.js +52 -0
- package/registry/blocks/diagram/palette.js +313 -0
- package/registry/blocks/diagram/source.js +98 -0
- package/registry/blocks/doc-editor/README.md +215 -0
- package/registry/blocks/doc-editor/block.json +15 -0
- package/registry/blocks/doc-editor/demo.jsx +225 -0
- package/registry/blocks/doc-editor/doc-editor.jsx +291 -0
- package/registry/blocks/doc-editor/doc.js +309 -0
- package/registry/blocks/doc-editor/index.js +7 -0
- package/registry/blocks/doc-editor/schema.jsx +56 -0
- package/registry/blocks/doc-editor/shadcn-classes.js +110 -0
- package/registry/blocks/doc-editor/theme.js +82 -0
- package/registry/blocks/doc-page/README.md +119 -0
- package/registry/blocks/doc-page/autosave.js +104 -0
- package/registry/blocks/doc-page/block.json +10 -0
- package/registry/blocks/doc-page/demo.jsx +137 -0
- package/registry/blocks/doc-page/doc-page.jsx +276 -0
- package/registry/blocks/doc-page/index.js +4 -0
- package/registry/blocks/email-render/README.md +151 -0
- package/registry/blocks/email-render/block.json +14 -0
- package/registry/blocks/email-render/demo.jsx +153 -0
- package/registry/blocks/email-render/doc-email.jsx +210 -0
- package/registry/blocks/email-render/email.jsx +221 -0
- package/registry/blocks/email-render/index.js +10 -0
- package/registry/blocks/email-render/render.js +50 -0
- package/registry/blocks/email-render/theme.js +215 -0
- package/registry/blocks/faq/README.md +101 -0
- package/registry/blocks/faq/block.json +11 -0
- package/registry/blocks/faq/demo.jsx +89 -0
- package/registry/blocks/faq/faq.jsx +220 -0
- package/registry/blocks/faq/index.js +4 -0
- package/registry/blocks/file-upload/README.md +138 -0
- package/registry/blocks/file-upload/block.json +10 -0
- package/registry/blocks/file-upload/demo.jsx +204 -0
- package/registry/blocks/file-upload/file-upload.jsx +635 -0
- package/registry/blocks/file-upload/index.js +4 -0
- package/registry/blocks/flow-canvas/README.md +420 -0
- package/registry/blocks/flow-canvas/block.json +13 -0
- package/registry/blocks/flow-canvas/board.js +819 -0
- package/registry/blocks/flow-canvas/chrome.jsx +649 -0
- package/registry/blocks/flow-canvas/context.jsx +35 -0
- package/registry/blocks/flow-canvas/demo.jsx +249 -0
- package/registry/blocks/flow-canvas/edges.jsx +107 -0
- package/registry/blocks/flow-canvas/flow-canvas.jsx +1329 -0
- package/registry/blocks/flow-canvas/index.js +41 -0
- package/registry/blocks/flow-canvas/nodes.jsx +392 -0
- package/registry/blocks/flow-canvas/theme.js +174 -0
- package/registry/blocks/footer/README.md +133 -0
- package/registry/blocks/footer/block.json +11 -0
- package/registry/blocks/footer/demo.jsx +119 -0
- package/registry/blocks/footer/footer.jsx +406 -0
- package/registry/blocks/footer/index.js +4 -0
- package/registry/blocks/gantt/README.md +220 -0
- package/registry/blocks/gantt/block.json +10 -0
- package/registry/blocks/gantt/demo.jsx +148 -0
- package/registry/blocks/gantt/gantt.jsx +923 -0
- package/registry/blocks/gantt/index.js +7 -0
- package/registry/blocks/gantt/rows.jsx +468 -0
- package/registry/blocks/gantt/schedule.js +278 -0
- package/registry/blocks/job-panel/README.md +80 -0
- package/registry/blocks/job-panel/block.json +3 -3
- package/registry/blocks/job-panel/demo.jsx +230 -3
- package/registry/blocks/job-panel/job-panel.jsx +169 -3
- package/registry/blocks/json-editor/README.md +133 -0
- package/registry/blocks/json-editor/block.json +10 -0
- package/registry/blocks/json-editor/demo.jsx +92 -0
- package/registry/blocks/json-editor/index.js +7 -0
- package/registry/blocks/json-editor/json-editor.jsx +305 -0
- package/registry/blocks/json-editor/json-tree.jsx +393 -0
- package/registry/blocks/json-editor/tree.js +225 -0
- package/registry/blocks/map/README.md +221 -0
- package/registry/blocks/map/block.json +14 -0
- package/registry/blocks/map/demo.jsx +201 -0
- package/registry/blocks/map/geo.js +198 -0
- package/registry/blocks/map/index.js +8 -0
- package/registry/blocks/map/list.jsx +128 -0
- package/registry/blocks/map/map-view.jsx +715 -0
- package/registry/blocks/map/maplibre.js +30 -0
- package/registry/blocks/map/marks.jsx +146 -0
- package/registry/blocks/map/theme.js +142 -0
- package/registry/blocks/menubar/README.md +109 -0
- package/registry/blocks/menubar/block.json +10 -0
- package/registry/blocks/menubar/demo.jsx +128 -0
- package/registry/blocks/menubar/index.js +4 -0
- package/registry/blocks/menubar/menubar.jsx +199 -0
- package/registry/blocks/mind-map/README.md +206 -0
- package/registry/blocks/mind-map/block.json +10 -0
- package/registry/blocks/mind-map/demo.jsx +103 -0
- package/registry/blocks/mind-map/index.js +31 -0
- package/registry/blocks/mind-map/mind-map.jsx +1125 -0
- package/registry/blocks/mind-map/topic.jsx +143 -0
- package/registry/blocks/mind-map/tree.js +645 -0
- package/registry/blocks/navigation-menu/README.md +124 -0
- package/registry/blocks/navigation-menu/block.json +10 -0
- package/registry/blocks/navigation-menu/demo.jsx +84 -0
- package/registry/blocks/navigation-menu/index.js +4 -0
- package/registry/blocks/navigation-menu/navigation-menu.jsx +448 -0
- package/registry/blocks/order-detail/README.md +109 -0
- package/registry/blocks/order-detail/block.json +10 -0
- package/registry/blocks/order-detail/demo.jsx +88 -0
- package/registry/blocks/order-detail/index.js +4 -0
- package/registry/blocks/order-detail/order-detail.jsx +354 -0
- package/registry/blocks/org-chart/README.md +145 -0
- package/registry/blocks/org-chart/block.json +10 -0
- package/registry/blocks/org-chart/demo.jsx +94 -0
- package/registry/blocks/org-chart/index.js +7 -0
- package/registry/blocks/org-chart/org-chart.jsx +631 -0
- package/registry/blocks/org-chart/org.js +245 -0
- package/registry/blocks/org-chart/person.jsx +96 -0
- package/registry/blocks/page-builder/README.md +257 -0
- package/registry/blocks/page-builder/block.json +14 -0
- package/registry/blocks/page-builder/demo.jsx +145 -0
- package/registry/blocks/page-builder/grapes.js +18 -0
- package/registry/blocks/page-builder/index.js +11 -0
- package/registry/blocks/page-builder/page-builder.jsx +704 -0
- package/registry/blocks/page-builder/page-view.jsx +33 -0
- package/registry/blocks/page-builder/page.js +118 -0
- package/registry/blocks/page-builder/panels.jsx +588 -0
- package/registry/blocks/page-builder/sections.js +209 -0
- package/registry/blocks/page-builder/theme.js +87 -0
- package/registry/blocks/page-builder/thumbs.jsx +189 -0
- package/registry/blocks/page-builder/tokens.js +329 -0
- package/registry/blocks/payment-methods/README.md +104 -0
- package/registry/blocks/payment-methods/block.json +10 -0
- package/registry/blocks/payment-methods/demo.jsx +71 -0
- package/registry/blocks/payment-methods/index.js +4 -0
- package/registry/blocks/payment-methods/payment-methods.jsx +302 -0
- package/registry/blocks/pdf-document/README.md +213 -0
- package/registry/blocks/pdf-document/block.json +13 -0
- package/registry/blocks/pdf-document/demo.jsx +136 -0
- package/registry/blocks/pdf-document/index.js +33 -0
- package/registry/blocks/pdf-document/money.js +83 -0
- package/registry/blocks/pdf-document/pdf-document.jsx +144 -0
- package/registry/blocks/pdf-document/primitives.jsx +271 -0
- package/registry/blocks/pdf-document/quote.jsx +179 -0
- package/registry/blocks/pdf-document/theme.js +193 -0
- package/registry/blocks/pdf-viewer/README.md +191 -0
- package/registry/blocks/pdf-viewer/block.json +14 -0
- package/registry/blocks/pdf-viewer/demo.jsx +252 -0
- package/registry/blocks/pdf-viewer/helpers.js +270 -0
- package/registry/blocks/pdf-viewer/index.js +8 -0
- package/registry/blocks/pdf-viewer/notes.jsx +195 -0
- package/registry/blocks/pdf-viewer/pdf-viewer.jsx +1368 -0
- package/registry/blocks/pdf-viewer/pdfjs.js +37 -0
- package/registry/blocks/pdf-viewer/ssr-shim-end.js +6 -0
- package/registry/blocks/pdf-viewer/ssr-shim.js +44 -0
- package/registry/blocks/product-detail/README.md +217 -0
- package/registry/blocks/product-detail/block.json +10 -0
- package/registry/blocks/product-detail/demo.jsx +233 -0
- package/registry/blocks/product-detail/index.js +7 -0
- package/registry/blocks/product-detail/price.jsx +115 -0
- package/registry/blocks/product-detail/product-detail.jsx +348 -0
- package/registry/blocks/product-detail/product-media.jsx +102 -0
- package/registry/blocks/product-detail/product-options.jsx +144 -0
- package/registry/blocks/product-grid/README.md +162 -0
- package/registry/blocks/product-grid/block.json +10 -0
- package/registry/blocks/product-grid/demo.jsx +255 -0
- package/registry/blocks/product-grid/index.js +5 -0
- package/registry/blocks/product-grid/product-card.jsx +232 -0
- package/registry/blocks/product-grid/product-grid.jsx +235 -0
- package/registry/blocks/record-form/README.md +196 -0
- package/registry/blocks/record-form/block.json +10 -0
- package/registry/blocks/record-form/demo.jsx +525 -0
- package/registry/blocks/record-form/index.js +4 -0
- package/registry/blocks/record-form/record-fields.jsx +457 -0
- package/registry/blocks/record-form/record-form.jsx +415 -0
- package/registry/blocks/record-form/rules.js +81 -0
- package/registry/blocks/record-notes/README.md +139 -0
- package/registry/blocks/record-notes/block.json +10 -0
- package/registry/blocks/record-notes/demo.jsx +179 -0
- package/registry/blocks/record-notes/index.js +4 -0
- package/registry/blocks/record-notes/record-notes.jsx +266 -0
- package/registry/blocks/search-dialog/README.md +108 -0
- package/registry/blocks/search-dialog/block.json +10 -0
- package/registry/blocks/search-dialog/demo.jsx +116 -0
- package/registry/blocks/search-dialog/index.js +3 -0
- package/registry/blocks/search-dialog/search-dialog.jsx +235 -0
- package/registry/blocks/settings-list/README.md +171 -0
- package/registry/blocks/settings-list/block.json +10 -0
- package/registry/blocks/settings-list/demo.jsx +333 -0
- package/registry/blocks/settings-list/index.js +4 -0
- package/registry/blocks/settings-list/row-state.jsx +93 -0
- package/registry/blocks/settings-list/settings-list.jsx +349 -0
- package/registry/blocks/settings-list/settings-matrix.jsx +118 -0
- package/registry/blocks/setup-checklist/README.md +142 -0
- package/registry/blocks/setup-checklist/block.json +10 -0
- package/registry/blocks/setup-checklist/demo.jsx +159 -0
- package/registry/blocks/setup-checklist/index.js +4 -0
- package/registry/blocks/setup-checklist/setup-checklist.jsx +446 -0
- package/registry/blocks/share-dialog/README.md +113 -0
- package/registry/blocks/share-dialog/block.json +10 -0
- package/registry/blocks/share-dialog/demo.jsx +140 -0
- package/registry/blocks/share-dialog/index.js +3 -0
- package/registry/blocks/share-dialog/share-dialog.jsx +384 -0
- package/registry/blocks/sidebar/README.md +190 -0
- package/registry/blocks/sidebar/block.json +43 -0
- package/registry/blocks/sidebar/demo.jsx +577 -0
- package/registry/blocks/sidebar/index.js +4 -0
- package/registry/blocks/sidebar/sidebar-panel.jsx +149 -0
- package/registry/blocks/sidebar/sidebar.jsx +864 -0
- package/registry/blocks/signature-pad/README.md +137 -0
- package/registry/blocks/signature-pad/block.json +13 -0
- package/registry/blocks/signature-pad/colors.js +115 -0
- package/registry/blocks/signature-pad/demo.jsx +94 -0
- package/registry/blocks/signature-pad/index.js +6 -0
- package/registry/blocks/signature-pad/pad.js +7 -0
- package/registry/blocks/signature-pad/signature-pad.jsx +434 -0
- package/registry/blocks/signature-pad/signature.js +68 -0
- package/registry/blocks/sketch-canvas/README.md +280 -0
- package/registry/blocks/sketch-canvas/block.json +13 -0
- package/registry/blocks/sketch-canvas/demo.jsx +159 -0
- package/registry/blocks/sketch-canvas/excalidraw.js +40 -0
- package/registry/blocks/sketch-canvas/index.js +9 -0
- package/registry/blocks/sketch-canvas/palette.js +226 -0
- package/registry/blocks/sketch-canvas/sketch-canvas.jsx +1188 -0
- package/registry/blocks/sketch-canvas/sketch.js +179 -0
- package/registry/blocks/sketch-canvas/ssr-shim-end.js +6 -0
- package/registry/blocks/sketch-canvas/ssr-shim.js +45 -0
- package/registry/blocks/sketch-canvas/stamps.js +104 -0
- package/registry/blocks/sketch-canvas/theme.js +131 -0
- package/registry/blocks/spreadsheet/README.md +307 -0
- package/registry/blocks/spreadsheet/block.json +13 -0
- package/registry/blocks/spreadsheet/cells.jsx +455 -0
- package/registry/blocks/spreadsheet/chrome.jsx +325 -0
- package/registry/blocks/spreadsheet/demo.jsx +177 -0
- package/registry/blocks/spreadsheet/grid.js +387 -0
- package/registry/blocks/spreadsheet/index.js +34 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +1732 -0
- package/registry/blocks/spreadsheet/types.js +473 -0
- package/registry/blocks/stat-cards/README.md +173 -0
- package/registry/blocks/stat-cards/block.json +10 -0
- package/registry/blocks/stat-cards/demo.jsx +257 -0
- package/registry/blocks/stat-cards/index.js +4 -0
- package/registry/blocks/stat-cards/stat-cards.jsx +307 -0
- package/registry/blocks/stat-cards/usage.jsx +243 -0
- package/registry/blocks/task-list/README.md +176 -0
- package/registry/blocks/task-list/block.json +10 -0
- package/registry/blocks/task-list/demo.jsx +134 -0
- package/registry/blocks/task-list/index.js +5 -0
- package/registry/blocks/task-list/task-list.jsx +907 -0
- package/registry/blocks/task-model/README.md +455 -0
- package/registry/blocks/task-model/block.json +167 -0
- package/registry/blocks/task-model/changes.js +89 -0
- package/registry/blocks/task-model/dates.js +371 -0
- package/registry/blocks/task-model/demo.jsx +215 -0
- package/registry/blocks/task-model/dnd.jsx +240 -0
- package/registry/blocks/task-model/index.js +97 -0
- package/registry/blocks/task-model/model.js +919 -0
- package/registry/blocks/task-model/parts.jsx +449 -0
- package/registry/blocks/task-model/toolbar.jsx +271 -0
- package/registry/blocks/testimonials/README.md +139 -0
- package/registry/blocks/testimonials/block.json +11 -0
- package/registry/blocks/testimonials/demo.jsx +137 -0
- package/registry/blocks/testimonials/index.js +4 -0
- package/registry/blocks/testimonials/testimonials.jsx +430 -0
- package/registry/blocks/transaction-list/README.md +126 -0
- package/registry/blocks/transaction-list/block.json +10 -0
- package/registry/blocks/transaction-list/demo.jsx +79 -0
- package/registry/blocks/transaction-list/index.js +4 -0
- package/registry/blocks/transaction-list/transaction-list.jsx +383 -0
- package/registry/blocks/whiteboard/README.md +179 -0
- package/registry/blocks/whiteboard/block.json +10 -0
- package/registry/blocks/whiteboard/demo.jsx +169 -0
- package/registry/blocks/whiteboard/index.js +6 -0
- package/registry/blocks/whiteboard/templates.js +150 -0
- package/registry/blocks/whiteboard/whiteboard.jsx +721 -0
- package/src/cli.mjs +19 -6
- package/src/install.mjs +18 -0
- package/src/registry.mjs +38 -4
- package/src/shadcn.mjs +14 -1
- package/templates/next/files/README.md +145 -11
- package/templates/next/files/app/globals.css +55 -4
- package/templates/next/files/check/run.mjs +3 -3
- package/templates/next/files/components/ui/avatar.jsx +141 -0
- package/templates/next/files/components/ui/breadcrumb.jsx +149 -0
- package/templates/next/files/components/ui/button-group.jsx +70 -0
- package/templates/next/files/components/ui/button.jsx +132 -12
- package/templates/next/files/components/ui/calendar.jsx +609 -0
- package/templates/next/files/components/ui/carousel.jsx +281 -0
- package/templates/next/files/components/ui/collapsible.jsx +208 -0
- package/templates/next/files/components/ui/combobox.jsx +608 -0
- package/templates/next/files/components/ui/command.jsx +577 -0
- package/templates/next/files/components/ui/context-menu.jsx +191 -0
- package/templates/next/files/components/ui/date-picker.jsx +360 -0
- package/templates/next/files/components/ui/distribution.jsx +117 -0
- package/templates/next/files/components/ui/feedback.jsx +143 -0
- package/templates/next/files/components/ui/field.jsx +115 -3
- package/templates/next/files/components/ui/file-input.jsx +234 -0
- package/templates/next/files/components/ui/icon.jsx +130 -8
- package/templates/next/files/components/ui/image.jsx +143 -0
- package/templates/next/files/components/ui/index.js +47 -2
- package/templates/next/files/components/ui/input-group.jsx +213 -0
- package/templates/next/files/components/ui/input-otp.jsx +170 -0
- package/templates/next/files/components/ui/item.jsx +206 -0
- package/templates/next/files/components/ui/menu.jsx +454 -82
- package/templates/next/files/components/ui/notice.jsx +126 -14
- package/templates/next/files/components/ui/pagination.jsx +156 -0
- package/templates/next/files/components/ui/popover.jsx +284 -0
- package/templates/next/files/components/ui/radio.jsx +193 -0
- package/templates/next/files/components/ui/rating.jsx +131 -0
- package/templates/next/files/components/ui/resizable.jsx +338 -0
- package/templates/next/files/components/ui/scroll-area.jsx +123 -0
- package/templates/next/files/components/ui/separator.jsx +119 -0
- package/templates/next/files/components/ui/sheet.jsx +96 -11
- package/templates/next/files/components/ui/slider.jsx +174 -0
- package/templates/next/files/components/ui/stat.jsx +574 -0
- package/templates/next/files/components/ui/steps.jsx +338 -0
- package/templates/next/files/components/ui/swatch.jsx +33 -0
- package/templates/next/files/components/ui/table.jsx +130 -0
- package/templates/next/files/components/ui/toast.jsx +440 -0
- package/templates/next/files/components/ui/toggle.jsx +195 -0
- package/templates/next/files/components/ui/tooltip.jsx +270 -0
- package/templates/next/template.json +264 -8
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# DocPage
|
|
2
|
+
|
|
3
|
+
One Notion-style page: an emoji icon, a big title, and the rich text
|
|
4
|
+
under it. The text is the `doc-editor` block (type `/` for headings,
|
|
5
|
+
lists, checklists, tables; `@` mentions a person). Edits save themselves
|
|
6
|
+
through `onSave` a moment after typing stops; the top bar says **Saved**,
|
|
7
|
+
**Saving…** or **Couldn't save** with a Retry. Cmd/Ctrl+S saves at once.
|
|
8
|
+
Read-only shows the same page with nothing to edit. It is the wiki page:
|
|
9
|
+
`knowledge-base` puts a page tree beside it.
|
|
10
|
+
|
|
11
|
+
```jsx
|
|
12
|
+
import { DocPage } from "../components/blocks/doc-page"; // a Makersclaw app ("./blocks/doc-page" from components/)
|
|
13
|
+
import { DocPage } from "@/components/makersclaw/blocks/doc-page"; // a shadcn project
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
It is a client component: render it from a `"use client"` component, and
|
|
17
|
+
load the page in the server surface (README §4). `makersclaw add
|
|
18
|
+
doc-page` adds `doc-editor` too (and its BlockNote packages: run `npm
|
|
19
|
+
install` after).
|
|
20
|
+
|
|
21
|
+
## The data
|
|
22
|
+
|
|
23
|
+
A page is `{ id, title, icon, doc }`:
|
|
24
|
+
|
|
25
|
+
- `id` — the page's key.
|
|
26
|
+
- `title` — plain text, one line (it wraps on screen).
|
|
27
|
+
- `icon` — an emoji, or `null` for none.
|
|
28
|
+
- `doc` — the body as BlockNote JSON (see the doc-editor README): store
|
|
29
|
+
it as it comes, in a `jsonb` column.
|
|
30
|
+
|
|
31
|
+
`onSave` receives the whole page, every time.
|
|
32
|
+
|
|
33
|
+
## Props
|
|
34
|
+
|
|
35
|
+
- `id` — which page this is. The page reads `title`, `icon` and `doc`
|
|
36
|
+
when it mounts and again whenever `id` changes; in between it owns the
|
|
37
|
+
draft, so a parent re-rendering with older props never throws away
|
|
38
|
+
typing. To show another page, change `id`.
|
|
39
|
+
- `title` — default `""`.
|
|
40
|
+
- `icon` — default `null`.
|
|
41
|
+
- `doc` — default an empty doc.
|
|
42
|
+
- `onSave(page)` — `async ({ id, title, icon, doc }) => ({ ok, error? })`.
|
|
43
|
+
Called `saveDelay` ms after the last edit, one save at a time (edits
|
|
44
|
+
made during a save go out right after it). `{ ok: false, error }` or a
|
|
45
|
+
throw shows "Couldn't save" with `error` as the reason and a Retry; the
|
|
46
|
+
edits stay on screen and the next edit tries again. Without `onSave`
|
|
47
|
+
there is no save state (an editor you read from `onChange`).
|
|
48
|
+
- `onChange(page)` — every edit, with the whole page (to update a title
|
|
49
|
+
in a list beside it as it is typed). Not a save.
|
|
50
|
+
- `readOnly` — `false` (default). `true`: the title is a heading, the
|
|
51
|
+
icon a picture, the editor read-only; no save state.
|
|
52
|
+
- `saveDelay` — ms of quiet before a save, default `1000`.
|
|
53
|
+
- `placeholder` — the empty body's hint, default "Write something, or
|
|
54
|
+
press / for blocks".
|
|
55
|
+
- `titlePlaceholder` — default "Untitled".
|
|
56
|
+
- `icons` — the emoji offered by the icon picker (default 24 common
|
|
57
|
+
ones). `[]` turns picking off (a given `icon` still shows).
|
|
58
|
+
- `meta` — a node under the title: "Edited by June · 2 days ago".
|
|
59
|
+
- `breadcrumb` — a node at the left of the top bar (where the page
|
|
60
|
+
sits).
|
|
61
|
+
- `actions` — a node at the right of the top bar: a kit `Menu` of page
|
|
62
|
+
actions, a Share button.
|
|
63
|
+
- `width` — `"normal"` (default, a reading column) · `"wide"` · `"full"`.
|
|
64
|
+
- `autoFocus` — put the caret in the title when it mounts (a page just
|
|
65
|
+
created).
|
|
66
|
+
- `mentions`, `fields`, `slashItems`, `uploadFile` — passed to the
|
|
67
|
+
editor; see the doc-editor README.
|
|
68
|
+
- `className` — layout around it. Never colour.
|
|
69
|
+
|
|
70
|
+
Keys: Enter or ↓ at the end of the title moves into the body; Cmd/Ctrl+S
|
|
71
|
+
saves now. A tab closed with an edit unsaved asks first; a page left
|
|
72
|
+
(another `id`, the surface unmounted) sends its unsent edit on the way
|
|
73
|
+
out without waiting for the answer.
|
|
74
|
+
|
|
75
|
+
## Example
|
|
76
|
+
|
|
77
|
+
```jsx
|
|
78
|
+
// components/WikiPage.jsx — client
|
|
79
|
+
"use client";
|
|
80
|
+
import { updateRow } from "../lib/actions";
|
|
81
|
+
import { DocPage } from "./blocks/doc-page";
|
|
82
|
+
|
|
83
|
+
export default function WikiPage({ page, team, canEdit }) {
|
|
84
|
+
return (
|
|
85
|
+
<DocPage
|
|
86
|
+
id={page.id}
|
|
87
|
+
title={page.title}
|
|
88
|
+
icon={page.icon}
|
|
89
|
+
doc={page.body}
|
|
90
|
+
readOnly={!canEdit}
|
|
91
|
+
meta={`Edited by ${page.edited_by_name}`}
|
|
92
|
+
onSave={({ id, title, icon, doc }) =>
|
|
93
|
+
updateRow({ source: "public.wiki_pages", id, patch: { title, icon, body: doc } })
|
|
94
|
+
}
|
|
95
|
+
mentions={async (q) =>
|
|
96
|
+
team.filter((p) => p.name.toLowerCase().includes(q.toLowerCase())).map((p) => ({ id: p.id, label: p.name }))
|
|
97
|
+
}
|
|
98
|
+
/>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For search or a preview, keep a plain-text copy beside the doc:
|
|
104
|
+
`docToText(doc)` from the doc-editor block, saved in the same `onSave`.
|
|
105
|
+
|
|
106
|
+
## What it will not do
|
|
107
|
+
|
|
108
|
+
- Hold more than one page, or a tree of them: that is `knowledge-base`.
|
|
109
|
+
- Collaborate live: saves are last-write-wins, whole page at a time.
|
|
110
|
+
- Keep an edit when the browser is closed without an answer: the tab
|
|
111
|
+
asks before closing while an edit is unsaved; after that it is gone.
|
|
112
|
+
- Cover images, page history, comments or sharing: put your own
|
|
113
|
+
controls in `actions`.
|
|
114
|
+
- Pick any emoji in the world: the grid is `icons`.
|
|
115
|
+
|
|
116
|
+
A page it cannot be is your own component in `components/`, built on
|
|
117
|
+
the doc-editor block — not an edit to this block. Its files are fixed:
|
|
118
|
+
`npm run check` refuses a change, and `makersclaw add doc-page` restores
|
|
119
|
+
them.
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
// useAutosave — the save loop behind DocPage, kept apart so it reads on
|
|
6
|
+
// its own. Every edit bumps a version and (re)starts a timer; when the
|
|
7
|
+
// timer fires the latest page goes out through `save`. One save at a
|
|
8
|
+
// time: edits made while one is in flight are sent right after it. A
|
|
9
|
+
// failure keeps the edits, says so, and the next edit (or `flush`) tries
|
|
10
|
+
// again. Leaving (unmount) sends what is still unsent, without waiting.
|
|
11
|
+
//
|
|
12
|
+
// status "saved" · "pending" (an edit waits for the timer) ·
|
|
13
|
+
// "saving" · "error"
|
|
14
|
+
// error the failure's text while status is "error"
|
|
15
|
+
// change(payload) an edit: payload is what `save` will receive
|
|
16
|
+
// flush() save now (Cmd/Ctrl+S, Retry)
|
|
17
|
+
|
|
18
|
+
export function useAutosave(save, delay = 1000) {
|
|
19
|
+
const [status, setStatus] = useState("saved");
|
|
20
|
+
const [error, setError] = useState(null);
|
|
21
|
+
const saveRef = useRef(save);
|
|
22
|
+
saveRef.current = save;
|
|
23
|
+
const latest = useRef(null);
|
|
24
|
+
const version = useRef(0);
|
|
25
|
+
const saved = useRef(0);
|
|
26
|
+
const busy = useRef(false);
|
|
27
|
+
const timer = useRef(null);
|
|
28
|
+
const alive = useRef(true);
|
|
29
|
+
|
|
30
|
+
const run = useCallback(async () => {
|
|
31
|
+
clearTimeout(timer.current);
|
|
32
|
+
timer.current = null;
|
|
33
|
+
if (busy.current || !saveRef.current) return;
|
|
34
|
+
if (version.current === saved.current) {
|
|
35
|
+
setStatus("saved");
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const v = version.current;
|
|
39
|
+
busy.current = true;
|
|
40
|
+
setStatus("saving");
|
|
41
|
+
let res;
|
|
42
|
+
try {
|
|
43
|
+
res = await saveRef.current(latest.current);
|
|
44
|
+
} catch (e) {
|
|
45
|
+
res = { ok: false, error: e?.message };
|
|
46
|
+
}
|
|
47
|
+
busy.current = false;
|
|
48
|
+
if (!alive.current) return;
|
|
49
|
+
if (res?.ok === false) {
|
|
50
|
+
setError(res.error || "Couldn't reach the server.");
|
|
51
|
+
setStatus("error");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
saved.current = Math.max(saved.current, v);
|
|
55
|
+
setError(null);
|
|
56
|
+
if (version.current !== saved.current) run();
|
|
57
|
+
else setStatus("saved");
|
|
58
|
+
}, []);
|
|
59
|
+
|
|
60
|
+
const change = useCallback(
|
|
61
|
+
(payload) => {
|
|
62
|
+
latest.current = payload;
|
|
63
|
+
version.current += 1;
|
|
64
|
+
if (!saveRef.current) return;
|
|
65
|
+
setStatus((s) => (s === "saving" ? s : "pending"));
|
|
66
|
+
clearTimeout(timer.current);
|
|
67
|
+
timer.current = setTimeout(run, delay);
|
|
68
|
+
},
|
|
69
|
+
[delay, run],
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const flush = useCallback(() => {
|
|
73
|
+
run();
|
|
74
|
+
}, [run]);
|
|
75
|
+
|
|
76
|
+
// Unsent edits on the way out: warn before the tab closes, and send them
|
|
77
|
+
// when the page goes (another page opened, the surface left).
|
|
78
|
+
const unsent = status !== "saved";
|
|
79
|
+
useEffect(() => {
|
|
80
|
+
if (!unsent) return;
|
|
81
|
+
const warn = (e) => {
|
|
82
|
+
e.preventDefault();
|
|
83
|
+
e.returnValue = "";
|
|
84
|
+
};
|
|
85
|
+
window.addEventListener("beforeunload", warn);
|
|
86
|
+
return () => window.removeEventListener("beforeunload", warn);
|
|
87
|
+
}, [unsent]);
|
|
88
|
+
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
alive.current = true;
|
|
91
|
+
return () => {
|
|
92
|
+
alive.current = false;
|
|
93
|
+
clearTimeout(timer.current);
|
|
94
|
+
if (version.current !== saved.current && saveRef.current) {
|
|
95
|
+
saved.current = version.current;
|
|
96
|
+
Promise.resolve()
|
|
97
|
+
.then(() => saveRef.current(latest.current))
|
|
98
|
+
.catch(() => {});
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
}, []);
|
|
102
|
+
|
|
103
|
+
return { status, error, change, flush };
|
|
104
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "doc-page",
|
|
3
|
+
"title": "DocPage",
|
|
4
|
+
"description": "A Notion-style page: an emoji icon (picked from a grid), a big plain title, and the rich-text editor under it (doc-editor: / menu, markdown shortcuts, @mentions). Edits autosave through an async onSave a moment after typing stops, with Saved / Saving… / Couldn't save + Retry in the top bar, Cmd/Ctrl+S to save now and unsent edits sent when the page is left. A read-only mode shows the same page with nothing to edit; slots for a breadcrumb, actions and a meta line.",
|
|
5
|
+
"useWhen": "A screen is one document someone reads and writes: a wiki or handbook page, a playbook, a policy, a project page, a one-pager. The knowledge-base block puts a page tree beside it. Not for a short field of rich text inside a form (doc-editor), notes on a record (record-notes) or a comment (comment-box).",
|
|
6
|
+
"tags": ["page", "document", "doc", "wiki", "notion", "editor", "rich text", "autosave", "title", "icon", "emoji", "read-only", "handbook", "notes"],
|
|
7
|
+
"exports": ["DocPage"],
|
|
8
|
+
"files": ["index.js", "doc-page.jsx", "autosave.js"],
|
|
9
|
+
"requires": ["doc-editor"]
|
|
10
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// DocPage two ways — the gallery's and the smoke test's surface. Not
|
|
4
|
+
// installed into apps.
|
|
5
|
+
// Editable a handbook page that autosaves. Pick whether the pretend
|
|
6
|
+
// server takes the save or refuses it, and watch the bar go
|
|
7
|
+
// Saving… → Saved, or → Couldn't save with Retry.
|
|
8
|
+
// Read-only the same page for someone who may only read it.
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
import { Card, Kicker, Menu, ToggleGroup } from "../components/ui";
|
|
11
|
+
import { DocPage } from "../components/blocks/doc-page";
|
|
12
|
+
import { docToText } from "../components/blocks/doc-editor";
|
|
13
|
+
|
|
14
|
+
const wait = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
15
|
+
const t = (text, styles = {}) => ({ type: "text", text, styles });
|
|
16
|
+
|
|
17
|
+
const PEOPLE = [
|
|
18
|
+
{ id: "u-june", label: "June Okafor", description: "People ops" },
|
|
19
|
+
{ id: "u-tomas", label: "Tomás Ortega", description: "Engineering" },
|
|
20
|
+
{ id: "u-lena", label: "Lena Fischer", description: "Sales" },
|
|
21
|
+
];
|
|
22
|
+
const findPeople = async (q) => PEOPLE.filter((p) => p.label.toLowerCase().includes(q.toLowerCase()));
|
|
23
|
+
|
|
24
|
+
const HANDBOOK_PAGE = {
|
|
25
|
+
id: "page-onboarding",
|
|
26
|
+
icon: "👋",
|
|
27
|
+
title: "Your first week at Larkspur",
|
|
28
|
+
doc: [
|
|
29
|
+
{ type: "paragraph", content: [t("Welcome aboard. This page is the one place to look during week one; "), { type: "mention", props: { id: "u-june", label: "June Okafor" } }, t(" keeps it current.")] },
|
|
30
|
+
{ type: "heading", props: { level: 2 }, content: [t("Day one")] },
|
|
31
|
+
{ type: "checkListItem", props: { checked: true }, content: [t("Pick up your laptop from the front desk")] },
|
|
32
|
+
{ type: "checkListItem", content: [t("Set up SSO and the password manager")] },
|
|
33
|
+
{ type: "checkListItem", content: [t("Book a 30-minute intro with your manager")] },
|
|
34
|
+
{ type: "heading", props: { level: 2 }, content: [t("How we work")] },
|
|
35
|
+
{ type: "bulletListItem", content: [t("Writing beats meetings: "), t("decisions live in docs", { bold: true }), t(", not in threads.")] },
|
|
36
|
+
{ type: "bulletListItem", content: [t("Core hours are 10:00–15:00 in your own time zone.")] },
|
|
37
|
+
{ type: "bulletListItem", content: [t("Ship small, ship on Tuesdays and Thursdays.")] },
|
|
38
|
+
{ type: "quote", content: [t("If you are unsure who owns something, ask in the team channel. Nobody minds.")] },
|
|
39
|
+
],
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
function EditableDemo() {
|
|
43
|
+
const [outcome, setOutcome] = useState("ok");
|
|
44
|
+
const [last, setLast] = useState(null);
|
|
45
|
+
const save = async (page) => {
|
|
46
|
+
await wait(700);
|
|
47
|
+
if (outcome === "fail") return { ok: false, error: "The server is not answering. Your edits are still here." };
|
|
48
|
+
setLast({ title: page.title || "Untitled", words: docToText(page.doc ?? []).split(/\s+/).filter(Boolean).length, icon: page.icon });
|
|
49
|
+
return { ok: true };
|
|
50
|
+
};
|
|
51
|
+
return (
|
|
52
|
+
<div className="grid gap-4">
|
|
53
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
54
|
+
<Kicker>Saves</Kicker>
|
|
55
|
+
<ToggleGroup
|
|
56
|
+
type="single"
|
|
57
|
+
size="sm"
|
|
58
|
+
aria-label="What the pretend server does"
|
|
59
|
+
value={outcome}
|
|
60
|
+
onChange={(v) => v && setOutcome(v)}
|
|
61
|
+
items={[
|
|
62
|
+
{ value: "ok", label: "Succeed" },
|
|
63
|
+
{ value: "fail", label: "Fail" },
|
|
64
|
+
]}
|
|
65
|
+
/>
|
|
66
|
+
<span className="text-sm text-muted-foreground">
|
|
67
|
+
{last ? `Last saved: ${last.icon ?? ""} ${last.title} · ${last.words} words` : "Nothing saved yet: edit the title or the page."}
|
|
68
|
+
</span>
|
|
69
|
+
</div>
|
|
70
|
+
<Card>
|
|
71
|
+
<DocPage
|
|
72
|
+
{...HANDBOOK_PAGE}
|
|
73
|
+
onSave={save}
|
|
74
|
+
mentions={findPeople}
|
|
75
|
+
meta="Edited by June Okafor · 2 days ago"
|
|
76
|
+
breadcrumb={<span className="truncate text-sm text-muted-foreground">Handbook / People</span>}
|
|
77
|
+
actions={
|
|
78
|
+
<Menu
|
|
79
|
+
label="Page actions"
|
|
80
|
+
items={[
|
|
81
|
+
{ label: "Copy link", icon: "link", onSelect: () => {} },
|
|
82
|
+
{ label: "Move to…", icon: "folder", onSelect: () => {} },
|
|
83
|
+
{ type: "separator" },
|
|
84
|
+
{ label: "Delete page", icon: "trash", tone: "danger", onSelect: () => {} },
|
|
85
|
+
]}
|
|
86
|
+
/>
|
|
87
|
+
}
|
|
88
|
+
/>
|
|
89
|
+
</Card>
|
|
90
|
+
</div>
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function ReadOnlyDemo() {
|
|
95
|
+
return (
|
|
96
|
+
<Card>
|
|
97
|
+
<DocPage {...HANDBOOK_PAGE} id="page-onboarding-ro" readOnly meta="You can read this page. Ask June Okafor to edit it." />
|
|
98
|
+
</Card>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function BlankDemo() {
|
|
103
|
+
const [saved, setSaved] = useState(0);
|
|
104
|
+
return (
|
|
105
|
+
<Card>
|
|
106
|
+
<DocPage
|
|
107
|
+
id="page-new"
|
|
108
|
+
onSave={async () => {
|
|
109
|
+
await wait(400);
|
|
110
|
+
setSaved((n) => n + 1);
|
|
111
|
+
return { ok: true };
|
|
112
|
+
}}
|
|
113
|
+
meta={saved ? `Saved ${saved} time${saved === 1 ? "" : "s"}` : "A new page: no icon, no title yet."}
|
|
114
|
+
/>
|
|
115
|
+
</Card>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export default function DocPageDemo() {
|
|
120
|
+
const [view, setView] = useState("edit");
|
|
121
|
+
return (
|
|
122
|
+
<div className="grid gap-4">
|
|
123
|
+
<ToggleGroup
|
|
124
|
+
type="single"
|
|
125
|
+
aria-label="Which page"
|
|
126
|
+
value={view}
|
|
127
|
+
onChange={(v) => v && setView(v)}
|
|
128
|
+
items={[
|
|
129
|
+
{ value: "edit", label: "Editable" },
|
|
130
|
+
{ value: "read", label: "Read-only" },
|
|
131
|
+
{ value: "blank", label: "New page" },
|
|
132
|
+
]}
|
|
133
|
+
/>
|
|
134
|
+
{view === "edit" ? <EditableDemo /> : view === "read" ? <ReadOnlyDemo /> : <BlankDemo />}
|
|
135
|
+
</div>
|
|
136
|
+
);
|
|
137
|
+
}
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// DocPage — one Notion-style page: an icon, a title, the rich text under
|
|
4
|
+
// it (DocEditor), and a save state in the top bar. Edits save themselves
|
|
5
|
+
// through `onSave` a moment after typing stops (useAutosave), and the bar
|
|
6
|
+
// says "Saved", "Saving…" or "Couldn't save" with a Retry. Read-only
|
|
7
|
+
// shows the same page with nothing to edit.
|
|
8
|
+
//
|
|
9
|
+
// The page reads `title`, `icon` and `doc` when it mounts and whenever
|
|
10
|
+
// `id` changes (another page opened); in between it owns the draft, so a
|
|
11
|
+
// parent re-rendering with stale props never throws away typing. Leaving
|
|
12
|
+
// a page with an edit still unsent sends it on the way out.
|
|
13
|
+
import { useEffect, useId, useLayoutEffect, useRef, useState } from "react";
|
|
14
|
+
import { Button, Icon, Popover, Spinner, cx } from "../../ui";
|
|
15
|
+
import { DocEditor, emptyDoc, useDocEditor } from "../doc-editor";
|
|
16
|
+
import { useAutosave } from "./autosave";
|
|
17
|
+
|
|
18
|
+
const DEFAULT_ICONS = [
|
|
19
|
+
"📄", "📘", "📝", "📌", "📎", "🗂️",
|
|
20
|
+
"💡", "🎯", "🚀", "📈", "🧭", "🛠️",
|
|
21
|
+
"🤝", "👋", "📣", "🧪", "🔒", "⚙️",
|
|
22
|
+
"🗓️", "✅", "❓", "⭐", "🌱", "🏁",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const WIDTHS = { normal: "max-w-3xl", wide: "max-w-5xl", full: "max-w-none" };
|
|
26
|
+
|
|
27
|
+
export function DocPage({ id, ...props }) {
|
|
28
|
+
// A new id is a new page: remount, so the draft, the editor and the save
|
|
29
|
+
// loop start from that page (the old one flushes on its way out).
|
|
30
|
+
return <PageBody key={id ?? "page"} id={id} {...props} />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function PageBody({
|
|
34
|
+
id,
|
|
35
|
+
title: initialTitle = "",
|
|
36
|
+
icon: initialIcon = null,
|
|
37
|
+
doc: initialDoc,
|
|
38
|
+
onSave,
|
|
39
|
+
onChange,
|
|
40
|
+
readOnly = false,
|
|
41
|
+
saveDelay = 1000,
|
|
42
|
+
placeholder = "Write something, or press / for blocks",
|
|
43
|
+
titlePlaceholder = "Untitled",
|
|
44
|
+
icons = DEFAULT_ICONS,
|
|
45
|
+
meta,
|
|
46
|
+
breadcrumb,
|
|
47
|
+
actions,
|
|
48
|
+
width = "normal",
|
|
49
|
+
autoFocus = false,
|
|
50
|
+
mentions,
|
|
51
|
+
fields,
|
|
52
|
+
slashItems,
|
|
53
|
+
uploadFile,
|
|
54
|
+
className,
|
|
55
|
+
}) {
|
|
56
|
+
const [title, setTitle] = useState(initialTitle ?? "");
|
|
57
|
+
const [icon, setIcon] = useState(initialIcon || null);
|
|
58
|
+
// The page as last edited, read by every save (the editor's onChange and
|
|
59
|
+
// the title's fire from different renders).
|
|
60
|
+
const current = useRef({ id, title: initialTitle ?? "", icon: initialIcon || null, doc: Array.isArray(initialDoc) && initialDoc.length ? initialDoc : emptyDoc() });
|
|
61
|
+
const editorRef = useDocEditor();
|
|
62
|
+
const titleRef = useRef(null);
|
|
63
|
+
const saving = useAutosave(onSave && !readOnly ? onSave : null, saveDelay);
|
|
64
|
+
|
|
65
|
+
const edit = (patch) => {
|
|
66
|
+
const page = { ...current.current, ...patch };
|
|
67
|
+
current.current = page;
|
|
68
|
+
onChange?.(page);
|
|
69
|
+
saving.change(page);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// The title grows with its text (it wraps on a phone) and never holds a
|
|
73
|
+
// line break: Enter moves into the page instead.
|
|
74
|
+
useLayoutEffect(() => {
|
|
75
|
+
const el = titleRef.current;
|
|
76
|
+
if (!el) return;
|
|
77
|
+
el.style.height = "auto";
|
|
78
|
+
el.style.height = `${el.scrollHeight}px`;
|
|
79
|
+
}, [title]);
|
|
80
|
+
useEffect(() => {
|
|
81
|
+
if (autoFocus && !readOnly) titleRef.current?.focus();
|
|
82
|
+
}, [autoFocus, readOnly]);
|
|
83
|
+
|
|
84
|
+
const onTitleKey = (e) => {
|
|
85
|
+
if (e.key === "Enter" && !e.nativeEvent.isComposing) {
|
|
86
|
+
e.preventDefault();
|
|
87
|
+
editorRef.current?.focus();
|
|
88
|
+
} else if (e.key === "ArrowDown" && e.currentTarget.selectionStart === e.currentTarget.value.length) {
|
|
89
|
+
e.preventDefault();
|
|
90
|
+
editorRef.current?.focus();
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
// Cmd/Ctrl+S saves now instead of the browser's "save page as".
|
|
95
|
+
const onKeyDown = (e) => {
|
|
96
|
+
if (!onSave || readOnly) return;
|
|
97
|
+
if ((e.metaKey || e.ctrlKey) && !e.altKey && e.key.toLowerCase() === "s") {
|
|
98
|
+
e.preventDefault();
|
|
99
|
+
saving.flush();
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
const showBar = breadcrumb || actions || (onSave && !readOnly);
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
// raw: a plain box that hears Cmd/Ctrl+S from the title and the editor
|
|
107
|
+
// inside it; no control of its own.
|
|
108
|
+
<article className={cx("group/page mx-auto w-full min-w-0", WIDTHS[width] ?? WIDTHS.normal, className)} onKeyDown={onKeyDown}>
|
|
109
|
+
{showBar ? (
|
|
110
|
+
<div className="flex min-h-11 items-center gap-2">
|
|
111
|
+
<div className="min-w-0 flex-1">{breadcrumb}</div>
|
|
112
|
+
{onSave && !readOnly ? <SaveState status={saving.status} error={saving.error} onRetry={saving.flush} /> : null}
|
|
113
|
+
{actions ? <div className="flex shrink-0 items-center gap-1">{actions}</div> : null}
|
|
114
|
+
</div>
|
|
115
|
+
) : null}
|
|
116
|
+
|
|
117
|
+
<header className="pt-6 sm:ps-12 sm:pt-10">
|
|
118
|
+
{readOnly ? (
|
|
119
|
+
icon ? (
|
|
120
|
+
<div className="mb-2 text-5xl leading-none" aria-hidden="true">
|
|
121
|
+
{icon}
|
|
122
|
+
</div>
|
|
123
|
+
) : null
|
|
124
|
+
) : (
|
|
125
|
+
<IconPicker icon={icon} icons={icons} onPick={(next) => {
|
|
126
|
+
setIcon(next);
|
|
127
|
+
edit({ icon: next });
|
|
128
|
+
}} />
|
|
129
|
+
)}
|
|
130
|
+
|
|
131
|
+
{readOnly ? (
|
|
132
|
+
<h1 className="text-3xl font-bold leading-tight tracking-tight text-foreground [overflow-wrap:anywhere] sm:text-4xl">
|
|
133
|
+
{title || <span className="text-muted-foreground">{titlePlaceholder}</span>}
|
|
134
|
+
</h1>
|
|
135
|
+
) : (
|
|
136
|
+
// raw: a page title is heading-sized text that wraps with no box
|
|
137
|
+
// around it; the kit's Input and Textarea are boxed form fields.
|
|
138
|
+
<textarea
|
|
139
|
+
ref={titleRef}
|
|
140
|
+
rows={1}
|
|
141
|
+
value={title}
|
|
142
|
+
placeholder={titlePlaceholder}
|
|
143
|
+
aria-label="Page title"
|
|
144
|
+
spellCheck
|
|
145
|
+
onChange={(e) => {
|
|
146
|
+
const next = e.target.value.replace(/\r?\n/g, " ");
|
|
147
|
+
setTitle(next);
|
|
148
|
+
edit({ title: next });
|
|
149
|
+
}}
|
|
150
|
+
onKeyDown={onTitleKey}
|
|
151
|
+
className="block w-full resize-none overflow-hidden border-0 bg-transparent p-0 text-3xl font-bold leading-tight tracking-tight text-foreground outline-none placeholder:text-muted-foreground/70 focus-visible:outline-none sm:text-4xl"
|
|
152
|
+
/>
|
|
153
|
+
)}
|
|
154
|
+
{meta ? <div className="mt-2 text-sm text-muted-foreground">{meta}</div> : null}
|
|
155
|
+
</header>
|
|
156
|
+
|
|
157
|
+
<div className="mt-4 pb-16">
|
|
158
|
+
<DocEditor
|
|
159
|
+
defaultValue={initialDoc}
|
|
160
|
+
onChange={(doc) => edit({ doc })}
|
|
161
|
+
editable={!readOnly}
|
|
162
|
+
placeholder={placeholder}
|
|
163
|
+
editorRef={editorRef}
|
|
164
|
+
label={title || titlePlaceholder}
|
|
165
|
+
mentions={mentions}
|
|
166
|
+
fields={fields}
|
|
167
|
+
slashItems={slashItems}
|
|
168
|
+
uploadFile={uploadFile}
|
|
169
|
+
className="min-h-64"
|
|
170
|
+
/>
|
|
171
|
+
</div>
|
|
172
|
+
</article>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** "Saved" · "Saving…" · "Couldn't save" + Retry, announced politely. */
|
|
177
|
+
function SaveState({ status, error, onRetry }) {
|
|
178
|
+
return (
|
|
179
|
+
<div role="status" aria-live="polite" className="flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground">
|
|
180
|
+
{status === "error" ? (
|
|
181
|
+
<>
|
|
182
|
+
<span className="flex items-center gap-1 text-destructive" title={error || undefined}>
|
|
183
|
+
<Icon name="warning" size={14} />
|
|
184
|
+
Couldn't save
|
|
185
|
+
</span>
|
|
186
|
+
<span className="sr-only">{error}</span>
|
|
187
|
+
<Button variant="link" size="sm" onClick={onRetry}>
|
|
188
|
+
Retry
|
|
189
|
+
</Button>
|
|
190
|
+
</>
|
|
191
|
+
) : status === "saved" ? (
|
|
192
|
+
<span className="flex items-center gap-1">
|
|
193
|
+
<Icon name="check" size={14} />
|
|
194
|
+
Saved
|
|
195
|
+
</span>
|
|
196
|
+
) : (
|
|
197
|
+
<span className="flex items-center gap-1.5">
|
|
198
|
+
<Spinner size="sm" label={null} />
|
|
199
|
+
Saving…
|
|
200
|
+
</span>
|
|
201
|
+
)}
|
|
202
|
+
</div>
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/** The page's icon: a big emoji that opens a grid of others, or "Add icon"
|
|
207
|
+
* while there is none. An empty `icons` list turns picking off. */
|
|
208
|
+
function IconPicker({ icon, icons, onPick }) {
|
|
209
|
+
const [open, setOpen] = useState(false);
|
|
210
|
+
const gridId = useId();
|
|
211
|
+
if (!icons?.length) {
|
|
212
|
+
return icon ? (
|
|
213
|
+
<div className="mb-2 text-5xl leading-none" aria-hidden="true">
|
|
214
|
+
{icon}
|
|
215
|
+
</div>
|
|
216
|
+
) : null;
|
|
217
|
+
}
|
|
218
|
+
const pick = (next) => {
|
|
219
|
+
onPick(next);
|
|
220
|
+
setOpen(false);
|
|
221
|
+
};
|
|
222
|
+
return (
|
|
223
|
+
<div className={cx(icon ? "mb-2" : "mb-1 -ms-3")}>
|
|
224
|
+
<Popover
|
|
225
|
+
open={open}
|
|
226
|
+
onOpenChange={setOpen}
|
|
227
|
+
side="bottom"
|
|
228
|
+
align="start"
|
|
229
|
+
width="auto"
|
|
230
|
+
label="Page icon"
|
|
231
|
+
className="p-2"
|
|
232
|
+
trigger={(p) =>
|
|
233
|
+
icon ? (
|
|
234
|
+
// raw: the page icon is a 64px emoji you press; the kit's
|
|
235
|
+
// Button squares are 36 and 44px and cx cannot resize them.
|
|
236
|
+
<button
|
|
237
|
+
{...p}
|
|
238
|
+
type="button"
|
|
239
|
+
aria-label={`Page icon ${icon}, change it`}
|
|
240
|
+
className="-ms-2 grid size-16 place-items-center rounded-md outline-none hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring"
|
|
241
|
+
>
|
|
242
|
+
<span className="text-5xl leading-none" aria-hidden="true">
|
|
243
|
+
{icon}
|
|
244
|
+
</span>
|
|
245
|
+
</button>
|
|
246
|
+
) : (
|
|
247
|
+
<Button
|
|
248
|
+
{...p}
|
|
249
|
+
variant="quiet"
|
|
250
|
+
size="sm"
|
|
251
|
+
icon="plus"
|
|
252
|
+
className="text-muted-foreground opacity-0 transition-opacity group-hover/page:opacity-100 group-focus-within/page:opacity-100 focus-visible:opacity-100 [@media(hover:none)]:opacity-100"
|
|
253
|
+
>
|
|
254
|
+
Add icon
|
|
255
|
+
</Button>
|
|
256
|
+
)
|
|
257
|
+
}
|
|
258
|
+
>
|
|
259
|
+
<div id={gridId} role="group" aria-label="Choose an icon" className="grid grid-cols-6 gap-1">
|
|
260
|
+
{icons.map((e) => (
|
|
261
|
+
<Button key={e} variant="quiet" iconOnly aria-label={e} aria-pressed={e === icon} onClick={() => pick(e)} className="aria-pressed:bg-accent">
|
|
262
|
+
<span className="text-2xl leading-none">{e}</span>
|
|
263
|
+
</Button>
|
|
264
|
+
))}
|
|
265
|
+
</div>
|
|
266
|
+
{icon ? (
|
|
267
|
+
<div className="mt-2 border-t border-border pt-2">
|
|
268
|
+
<Button variant="quiet" size="sm" icon="trash" onClick={() => pick(null)} className="w-full">
|
|
269
|
+
Remove icon
|
|
270
|
+
</Button>
|
|
271
|
+
</div>
|
|
272
|
+
) : null}
|
|
273
|
+
</Popover>
|
|
274
|
+
</div>
|
|
275
|
+
);
|
|
276
|
+
}
|