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,221 @@
|
|
|
1
|
+
# MapView
|
|
2
|
+
|
|
3
|
+
Places on an interactive map, with their list beside it. Every row that
|
|
4
|
+
has a position becomes a marker — a dot in a token tone, or your own kit
|
|
5
|
+
content. Markers that crowd merge into a count; click it to zoom in. Click
|
|
6
|
+
a marker and a popup opens with kit parts. The list on the left is
|
|
7
|
+
searchable and shares one selection and one hover with the map: pick a
|
|
8
|
+
row and the map moves to it, pick a marker and the list scrolls to it.
|
|
9
|
+
Zoom buttons and "Show all" sit top right. Rows with no position are
|
|
10
|
+
listed under "Not on the map", never lost.
|
|
11
|
+
|
|
12
|
+
On a phone the map fills the block, a strip of cards sits under it, and a
|
|
13
|
+
"List" button swaps to the full list.
|
|
14
|
+
|
|
15
|
+
It is [MapLibre GL JS](https://github.com/maplibre/maplibre-gl-js)
|
|
16
|
+
(BSD-3-Clause) underneath, through
|
|
17
|
+
[react-map-gl](https://github.com/visgl/react-map-gl) (MIT): its tiles,
|
|
18
|
+
camera, gestures and keyboard (arrows pan, + and − zoom). The markers,
|
|
19
|
+
clusters, popup, list and controls are ours, in the kit and the
|
|
20
|
+
workspace's tokens. The blocks built on this one — `location-map`,
|
|
21
|
+
`store-locator`, the Map view of `work-views` — import it, never the map
|
|
22
|
+
packages.
|
|
23
|
+
|
|
24
|
+
```jsx
|
|
25
|
+
import { MapView, useMapView } from "../components/blocks/map"; // a Makersclaw app ("./blocks/map" from components/)
|
|
26
|
+
import { MapView, useMapView } from "@/components/makersclaw/blocks/map"; // a shadcn project
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
`MapView` is a client component: render it from a `"use client"`
|
|
30
|
+
component, and load the rows in the server surface (README §4). The
|
|
31
|
+
helpers (`readPosition`, `boundsOf`, `distanceKm`…) are plain functions and
|
|
32
|
+
work on the server.
|
|
33
|
+
|
|
34
|
+
`makersclaw add map` adds `maplibre-gl` and `react-map-gl` (pinned, exact)
|
|
35
|
+
to package.json: run `npm install` after. They load with the page (no
|
|
36
|
+
`import()`); the map mounts after the first render. Until then — on the
|
|
37
|
+
server — the frame, the list and a quiet "Loading the map…" show.
|
|
38
|
+
|
|
39
|
+
## What it fetches at run time
|
|
40
|
+
|
|
41
|
+
A map cannot ship its world inside the app. In the browser it fetches:
|
|
42
|
+
|
|
43
|
+
- **The tiles, style and fonts** from the style URL. The default is
|
|
44
|
+
[OpenFreeMap](https://openfreemap.org): free, no key, no sign-up,
|
|
45
|
+
OpenStreetMap data. `positron` (light) or `dark`, picked from the
|
|
46
|
+
workspace's scheme and swapped when it changes. The attribution line
|
|
47
|
+
(bottom right) is the data licence's condition: it stays.
|
|
48
|
+
- **MapLibre's tile worker** from jsDelivr, at exactly the installed
|
|
49
|
+
version (`https://cdn.jsdelivr.net/npm/maplibre-gl@<version>/dist/maplibre-gl-worker.mjs`).
|
|
50
|
+
MapLibre 6 finds its worker beside its own file, which a bundled app
|
|
51
|
+
does not have. Host the file yourself and pass `workerUrl` to avoid
|
|
52
|
+
the CDN.
|
|
53
|
+
|
|
54
|
+
Nothing else leaves the page: no geocoding, no analytics.
|
|
55
|
+
|
|
56
|
+
## The data
|
|
57
|
+
|
|
58
|
+
Rows are your records. A row's position is read by `positionOf`; the
|
|
59
|
+
default reads any of these:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
{ lat: 38.71, lng: -9.14 } // or latitude / longitude, or lon
|
|
63
|
+
{ location: { lat: 38.71, lng: -9.14, label } } // the task-model shape
|
|
64
|
+
{ position: [-9.14, 38.71] } // a GeoJSON pair: [lng, lat]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Numbers or numeric strings, in degrees (WGS 84 — what GPS and every
|
|
68
|
+
geocoder give). A row with no valid position is listed, not mapped.
|
|
69
|
+
|
|
70
|
+
Tones are token names, never colours: `primary` (the default), `success`,
|
|
71
|
+
`warning`, `destructive`, `info`, `chart-1` … `chart-5`, `muted`. A tone
|
|
72
|
+
means something — status, owner, type — so say it in words too (a badge
|
|
73
|
+
in `asideOf`).
|
|
74
|
+
|
|
75
|
+
## Props
|
|
76
|
+
|
|
77
|
+
- `items` — the rows. Pass `useFixture(rows, SAMPLE).rows` to open with an
|
|
78
|
+
example.
|
|
79
|
+
- `positionOf(row)` — `{ lat, lng }` or null. Default: `readPosition` (above).
|
|
80
|
+
- `idOf(row)` — default `row.id`. Ids are compared as strings.
|
|
81
|
+
- `titleOf(row)` — the name on the marker's label, the list row and the
|
|
82
|
+
popup. Default `row.title ?? row.name ?? row.label ?? row.location.label`.
|
|
83
|
+
- `metaOf(row)` — a muted line under the title (list, card, popup): a
|
|
84
|
+
city, an address. A string, or a node.
|
|
85
|
+
- `asideOf(row)` — a node at the row's end: a `Badge`, a distance.
|
|
86
|
+
- `toneOf(row)` — the marker's tone (above). Default `primary`.
|
|
87
|
+
- `searchOf(row)` — the words the search finds a row by. Default: title
|
|
88
|
+
and meta. Every word typed must appear.
|
|
89
|
+
- `renderMarker(row, { selected, hovered, tone })` — your own marker
|
|
90
|
+
content (an `Avatar`, a price, an icon), kit parts only. It sits inside
|
|
91
|
+
the block's button, so clicks, labels and selection still work.
|
|
92
|
+
- `renderPopup(row, { close })` — the popup's content: kit parts and
|
|
93
|
+
tokens. Default: title, meta and aside. The box and its close button
|
|
94
|
+
are the block's.
|
|
95
|
+
- `selectedId` · `onSelect(id, row)` — control the selection (the row
|
|
96
|
+
your form edits, a URL param). `id` is a string, or null when cleared.
|
|
97
|
+
Leave `selectedId` out and the block keeps its own; `defaultSelectedId`
|
|
98
|
+
sets where it starts. A selection set from outside moves the map to it.
|
|
99
|
+
- `onMove(row, { lat, lng })` — markers become draggable. The marker stays
|
|
100
|
+
where it was dropped at once; return `{ ok: true }` or
|
|
101
|
+
`{ ok: false, error }` (a throw counts as failed) and a failed save puts
|
|
102
|
+
it back with a toast saying why. The block keeps the new spot until
|
|
103
|
+
`positionOf(row)` reports a different one.
|
|
104
|
+
- `onMapClick({ lat, lng })` — a click on the map, not a marker ("add a
|
|
105
|
+
place here"). The block also clears the selection.
|
|
106
|
+
- `onViewChange({ center, zoom, bounds })` — after the map stops moving
|
|
107
|
+
(and once on load). `bounds` is `{ west, south, east, north }`: search
|
|
108
|
+
this area, load what is in view.
|
|
109
|
+
- `areas` — tinted regions: `[{ id, label?, tone?, geometry }]`, where
|
|
110
|
+
`geometry` is GeoJSON (`Polygon`, `MultiPolygon`, `LineString`). A soft
|
|
111
|
+
fill and a line in the tone, repainted when the scheme changes.
|
|
112
|
+
- `onAreaClick(area)` — an area is clicked.
|
|
113
|
+
- `list` — `"side"` (default: the list beside the map, the card strip on
|
|
114
|
+
a phone) or `"none"` (the map alone; markers then take Tab focus).
|
|
115
|
+
- `search` — the search field over the list. Default `true`.
|
|
116
|
+
- `cluster` — merge crowded markers. Default `true`. `clusterRadius` —
|
|
117
|
+
how close in pixels, default `48`.
|
|
118
|
+
- `popup` — open a popup for the selected row. Default `true`.
|
|
119
|
+
- `styleUrl` — the tiles: one URL, or `{ light, dark }` to follow the
|
|
120
|
+
scheme. Default `DEFAULT_STYLE` (OpenFreeMap positron / dark).
|
|
121
|
+
`MAP_STYLES` has OpenFreeMap's five: `positron`, `liberty`, `bright`,
|
|
122
|
+
`dark`, `fiord`. Any MapLibre style URL works (MapTiler, Stadia, your
|
|
123
|
+
own), with the provider's key in the URL if it needs one.
|
|
124
|
+
- `scheme` — `"auto"` (default: the workspace's light or dark), `"light"`
|
|
125
|
+
or `"dark"`.
|
|
126
|
+
- `workerUrl` — where MapLibre's worker loads from (above).
|
|
127
|
+
- `initialView` — `{ lat, lng, zoom }` when there are no positions to
|
|
128
|
+
fit. Default: the whole world.
|
|
129
|
+
- `fitOnChange` — refit to the places when the set on the map changes
|
|
130
|
+
(new rows, a search). Default `true`. `fitMaxZoom` — how close a fit
|
|
131
|
+
may go, default `14`. `maxZoom` — default `18`.
|
|
132
|
+
- `scrollZoom` — the wheel zooms. Default `true`; set `false` for a map
|
|
133
|
+
inside a long page (buttons and pinch still zoom).
|
|
134
|
+
- `controls` — the zoom and "Show all" buttons. Default `true`.
|
|
135
|
+
- `height` — the block's height: a number (px) or a CSS length. Default
|
|
136
|
+
`520`. The map needs one; the block never sizes itself to its content.
|
|
137
|
+
- `noun` · `nounPlural` — what a row is called: "account" → "Search
|
|
138
|
+
accounts", "12 accounts", "No accounts yet". Default `place`.
|
|
139
|
+
- `emptyTitle` · `emptyBody` · `emptyAction` — the empty list's words and
|
|
140
|
+
its one button (a `Button`), when `items` is empty.
|
|
141
|
+
- `label` — what the region is called to a screen reader. Default `Map`.
|
|
142
|
+
- `mapRef` — the handle (below).
|
|
143
|
+
- `className` — layout around it (width, margin). Never colour.
|
|
144
|
+
|
|
145
|
+
## The handle
|
|
146
|
+
|
|
147
|
+
```jsx
|
|
148
|
+
const map = useMapView();
|
|
149
|
+
<MapView mapRef={map} items={rows} />
|
|
150
|
+
map.current?.fitAll(); // every place on the map in view
|
|
151
|
+
map.current?.select("acc-07"); // select and move to it
|
|
152
|
+
map.current?.flyTo({ lat: 48.86, lng: 2.35, zoom: 11 });
|
|
153
|
+
map.current?.fitBounds({ west, south, east, north });
|
|
154
|
+
map.current?.zoomIn(); map.current?.zoomOut();
|
|
155
|
+
map.current?.getView(); // { center, zoom, bounds } or null before load
|
|
156
|
+
map.current?.getMap(); // the maplibre Map, for what the props do not cover
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## Helpers
|
|
160
|
+
|
|
161
|
+
- `readPosition(row)` → `{ lat, lng }` or null.
|
|
162
|
+
- `boundsOf(positions)` → `{ west, south, east, north }` or null;
|
|
163
|
+
`inBounds(bounds, position)`.
|
|
164
|
+
- `distanceKm(a, b)` — great-circle distance; `formatDistance(km, "km" | "mi")`
|
|
165
|
+
→ "350 m", "4.2 km", "24 mi".
|
|
166
|
+
- `sortByDistance(rows, origin, positionOf?)` → `[{ row, distanceKm }]`,
|
|
167
|
+
nearest first, rows without a position last.
|
|
168
|
+
- `clusterPoints(points, zoom, radius?)` and `zoomToReveal(points, id, zoom)`
|
|
169
|
+
— the clustering the map uses, for a list that groups the same way.
|
|
170
|
+
|
|
171
|
+
## Example
|
|
172
|
+
|
|
173
|
+
```jsx
|
|
174
|
+
// components/Customers.jsx — client: the map, the list, the popup
|
|
175
|
+
"use client";
|
|
176
|
+
import { Badge, Button } from "./ui";
|
|
177
|
+
import { FixtureNotice, useFixture } from "./parts";
|
|
178
|
+
import { MapView } from "./blocks/map";
|
|
179
|
+
|
|
180
|
+
const SAMPLE = [
|
|
181
|
+
{ name: "Northwind Café", city: "Lisbon", lat: 38.7107, lng: -9.1422, status: "active" },
|
|
182
|
+
{ name: "Acme Bakery", city: "Porto", lat: 41.1405, lng: -8.6132, status: "at risk" },
|
|
183
|
+
];
|
|
184
|
+
|
|
185
|
+
export default function Customers({ customers }) {
|
|
186
|
+
const fixture = useFixture(customers, SAMPLE);
|
|
187
|
+
return (
|
|
188
|
+
<div className="grid gap-4">
|
|
189
|
+
{fixture.isFixture ? <FixtureNotice onClear={fixture.clear} /> : null}
|
|
190
|
+
<MapView
|
|
191
|
+
items={fixture.rows}
|
|
192
|
+
noun="customer"
|
|
193
|
+
titleOf={(c) => c.name}
|
|
194
|
+
metaOf={(c) => c.city}
|
|
195
|
+
toneOf={(c) => (c.status === "active" ? "success" : "warning")}
|
|
196
|
+
asideOf={(c) => <Badge tone={c.status === "active" ? "success" : "warning"} variant="soft">{c.status}</Badge>}
|
|
197
|
+
renderPopup={(c) => (
|
|
198
|
+
<div className="grid gap-2 pr-8">
|
|
199
|
+
<p className="font-medium">{c.name}</p>
|
|
200
|
+
<p className="text-muted-foreground">{c.city}</p>
|
|
201
|
+
<Button size="sm" variant="primary" href={`/customers/${c.id}`}>Open</Button>
|
|
202
|
+
</div>
|
|
203
|
+
)}
|
|
204
|
+
/>
|
|
205
|
+
</div>
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## What it will not do
|
|
211
|
+
|
|
212
|
+
- Geocode. It maps coordinates; turning an address into them is a
|
|
213
|
+
server job (store the result on the row).
|
|
214
|
+
- Heatmaps, routes, 3D buildings, satellite imagery. Pass `styleUrl` for
|
|
215
|
+
another base map; `getMap()` reaches maplibre for the rest.
|
|
216
|
+
- Move a marker from the keyboard. Dragging is pointer-only; keep a
|
|
217
|
+
coordinates field in the row's form for keyboard users.
|
|
218
|
+
- Scale past a few thousand markers. Each marker is a DOM node; for tens
|
|
219
|
+
of thousands, use maplibre layers through `getMap()`.
|
|
220
|
+
- Work offline or without WebGL: the list stays, and the map says why it
|
|
221
|
+
cannot show.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "map",
|
|
3
|
+
"title": "MapView",
|
|
4
|
+
"description": "An interactive map (MapLibre) in the workspace theme: places from rows as markers in token tones or your own kit content, clustered when they crowd, a popup with kit parts, a searchable list beside the map that shares one selection and hover with it, fit-to-all, zoom controls, draggable markers with an optimistic save, and tinted areas for territories. Light and dark tiles follow the workspace scheme (OpenFreeMap by default, no key); `styleUrl` swaps them. Helpers for bounds, clustering and distances run anywhere.",
|
|
5
|
+
"useWhen": "A screen shows records that have a place: customers, stores, offices, field visits, deliveries, events, territories. The location-map and store-locator blocks and the Map view of work-views are built on it. Not for a chart of numbers by region (chart) or a hand-drawn diagram (sketch-canvas, flow-canvas).",
|
|
6
|
+
"tags": ["map", "maps", "location", "geo", "markers", "pins", "cluster", "maplibre", "openstreetmap", "store locator", "territory", "places", "coordinates", "latitude", "longitude", "distance"],
|
|
7
|
+
"exports": ["MapView", "useMapView", "MAP_STYLES", "DEFAULT_STYLE", "TONES", "readPosition", "boundsOf", "inBounds", "clusterPoints", "zoomToReveal", "distanceKm", "formatDistance", "sortByDistance"],
|
|
8
|
+
"files": ["index.js", "map-view.jsx", "list.jsx", "marks.jsx", "maplibre.js", "theme.js", "geo.js"],
|
|
9
|
+
"requires": [],
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"maplibre-gl": "6.11.1",
|
|
12
|
+
"react-map-gl": "8.1.3"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// MapView three ways — the gallery's and the smoke test's surface. Not
|
|
4
|
+
// installed into apps.
|
|
5
|
+
// Accounts a roaster's wholesale cafés: tones by status, a cluster in
|
|
6
|
+
// Lisbon, a popup with kit parts, the list beside the map,
|
|
7
|
+
// markers that drag (with a switch that makes the save fail),
|
|
8
|
+
// one account with no location yet.
|
|
9
|
+
// Territories the reps' regions as tinted areas, each rep's visits as
|
|
10
|
+
// an avatar marker (renderMarker), no list, a handle that
|
|
11
|
+
// flies between regions.
|
|
12
|
+
// Empty a new map: the friendly empty state and its one action.
|
|
13
|
+
import { useState } from "react";
|
|
14
|
+
import { Avatar, Badge, Button, Switch, TabPanel, Tabs, toast } from "../components/ui";
|
|
15
|
+
import { FixtureNotice, useFixture } from "../components/parts";
|
|
16
|
+
import { MapView, useMapView } from "../components/blocks/map";
|
|
17
|
+
|
|
18
|
+
const STATUS = {
|
|
19
|
+
active: { label: "Active", tone: "success", badge: "success" },
|
|
20
|
+
risk: { label: "At risk", tone: "warning", badge: "warning" },
|
|
21
|
+
trial: { label: "Trial", tone: "info", badge: "info" },
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const ACCOUNTS = [
|
|
25
|
+
{ id: "acc-01", name: "Fábrica do Grão", city: "Lisbon · Chiado", lat: 38.7107, lng: -9.1422, status: "active", mrr: 1840, bags: 64 },
|
|
26
|
+
{ id: "acc-02", name: "Copo Alto", city: "Lisbon · Príncipe Real", lat: 38.7167, lng: -9.1489, status: "active", mrr: 1210, bags: 41 },
|
|
27
|
+
{ id: "acc-03", name: "Rua Nova Espresso", city: "Lisbon · Cais do Sodré", lat: 38.7066, lng: -9.1447, status: "risk", mrr: 690, bags: 22 },
|
|
28
|
+
{ id: "acc-04", name: "Alfama Torra", city: "Lisbon · Alfama", lat: 38.7118, lng: -9.1301, status: "trial", mrr: 0, bags: 6 },
|
|
29
|
+
{ id: "acc-05", name: "Estrela Café", city: "Lisbon · Estrela", lat: 38.7131, lng: -9.1597, status: "active", mrr: 980, bags: 33 },
|
|
30
|
+
{ id: "acc-06", name: "Ribeira Roasters", city: "Porto · Ribeira", lat: 41.1405, lng: -8.6132, status: "active", mrr: 1560, bags: 52 },
|
|
31
|
+
{ id: "acc-07", name: "Mercado 14", city: "Madrid · Malasaña", lat: 40.4262, lng: -3.7041, status: "risk", mrr: 740, bags: 25 },
|
|
32
|
+
{ id: "acc-08", name: "Café Brisa", city: "Barcelona · Gràcia", lat: 41.4036, lng: 2.1567, status: "active", mrr: 2120, bags: 71 },
|
|
33
|
+
{ id: "acc-09", name: "Le Comptoir Bleu", city: "Paris · Canal Saint-Martin", lat: 48.8717, lng: 2.3645, status: "trial", mrr: 0, bags: 8 },
|
|
34
|
+
{ id: "acc-10", name: "Maison Sillon", city: "Lyon · Croix-Rousse", lat: 45.7745, lng: 4.8311, status: "active", mrr: 1320, bags: 44 },
|
|
35
|
+
{ id: "acc-11", name: "Kaffeehaus Nord", city: "Berlin · Prenzlauer Berg", lat: 52.5388, lng: 13.4244, status: "active", mrr: 1750, bags: 58 },
|
|
36
|
+
{ id: "acc-12", name: "Grachtbar", city: "Amsterdam · Jordaan", lat: 52.3752, lng: 4.8836, status: "risk", mrr: 560, bags: 19 },
|
|
37
|
+
{ id: "acc-13", name: "Mill & Pour", city: "London · Hackney", lat: 51.5450, lng: -0.0553, status: "active", mrr: 2410, bags: 80 },
|
|
38
|
+
{ id: "acc-14", name: "Liffey Cup", city: "Dublin", status: "trial", mrr: 0, bags: 4 },
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
const euro = (n) => (n ? `€${n.toLocaleString("en")}/mo` : "Trial");
|
|
42
|
+
|
|
43
|
+
export default function MapDemo() {
|
|
44
|
+
const [tab, setTab] = useState("accounts");
|
|
45
|
+
return (
|
|
46
|
+
<div className="grid gap-4">
|
|
47
|
+
<Tabs
|
|
48
|
+
aria-label="Map examples"
|
|
49
|
+
tabs={[
|
|
50
|
+
{ key: "accounts", label: "Accounts" },
|
|
51
|
+
{ key: "territories", label: "Territories" },
|
|
52
|
+
{ key: "empty", label: "Empty" },
|
|
53
|
+
]}
|
|
54
|
+
value={tab}
|
|
55
|
+
onChange={setTab}
|
|
56
|
+
/>
|
|
57
|
+
<TabPanel tab="accounts" value={tab}>
|
|
58
|
+
<Accounts />
|
|
59
|
+
</TabPanel>
|
|
60
|
+
<TabPanel tab="territories" value={tab}>
|
|
61
|
+
{tab === "territories" ? <Territories /> : null}
|
|
62
|
+
</TabPanel>
|
|
63
|
+
<TabPanel tab="empty" value={tab}>
|
|
64
|
+
{tab === "empty" ? (
|
|
65
|
+
<MapView
|
|
66
|
+
items={[]}
|
|
67
|
+
noun="account"
|
|
68
|
+
emptyTitle="No accounts on the map yet"
|
|
69
|
+
emptyBody="Add an account with an address and it lands here, with its neighbours."
|
|
70
|
+
emptyAction={
|
|
71
|
+
<Button variant="primary" size="sm" icon="plus" onClick={() => toast("A new account form would open here")}>
|
|
72
|
+
Add account
|
|
73
|
+
</Button>
|
|
74
|
+
}
|
|
75
|
+
height={420}
|
|
76
|
+
/>
|
|
77
|
+
) : null}
|
|
78
|
+
</TabPanel>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function Accounts() {
|
|
84
|
+
const fixture = useFixture([], ACCOUNTS);
|
|
85
|
+
const [moved, setMoved] = useState({});
|
|
86
|
+
const [failSaves, setFailSaves] = useState(false);
|
|
87
|
+
const [selected, setSelected] = useState(null);
|
|
88
|
+
const rows = fixture.rows.map((r) => (moved[r.id] ? { ...r, ...moved[r.id] } : r));
|
|
89
|
+
|
|
90
|
+
async function onMove(row, pos) {
|
|
91
|
+
await new Promise((res) => setTimeout(res, 500));
|
|
92
|
+
if (failSaves) return { ok: false, error: "The server said no (the failing-save switch is on)." };
|
|
93
|
+
setMoved((m) => ({ ...m, [row.id]: pos }));
|
|
94
|
+
toast.success(`${row.name} moved`);
|
|
95
|
+
return { ok: true };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return (
|
|
99
|
+
<div className="grid gap-3">
|
|
100
|
+
{fixture.isFixture ? <FixtureNotice onClear={fixture.clear} /> : null}
|
|
101
|
+
<MapView
|
|
102
|
+
items={rows}
|
|
103
|
+
noun="account"
|
|
104
|
+
titleOf={(r) => r.name}
|
|
105
|
+
metaOf={(r) => r.city}
|
|
106
|
+
toneOf={(r) => STATUS[r.status].tone}
|
|
107
|
+
asideOf={(r) => (
|
|
108
|
+
<Badge tone={STATUS[r.status].badge} variant="soft">
|
|
109
|
+
{STATUS[r.status].label}
|
|
110
|
+
</Badge>
|
|
111
|
+
)}
|
|
112
|
+
searchOf={(r) => `${r.name} ${r.city} ${STATUS[r.status].label}`}
|
|
113
|
+
selectedId={selected}
|
|
114
|
+
onSelect={(id) => setSelected(id)}
|
|
115
|
+
onMove={onMove}
|
|
116
|
+
renderPopup={(r, { close }) => (
|
|
117
|
+
<div className="grid gap-3 pr-8">
|
|
118
|
+
<div className="grid gap-0.5">
|
|
119
|
+
<p className="font-medium leading-5">{r.name}</p>
|
|
120
|
+
<p className="text-muted-foreground">{r.city}</p>
|
|
121
|
+
</div>
|
|
122
|
+
<div className="flex items-center gap-3 text-sm">
|
|
123
|
+
<Badge tone={STATUS[r.status].badge} variant="soft">
|
|
124
|
+
{STATUS[r.status].label}
|
|
125
|
+
</Badge>
|
|
126
|
+
<span className="tabular-nums">{euro(r.mrr)}</span>
|
|
127
|
+
<span className="tabular-nums text-muted-foreground">{r.bags} bags</span>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="flex gap-2">
|
|
130
|
+
<Button
|
|
131
|
+
size="sm"
|
|
132
|
+
variant="primary"
|
|
133
|
+
onClick={() => {
|
|
134
|
+
toast(`Opening ${r.name}`);
|
|
135
|
+
close();
|
|
136
|
+
}}
|
|
137
|
+
>
|
|
138
|
+
Open account
|
|
139
|
+
</Button>
|
|
140
|
+
<Button size="sm" variant="quiet" icon="calendar" onClick={() => toast(`Visit planned at ${r.name}`)}>
|
|
141
|
+
Plan a visit
|
|
142
|
+
</Button>
|
|
143
|
+
</div>
|
|
144
|
+
</div>
|
|
145
|
+
)}
|
|
146
|
+
/>
|
|
147
|
+
<Switch id="map-fail" checked={failSaves} onChange={(e) => setFailSaves(e.target.checked)} label="Make the next drag fail to save" />
|
|
148
|
+
</div>
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const REGIONS = [
|
|
153
|
+
{ id: "iberia", label: "Iberia · Inês", tone: "chart-2", center: { lat: 40.2, lng: -5.5, zoom: 4.6 }, ring: [[-9.6, 36.9], [3.3, 36.9], [3.3, 43.8], [-9.6, 43.8]] },
|
|
154
|
+
{ id: "france", label: "France · Hugo", tone: "chart-3", center: { lat: 46.6, lng: 2.4, zoom: 4.8 }, ring: [[-4.8, 42.4], [7.9, 42.4], [7.9, 51.1], [-4.8, 51.1]] },
|
|
155
|
+
{ id: "north", label: "North · Femke", tone: "chart-4", center: { lat: 52.4, lng: 7.5, zoom: 4.8 }, ring: [[-6.5, 49.9], [15.1, 49.9], [15.1, 55.2], [-6.5, 55.2]] },
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
const AREAS = REGIONS.map((r) => ({ id: r.id, label: r.label, tone: r.tone, geometry: { type: "Polygon", coordinates: [[...r.ring, r.ring[0]]] } }));
|
|
159
|
+
|
|
160
|
+
const VISITS = [
|
|
161
|
+
{ id: "v-1", rep: "Inês Duarte", where: "Porto", lat: 41.15, lng: -8.61, region: "iberia" },
|
|
162
|
+
{ id: "v-2", rep: "Inês Duarte", where: "Seville", lat: 37.39, lng: -5.98, region: "iberia" },
|
|
163
|
+
{ id: "v-3", rep: "Hugo Marchal", where: "Bordeaux", lat: 44.84, lng: -0.58, region: "france" },
|
|
164
|
+
{ id: "v-4", rep: "Hugo Marchal", where: "Lille", lat: 50.63, lng: 3.06, region: "france" },
|
|
165
|
+
{ id: "v-5", rep: "Femke de Vries", where: "Hamburg", lat: 53.55, lng: 9.99, region: "north" },
|
|
166
|
+
{ id: "v-6", rep: "Femke de Vries", where: "Utrecht", lat: 52.09, lng: 5.12, region: "north" },
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
function Territories() {
|
|
170
|
+
const map = useMapView();
|
|
171
|
+
const visits = useFixture([], VISITS);
|
|
172
|
+
return (
|
|
173
|
+
<div className="grid gap-3">
|
|
174
|
+
<div className="flex flex-wrap gap-2">
|
|
175
|
+
{REGIONS.map((r) => (
|
|
176
|
+
<Button key={r.id} size="sm" variant="secondary" onClick={() => map.current?.flyTo(r.center)}>
|
|
177
|
+
{r.label}
|
|
178
|
+
</Button>
|
|
179
|
+
))}
|
|
180
|
+
<Button size="sm" variant="quiet" icon="maximize" onClick={() => map.current?.fitAll()}>
|
|
181
|
+
All regions
|
|
182
|
+
</Button>
|
|
183
|
+
</div>
|
|
184
|
+
<MapView
|
|
185
|
+
mapRef={map}
|
|
186
|
+
items={visits.rows}
|
|
187
|
+
list="none"
|
|
188
|
+
cluster={false}
|
|
189
|
+
noun="visit"
|
|
190
|
+
titleOf={(v) => `${v.rep} · ${v.where}`}
|
|
191
|
+
metaOf={(v) => `Next visit in ${v.where}`}
|
|
192
|
+
toneOf={(v) => REGIONS.find((r) => r.id === v.region)?.tone}
|
|
193
|
+
renderMarker={(v, { selected }) => <Avatar name={v.rep} size="xs" className={selected ? "ring-2 ring-primary" : "ring-2 ring-background"} />}
|
|
194
|
+
areas={AREAS}
|
|
195
|
+
onAreaClick={(a) => toast(a.label)}
|
|
196
|
+
height={440}
|
|
197
|
+
label="Sales territories"
|
|
198
|
+
/>
|
|
199
|
+
</div>
|
|
200
|
+
);
|
|
201
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
// The map's arithmetic: plain functions of plain objects, so they run in
|
|
2
|
+
// node, on the server and in the blocks built on this one (location-map,
|
|
3
|
+
// store-locator, the Map view of work-views). No maplibre in here.
|
|
4
|
+
//
|
|
5
|
+
// A position is { lat, lng } in degrees (WGS 84, what every geocoder and
|
|
6
|
+
// GPS gives). Longitude first only where maplibre wants it ([lng, lat]).
|
|
7
|
+
|
|
8
|
+
const EARTH_KM = 6371.0088;
|
|
9
|
+
const MAX_LAT = 85.05112878; // web mercator stops here
|
|
10
|
+
|
|
11
|
+
const num = (v) => (typeof v === "string" && v.trim() !== "" ? Number(v) : v);
|
|
12
|
+
const isLat = (v) => typeof v === "number" && Number.isFinite(v) && v >= -90 && v <= 90;
|
|
13
|
+
const isLng = (v) => typeof v === "number" && Number.isFinite(v) && v >= -180 && v <= 180;
|
|
14
|
+
|
|
15
|
+
/** A valid { lat, lng } from whatever a row holds, or null. Reads
|
|
16
|
+
* `lat`/`lng`, `latitude`/`longitude`, `lon`, `location: { lat, lng }`
|
|
17
|
+
* (the task-model shape), `position` and `[lng, lat]` pairs (GeoJSON order). */
|
|
18
|
+
export function readPosition(item) {
|
|
19
|
+
if (!item || typeof item !== "object") return null;
|
|
20
|
+
if (Array.isArray(item)) return toPos(num(item[1]), num(item[0]));
|
|
21
|
+
const direct = toPos(num(item.lat ?? item.latitude), num(item.lng ?? item.lon ?? item.longitude));
|
|
22
|
+
if (direct) return direct;
|
|
23
|
+
for (const key of ["location", "position", "coords", "geo"]) {
|
|
24
|
+
const inner = item[key];
|
|
25
|
+
if (inner && typeof inner === "object" && inner !== item) {
|
|
26
|
+
const p = readPosition(inner);
|
|
27
|
+
if (p) return p;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function toPos(lat, lng) {
|
|
34
|
+
return isLat(lat) && isLng(lng) ? { lat, lng } : null;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** [lng, lat] → a point on the web-mercator world at `zoom`, in pixels
|
|
38
|
+
* (the world is 512 px wide at zoom 0, as maplibre draws it). */
|
|
39
|
+
export function project({ lat, lng }, zoom) {
|
|
40
|
+
const size = 512 * 2 ** zoom;
|
|
41
|
+
const clamped = Math.max(-MAX_LAT, Math.min(MAX_LAT, lat));
|
|
42
|
+
const s = Math.sin((clamped * Math.PI) / 180);
|
|
43
|
+
return {
|
|
44
|
+
x: ((lng + 180) / 360) * size,
|
|
45
|
+
y: (0.5 - Math.log((1 + s) / (1 - s)) / (4 * Math.PI)) * size,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The box around some positions: { west, south, east, north }, or null
|
|
50
|
+
* for none. One position gives a box of no size (fitBounds pads it). */
|
|
51
|
+
export function boundsOf(positions) {
|
|
52
|
+
let west = Infinity;
|
|
53
|
+
let south = Infinity;
|
|
54
|
+
let east = -Infinity;
|
|
55
|
+
let north = -Infinity;
|
|
56
|
+
for (const p of positions ?? []) {
|
|
57
|
+
if (!p) continue;
|
|
58
|
+
if (p.lng < west) west = p.lng;
|
|
59
|
+
if (p.lng > east) east = p.lng;
|
|
60
|
+
if (p.lat < south) south = p.lat;
|
|
61
|
+
if (p.lat > north) north = p.lat;
|
|
62
|
+
}
|
|
63
|
+
return west === Infinity ? null : { west, south, east, north };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Is the box a single spot (every position the same, give or take a metre)? */
|
|
67
|
+
export function isPointBounds(b) {
|
|
68
|
+
return Boolean(b) && Math.abs(b.east - b.west) < 1e-5 && Math.abs(b.north - b.south) < 1e-5;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** Does `b` hold the position? */
|
|
72
|
+
export function inBounds(b, p) {
|
|
73
|
+
if (!b || !p) return false;
|
|
74
|
+
const lngIn = b.west <= b.east ? p.lng >= b.west && p.lng <= b.east : p.lng >= b.west || p.lng <= b.east; // across the date line
|
|
75
|
+
return lngIn && p.lat >= b.south && p.lat <= b.north;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Group points that would overlap on screen at `zoom`.
|
|
80
|
+
*
|
|
81
|
+
* points [{ id, lat, lng }]
|
|
82
|
+
* radius how close, in screen pixels, two points may sit before they
|
|
83
|
+
* merge (default 48 — a marker and its label)
|
|
84
|
+
*
|
|
85
|
+
* Returns, in the input order of each group's first point:
|
|
86
|
+
* { type: "point", id, lat, lng }
|
|
87
|
+
* { type: "cluster", id, ids, count, lat, lng, bounds }
|
|
88
|
+
*
|
|
89
|
+
* Greedy, like supercluster's single level: each point not yet taken
|
|
90
|
+
* gathers every free point within `radius` of it (a grid of radius-sized
|
|
91
|
+
* cells finds them), and the group sits at their mean. It depends on the
|
|
92
|
+
* zoom only, never on the pan, so the map recomputes it when the zoom
|
|
93
|
+
* crosses a step, not on every frame. Thousands of points take a few ms.
|
|
94
|
+
*/
|
|
95
|
+
export function clusterPoints(points, zoom, radius = 48) {
|
|
96
|
+
const list = (points ?? []).filter((p) => p && isLat(p.lat) && isLng(p.lng));
|
|
97
|
+
const px = list.map((p) => project(p, zoom));
|
|
98
|
+
const cell = Math.max(1, radius);
|
|
99
|
+
const grid = new Map();
|
|
100
|
+
px.forEach((q, i) => {
|
|
101
|
+
const key = `${Math.floor(q.x / cell)}:${Math.floor(q.y / cell)}`;
|
|
102
|
+
const bucket = grid.get(key);
|
|
103
|
+
if (bucket) bucket.push(i);
|
|
104
|
+
else grid.set(key, [i]);
|
|
105
|
+
});
|
|
106
|
+
const taken = new Uint8Array(list.length);
|
|
107
|
+
const out = [];
|
|
108
|
+
const r2 = radius * radius;
|
|
109
|
+
for (let i = 0; i < list.length; i++) {
|
|
110
|
+
if (taken[i]) continue;
|
|
111
|
+
taken[i] = 1;
|
|
112
|
+
const members = [i];
|
|
113
|
+
const cx = Math.floor(px[i].x / cell);
|
|
114
|
+
const cy = Math.floor(px[i].y / cell);
|
|
115
|
+
for (let gx = cx - 1; gx <= cx + 1; gx++) {
|
|
116
|
+
for (let gy = cy - 1; gy <= cy + 1; gy++) {
|
|
117
|
+
for (const j of grid.get(`${gx}:${gy}`) ?? []) {
|
|
118
|
+
if (taken[j]) continue;
|
|
119
|
+
const dx = px[j].x - px[i].x;
|
|
120
|
+
const dy = px[j].y - px[i].y;
|
|
121
|
+
if (dx * dx + dy * dy <= r2) {
|
|
122
|
+
taken[j] = 1;
|
|
123
|
+
members.push(j);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (members.length === 1) {
|
|
129
|
+
const p = list[i];
|
|
130
|
+
out.push({ type: "point", id: p.id, lat: p.lat, lng: p.lng });
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const ps = members.map((m) => list[m]);
|
|
134
|
+
out.push({
|
|
135
|
+
type: "cluster",
|
|
136
|
+
id: `cluster:${ps[0].id}:${ps.length}`,
|
|
137
|
+
ids: ps.map((p) => p.id),
|
|
138
|
+
count: ps.length,
|
|
139
|
+
lat: ps.reduce((s, p) => s + p.lat, 0) / ps.length,
|
|
140
|
+
lng: ps.reduce((s, p) => s + p.lng, 0) / ps.length,
|
|
141
|
+
bounds: boundsOf(ps),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return out;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** The lowest zoom, from `zoom` up in half steps to `maxZoom`, at which
|
|
148
|
+
* point `id` stands on its own — where the map must go to show it. Null
|
|
149
|
+
* when it never does (it shares its spot with another point). */
|
|
150
|
+
export function zoomToReveal(points, id, zoom, { radius = 48, maxZoom = 18 } = {}) {
|
|
151
|
+
for (let z = Math.max(0, Math.ceil(zoom * 2) / 2); z <= maxZoom; z += 0.5) {
|
|
152
|
+
const groups = clusterPoints(points, z, radius);
|
|
153
|
+
if (groups.some((g) => g.type === "point" && g.id === id)) return z;
|
|
154
|
+
}
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/** Great-circle distance between two positions, in kilometres. */
|
|
159
|
+
export function distanceKm(a, b) {
|
|
160
|
+
if (!a || !b) return NaN;
|
|
161
|
+
const rad = Math.PI / 180;
|
|
162
|
+
const dLat = (b.lat - a.lat) * rad;
|
|
163
|
+
const dLng = (b.lng - a.lng) * rad;
|
|
164
|
+
const h = Math.sin(dLat / 2) ** 2 + Math.cos(a.lat * rad) * Math.cos(b.lat * rad) * Math.sin(dLng / 2) ** 2;
|
|
165
|
+
return 2 * EARTH_KM * Math.asin(Math.min(1, Math.sqrt(h)));
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** "350 m", "4.2 km", "38 km" — or miles with unit "mi" ("0.3 mi", "24 mi"). */
|
|
169
|
+
export function formatDistance(km, unit = "km") {
|
|
170
|
+
if (!Number.isFinite(km)) return "";
|
|
171
|
+
if (unit === "mi") {
|
|
172
|
+
const mi = km / 1.609344;
|
|
173
|
+
return mi < 10 ? `${mi.toFixed(1)} mi` : `${Math.round(mi).toLocaleString("en")} mi`;
|
|
174
|
+
}
|
|
175
|
+
if (km < 1) return `${Math.max(10, Math.round((km * 1000) / 10) * 10)} m`;
|
|
176
|
+
return km < 10 ? `${km.toFixed(1)} km` : `${Math.round(km).toLocaleString("en")} km`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/** Rows nearest first from `origin`, each with its `distanceKm`; rows
|
|
180
|
+
* with no position go last (distance NaN). `positionOf` reads a row. */
|
|
181
|
+
export function sortByDistance(rows, origin, positionOf = readPosition) {
|
|
182
|
+
return (rows ?? [])
|
|
183
|
+
.map((row) => ({ row, distanceKm: distanceKm(origin, positionOf(row)) }))
|
|
184
|
+
.sort((a, b) => (Number.isNaN(a.distanceKm) ? 1 : 0) - (Number.isNaN(b.distanceKm) ? 1 : 0) || a.distanceKm - b.distanceKm);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** A cheap key for a set of ids: equal keys, the same set. */
|
|
188
|
+
export function idsKey(ids) {
|
|
189
|
+
return [...ids].map(String).sort().join("|");
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** Lower-cased words a row is found by: the title and the meta line. */
|
|
193
|
+
export function matches(text, query) {
|
|
194
|
+
const q = String(query ?? "").trim().toLowerCase();
|
|
195
|
+
if (!q) return true;
|
|
196
|
+
const hay = String(text ?? "").toLowerCase();
|
|
197
|
+
return q.split(/\s+/).every((w) => hay.includes(w));
|
|
198
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// The Map block — `makersclaw add map`. Fixed: compose it through its
|
|
2
|
+
// props (README.md beside this file). The helpers (geo.js) are plain
|
|
3
|
+
// functions and work on the server; the blocks built on this one
|
|
4
|
+
// (location-map, store-locator, the Map view of work-views) import it,
|
|
5
|
+
// never maplibre-gl or react-map-gl directly.
|
|
6
|
+
export { MapView, useMapView } from "./map-view";
|
|
7
|
+
export { MAP_STYLES, DEFAULT_STYLE, TONES } from "./theme";
|
|
8
|
+
export { readPosition, boundsOf, inBounds, clusterPoints, zoomToReveal, distanceKm, formatDistance, sortByDistance } from "./geo";
|