@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/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bsuite/data-grid — main entry.
|
|
3
|
+
*
|
|
4
|
+
* A virtualized, spreadsheet-feel data grid: row + column virtualization
|
|
5
|
+
* (@tanstack/react-virtual), sorting/column order/sizing
|
|
6
|
+
* (@tanstack/react-table), cell-range selection, keyboard grid navigation,
|
|
7
|
+
* a TSV clipboard round-trip, a fill handle, and an undo/redo stack.
|
|
8
|
+
*
|
|
9
|
+
* No consumer app is wired to this package yet — it ships standalone.
|
|
10
|
+
* Import the pure logic (TSV, selection maths, fill maths, undo stack,
|
|
11
|
+
* keyboard maths, clipboard glue) independently via `@bsuite/data-grid/lib`
|
|
12
|
+
* if a host only needs the non-React pieces.
|
|
13
|
+
*/
|
|
14
|
+
export { DataGrid } from './DataGrid.js';
|
|
15
|
+
export { TextEditor, NumberEditor, DateEditor, BooleanEditor, SelectEditor, } from './editors/index.js';
|
|
16
|
+
export { formatCellValue, parseCellValue } from './formatting.js';
|
|
17
|
+
export { cn } from './cn.js';
|
|
18
|
+
export type { ColumnDataType, SelectOption, CellEditorProps, CellRendererProps, DataGridColumn, CellEdit, DataGridErrorPhase, DataGridError, DataGridHandle, DataGridProps, } from './types.js';
|
|
19
|
+
export type { CellPosition, CellRange } from './lib/selection.js';
|
|
20
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,YAAY,EACV,cAAc,EACd,YAAY,EACZ,eAAe,EACf,iBAAiB,EACjB,cAAc,EACd,QAAQ,EACR,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,aAAa,GACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @bsuite/data-grid — main entry.
|
|
3
|
+
*
|
|
4
|
+
* A virtualized, spreadsheet-feel data grid: row + column virtualization
|
|
5
|
+
* (@tanstack/react-virtual), sorting/column order/sizing
|
|
6
|
+
* (@tanstack/react-table), cell-range selection, keyboard grid navigation,
|
|
7
|
+
* a TSV clipboard round-trip, a fill handle, and an undo/redo stack.
|
|
8
|
+
*
|
|
9
|
+
* No consumer app is wired to this package yet — it ships standalone.
|
|
10
|
+
* Import the pure logic (TSV, selection maths, fill maths, undo stack,
|
|
11
|
+
* keyboard maths, clipboard glue) independently via `@bsuite/data-grid/lib`
|
|
12
|
+
* if a host only needs the non-React pieces.
|
|
13
|
+
*/
|
|
14
|
+
export { DataGrid } from './DataGrid.js';
|
|
15
|
+
export { TextEditor, NumberEditor, DateEditor, BooleanEditor, SelectEditor, } from './editors/index.js';
|
|
16
|
+
export { formatCellValue, parseCellValue } from './formatting.js';
|
|
17
|
+
export { cn } from './cn.js';
|
|
18
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EACL,UAAU,EACV,YAAY,EACZ,UAAU,EACV,aAAa,EACb,YAAY,GACb,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAClE,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { CellPosition, CellRange } from './selection.js';
|
|
2
|
+
export interface ClipboardBounds {
|
|
3
|
+
maxRow: number;
|
|
4
|
+
maxCol: number;
|
|
5
|
+
}
|
|
6
|
+
/** Build the TSV text for a copy of `range`, using `getCellText` to render
|
|
7
|
+
* each cell's display value. Row-major, matching how a real spreadsheet
|
|
8
|
+
* serializes a selection. */
|
|
9
|
+
export declare function buildCopyText(range: CellRange, getCellText: (pos: CellPosition) => string): string;
|
|
10
|
+
export interface PasteEdit {
|
|
11
|
+
row: number;
|
|
12
|
+
col: number;
|
|
13
|
+
value: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Parse pasted TSV text anchored at `anchor`, clipped to the grid bounds.
|
|
17
|
+
* A paste that would run off the bottom/right edge of the grid is
|
|
18
|
+
* truncated rather than throwing — matches Excel/Sheets, which silently
|
|
19
|
+
* drop the overflow instead of erroring or resizing the sheet.
|
|
20
|
+
*/
|
|
21
|
+
export declare function buildPasteEdits(tsvText: string, anchor: CellPosition, bounds: ClipboardBounds): PasteEdit[];
|
|
22
|
+
/**
|
|
23
|
+
* Excel/Sheets semantics: pasting a SINGLE copied cell onto a multi-cell
|
|
24
|
+
* selection repeats that value across the whole selection, rather than only
|
|
25
|
+
* landing on the anchor cell.
|
|
26
|
+
*/
|
|
27
|
+
export declare function expandSingleCellPasteToSelection(edits: PasteEdit[], selection: CellRange | undefined): PasteEdit[];
|
|
28
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/lib/clipboard.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG9D,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;6BAE6B;AAC7B,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,MAAM,GAAG,MAAM,CAUlG;AAED,MAAM,WAAW,SAAS;IACxB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,eAAe,GAAG,SAAS,EAAE,CAc3G;AAED;;;;GAIG;AACH,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,SAAS,EAAE,EAClB,SAAS,EAAE,SAAS,GAAG,SAAS,GAC/B,SAAS,EAAE,CAKb"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { parseTsv, serializeTsv } from './tsv.js';
|
|
2
|
+
import { rangeColCount, rangeRowCount, rangeToCells } from './selection.js';
|
|
3
|
+
/** Build the TSV text for a copy of `range`, using `getCellText` to render
|
|
4
|
+
* each cell's display value. Row-major, matching how a real spreadsheet
|
|
5
|
+
* serializes a selection. */
|
|
6
|
+
export function buildCopyText(range, getCellText) {
|
|
7
|
+
const rows = [];
|
|
8
|
+
for (let r = range.startRow; r <= range.endRow; r += 1) {
|
|
9
|
+
const row = [];
|
|
10
|
+
for (let c = range.startCol; c <= range.endCol; c += 1) {
|
|
11
|
+
row.push(getCellText({ row: r, col: c }));
|
|
12
|
+
}
|
|
13
|
+
rows.push(row);
|
|
14
|
+
}
|
|
15
|
+
return serializeTsv(rows);
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Parse pasted TSV text anchored at `anchor`, clipped to the grid bounds.
|
|
19
|
+
* A paste that would run off the bottom/right edge of the grid is
|
|
20
|
+
* truncated rather than throwing — matches Excel/Sheets, which silently
|
|
21
|
+
* drop the overflow instead of erroring or resizing the sheet.
|
|
22
|
+
*/
|
|
23
|
+
export function buildPasteEdits(tsvText, anchor, bounds) {
|
|
24
|
+
const grid = parseTsv(tsvText);
|
|
25
|
+
const edits = [];
|
|
26
|
+
for (let r = 0; r < grid.length; r += 1) {
|
|
27
|
+
const targetRow = anchor.row + r;
|
|
28
|
+
if (targetRow < 0 || targetRow > bounds.maxRow)
|
|
29
|
+
continue;
|
|
30
|
+
const rowValues = grid[r];
|
|
31
|
+
for (let c = 0; c < rowValues.length; c += 1) {
|
|
32
|
+
const targetCol = anchor.col + c;
|
|
33
|
+
if (targetCol < 0 || targetCol > bounds.maxCol)
|
|
34
|
+
continue;
|
|
35
|
+
edits.push({ row: targetRow, col: targetCol, value: rowValues[c] });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return edits;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Excel/Sheets semantics: pasting a SINGLE copied cell onto a multi-cell
|
|
42
|
+
* selection repeats that value across the whole selection, rather than only
|
|
43
|
+
* landing on the anchor cell.
|
|
44
|
+
*/
|
|
45
|
+
export function expandSingleCellPasteToSelection(edits, selection) {
|
|
46
|
+
if (!selection || edits.length !== 1)
|
|
47
|
+
return edits;
|
|
48
|
+
if (rangeRowCount(selection) <= 1 && rangeColCount(selection) <= 1)
|
|
49
|
+
return edits;
|
|
50
|
+
const { value } = edits[0];
|
|
51
|
+
return rangeToCells(selection).map((pos) => ({ row: pos.row, col: pos.col, value }));
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=clipboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../../src/lib/clipboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAElD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO5E;;6BAE6B;AAC7B,MAAM,UAAU,aAAa,CAAC,KAAgB,EAAE,WAA0C;IACxF,MAAM,IAAI,GAAe,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACvD,MAAM,GAAG,GAAa,EAAE,CAAC;QACzB,KAAK,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,EAAE,CAAC,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YACvD,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACjB,CAAC;IACD,OAAO,YAAY,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,OAAe,EAAE,MAAoB,EAAE,MAAuB;IAC5F,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAgB,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QACxC,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;QACjC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM;YAAE,SAAS;QACzD,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,SAAS,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;YAC7C,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,GAAG,CAAC,CAAC;YACjC,IAAI,SAAS,GAAG,CAAC,IAAI,SAAS,GAAG,MAAM,CAAC,MAAM;gBAAE,SAAS;YACzD,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,SAAS,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gCAAgC,CAC9C,KAAkB,EAClB,SAAgC;IAEhC,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,IAAI,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACjF,MAAM,EAAE,KAAK,EAAE,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,OAAO,YAAY,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACvF,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { CellPosition, CellRange } from './selection.js';
|
|
2
|
+
export type FillDirection = 'down' | 'up' | 'left' | 'right' | 'none';
|
|
3
|
+
export interface FillResult {
|
|
4
|
+
/** The full range after the fill (source ∪ newly-filled cells). */
|
|
5
|
+
range: CellRange;
|
|
6
|
+
direction: FillDirection;
|
|
7
|
+
/** Cells newly covered by the fill — excludes the original source range. */
|
|
8
|
+
filledCells: CellPosition[];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Excel's fill handle only ever extends along ONE axis, even when the
|
|
12
|
+
* pointer moves diagonally — it picks whichever direction moved furthest
|
|
13
|
+
* past the source range's edge. Dragging back inside the source range (or
|
|
14
|
+
* not past any edge) is a no-op.
|
|
15
|
+
*/
|
|
16
|
+
export declare function computeFillRange(source: CellRange, dragTo: CellPosition): FillResult;
|
|
17
|
+
/**
|
|
18
|
+
* Maps a newly-filled cell back to the source cell whose value it should
|
|
19
|
+
* copy, cycling through the source range's rows (for vertical fills) or
|
|
20
|
+
* columns (for horizontal fills) — the same "repeat the pattern" behaviour
|
|
21
|
+
* Excel uses for a plain (non-series) fill.
|
|
22
|
+
*/
|
|
23
|
+
export declare function mapFillTargetToSource(target: CellPosition, source: CellRange, direction: FillDirection): CellPosition;
|
|
24
|
+
/**
|
|
25
|
+
* Double-click-fill-down: Excel extends the fill down to match the extent of
|
|
26
|
+
* contiguous data in an adjacent column. `adjacentColHasValue` answers "does
|
|
27
|
+
* the adjacent column have a non-empty value at this row" for each row
|
|
28
|
+
* below `startRow`; the extent stops at the first gap or `maxRow`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function computeFillDownExtent(params: {
|
|
31
|
+
startRow: number;
|
|
32
|
+
maxRow: number;
|
|
33
|
+
adjacentColHasValue: (row: number) => boolean;
|
|
34
|
+
}): number;
|
|
35
|
+
//# sourceMappingURL=fill.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill.d.ts","sourceRoot":"","sources":["../../src/lib/fill.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAG9D,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;AAEtE,MAAM,WAAW,UAAU;IACzB,mEAAmE;IACnE,KAAK,EAAE,SAAS,CAAC;IACjB,SAAS,EAAE,aAAa,CAAC;IACzB,4EAA4E;IAC5E,WAAW,EAAE,YAAY,EAAE,CAAC;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,GAAG,UAAU,CAoCpF;AAMD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,SAAS,EACjB,SAAS,EAAE,aAAa,GACvB,YAAY,CAYd;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,mBAAmB,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CAC/C,GAAG,MAAM,CAOT"}
|
package/dist/lib/fill.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { isCellInRange, rangeColCount, rangeRowCount, rangeToCells } from './selection.js';
|
|
2
|
+
/**
|
|
3
|
+
* Excel's fill handle only ever extends along ONE axis, even when the
|
|
4
|
+
* pointer moves diagonally — it picks whichever direction moved furthest
|
|
5
|
+
* past the source range's edge. Dragging back inside the source range (or
|
|
6
|
+
* not past any edge) is a no-op.
|
|
7
|
+
*/
|
|
8
|
+
export function computeFillRange(source, dragTo) {
|
|
9
|
+
const allCandidates = [
|
|
10
|
+
{ dir: 'down', amount: dragTo.row - source.endRow },
|
|
11
|
+
{ dir: 'up', amount: source.startRow - dragTo.row },
|
|
12
|
+
{ dir: 'right', amount: dragTo.col - source.endCol },
|
|
13
|
+
{ dir: 'left', amount: source.startCol - dragTo.col },
|
|
14
|
+
];
|
|
15
|
+
const candidates = allCandidates.filter((c) => c.amount > 0);
|
|
16
|
+
if (candidates.length === 0) {
|
|
17
|
+
return { range: source, direction: 'none', filledCells: [] };
|
|
18
|
+
}
|
|
19
|
+
candidates.sort((a, b) => b.amount - a.amount);
|
|
20
|
+
const winner = candidates[0];
|
|
21
|
+
let range;
|
|
22
|
+
switch (winner.dir) {
|
|
23
|
+
case 'down':
|
|
24
|
+
range = { ...source, endRow: source.endRow + winner.amount };
|
|
25
|
+
break;
|
|
26
|
+
case 'up':
|
|
27
|
+
range = { ...source, startRow: source.startRow - winner.amount };
|
|
28
|
+
break;
|
|
29
|
+
case 'right':
|
|
30
|
+
range = { ...source, endCol: source.endCol + winner.amount };
|
|
31
|
+
break;
|
|
32
|
+
case 'left':
|
|
33
|
+
range = { ...source, startCol: source.startCol - winner.amount };
|
|
34
|
+
break;
|
|
35
|
+
default:
|
|
36
|
+
range = source;
|
|
37
|
+
}
|
|
38
|
+
const filledCells = rangeToCells(range).filter((cell) => !isCellInRange(cell, source));
|
|
39
|
+
return { range, direction: winner.dir, filledCells };
|
|
40
|
+
}
|
|
41
|
+
function wrapIndex(offset, period) {
|
|
42
|
+
return ((offset % period) + period) % period;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Maps a newly-filled cell back to the source cell whose value it should
|
|
46
|
+
* copy, cycling through the source range's rows (for vertical fills) or
|
|
47
|
+
* columns (for horizontal fills) — the same "repeat the pattern" behaviour
|
|
48
|
+
* Excel uses for a plain (non-series) fill.
|
|
49
|
+
*/
|
|
50
|
+
export function mapFillTargetToSource(target, source, direction) {
|
|
51
|
+
if (direction === 'none')
|
|
52
|
+
return target;
|
|
53
|
+
if (direction === 'down' || direction === 'up') {
|
|
54
|
+
const rowCount = rangeRowCount(source);
|
|
55
|
+
const rowOffset = wrapIndex(target.row - source.startRow, rowCount);
|
|
56
|
+
return { row: source.startRow + rowOffset, col: target.col };
|
|
57
|
+
}
|
|
58
|
+
const colCount = rangeColCount(source);
|
|
59
|
+
const colOffset = wrapIndex(target.col - source.startCol, colCount);
|
|
60
|
+
return { row: target.row, col: source.startCol + colOffset };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Double-click-fill-down: Excel extends the fill down to match the extent of
|
|
64
|
+
* contiguous data in an adjacent column. `adjacentColHasValue` answers "does
|
|
65
|
+
* the adjacent column have a non-empty value at this row" for each row
|
|
66
|
+
* below `startRow`; the extent stops at the first gap or `maxRow`.
|
|
67
|
+
*/
|
|
68
|
+
export function computeFillDownExtent(params) {
|
|
69
|
+
const { startRow, maxRow, adjacentColHasValue } = params;
|
|
70
|
+
let row = startRow;
|
|
71
|
+
while (row + 1 <= maxRow && adjacentColHasValue(row + 1)) {
|
|
72
|
+
row += 1;
|
|
73
|
+
}
|
|
74
|
+
return row;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=fill.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fill.js","sourceRoot":"","sources":["../../src/lib/fill.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { needsQuoting, quoteCell, serializeTsv, parseTsv } from './tsv.js';
|
|
2
|
+
export { normalizeRange, clampRange, isCellInRange, rangeRowCount, rangeColCount, rangeToCells, rangesEqual, singleCellRange, isSingleCell, type CellPosition, type CellRange, } from './selection.js';
|
|
3
|
+
export { computeFillRange, mapFillTargetToSource, computeFillDownExtent, type FillDirection, type FillResult, } from './fill.js';
|
|
4
|
+
export { UndoStack, type UndoEntry } from './undo.js';
|
|
5
|
+
export { moveFocus, moveTab, moveEnter, isPrintableEditTrigger, type ArrowKey, type GridBounds, type MoveOptions, } from './keyboard.js';
|
|
6
|
+
export { buildCopyText, buildPasteEdits, expandSingleCellPasteToSelection, type ClipboardBounds, type PasteEdit, } from './clipboard.js';
|
|
7
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EACL,cAAc,EACd,UAAU,EACV,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,EACX,eAAe,EACf,YAAY,EACZ,KAAK,YAAY,EACjB,KAAK,SAAS,GACf,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,aAAa,EAClB,KAAK,UAAU,GAChB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAE,KAAK,SAAS,EAAE,MAAM,WAAW,CAAC;AACtD,OAAO,EACL,SAAS,EACT,OAAO,EACP,SAAS,EACT,sBAAsB,EACtB,KAAK,QAAQ,EACb,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,eAAe,EACf,gCAAgC,EAChC,KAAK,eAAe,EACpB,KAAK,SAAS,GACf,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { needsQuoting, quoteCell, serializeTsv, parseTsv } from './tsv.js';
|
|
2
|
+
export { normalizeRange, clampRange, isCellInRange, rangeRowCount, rangeColCount, rangeToCells, rangesEqual, singleCellRange, isSingleCell, } from './selection.js';
|
|
3
|
+
export { computeFillRange, mapFillTargetToSource, computeFillDownExtent, } from './fill.js';
|
|
4
|
+
export { UndoStack } from './undo.js';
|
|
5
|
+
export { moveFocus, moveTab, moveEnter, isPrintableEditTrigger, } from './keyboard.js';
|
|
6
|
+
export { buildCopyText, buildPasteEdits, expandSingleCellPasteToSelection, } from './clipboard.js';
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC;AAC3E,OAAO,EACL,cAAc,EACd,UAAU,EACV,aAAa,EACb,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,EACX,eAAe,EACf,YAAY,GAGb,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,gBAAgB,EAChB,qBAAqB,EACrB,qBAAqB,GAGtB,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,SAAS,EAAkB,MAAM,WAAW,CAAC;AACtD,OAAO,EACL,SAAS,EACT,OAAO,EACP,SAAS,EACT,sBAAsB,GAIvB,MAAM,eAAe,CAAC;AACvB,OAAO,EACL,aAAa,EACb,eAAe,EACf,gCAAgC,GAGjC,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { CellPosition } from './selection.js';
|
|
2
|
+
export interface GridBounds {
|
|
3
|
+
minRow: number;
|
|
4
|
+
maxRow: number;
|
|
5
|
+
minCol: number;
|
|
6
|
+
maxCol: number;
|
|
7
|
+
}
|
|
8
|
+
export type ArrowKey = 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight';
|
|
9
|
+
export interface MoveOptions {
|
|
10
|
+
/** Ctrl (Windows/Linux) or Cmd (macOS) held — jump to the edge of a
|
|
11
|
+
* contiguous data region instead of moving one cell. */
|
|
12
|
+
ctrlOrCmd?: boolean;
|
|
13
|
+
/** Predicate for "this cell has no value" — drives the Ctrl+Arrow jump.
|
|
14
|
+
* Defaults to "never empty", which degrades Ctrl+Arrow to "jump to the
|
|
15
|
+
* grid edge" when the host doesn't supply data-awareness. */
|
|
16
|
+
isEmpty?: (pos: CellPosition) => boolean;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Plain arrow: move one cell, clamped to bounds.
|
|
20
|
+
* Ctrl/Cmd+arrow: jump to the edge of contiguous data, Excel-style —
|
|
21
|
+
* - if the adjacent cell is empty, advance through empty cells and land on
|
|
22
|
+
* the first non-empty cell found (or the grid edge if there is none)
|
|
23
|
+
* - if the adjacent cell is non-empty, advance while cells stay non-empty
|
|
24
|
+
* and land on the last non-empty cell before a gap (or the grid edge)
|
|
25
|
+
*/
|
|
26
|
+
export declare function moveFocus(current: CellPosition, key: ArrowKey, bounds: GridBounds, options?: MoveOptions): CellPosition;
|
|
27
|
+
/** Tab moves right and wraps to the next row's first column; Shift+Tab
|
|
28
|
+
* moves left and wraps to the previous row's last column. */
|
|
29
|
+
export declare function moveTab(current: CellPosition, bounds: GridBounds, shift: boolean): CellPosition;
|
|
30
|
+
/** Enter moves down a row (same column); Shift+Enter moves up. */
|
|
31
|
+
export declare function moveEnter(current: CellPosition, bounds: GridBounds, shift: boolean): CellPosition;
|
|
32
|
+
/** True for a single, non-modified printable character — the trigger for
|
|
33
|
+
* "typing over a focused cell starts editing" (Excel/Sheets semantics). */
|
|
34
|
+
export declare function isPrintableEditTrigger(e: {
|
|
35
|
+
key: string;
|
|
36
|
+
ctrlKey?: boolean;
|
|
37
|
+
metaKey?: boolean;
|
|
38
|
+
altKey?: boolean;
|
|
39
|
+
}): boolean;
|
|
40
|
+
//# sourceMappingURL=keyboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard.d.ts","sourceRoot":"","sources":["../../src/lib/keyboard.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,MAAM,QAAQ,GAAG,SAAS,GAAG,WAAW,GAAG,WAAW,GAAG,YAAY,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B;4DACwD;IACxD,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB;;iEAE6D;IAC7D,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,YAAY,KAAK,OAAO,CAAC;CAC1C;AAyBD;;;;;;;GAOG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,YAAY,EACrB,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,WAAgB,GACxB,YAAY,CA+Bd;AAED;6DAC6D;AAC7D,wBAAgB,OAAO,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAS/F;AAED,kEAAkE;AAClE,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,GAAG,YAAY,CAGjG;AAED;2EAC2E;AAC3E,wBAAgB,sBAAsB,CAAC,CAAC,EAAE;IACxC,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,GAAG,OAAO,CAGV"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
const DIRECTION = {
|
|
2
|
+
ArrowUp: { dRow: -1, dCol: 0 },
|
|
3
|
+
ArrowDown: { dRow: 1, dCol: 0 },
|
|
4
|
+
ArrowLeft: { dRow: 0, dCol: -1 },
|
|
5
|
+
ArrowRight: { dRow: 0, dCol: 1 },
|
|
6
|
+
};
|
|
7
|
+
function inBounds(pos, bounds) {
|
|
8
|
+
return (pos.row >= bounds.minRow &&
|
|
9
|
+
pos.row <= bounds.maxRow &&
|
|
10
|
+
pos.col >= bounds.minCol &&
|
|
11
|
+
pos.col <= bounds.maxCol);
|
|
12
|
+
}
|
|
13
|
+
function clampPosition(pos, bounds) {
|
|
14
|
+
return {
|
|
15
|
+
row: Math.max(bounds.minRow, Math.min(pos.row, bounds.maxRow)),
|
|
16
|
+
col: Math.max(bounds.minCol, Math.min(pos.col, bounds.maxCol)),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Plain arrow: move one cell, clamped to bounds.
|
|
21
|
+
* Ctrl/Cmd+arrow: jump to the edge of contiguous data, Excel-style —
|
|
22
|
+
* - if the adjacent cell is empty, advance through empty cells and land on
|
|
23
|
+
* the first non-empty cell found (or the grid edge if there is none)
|
|
24
|
+
* - if the adjacent cell is non-empty, advance while cells stay non-empty
|
|
25
|
+
* and land on the last non-empty cell before a gap (or the grid edge)
|
|
26
|
+
*/
|
|
27
|
+
export function moveFocus(current, key, bounds, options = {}) {
|
|
28
|
+
const { dRow, dCol } = DIRECTION[key];
|
|
29
|
+
const isEmpty = options.isEmpty ?? (() => false);
|
|
30
|
+
if (!options.ctrlOrCmd) {
|
|
31
|
+
return clampPosition({ row: current.row + dRow, col: current.col + dCol }, bounds);
|
|
32
|
+
}
|
|
33
|
+
const next = { row: current.row + dRow, col: current.col + dCol };
|
|
34
|
+
if (!inBounds(next, bounds))
|
|
35
|
+
return current;
|
|
36
|
+
if (isEmpty(next)) {
|
|
37
|
+
let pos = next;
|
|
38
|
+
let forward = { row: pos.row + dRow, col: pos.col + dCol };
|
|
39
|
+
while (inBounds(forward, bounds) && isEmpty(forward)) {
|
|
40
|
+
pos = forward;
|
|
41
|
+
forward = { row: pos.row + dRow, col: pos.col + dCol };
|
|
42
|
+
}
|
|
43
|
+
if (inBounds(forward, bounds) && !isEmpty(forward)) {
|
|
44
|
+
pos = forward;
|
|
45
|
+
}
|
|
46
|
+
return pos;
|
|
47
|
+
}
|
|
48
|
+
let pos = next;
|
|
49
|
+
for (;;) {
|
|
50
|
+
const forward = { row: pos.row + dRow, col: pos.col + dCol };
|
|
51
|
+
if (!inBounds(forward, bounds) || isEmpty(forward))
|
|
52
|
+
break;
|
|
53
|
+
pos = forward;
|
|
54
|
+
}
|
|
55
|
+
return pos;
|
|
56
|
+
}
|
|
57
|
+
/** Tab moves right and wraps to the next row's first column; Shift+Tab
|
|
58
|
+
* moves left and wraps to the previous row's last column. */
|
|
59
|
+
export function moveTab(current, bounds, shift) {
|
|
60
|
+
if (!shift) {
|
|
61
|
+
if (current.col < bounds.maxCol)
|
|
62
|
+
return { row: current.row, col: current.col + 1 };
|
|
63
|
+
if (current.row < bounds.maxRow)
|
|
64
|
+
return { row: current.row + 1, col: bounds.minCol };
|
|
65
|
+
return current;
|
|
66
|
+
}
|
|
67
|
+
if (current.col > bounds.minCol)
|
|
68
|
+
return { row: current.row, col: current.col - 1 };
|
|
69
|
+
if (current.row > bounds.minRow)
|
|
70
|
+
return { row: current.row - 1, col: bounds.maxCol };
|
|
71
|
+
return current;
|
|
72
|
+
}
|
|
73
|
+
/** Enter moves down a row (same column); Shift+Enter moves up. */
|
|
74
|
+
export function moveEnter(current, bounds, shift) {
|
|
75
|
+
const delta = shift ? -1 : 1;
|
|
76
|
+
return clampPosition({ row: current.row + delta, col: current.col }, bounds);
|
|
77
|
+
}
|
|
78
|
+
/** True for a single, non-modified printable character — the trigger for
|
|
79
|
+
* "typing over a focused cell starts editing" (Excel/Sheets semantics). */
|
|
80
|
+
export function isPrintableEditTrigger(e) {
|
|
81
|
+
if (e.ctrlKey || e.metaKey || e.altKey)
|
|
82
|
+
return false;
|
|
83
|
+
return e.key.length === 1;
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=keyboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyboard.js","sourceRoot":"","sources":["../../src/lib/keyboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell-range selection maths — pure, no DOM/React. A selection is tracked
|
|
3
|
+
* as an anchor (where the drag/shift-click started) + a focus (where the
|
|
4
|
+
* pointer/keyboard currently is); this module normalizes that pair into an
|
|
5
|
+
* ordered rectangle and answers containment/enumeration questions over it.
|
|
6
|
+
*/
|
|
7
|
+
export interface CellPosition {
|
|
8
|
+
row: number;
|
|
9
|
+
col: number;
|
|
10
|
+
}
|
|
11
|
+
export interface CellRange {
|
|
12
|
+
startRow: number;
|
|
13
|
+
endRow: number;
|
|
14
|
+
startCol: number;
|
|
15
|
+
endCol: number;
|
|
16
|
+
}
|
|
17
|
+
export declare function normalizeRange(anchor: CellPosition, focus: CellPosition): CellRange;
|
|
18
|
+
export declare function clampRange(range: CellRange, maxRow: number, maxCol: number): CellRange;
|
|
19
|
+
export declare function isCellInRange(pos: CellPosition, range: CellRange): boolean;
|
|
20
|
+
export declare function rangeRowCount(range: CellRange): number;
|
|
21
|
+
export declare function rangeColCount(range: CellRange): number;
|
|
22
|
+
export declare function rangeToCells(range: CellRange): CellPosition[];
|
|
23
|
+
export declare function rangesEqual(a: CellRange, b: CellRange): boolean;
|
|
24
|
+
export declare function singleCellRange(pos: CellPosition): CellRange;
|
|
25
|
+
export declare function isSingleCell(range: CellRange): boolean;
|
|
26
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../src/lib/selection.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,GAAG,SAAS,CAOnF;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,CAQtF;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,YAAY,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAO1E;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAEtD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,YAAY,EAAE,CAQ7D;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAO/D;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,YAAY,GAAG,SAAS,CAE5D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,OAAO,CAEtD"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cell-range selection maths — pure, no DOM/React. A selection is tracked
|
|
3
|
+
* as an anchor (where the drag/shift-click started) + a focus (where the
|
|
4
|
+
* pointer/keyboard currently is); this module normalizes that pair into an
|
|
5
|
+
* ordered rectangle and answers containment/enumeration questions over it.
|
|
6
|
+
*/
|
|
7
|
+
export function normalizeRange(anchor, focus) {
|
|
8
|
+
return {
|
|
9
|
+
startRow: Math.min(anchor.row, focus.row),
|
|
10
|
+
endRow: Math.max(anchor.row, focus.row),
|
|
11
|
+
startCol: Math.min(anchor.col, focus.col),
|
|
12
|
+
endCol: Math.max(anchor.col, focus.col),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function clampRange(range, maxRow, maxCol) {
|
|
16
|
+
const clamp = (v, max) => Math.max(0, Math.min(v, max));
|
|
17
|
+
return {
|
|
18
|
+
startRow: clamp(range.startRow, maxRow),
|
|
19
|
+
endRow: clamp(range.endRow, maxRow),
|
|
20
|
+
startCol: clamp(range.startCol, maxCol),
|
|
21
|
+
endCol: clamp(range.endCol, maxCol),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function isCellInRange(pos, range) {
|
|
25
|
+
return (pos.row >= range.startRow &&
|
|
26
|
+
pos.row <= range.endRow &&
|
|
27
|
+
pos.col >= range.startCol &&
|
|
28
|
+
pos.col <= range.endCol);
|
|
29
|
+
}
|
|
30
|
+
export function rangeRowCount(range) {
|
|
31
|
+
return range.endRow - range.startRow + 1;
|
|
32
|
+
}
|
|
33
|
+
export function rangeColCount(range) {
|
|
34
|
+
return range.endCol - range.startCol + 1;
|
|
35
|
+
}
|
|
36
|
+
export function rangeToCells(range) {
|
|
37
|
+
const cells = [];
|
|
38
|
+
for (let r = range.startRow; r <= range.endRow; r += 1) {
|
|
39
|
+
for (let c = range.startCol; c <= range.endCol; c += 1) {
|
|
40
|
+
cells.push({ row: r, col: c });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return cells;
|
|
44
|
+
}
|
|
45
|
+
export function rangesEqual(a, b) {
|
|
46
|
+
return (a.startRow === b.startRow &&
|
|
47
|
+
a.endRow === b.endRow &&
|
|
48
|
+
a.startCol === b.startCol &&
|
|
49
|
+
a.endCol === b.endCol);
|
|
50
|
+
}
|
|
51
|
+
export function singleCellRange(pos) {
|
|
52
|
+
return { startRow: pos.row, endRow: pos.row, startCol: pos.col, endCol: pos.col };
|
|
53
|
+
}
|
|
54
|
+
export function isSingleCell(range) {
|
|
55
|
+
return range.startRow === range.endRow && range.startCol === range.endCol;
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../src/lib/selection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
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 declare function needsQuoting(value: string): boolean;
|
|
22
|
+
export declare function quoteCell(value: string): string;
|
|
23
|
+
export declare function serializeTsv(rows: readonly (readonly string[])[]): string;
|
|
24
|
+
/**
|
|
25
|
+
* Hand-rolled state machine rather than a split() — a naive `text.split('\n')`
|
|
26
|
+
* followed by `line.split('\t')` corrupts any cell containing an embedded
|
|
27
|
+
* tab or newline inside quotes, which is exactly the case #1 requirement
|
|
28
|
+
* calls out ("values containing tabs/newlines/quotes").
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseTsv(text: string): string[][];
|
|
31
|
+
//# sourceMappingURL=tsv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tsv.d.ts","sourceRoot":"","sources":["../../src/lib/tsv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,CAAC,SAAS,MAAM,EAAE,CAAC,EAAE,GAAG,MAAM,CAEzE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,CAiFjD"}
|