vastgrid 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +175 -13
- package/dist/canvas/geometry.d.ts +92 -0
- package/dist/canvas/geometry.d.ts.map +1 -0
- package/dist/canvas/geometry.js +197 -0
- package/dist/canvas/geometry.js.map +1 -0
- package/dist/canvas/grid-canvas.d.ts +161 -0
- package/dist/canvas/grid-canvas.d.ts.map +1 -0
- package/dist/canvas/grid-canvas.js +1146 -0
- package/dist/canvas/grid-canvas.js.map +1 -0
- package/dist/canvas/hooks.d.ts +61 -0
- package/dist/canvas/hooks.d.ts.map +1 -0
- package/dist/canvas/hooks.js +2 -0
- package/dist/canvas/hooks.js.map +1 -0
- package/dist/canvas/keys.d.ts +26 -0
- package/dist/canvas/keys.d.ts.map +1 -0
- package/dist/canvas/keys.js +133 -0
- package/dist/canvas/keys.js.map +1 -0
- package/dist/canvas/layout.d.ts +26 -0
- package/dist/canvas/layout.d.ts.map +1 -0
- package/dist/canvas/layout.js +73 -0
- package/dist/canvas/layout.js.map +1 -0
- package/dist/canvas/scroll.d.ts +31 -0
- package/dist/canvas/scroll.d.ts.map +1 -0
- package/dist/canvas/scroll.js +111 -0
- package/dist/canvas/scroll.js.map +1 -0
- package/dist/canvas/text.d.ts +23 -0
- package/dist/canvas/text.d.ts.map +1 -0
- package/dist/canvas/text.js +142 -0
- package/dist/canvas/text.js.map +1 -0
- package/dist/{theme.d.ts → canvas/theme.d.ts} +8 -0
- package/dist/canvas/theme.d.ts.map +1 -0
- package/dist/canvas/theme.js +62 -0
- package/dist/canvas/theme.js.map +1 -0
- package/dist/core/cache.d.ts +48 -0
- package/dist/core/cache.d.ts.map +1 -0
- package/dist/core/cache.js +222 -0
- package/dist/core/cache.js.map +1 -0
- package/dist/core/checked.d.ts +20 -0
- package/dist/core/checked.d.ts.map +1 -0
- package/dist/core/checked.js +61 -0
- package/dist/core/checked.js.map +1 -0
- package/dist/core/client.d.ts +33 -0
- package/dist/core/client.d.ts.map +1 -0
- package/dist/core/client.js +103 -0
- package/dist/core/client.js.map +1 -0
- package/dist/core/conditions.d.ts +48 -0
- package/dist/core/conditions.d.ts.map +1 -0
- package/dist/core/conditions.js +105 -0
- package/dist/core/conditions.js.map +1 -0
- package/dist/core/evaluate.d.ts +20 -0
- package/dist/core/evaluate.d.ts.map +1 -0
- package/dist/core/evaluate.js +210 -0
- package/dist/core/evaluate.js.map +1 -0
- package/dist/core/filters.d.ts +51 -0
- package/dist/core/filters.d.ts.map +1 -0
- package/dist/core/filters.js +303 -0
- package/dist/core/filters.js.map +1 -0
- package/dist/core/format.d.ts +14 -0
- package/dist/core/format.d.ts.map +1 -0
- package/dist/core/format.js +303 -0
- package/dist/core/format.js.map +1 -0
- package/dist/core/measures.d.ts +26 -0
- package/dist/core/measures.d.ts.map +1 -0
- package/dist/core/measures.js +105 -0
- package/dist/core/measures.js.map +1 -0
- package/dist/core/request-ids.d.ts +20 -0
- package/dist/core/request-ids.d.ts.map +1 -0
- package/dist/core/request-ids.js +55 -0
- package/dist/core/request-ids.js.map +1 -0
- package/dist/core/rows.d.ts +79 -0
- package/dist/core/rows.d.ts.map +1 -0
- package/dist/core/rows.js +173 -0
- package/dist/core/rows.js.map +1 -0
- package/dist/core/store.d.ts +233 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +1162 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/totals.d.ts +24 -0
- package/dist/core/totals.d.ts.map +1 -0
- package/dist/core/totals.js +49 -0
- package/dist/core/totals.js.map +1 -0
- package/dist/core/trace.d.ts +7 -0
- package/dist/core/trace.d.ts.map +1 -0
- package/dist/core/trace.js +26 -0
- package/dist/core/trace.js.map +1 -0
- package/dist/core/transports.d.ts +50 -0
- package/dist/core/transports.d.ts.map +1 -0
- package/dist/core/transports.js +160 -0
- package/dist/core/transports.js.map +1 -0
- package/dist/core/types.d.ts +150 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +45 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core.d.ts +12 -0
- package/dist/core.d.ts.map +1 -0
- package/dist/core.js +11 -0
- package/dist/core.js.map +1 -0
- package/dist/index.d.ts +15 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -7
- package/dist/index.js.map +1 -1
- package/dist/react/accessible-rows.d.ts +13 -0
- package/dist/react/accessible-rows.d.ts.map +1 -0
- package/dist/react/accessible-rows.js +320 -0
- package/dist/react/accessible-rows.js.map +1 -0
- package/dist/react/cell-editor.d.ts +22 -0
- package/dist/react/cell-editor.d.ts.map +1 -0
- package/dist/react/cell-editor.js +431 -0
- package/dist/react/cell-editor.js.map +1 -0
- package/dist/react/cell-menu.d.ts +18 -0
- package/dist/react/cell-menu.d.ts.map +1 -0
- package/dist/react/cell-menu.js +299 -0
- package/dist/react/cell-menu.js.map +1 -0
- package/dist/react/column-menu.d.ts +16 -0
- package/dist/react/column-menu.d.ts.map +1 -0
- package/dist/react/column-menu.js +1311 -0
- package/dist/react/column-menu.js.map +1 -0
- package/dist/react/columns-menu.d.ts +7 -0
- package/dist/react/columns-menu.d.ts.map +1 -0
- package/dist/react/columns-menu.js +249 -0
- package/dist/react/columns-menu.js.map +1 -0
- package/dist/react/conditions-menu.d.ts +10 -0
- package/dist/react/conditions-menu.d.ts.map +1 -0
- package/dist/react/conditions-menu.js +795 -0
- package/dist/react/conditions-menu.js.map +1 -0
- package/dist/react/context.d.ts +29 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/context.js +18 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/data-grid.d.ts +79 -0
- package/dist/react/data-grid.d.ts.map +1 -0
- package/dist/react/data-grid.js +653 -0
- package/dist/react/data-grid.js.map +1 -0
- package/dist/react/detail-panel.d.ts +22 -0
- package/dist/react/detail-panel.d.ts.map +1 -0
- package/dist/react/detail-panel.js +242 -0
- package/dist/react/detail-panel.js.map +1 -0
- package/dist/react/export-menu.d.ts +11 -0
- package/dist/react/export-menu.d.ts.map +1 -0
- package/dist/react/export-menu.js +304 -0
- package/dist/react/export-menu.js.map +1 -0
- package/dist/react/find-bar.d.ts +11 -0
- package/dist/react/find-bar.d.ts.map +1 -0
- package/dist/react/find-bar.js +303 -0
- package/dist/react/find-bar.js.map +1 -0
- package/dist/react/format-form.d.ts +12 -0
- package/dist/react/format-form.d.ts.map +1 -0
- package/dist/react/format-form.js +863 -0
- package/dist/react/format-form.js.map +1 -0
- package/dist/react/formula-menu.d.ts +6 -0
- package/dist/react/formula-menu.d.ts.map +1 -0
- package/dist/react/formula-menu.js +343 -0
- package/dist/react/formula-menu.js.map +1 -0
- package/dist/react/grid-body.d.ts +33 -0
- package/dist/react/grid-body.d.ts.map +1 -0
- package/dist/react/grid-body.js +1389 -0
- package/dist/react/grid-body.js.map +1 -0
- package/dist/react/group-menu.d.ts +7 -0
- package/dist/react/group-menu.d.ts.map +1 -0
- package/dist/react/group-menu.js +508 -0
- package/dist/react/group-menu.js.map +1 -0
- package/dist/react/hooks.d.ts +33 -0
- package/dist/react/hooks.d.ts.map +1 -0
- package/dist/react/hooks.js +161 -0
- package/dist/react/hooks.js.map +1 -0
- package/dist/react/messages.d.ts +180 -0
- package/dist/react/messages.d.ts.map +1 -0
- package/dist/react/messages.js +414 -0
- package/dist/react/messages.js.map +1 -0
- package/dist/react/save.d.ts +9 -0
- package/dist/react/save.d.ts.map +1 -0
- package/dist/react/save.js +102 -0
- package/dist/react/save.js.map +1 -0
- package/dist/react/static-rows.d.ts +9 -0
- package/dist/react/static-rows.d.ts.map +1 -0
- package/dist/react/static-rows.js +176 -0
- package/dist/react/static-rows.js.map +1 -0
- package/dist/react/status-bar.d.ts +12 -0
- package/dist/react/status-bar.d.ts.map +1 -0
- package/dist/react/status-bar.js +413 -0
- package/dist/react/status-bar.js.map +1 -0
- package/dist/react/toolbar-popover.d.ts +14 -0
- package/dist/react/toolbar-popover.d.ts.map +1 -0
- package/dist/react/toolbar-popover.js +143 -0
- package/dist/react/toolbar-popover.js.map +1 -0
- package/dist/react/toolbar.d.ts +13 -0
- package/dist/react/toolbar.d.ts.map +1 -0
- package/dist/react/toolbar.js +344 -0
- package/dist/react/toolbar.js.map +1 -0
- package/dist/server.d.ts +22 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +45 -0
- package/dist/server.js.map +1 -0
- package/dist/styles.css +496 -316
- package/package.json +42 -17
- package/src/canvas/geometry.ts +197 -0
- package/src/canvas/grid-canvas.ts +1222 -0
- package/src/canvas/hooks.ts +61 -0
- package/src/canvas/keys.ts +96 -0
- package/src/canvas/layout.ts +77 -0
- package/src/canvas/scroll.ts +120 -0
- package/src/canvas/text.ts +155 -0
- package/src/{theme.ts → canvas/theme.ts} +14 -1
- package/src/core/cache.ts +247 -0
- package/src/core/checked.ts +64 -0
- package/src/core/client.ts +103 -0
- package/src/core/conditions.ts +89 -0
- package/src/core/evaluate.ts +221 -0
- package/src/core/filters.ts +268 -0
- package/src/core/format.ts +309 -0
- package/src/core/measures.ts +99 -0
- package/src/core/request-ids.ts +54 -0
- package/src/core/rows.ts +213 -0
- package/src/core/store.ts +1122 -0
- package/src/core/totals.ts +59 -0
- package/src/core/trace.ts +44 -0
- package/src/core/transports.ts +177 -0
- package/src/core/types.ts +168 -0
- package/src/core.ts +39 -0
- package/src/index.ts +24 -12
- package/src/react/accessible-rows.tsx +242 -0
- package/src/react/cell-editor.tsx +216 -0
- package/src/react/cell-menu.tsx +166 -0
- package/src/react/column-menu.tsx +619 -0
- package/src/react/columns-menu.tsx +116 -0
- package/src/react/conditions-menu.tsx +289 -0
- package/src/react/context.ts +33 -0
- package/src/react/data-grid.tsx +286 -0
- package/src/react/detail-panel.tsx +92 -0
- package/src/react/export-menu.tsx +112 -0
- package/src/react/find-bar.tsx +112 -0
- package/src/react/format-form.tsx +421 -0
- package/src/react/formula-menu.tsx +163 -0
- package/src/react/grid-body.tsx +744 -0
- package/src/react/group-menu.tsx +214 -0
- package/src/react/hooks.ts +104 -0
- package/src/react/messages.ts +556 -0
- package/src/react/save.ts +42 -0
- package/src/react/static-rows.tsx +59 -0
- package/src/react/status-bar.tsx +175 -0
- package/src/react/toolbar-popover.tsx +69 -0
- package/src/react/toolbar.tsx +152 -0
- package/src/server.ts +58 -0
- package/src/styles.css +496 -316
- package/dist/components/bars.d.ts +0 -14
- package/dist/components/bars.d.ts.map +0 -1
- package/dist/components/bars.js +0 -68
- package/dist/components/bars.js.map +0 -1
- package/dist/components/cell-inspector.d.ts +0 -11
- package/dist/components/cell-inspector.d.ts.map +0 -1
- package/dist/components/cell-inspector.js +0 -37
- package/dist/components/cell-inspector.js.map +0 -1
- package/dist/components/columns-panel.d.ts +0 -12
- package/dist/components/columns-panel.d.ts.map +0 -1
- package/dist/components/columns-panel.js +0 -11
- package/dist/components/columns-panel.js.map +0 -1
- package/dist/components/filter-panel.d.ts +0 -22
- package/dist/components/filter-panel.d.ts.map +0 -1
- package/dist/components/filter-panel.js +0 -169
- package/dist/components/filter-panel.js.map +0 -1
- package/dist/components/find-bar.d.ts +0 -9
- package/dist/components/find-bar.d.ts.map +0 -1
- package/dist/components/find-bar.js +0 -46
- package/dist/components/find-bar.js.map +0 -1
- package/dist/components/popover.d.ts +0 -38
- package/dist/components/popover.d.ts.map +0 -1
- package/dist/components/popover.js +0 -104
- package/dist/components/popover.js.map +0 -1
- package/dist/components/stats-panel.d.ts +0 -12
- package/dist/components/stats-panel.d.ts.map +0 -1
- package/dist/components/stats-panel.js +0 -40
- package/dist/components/stats-panel.js.map +0 -1
- package/dist/controller.d.ts +0 -227
- package/dist/controller.d.ts.map +0 -1
- package/dist/controller.js +0 -845
- package/dist/controller.js.map +0 -1
- package/dist/data-grid.d.ts +0 -51
- package/dist/data-grid.d.ts.map +0 -1
- package/dist/data-grid.js +0 -704
- package/dist/data-grid.js.map +0 -1
- package/dist/labels.d.ts +0 -9
- package/dist/labels.d.ts.map +0 -1
- package/dist/labels.js +0 -38
- package/dist/labels.js.map +0 -1
- package/dist/messages.d.ts +0 -99
- package/dist/messages.d.ts.map +0 -1
- package/dist/messages.js +0 -276
- package/dist/messages.js.map +0 -1
- package/dist/renderer.d.ts +0 -52
- package/dist/renderer.d.ts.map +0 -1
- package/dist/renderer.js +0 -522
- package/dist/renderer.js.map +0 -1
- package/dist/text.d.ts +0 -14
- package/dist/text.d.ts.map +0 -1
- package/dist/text.js +0 -63
- package/dist/text.js.map +0 -1
- package/dist/theme.d.ts.map +0 -1
- package/dist/theme.js +0 -54
- package/dist/theme.js.map +0 -1
- package/src/components/bars.tsx +0 -180
- package/src/components/cell-inspector.tsx +0 -70
- package/src/components/columns-panel.tsx +0 -59
- package/src/components/filter-panel.tsx +0 -293
- package/src/components/find-bar.tsx +0 -104
- package/src/components/popover.tsx +0 -194
- package/src/components/stats-panel.tsx +0 -153
- package/src/controller.ts +0 -945
- package/src/data-grid.tsx +0 -1035
- package/src/labels.ts +0 -46
- package/src/messages.ts +0 -390
- package/src/renderer.ts +0 -535
- package/src/text.ts +0 -55
package/README.md
CHANGED
|
@@ -1,25 +1,187 @@
|
|
|
1
1
|
# vastgrid
|
|
2
2
|
|
|
3
|
-
A canvas data grid for React that opens CSV, TSV, Parquet
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
A canvas data grid for React that opens tables of any size like a spreadsheet: huge CSV, TSV, Parquet, JSON Lines
|
|
4
|
+
and Excel files, read a block of rows at a time as you scroll. People sort, filter, group, pivot, find, format,
|
|
5
|
+
edit, undo and export; engines in the browser or on Node.js do the work, and save edited CSV and TSV files with
|
|
6
|
+
every byte that was not edited kept as it is.
|
|
7
|
+
|
|
8
|
+
**[Demo](https://vastgrid.yhay81.com/demo/)** · [Open a file](https://vastgrid.yhay81.com/open/) ·
|
|
9
|
+
[Documentation](https://vastgrid.yhay81.com) · [GitHub](https://github.com/yhay81/vastgrid)
|
|
10
|
+
|
|
11
|
+
[](https://vastgrid.yhay81.com/demo/)
|
|
12
|
+
|
|
13
|
+
## Install
|
|
6
14
|
|
|
7
15
|
```sh
|
|
8
|
-
npm install vastgrid
|
|
16
|
+
npm install vastgrid vastgrid-js
|
|
9
17
|
```
|
|
10
18
|
|
|
19
|
+
`vastgrid` needs React 19.3 or later and a browser with CSS `light-dark()` (Chrome and Edge 123, Firefox 120,
|
|
20
|
+
Safari 17.5 or later). React is an optional peer dependency: `vastgrid/core` runs on servers and in workers without
|
|
21
|
+
it.
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
11
25
|
```tsx
|
|
12
|
-
import {
|
|
26
|
+
import { DataGrid, localTransport } from "vastgrid";
|
|
13
27
|
import "vastgrid/styles.css";
|
|
28
|
+
import { memoryEngine } from "vastgrid-js";
|
|
14
29
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
30
|
+
const transport = localTransport(
|
|
31
|
+
memoryEngine({
|
|
32
|
+
tables: {
|
|
33
|
+
orders: {
|
|
34
|
+
columns: [
|
|
35
|
+
{ name: "id", type: "integer" },
|
|
36
|
+
{ name: "customer", type: "string" },
|
|
37
|
+
{ name: "price", type: "decimal", precision: 10, scale: 2 },
|
|
38
|
+
],
|
|
39
|
+
rows: [
|
|
40
|
+
[1, "Acme", "12.50"],
|
|
41
|
+
[2, "Globex", "8.00"],
|
|
42
|
+
],
|
|
43
|
+
editable: true,
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
}),
|
|
47
|
+
);
|
|
19
48
|
|
|
20
|
-
|
|
21
|
-
<DataGrid
|
|
22
|
-
|
|
49
|
+
export function Orders() {
|
|
50
|
+
return <DataGrid transport={transport} table="orders" style={{ height: 480 }} />;
|
|
51
|
+
}
|
|
23
52
|
```
|
|
24
53
|
|
|
25
|
-
|
|
54
|
+
The grid fills the height you give it. Rows are typed JSON (decimals as strings with their exact digits, dates as
|
|
55
|
+
`YYYY-MM-DD`); the memory engine also opens Arrow, CSV, TSV, JSON Lines, Excel and Parquet files
|
|
56
|
+
([vastgrid-js](https://www.npmjs.com/package/vastgrid-js)).
|
|
57
|
+
|
|
58
|
+
## Entry points
|
|
59
|
+
|
|
60
|
+
| Entry | Holds | Imports React |
|
|
61
|
+
| --- | --- | --- |
|
|
62
|
+
| `vastgrid` | `DataGrid`, `GridProvider`, the hooks, and everything of `vastgrid/core` | yes (`"use client"`) |
|
|
63
|
+
| `vastgrid/core` | `createGridStore` and `GridStore`, and the transports | no |
|
|
64
|
+
| `vastgrid/server` | `readInitialView`: the first rows, read on a server for `initial` | no |
|
|
65
|
+
| `vastgrid/styles.css` | the styles, themed by CSS custom properties on `.vg-root` | — |
|
|
66
|
+
|
|
67
|
+
## Engines and transports
|
|
68
|
+
|
|
69
|
+
A transport carries the grid's messages to an engine; a new transport or table makes a new store.
|
|
70
|
+
|
|
71
|
+
| Transport | Engine |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `localTransport(engine)` | in the same thread, such as `memoryEngine` of `vastgrid-js` |
|
|
74
|
+
| `workerTransport(worker)` | in a Web Worker that calls `serveEngine(engine, self)`, or behind anything that carries messages as a worker does (a `MessagePort`, a webview) |
|
|
75
|
+
| `httpTransport(url, { events })` | on a server, such as `createHttpHandler` of [`vastgrid-duckdb`](https://www.npmjs.com/package/vastgrid-duckdb) |
|
|
76
|
+
| `serverFunctionTransport(action)` | behind a React Server Function |
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { DataGrid, httpTransport, workerTransport } from "vastgrid";
|
|
80
|
+
|
|
81
|
+
// The memory engine in a Web Worker: the page never stops while it sorts a million rows.
|
|
82
|
+
const worker = new Worker(new URL("./engine.worker.ts", import.meta.url), { type: "module" });
|
|
83
|
+
const inWorker = workerTransport(worker);
|
|
84
|
+
|
|
85
|
+
// The DuckDB engine on a Node.js server, with its events.
|
|
86
|
+
const onServer = httpTransport("/api", { events: "/api/events" });
|
|
87
|
+
|
|
88
|
+
export function Sales() {
|
|
89
|
+
return <DataGrid transport={onServer} table="sales.parquet" style={{ height: 600 }} />;
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
// engine.worker.ts
|
|
95
|
+
import { serveEngine, type WorkerLike } from "vastgrid/core";
|
|
96
|
+
import { memoryEngine } from "vastgrid-js";
|
|
97
|
+
|
|
98
|
+
const engine = memoryEngine({ tables: { trips: { parquet: "https://example.com/trips.parquet" } } });
|
|
99
|
+
serveEngine(engine, self as unknown as WorkerLike);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## `DataGrid`
|
|
103
|
+
|
|
104
|
+
| Prop | Meaning |
|
|
105
|
+
| --- | --- |
|
|
106
|
+
| `transport`, `table`, `open` | where the rows come from: a transport, the table to open, and how to read it (a workbook's `sheet`) |
|
|
107
|
+
| `store` | a store made outside (`useCreateGridStore`), instead of `transport` and `table` |
|
|
108
|
+
| `document`, `defaultDocument`, `onDocumentChange` | the view and its columns: plan, widths, pins, formats, totals; controlled or not |
|
|
109
|
+
| `session`, `defaultSession`, `onSessionChange` | where the person is: selection, scroll, checked rows, the undo history |
|
|
110
|
+
| `initial` | rows read on a server (`readInitialView`), or a promise of them, shown before the first read |
|
|
111
|
+
| `toolbar`, `statusBar` | the built-in toolbar (search, Conditions, Group, Columns, Formulas, Export, Save) and status bar |
|
|
112
|
+
| `checkboxes` | a check box for each row, kept on the same records through sorts and filters |
|
|
113
|
+
| `columnGroups`, `groupHeaderHeight` | header rows of groups of columns |
|
|
114
|
+
| `wrapLines` | the lines of a row while a column wraps its text |
|
|
115
|
+
| `cellText`, `cellStyle`, `drawCell`, `cellAction`, `onCellAction` | the text, colors, drawing and actions (links, buttons) of cells |
|
|
116
|
+
| `detailPanel` | your own content of the detail panel of the active row |
|
|
117
|
+
| `rowHeight`, `headerHeight` | sizes in CSS pixels |
|
|
118
|
+
| `locale`, `messages` | the language of the UI (English and Japanese built in) and any of its texts |
|
|
119
|
+
| `onError` | failures that belong to no command |
|
|
120
|
+
| `ref` | `{ store, dispatch, focus }` |
|
|
121
|
+
| `className`, `style`, `aria-label` | on the root element |
|
|
122
|
+
|
|
123
|
+
## Hooks and commands
|
|
124
|
+
|
|
125
|
+
Every change is a typed command (`GridCommand`), which the grid's keys and menus dispatch as your code does.
|
|
126
|
+
|
|
127
|
+
| Hook | Gives |
|
|
128
|
+
| --- | --- |
|
|
129
|
+
| `useGridSelector(selector, isEqual?)` | a part of the store's immutable snapshot; renders again only when it changes |
|
|
130
|
+
| `useGridDispatch()` | `dispatch`, for commands such as `{ command: "sort.set", keys: [{ column: "price", direction: "desc" }] }` |
|
|
131
|
+
| `useGridRows({ start, end, columns })` | values of rows of the view, for cells that React renders |
|
|
132
|
+
| `useCreateGridStore(options)`, `GridProvider` | a store of your own, shared by the grid and your controls |
|
|
133
|
+
| `useGridStore()` | the store of the nearest grid or provider |
|
|
134
|
+
|
|
135
|
+
```tsx
|
|
136
|
+
import { DataGrid, GridProvider, useCreateGridStore, useGridDispatch, useGridSelector } from "vastgrid";
|
|
137
|
+
|
|
138
|
+
function Workbench() {
|
|
139
|
+
const store = useCreateGridStore({ transport, table: "orders" });
|
|
140
|
+
return (
|
|
141
|
+
<GridProvider store={store}>
|
|
142
|
+
<ClearSort />
|
|
143
|
+
<DataGrid store={store} style={{ height: 480 }} />
|
|
144
|
+
</GridProvider>
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function ClearSort() {
|
|
149
|
+
const sort = useGridSelector((snapshot) => snapshot.document.plan?.sort);
|
|
150
|
+
const dispatch = useGridDispatch();
|
|
151
|
+
return (
|
|
152
|
+
<button type="button" disabled={!sort} onClick={() => dispatch({ command: "sort.clear" })}>
|
|
153
|
+
Clear sort
|
|
154
|
+
</button>
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
## How it renders
|
|
160
|
+
|
|
161
|
+
Scrolling, drawing and dragging a selection run in the store and the canvas and render no React; moving the active
|
|
162
|
+
cell commits once and renders only what reads the session. The rows on screen are also accessible rows of the ARIA
|
|
163
|
+
grid pattern under the canvas. Every component and hook compiles with React Compiler 1.0, and the package ships
|
|
164
|
+
compiled. On a server, `DataGrid` renders the first rows of `initial` as an HTML table, streamed when it is a
|
|
165
|
+
promise, and the canvas takes over in the browser.
|
|
166
|
+
|
|
167
|
+
When the engine shows a sort's first rows while the rest builds (the DuckDB engine on a million rows or more), the
|
|
168
|
+
status bar says it is reading and the row count stays exact; changing the plan or disconnecting cancels old reads,
|
|
169
|
+
so that late answers never replace the current view.
|
|
170
|
+
|
|
171
|
+
## Documentation
|
|
172
|
+
|
|
173
|
+
- [docs/react.md](https://github.com/yhay81/vastgrid/blob/main/docs/react.md): the store, the transports,
|
|
174
|
+
`DataGrid`, the hooks, rendering, theming and server rendering.
|
|
175
|
+
- [docs/spec.md](https://github.com/yhay81/vastgrid/blob/main/docs/spec.md): the document and session (§5),
|
|
176
|
+
the commands (§6), editing and saving (§7) and the messages (§8).
|
|
177
|
+
- [Where vastgrid opens files](https://vastgrid.yhay81.com/apps/overview/): the same grid in the command line,
|
|
178
|
+
VS Code, an MCP App and a notebook.
|
|
179
|
+
- [docs/migrating-from-0.1.md](https://github.com/yhay81/vastgrid/blob/main/docs/migrating-from-0.1.md): from vastgrid 0.1.x.
|
|
180
|
+
|
|
181
|
+
The tests run on the sources (`npm test`); the end-to-end tests run the bench of
|
|
182
|
+
[`examples/bench`](https://github.com/yhay81/vastgrid/tree/main/examples/bench) in Chromium against the build
|
|
183
|
+
(`npm run build && npm run e2e`).
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { ColumnLayout } from "./layout.js";
|
|
2
|
+
import { ScrollAxis } from "./scroll.js";
|
|
3
|
+
/** The width of the check boxes at the left of the gutter (§5.2.1 of the specification). */
|
|
4
|
+
export declare const CHECK_WIDTH = 24;
|
|
5
|
+
/** The width of the button at the right of a header that opens the column menu. */
|
|
6
|
+
export declare const MENU_WIDTH = 16;
|
|
7
|
+
/** What is under a point of the grid. */
|
|
8
|
+
export interface Hit {
|
|
9
|
+
area: "cell" | "header" | "group" | "gutter" | "corner" | "none";
|
|
10
|
+
/** A view position, for cells and the gutter; clamped to the rows. */
|
|
11
|
+
row: number;
|
|
12
|
+
/** A display position, for cells and headers. */
|
|
13
|
+
column: number;
|
|
14
|
+
/** Within a few pixels of the right edge of a header, where dragging resizes the column. */
|
|
15
|
+
edge: boolean;
|
|
16
|
+
/** On the button of a header that opens the column menu. */
|
|
17
|
+
menu: boolean;
|
|
18
|
+
/** On the check box of a row (the gutter) or of every row (the corner). */
|
|
19
|
+
check?: boolean;
|
|
20
|
+
/** For a group header: its row of groups, from the top. */
|
|
21
|
+
level?: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Where the parts of the grid are on the canvas (§6 of react.md): the rows of groups and the column names at the
|
|
25
|
+
* top, the gutter at the left, the rows, the totals row at the bottom, and the columns pinned at each side with those
|
|
26
|
+
* that scroll between them. The canvas lays it out and draws with it; React places what it puts over the canvas
|
|
27
|
+
* with it.
|
|
28
|
+
*/
|
|
29
|
+
export declare class Geometry {
|
|
30
|
+
readonly scrollX: ScrollAxis;
|
|
31
|
+
readonly scrollY: ScrollAxis;
|
|
32
|
+
width: number;
|
|
33
|
+
height: number;
|
|
34
|
+
rowHeight: number;
|
|
35
|
+
headerHeight: number;
|
|
36
|
+
/** The height of the row of column names; `headerHeight` adds the rows of groups above it. */
|
|
37
|
+
nameHeight: number;
|
|
38
|
+
groupHeight: number;
|
|
39
|
+
/** Rows of groups above the column names. */
|
|
40
|
+
groupRows: number;
|
|
41
|
+
/** By display position: its group in each row of groups (an id and a name), or null. */
|
|
42
|
+
groupPaths: ({
|
|
43
|
+
id: number;
|
|
44
|
+
name: string;
|
|
45
|
+
} | null)[][];
|
|
46
|
+
gutterWidth: number;
|
|
47
|
+
/** Whether the gutter has a check box for each row. */
|
|
48
|
+
checkboxes: boolean;
|
|
49
|
+
/** The height of the totals row at the bottom (§5.1 of the specification), 0 without totals. */
|
|
50
|
+
footer: number;
|
|
51
|
+
/** The rows of the view on screen. */
|
|
52
|
+
rowCount: number;
|
|
53
|
+
/** The widths of the displayed columns, by display position. */
|
|
54
|
+
layout: ColumnLayout;
|
|
55
|
+
/** How many displayed columns are pinned at the left and at the right. */
|
|
56
|
+
frozenLeft: number;
|
|
57
|
+
frozenRight: number;
|
|
58
|
+
/** The top of the row of column names: the rows of groups are above it. */
|
|
59
|
+
get groupTop(): number;
|
|
60
|
+
/** The first row on screen. */
|
|
61
|
+
topRow(): number;
|
|
62
|
+
/** Rows on screen `[first, last)`. */
|
|
63
|
+
visibleRows(): [number, number];
|
|
64
|
+
/** The bottom of the rows: the top of the totals row. */
|
|
65
|
+
get bottom(): number;
|
|
66
|
+
get frozenWidth(): number;
|
|
67
|
+
get rightStart(): number;
|
|
68
|
+
get frozenRightWidth(): number;
|
|
69
|
+
/** Display positions `[first, last]` of the scrolling columns on screen, or `[0, -1]`. */
|
|
70
|
+
visibleScrollColumns(): [number, number];
|
|
71
|
+
/** The x of a displayed column's left edge on the canvas. */
|
|
72
|
+
columnX(column: number): number;
|
|
73
|
+
rowY(row: number): number;
|
|
74
|
+
/** The width of a displayed column. */
|
|
75
|
+
columnWidth(column: number): number;
|
|
76
|
+
/**
|
|
77
|
+
* Where a cell is, in the body's coordinates, and whether it is on screen: outside the header and the gutter,
|
|
78
|
+
* and for a scrolling column, between the columns pinned at each side.
|
|
79
|
+
*/
|
|
80
|
+
cellBox(row: number, column: number): {
|
|
81
|
+
left: number;
|
|
82
|
+
top: number;
|
|
83
|
+
width: number;
|
|
84
|
+
height: number;
|
|
85
|
+
shown: boolean;
|
|
86
|
+
};
|
|
87
|
+
/** What is at a point given in the scroller's client coordinates. */
|
|
88
|
+
hit(x: number, y: number): Hit;
|
|
89
|
+
/** The display position of the column at an x of the canvas; -1 when there is none. */
|
|
90
|
+
columnAt(x: number): number;
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/canvas/geometry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,4FAA4F;AAC5F,eAAO,MAAM,WAAW,KAAK,CAAC;AAC9B,mFAAmF;AACnF,eAAO,MAAM,UAAU,KAAK,CAAC;AAE7B,yCAAyC;AACzC,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;IACjE,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf,4FAA4F;IAC5F,IAAI,EAAE,OAAO,CAAC;IACd,4DAA4D;IAC5D,IAAI,EAAE,OAAO,CAAC;IACd,2EAA2E;IAC3E,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;GAKG;AACH,qBAAa,QAAQ;IACnB,QAAQ,CAAC,OAAO,aAAoB;IACpC,QAAQ,CAAC,OAAO,aAAoB;IACpC,KAAK,SAAK;IACV,MAAM,SAAK;IACX,SAAS,SAAM;IACf,YAAY,SAAM;IAClB,8FAA8F;IAC9F,UAAU,SAAM;IAChB,WAAW,SAAM;IACjB,6CAA6C;IAC7C,SAAS,SAAK;IACd,wFAAwF;IACxF,UAAU,EAAE,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,EAAE,EAAE,CAAM;IAC3D,WAAW,SAAM;IACjB,uDAAuD;IACvD,UAAU,UAAS;IACnB,gGAAgG;IAChG,MAAM,SAAK;IACX,sCAAsC;IACtC,QAAQ,SAAK;IACb,gEAAgE;IAChE,MAAM,eAAwB;IAC9B,0EAA0E;IAC1E,UAAU,SAAK;IACf,WAAW,SAAK;IAEhB,2EAA2E;IAC3E,IAAI,QAAQ,WAEX;IAED,+BAA+B;IAC/B,MAAM,WAEL;IAED,sCAAsC;IACtC,WAAW,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAM9B;IAED,yDAAyD;IACzD,IAAI,MAAM,WAET;IAED,IAAI,WAAW,WAEd;IAED,IAAI,UAAU,WAEb;IAED,IAAI,gBAAgB,WAEnB;IAED,0FAA0F;IAC1F,oBAAoB,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAYvC;IAED,6DAA6D;IAC7D,OAAO,CAAC,MAAM,EAAE,MAAM,UAMrB;IAED,IAAI,CAAC,GAAG,EAAE,MAAM,UAEf;IAED,uCAAuC;IACvC,WAAW,CAAC,MAAM,EAAE,MAAM,UAEzB;IAED;;;OAGG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAUjH;IAED,qEAAqE;IACrE,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,GAAG,CAsC7B;IAED,uFAAuF;IACvF,QAAQ,CAAC,CAAC,EAAE,MAAM,UAUjB;CACF"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { ColumnLayout } from "./layout.js";
|
|
2
|
+
import { ScrollAxis } from "./scroll.js";
|
|
3
|
+
|
|
4
|
+
/** The width of the check boxes at the left of the gutter (§5.2.1 of the specification). */
|
|
5
|
+
export const CHECK_WIDTH = 24;
|
|
6
|
+
/** The width of the button at the right of a header that opens the column menu. */
|
|
7
|
+
export const MENU_WIDTH = 16;
|
|
8
|
+
|
|
9
|
+
/** What is under a point of the grid. */
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Where the parts of the grid are on the canvas (§6 of react.md): the rows of groups and the column names at the
|
|
13
|
+
* top, the gutter at the left, the rows, the totals row at the bottom, and the columns pinned at each side with those
|
|
14
|
+
* that scroll between them. The canvas lays it out and draws with it; React places what it puts over the canvas
|
|
15
|
+
* with it.
|
|
16
|
+
*/
|
|
17
|
+
export class Geometry {
|
|
18
|
+
scrollX = new ScrollAxis();
|
|
19
|
+
scrollY = new ScrollAxis();
|
|
20
|
+
width = 0;
|
|
21
|
+
height = 0;
|
|
22
|
+
rowHeight = 24;
|
|
23
|
+
headerHeight = 32;
|
|
24
|
+
/** The height of the row of column names; `headerHeight` adds the rows of groups above it. */
|
|
25
|
+
nameHeight = 32;
|
|
26
|
+
groupHeight = 24;
|
|
27
|
+
/** Rows of groups above the column names. */
|
|
28
|
+
groupRows = 0;
|
|
29
|
+
/** By display position: its group in each row of groups (an id and a name), or null. */
|
|
30
|
+
groupPaths = [];
|
|
31
|
+
gutterWidth = 48;
|
|
32
|
+
/** Whether the gutter has a check box for each row. */
|
|
33
|
+
checkboxes = false;
|
|
34
|
+
/** The height of the totals row at the bottom (§5.1 of the specification), 0 without totals. */
|
|
35
|
+
footer = 0;
|
|
36
|
+
/** The rows of the view on screen. */
|
|
37
|
+
rowCount = 0;
|
|
38
|
+
/** The widths of the displayed columns, by display position. */
|
|
39
|
+
layout = new ColumnLayout([]);
|
|
40
|
+
/** How many displayed columns are pinned at the left and at the right. */
|
|
41
|
+
frozenLeft = 0;
|
|
42
|
+
frozenRight = 0;
|
|
43
|
+
|
|
44
|
+
/** The top of the row of column names: the rows of groups are above it. */
|
|
45
|
+
get groupTop() {
|
|
46
|
+
return this.groupRows * this.groupHeight;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The first row on screen. */
|
|
50
|
+
topRow() {
|
|
51
|
+
return Math.max(0, Math.floor(this.scrollY.position / this.rowHeight));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Rows on screen `[first, last)`. */
|
|
55
|
+
visibleRows() {
|
|
56
|
+
const rowCount = this.rowCount;
|
|
57
|
+
const body = Math.max(0, this.bottom - this.headerHeight);
|
|
58
|
+
const first = Math.min(rowCount, this.topRow());
|
|
59
|
+
const last = Math.min(rowCount, Math.ceil((this.scrollY.position + body) / this.rowHeight));
|
|
60
|
+
return [first, Math.max(first, last)];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/** The bottom of the rows: the top of the totals row. */
|
|
64
|
+
get bottom() {
|
|
65
|
+
return this.height - this.footer;
|
|
66
|
+
}
|
|
67
|
+
get frozenWidth() {
|
|
68
|
+
return this.layout.left(this.frozenLeft);
|
|
69
|
+
}
|
|
70
|
+
get rightStart() {
|
|
71
|
+
return this.layout.count - this.frozenRight;
|
|
72
|
+
}
|
|
73
|
+
get frozenRightWidth() {
|
|
74
|
+
return this.layout.total - this.layout.left(this.rightStart);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Display positions `[first, last]` of the scrolling columns on screen, or `[0, -1]`. */
|
|
78
|
+
visibleScrollColumns() {
|
|
79
|
+
const rightStart = this.rightStart;
|
|
80
|
+
if (rightStart <= this.frozenLeft) return [0, -1];
|
|
81
|
+
const start = this.frozenWidth + this.scrollX.position;
|
|
82
|
+
const end = Math.min(this.scrollX.position + this.width - this.gutterWidth - this.frozenRightWidth, this.layout.left(rightStart));
|
|
83
|
+
if (start >= this.layout.left(rightStart) || end <= start) return [0, -1];
|
|
84
|
+
const first = Math.max(this.frozenLeft, this.layout.indexAt(start));
|
|
85
|
+
const last = Math.min(rightStart - 1, this.layout.indexAt(Math.max(start, end - 0.001)));
|
|
86
|
+
return [first, last];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** The x of a displayed column's left edge on the canvas. */
|
|
90
|
+
columnX(column) {
|
|
91
|
+
if (column < this.frozenLeft) return this.gutterWidth + this.layout.left(column);
|
|
92
|
+
if (column >= this.rightStart) {
|
|
93
|
+
return this.width - this.frozenRightWidth + this.layout.left(column) - this.layout.left(this.rightStart);
|
|
94
|
+
}
|
|
95
|
+
return this.gutterWidth + this.layout.left(column) - this.scrollX.position;
|
|
96
|
+
}
|
|
97
|
+
rowY(row) {
|
|
98
|
+
return this.headerHeight + row * this.rowHeight - this.scrollY.position;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** The width of a displayed column. */
|
|
102
|
+
columnWidth(column) {
|
|
103
|
+
return this.layout.width(column);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Where a cell is, in the body's coordinates, and whether it is on screen: outside the header and the gutter,
|
|
108
|
+
* and for a scrolling column, between the columns pinned at each side.
|
|
109
|
+
*/
|
|
110
|
+
cellBox(row, column) {
|
|
111
|
+
const left = this.columnX(column);
|
|
112
|
+
const top = this.rowY(row);
|
|
113
|
+
const width = this.layout.width(column);
|
|
114
|
+
const height = this.rowHeight;
|
|
115
|
+
const scrolls = column >= this.frozenLeft && column < this.rightStart;
|
|
116
|
+
const from = scrolls ? this.gutterWidth + this.frozenWidth : this.gutterWidth;
|
|
117
|
+
const to = scrolls ? this.width - this.frozenRightWidth : this.width;
|
|
118
|
+
const shown = top + height > this.headerHeight && top < this.bottom && left + width > from && left < to;
|
|
119
|
+
return {
|
|
120
|
+
left,
|
|
121
|
+
top,
|
|
122
|
+
width,
|
|
123
|
+
height,
|
|
124
|
+
shown
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** What is at a point given in the scroller's client coordinates. */
|
|
129
|
+
hit(x, y) {
|
|
130
|
+
const rowCount = this.rowCount;
|
|
131
|
+
const none = {
|
|
132
|
+
area: "none",
|
|
133
|
+
row: 0,
|
|
134
|
+
column: 0,
|
|
135
|
+
edge: false,
|
|
136
|
+
menu: false
|
|
137
|
+
};
|
|
138
|
+
if (x < 0 || y < 0 || x > this.width || y > this.height || y >= this.bottom) return none;
|
|
139
|
+
const row = Math.max(0, Math.min(rowCount - 1, Math.floor((y - this.headerHeight + this.scrollY.position) / this.rowHeight)));
|
|
140
|
+
if (x < this.gutterWidth) {
|
|
141
|
+
return {
|
|
142
|
+
area: y < this.headerHeight ? "corner" : "gutter",
|
|
143
|
+
row,
|
|
144
|
+
column: 0,
|
|
145
|
+
edge: false,
|
|
146
|
+
menu: false,
|
|
147
|
+
check: this.checkboxes && x < CHECK_WIDTH
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const column = this.columnAt(x);
|
|
151
|
+
if (column < 0) return none;
|
|
152
|
+
const right = this.columnX(column) + this.layout.width(column);
|
|
153
|
+
const edge = Math.abs(x - right) <= 4;
|
|
154
|
+
if (y < this.groupTop) {
|
|
155
|
+
const level = Math.floor(y / this.groupHeight);
|
|
156
|
+
return {
|
|
157
|
+
area: this.groupPaths[column]?.[level] ? "group" : "none",
|
|
158
|
+
row: 0,
|
|
159
|
+
column,
|
|
160
|
+
edge: false,
|
|
161
|
+
menu: false,
|
|
162
|
+
level
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
if (y < this.headerHeight) {
|
|
166
|
+
return {
|
|
167
|
+
area: "header",
|
|
168
|
+
row: 0,
|
|
169
|
+
column,
|
|
170
|
+
edge,
|
|
171
|
+
menu: !edge && x >= right - MENU_WIDTH - 4
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
if (rowCount === 0 || y - this.headerHeight + this.scrollY.position >= rowCount * this.rowHeight) return none;
|
|
175
|
+
return {
|
|
176
|
+
area: "cell",
|
|
177
|
+
row,
|
|
178
|
+
column,
|
|
179
|
+
edge: false,
|
|
180
|
+
menu: false
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** The display position of the column at an x of the canvas; -1 when there is none. */
|
|
185
|
+
columnAt(x) {
|
|
186
|
+
if (this.layout.count === 0) return -1;
|
|
187
|
+
const rightX = this.width - this.frozenRightWidth;
|
|
188
|
+
if (this.frozenRight > 0 && x >= rightX) {
|
|
189
|
+
return Math.max(this.rightStart, this.layout.indexAt(this.layout.left(this.rightStart) + x - rightX));
|
|
190
|
+
}
|
|
191
|
+
const local = x - this.gutterWidth;
|
|
192
|
+
if (this.frozenLeft > 0 && local < this.frozenWidth) return this.layout.indexAt(local);
|
|
193
|
+
const column = this.layout.indexAt(local + this.scrollX.position);
|
|
194
|
+
return Math.min(column, this.rightStart - 1);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":["ColumnLayout","ScrollAxis","CHECK_WIDTH","MENU_WIDTH","Geometry","scrollX","scrollY","width","height","rowHeight","headerHeight","nameHeight","groupHeight","groupRows","groupPaths","gutterWidth","checkboxes","footer","rowCount","layout","frozenLeft","frozenRight","groupTop","topRow","Math","max","floor","position","visibleRows","body","bottom","first","min","last","ceil","frozenWidth","left","rightStart","count","frozenRightWidth","total","visibleScrollColumns","start","end","indexAt","columnX","column","rowY","row","columnWidth","cellBox","top","scrolls","from","to","shown","hit","x","y","none","area","edge","menu","check","columnAt","right","abs","level","rightX","local"],"sources":["geometry.ts"],"sourcesContent":["import { ColumnLayout } from \"./layout.js\";\nimport { ScrollAxis } from \"./scroll.js\";\n\n/** The width of the check boxes at the left of the gutter (§5.2.1 of the specification). */\nexport const CHECK_WIDTH = 24;\n/** The width of the button at the right of a header that opens the column menu. */\nexport const MENU_WIDTH = 16;\n\n/** What is under a point of the grid. */\nexport interface Hit {\n area: \"cell\" | \"header\" | \"group\" | \"gutter\" | \"corner\" | \"none\";\n /** A view position, for cells and the gutter; clamped to the rows. */\n row: number;\n /** A display position, for cells and headers. */\n column: number;\n /** Within a few pixels of the right edge of a header, where dragging resizes the column. */\n edge: boolean;\n /** On the button of a header that opens the column menu. */\n menu: boolean;\n /** On the check box of a row (the gutter) or of every row (the corner). */\n check?: boolean;\n /** For a group header: its row of groups, from the top. */\n level?: number;\n}\n\n/**\n * Where the parts of the grid are on the canvas (§6 of react.md): the rows of groups and the column names at the\n * top, the gutter at the left, the rows, the totals row at the bottom, and the columns pinned at each side with those\n * that scroll between them. The canvas lays it out and draws with it; React places what it puts over the canvas\n * with it.\n */\nexport class Geometry {\n readonly scrollX = new ScrollAxis();\n readonly scrollY = new ScrollAxis();\n width = 0;\n height = 0;\n rowHeight = 24;\n headerHeight = 32;\n /** The height of the row of column names; `headerHeight` adds the rows of groups above it. */\n nameHeight = 32;\n groupHeight = 24;\n /** Rows of groups above the column names. */\n groupRows = 0;\n /** By display position: its group in each row of groups (an id and a name), or null. */\n groupPaths: ({ id: number; name: string } | null)[][] = [];\n gutterWidth = 48;\n /** Whether the gutter has a check box for each row. */\n checkboxes = false;\n /** The height of the totals row at the bottom (§5.1 of the specification), 0 without totals. */\n footer = 0;\n /** The rows of the view on screen. */\n rowCount = 0;\n /** The widths of the displayed columns, by display position. */\n layout = new ColumnLayout([]);\n /** How many displayed columns are pinned at the left and at the right. */\n frozenLeft = 0;\n frozenRight = 0;\n\n /** The top of the row of column names: the rows of groups are above it. */\n get groupTop() {\n return this.groupRows * this.groupHeight;\n }\n\n /** The first row on screen. */\n topRow() {\n return Math.max(0, Math.floor(this.scrollY.position / this.rowHeight));\n }\n\n /** Rows on screen `[first, last)`. */\n visibleRows(): [number, number] {\n const rowCount = this.rowCount;\n const body = Math.max(0, this.bottom - this.headerHeight);\n const first = Math.min(rowCount, this.topRow());\n const last = Math.min(rowCount, Math.ceil((this.scrollY.position + body) / this.rowHeight));\n return [first, Math.max(first, last)];\n }\n\n /** The bottom of the rows: the top of the totals row. */\n get bottom() {\n return this.height - this.footer;\n }\n\n get frozenWidth() {\n return this.layout.left(this.frozenLeft);\n }\n\n get rightStart() {\n return this.layout.count - this.frozenRight;\n }\n\n get frozenRightWidth() {\n return this.layout.total - this.layout.left(this.rightStart);\n }\n\n /** Display positions `[first, last]` of the scrolling columns on screen, or `[0, -1]`. */\n visibleScrollColumns(): [number, number] {\n const rightStart = this.rightStart;\n if (rightStart <= this.frozenLeft) return [0, -1];\n const start = this.frozenWidth + this.scrollX.position;\n const end = Math.min(\n this.scrollX.position + this.width - this.gutterWidth - this.frozenRightWidth,\n this.layout.left(rightStart),\n );\n if (start >= this.layout.left(rightStart) || end <= start) return [0, -1];\n const first = Math.max(this.frozenLeft, this.layout.indexAt(start));\n const last = Math.min(rightStart - 1, this.layout.indexAt(Math.max(start, end - 0.001)));\n return [first, last];\n }\n\n /** The x of a displayed column's left edge on the canvas. */\n columnX(column: number) {\n if (column < this.frozenLeft) return this.gutterWidth + this.layout.left(column);\n if (column >= this.rightStart) {\n return this.width - this.frozenRightWidth + this.layout.left(column) - this.layout.left(this.rightStart);\n }\n return this.gutterWidth + this.layout.left(column) - this.scrollX.position;\n }\n\n rowY(row: number) {\n return this.headerHeight + row * this.rowHeight - this.scrollY.position;\n }\n\n /** The width of a displayed column. */\n columnWidth(column: number) {\n return this.layout.width(column);\n }\n\n /**\n * Where a cell is, in the body's coordinates, and whether it is on screen: outside the header and the gutter,\n * and for a scrolling column, between the columns pinned at each side.\n */\n cellBox(row: number, column: number): { left: number; top: number; width: number; height: number; shown: boolean } {\n const left = this.columnX(column);\n const top = this.rowY(row);\n const width = this.layout.width(column);\n const height = this.rowHeight;\n const scrolls = column >= this.frozenLeft && column < this.rightStart;\n const from = scrolls ? this.gutterWidth + this.frozenWidth : this.gutterWidth;\n const to = scrolls ? this.width - this.frozenRightWidth : this.width;\n const shown = top + height > this.headerHeight && top < this.bottom && left + width > from && left < to;\n return { left, top, width, height, shown };\n }\n\n /** What is at a point given in the scroller's client coordinates. */\n hit(x: number, y: number): Hit {\n const rowCount = this.rowCount;\n const none: Hit = { area: \"none\", row: 0, column: 0, edge: false, menu: false };\n if (x < 0 || y < 0 || x > this.width || y > this.height || y >= this.bottom) return none;\n const row = Math.max(\n 0,\n Math.min(rowCount - 1, Math.floor((y - this.headerHeight + this.scrollY.position) / this.rowHeight)),\n );\n if (x < this.gutterWidth) {\n return {\n area: y < this.headerHeight ? \"corner\" : \"gutter\",\n row,\n column: 0,\n edge: false,\n menu: false,\n check: this.checkboxes && x < CHECK_WIDTH,\n };\n }\n const column = this.columnAt(x);\n if (column < 0) return none;\n const right = this.columnX(column) + this.layout.width(column);\n const edge = Math.abs(x - right) <= 4;\n if (y < this.groupTop) {\n const level = Math.floor(y / this.groupHeight);\n return {\n area: this.groupPaths[column]?.[level] ? \"group\" : \"none\",\n row: 0,\n column,\n edge: false,\n menu: false,\n level,\n };\n }\n if (y < this.headerHeight) {\n return { area: \"header\", row: 0, column, edge, menu: !edge && x >= right - MENU_WIDTH - 4 };\n }\n if (rowCount === 0 || y - this.headerHeight + this.scrollY.position >= rowCount * this.rowHeight) return none;\n return { area: \"cell\", row, column, edge: false, menu: false };\n }\n\n /** The display position of the column at an x of the canvas; -1 when there is none. */\n columnAt(x: number) {\n if (this.layout.count === 0) return -1;\n const rightX = this.width - this.frozenRightWidth;\n if (this.frozenRight > 0 && x >= rightX) {\n return Math.max(this.rightStart, this.layout.indexAt(this.layout.left(this.rightStart) + x - rightX));\n }\n const local = x - this.gutterWidth;\n if (this.frozenLeft > 0 && local < this.frozenWidth) return this.layout.indexAt(local);\n const column = this.layout.indexAt(local + this.scrollX.position);\n return Math.min(column, this.rightStart - 1);\n }\n}\n"],"mappings":"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","ignoreList":[]}
|