@box-kite/react 2.0.1 → 2.1.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/.claude/rules/box-kite-rules.md +16 -0
- package/.claude/skills/box-kite/SKILL.md +16 -0
- package/.claude/skills/box-kite/references/components.md +8 -3
- package/.claude/skills/box-kite/references/patterns.md +23 -0
- package/.cursor/rules/box-kite.mdc +16 -0
- package/AGENTS.md +19 -0
- package/BOX_AI_CONTEXT.md +562 -1
- package/BOX_KITE_AI_CONTEXT.md +562 -1
- package/README.md +39 -7
- package/catalog.cjs +1 -0
- package/catalog.d.ts +8 -0
- package/catalog.mjs +2333 -0
- package/client.cjs +1 -1
- package/client.mjs +40 -42
- package/components/agent.cjs +1 -0
- package/components/agent.d.ts +215 -0
- package/components/agent.mjs +405 -0
- package/components/chart.d.ts +1 -0
- package/components/dashboard.cjs +1 -0
- package/components/dashboard.d.ts +121 -0
- package/components/dashboard.mjs +690 -0
- package/components/dataGrid/components/dataGridBody.d.ts +2 -1
- package/components/dataGrid/components/dataGridDetailRow.d.ts +13 -2
- package/components/dataGrid/components/dataGridGroupRow.d.ts +13 -2
- package/components/dataGrid/components/dataGridRow.d.ts +13 -2
- package/components/dataGrid/contracts/dataGridContract.d.ts +66 -2
- package/components/dataGrid/models/cellModel.d.ts +3 -0
- package/components/dataGrid/models/editModel.d.ts +27 -0
- package/components/dataGrid/models/exportModel.d.ts +2 -1
- package/components/dataGrid/models/gridModel.d.ts +3 -0
- package/components/dataGrid/models/rangeModel.d.ts +119 -0
- package/components/dataGrid/models/viewportModel.d.ts +23 -2
- package/components/dataGrid/useGridNavigation.d.ts +10 -2
- package/components/dataGrid.cjs +4 -1
- package/components/dataGrid.d.ts +3 -0
- package/components/dataGrid.mjs +1056 -765
- package/components/markdown.cjs +1 -0
- package/components/markdown.d.ts +55 -0
- package/components/markdown.mjs +179 -0
- package/components/semantics.d.ts +29 -29
- package/components/skeleton.cjs +1 -0
- package/components/skeleton.d.ts +34 -0
- package/components/skeleton.mjs +25 -0
- package/dashboardSchema.cjs +1 -0
- package/dashboardSchema.mjs +78 -0
- package/docs/a11y.md +2 -3
- package/docs/catalog.md +451 -0
- package/docs/components.md +4 -0
- package/docs/index.md +3 -0
- package/docs/interop.md +237 -0
- package/docs/mcp.md +69 -0
- package/docs/props.md +3 -3
- package/interop.cjs +1 -0
- package/interop.d.ts +51 -0
- package/interop.mjs +454 -0
- package/mcp/src/data.d.ts +88 -0
- package/mcp/src/data.test.d.ts +1 -0
- package/mcp/src/engine.d.ts +23 -0
- package/mcp/src/index.d.ts +1 -0
- package/mcp/src/search.d.ts +26 -0
- package/mcp/src/search.test.d.ts +1 -0
- package/mcp/src/server.d.ts +18 -0
- package/mcp/src/server.test.d.ts +1 -0
- package/mcp/src/styles.d.ts +27 -0
- package/mcp/src/styles.test.d.ts +1 -0
- package/mcp/src/tools.d.ts +18 -0
- package/mcp/src/vocabulary.d.ts +41 -0
- package/package.json +124 -17
- package/react/spec/renderSpec.d.ts +51 -0
- package/react/spec/specBoundary.d.ts +25 -0
- package/react/spec/specRegistry.d.ts +42 -0
- package/react/spec/specRenderer.d.ts +10 -0
- package/registry/blocks/dashboard-shell/dashboard-shell.d.ts +22 -0
- package/registry/blocks/dashboard-shell/dashboard-stats.d.ts +13 -0
- package/registry/blocks/data-grid/invoice-rows.d.ts +15 -0
- package/registry/blocks/data-grid/invoices-grid.d.ts +1 -0
- package/registry/blocks/settings-form/settings-form.d.ts +17 -0
- package/spec.cjs +2 -0
- package/spec.d.ts +12 -0
- package/spec.mjs +382 -0
- package/specData.cjs +1 -0
- package/specData.mjs +69 -0
- package/specDefs.cjs +1 -0
- package/specDefs.mjs +33 -0
- package/specSchema.cjs +1 -0
- package/specSchema.mjs +88 -0
- package/utils/agent/agentUtils.d.ts +81 -0
- package/utils/catalog/catalogContracts.d.ts +19 -0
- package/utils/catalog/catalogManifest.d.ts +8 -0
- package/utils/dashboard/dashboardSchema.d.ts +18 -0
- package/utils/dashboard/dashboardUtils.d.ts +97 -0
- package/utils/interop/a2uiInterop.d.ts +82 -0
- package/utils/interop/assistantUiInterop.d.ts +34 -0
- package/utils/interop/toolInterop.d.ts +30 -0
- package/utils/spec/specData.d.ts +19 -0
- package/utils/spec/specDefs.d.ts +11 -0
- package/utils/spec/specProps.d.ts +15 -0
- package/utils/spec/specSchema.d.ts +23 -0
- package/utils/spec/specTypes.d.ts +71 -0
- package/utils/spec/specValidate.d.ts +10 -0
|
@@ -3,6 +3,17 @@ interface Props<TRow> {
|
|
|
3
3
|
row: GroupRowModel<TRow>;
|
|
4
4
|
/** Position in the whole row list, not in the rendered window. */
|
|
5
5
|
index: number;
|
|
6
|
+
/**
|
|
7
|
+
* The grid's store version. Not read: it is what tells the memo below that something the row draws
|
|
8
|
+
* from the model has moved, since every other prop it takes holds still while the window slides.
|
|
9
|
+
*/
|
|
10
|
+
version: number;
|
|
6
11
|
}
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
declare function DataGridGroupRowImpl<TRow>(props: Props<TRow>): import("react").JSX.Element;
|
|
13
|
+
/**
|
|
14
|
+
* Memoized on its props, which is what makes a scroll cost one row rather than a window of them: the
|
|
15
|
+
* rows either side of the new one are handed the same model and the same index, so they bail out.
|
|
16
|
+
* `memo` loses the type parameter, and the cast is what hands it back.
|
|
17
|
+
*/
|
|
18
|
+
declare const DataGridGroupRow: typeof DataGridGroupRowImpl;
|
|
19
|
+
export default DataGridGroupRow;
|
|
@@ -3,6 +3,17 @@ interface Props<TRow> {
|
|
|
3
3
|
row: RowModel<TRow>;
|
|
4
4
|
/** Position in the whole row list, not in the rendered window. */
|
|
5
5
|
index: number;
|
|
6
|
+
/**
|
|
7
|
+
* The grid's store version. Not read: it is what tells the memo below that something the row draws
|
|
8
|
+
* from the model has moved, since every other prop it takes holds still while the window slides.
|
|
9
|
+
*/
|
|
10
|
+
version: number;
|
|
6
11
|
}
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
declare function DataGridRowImpl<TRow>(props: Props<TRow>): import("react").JSX.Element;
|
|
13
|
+
/**
|
|
14
|
+
* Memoized on its props, which is what makes a scroll cost one row rather than a window of them: the
|
|
15
|
+
* rows either side of the new one are handed the same model and the same index, so they bail out.
|
|
16
|
+
* `memo` loses the type parameter, and the cast is what hands it back.
|
|
17
|
+
*/
|
|
18
|
+
declare const DataGridRow: typeof DataGridRowImpl;
|
|
19
|
+
export default DataGridRow;
|
|
@@ -309,8 +309,48 @@ export interface CellEdit<TRow> {
|
|
|
309
309
|
* message the cell shows and a screen reader is told; `false` rejects it with the grid's own wording.
|
|
310
310
|
*/
|
|
311
311
|
export type CellEditResult = void | boolean | string;
|
|
312
|
-
/** Why the edit stream changed: a cell was accepted, or the host took the edits
|
|
313
|
-
export type DataGridEditReason = 'edit' | 'clear';
|
|
312
|
+
/** Why the edit stream changed: a cell was accepted, a block was pasted, or the host took the edits. */
|
|
313
|
+
export type DataGridEditReason = 'edit' | 'paste' | 'clear';
|
|
314
|
+
/** One cell a paste could not write, and the message `def.onCellEdit` refused it with. */
|
|
315
|
+
export interface RejectedCellEdit<TRow> extends CellEdit<TRow> {
|
|
316
|
+
error: string;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* What one Ctrl+V did, reported once for the whole block however many cells it covered. A refusal skips
|
|
320
|
+
* its own cell and nothing else — a paste is many independent judgements, and one bad value must not lose
|
|
321
|
+
* the ninety-nine good ones — so `applied` and `rejected` together are every cell that was asked about.
|
|
322
|
+
*/
|
|
323
|
+
export interface DataGridPaste<TRow> {
|
|
324
|
+
/** The block it landed in. It reads the values the grid now shows, so `values()` is the result. */
|
|
325
|
+
range: CellRange;
|
|
326
|
+
/** The cells that were written, in the order they were judged. */
|
|
327
|
+
applied: CellEdit<TRow>[];
|
|
328
|
+
/** The cells that were refused, each with what it was refused with. */
|
|
329
|
+
rejected: RejectedCellEdit<TRow>[];
|
|
330
|
+
/** How many cells the block covered that cannot be edited at all — a read-only column, a group row, a row nobody has fetched. */
|
|
331
|
+
skipped: number;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* The block of cells that is marked, in display order. `startRow`/`endRow` are indices into the rows on
|
|
335
|
+
* screen rather than keys, because a range may reach rows a datasource has not fetched — and building a
|
|
336
|
+
* key for each of them is exactly what a lazily-listed grid must not do.
|
|
337
|
+
*/
|
|
338
|
+
export interface CellRange {
|
|
339
|
+
/** The first row the rectangle covers. */
|
|
340
|
+
startRow: number;
|
|
341
|
+
/** The last row it covers, inclusive. */
|
|
342
|
+
endRow: number;
|
|
343
|
+
/** The columns it covers, by key, in display order. */
|
|
344
|
+
columns: Key[];
|
|
345
|
+
/**
|
|
346
|
+
* What is in it, row by row in display order — through the same pipeline an export uses, so a figure
|
|
347
|
+
* read here is the one a `.csv` would carry. A call rather than a field: a range can cover more rows
|
|
348
|
+
* than anybody has fetched, so the values are gathered when they are asked for.
|
|
349
|
+
*/
|
|
350
|
+
values: () => unknown[][];
|
|
351
|
+
}
|
|
352
|
+
/** Why the range changed: a cell became the current one, the block grew, or the grid gave it up. */
|
|
353
|
+
export type DataGridRangeReason = 'select' | 'extend' | 'clear';
|
|
314
354
|
/** Controls which sections appear in the column header context menu */
|
|
315
355
|
export interface ContextMenuConfig {
|
|
316
356
|
/** Show sort actions (Sort Ascending, Sort Descending, Clear Sort). Default: true */
|
|
@@ -500,6 +540,17 @@ export interface GridDefinition<TRow> {
|
|
|
500
540
|
* not, since it costs the keyboard its way out of the grid.
|
|
501
541
|
*/
|
|
502
542
|
tabNavigation?: 'none' | 'cells';
|
|
543
|
+
/**
|
|
544
|
+
* Rectangular cell selection: a drag or Shift with the arrow keys marks a block of cells, and Ctrl+C
|
|
545
|
+
* copies it as the tab-separated text a spreadsheet pastes. Off by default, because a drag that marks
|
|
546
|
+
* cells is a drag that no longer selects text — which is a decision about the page, not about the grid.
|
|
547
|
+
*
|
|
548
|
+
* The *current* cell needs no opt-in and never did: every grid marks the cell its arrows carry on from,
|
|
549
|
+
* whether the pointer or the keyboard put it there, and Ctrl+C on it copies that one cell. Ctrl+V is
|
|
550
|
+
* the same — it fills from the current cell wherever a column is editable, and a block only gives it a
|
|
551
|
+
* bigger target.
|
|
552
|
+
*/
|
|
553
|
+
rangeSelection?: boolean;
|
|
503
554
|
rowHeight?: number;
|
|
504
555
|
/** Number of visible rows. Set to 'all' to render all rows without virtualization or vertical scrollbar. */
|
|
505
556
|
visibleRowsCount?: number | 'all';
|
|
@@ -653,6 +704,19 @@ export interface DataGridProps<TRow> extends Omit<BoxProps<'div', 'datagrid'>, '
|
|
|
653
704
|
* when `clearEdits()` says the host has saved them.
|
|
654
705
|
*/
|
|
655
706
|
onCellEditsChange?: ChangeHandler<CellEdit<TRow>[], DataGridEditReason>;
|
|
707
|
+
/**
|
|
708
|
+
* Fires with the block of cells that is marked, or `undefined` once the grid has given it up. It
|
|
709
|
+
* reports the single current cell too, which is the degenerate range and what Ctrl+C acts on — so a
|
|
710
|
+
* status bar summing the selection is `range?.values()`, and a `reason` of `'select'` is the press or
|
|
711
|
+
* the arrow key that collapsed it back to one.
|
|
712
|
+
*/
|
|
713
|
+
onRangeChange?: ChangeHandler<CellRange | undefined, DataGridRangeReason>;
|
|
714
|
+
/**
|
|
715
|
+
* Fires once with everything a Ctrl+V did: the cells that were written, the cells `def.onCellEdit`
|
|
716
|
+
* refused and the message each was refused with, and how many the block covered that nothing could be
|
|
717
|
+
* written to. It fires after the values have landed, so `range.values()` reads what the grid now shows.
|
|
718
|
+
*/
|
|
719
|
+
onPaste?: (paste: DataGridPaste<TRow>) => void;
|
|
656
720
|
/** Fires with the column and direction the grid is sorted by, or `undefined` once the sort is cleared. */
|
|
657
721
|
onSortingChange?: ChangeHandler<DataGridSort | undefined, DataGridSortReason>;
|
|
658
722
|
/**
|
|
@@ -28,6 +28,9 @@ export default class CellModel<TRow> {
|
|
|
28
28
|
get draft(): unknown;
|
|
29
29
|
/** What the last commit was rejected with. The editor stays open until it is fixed or abandoned. */
|
|
30
30
|
get error(): string | undefined;
|
|
31
|
+
/** Whether the last paste was refused on this cell. A batch shows its refusals on the cells, not in
|
|
32
|
+
* an editor: there is no editor open on any of them, and the messages go to `onPaste`. */
|
|
33
|
+
get rejected(): boolean;
|
|
31
34
|
/** Whether an async `def.onCellEdit` has been asked about this cell and has not answered. */
|
|
32
35
|
get pending(): boolean;
|
|
33
36
|
/**
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { CellEdit, CellEditorConfig, Key } from '../contracts/dataGridContract';
|
|
2
2
|
import { default as ColumnModel } from './columnModel';
|
|
3
3
|
import { default as GridModel } from './gridModel';
|
|
4
|
+
import { RangeBounds } from './rangeModel';
|
|
4
5
|
import { default as RowModel } from './rowModel';
|
|
5
6
|
/** What a rejection with no message of its own says. A silent refusal is one nobody can act on. */
|
|
6
7
|
export declare const DEFAULT_EDIT_ERROR = "Invalid value";
|
|
8
|
+
/** What a pasted value is refused with when it is not the kind of value the cell holds at all. */
|
|
9
|
+
export declare const WRONG_KIND_ERROR = "Wrong kind of value";
|
|
7
10
|
/** A key that starts an edit by replacing the value: one printable character, unmodified. */
|
|
8
11
|
export declare function isTypingKey(event: React.KeyboardEvent): boolean;
|
|
9
12
|
/**
|
|
@@ -21,6 +24,8 @@ export default class EditModel<TRow> {
|
|
|
21
24
|
constructor(grid: GridModel<TRow>);
|
|
22
25
|
/** The accepted values, keyed by cell. Cleared by `clearEdits()` and by a datasource refresh. */
|
|
23
26
|
private readonly values;
|
|
27
|
+
/** The cells the last paste was refused on. They keep the invalid ring, which is all a batch can show. */
|
|
28
|
+
private rejected;
|
|
24
29
|
private _edits;
|
|
25
30
|
private _active?;
|
|
26
31
|
private _token;
|
|
@@ -31,6 +36,10 @@ export default class EditModel<TRow> {
|
|
|
31
36
|
/** The editor a cell opens, worked out from the value in it when the column names none. */
|
|
32
37
|
editorFor(column: ColumnModel<TRow>, value: unknown): CellEditorConfig<TRow>;
|
|
33
38
|
isEditing(rowKey: Key, columnKey: Key): boolean;
|
|
39
|
+
/** Whether an editor is open anywhere. A Ctrl+V while one is belongs to the editor, not to the grid. */
|
|
40
|
+
get isOpen(): boolean;
|
|
41
|
+
/** Whether the last paste was refused on this cell, which is the only mark a batch of them can carry. */
|
|
42
|
+
isRejected(rowKey: Key, columnKey: Key): boolean;
|
|
34
43
|
/** What is in the open editor. `undefined` when none is open, which a cell checks with `isEditing`. */
|
|
35
44
|
get draft(): unknown;
|
|
36
45
|
/** What the last commit was rejected with. The editor stays open on it. */
|
|
@@ -65,6 +74,24 @@ export default class EditModel<TRow> {
|
|
|
65
74
|
* the server says next is the newer answer.
|
|
66
75
|
*/
|
|
67
76
|
clearEdits: () => void;
|
|
77
|
+
/**
|
|
78
|
+
* Fill a block from the clipboard, one `def.onCellEdit` judgement per cell. `textAt` is read by offsets
|
|
79
|
+
* into the block rather than by grid coordinates, because a short clipboard wraps to fill a long block
|
|
80
|
+
* and only the caller knows what it was carrying.
|
|
81
|
+
*
|
|
82
|
+
* A refusal skips its own cell and nothing else: a paste is many independent judgements, and stopping
|
|
83
|
+
* at the first bad value would leave the block half written with no way back. Everything the block
|
|
84
|
+
* covered that cannot be edited at all — a read-only column, a group row, a row nobody has fetched — is
|
|
85
|
+
* skipped before anything is asked, so a validator hears about cells that could actually take a value.
|
|
86
|
+
*/
|
|
87
|
+
paste(bounds: RangeBounds, textAt: (row: number, column: number) => string): void;
|
|
88
|
+
/** A `select`'s options, which may be a function of the row being pasted into. */
|
|
89
|
+
private optionsFor;
|
|
90
|
+
/**
|
|
91
|
+
* What the block's answers do, once they are all in. One notification for the whole paste however many
|
|
92
|
+
* cells it held — the edit stream is a list, and a hundred appends to it is a hundred renders.
|
|
93
|
+
*/
|
|
94
|
+
private settlePaste;
|
|
68
95
|
/**
|
|
69
96
|
* The next editable cell, as a pair of grid coordinates — along the row first and on into the rows
|
|
70
97
|
* after it, which is the order Tab walks a spreadsheet in. Nothing when there is none: Tab then does
|
|
@@ -25,8 +25,9 @@ export default class ExportModel<TRow> {
|
|
|
25
25
|
/**
|
|
26
26
|
* One row's value for one column: the column's own `exportValue` when it has one, else the raw field —
|
|
27
27
|
* either way through the edits, so a file carries what the grid is showing rather than what it was given.
|
|
28
|
+
* Public because the clipboard is an export too: a copied figure is the one a `.csv` would hold.
|
|
28
29
|
*/
|
|
29
|
-
|
|
30
|
+
value(column: ColumnModel<TRow>, row: TRow): unknown;
|
|
30
31
|
/** A column's aggregate over a set of rows, or null where it has no aggregation. */
|
|
31
32
|
private aggregateOf;
|
|
32
33
|
/**
|
|
@@ -10,6 +10,7 @@ import { default as ExportModel } from './exportModel';
|
|
|
10
10
|
import { default as FilterModel } from './filterModel';
|
|
11
11
|
import { default as GroupRowModel } from './groupRowModel';
|
|
12
12
|
import { default as PaginationModel } from './paginationModel';
|
|
13
|
+
import { default as RangeModel } from './rangeModel';
|
|
13
14
|
import { default as RowModel } from './rowModel';
|
|
14
15
|
import { default as TreeModel } from './treeModel';
|
|
15
16
|
import { default as ViewportModel } from './viewportModel';
|
|
@@ -207,6 +208,8 @@ export default class GridModel<TRow> {
|
|
|
207
208
|
readonly tree: TreeModel<TRow>;
|
|
208
209
|
/** Cell editing: which cell is open, what a commit is judged by, and the values the grid has accepted. */
|
|
209
210
|
readonly edits: EditModel<TRow>;
|
|
211
|
+
/** The current cell, the block of cells marked around it, and what the clipboard is written from. */
|
|
212
|
+
readonly range: RangeModel<TRow>;
|
|
210
213
|
/**
|
|
211
214
|
* Throw the fetched blocks away and ask for them again. The grid invalidates on its own whenever it
|
|
212
215
|
* changes the query; this is for the half it cannot see — a filter of the page's own, a row somebody
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { CellRange } from '../contracts/dataGridContract';
|
|
2
|
+
import { default as ColumnModel } from './columnModel';
|
|
3
|
+
import { default as GridModel } from './gridModel';
|
|
4
|
+
/** A body cell, as the grid counts them: a row of the display list and a visible leaf column. */
|
|
5
|
+
export interface CellPosition {
|
|
6
|
+
row: number;
|
|
7
|
+
column: number;
|
|
8
|
+
}
|
|
9
|
+
/** The rectangle two corners describe, in display order and inclusive at both ends. */
|
|
10
|
+
export interface RangeBounds {
|
|
11
|
+
startRow: number;
|
|
12
|
+
endRow: number;
|
|
13
|
+
startColumn: number;
|
|
14
|
+
endColumn: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* A rectangle of values as the text a spreadsheet pastes: tabs between the cells, CRLF between the rows.
|
|
18
|
+
* Excel, Sheets and Numbers all split on the tab natively, which is why the clipboard carries no file.
|
|
19
|
+
*/
|
|
20
|
+
export declare function toTsv(rows: unknown[][]): string;
|
|
21
|
+
/**
|
|
22
|
+
* The same text back, as a rectangle. Scanned character by character rather than split on the separators,
|
|
23
|
+
* because inside a quoted field a tab is a character and a newline is not the end of a row — which is the
|
|
24
|
+
* whole reason the quoting exists. A trailing newline ends the last row rather than opening an empty one.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fromTsv(text: string): string[][];
|
|
27
|
+
/**
|
|
28
|
+
* The current cell and the rectangle it reaches: which cell the arrows carry on from, which block a drag
|
|
29
|
+
* or a Shift+arrow has marked, and what Ctrl+C writes.
|
|
30
|
+
*
|
|
31
|
+
* The current cell lives here rather than in a `:focus-visible` rule (bug #64) for two reasons a CSS
|
|
32
|
+
* pseudo-class cannot answer: a *clicked* cell never matches one, and the mark has to survive the grid
|
|
33
|
+
* losing focus — otherwise a copy has nothing to act on and a range has nowhere to extend from. The
|
|
34
|
+
* roving focus still owns the tab stop and moves the DOM focus; every move it makes is mirrored here,
|
|
35
|
+
* which is what keeps the two from disagreeing about where the user is.
|
|
36
|
+
*/
|
|
37
|
+
export default class RangeModel<TRow> {
|
|
38
|
+
readonly grid: GridModel<TRow>;
|
|
39
|
+
constructor(grid: GridModel<TRow>);
|
|
40
|
+
private _anchor?;
|
|
41
|
+
private _focus?;
|
|
42
|
+
private _dragging;
|
|
43
|
+
/**
|
|
44
|
+
* Whether a block of cells can be marked at all. Copy needs no opt-in — it acts on the current cell,
|
|
45
|
+
* which every grid has — but extending does: a drag that selects cells is a drag that no longer
|
|
46
|
+
* selects text, and that is a decision about the page rather than about the grid.
|
|
47
|
+
*/
|
|
48
|
+
get enabled(): boolean;
|
|
49
|
+
/** The cell the arrows carry on from, and the corner a Shift+arrow moves. */
|
|
50
|
+
get current(): CellPosition | undefined;
|
|
51
|
+
/** Whether a pointer is marking cells right now, which is what makes a cell answer to being entered. */
|
|
52
|
+
get isDragging(): boolean;
|
|
53
|
+
/** The rectangle, normalized, or nothing at all when no cell is current. */
|
|
54
|
+
get bounds(): RangeBounds | undefined;
|
|
55
|
+
/** Whether the rectangle covers more than the current cell — the only case anything is *selected*. */
|
|
56
|
+
get hasSelection(): boolean;
|
|
57
|
+
isCurrent(row: number, column: number): boolean;
|
|
58
|
+
/** Whether the cell is part of a marked block. A single current cell is the cursor, not a selection. */
|
|
59
|
+
isSelected(row: number, column: number): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Put the current cell here. `extend` keeps the anchor where it is, which is what makes Shift+arrow and
|
|
62
|
+
* a drag grow a rectangle instead of moving a cursor.
|
|
63
|
+
*/
|
|
64
|
+
setCurrent(row: number, column: number, extend?: boolean): void;
|
|
65
|
+
/**
|
|
66
|
+
* Follow a focus the grid did not ask for — a press, a Tab out of an editor, a cell focused by name.
|
|
67
|
+
* It collapses the rectangle only when it lands somewhere the model was not already: a Shift+press has
|
|
68
|
+
* already extended by the time the browser focuses the cell it landed on, and must not be undone.
|
|
69
|
+
*/
|
|
70
|
+
syncCurrent(row: number, column: number): void;
|
|
71
|
+
/** Give up the current cell — what a move into the header or the footer leaves behind. */
|
|
72
|
+
clear(): void;
|
|
73
|
+
/**
|
|
74
|
+
* A press on a cell. Shift extends from wherever the anchor is; anything else collapses onto this cell
|
|
75
|
+
* and starts marking, so a drag out of it is one gesture. The browser focuses the cell straight after,
|
|
76
|
+
* which `syncCurrent` then sees as the position it already holds.
|
|
77
|
+
*/
|
|
78
|
+
press(row: number, column: number, extend: boolean): void;
|
|
79
|
+
/** A cell entered while the pointer is down. */
|
|
80
|
+
dragTo(row: number, column: number): void;
|
|
81
|
+
endDrag: () => void;
|
|
82
|
+
/** The columns a rectangle covers, in display order. The marked one when none is named. */
|
|
83
|
+
columnsOf(bounds?: RangeBounds | undefined): ColumnModel<TRow>[];
|
|
84
|
+
/**
|
|
85
|
+
* One cell's value for the clipboard: the export pipeline, so a copied figure is the one a `.csv` of
|
|
86
|
+
* the same grid would carry — the column's `exportValue` when it has one, and the accepted edit over
|
|
87
|
+
* either. The grid's own columns hold no data, bar the row number, which is a value worth copying.
|
|
88
|
+
*/
|
|
89
|
+
private valueOf;
|
|
90
|
+
/**
|
|
91
|
+
* The rectangle, row by row. Rows that hold no cells of their own keep their place rather than closing
|
|
92
|
+
* the gap — a group row carries its own value in the column it groups by, and a detail panel or a block
|
|
93
|
+
* nobody has fetched copies as blanks, so what is pasted is the shape that was on screen.
|
|
94
|
+
*/
|
|
95
|
+
values(bounds?: RangeBounds | undefined): unknown[][];
|
|
96
|
+
/** A rectangle as a consumer hears about it. `values` is a call because a range can be very large. */
|
|
97
|
+
rangeOf(bounds: RangeBounds): CellRange;
|
|
98
|
+
private get range();
|
|
99
|
+
private emit;
|
|
100
|
+
/**
|
|
101
|
+
* Ctrl+C, through the browser's own copy event rather than through a keystroke: `clipboardData` is
|
|
102
|
+
* writable there with no permission and no promise, and the same press on a page with text selected in
|
|
103
|
+
* it still belongs to the browser — which is why a live selection is left alone rather than overridden.
|
|
104
|
+
*/
|
|
105
|
+
onCopy: (event: React.ClipboardEvent) => void;
|
|
106
|
+
/**
|
|
107
|
+
* Where a paste lands. Each axis takes whichever is longer — the block that is marked, or the clipboard
|
|
108
|
+
* — so a block bigger than the clipboard is tiled with it and a clipboard bigger than the block spills
|
|
109
|
+
* past it, both clipped at the edge of the grid. That one rule covers the two cases a spreadsheet keeps
|
|
110
|
+
* apart, and the degenerate one with it: a paste onto the current cell alone starts from a 1×1 block.
|
|
111
|
+
*/
|
|
112
|
+
private pasteBounds;
|
|
113
|
+
/**
|
|
114
|
+
* Ctrl+V, through the browser's own paste event for the reason the copy rides its own: the text is on
|
|
115
|
+
* the event, with no permission to ask for and no promise to wait on. A paste that reaches an open
|
|
116
|
+
* editor belongs to the editor — that is a value being typed, not a block being filled.
|
|
117
|
+
*/
|
|
118
|
+
onPaste: (event: React.ClipboardEvent) => void;
|
|
119
|
+
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { default as GridModel } from './gridModel';
|
|
2
|
+
/** Which way the reader is going. The rendered buffer is only worth its cost in the direction of travel. */
|
|
3
|
+
export type ScrollDirection = 'down' | 'up';
|
|
4
|
+
/** Everything the window needs of a scroll: where the rows are, and which way they are moving. */
|
|
5
|
+
export interface ScrollPosition {
|
|
6
|
+
top: number;
|
|
7
|
+
direction: ScrollDirection;
|
|
8
|
+
}
|
|
2
9
|
/** Windowing parameters for the virtualized body. Rows are sliced by the adapter from these. */
|
|
3
10
|
export interface ViewportWindow {
|
|
4
11
|
/** Index of the first row to render. */
|
|
@@ -20,7 +27,21 @@ export interface ViewportWindow {
|
|
|
20
27
|
export default class ViewportModel<TRow> {
|
|
21
28
|
readonly grid: GridModel<TRow>;
|
|
22
29
|
static readonly DEFAULT_VISIBLE_ROWS_COUNT = 10;
|
|
23
|
-
|
|
30
|
+
/**
|
|
31
|
+
* Rows rendered outside the viewport, and the reason the two numbers differ: the buffer is cover for
|
|
32
|
+
* the frame between a scroll and the render that answers it, so it is only ever needed ahead. Twenty
|
|
33
|
+
* each way rendered fifty-eight rows around an eighteen-row viewport, and cost seven times the fling
|
|
34
|
+
* of a grid rendering twenty-eight (bug #178).
|
|
35
|
+
*/
|
|
36
|
+
static readonly ROWS_AHEAD = 12;
|
|
37
|
+
static readonly ROWS_BEHIND = 4;
|
|
38
|
+
/** The row a scroll cuts through at the top, and the fifth of one `viewHeight` adds at the bottom. */
|
|
39
|
+
private static readonly PARTIAL_ROWS;
|
|
40
|
+
/**
|
|
41
|
+
* Which way the reader is going, given where they were. A scroll that stops keeps the direction it
|
|
42
|
+
* arrived in: the cover belongs ahead of the fling that is about to carry on, not behind it.
|
|
43
|
+
*/
|
|
44
|
+
static directionOf(from: number, to: number, last: ScrollDirection): ScrollDirection;
|
|
24
45
|
constructor(grid: GridModel<TRow>);
|
|
25
46
|
get showAll(): boolean;
|
|
26
47
|
get hasDetailRows(): boolean;
|
|
@@ -40,5 +61,5 @@ export default class ViewportModel<TRow> {
|
|
|
40
61
|
rowTop(index: number): number;
|
|
41
62
|
/** Binary search for the first row whose offset is <= scrollTop (variable-height rows). */
|
|
42
63
|
private findStartIndex;
|
|
43
|
-
window(scrollTop: number): ViewportWindow;
|
|
64
|
+
window(scrollTop: number, direction?: ScrollDirection): ViewportWindow;
|
|
44
65
|
}
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import { GridNavigation } from './gridNavigationContext';
|
|
2
2
|
import { default as GridModel } from './models/gridModel';
|
|
3
|
+
import { ScrollPosition } from './models/viewportModel';
|
|
3
4
|
export interface GridNavigationOptions<TRow> {
|
|
4
5
|
grid: GridModel<TRow>;
|
|
5
6
|
/** The scrolling element — a jump to a row outside the rendered window has to scroll first. */
|
|
6
7
|
scrollerRef: React.RefObject<HTMLElement | null>;
|
|
7
|
-
|
|
8
|
+
/** Read rather than written: a jump has to know which rows are rendered, which the direction decides. */
|
|
9
|
+
scroll: ScrollPosition;
|
|
8
10
|
onScrollTo: (top: number) => void;
|
|
9
11
|
}
|
|
10
|
-
export interface GridNavigator
|
|
12
|
+
export interface GridNavigator {
|
|
13
|
+
/**
|
|
14
|
+
* What every cell reads, through the context. Held apart from `onKeyDown` because it has to be the
|
|
15
|
+
* same object between two renders that changed nothing a cell can see: a scroll re-renders the content,
|
|
16
|
+
* and a fresh context value there re-renders every cell in the window whatever their elements memoize.
|
|
17
|
+
*/
|
|
18
|
+
navigation: GridNavigation;
|
|
11
19
|
/** Put this on the `role="grid"` element: cell keystrokes bubble to it. */
|
|
12
20
|
onKeyDown: (event: React.KeyboardEvent) => void;
|
|
13
21
|
}
|