@pagent-libs/core 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/README.md +32 -0
- package/dist/canvas/cell-renderer.d.ts +45 -0
- package/dist/canvas/cell-renderer.d.ts.map +1 -0
- package/dist/canvas/grid-renderer.d.ts +29 -0
- package/dist/canvas/grid-renderer.d.ts.map +1 -0
- package/dist/canvas/header-renderer.d.ts +58 -0
- package/dist/canvas/header-renderer.d.ts.map +1 -0
- package/dist/canvas/hit-testing.d.ts +81 -0
- package/dist/canvas/hit-testing.d.ts.map +1 -0
- package/dist/canvas/index.d.ts +9 -0
- package/dist/canvas/index.d.ts.map +1 -0
- package/dist/canvas/renderer.d.ts +140 -0
- package/dist/canvas/renderer.d.ts.map +1 -0
- package/dist/canvas/selection-renderer.d.ts +55 -0
- package/dist/canvas/selection-renderer.d.ts.map +1 -0
- package/dist/canvas/text-renderer.d.ts +49 -0
- package/dist/canvas/text-renderer.d.ts.map +1 -0
- package/dist/canvas/types.d.ts +200 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/collaboration/firebase-provider.d.ts +13 -0
- package/dist/collaboration/firebase-provider.d.ts.map +1 -0
- package/dist/collaboration/index.d.ts +3 -0
- package/dist/collaboration/index.d.ts.map +1 -0
- package/dist/collaboration/types.d.ts +34 -0
- package/dist/collaboration/types.d.ts.map +1 -0
- package/dist/event-emitter.d.ts +13 -0
- package/dist/event-emitter.d.ts.map +1 -0
- package/dist/export/csv.d.ts +5 -0
- package/dist/export/csv.d.ts.map +1 -0
- package/dist/export/index.d.ts +2 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/features/filter.d.ts +58 -0
- package/dist/features/filter.d.ts.map +1 -0
- package/dist/features/freeze.d.ts +86 -0
- package/dist/features/freeze.d.ts.map +1 -0
- package/dist/features/index.d.ts +4 -0
- package/dist/features/index.d.ts.map +1 -0
- package/dist/features/sort.d.ts +15 -0
- package/dist/features/sort.d.ts.map +1 -0
- package/dist/format-pool.d.ts +17 -0
- package/dist/format-pool.d.ts.map +1 -0
- package/dist/formula-graph.d.ts +12 -0
- package/dist/formula-graph.d.ts.map +1 -0
- package/dist/formula-parser/cell-reference.d.ts +7 -0
- package/dist/formula-parser/cell-reference.d.ts.map +1 -0
- package/dist/formula-parser/formula-adjust.d.ts +13 -0
- package/dist/formula-parser/formula-adjust.d.ts.map +1 -0
- package/dist/formula-parser/formula-ranges.d.ts +22 -0
- package/dist/formula-parser/formula-ranges.d.ts.map +1 -0
- package/dist/formula-parser/index.d.ts +6 -0
- package/dist/formula-parser/index.d.ts.map +1 -0
- package/dist/formula-parser/parser.d.ts +18 -0
- package/dist/formula-parser/parser.d.ts.map +1 -0
- package/dist/formula-parser/types.d.ts +33 -0
- package/dist/formula-parser/types.d.ts.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.esm.js +5823 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/index.js +5885 -0
- package/dist/index.js.map +1 -0
- package/dist/sheet.d.ts +119 -0
- package/dist/sheet.d.ts.map +1 -0
- package/dist/style-pool.d.ts +17 -0
- package/dist/style-pool.d.ts.map +1 -0
- package/dist/types.d.ts +260 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/utils/cell-key.d.ts +7 -0
- package/dist/utils/cell-key.d.ts.map +1 -0
- package/dist/utils/format-utils.d.ts +75 -0
- package/dist/utils/format-utils.d.ts.map +1 -0
- package/dist/utils/range.d.ts +13 -0
- package/dist/utils/range.d.ts.map +1 -0
- package/dist/workbook.d.ts +155 -0
- package/dist/workbook.d.ts.map +1 -0
- package/package.json +46 -0
- package/src/canvas/cell-renderer.ts +181 -0
- package/src/canvas/grid-renderer.ts +238 -0
- package/src/canvas/header-renderer.ts +402 -0
- package/src/canvas/hit-testing.ts +537 -0
- package/src/canvas/index.ts +16 -0
- package/src/canvas/renderer.ts +1056 -0
- package/src/canvas/selection-renderer.ts +604 -0
- package/src/canvas/text-renderer.ts +321 -0
- package/src/canvas/types.ts +289 -0
- package/src/collaboration/firebase-provider.ts +48 -0
- package/src/collaboration/index.ts +5 -0
- package/src/collaboration/types.ts +38 -0
- package/src/event-emitter.ts +73 -0
- package/src/export/csv.ts +101 -0
- package/src/export/index.ts +4 -0
- package/src/features/filter.ts +231 -0
- package/src/features/freeze.ts +271 -0
- package/src/features/index.ts +5 -0
- package/src/features/sort.ts +282 -0
- package/src/format-pool.ts +61 -0
- package/src/formula-graph.ts +84 -0
- package/src/formula-parser/cell-reference.ts +99 -0
- package/src/formula-parser/formula-adjust.ts +129 -0
- package/src/formula-parser/formula-ranges.ts +159 -0
- package/src/formula-parser/index.ts +8 -0
- package/src/formula-parser/parser.ts +438 -0
- package/src/formula-parser/types.ts +39 -0
- package/src/index.ts +25 -0
- package/src/sheet.ts +502 -0
- package/src/style-pool.ts +62 -0
- package/src/types.ts +291 -0
- package/src/utils/cell-key.ts +19 -0
- package/src/utils/format-utils.ts +515 -0
- package/src/utils/range.ts +53 -0
- package/src/workbook.ts +1031 -0
package/src/sheet.ts
ADDED
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
// Sheet model with sparse cell storage
|
|
2
|
+
|
|
3
|
+
import type { Sheet, SheetConfig, Cell, Range, SortOrder } from './types';
|
|
4
|
+
import { getCellKey, parseCellKey } from './utils/cell-key';
|
|
5
|
+
import { getRangeCells } from './utils/range';
|
|
6
|
+
|
|
7
|
+
export class SheetImpl implements Sheet {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
cells: Map<string, Cell> = new Map();
|
|
11
|
+
config: SheetConfig;
|
|
12
|
+
rowCount: number;
|
|
13
|
+
colCount: number;
|
|
14
|
+
|
|
15
|
+
constructor(id: string, name: string, config: Partial<SheetConfig> = {}) {
|
|
16
|
+
this.id = id;
|
|
17
|
+
this.name = name;
|
|
18
|
+
this.config = {
|
|
19
|
+
defaultRowHeight: 20,
|
|
20
|
+
defaultColWidth: 100,
|
|
21
|
+
showGridLines: true,
|
|
22
|
+
...config,
|
|
23
|
+
};
|
|
24
|
+
this.rowCount = 1000;
|
|
25
|
+
this.colCount = 100;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
getCell(row: number, col: number): Cell | undefined {
|
|
29
|
+
const key = getCellKey(row, col);
|
|
30
|
+
return this.cells.get(key);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
setCell(row: number, col: number, cell: Partial<Cell>): void {
|
|
34
|
+
const key = getCellKey(row, col);
|
|
35
|
+
const existing = this.cells.get(key);
|
|
36
|
+
|
|
37
|
+
if (existing) {
|
|
38
|
+
// Merge with existing cell
|
|
39
|
+
this.cells.set(key, { ...existing, ...cell });
|
|
40
|
+
} else {
|
|
41
|
+
// Create new cell
|
|
42
|
+
this.cells.set(key, {
|
|
43
|
+
value: cell.value ?? null,
|
|
44
|
+
...cell,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
deleteCell(row: number, col: number): void {
|
|
51
|
+
const key = getCellKey(row, col);
|
|
52
|
+
this.cells.delete(key);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
getCellValue(row: number, col: number): unknown {
|
|
56
|
+
const cell = this.getCell(row, col);
|
|
57
|
+
return cell?.value ?? null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
setCellValue(row: number, col: number, value: unknown): void {
|
|
61
|
+
this.setCell(row, col, { value: value as string | number | boolean | null });
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
getRange(range: Range): Map<string, Cell> {
|
|
65
|
+
const result = new Map<string, Cell>();
|
|
66
|
+
const cells = getRangeCells(range);
|
|
67
|
+
|
|
68
|
+
for (const { row, col } of cells) {
|
|
69
|
+
const cell = this.getCell(row, col);
|
|
70
|
+
if (cell) {
|
|
71
|
+
result.set(getCellKey(row, col), cell);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setRange(range: Range, cells: Map<string, Cell> | Cell[][]): void {
|
|
79
|
+
if (cells instanceof Map) {
|
|
80
|
+
// Map of cellKey -> Cell
|
|
81
|
+
for (const [key, cell] of cells) {
|
|
82
|
+
const { row, col } = parseCellKey(key);
|
|
83
|
+
this.setCell(row, col, cell);
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
// 2D array
|
|
87
|
+
const rangeCells = getRangeCells(range);
|
|
88
|
+
for (let i = 0; i < rangeCells.length && i < cells.flat().length; i++) {
|
|
89
|
+
const { row, col } = rangeCells[i];
|
|
90
|
+
const cell = cells.flat()[i];
|
|
91
|
+
if (cell) {
|
|
92
|
+
this.setCell(row, col, cell);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
clearRange(range: Range): void {
|
|
99
|
+
const cells = getRangeCells(range);
|
|
100
|
+
for (const { row, col } of cells) {
|
|
101
|
+
this.deleteCell(row, col);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
getRowHeight(row: number): number {
|
|
106
|
+
return this.config.rowHeights?.get(row) ?? this.config.defaultRowHeight ?? 20;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
setRowHeight(row: number, height: number): void {
|
|
110
|
+
if (!this.config.rowHeights) {
|
|
111
|
+
this.config.rowHeights = new Map();
|
|
112
|
+
}
|
|
113
|
+
this.config.rowHeights.set(row, height);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
getColWidth(col: number): number {
|
|
117
|
+
return this.config.colWidths?.get(col) ?? this.config.defaultColWidth ?? 100;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
setColWidth(col: number, width: number): void {
|
|
121
|
+
if (!this.config.colWidths) {
|
|
122
|
+
this.config.colWidths = new Map();
|
|
123
|
+
}
|
|
124
|
+
this.config.colWidths.set(col, width);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
insertRows(startRow: number, count: number): void {
|
|
128
|
+
// Shift existing cells down
|
|
129
|
+
const cellsToMove: Array<{ key: string; cell: Cell; newRow: number }> = [];
|
|
130
|
+
|
|
131
|
+
for (const [key, cell] of this.cells) {
|
|
132
|
+
const { row } = parseCellKey(key);
|
|
133
|
+
if (row >= startRow) {
|
|
134
|
+
cellsToMove.push({ key, cell, newRow: row + count });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Remove old cells
|
|
139
|
+
for (const { key } of cellsToMove) {
|
|
140
|
+
this.cells.delete(key);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Add cells at new positions
|
|
144
|
+
for (const { cell, newRow, key } of cellsToMove) {
|
|
145
|
+
const { col } = parseCellKey(key);
|
|
146
|
+
this.setCell(newRow, col, cell);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
this.rowCount += count;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
deleteRows(startRow: number, count: number): void {
|
|
153
|
+
// Delete cells in range
|
|
154
|
+
for (let row = startRow; row < startRow + count; row++) {
|
|
155
|
+
for (let col = 0; col < this.colCount; col++) {
|
|
156
|
+
this.deleteCell(row, col);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Shift cells up
|
|
161
|
+
const cellsToMove: Array<{ key: string; cell: Cell; newRow: number }> = [];
|
|
162
|
+
|
|
163
|
+
for (const [key, cell] of this.cells) {
|
|
164
|
+
const { row } = parseCellKey(key);
|
|
165
|
+
if (row > startRow + count - 1) {
|
|
166
|
+
cellsToMove.push({ key, cell, newRow: row - count });
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Remove old cells
|
|
171
|
+
for (const { key } of cellsToMove) {
|
|
172
|
+
this.cells.delete(key);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Add cells at new positions
|
|
176
|
+
for (const { cell, newRow, key } of cellsToMove) {
|
|
177
|
+
const { col } = parseCellKey(key);
|
|
178
|
+
this.setCell(newRow, col, cell);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
this.rowCount = Math.max(0, this.rowCount - count);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
insertCols(startCol: number, count: number): void {
|
|
185
|
+
// Shift existing cells right
|
|
186
|
+
const cellsToMove: Array<{ key: string; cell: Cell; newCol: number }> = [];
|
|
187
|
+
|
|
188
|
+
for (const [key, cell] of this.cells) {
|
|
189
|
+
const { col } = parseCellKey(key);
|
|
190
|
+
if (col >= startCol) {
|
|
191
|
+
cellsToMove.push({ key, cell, newCol: col + count });
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Remove old cells
|
|
196
|
+
for (const { key } of cellsToMove) {
|
|
197
|
+
this.cells.delete(key);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Add cells at new positions
|
|
201
|
+
for (const { cell, newCol, key } of cellsToMove) {
|
|
202
|
+
const { row } = parseCellKey(key);
|
|
203
|
+
this.setCell(row, newCol, cell);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.colCount += count;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
deleteCols(startCol: number, count: number): void {
|
|
210
|
+
// Delete cells in range
|
|
211
|
+
for (let col = startCol; col < startCol + count; col++) {
|
|
212
|
+
for (let row = 0; row < this.rowCount; row++) {
|
|
213
|
+
this.deleteCell(row, col);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Shift cells left
|
|
218
|
+
const cellsToMove: Array<{ key: string; cell: Cell; newCol: number }> = [];
|
|
219
|
+
|
|
220
|
+
for (const [key, cell] of this.cells) {
|
|
221
|
+
const { col } = parseCellKey(key);
|
|
222
|
+
if (col > startCol + count - 1) {
|
|
223
|
+
cellsToMove.push({ key, cell, newCol: col - count });
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Remove old cells
|
|
228
|
+
for (const { key } of cellsToMove) {
|
|
229
|
+
this.cells.delete(key);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
// Add cells at new positions
|
|
233
|
+
for (const { cell, newCol, key } of cellsToMove) {
|
|
234
|
+
const { row } = parseCellKey(key);
|
|
235
|
+
this.setCell(row, newCol, cell);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
this.colCount = Math.max(0, this.colCount - count);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
isRowHidden(row: number): boolean {
|
|
242
|
+
return this.config.hiddenRows?.has(row) ?? false;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
hideRow(row: number): void {
|
|
246
|
+
if (!this.config.hiddenRows) {
|
|
247
|
+
this.config.hiddenRows = new Set();
|
|
248
|
+
}
|
|
249
|
+
this.config.hiddenRows.add(row);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
showRow(row: number): void {
|
|
253
|
+
this.config.hiddenRows?.delete(row);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
isColHidden(col: number): boolean {
|
|
257
|
+
return this.config.hiddenCols?.has(col) ?? false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
hideCol(col: number): void {
|
|
261
|
+
if (!this.config.hiddenCols) {
|
|
262
|
+
this.config.hiddenCols = new Set();
|
|
263
|
+
}
|
|
264
|
+
this.config.hiddenCols.add(col);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
showCol(col: number): void {
|
|
268
|
+
this.config.hiddenCols?.delete(col);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Find hidden columns adjacent to a given column (before and after)
|
|
272
|
+
getHiddenColsAdjacent(col: number): { before: number[]; after: number[] } {
|
|
273
|
+
const hiddenCols = this.config.hiddenCols;
|
|
274
|
+
if (!hiddenCols || hiddenCols.size === 0) {
|
|
275
|
+
return { before: [], after: [] };
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const before: number[] = [];
|
|
279
|
+
const after: number[] = [];
|
|
280
|
+
|
|
281
|
+
for (let c = col - 1; c >= 0; c--) {
|
|
282
|
+
if (hiddenCols.has(c)) {
|
|
283
|
+
before.push(c);
|
|
284
|
+
} else {
|
|
285
|
+
break; // Stop at first visible column
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
for (let c = col + 1; c < this.colCount; c++) {
|
|
290
|
+
if (hiddenCols.has(c)) {
|
|
291
|
+
after.push(c);
|
|
292
|
+
} else {
|
|
293
|
+
break; // Stop at first visible column
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return { before, after };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Find hidden rows adjacent to a given row (above and below)
|
|
301
|
+
getHiddenRowsAdjacent(row: number): { above: number[]; below: number[] } {
|
|
302
|
+
const hiddenRows = this.config.hiddenRows;
|
|
303
|
+
if (!hiddenRows || hiddenRows.size === 0) {
|
|
304
|
+
return { above: [], below: [] };
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const above: number[] = [];
|
|
308
|
+
const below: number[] = [];
|
|
309
|
+
|
|
310
|
+
for (let r = row - 1; r >= 0; r--) {
|
|
311
|
+
if (hiddenRows.has(r)) {
|
|
312
|
+
above.push(r);
|
|
313
|
+
} else {
|
|
314
|
+
break; // Stop at first visible row
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
for (let r = row + 1; r < this.rowCount; r++) {
|
|
319
|
+
if (hiddenRows.has(r)) {
|
|
320
|
+
below.push(r);
|
|
321
|
+
} else {
|
|
322
|
+
break; // Stop at first visible row
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return { above, below };
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Show all hidden columns in a range
|
|
330
|
+
showColsInRange(startCol: number, endCol: number): void {
|
|
331
|
+
if (!this.config.hiddenCols) return;
|
|
332
|
+
const minCol = Math.min(startCol, endCol);
|
|
333
|
+
const maxCol = Math.max(startCol, endCol);
|
|
334
|
+
for (let c = minCol; c <= maxCol; c++) {
|
|
335
|
+
this.config.hiddenCols.delete(c);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// Show all hidden rows in a range
|
|
340
|
+
showRowsInRange(startRow: number, endRow: number): void {
|
|
341
|
+
if (!this.config.hiddenRows) return;
|
|
342
|
+
const minRow = Math.min(startRow, endRow);
|
|
343
|
+
const maxRow = Math.max(startRow, endRow);
|
|
344
|
+
for (let r = minRow; r <= maxRow; r++) {
|
|
345
|
+
this.config.hiddenRows.delete(r);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// ============================================
|
|
350
|
+
// Freeze Panes Methods
|
|
351
|
+
// ============================================
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get the number of frozen rows
|
|
355
|
+
*/
|
|
356
|
+
getFrozenRows(): number {
|
|
357
|
+
return this.config.frozenRows ?? 0;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Set the number of frozen rows
|
|
362
|
+
* @param count Number of rows to freeze (0 to unfreeze rows)
|
|
363
|
+
*/
|
|
364
|
+
setFrozenRows(count: number): void {
|
|
365
|
+
if (count < 0) {
|
|
366
|
+
throw new Error('Frozen row count cannot be negative');
|
|
367
|
+
}
|
|
368
|
+
if (count >= this.rowCount) {
|
|
369
|
+
throw new Error('Cannot freeze all rows');
|
|
370
|
+
}
|
|
371
|
+
this.config.frozenRows = count > 0 ? count : undefined;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
/**
|
|
375
|
+
* Get the number of frozen columns
|
|
376
|
+
*/
|
|
377
|
+
getFrozenCols(): number {
|
|
378
|
+
return this.config.frozenCols ?? 0;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Set the number of frozen columns
|
|
383
|
+
* @param count Number of columns to freeze (0 to unfreeze columns)
|
|
384
|
+
*/
|
|
385
|
+
setFrozenCols(count: number): void {
|
|
386
|
+
if (count < 0) {
|
|
387
|
+
throw new Error('Frozen column count cannot be negative');
|
|
388
|
+
}
|
|
389
|
+
if (count >= this.colCount) {
|
|
390
|
+
throw new Error('Cannot freeze all columns');
|
|
391
|
+
}
|
|
392
|
+
this.config.frozenCols = count > 0 ? count : undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* Freeze rows and columns at the specified position
|
|
397
|
+
* @param rows Number of rows to freeze
|
|
398
|
+
* @param cols Number of columns to freeze
|
|
399
|
+
*/
|
|
400
|
+
setFreeze(rows: number, cols: number): void {
|
|
401
|
+
this.setFrozenRows(rows);
|
|
402
|
+
this.setFrozenCols(cols);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Clear all freeze panes (unfreeze both rows and columns)
|
|
407
|
+
*/
|
|
408
|
+
clearFreeze(): void {
|
|
409
|
+
this.config.frozenRows = undefined;
|
|
410
|
+
this.config.frozenCols = undefined;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Check if freeze panes are active
|
|
415
|
+
*/
|
|
416
|
+
hasFrozenPanes(): boolean {
|
|
417
|
+
return (this.config.frozenRows ?? 0) > 0 || (this.config.frozenCols ?? 0) > 0;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ============================================
|
|
421
|
+
// Sorting Methods
|
|
422
|
+
// ============================================
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Set the sort order for the sheet
|
|
426
|
+
* @param sortOrder Array of sort criteria
|
|
427
|
+
*/
|
|
428
|
+
setSortOrder(sortOrder: SortOrder[]): void {
|
|
429
|
+
this.config.sortOrder = [...sortOrder];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get the current sort order
|
|
434
|
+
* @returns Current sort order array
|
|
435
|
+
*/
|
|
436
|
+
getSortOrder(): SortOrder[] {
|
|
437
|
+
return this.config.sortOrder ?? [];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Clear all sorting
|
|
442
|
+
*/
|
|
443
|
+
clearSort(): void {
|
|
444
|
+
this.config.sortOrder = undefined;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Check if the sheet has any sorting applied
|
|
449
|
+
*/
|
|
450
|
+
hasSort(): boolean {
|
|
451
|
+
return (this.config.sortOrder?.length ?? 0) > 0;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
// ============================================
|
|
455
|
+
// Filtering Methods
|
|
456
|
+
// ============================================
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Set a filter for a column
|
|
460
|
+
* @param column Column index
|
|
461
|
+
* @param filter Filter configuration
|
|
462
|
+
*/
|
|
463
|
+
setFilter(column: number, filter: import('./types').ColumnFilter): void {
|
|
464
|
+
if (!this.config.filters) {
|
|
465
|
+
this.config.filters = new Map();
|
|
466
|
+
}
|
|
467
|
+
this.config.filters.set(column, filter);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Clear filter for a specific column
|
|
472
|
+
* @param column Column index
|
|
473
|
+
*/
|
|
474
|
+
clearFilter(column: number): void {
|
|
475
|
+
this.config.filters?.delete(column);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/**
|
|
479
|
+
* Get all active filters
|
|
480
|
+
* @returns Map of column -> filter
|
|
481
|
+
*/
|
|
482
|
+
getFilters(): Map<number, import('./types').ColumnFilter> {
|
|
483
|
+
return this.config.filters ?? new Map();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* Check if a column has an active filter
|
|
488
|
+
* @param column Column index
|
|
489
|
+
* @returns true if column has a filter
|
|
490
|
+
*/
|
|
491
|
+
hasFilter(column: number): boolean {
|
|
492
|
+
return this.config.filters?.has(column) ?? false;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Clear all filters from the sheet
|
|
497
|
+
*/
|
|
498
|
+
clearAllFilters(): void {
|
|
499
|
+
this.config.filters = undefined;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Style pool for shared style objects
|
|
2
|
+
|
|
3
|
+
import type { CellStyle } from './types';
|
|
4
|
+
|
|
5
|
+
export class StylePool {
|
|
6
|
+
private styles: Map<string, CellStyle> = new Map();
|
|
7
|
+
private styleToId: Map<string, string> = new Map();
|
|
8
|
+
private nextId = 1;
|
|
9
|
+
|
|
10
|
+
getOrCreate(style: CellStyle): string {
|
|
11
|
+
const styleKey = this.getStyleKey(style);
|
|
12
|
+
const existingId = this.styleToId.get(styleKey);
|
|
13
|
+
if (existingId) {
|
|
14
|
+
return existingId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const id = `style_${this.nextId++}`;
|
|
18
|
+
this.styles.set(id, style);
|
|
19
|
+
this.styleToId.set(styleKey, id);
|
|
20
|
+
return id;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
get(styleId: string): CellStyle | undefined {
|
|
24
|
+
return this.styles.get(styleId);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
getStyleKey(style: CellStyle): string {
|
|
28
|
+
// Create a deterministic key from style properties
|
|
29
|
+
const keys = Object.keys(style).sort();
|
|
30
|
+
return keys.map((key) => `${key}:${style[key as keyof CellStyle]}`).join('|');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
clear(): void {
|
|
34
|
+
this.styles.clear();
|
|
35
|
+
this.styleToId.clear();
|
|
36
|
+
this.nextId = 1;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
size(): number {
|
|
40
|
+
return this.styles.size;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getAllStyles(): Map<string, CellStyle> {
|
|
44
|
+
return new Map(this.styles);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
setStyleToId(styleToId: Map<string, string>): void {
|
|
48
|
+
this.styleToId = styleToId;
|
|
49
|
+
}
|
|
50
|
+
setStyles(styles: Map<string, CellStyle>): void {
|
|
51
|
+
this.styles = styles;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
getNextId(): number {
|
|
55
|
+
return this.nextId;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
setNextId(nextId: number): void {
|
|
59
|
+
this.nextId = nextId;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|