@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.
Files changed (89) hide show
  1. package/dist/DataGrid.d.ts +6 -0
  2. package/dist/DataGrid.d.ts.map +1 -0
  3. package/dist/DataGrid.js +642 -0
  4. package/dist/DataGrid.js.map +1 -0
  5. package/dist/cn.d.ts +7 -0
  6. package/dist/cn.d.ts.map +1 -0
  7. package/dist/cn.js +9 -0
  8. package/dist/cn.js.map +1 -0
  9. package/dist/components/ColumnHeaderCell.d.ts +18 -0
  10. package/dist/components/ColumnHeaderCell.d.ts.map +1 -0
  11. package/dist/components/ColumnHeaderCell.js +30 -0
  12. package/dist/components/ColumnHeaderCell.js.map +1 -0
  13. package/dist/components/FillHandle.d.ts +11 -0
  14. package/dist/components/FillHandle.d.ts.map +1 -0
  15. package/dist/components/FillHandle.js +16 -0
  16. package/dist/components/FillHandle.js.map +1 -0
  17. package/dist/components/GridCell.d.ts +25 -0
  18. package/dist/components/GridCell.d.ts.map +1 -0
  19. package/dist/components/GridCell.js +9 -0
  20. package/dist/components/GridCell.js.map +1 -0
  21. package/dist/editors/BooleanEditor.d.ts +5 -0
  22. package/dist/editors/BooleanEditor.d.ts.map +1 -0
  23. package/dist/editors/BooleanEditor.js +23 -0
  24. package/dist/editors/BooleanEditor.js.map +1 -0
  25. package/dist/editors/DateEditor.d.ts +7 -0
  26. package/dist/editors/DateEditor.d.ts.map +1 -0
  27. package/dist/editors/DateEditor.js +26 -0
  28. package/dist/editors/DateEditor.js.map +1 -0
  29. package/dist/editors/NumberEditor.d.ts +3 -0
  30. package/dist/editors/NumberEditor.d.ts.map +1 -0
  31. package/dist/editors/NumberEditor.js +30 -0
  32. package/dist/editors/NumberEditor.js.map +1 -0
  33. package/dist/editors/SelectEditor.d.ts +3 -0
  34. package/dist/editors/SelectEditor.d.ts.map +1 -0
  35. package/dist/editors/SelectEditor.js +18 -0
  36. package/dist/editors/SelectEditor.js.map +1 -0
  37. package/dist/editors/TextEditor.d.ts +3 -0
  38. package/dist/editors/TextEditor.d.ts.map +1 -0
  39. package/dist/editors/TextEditor.js +22 -0
  40. package/dist/editors/TextEditor.js.map +1 -0
  41. package/dist/editors/index.d.ts +6 -0
  42. package/dist/editors/index.d.ts.map +1 -0
  43. package/dist/editors/index.js +6 -0
  44. package/dist/editors/index.js.map +1 -0
  45. package/dist/editors/useAutoFocusSelect.d.ts +9 -0
  46. package/dist/editors/useAutoFocusSelect.d.ts.map +1 -0
  47. package/dist/editors/useAutoFocusSelect.js +30 -0
  48. package/dist/editors/useAutoFocusSelect.js.map +1 -0
  49. package/dist/formatting.d.ts +9 -0
  50. package/dist/formatting.d.ts.map +1 -0
  51. package/dist/formatting.js +38 -0
  52. package/dist/formatting.js.map +1 -0
  53. package/dist/index.d.ts +20 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +18 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/lib/clipboard.d.ts +28 -0
  58. package/dist/lib/clipboard.d.ts.map +1 -0
  59. package/dist/lib/clipboard.js +53 -0
  60. package/dist/lib/clipboard.js.map +1 -0
  61. package/dist/lib/fill.d.ts +35 -0
  62. package/dist/lib/fill.d.ts.map +1 -0
  63. package/dist/lib/fill.js +76 -0
  64. package/dist/lib/fill.js.map +1 -0
  65. package/dist/lib/index.d.ts +7 -0
  66. package/dist/lib/index.d.ts.map +1 -0
  67. package/dist/lib/index.js +7 -0
  68. package/dist/lib/index.js.map +1 -0
  69. package/dist/lib/keyboard.d.ts +40 -0
  70. package/dist/lib/keyboard.d.ts.map +1 -0
  71. package/dist/lib/keyboard.js +85 -0
  72. package/dist/lib/keyboard.js.map +1 -0
  73. package/dist/lib/selection.d.ts +26 -0
  74. package/dist/lib/selection.d.ts.map +1 -0
  75. package/dist/lib/selection.js +57 -0
  76. package/dist/lib/selection.js.map +1 -0
  77. package/dist/lib/tsv.d.ts +31 -0
  78. package/dist/lib/tsv.d.ts.map +1 -0
  79. package/dist/lib/tsv.js +113 -0
  80. package/dist/lib/tsv.js.map +1 -0
  81. package/dist/lib/undo.d.ts +36 -0
  82. package/dist/lib/undo.d.ts.map +1 -0
  83. package/dist/lib/undo.js +57 -0
  84. package/dist/lib/undo.js.map +1 -0
  85. package/dist/types.d.ts +119 -0
  86. package/dist/types.d.ts.map +1 -0
  87. package/dist/types.js +2 -0
  88. package/dist/types.js.map +1 -0
  89. package/package.json +66 -0
@@ -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"}
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -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
+ }