@bsuite/data-grid 0.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/dist/DataGrid.d.ts +6 -0
- package/dist/DataGrid.d.ts.map +1 -0
- package/dist/DataGrid.js +642 -0
- package/dist/DataGrid.js.map +1 -0
- package/dist/cn.d.ts +7 -0
- package/dist/cn.d.ts.map +1 -0
- package/dist/cn.js +9 -0
- package/dist/cn.js.map +1 -0
- package/dist/components/ColumnHeaderCell.d.ts +18 -0
- package/dist/components/ColumnHeaderCell.d.ts.map +1 -0
- package/dist/components/ColumnHeaderCell.js +30 -0
- package/dist/components/ColumnHeaderCell.js.map +1 -0
- package/dist/components/FillHandle.d.ts +11 -0
- package/dist/components/FillHandle.d.ts.map +1 -0
- package/dist/components/FillHandle.js +16 -0
- package/dist/components/FillHandle.js.map +1 -0
- package/dist/components/GridCell.d.ts +25 -0
- package/dist/components/GridCell.d.ts.map +1 -0
- package/dist/components/GridCell.js +9 -0
- package/dist/components/GridCell.js.map +1 -0
- package/dist/editors/BooleanEditor.d.ts +5 -0
- package/dist/editors/BooleanEditor.d.ts.map +1 -0
- package/dist/editors/BooleanEditor.js +23 -0
- package/dist/editors/BooleanEditor.js.map +1 -0
- package/dist/editors/DateEditor.d.ts +7 -0
- package/dist/editors/DateEditor.d.ts.map +1 -0
- package/dist/editors/DateEditor.js +26 -0
- package/dist/editors/DateEditor.js.map +1 -0
- package/dist/editors/NumberEditor.d.ts +3 -0
- package/dist/editors/NumberEditor.d.ts.map +1 -0
- package/dist/editors/NumberEditor.js +30 -0
- package/dist/editors/NumberEditor.js.map +1 -0
- package/dist/editors/SelectEditor.d.ts +3 -0
- package/dist/editors/SelectEditor.d.ts.map +1 -0
- package/dist/editors/SelectEditor.js +18 -0
- package/dist/editors/SelectEditor.js.map +1 -0
- package/dist/editors/TextEditor.d.ts +3 -0
- package/dist/editors/TextEditor.d.ts.map +1 -0
- package/dist/editors/TextEditor.js +22 -0
- package/dist/editors/TextEditor.js.map +1 -0
- package/dist/editors/index.d.ts +6 -0
- package/dist/editors/index.d.ts.map +1 -0
- package/dist/editors/index.js +6 -0
- package/dist/editors/index.js.map +1 -0
- package/dist/editors/useAutoFocusSelect.d.ts +9 -0
- package/dist/editors/useAutoFocusSelect.d.ts.map +1 -0
- package/dist/editors/useAutoFocusSelect.js +30 -0
- package/dist/editors/useAutoFocusSelect.js.map +1 -0
- package/dist/formatting.d.ts +9 -0
- package/dist/formatting.d.ts.map +1 -0
- package/dist/formatting.js +38 -0
- package/dist/formatting.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/clipboard.d.ts +28 -0
- package/dist/lib/clipboard.d.ts.map +1 -0
- package/dist/lib/clipboard.js +53 -0
- package/dist/lib/clipboard.js.map +1 -0
- package/dist/lib/fill.d.ts +35 -0
- package/dist/lib/fill.d.ts.map +1 -0
- package/dist/lib/fill.js +76 -0
- package/dist/lib/fill.js.map +1 -0
- package/dist/lib/index.d.ts +7 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/index.js +7 -0
- package/dist/lib/index.js.map +1 -0
- package/dist/lib/keyboard.d.ts +40 -0
- package/dist/lib/keyboard.d.ts.map +1 -0
- package/dist/lib/keyboard.js +85 -0
- package/dist/lib/keyboard.js.map +1 -0
- package/dist/lib/selection.d.ts +26 -0
- package/dist/lib/selection.d.ts.map +1 -0
- package/dist/lib/selection.js +57 -0
- package/dist/lib/selection.js.map +1 -0
- package/dist/lib/tsv.d.ts +31 -0
- package/dist/lib/tsv.d.ts.map +1 -0
- package/dist/lib/tsv.js +113 -0
- package/dist/lib/tsv.js.map +1 -0
- package/dist/lib/undo.d.ts +36 -0
- package/dist/lib/undo.d.ts.map +1 -0
- package/dist/lib/undo.js +57 -0
- package/dist/lib/undo.js.map +1 -0
- package/dist/types.d.ts +119 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +66 -0
package/dist/lib/tsv.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TSV (tab-separated values) round-trip for spreadsheet clipboard interop.
|
|
3
|
+
*
|
|
4
|
+
* Excel/Sheets/Airtable all read and write TSV on the system clipboard for
|
|
5
|
+
* cell-range copy/paste. This is the single most important "feels like
|
|
6
|
+
* Excel" behaviour in the package — getting the grammar exactly right is
|
|
7
|
+
* what lets a range copied here paste cleanly into a real spreadsheet, and
|
|
8
|
+
* vice versa:
|
|
9
|
+
*
|
|
10
|
+
* - Cells are separated by \t, rows by \n on output. Real spreadsheet
|
|
11
|
+
* software (Excel on Windows in particular) emits \r\n between rows; the
|
|
12
|
+
* parser accepts \n, \r\n and bare \r so paste-from-Excel works too.
|
|
13
|
+
* - A cell whose value contains a tab, a newline/CR, or a double quote is
|
|
14
|
+
* wrapped in double quotes with interior quotes doubled (the same
|
|
15
|
+
* RFC-4180-style escaping Excel/Sheets use for TSV, not just CSV).
|
|
16
|
+
* - A cell that needs no escaping is emitted bare. This matters: Excel does
|
|
17
|
+
* NOT quote plain cells, and quoting every cell unconditionally breaks
|
|
18
|
+
* round-trips with real spreadsheet software (a leading `"` is
|
|
19
|
+
* significant there).
|
|
20
|
+
*/
|
|
21
|
+
export function needsQuoting(value) {
|
|
22
|
+
return value.includes('\t') || value.includes('\n') || value.includes('\r') || value.includes('"');
|
|
23
|
+
}
|
|
24
|
+
export function quoteCell(value) {
|
|
25
|
+
if (!needsQuoting(value))
|
|
26
|
+
return value;
|
|
27
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
28
|
+
}
|
|
29
|
+
export function serializeTsv(rows) {
|
|
30
|
+
return rows.map((row) => row.map(quoteCell).join('\t')).join('\n');
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Hand-rolled state machine rather than a split() — a naive `text.split('\n')`
|
|
34
|
+
* followed by `line.split('\t')` corrupts any cell containing an embedded
|
|
35
|
+
* tab or newline inside quotes, which is exactly the case #1 requirement
|
|
36
|
+
* calls out ("values containing tabs/newlines/quotes").
|
|
37
|
+
*/
|
|
38
|
+
export function parseTsv(text) {
|
|
39
|
+
const rows = [];
|
|
40
|
+
let row = [];
|
|
41
|
+
let cell = '';
|
|
42
|
+
let inQuotes = false;
|
|
43
|
+
let i = 0;
|
|
44
|
+
const n = text.length;
|
|
45
|
+
const pushCell = () => {
|
|
46
|
+
row.push(cell);
|
|
47
|
+
cell = '';
|
|
48
|
+
};
|
|
49
|
+
const pushRow = () => {
|
|
50
|
+
pushCell();
|
|
51
|
+
rows.push(row);
|
|
52
|
+
row = [];
|
|
53
|
+
};
|
|
54
|
+
while (i < n) {
|
|
55
|
+
const ch = text[i];
|
|
56
|
+
if (inQuotes) {
|
|
57
|
+
if (ch === '"') {
|
|
58
|
+
if (text[i + 1] === '"') {
|
|
59
|
+
cell += '"';
|
|
60
|
+
i += 2;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
inQuotes = false;
|
|
64
|
+
i += 1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
cell += ch;
|
|
68
|
+
i += 1;
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (ch === '"' && cell.length === 0) {
|
|
72
|
+
inQuotes = true;
|
|
73
|
+
i += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (ch === '\t') {
|
|
77
|
+
pushCell();
|
|
78
|
+
i += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (ch === '\r') {
|
|
82
|
+
if (text[i + 1] === '\n') {
|
|
83
|
+
pushRow();
|
|
84
|
+
i += 2;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
pushRow();
|
|
88
|
+
i += 1;
|
|
89
|
+
continue;
|
|
90
|
+
}
|
|
91
|
+
if (ch === '\n') {
|
|
92
|
+
pushRow();
|
|
93
|
+
i += 1;
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
cell += ch;
|
|
97
|
+
i += 1;
|
|
98
|
+
}
|
|
99
|
+
// Trailing cell/row not terminated by a delimiter.
|
|
100
|
+
if (cell.length > 0 || row.length > 0 || rows.length === 0) {
|
|
101
|
+
pushRow();
|
|
102
|
+
}
|
|
103
|
+
// A single trailing newline in the source (the common case when copying a
|
|
104
|
+
// rectangular range) must not manifest as an extra, wholly-empty row.
|
|
105
|
+
if (rows.length > 1 && /[\r\n]$/.test(text)) {
|
|
106
|
+
const last = rows[rows.length - 1];
|
|
107
|
+
if (last.length === 1 && last[0] === '') {
|
|
108
|
+
rows.pop();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return rows;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=tsv.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsv.js","sourceRoot":"","sources":["../../src/lib/tsv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic undo/redo stack over batch entries. Deliberately data-agnostic
|
|
3
|
+
* (`T` is typically `CellEdit[]`) so the same class backs both the grid's
|
|
4
|
+
* internal Ctrl/Cmd+Z handling AND a host toolbar's Undo/Redo buttons —
|
|
5
|
+
* DataGridHandle exposes `undo()`/`redo()` backed by the same instance.
|
|
6
|
+
*/
|
|
7
|
+
export interface UndoEntry<T> {
|
|
8
|
+
label?: string;
|
|
9
|
+
before: T;
|
|
10
|
+
after: T;
|
|
11
|
+
}
|
|
12
|
+
export declare class UndoStack<T> {
|
|
13
|
+
private undoList;
|
|
14
|
+
private redoList;
|
|
15
|
+
private readonly limit;
|
|
16
|
+
constructor(limit?: number);
|
|
17
|
+
/** Push a completed edit. Clears the redo stack — the standard editor
|
|
18
|
+
* convention: once you make a new edit, the old "future" is gone. */
|
|
19
|
+
push(entry: UndoEntry<T>): void;
|
|
20
|
+
canUndo(): boolean;
|
|
21
|
+
canRedo(): boolean;
|
|
22
|
+
/** Pop the most recent entry and move it to the redo stack. The caller is
|
|
23
|
+
* responsible for applying `entry.before`. Returns undefined if empty. */
|
|
24
|
+
undo(): UndoEntry<T> | undefined;
|
|
25
|
+
/** Pop the most recently undone entry and move it back to the undo stack.
|
|
26
|
+
* The caller is responsible for applying `entry.after`. */
|
|
27
|
+
redo(): UndoEntry<T> | undefined;
|
|
28
|
+
/** Discard the most recently pushed entry without applying anything —
|
|
29
|
+
* used when a commit's mutation callback fails and the edit never really
|
|
30
|
+
* happened (see DataGrid's onError handling). */
|
|
31
|
+
discardLast(): void;
|
|
32
|
+
clear(): void;
|
|
33
|
+
get size(): number;
|
|
34
|
+
get redoSize(): number;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=undo.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undo.d.ts","sourceRoot":"","sources":["../../src/lib/undo.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,CAAC,CAAC;CACV;AAED,qBAAa,SAAS,CAAC,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,KAAK,SAAM;IAIvB;yEACqE;IACrE,IAAI,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI;IAQ/B,OAAO,IAAI,OAAO;IAIlB,OAAO,IAAI,OAAO;IAIlB;8EAC0E;IAC1E,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;IAOhC;+DAC2D;IAC3D,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS;IAOhC;;qDAEiD;IACjD,WAAW,IAAI,IAAI;IAInB,KAAK,IAAI,IAAI;IAKb,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;CACF"}
|
package/dist/lib/undo.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export class UndoStack {
|
|
2
|
+
constructor(limit = 200) {
|
|
3
|
+
this.undoList = [];
|
|
4
|
+
this.redoList = [];
|
|
5
|
+
this.limit = limit;
|
|
6
|
+
}
|
|
7
|
+
/** Push a completed edit. Clears the redo stack — the standard editor
|
|
8
|
+
* convention: once you make a new edit, the old "future" is gone. */
|
|
9
|
+
push(entry) {
|
|
10
|
+
this.undoList.push(entry);
|
|
11
|
+
if (this.undoList.length > this.limit) {
|
|
12
|
+
this.undoList.shift();
|
|
13
|
+
}
|
|
14
|
+
this.redoList = [];
|
|
15
|
+
}
|
|
16
|
+
canUndo() {
|
|
17
|
+
return this.undoList.length > 0;
|
|
18
|
+
}
|
|
19
|
+
canRedo() {
|
|
20
|
+
return this.redoList.length > 0;
|
|
21
|
+
}
|
|
22
|
+
/** Pop the most recent entry and move it to the redo stack. The caller is
|
|
23
|
+
* responsible for applying `entry.before`. Returns undefined if empty. */
|
|
24
|
+
undo() {
|
|
25
|
+
const entry = this.undoList.pop();
|
|
26
|
+
if (!entry)
|
|
27
|
+
return undefined;
|
|
28
|
+
this.redoList.push(entry);
|
|
29
|
+
return entry;
|
|
30
|
+
}
|
|
31
|
+
/** Pop the most recently undone entry and move it back to the undo stack.
|
|
32
|
+
* The caller is responsible for applying `entry.after`. */
|
|
33
|
+
redo() {
|
|
34
|
+
const entry = this.redoList.pop();
|
|
35
|
+
if (!entry)
|
|
36
|
+
return undefined;
|
|
37
|
+
this.undoList.push(entry);
|
|
38
|
+
return entry;
|
|
39
|
+
}
|
|
40
|
+
/** Discard the most recently pushed entry without applying anything —
|
|
41
|
+
* used when a commit's mutation callback fails and the edit never really
|
|
42
|
+
* happened (see DataGrid's onError handling). */
|
|
43
|
+
discardLast() {
|
|
44
|
+
this.undoList.pop();
|
|
45
|
+
}
|
|
46
|
+
clear() {
|
|
47
|
+
this.undoList = [];
|
|
48
|
+
this.redoList = [];
|
|
49
|
+
}
|
|
50
|
+
get size() {
|
|
51
|
+
return this.undoList.length;
|
|
52
|
+
}
|
|
53
|
+
get redoSize() {
|
|
54
|
+
return this.redoList.length;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=undo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"undo.js","sourceRoot":"","sources":["../../src/lib/undo.ts"],"names":[],"mappings":"AAYA,MAAM,OAAO,SAAS;IAKpB,YAAY,KAAK,GAAG,GAAG;QAJf,aAAQ,GAAmB,EAAE,CAAC;QAC9B,aAAQ,GAAmB,EAAE,CAAC;QAIpC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED;yEACqE;IACrE,IAAI,CAAC,KAAmB;QACtB,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;YACtC,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;QACD,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;IAClC,CAAC;IAED;8EAC0E;IAC1E,IAAI;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;+DAC2D;IAC3D,IAAI;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC1B,OAAO,KAAK,CAAC;IACf,CAAC;IAED;;qDAEiD;IACjD,WAAW;QACT,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;IACtB,CAAC;IAED,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;IAED,IAAI,QAAQ;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;IAC9B,CAAC;CACF"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { CellPosition, CellRange } from './lib/selection.js';
|
|
3
|
+
/**
|
|
4
|
+
* Column value type — drives which built-in inline editor renders and how
|
|
5
|
+
* values serialize to/from the TSV clipboard (see lib/clipboard.ts and
|
|
6
|
+
* lib/tsv.ts). `renderEditor` is the escape hatch for anything this set
|
|
7
|
+
* doesn't cover (e.g. an FK-lookup editor) — deliberately NOT implemented
|
|
8
|
+
* in this package; a host wires its own via `renderEditor`.
|
|
9
|
+
*/
|
|
10
|
+
export type ColumnDataType = 'text' | 'number' | 'date' | 'boolean' | 'select';
|
|
11
|
+
export interface SelectOption {
|
|
12
|
+
value: string;
|
|
13
|
+
label: string;
|
|
14
|
+
}
|
|
15
|
+
export interface CellEditorProps<TRow = unknown> {
|
|
16
|
+
value: unknown;
|
|
17
|
+
row: TRow;
|
|
18
|
+
rowIndex: number;
|
|
19
|
+
column: DataGridColumn<TRow>;
|
|
20
|
+
/** Set when editing was triggered by typing a printable character over a
|
|
21
|
+
* focused (non-editing) cell — Excel/Sheets semantics: the typed
|
|
22
|
+
* character replaces the previous value rather than appending to it. */
|
|
23
|
+
initialInputChar?: string;
|
|
24
|
+
onCommit: (nextValue: unknown) => void;
|
|
25
|
+
onCancel: () => void;
|
|
26
|
+
}
|
|
27
|
+
export interface CellRendererProps<TRow = unknown> {
|
|
28
|
+
value: unknown;
|
|
29
|
+
row: TRow;
|
|
30
|
+
rowIndex: number;
|
|
31
|
+
column: DataGridColumn<TRow>;
|
|
32
|
+
}
|
|
33
|
+
export interface DataGridColumn<TRow = unknown> {
|
|
34
|
+
/** Stable column identifier — used for column order, sizing, TSV column
|
|
35
|
+
* position, and edit callbacks. */
|
|
36
|
+
id: string;
|
|
37
|
+
header: string;
|
|
38
|
+
accessor: (row: TRow) => unknown;
|
|
39
|
+
dataType?: ColumnDataType;
|
|
40
|
+
/** Options for dataType: 'select'. Ignored otherwise. */
|
|
41
|
+
options?: SelectOption[];
|
|
42
|
+
width?: number;
|
|
43
|
+
minWidth?: number;
|
|
44
|
+
maxWidth?: number;
|
|
45
|
+
sortable?: boolean;
|
|
46
|
+
/** Default true. Set false for a computed/read-only column. */
|
|
47
|
+
editable?: boolean;
|
|
48
|
+
/** Escape hatch for a fully custom inline editor (e.g. an FK-lookup
|
|
49
|
+
* picker). Not implemented in this package by design. */
|
|
50
|
+
renderEditor?: (props: CellEditorProps<TRow>) => ReactNode;
|
|
51
|
+
renderCell?: (props: CellRendererProps<TRow>) => ReactNode;
|
|
52
|
+
/** Render/copy formatting. Defaults are type-aware (see formatCellValue). */
|
|
53
|
+
formatValue?: (value: unknown, row: TRow) => string;
|
|
54
|
+
/** Parse pasted/typed text back into a typed value. Defaults are
|
|
55
|
+
* type-aware (see parseCellValue). */
|
|
56
|
+
parseValue?: (raw: string, row: TRow) => unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface CellEdit<TRow = unknown> {
|
|
59
|
+
rowIndex: number;
|
|
60
|
+
columnId: string;
|
|
61
|
+
previousValue: unknown;
|
|
62
|
+
value: unknown;
|
|
63
|
+
row: TRow;
|
|
64
|
+
}
|
|
65
|
+
export type DataGridErrorPhase = 'edit' | 'paste' | 'fill' | 'clear' | 'undo' | 'redo' | 'clipboard';
|
|
66
|
+
/**
|
|
67
|
+
* Reported on ANY failed mutation. `onError` is a REQUIRED prop (see
|
|
68
|
+
* DataGridProps) — the single most common defect class across this estate
|
|
69
|
+
* is a mutation that fails silently and reads as "nothing happened" (a
|
|
70
|
+
* failed Supabase write, a stale RLS policy, a network blip). A grid that
|
|
71
|
+
* quietly reverts an edit without telling the host reproduces exactly that
|
|
72
|
+
* defect at the UI's most interactive surface. Making this required forces
|
|
73
|
+
* every integration to decide — even if the decision is "toast it" — instead
|
|
74
|
+
* of forgetting to.
|
|
75
|
+
*/
|
|
76
|
+
export interface DataGridError<TRow = unknown> {
|
|
77
|
+
message: string;
|
|
78
|
+
cause?: unknown;
|
|
79
|
+
phase: DataGridErrorPhase;
|
|
80
|
+
edits: CellEdit<TRow>[];
|
|
81
|
+
}
|
|
82
|
+
export interface DataGridHandle {
|
|
83
|
+
undo: () => void;
|
|
84
|
+
redo: () => void;
|
|
85
|
+
canUndo: () => boolean;
|
|
86
|
+
canRedo: () => boolean;
|
|
87
|
+
getSelection: () => CellRange | null;
|
|
88
|
+
getFocusedCell: () => CellPosition | null;
|
|
89
|
+
copySelection: () => Promise<void>;
|
|
90
|
+
}
|
|
91
|
+
export interface DataGridProps<TRow = unknown> {
|
|
92
|
+
columns: DataGridColumn<TRow>[];
|
|
93
|
+
data: TRow[];
|
|
94
|
+
/** Stable row id extractor, used for React keys and edit bookkeeping.
|
|
95
|
+
* Defaults to row index if omitted. */
|
|
96
|
+
getRowId?: (row: TRow, index: number) => string;
|
|
97
|
+
/**
|
|
98
|
+
* Batch cell-edit callback — called for every commit (typed edit, paste,
|
|
99
|
+
* fill, clear, undo, redo). May be sync or return a Promise; a rejected
|
|
100
|
+
* Promise (or thrown error) is reported via `onError` and the optimistic
|
|
101
|
+
* value is reverted. Required — a grid nobody can persist edits through
|
|
102
|
+
* is a display, not a grid, and making it optional invites exactly the
|
|
103
|
+
* "edited, nothing happened" gap this package exists to close.
|
|
104
|
+
*/
|
|
105
|
+
onCellsEdited: (edits: CellEdit<TRow>[]) => void | Promise<void>;
|
|
106
|
+
/** REQUIRED — see DataGridError doc comment. */
|
|
107
|
+
onError: (error: DataGridError<TRow>) => void;
|
|
108
|
+
/** Freeze the first column (sticky left, excluded from reorder). Default true. */
|
|
109
|
+
frozenFirstColumn?: boolean;
|
|
110
|
+
rowHeight?: number;
|
|
111
|
+
headerHeight?: number;
|
|
112
|
+
/** Height of the scroll viewport. Accepts any CSS length. Default '100%'. */
|
|
113
|
+
height?: string | number;
|
|
114
|
+
className?: string;
|
|
115
|
+
/** Max entries retained in the undo stack. Default 200. */
|
|
116
|
+
undoLimit?: number;
|
|
117
|
+
emptyState?: ReactNode;
|
|
118
|
+
}
|
|
119
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@bsuite/data-grid",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Virtualized, spreadsheet-feel data grid for BSuite apps — cell range selection, keyboard grid navigation, TSV clipboard round-trip, fill handle, undo/redo. Built on @tanstack/react-table + @tanstack/react-virtual.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "UNLICENSED",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/GaryOcean428/bsuite.git",
|
|
13
|
+
"directory": "packages/data-grid"
|
|
14
|
+
},
|
|
15
|
+
"sideEffects": false,
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"main": "dist/index.js",
|
|
21
|
+
"types": "dist/index.d.ts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"types": "./dist/index.d.ts"
|
|
26
|
+
},
|
|
27
|
+
"./lib": {
|
|
28
|
+
"import": "./dist/lib/index.js",
|
|
29
|
+
"types": "./dist/lib/index.d.ts"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.build.json",
|
|
34
|
+
"typecheck": "tsc --noEmit",
|
|
35
|
+
"test": "vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"prepublishOnly": "pnpm build"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@dnd-kit/core": "^6.3.1",
|
|
41
|
+
"@dnd-kit/sortable": "^10.0.0",
|
|
42
|
+
"@dnd-kit/utilities": "^3.2.2",
|
|
43
|
+
"@tanstack/react-virtual": "^3.14.9",
|
|
44
|
+
"clsx": "^2.1.1"
|
|
45
|
+
},
|
|
46
|
+
"peerDependencies": {
|
|
47
|
+
"@tanstack/react-table": "^8.21.3",
|
|
48
|
+
"react": "^19.0.0",
|
|
49
|
+
"react-dom": "^19.0.0"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@tanstack/react-table": "^8.21.3",
|
|
53
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
54
|
+
"@testing-library/react": "^16.3.2",
|
|
55
|
+
"@testing-library/user-event": "^14.6.1",
|
|
56
|
+
"@types/react": "^19.2.17",
|
|
57
|
+
"@types/react-dom": "^19.2.3",
|
|
58
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
59
|
+
"jsdom": "^29.1.1",
|
|
60
|
+
"react": "^19.2.7",
|
|
61
|
+
"react-dom": "^19.2.7",
|
|
62
|
+
"typescript": "~6.0.3",
|
|
63
|
+
"vite": "^8.0.16",
|
|
64
|
+
"vitest": "^4.1.8"
|
|
65
|
+
}
|
|
66
|
+
}
|