makersclaw 0.8.0 → 0.8.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/README.md +9 -3
- package/package.json +1 -1
- package/r/activity-log.json +52 -0
- package/r/agent-changes.json +51 -0
- package/r/agent-chat.json +53 -0
- package/r/agent-column.json +85 -0
- package/r/agent-dock.json +57 -0
- package/r/agent-kit.json +49 -0
- package/r/agent-marks.json +49 -0
- package/r/agent-pane.json +93 -0
- package/r/ai-action.json +56 -0
- package/r/annotate.json +3 -3
- package/r/approval-request.json +2 -2
- package/r/approval.json +54 -0
- package/r/ask-bar.json +50 -0
- package/r/audio-player.json +2 -2
- package/r/auth-form.json +2 -2
- package/r/automation-editor.json +66 -0
- package/r/automation-list.json +57 -0
- package/r/automation-model.json +118 -0
- package/r/automations-screen.json +71 -0
- package/r/board.json +2 -2
- package/r/calendar-view.json +3 -3
- package/r/cart.json +2 -2
- package/r/chart.json +2 -2
- package/r/chat-pane.json +84 -0
- package/r/chat.json +2 -2
- package/r/code-editor.json +2 -2
- package/r/comment-box.json +3 -3
- package/r/connect-card.json +51 -0
- package/r/contact-section.json +2 -2
- package/r/core.json +13 -7
- package/r/cost.json +51 -0
- package/r/csv-import.json +3 -3
- package/r/data-picker.json +60 -0
- package/r/data-table.json +2 -2
- package/r/diagram.json +2 -2
- package/r/doc-editor.json +2 -2
- package/r/doc-page.json +3 -3
- package/r/email-render.json +2 -2
- package/r/faq.json +2 -2
- package/r/file-upload.json +2 -2
- package/r/first-run.json +63 -0
- package/r/fix-note.json +57 -0
- package/r/flow-canvas.json +2 -2
- package/r/flow-editor.json +79 -0
- package/r/footer.json +2 -2
- package/r/gantt.json +3 -3
- package/r/job-panel.json +2 -2
- package/r/json-editor.json +3 -3
- package/r/library-shelf.json +56 -0
- package/r/map.json +2 -2
- package/r/margin-notes.json +67 -0
- package/r/menubar.json +2 -2
- package/r/mind-map.json +3 -3
- package/r/navigation-menu.json +2 -2
- package/r/needs-you.json +55 -0
- package/r/order-detail.json +2 -2
- package/r/org-chart.json +3 -3
- package/r/pace-bar.json +69 -0
- package/r/page-builder.json +2 -2
- package/r/payment-methods.json +2 -2
- package/r/pdf-document.json +2 -2
- package/r/pdf-viewer.json +2 -2
- package/r/product-detail.json +2 -2
- package/r/product-grid.json +3 -3
- package/r/record-form.json +2 -2
- package/r/record-grid.json +52 -0
- package/r/record-notes.json +4 -4
- package/r/registry.json +4086 -1512
- package/r/run-history.json +52 -0
- package/r/run-view.json +80 -0
- package/r/schedule-card.json +54 -0
- package/r/schedule-engine.json +79 -0
- package/r/schedule-gantt.json +83 -0
- package/r/schedule-ruler.json +64 -0
- package/r/search-dialog.json +2 -2
- package/r/settings-list.json +2 -2
- package/r/setup-checklist.json +2 -2
- package/r/share-dialog.json +2 -2
- package/r/sidebar.json +2 -2
- package/r/signature-pad.json +2 -2
- package/r/sketch-canvas.json +2 -2
- package/r/source-receipt.json +52 -0
- package/r/spreadsheet.json +3 -3
- package/r/stat-cards.json +3 -3
- package/r/step-picker.json +53 -0
- package/r/step-settings.json +72 -0
- package/r/suggestion.json +58 -0
- package/r/sum-bar.json +64 -0
- package/r/task-list.json +3 -3
- package/r/task-model.json +2 -2
- package/r/testimonials.json +2 -2
- package/r/tier-map.json +67 -0
- package/r/time-grid.json +73 -0
- package/r/time-strip.json +64 -0
- package/r/top-bar.json +66 -0
- package/r/transaction-list.json +2 -2
- package/r/trigger-picker.json +61 -0
- package/r/whiteboard.json +3 -3
- package/r/workflow-view.json +55 -0
- package/registry/blocks/activity-log/README.md +112 -0
- package/registry/blocks/activity-log/activity-log.jsx +351 -0
- package/registry/blocks/activity-log/block.json +33 -0
- package/registry/blocks/activity-log/demo.jsx +48 -0
- package/registry/blocks/activity-log/index.js +4 -0
- package/registry/blocks/agent-changes/README.md +94 -0
- package/registry/blocks/agent-changes/agent-changes.jsx +118 -0
- package/registry/blocks/agent-changes/block.json +28 -0
- package/registry/blocks/agent-changes/demo.jsx +39 -0
- package/registry/blocks/agent-changes/index.js +3 -0
- package/registry/blocks/agent-chat/README.md +145 -0
- package/registry/blocks/agent-chat/agent-chat.jsx +415 -0
- package/registry/blocks/agent-chat/block.json +30 -0
- package/registry/blocks/agent-chat/demo.jsx +165 -0
- package/registry/blocks/agent-chat/index.js +3 -0
- package/registry/blocks/agent-column/README.md +197 -0
- package/registry/blocks/agent-column/agent-column.jsx +492 -0
- package/registry/blocks/agent-column/block.json +67 -0
- package/registry/blocks/agent-column/column-picker.jsx +163 -0
- package/registry/blocks/agent-column/demo.jsx +50 -0
- package/registry/blocks/agent-column/index.js +7 -0
- package/registry/blocks/agent-column/model.js +164 -0
- package/registry/blocks/agent-column/receipt.jsx +483 -0
- package/registry/blocks/agent-column/spreadsheet-type.jsx +24 -0
- package/registry/blocks/agent-dock/README.md +210 -0
- package/registry/blocks/agent-dock/agent-dock.jsx +356 -0
- package/registry/blocks/agent-dock/block.json +47 -0
- package/registry/blocks/agent-dock/demo.jsx +153 -0
- package/registry/blocks/agent-dock/dock-model.js +118 -0
- package/registry/blocks/agent-dock/index.js +5 -0
- package/registry/blocks/agent-kit/README.md +73 -0
- package/registry/blocks/agent-kit/agent-kit.jsx +86 -0
- package/registry/blocks/agent-kit/block.json +10 -0
- package/registry/blocks/agent-kit/demo.jsx +35 -0
- package/registry/blocks/agent-kit/index.js +3 -0
- package/registry/blocks/agent-marks/README.md +80 -0
- package/registry/blocks/agent-marks/agent-marks.jsx +133 -0
- package/registry/blocks/agent-marks/block.json +27 -0
- package/registry/blocks/agent-marks/demo.jsx +49 -0
- package/registry/blocks/agent-marks/index.js +3 -0
- package/registry/blocks/agent-pane/README.md +225 -0
- package/registry/blocks/agent-pane/agent-pane.jsx +244 -0
- package/registry/blocks/agent-pane/block.json +41 -0
- package/registry/blocks/agent-pane/composer.jsx +6 -0
- package/registry/blocks/agent-pane/demo.jsx +233 -0
- package/registry/blocks/agent-pane/dock-context.js +10 -0
- package/registry/blocks/agent-pane/index.js +4 -0
- package/registry/blocks/agent-pane/prose.jsx +84 -0
- package/registry/blocks/agent-pane/questions.jsx +134 -0
- package/registry/blocks/agent-pane/transcript.jsx +268 -0
- package/registry/blocks/agent-pane/work.jsx +368 -0
- package/registry/blocks/ai-action/README.md +94 -0
- package/registry/blocks/ai-action/ai-action.jsx +188 -0
- package/registry/blocks/ai-action/block.json +34 -0
- package/registry/blocks/ai-action/demo.jsx +93 -0
- package/registry/blocks/ai-action/index.js +3 -0
- package/registry/blocks/approval/README.md +145 -0
- package/registry/blocks/approval/approval.jsx +163 -0
- package/registry/blocks/approval/block.json +31 -0
- package/registry/blocks/approval/demo.jsx +121 -0
- package/registry/blocks/approval/index.js +3 -0
- package/registry/blocks/ask-bar/README.md +105 -0
- package/registry/blocks/ask-bar/ask-bar.jsx +166 -0
- package/registry/blocks/ask-bar/block.json +26 -0
- package/registry/blocks/ask-bar/demo.jsx +78 -0
- package/registry/blocks/ask-bar/index.js +3 -0
- package/registry/blocks/automation-editor/README.md +291 -0
- package/registry/blocks/automation-editor/automation-editor.jsx +656 -0
- package/registry/blocks/automation-editor/block.json +39 -0
- package/registry/blocks/automation-editor/demo.jsx +284 -0
- package/registry/blocks/automation-editor/index.js +4 -0
- package/registry/blocks/automation-editor/run-inputs.jsx +82 -0
- package/registry/blocks/automation-list/README.md +129 -0
- package/registry/blocks/automation-list/automation-list.jsx +476 -0
- package/registry/blocks/automation-list/block.json +35 -0
- package/registry/blocks/automation-list/demo.jsx +67 -0
- package/registry/blocks/automation-list/index.js +3 -0
- package/registry/blocks/automation-model/README.md +109 -0
- package/registry/blocks/automation-model/block.json +135 -0
- package/registry/blocks/automation-model/catalog.js +256 -0
- package/registry/blocks/automation-model/check.js +161 -0
- package/registry/blocks/automation-model/demo.jsx +42 -0
- package/registry/blocks/automation-model/edit.js +262 -0
- package/registry/blocks/automation-model/fixes.js +287 -0
- package/registry/blocks/automation-model/glyph.jsx +13 -0
- package/registry/blocks/automation-model/graph.js +410 -0
- package/registry/blocks/automation-model/index.js +33 -0
- package/registry/blocks/automation-model/overlay.js +175 -0
- package/registry/blocks/automation-model/parts.jsx +94 -0
- package/registry/blocks/automation-model/schedule.js +53 -0
- package/registry/blocks/automation-model/templates.js +128 -0
- package/registry/blocks/automation-model/values.js +29 -0
- package/registry/blocks/automation-model/words.js +439 -0
- package/registry/blocks/automations-screen/README.md +207 -0
- package/registry/blocks/automations-screen/automations-screen.jsx +452 -0
- package/registry/blocks/automations-screen/block.json +47 -0
- package/registry/blocks/automations-screen/demo.jsx +364 -0
- package/registry/blocks/automations-screen/index.js +4 -0
- package/registry/blocks/automations-screen/screen-model.js +129 -0
- package/registry/blocks/chat-pane/README.md +143 -0
- package/registry/blocks/chat-pane/block.json +48 -0
- package/registry/blocks/chat-pane/chat-attach.js +166 -0
- package/registry/blocks/chat-pane/chat-composer.jsx +195 -0
- package/registry/blocks/chat-pane/chat-context.js +17 -0
- package/registry/blocks/chat-pane/chat-model.js +43 -0
- package/registry/blocks/chat-pane/chat-pane.jsx +398 -0
- package/registry/blocks/chat-pane/chats-list.jsx +52 -0
- package/registry/blocks/chat-pane/demo.jsx +103 -0
- package/registry/blocks/chat-pane/index.js +9 -0
- package/registry/blocks/connect-card/README.md +99 -0
- package/registry/blocks/connect-card/block.json +10 -0
- package/registry/blocks/connect-card/connect-card.jsx +103 -0
- package/registry/blocks/connect-card/demo.jsx +49 -0
- package/registry/blocks/connect-card/index.js +3 -0
- package/registry/blocks/cost/README.md +115 -0
- package/registry/blocks/cost/block.json +10 -0
- package/registry/blocks/cost/cost.jsx +176 -0
- package/registry/blocks/cost/demo.jsx +66 -0
- package/registry/blocks/cost/index.js +3 -0
- package/registry/blocks/data-picker/README.md +81 -0
- package/registry/blocks/data-picker/block.json +38 -0
- package/registry/blocks/data-picker/data-picker.jsx +210 -0
- package/registry/blocks/data-picker/demo.jsx +34 -0
- package/registry/blocks/data-picker/fields.js +89 -0
- package/registry/blocks/data-picker/index.js +4 -0
- package/registry/blocks/data-table/demo.jsx +1 -1
- package/registry/blocks/first-run/README.md +118 -0
- package/registry/blocks/first-run/block.json +11 -0
- package/registry/blocks/first-run/demo.jsx +88 -0
- package/registry/blocks/first-run/first-run-model.js +51 -0
- package/registry/blocks/first-run/first-run.jsx +156 -0
- package/registry/blocks/first-run/index.js +5 -0
- package/registry/blocks/first-run/probe-rules.js +26 -0
- package/registry/blocks/fix-note/README.md +79 -0
- package/registry/blocks/fix-note/block.json +35 -0
- package/registry/blocks/fix-note/demo.jsx +30 -0
- package/registry/blocks/fix-note/fix-note.jsx +235 -0
- package/registry/blocks/fix-note/index.js +3 -0
- package/registry/blocks/flow-editor/README.md +108 -0
- package/registry/blocks/flow-editor/block.json +44 -0
- package/registry/blocks/flow-editor/demo.jsx +115 -0
- package/registry/blocks/flow-editor/flow-canvas.jsx +568 -0
- package/registry/blocks/flow-editor/flow-editor.jsx +154 -0
- package/registry/blocks/flow-editor/index.js +6 -0
- package/registry/blocks/flow-editor/minimap.jsx +78 -0
- package/registry/blocks/flow-editor/outline.jsx +134 -0
- package/registry/blocks/library-shelf/README.md +117 -0
- package/registry/blocks/library-shelf/block.json +28 -0
- package/registry/blocks/library-shelf/demo.jsx +68 -0
- package/registry/blocks/library-shelf/index.js +3 -0
- package/registry/blocks/library-shelf/library-shelf.jsx +211 -0
- package/registry/blocks/library-shelf/probe-rules.js +15 -0
- package/registry/blocks/margin-notes/README.md +222 -0
- package/registry/blocks/margin-notes/block.json +10 -0
- package/registry/blocks/margin-notes/demo.jsx +144 -0
- package/registry/blocks/margin-notes/index.js +20 -0
- package/registry/blocks/margin-notes/margin-model.js +224 -0
- package/registry/blocks/margin-notes/margin-notes.jsx +465 -0
- package/registry/blocks/needs-you/README.md +177 -0
- package/registry/blocks/needs-you/block.json +34 -0
- package/registry/blocks/needs-you/demo.jsx +143 -0
- package/registry/blocks/needs-you/index.js +3 -0
- package/registry/blocks/needs-you/needs-you.jsx +441 -0
- package/registry/blocks/pace-bar/README.md +230 -0
- package/registry/blocks/pace-bar/block.json +10 -0
- package/registry/blocks/pace-bar/demo.jsx +255 -0
- package/registry/blocks/pace-bar/index.js +22 -0
- package/registry/blocks/pace-bar/pace-bar.jsx +461 -0
- package/registry/blocks/pace-bar/pace-model.js +288 -0
- package/registry/blocks/record-grid/README.md +100 -0
- package/registry/blocks/record-grid/block.json +28 -0
- package/registry/blocks/record-grid/demo.jsx +43 -0
- package/registry/blocks/record-grid/index.js +3 -0
- package/registry/blocks/record-grid/record-grid.jsx +251 -0
- package/registry/blocks/run-history/README.md +138 -0
- package/registry/blocks/run-history/block.json +10 -0
- package/registry/blocks/run-history/demo.jsx +74 -0
- package/registry/blocks/run-history/index.js +3 -0
- package/registry/blocks/run-history/run-history.jsx +218 -0
- package/registry/blocks/run-view/README.md +174 -0
- package/registry/blocks/run-view/block.json +50 -0
- package/registry/blocks/run-view/demo.jsx +286 -0
- package/registry/blocks/run-view/index.js +5 -0
- package/registry/blocks/run-view/run-controls.jsx +266 -0
- package/registry/blocks/run-view/run-view.jsx +236 -0
- package/registry/blocks/run-view/runs-list.jsx +99 -0
- package/registry/blocks/schedule-card/README.md +121 -0
- package/registry/blocks/schedule-card/block.json +10 -0
- package/registry/blocks/schedule-card/demo.jsx +82 -0
- package/registry/blocks/schedule-card/index.js +3 -0
- package/registry/blocks/schedule-card/schedule-card.jsx +136 -0
- package/registry/blocks/schedule-engine/README.md +137 -0
- package/registry/blocks/schedule-engine/block.json +10 -0
- package/registry/blocks/schedule-engine/demo.jsx +82 -0
- package/registry/blocks/schedule-engine/engine.js +769 -0
- package/registry/blocks/schedule-engine/index.js +9 -0
- package/registry/blocks/schedule-engine/ops.js +71 -0
- package/registry/blocks/schedule-engine/place.js +294 -0
- package/registry/blocks/schedule-engine/tree.js +79 -0
- package/registry/blocks/schedule-engine/waits.js +80 -0
- package/registry/blocks/schedule-gantt/README.md +159 -0
- package/registry/blocks/schedule-gantt/block.json +10 -0
- package/registry/blocks/schedule-gantt/demo.jsx +223 -0
- package/registry/blocks/schedule-gantt/gantt-model.js +219 -0
- package/registry/blocks/schedule-gantt/geometry.js +176 -0
- package/registry/blocks/schedule-gantt/index.js +26 -0
- package/registry/blocks/schedule-gantt/schedule-gantt.jsx +882 -0
- package/registry/blocks/schedule-gantt/sheet.jsx +372 -0
- package/registry/blocks/schedule-gantt/track.jsx +190 -0
- package/registry/blocks/schedule-ruler/README.md +148 -0
- package/registry/blocks/schedule-ruler/block.json +10 -0
- package/registry/blocks/schedule-ruler/demo.jsx +181 -0
- package/registry/blocks/schedule-ruler/index.js +21 -0
- package/registry/blocks/schedule-ruler/ruler-model.js +239 -0
- package/registry/blocks/schedule-ruler/schedule-ruler.jsx +548 -0
- package/registry/blocks/source-receipt/README.md +97 -0
- package/registry/blocks/source-receipt/block.json +30 -0
- package/registry/blocks/source-receipt/demo.jsx +71 -0
- package/registry/blocks/source-receipt/index.js +3 -0
- package/registry/blocks/source-receipt/source-receipt.jsx +107 -0
- package/registry/blocks/spreadsheet/spreadsheet.jsx +15 -1
- package/registry/blocks/step-picker/README.md +78 -0
- package/registry/blocks/step-picker/block.json +31 -0
- package/registry/blocks/step-picker/demo.jsx +23 -0
- package/registry/blocks/step-picker/index.js +3 -0
- package/registry/blocks/step-picker/step-picker.jsx +144 -0
- package/registry/blocks/step-settings/README.md +100 -0
- package/registry/blocks/step-settings/block.json +44 -0
- package/registry/blocks/step-settings/demo.jsx +93 -0
- package/registry/blocks/step-settings/fields.jsx +58 -0
- package/registry/blocks/step-settings/flow-settings.jsx +115 -0
- package/registry/blocks/step-settings/index.js +5 -0
- package/registry/blocks/step-settings/step-settings.jsx +290 -0
- package/registry/blocks/suggestion/README.md +171 -0
- package/registry/blocks/suggestion/block.json +40 -0
- package/registry/blocks/suggestion/demo.jsx +91 -0
- package/registry/blocks/suggestion/index.js +3 -0
- package/registry/blocks/suggestion/suggestion.jsx +436 -0
- package/registry/blocks/sum-bar/README.md +122 -0
- package/registry/blocks/sum-bar/block.json +10 -0
- package/registry/blocks/sum-bar/demo.jsx +110 -0
- package/registry/blocks/sum-bar/index.js +5 -0
- package/registry/blocks/sum-bar/sum-bar.jsx +209 -0
- package/registry/blocks/sum-bar/sum-model.js +126 -0
- package/registry/blocks/tier-map/README.md +217 -0
- package/registry/blocks/tier-map/block.json +10 -0
- package/registry/blocks/tier-map/demo.jsx +222 -0
- package/registry/blocks/tier-map/index.js +24 -0
- package/registry/blocks/tier-map/tier-map.jsx +574 -0
- package/registry/blocks/tier-map/tier-model.js +318 -0
- package/registry/blocks/time-grid/README.md +305 -0
- package/registry/blocks/time-grid/block.json +10 -0
- package/registry/blocks/time-grid/demo.jsx +279 -0
- package/registry/blocks/time-grid/grid-model.js +322 -0
- package/registry/blocks/time-grid/index.js +25 -0
- package/registry/blocks/time-grid/time-grid.jsx +1325 -0
- package/registry/blocks/time-strip/README.md +141 -0
- package/registry/blocks/time-strip/block.json +10 -0
- package/registry/blocks/time-strip/demo.jsx +129 -0
- package/registry/blocks/time-strip/index.js +19 -0
- package/registry/blocks/time-strip/strip-model.js +178 -0
- package/registry/blocks/time-strip/time-strip.jsx +260 -0
- package/registry/blocks/top-bar/README.md +149 -0
- package/registry/blocks/top-bar/block.json +35 -0
- package/registry/blocks/top-bar/demo.jsx +78 -0
- package/registry/blocks/top-bar/index.js +5 -0
- package/registry/blocks/top-bar/probe-rules.js +74 -0
- package/registry/blocks/top-bar/top-bar-model.js +152 -0
- package/registry/blocks/top-bar/top-bar.jsx +244 -0
- package/registry/blocks/trigger-picker/README.md +98 -0
- package/registry/blocks/trigger-picker/block.json +38 -0
- package/registry/blocks/trigger-picker/demo.jsx +59 -0
- package/registry/blocks/trigger-picker/index.js +3 -0
- package/registry/blocks/trigger-picker/trigger-picker.jsx +374 -0
- package/registry/blocks/workflow-view/README.md +123 -0
- package/registry/blocks/workflow-view/block.json +10 -0
- package/registry/blocks/workflow-view/demo.jsx +90 -0
- package/registry/blocks/workflow-view/index.js +3 -0
- package/registry/blocks/workflow-view/workflow-view.jsx +203 -0
- package/src/install.mjs +77 -16
- package/src/registry.mjs +3 -0
- package/templates/next/browser/lib/data.js +6 -1
- package/templates/next/files/README.md +627 -24
- package/templates/next/files/check/automation-format.ts +2103 -0
- package/templates/next/files/check/automation-package.ts +142 -0
- package/templates/next/files/check/run.mjs +968 -9
- package/templates/next/files/components/ui/field.jsx +17 -4
- package/templates/next/files/components/ui/icon.jsx +13 -0
- package/templates/next/files/components/ui/index.js +2 -1
- package/templates/next/files/components/ui/layout.jsx +77 -2
- package/templates/next/files/components/ui/notice.jsx +70 -0
- package/templates/next/files/components/ui/page.jsx +124 -0
- package/templates/next/files/components/ui/stat.jsx +22 -7
- package/templates/next/files/components/ui/toast.jsx +6 -3
- package/templates/next/files/lib/automations.js +660 -0
- package/templates/next/files/lib/claw.js +241 -5
- package/templates/next/files/lib/door.js +92 -0
- package/templates/next/files/lib/hooks.js +1627 -8
- package/templates/next/files/lib/platform.js +529 -21
- package/templates/next/template.json +13 -3
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
// The pace bar's arithmetic: a period on a clock, how much of it is gone,
|
|
2
|
+
// how a piece of work stands against that, the gap said in weeks (or days,
|
|
3
|
+
// or points), the words for it, the axis (month names, a tick each Monday,
|
|
4
|
+
// a today line) and the weeks of a strip. Pure: no React, no DOM, no clock
|
|
5
|
+
// of its own (`today` always comes in). The components (pace-bar.jsx) draw
|
|
6
|
+
// what this returns; test/pace-bar.test.mjs pins it.
|
|
7
|
+
//
|
|
8
|
+
// A "day" is a whole number: days since 1970-01-01 in UTC, so a date string
|
|
9
|
+
// means the same day on the server and in the browser. A period includes
|
|
10
|
+
// both its first and its last day, and TODAY COUNTS AS GONE: on the 23rd of
|
|
11
|
+
// a quarter that began on the 1st of July and ends on the 30th of
|
|
12
|
+
// September, 85 of its 92 days are gone, and 92% of the time is gone.
|
|
13
|
+
|
|
14
|
+
export const DAY_MS = 86400000;
|
|
15
|
+
export const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
16
|
+
export const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"];
|
|
17
|
+
|
|
18
|
+
/** 'YYYY-MM-DD' (or an ISO time) to a day number; null when it is not a date. */
|
|
19
|
+
export function dayNumber(iso) {
|
|
20
|
+
if (typeof iso === "number" && Number.isFinite(iso)) return Math.round(iso);
|
|
21
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(String(iso ?? ""));
|
|
22
|
+
if (!m) return null;
|
|
23
|
+
const t = Date.UTC(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
|
|
24
|
+
// 2026-02-31 is not a day: Date rolls it over, and the roll-over shows.
|
|
25
|
+
if (new Date(t).getUTCMonth() !== Number(m[2]) - 1) return null;
|
|
26
|
+
return Math.round(t / DAY_MS);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** A day number back to 'YYYY-MM-DD'. */
|
|
30
|
+
export function isoOf(day) {
|
|
31
|
+
const d = new Date(day * DAY_MS);
|
|
32
|
+
return `${d.getUTCFullYear()}-${String(d.getUTCMonth() + 1).padStart(2, "0")}-${String(d.getUTCDate()).padStart(2, "0")}`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** 0 is Monday, 6 is Sunday. */
|
|
36
|
+
export const weekdayOf = (day) => (new Date(day * DAY_MS).getUTCDay() + 6) % 7;
|
|
37
|
+
export const dateOfMonth = (day) => new Date(day * DAY_MS).getUTCDate();
|
|
38
|
+
export const monthOf = (day) => new Date(day * DAY_MS).getUTCMonth();
|
|
39
|
+
|
|
40
|
+
/** "23 Sep" */
|
|
41
|
+
export const shortDay = (day) => `${dateOfMonth(day)} ${MONTHS[monthOf(day)]}`;
|
|
42
|
+
/** "Wed 23 Sep" */
|
|
43
|
+
export const dayName = (day) => `${WEEKDAYS[weekdayOf(day)]} ${shortDay(day)}`;
|
|
44
|
+
|
|
45
|
+
/** A number as a 0..1 share, clamped; null when it is not a number. */
|
|
46
|
+
export function share(x) {
|
|
47
|
+
if (x == null || x === "") return null;
|
|
48
|
+
const v = Number(x);
|
|
49
|
+
return Number.isFinite(v) ? Math.max(0, Math.min(1, v)) : null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A share as a whole percent; null stays null. */
|
|
53
|
+
export const percent = (x) => (x == null ? null : Math.round(x * 100));
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The period and where today stands in it.
|
|
57
|
+
* total days in it, both ends counted
|
|
58
|
+
* gone days gone, today counted (0 before it starts, total after it ends)
|
|
59
|
+
* share gone / total, 0..1
|
|
60
|
+
* left days after today (0 on the last day)
|
|
61
|
+
* where "before" | "in" | "after"
|
|
62
|
+
* Null when either end is missing or the end is before the start. With no
|
|
63
|
+
* `today`, `where` is "in" with nothing gone and `share` is null: the
|
|
64
|
+
* period is known but the clock is not.
|
|
65
|
+
*/
|
|
66
|
+
export function periodOf({ start, end, today } = {}) {
|
|
67
|
+
const s = dayNumber(start);
|
|
68
|
+
const e = dayNumber(end);
|
|
69
|
+
if (s == null || e == null || e < s) return null;
|
|
70
|
+
const t = dayNumber(today);
|
|
71
|
+
const total = e - s + 1;
|
|
72
|
+
if (t == null) return { s, e, t: null, total, gone: null, share: null, left: null, where: "in" };
|
|
73
|
+
if (t < s) return { s, e, t, total, gone: 0, share: 0, left: total, where: "before" };
|
|
74
|
+
if (t > e) return { s, e, t, total, gone: total, share: 1, left: 0, where: "after" };
|
|
75
|
+
const gone = t - s + 1;
|
|
76
|
+
return { s, e, t, total, gone, share: gone / total, left: e - t, where: "in" };
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* How a piece of work stands against the clock.
|
|
81
|
+
*
|
|
82
|
+
* value how far along: 0..1 (a spend: how much is used). Null: not known.
|
|
83
|
+
* pace where it should be by now, 0..1. Left out, it is worked out
|
|
84
|
+
* from `start`, `end` and `today` (the share of the time gone).
|
|
85
|
+
* `null` on purpose: pace is not judged.
|
|
86
|
+
* kind "progress" (default: short of the pace is the gap) or
|
|
87
|
+
* "spend" (over the pace is the gap: hours burned against
|
|
88
|
+
* time gone)
|
|
89
|
+
* tolerance whole points of the period inside which it is "on pace" (5)
|
|
90
|
+
* graceDays days from the start in which it is not judged yet (0)
|
|
91
|
+
* graceFraction the same as a share of the period, 0..1 (0.2: the first
|
|
92
|
+
* fifth; 19 days of a 92-day quarter). The longer of the two
|
|
93
|
+
* counts. Without a period it does nothing.
|
|
94
|
+
* unit "week" · "day" · "point". Default "week" when the period is
|
|
95
|
+
* known and the kind is progress; otherwise "point".
|
|
96
|
+
*
|
|
97
|
+
* Returns
|
|
98
|
+
* state "done" · "unjudged" · "better" · "on" · "worse"
|
|
99
|
+
* better/worse mean ahead/behind for progress, under/over for a spend
|
|
100
|
+
* value, pace 0..1 (pace null when not judged)
|
|
101
|
+
* valuePct, pacePct whole percents
|
|
102
|
+
* gapPts whole points between them, always >= 0
|
|
103
|
+
* amount the gap in `unit`s (whole, at least 1 when it is not "on"),
|
|
104
|
+
* null when on pace or not judged
|
|
105
|
+
* unit
|
|
106
|
+
* period periodOf(), or null
|
|
107
|
+
*/
|
|
108
|
+
export function paceOf({ value, pace, start, end, today, kind = "progress", tolerance = 5, graceDays = 0, graceFraction = 0, unit } = {}) {
|
|
109
|
+
const period = periodOf({ start, end, today });
|
|
110
|
+
const v = share(value);
|
|
111
|
+
// pace: undefined → from the clock; null → none.
|
|
112
|
+
const p = pace === undefined ? (period && period.where !== "before" ? period.share : null) : share(pace);
|
|
113
|
+
const spend = kind === "spend";
|
|
114
|
+
const u = unit ?? (period && !spend ? "week" : "point");
|
|
115
|
+
const valuePct = percent(v);
|
|
116
|
+
const pacePct = percent(p);
|
|
117
|
+
const base = { kind, value: v, pace: p, valuePct, pacePct, gapPts: 0, amount: null, unit: u, period };
|
|
118
|
+
if (!spend && v != null && v >= 1) return { ...base, state: "done" };
|
|
119
|
+
const fraction = Number.isFinite(graceFraction) && graceFraction > 0 ? Math.min(1, graceFraction) : 0;
|
|
120
|
+
const graceUntil = period ? Math.max(Number(graceDays) || 0, Math.ceil(fraction * period.total)) : 0;
|
|
121
|
+
const grace = period && period.gone != null && period.where === "in" && period.gone <= graceUntil;
|
|
122
|
+
if (v == null || p == null || grace) return { ...base, state: "unjudged" };
|
|
123
|
+
const diff = spend ? valuePct - pacePct : pacePct - valuePct; // positive: worse
|
|
124
|
+
const gapPts = Math.abs(diff);
|
|
125
|
+
if (gapPts <= tolerance) return { ...base, state: "on", gapPts };
|
|
126
|
+
let amount = gapPts;
|
|
127
|
+
if (u === "day" && period) amount = Math.max(1, Math.round((gapPts / 100) * period.total));
|
|
128
|
+
else if (u === "week" && period) amount = Math.max(1, Math.round(((gapPts / 100) * period.total) / 7));
|
|
129
|
+
return { ...base, state: diff > 0 ? "worse" : "better", gapPts, amount, unit: period || u === "point" ? u : "point" };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export const WORDS = {
|
|
133
|
+
on: "On pace",
|
|
134
|
+
unjudged: "Not judged yet",
|
|
135
|
+
done: "Done",
|
|
136
|
+
today: "Today",
|
|
137
|
+
before: "Not started",
|
|
138
|
+
after: "Ended",
|
|
139
|
+
units: { week: "wk", day: "d", point: "pts" },
|
|
140
|
+
behind: (n, unit) => `${n} ${unit} behind`,
|
|
141
|
+
ahead: (n, unit) => `${n} ${unit} ahead`,
|
|
142
|
+
over: (n, unit) => `${n} ${unit} over`,
|
|
143
|
+
under: (n, unit) => `${n} ${unit} under`,
|
|
144
|
+
sentence: ({ done, gone }) => `${done}% done, ${gone}% of the time gone`,
|
|
145
|
+
spendSentence: ({ done, gone }) => `${done}% used, ${gone}% of the time gone`,
|
|
146
|
+
gone: (n) => `${n}% gone`,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const merge = (words) => ({ ...WORDS, ...words, units: { ...WORDS.units, ...(words?.units ?? {}) } });
|
|
150
|
+
|
|
151
|
+
/** The gap in words: "4 wk behind", "On pace", "2 wk ahead", "Not judged yet", "Done". */
|
|
152
|
+
export function paceWords(p, words) {
|
|
153
|
+
if (!p) return "";
|
|
154
|
+
const w = merge(words);
|
|
155
|
+
if (p.state === "done") return w.done;
|
|
156
|
+
if (p.state === "unjudged") return w.unjudged;
|
|
157
|
+
if (p.state === "on") return w.on;
|
|
158
|
+
const unit = w.units[p.unit] ?? p.unit;
|
|
159
|
+
const spend = p.kind === "spend";
|
|
160
|
+
const say = p.state === "worse" ? (spend ? w.over : w.behind) : spend ? w.under : w.ahead;
|
|
161
|
+
return say(p.amount, unit);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** The sum in words: "57% done, 88% of the time gone". Empty when either number is missing. */
|
|
165
|
+
export function paceSentence(p, words) {
|
|
166
|
+
if (!p || p.valuePct == null) return "";
|
|
167
|
+
const w = merge(words);
|
|
168
|
+
const gone = p.period?.share != null ? percent(p.period.share) : p.pacePct;
|
|
169
|
+
if (gone == null) return `${p.valuePct}%`;
|
|
170
|
+
return (p.kind === "spend" ? w.spendSentence : w.sentence)({ done: p.valuePct, gone });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* A tone for a state, for a screen that has no status of its own to say:
|
|
175
|
+
* on pace or better is "ink", worse by `warnAt` points is "warning", by
|
|
176
|
+
* `dangerAt` points "danger"; not judged yet is "quiet"; done is "success".
|
|
177
|
+
*/
|
|
178
|
+
export function toneOf(p, { warnAt = 10, dangerAt = 25 } = {}) {
|
|
179
|
+
if (!p) return "ink";
|
|
180
|
+
if (p.state === "done") return "success";
|
|
181
|
+
if (p.state === "unjudged") return "quiet";
|
|
182
|
+
if (p.state !== "worse") return "ink";
|
|
183
|
+
if (p.gapPts >= dangerAt) return "danger";
|
|
184
|
+
if (p.gapPts >= warnAt) return "warning";
|
|
185
|
+
return "ink";
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Where a day's left edge falls on the period, 0..1 (can be outside it). */
|
|
189
|
+
export const atDay = (period, day) => (day - period.s) / period.total;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* The axis of a period: the share each Monday's left edge falls at (a tick
|
|
193
|
+
* each, while the period is short enough to read them), each month's 1st
|
|
194
|
+
* with its name (the period's first day names its month unless the next
|
|
195
|
+
* month starts right after it), and today.
|
|
196
|
+
*
|
|
197
|
+
* weeks [{ n, day, at }] n counts the weeks from 1; `at` is 0..1
|
|
198
|
+
* months [{ day, label, at }]
|
|
199
|
+
* todayAt the share the today line stands at (the end of today), or null
|
|
200
|
+
* where "before" | "in" | "after" (as periodOf)
|
|
201
|
+
*/
|
|
202
|
+
export function axisOf({ start, end, today, weekStartsOn = 0 } = {}) {
|
|
203
|
+
const period = periodOf({ start, end, today });
|
|
204
|
+
if (!period) return null;
|
|
205
|
+
const { s, e, total } = period;
|
|
206
|
+
const weeks = [];
|
|
207
|
+
if (total <= 200) {
|
|
208
|
+
let n = 0;
|
|
209
|
+
for (let d = s; d <= e; d += 1) {
|
|
210
|
+
if ((weekdayOf(d) - weekStartsOn + 7) % 7 === 0) {
|
|
211
|
+
n += 1;
|
|
212
|
+
weeks.push({ n, day: d, at: atDay(period, d) });
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
const months = [];
|
|
217
|
+
for (let d = s; d <= e; d += 1) if (dateOfMonth(d) === 1) months.push({ day: d, label: MONTHS[monthOf(d)], at: atDay(period, d) });
|
|
218
|
+
const startsOnFirst = dateOfMonth(s) === 1;
|
|
219
|
+
const crowded = !startsOnFirst && months.length > 0 && months[0].at < 0.09;
|
|
220
|
+
if (!startsOnFirst && !crowded) months.unshift({ day: s, label: MONTHS[monthOf(s)], at: 0 });
|
|
221
|
+
else if (!months.length) months.push({ day: s, label: MONTHS[monthOf(s)], at: 0 });
|
|
222
|
+
const todayAt = period.where === "in" && period.share != null ? period.share : period.where === "after" ? 1 : null;
|
|
223
|
+
return { period, weeks, months, todayAt, where: period.where };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* The weeks of a strip: one for each week start (Monday by default) in the
|
|
228
|
+
* period. The days before the first week start belong to week 1, the last
|
|
229
|
+
* week runs to the period's end. Each week says whether it is gone, current
|
|
230
|
+
* or still to come.
|
|
231
|
+
*
|
|
232
|
+
* [{ n, from, to, current, state }] state: "past" · "current" · "future"
|
|
233
|
+
*/
|
|
234
|
+
export function weeksOf({ start, end, today, weekStartsOn = 0 } = {}) {
|
|
235
|
+
const period = periodOf({ start, end, today });
|
|
236
|
+
if (!period) return [];
|
|
237
|
+
const { s, e } = period;
|
|
238
|
+
const starts = [];
|
|
239
|
+
for (let d = s; d <= e; d += 1) if ((weekdayOf(d) - weekStartsOn + 7) % 7 === 0) starts.push(d);
|
|
240
|
+
if (!starts.length) starts.push(s);
|
|
241
|
+
else starts[0] = s;
|
|
242
|
+
return starts.map((from, i) => {
|
|
243
|
+
const to = i + 1 < starts.length ? starts[i + 1] - 1 : e;
|
|
244
|
+
const t = period.t;
|
|
245
|
+
const state = t == null ? "future" : t > to ? "past" : t >= from ? "current" : "future";
|
|
246
|
+
return { n: i + 1, from, to, state, current: state === "current" };
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Marks on the weeks: each mark `{ id, date, by, label }` lands on the week
|
|
252
|
+
* its date falls in (a date before the first week goes to the first, after
|
|
253
|
+
* the last to the last; no date is left off, and counted in `skipped`).
|
|
254
|
+
* `by` is "person", "agent" or nothing.
|
|
255
|
+
* Returns { weeks: [{ ...week, marks: [...] }], skipped }.
|
|
256
|
+
*/
|
|
257
|
+
export function marksOnWeeks(weeks, marks) {
|
|
258
|
+
const out = weeks.map((w) => ({ ...w, marks: [] }));
|
|
259
|
+
let skipped = 0;
|
|
260
|
+
for (const [i, m] of (marks ?? []).entries()) {
|
|
261
|
+
const d = dayNumber(m?.date);
|
|
262
|
+
if (d == null || !out.length) {
|
|
263
|
+
skipped += 1;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
const at = d < out[0].from ? 0 : out.findLastIndex((w) => w.from <= d);
|
|
267
|
+
out[Math.max(0, at)].marks.push({ ...m, id: m.id ?? `mark-${i}`, day: d, by: m.by === "person" || m.by === "agent" ? m.by : null });
|
|
268
|
+
}
|
|
269
|
+
for (const w of out) w.marks.sort((a, b) => a.day - b.day);
|
|
270
|
+
return { weeks: out, skipped };
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Rows in groups. `groups` is `[{ id, ... }]` in the order to draw; a row
|
|
275
|
+
* names its group with `group`. A row whose group is not listed (or has
|
|
276
|
+
* none) goes in a trailing group with no heading when any group exists.
|
|
277
|
+
* With no `groups` there is one headless group of everything.
|
|
278
|
+
* Returns [{ group | null, rows }].
|
|
279
|
+
*/
|
|
280
|
+
export function groupRows(rows, groups) {
|
|
281
|
+
const list = rows ?? [];
|
|
282
|
+
if (!groups?.length) return [{ group: null, rows: list }];
|
|
283
|
+
const known = new Set(groups.map((g) => g.id));
|
|
284
|
+
const out = groups.map((g) => ({ group: g, rows: list.filter((r) => r.group === g.id) }));
|
|
285
|
+
const rest = list.filter((r) => !known.has(r.group));
|
|
286
|
+
if (rest.length) out.push({ group: null, rows: rest });
|
|
287
|
+
return out;
|
|
288
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# RecordGrid
|
|
2
|
+
|
|
3
|
+
A dense grid for reading many records at once. Each row starts with what
|
|
4
|
+
you put in `lead` (a row number, a tick), then the pinned columns (the
|
|
5
|
+
name), then the rest, which scroll sideways under a sticky header. Every
|
|
6
|
+
header and every cell is yours to draw: a column whose header carries its
|
|
7
|
+
live progress, its price and its own menu; a cell that draws an answer
|
|
8
|
+
with its mark. Whole rows take marks (selected, highlighted, struck,
|
|
9
|
+
dimmed), one cell is active (an outline), the arrow keys move it, Enter
|
|
10
|
+
opens it, a click opens it. Only the rows in the window are drawn, at a
|
|
11
|
+
fixed height, so 2,000 rows scroll like twenty. It scrolls itself: put it
|
|
12
|
+
in a bounded parent (`h-full`, `flex-1 min-h-0`, an AppLayout's content
|
|
13
|
+
with `contentMode="fill"`) that can also shrink sideways: in a grid or
|
|
14
|
+
flex row give that parent `min-w-0`, or the grid's columns widen the
|
|
15
|
+
parent instead of scrolling inside it.
|
|
16
|
+
|
|
17
|
+
Born in Data Enrichment (0.5.0), moved into the template with the app kit: the list, with
|
|
18
|
+
each agent column a job whose header says how far it got and what a row
|
|
19
|
+
costs, and the receipt of an answer opened beside it.
|
|
20
|
+
|
|
21
|
+
```jsx
|
|
22
|
+
import { RecordGrid } from "../components/blocks/record-grid"; // a Makersclaw app ("./blocks/record-grid" from components/)
|
|
23
|
+
import { RecordGrid } from "@/components/makersclaw/blocks/record-grid"; // a shadcn project
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Props
|
|
27
|
+
|
|
28
|
+
- `rows` — the records, in order. Each has a key (`rowKey`).
|
|
29
|
+
- `columns` — `[{ key, width, header, cell(row, index), className?,
|
|
30
|
+
headerClassName?, label? }]`. `width` in px (default 120). `header` is
|
|
31
|
+
any node, drawn at the top of a `headerHeight` cell; `cell(row, index)`
|
|
32
|
+
any node, in a one-line cell of `rowHeight` (it clips: truncate your
|
|
33
|
+
text). `label` names the column for a screen reader when the header is
|
|
34
|
+
not words.
|
|
35
|
+
- `lead` — columns before the pinned ones, the same shape: a row number,
|
|
36
|
+
a tick. They are pinned too, and a click on them is theirs (no active
|
|
37
|
+
cell).
|
|
38
|
+
- `pinned` — how many of `columns` stay put beside `lead` (default 1).
|
|
39
|
+
When the grid has scrolled sideways the last pinned column casts an
|
|
40
|
+
edge.
|
|
41
|
+
- `rowKey` — a key or `(row) => key` (default `"id"`).
|
|
42
|
+
- `rowHeight` (32) · `headerHeight` (50) · `overscan` (8 rows drawn past
|
|
43
|
+
each end of the window).
|
|
44
|
+
- `rowMark(row)` — `{ selected, highlighted, struck, dimmed }`: selected
|
|
45
|
+
tints the row with `accent`; highlighted draws a bar in `primary` at
|
|
46
|
+
its start (the rows a fill will work on); struck lines through the
|
|
47
|
+
last pinned column and quiets the row (a skipped row); dimmed fades the
|
|
48
|
+
unpinned cells.
|
|
49
|
+
- `active` — `{ row, column }`, the cell with the outline, or null.
|
|
50
|
+
- `onActiveChange(next)` — a click or a key moved it. Keep it in state.
|
|
51
|
+
- `onCellClick(row, columnKey)` — a click on an unpinned or pinned
|
|
52
|
+
(not `lead`) cell: open what it shows.
|
|
53
|
+
- `onOpen(row, columnKey)` — Enter on the active cell.
|
|
54
|
+
- `after` — a node in a row of its own after the last row ("+ Add rows").
|
|
55
|
+
- `empty` — what an empty grid says.
|
|
56
|
+
- `label` — what a screen reader calls the grid (`"Records"`).
|
|
57
|
+
- `className` — layout around it. Never colour.
|
|
58
|
+
- `ref` — `{ scrollToRow(key), scrollToEnd(), focus() }`.
|
|
59
|
+
|
|
60
|
+
Keys, with the grid focused: ↑ ↓ ← → move the active cell · Home / End
|
|
61
|
+
the row's ends (⌘ for the grid's) · Page Up / Down · Enter opens. Your
|
|
62
|
+
own keys (J and K for the next answer to check) go on a parent.
|
|
63
|
+
|
|
64
|
+
## Example
|
|
65
|
+
|
|
66
|
+
```jsx
|
|
67
|
+
"use client";
|
|
68
|
+
import { useState } from "react";
|
|
69
|
+
import { RecordGrid } from "./blocks/record-grid";
|
|
70
|
+
|
|
71
|
+
export function Companies({ rows, open }) {
|
|
72
|
+
const [active, setActive] = useState(null);
|
|
73
|
+
return (
|
|
74
|
+
<div className="h-full min-h-0">
|
|
75
|
+
<RecordGrid
|
|
76
|
+
label="Companies"
|
|
77
|
+
rows={rows}
|
|
78
|
+
lead={[{ key: "n", width: 44, header: "#", cell: (r, i) => i + 1 }]}
|
|
79
|
+
columns={[
|
|
80
|
+
{ key: "name", width: 180, header: <b>Company</b>, cell: (r) => <span className="truncate">{r.name}</span> },
|
|
81
|
+
{ key: "headcount", width: 120, header: <b>Headcount</b>, cell: (r) => r.headcount },
|
|
82
|
+
]}
|
|
83
|
+
active={active}
|
|
84
|
+
onActiveChange={setActive}
|
|
85
|
+
onCellClick={(r, key) => open(r, key)}
|
|
86
|
+
onOpen={(r, key) => open(r, key)}
|
|
87
|
+
rowMark={(r) => ({ selected: active?.row === r.id })}
|
|
88
|
+
/>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## What it will not do
|
|
95
|
+
|
|
96
|
+
- Edit a cell, sort, filter or page: the rows come in as you want them
|
|
97
|
+
shown, and your cells' buttons do the work.
|
|
98
|
+
- Rows of their own heights: one height keeps 2,000 rows smooth.
|
|
99
|
+
- Draw a phone layout: below 768px draw your own cards, or let it scroll
|
|
100
|
+
sideways inside its box.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "record-grid",
|
|
3
|
+
"title": "RecordGrid",
|
|
4
|
+
"description": "A dense grid for reading many records at once: a row number and a tick at the start of each row, the name column pinned beside them while the rest scrolls sideways, headers and cells you draw yourself (a column's live progress, its cost, its menu; an answer with its mark), whole-row marks (selected, highlighted, struck, dimmed), one active cell moved by the arrow keys and opened with Enter, and a line after the last row. Only the rows in the window are drawn, so 2,000 rows of 32px scroll smoothly. It scrolls itself inside a bounded parent.",
|
|
5
|
+
"useWhen": "A list that is read and worked rather than typed into, where each column needs its own header (an agent's column with its progress and price, a column with a menu) and each cell its own drawing: a research list being enriched, a review queue across many records, a report of a long job's rows. Not for editing cells like a sheet (spreadsheet) or a few dozen rows with sort and filters built in (data-table).",
|
|
6
|
+
"tags": [
|
|
7
|
+
"grid",
|
|
8
|
+
"table",
|
|
9
|
+
"records",
|
|
10
|
+
"virtual",
|
|
11
|
+
"pinned column",
|
|
12
|
+
"header",
|
|
13
|
+
"dense",
|
|
14
|
+
"keyboard",
|
|
15
|
+
"rows",
|
|
16
|
+
"review",
|
|
17
|
+
"enrichment",
|
|
18
|
+
"list"
|
|
19
|
+
],
|
|
20
|
+
"exports": [
|
|
21
|
+
"RecordGrid"
|
|
22
|
+
],
|
|
23
|
+
"files": [
|
|
24
|
+
"index.js",
|
|
25
|
+
"record-grid.jsx"
|
|
26
|
+
],
|
|
27
|
+
"requires": []
|
|
28
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
// RecordGrid over 480 rows, and empty — the gallery's and the smoke
|
|
3
|
+
// test's surface. Not installed into apps.
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { RecordGrid } from "../components/blocks/record-grid";
|
|
6
|
+
|
|
7
|
+
const ROWS = Array.from({ length: 480 }, (_, i) => ({ id: `r${i + 1}`, n: i + 1, name: `Company ${i + 1}`, domain: `company${i + 1}.de`, people: 20 + ((i * 7) % 180), right: i % 5 === 0 }));
|
|
8
|
+
|
|
9
|
+
export default function Demo() {
|
|
10
|
+
const [active, setActive] = useState(null);
|
|
11
|
+
return (
|
|
12
|
+
<div className="grid grid-cols-[minmax(0,1fr)] gap-6 p-6">
|
|
13
|
+
<div className="h-96 min-w-0 border border-border">
|
|
14
|
+
<RecordGrid
|
|
15
|
+
label="Companies"
|
|
16
|
+
rows={ROWS}
|
|
17
|
+
lead={[
|
|
18
|
+
{ key: "n", width: 44, header: <span className="text-xs text-muted-foreground">#</span>, cell: (r) => <span className="ml-auto text-xs text-muted-foreground">{r.n}</span> },
|
|
19
|
+
{ key: "tick", width: 28, header: null, cell: (r) => (r.right ? "✓" : "") },
|
|
20
|
+
]}
|
|
21
|
+
columns={[
|
|
22
|
+
{ key: "name", width: 180, header: <b>Company</b>, cell: (r) => <span className="truncate font-medium">{r.name}</span> },
|
|
23
|
+
{ key: "domain", width: 180, header: <b>Domain</b>, cell: (r) => <span className="truncate">{r.domain}</span> },
|
|
24
|
+
{ key: "people", width: 120, header: <b>Headcount</b>, cell: (r) => r.people },
|
|
25
|
+
]}
|
|
26
|
+
rowMark={(r) => ({ selected: active?.row === r.id, struck: r.n === 4 })}
|
|
27
|
+
active={active}
|
|
28
|
+
onActiveChange={setActive}
|
|
29
|
+
onOpen={(r, key) => setActive({ row: r.id, column: key })}
|
|
30
|
+
after={<span>+ Add rows</span>}
|
|
31
|
+
/>
|
|
32
|
+
</div>
|
|
33
|
+
<div className="h-40 w-80 max-w-full border border-border">
|
|
34
|
+
<RecordGrid
|
|
35
|
+
label="No companies yet"
|
|
36
|
+
rows={[]}
|
|
37
|
+
columns={[{ key: "name", width: 180, header: <b>Company</b>, cell: (r) => r.name }]}
|
|
38
|
+
after={<span>No companies yet. Import a CSV, or ask the agent to find some.</span>}
|
|
39
|
+
/>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|