@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.
Files changed (111) hide show
  1. package/README.md +32 -0
  2. package/dist/canvas/cell-renderer.d.ts +45 -0
  3. package/dist/canvas/cell-renderer.d.ts.map +1 -0
  4. package/dist/canvas/grid-renderer.d.ts +29 -0
  5. package/dist/canvas/grid-renderer.d.ts.map +1 -0
  6. package/dist/canvas/header-renderer.d.ts +58 -0
  7. package/dist/canvas/header-renderer.d.ts.map +1 -0
  8. package/dist/canvas/hit-testing.d.ts +81 -0
  9. package/dist/canvas/hit-testing.d.ts.map +1 -0
  10. package/dist/canvas/index.d.ts +9 -0
  11. package/dist/canvas/index.d.ts.map +1 -0
  12. package/dist/canvas/renderer.d.ts +140 -0
  13. package/dist/canvas/renderer.d.ts.map +1 -0
  14. package/dist/canvas/selection-renderer.d.ts +55 -0
  15. package/dist/canvas/selection-renderer.d.ts.map +1 -0
  16. package/dist/canvas/text-renderer.d.ts +49 -0
  17. package/dist/canvas/text-renderer.d.ts.map +1 -0
  18. package/dist/canvas/types.d.ts +200 -0
  19. package/dist/canvas/types.d.ts.map +1 -0
  20. package/dist/collaboration/firebase-provider.d.ts +13 -0
  21. package/dist/collaboration/firebase-provider.d.ts.map +1 -0
  22. package/dist/collaboration/index.d.ts +3 -0
  23. package/dist/collaboration/index.d.ts.map +1 -0
  24. package/dist/collaboration/types.d.ts +34 -0
  25. package/dist/collaboration/types.d.ts.map +1 -0
  26. package/dist/event-emitter.d.ts +13 -0
  27. package/dist/event-emitter.d.ts.map +1 -0
  28. package/dist/export/csv.d.ts +5 -0
  29. package/dist/export/csv.d.ts.map +1 -0
  30. package/dist/export/index.d.ts +2 -0
  31. package/dist/export/index.d.ts.map +1 -0
  32. package/dist/features/filter.d.ts +58 -0
  33. package/dist/features/filter.d.ts.map +1 -0
  34. package/dist/features/freeze.d.ts +86 -0
  35. package/dist/features/freeze.d.ts.map +1 -0
  36. package/dist/features/index.d.ts +4 -0
  37. package/dist/features/index.d.ts.map +1 -0
  38. package/dist/features/sort.d.ts +15 -0
  39. package/dist/features/sort.d.ts.map +1 -0
  40. package/dist/format-pool.d.ts +17 -0
  41. package/dist/format-pool.d.ts.map +1 -0
  42. package/dist/formula-graph.d.ts +12 -0
  43. package/dist/formula-graph.d.ts.map +1 -0
  44. package/dist/formula-parser/cell-reference.d.ts +7 -0
  45. package/dist/formula-parser/cell-reference.d.ts.map +1 -0
  46. package/dist/formula-parser/formula-adjust.d.ts +13 -0
  47. package/dist/formula-parser/formula-adjust.d.ts.map +1 -0
  48. package/dist/formula-parser/formula-ranges.d.ts +22 -0
  49. package/dist/formula-parser/formula-ranges.d.ts.map +1 -0
  50. package/dist/formula-parser/index.d.ts +6 -0
  51. package/dist/formula-parser/index.d.ts.map +1 -0
  52. package/dist/formula-parser/parser.d.ts +18 -0
  53. package/dist/formula-parser/parser.d.ts.map +1 -0
  54. package/dist/formula-parser/types.d.ts +33 -0
  55. package/dist/formula-parser/types.d.ts.map +1 -0
  56. package/dist/index.d.ts +15 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.esm.js +5823 -0
  59. package/dist/index.esm.js.map +1 -0
  60. package/dist/index.js +5885 -0
  61. package/dist/index.js.map +1 -0
  62. package/dist/sheet.d.ts +119 -0
  63. package/dist/sheet.d.ts.map +1 -0
  64. package/dist/style-pool.d.ts +17 -0
  65. package/dist/style-pool.d.ts.map +1 -0
  66. package/dist/types.d.ts +260 -0
  67. package/dist/types.d.ts.map +1 -0
  68. package/dist/utils/cell-key.d.ts +7 -0
  69. package/dist/utils/cell-key.d.ts.map +1 -0
  70. package/dist/utils/format-utils.d.ts +75 -0
  71. package/dist/utils/format-utils.d.ts.map +1 -0
  72. package/dist/utils/range.d.ts +13 -0
  73. package/dist/utils/range.d.ts.map +1 -0
  74. package/dist/workbook.d.ts +155 -0
  75. package/dist/workbook.d.ts.map +1 -0
  76. package/package.json +46 -0
  77. package/src/canvas/cell-renderer.ts +181 -0
  78. package/src/canvas/grid-renderer.ts +238 -0
  79. package/src/canvas/header-renderer.ts +402 -0
  80. package/src/canvas/hit-testing.ts +537 -0
  81. package/src/canvas/index.ts +16 -0
  82. package/src/canvas/renderer.ts +1056 -0
  83. package/src/canvas/selection-renderer.ts +604 -0
  84. package/src/canvas/text-renderer.ts +321 -0
  85. package/src/canvas/types.ts +289 -0
  86. package/src/collaboration/firebase-provider.ts +48 -0
  87. package/src/collaboration/index.ts +5 -0
  88. package/src/collaboration/types.ts +38 -0
  89. package/src/event-emitter.ts +73 -0
  90. package/src/export/csv.ts +101 -0
  91. package/src/export/index.ts +4 -0
  92. package/src/features/filter.ts +231 -0
  93. package/src/features/freeze.ts +271 -0
  94. package/src/features/index.ts +5 -0
  95. package/src/features/sort.ts +282 -0
  96. package/src/format-pool.ts +61 -0
  97. package/src/formula-graph.ts +84 -0
  98. package/src/formula-parser/cell-reference.ts +99 -0
  99. package/src/formula-parser/formula-adjust.ts +129 -0
  100. package/src/formula-parser/formula-ranges.ts +159 -0
  101. package/src/formula-parser/index.ts +8 -0
  102. package/src/formula-parser/parser.ts +438 -0
  103. package/src/formula-parser/types.ts +39 -0
  104. package/src/index.ts +25 -0
  105. package/src/sheet.ts +502 -0
  106. package/src/style-pool.ts +62 -0
  107. package/src/types.ts +291 -0
  108. package/src/utils/cell-key.ts +19 -0
  109. package/src/utils/format-utils.ts +515 -0
  110. package/src/utils/range.ts +53 -0
  111. package/src/workbook.ts +1031 -0
@@ -0,0 +1,537 @@
1
+ // Hit Testing - Maps mouse coordinates to grid positions
2
+
3
+ import type { CellPosition, HeaderHit, ResizeHandle } from './types';
4
+ import type { Range } from '../types';
5
+
6
+ /**
7
+ * Resize handle hit area size (pixels from edge)
8
+ */
9
+ const RESIZE_HANDLE_SIZE = 5;
10
+
11
+ /**
12
+ * Fill handle hit area size
13
+ */
14
+ const FILL_HANDLE_HIT_SIZE = 8;
15
+
16
+ /**
17
+ * Handles hit testing for mouse interactions
18
+ */
19
+ export class HitTester {
20
+ private headerWidth: number;
21
+ private headerHeight: number;
22
+ private defaultRowHeight: number;
23
+ private defaultColWidth: number;
24
+
25
+ // Current scroll position
26
+ private scrollTop: number = 0;
27
+ private scrollLeft: number = 0;
28
+
29
+ // Dimension maps (updated from render state)
30
+ private rowHeights: Map<number, number> = new Map();
31
+ private colWidths: Map<number, number> = new Map();
32
+ private rowCount: number = 0;
33
+ private colCount: number = 0;
34
+
35
+ // Hidden rows/cols
36
+ private hiddenRows: Set<number> = new Set();
37
+ private hiddenCols: Set<number> = new Set();
38
+
39
+ // Freeze panes configuration
40
+ private frozenRows: number = 0;
41
+ private frozenCols: number = 0;
42
+ private frozenWidth: number = 0;
43
+ private frozenHeight: number = 0;
44
+
45
+ constructor(
46
+ headerWidth: number,
47
+ headerHeight: number,
48
+ defaultRowHeight: number,
49
+ defaultColWidth: number
50
+ ) {
51
+ this.headerWidth = headerWidth;
52
+ this.headerHeight = headerHeight;
53
+ this.defaultRowHeight = defaultRowHeight;
54
+ this.defaultColWidth = defaultColWidth;
55
+ }
56
+
57
+ /**
58
+ * Update scroll position
59
+ */
60
+ setScroll(scrollTop: number, scrollLeft: number): void {
61
+ this.scrollTop = scrollTop;
62
+ this.scrollLeft = scrollLeft;
63
+ }
64
+
65
+ /**
66
+ * Update dimension information
67
+ */
68
+ setDimensions(
69
+ rowHeights: Map<number, number>,
70
+ colWidths: Map<number, number>,
71
+ defaultRowHeight: number,
72
+ defaultColWidth: number,
73
+ rowCount: number,
74
+ colCount: number,
75
+ hiddenRows?: Set<number>,
76
+ hiddenCols?: Set<number>
77
+ ): void {
78
+ this.rowHeights = rowHeights;
79
+ this.colWidths = colWidths;
80
+ this.defaultRowHeight = defaultRowHeight;
81
+ this.defaultColWidth = defaultColWidth;
82
+ this.rowCount = rowCount;
83
+ this.colCount = colCount;
84
+ this.hiddenRows = hiddenRows ?? new Set();
85
+ this.hiddenCols = hiddenCols ?? new Set();
86
+ }
87
+
88
+ /**
89
+ * Update freeze panes configuration
90
+ */
91
+ setFreezeConfig(
92
+ frozenRows: number,
93
+ frozenCols: number,
94
+ frozenWidth: number,
95
+ frozenHeight: number
96
+ ): void {
97
+ this.frozenRows = frozenRows;
98
+ this.frozenCols = frozenCols;
99
+ this.frozenWidth = frozenWidth;
100
+ this.frozenHeight = frozenHeight;
101
+ }
102
+
103
+ /**
104
+ * Get the cell at a canvas point (accounting for freeze panes)
105
+ */
106
+ getCellAt(x: number, y: number): CellPosition | null {
107
+ // Check if point is in the cell area (not headers)
108
+ if (x <= this.headerWidth || y <= this.headerHeight) {
109
+ return null;
110
+ }
111
+
112
+ // Determine which freeze region the point is in
113
+ const inFrozenCols = x < this.headerWidth + this.frozenWidth && this.frozenCols > 0;
114
+ const inFrozenRows = y < this.headerHeight + this.frozenHeight && this.frozenRows > 0;
115
+
116
+ // Calculate effective scroll based on region
117
+ const effectiveScrollLeft = inFrozenCols ? 0 : this.scrollLeft;
118
+ const effectiveScrollTop = inFrozenRows ? 0 : this.scrollTop;
119
+
120
+ // Determine starting column and position for search
121
+ let col: number;
122
+ let accX: number;
123
+
124
+ if (inFrozenCols) {
125
+ // In frozen columns - start from column 0
126
+ col = 0;
127
+ accX = 0;
128
+ const localX = x - this.headerWidth;
129
+
130
+ while (col < this.frozenCols && col < this.colCount) {
131
+ if (this.hiddenCols.has(col)) {
132
+ col++;
133
+ continue;
134
+ }
135
+ const width = this.colWidths.get(col) ?? this.defaultColWidth;
136
+ if (accX + width > localX) {
137
+ break;
138
+ }
139
+ accX += width;
140
+ col++;
141
+ }
142
+ } else {
143
+ // In scrollable columns - start from first visible scrollable column
144
+ col = this.frozenCols;
145
+ accX = 0;
146
+ const localX = x - this.headerWidth - this.frozenWidth + effectiveScrollLeft;
147
+
148
+ while (col < this.colCount) {
149
+ if (this.hiddenCols.has(col)) {
150
+ col++;
151
+ continue;
152
+ }
153
+ const width = this.colWidths.get(col) ?? this.defaultColWidth;
154
+ if (accX + width > localX) {
155
+ break;
156
+ }
157
+ accX += width;
158
+ col++;
159
+ }
160
+ }
161
+
162
+ // Determine starting row and position for search
163
+ let row: number;
164
+ let accY: number;
165
+
166
+ if (inFrozenRows) {
167
+ // In frozen rows - start from row 0
168
+ row = 0;
169
+ accY = 0;
170
+ const localY = y - this.headerHeight;
171
+
172
+ while (row < this.frozenRows && row < this.rowCount) {
173
+ if (this.hiddenRows.has(row)) {
174
+ row++;
175
+ continue;
176
+ }
177
+ const height = this.rowHeights.get(row) ?? this.defaultRowHeight;
178
+ if (accY + height > localY) {
179
+ break;
180
+ }
181
+ accY += height;
182
+ row++;
183
+ }
184
+ } else {
185
+ // In scrollable rows - start from first visible scrollable row
186
+ row = this.frozenRows;
187
+ accY = 0;
188
+ const localY = y - this.headerHeight - this.frozenHeight + effectiveScrollTop;
189
+
190
+ while (row < this.rowCount) {
191
+ if (this.hiddenRows.has(row)) {
192
+ row++;
193
+ continue;
194
+ }
195
+ const height = this.rowHeights.get(row) ?? this.defaultRowHeight;
196
+ if (accY + height > localY) {
197
+ break;
198
+ }
199
+ accY += height;
200
+ row++;
201
+ }
202
+ }
203
+
204
+ // Check bounds and if cell is hidden
205
+ if (row >= this.rowCount || col >= this.colCount) {
206
+ return null;
207
+ }
208
+ if (this.hiddenRows.has(row) || this.hiddenCols.has(col)) {
209
+ return null;
210
+ }
211
+
212
+ return { row, col };
213
+ }
214
+
215
+ /**
216
+ * Get the header at a canvas point (accounting for freeze panes)
217
+ */
218
+ getHeaderAt(x: number, y: number): HeaderHit | null {
219
+ // Column header
220
+ if (y <= this.headerHeight && x > this.headerWidth) {
221
+ // Check if in frozen columns area
222
+ const inFrozenCols = x < this.headerWidth + this.frozenWidth && this.frozenCols > 0;
223
+
224
+ if (inFrozenCols) {
225
+ // Frozen column headers - no scroll
226
+ const localX = x - this.headerWidth;
227
+
228
+ let col = 0;
229
+ let accX = 0;
230
+ while (col < this.frozenCols && col < this.colCount) {
231
+ if (this.hiddenCols.has(col)) {
232
+ col++;
233
+ continue;
234
+ }
235
+
236
+ const width = this.colWidths.get(col) ?? this.defaultColWidth;
237
+ const nextAccX = accX + width;
238
+
239
+ // Check for resize handle
240
+ if (localX >= nextAccX - RESIZE_HANDLE_SIZE && localX <= nextAccX + RESIZE_HANDLE_SIZE) {
241
+ return { type: 'column', index: col, isResize: true };
242
+ }
243
+
244
+ if (nextAccX > localX) {
245
+ return { type: 'column', index: col, isResize: false };
246
+ }
247
+
248
+ accX = nextAccX;
249
+ col++;
250
+ }
251
+ } else {
252
+ // Scrollable column headers
253
+ const localX = x - this.headerWidth - this.frozenWidth + this.scrollLeft;
254
+
255
+ let col = this.frozenCols;
256
+ let accX = 0;
257
+ while (col < this.colCount) {
258
+ if (this.hiddenCols.has(col)) {
259
+ col++;
260
+ continue;
261
+ }
262
+
263
+ const width = this.colWidths.get(col) ?? this.defaultColWidth;
264
+ const nextAccX = accX + width;
265
+
266
+ // Check for resize handle
267
+ if (localX >= nextAccX - RESIZE_HANDLE_SIZE && localX <= nextAccX + RESIZE_HANDLE_SIZE) {
268
+ return { type: 'column', index: col, isResize: true };
269
+ }
270
+
271
+ if (nextAccX > localX) {
272
+ return { type: 'column', index: col, isResize: false };
273
+ }
274
+
275
+ accX = nextAccX;
276
+ col++;
277
+ }
278
+ }
279
+
280
+ return null;
281
+ }
282
+
283
+ // Row header
284
+ if (x <= this.headerWidth && y > this.headerHeight) {
285
+ // Check if in frozen rows area
286
+ const inFrozenRows = y < this.headerHeight + this.frozenHeight && this.frozenRows > 0;
287
+
288
+ if (inFrozenRows) {
289
+ // Frozen row headers - no scroll
290
+ const localY = y - this.headerHeight;
291
+
292
+ let row = 0;
293
+ let accY = 0;
294
+ while (row < this.frozenRows && row < this.rowCount) {
295
+ if (this.hiddenRows.has(row)) {
296
+ row++;
297
+ continue;
298
+ }
299
+
300
+ const height = this.rowHeights.get(row) ?? this.defaultRowHeight;
301
+ const nextAccY = accY + height;
302
+
303
+ // Check for resize handle
304
+ if (localY >= nextAccY - RESIZE_HANDLE_SIZE && localY <= nextAccY + RESIZE_HANDLE_SIZE) {
305
+ return { type: 'row', index: row, isResize: true };
306
+ }
307
+
308
+ if (nextAccY > localY) {
309
+ return { type: 'row', index: row, isResize: false };
310
+ }
311
+
312
+ accY = nextAccY;
313
+ row++;
314
+ }
315
+ } else {
316
+ // Scrollable row headers
317
+ const localY = y - this.headerHeight - this.frozenHeight + this.scrollTop;
318
+
319
+ let row = this.frozenRows;
320
+ let accY = 0;
321
+ while (row < this.rowCount) {
322
+ if (this.hiddenRows.has(row)) {
323
+ row++;
324
+ continue;
325
+ }
326
+
327
+ const height = this.rowHeights.get(row) ?? this.defaultRowHeight;
328
+ const nextAccY = accY + height;
329
+
330
+ // Check for resize handle
331
+ if (localY >= nextAccY - RESIZE_HANDLE_SIZE && localY <= nextAccY + RESIZE_HANDLE_SIZE) {
332
+ return { type: 'row', index: row, isResize: true };
333
+ }
334
+
335
+ if (nextAccY > localY) {
336
+ return { type: 'row', index: row, isResize: false };
337
+ }
338
+
339
+ accY = nextAccY;
340
+ row++;
341
+ }
342
+ }
343
+
344
+ return null;
345
+ }
346
+
347
+ // Corner cell
348
+ if (x <= this.headerWidth && y <= this.headerHeight) {
349
+ // Corner cell doesn't have a specific header hit type
350
+ return null;
351
+ }
352
+
353
+ return null;
354
+ }
355
+
356
+ /**
357
+ * Get resize handle at a point
358
+ */
359
+ getResizeHandleAt(x: number, y: number): ResizeHandle | null {
360
+ const header = this.getHeaderAt(x, y);
361
+ if (header && header.isResize) {
362
+ return { type: header.type, index: header.index };
363
+ }
364
+ return null;
365
+ }
366
+
367
+ /**
368
+ * Check if point is on the fill handle (accounting for freeze panes)
369
+ */
370
+ getFillHandleAt(
371
+ x: number,
372
+ y: number,
373
+ selectionRange: Range | undefined
374
+ ): boolean {
375
+ if (!selectionRange) {
376
+ return false;
377
+ }
378
+
379
+ // Calculate fill handle position (bottom-right of selection)
380
+ const range = selectionRange;
381
+
382
+ // Determine if the end of the range is in frozen area
383
+ const endColFrozen = range.endCol < this.frozenCols;
384
+ const endRowFrozen = range.endRow < this.frozenRows;
385
+
386
+ // Calculate handleX
387
+ let handleX: number;
388
+ if (endColFrozen) {
389
+ // End column is frozen - no scroll offset
390
+ handleX = this.headerWidth;
391
+ for (let c = 0; c <= range.endCol; c++) {
392
+ if (!this.hiddenCols.has(c)) {
393
+ handleX += this.colWidths.get(c) ?? this.defaultColWidth;
394
+ }
395
+ }
396
+ } else {
397
+ // End column is scrollable
398
+ handleX = this.headerWidth + this.frozenWidth;
399
+ for (let c = this.frozenCols; c <= range.endCol; c++) {
400
+ if (!this.hiddenCols.has(c)) {
401
+ handleX += this.colWidths.get(c) ?? this.defaultColWidth;
402
+ }
403
+ }
404
+ handleX -= this.scrollLeft;
405
+ }
406
+
407
+ // Calculate handleY
408
+ let handleY: number;
409
+ if (endRowFrozen) {
410
+ // End row is frozen - no scroll offset
411
+ handleY = this.headerHeight;
412
+ for (let r = 0; r <= range.endRow; r++) {
413
+ if (!this.hiddenRows.has(r)) {
414
+ handleY += this.rowHeights.get(r) ?? this.defaultRowHeight;
415
+ }
416
+ }
417
+ } else {
418
+ // End row is scrollable
419
+ handleY = this.headerHeight + this.frozenHeight;
420
+ for (let r = this.frozenRows; r <= range.endRow; r++) {
421
+ if (!this.hiddenRows.has(r)) {
422
+ handleY += this.rowHeights.get(r) ?? this.defaultRowHeight;
423
+ }
424
+ }
425
+ handleY -= this.scrollTop;
426
+ }
427
+
428
+ // Check if point is within fill handle hit area
429
+ const dx = Math.abs(x - handleX);
430
+ const dy = Math.abs(y - handleY);
431
+
432
+ return dx <= FILL_HANDLE_HIT_SIZE && dy <= FILL_HANDLE_HIT_SIZE;
433
+ }
434
+
435
+ /**
436
+ * Convert grid position to canvas coordinates (accounting for freeze panes)
437
+ */
438
+ gridToCanvas(row: number, col: number): { x: number; y: number } {
439
+ // Determine if cell is in frozen area
440
+ const colFrozen = col < this.frozenCols;
441
+ const rowFrozen = row < this.frozenRows;
442
+
443
+ // Calculate X position
444
+ let x: number;
445
+ if (colFrozen) {
446
+ // Column is frozen - no scroll offset
447
+ x = this.headerWidth;
448
+ for (let c = 0; c < col; c++) {
449
+ if (!this.hiddenCols.has(c)) {
450
+ x += this.colWidths.get(c) ?? this.defaultColWidth;
451
+ }
452
+ }
453
+ } else {
454
+ // Column is scrollable
455
+ x = this.headerWidth + this.frozenWidth;
456
+ for (let c = this.frozenCols; c < col; c++) {
457
+ if (!this.hiddenCols.has(c)) {
458
+ x += this.colWidths.get(c) ?? this.defaultColWidth;
459
+ }
460
+ }
461
+ x -= this.scrollLeft;
462
+ }
463
+
464
+ // Calculate Y position
465
+ let y: number;
466
+ if (rowFrozen) {
467
+ // Row is frozen - no scroll offset
468
+ y = this.headerHeight;
469
+ for (let r = 0; r < row; r++) {
470
+ if (!this.hiddenRows.has(r)) {
471
+ y += this.rowHeights.get(r) ?? this.defaultRowHeight;
472
+ }
473
+ }
474
+ } else {
475
+ // Row is scrollable
476
+ y = this.headerHeight + this.frozenHeight;
477
+ for (let r = this.frozenRows; r < row; r++) {
478
+ if (!this.hiddenRows.has(r)) {
479
+ y += this.rowHeights.get(r) ?? this.defaultRowHeight;
480
+ }
481
+ }
482
+ y -= this.scrollTop;
483
+ }
484
+
485
+ return { x, y };
486
+ }
487
+
488
+ /**
489
+ * Get the bounds of a cell in canvas coordinates (accounting for freeze panes)
490
+ */
491
+ getCellBounds(row: number, col: number): { x: number; y: number; width: number; height: number } | null {
492
+ // Return null for hidden cells
493
+ if (this.hiddenRows.has(row) || this.hiddenCols.has(col)) {
494
+ return null;
495
+ }
496
+
497
+ const { x, y } = this.gridToCanvas(row, col);
498
+ const width = this.colWidths.get(col) ?? this.defaultColWidth;
499
+ const height = this.rowHeights.get(row) ?? this.defaultRowHeight;
500
+
501
+ return { x, y, width, height };
502
+ }
503
+
504
+ /**
505
+ * Check if a point is in the corner cell
506
+ */
507
+ isInCornerCell(x: number, y: number): boolean {
508
+ return x <= this.headerWidth && y <= this.headerHeight;
509
+ }
510
+
511
+ /**
512
+ * Get the total width of the grid (excluding hidden columns)
513
+ */
514
+ getTotalWidth(): number {
515
+ let total = 0;
516
+ for (let c = 0; c < this.colCount; c++) {
517
+ if (!this.hiddenCols.has(c)) {
518
+ total += this.colWidths.get(c) ?? this.defaultColWidth;
519
+ }
520
+ }
521
+ return total;
522
+ }
523
+
524
+ /**
525
+ * Get the total height of the grid (excluding hidden rows)
526
+ */
527
+ getTotalHeight(): number {
528
+ let total = 0;
529
+ for (let r = 0; r < this.rowCount; r++) {
530
+ if (!this.hiddenRows.has(r)) {
531
+ total += this.rowHeights.get(r) ?? this.defaultRowHeight;
532
+ }
533
+ }
534
+ return total;
535
+ }
536
+ }
537
+
@@ -0,0 +1,16 @@
1
+ // Canvas rendering module for pagent-sheets
2
+
3
+ // Types
4
+ export * from './types';
5
+
6
+ // Renderers
7
+ export { CanvasRenderer } from './renderer';
8
+ export { TextRenderer } from './text-renderer';
9
+ export { GridRenderer } from './grid-renderer';
10
+ export { CellRenderer } from './cell-renderer';
11
+ export { HeaderRenderer } from './header-renderer';
12
+ export { SelectionRenderer } from './selection-renderer';
13
+
14
+ // Hit testing
15
+ export { HitTester } from './hit-testing';
16
+