@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,181 @@
1
+ // Cell Renderer - Handles cell content rendering
2
+
3
+ import type { CanvasTheme, Rect, TextStyle, CellBorders } from './types';
4
+ import type { Cell, CellStyle, CellFormat } from '../types';
5
+ import { TextRenderer } from './text-renderer';
6
+ import { GridRenderer } from './grid-renderer';
7
+ import { formatNumber } from '../utils/format-utils';
8
+
9
+ /**
10
+ * Renders cell content including backgrounds, text, and borders
11
+ */
12
+ export class CellRenderer {
13
+ private theme: CanvasTheme;
14
+ private textRenderer: TextRenderer;
15
+ private gridRenderer: GridRenderer;
16
+
17
+ constructor(theme: CanvasTheme, textRenderer: TextRenderer) {
18
+ this.theme = theme;
19
+ this.textRenderer = textRenderer;
20
+ this.gridRenderer = new GridRenderer(theme);
21
+ }
22
+
23
+ /**
24
+ * Render a cell with all its content
25
+ */
26
+ renderCell(
27
+ ctx: CanvasRenderingContext2D,
28
+ cell: Cell | undefined,
29
+ bounds: Rect,
30
+ style: CellStyle | undefined,
31
+ format: CellFormat | undefined
32
+ ): void {
33
+ // 1. Render background
34
+ this.renderBackground(ctx, bounds, style);
35
+
36
+ // 2. Render cell content (text/value)
37
+ if (cell) {
38
+ const displayValue = this.formatCellValue(cell, format);
39
+ if (displayValue) {
40
+ const textStyle = this.cellStyleToTextStyle(style);
41
+
42
+ // Check if text wrapping is enabled
43
+ if (style?.textWrap) {
44
+ this.textRenderer.renderWrappedText(ctx, displayValue, bounds, textStyle);
45
+ } else {
46
+ this.textRenderer.renderText(ctx, displayValue, bounds, textStyle);
47
+ }
48
+ }
49
+ }
50
+
51
+ // 3. Render borders (if any)
52
+ if (style) {
53
+ const borders = this.cellStyleToBorders(style);
54
+ if (borders.top || borders.right || borders.bottom || borders.left) {
55
+ this.gridRenderer.renderCellBorders(ctx, bounds, borders);
56
+ }
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Render an empty cell (just background)
62
+ */
63
+ renderEmptyCell(ctx: CanvasRenderingContext2D, bounds: Rect): void {
64
+ this.renderBackground(ctx, bounds, undefined);
65
+ }
66
+
67
+ /**
68
+ * Render cell background
69
+ */
70
+ private renderBackground(
71
+ ctx: CanvasRenderingContext2D,
72
+ bounds: Rect,
73
+ style: CellStyle | undefined
74
+ ): void {
75
+ const bgColor = style?.backgroundColor ?? this.theme.cellBackgroundColor;
76
+
77
+ ctx.fillStyle = bgColor;
78
+ ctx.fillRect(bounds.x, bounds.y, bounds.width, bounds.height);
79
+ }
80
+
81
+ /**
82
+ * Format cell value for display
83
+ */
84
+ private formatCellValue(cell: Cell, format: CellFormat | undefined): string {
85
+ const value = cell.value;
86
+
87
+ if (value === null || value === undefined) {
88
+ return '';
89
+ }
90
+
91
+ // If there's a formula, show the computed value (not the formula)
92
+ // The formula is shown in the formula bar, not in the cell
93
+
94
+ if (typeof value === 'number') {
95
+ return this.formatNumber(value, format);
96
+ }
97
+
98
+ if (typeof value === 'boolean') {
99
+ return value ? 'TRUE' : 'FALSE';
100
+ }
101
+
102
+ return String(value);
103
+ }
104
+
105
+ /**
106
+ * Format a number according to the cell format
107
+ */
108
+ private formatNumber(value: number, format: CellFormat | undefined): string {
109
+ // Use the comprehensive formatNumber function from format-utils
110
+ // Pass an empty format object if none is provided
111
+ return formatNumber(value, format || {});
112
+ }
113
+
114
+
115
+ /**
116
+ * Convert CellStyle to TextStyle for rendering
117
+ */
118
+ private cellStyleToTextStyle(style: CellStyle | undefined): Partial<TextStyle> {
119
+ if (!style) {
120
+ return {};
121
+ }
122
+
123
+ return {
124
+ fontFamily: style.fontFamily,
125
+ fontSize: style.fontSize,
126
+ fontWeight: style.bold ? 'bold' : 'normal',
127
+ fontStyle: style.italic ? 'italic' : 'normal',
128
+ color: style.fontColor,
129
+ textAlign: style.textAlign,
130
+ verticalAlign: style.verticalAlign,
131
+ textDecoration: style.textDecoration,
132
+ };
133
+ }
134
+
135
+ /**
136
+ * Convert CellStyle borders to CellBorders
137
+ */
138
+ private cellStyleToBorders(style: CellStyle): CellBorders {
139
+ return {
140
+ top: this.gridRenderer.parseBorderString(style.borderTop),
141
+ right: this.gridRenderer.parseBorderString(style.borderRight),
142
+ bottom: this.gridRenderer.parseBorderString(style.borderBottom),
143
+ left: this.gridRenderer.parseBorderString(style.borderLeft),
144
+ };
145
+ }
146
+
147
+ /**
148
+ * Render a hyperlink cell (with special styling)
149
+ */
150
+ renderHyperlinkCell(
151
+ ctx: CanvasRenderingContext2D,
152
+ cell: Cell,
153
+ bounds: Rect,
154
+ style: CellStyle | undefined,
155
+ format: CellFormat | undefined
156
+ ): void {
157
+ // Render background
158
+ this.renderBackground(ctx, bounds, style);
159
+
160
+ // Render text with hyperlink styling
161
+ const displayValue = this.formatCellValue(cell, format);
162
+ if (displayValue) {
163
+ const textStyle: Partial<TextStyle> = {
164
+ ...this.cellStyleToTextStyle(style),
165
+ color: '#1a73e8', // Blue color for hyperlinks
166
+ textDecoration: 'underline',
167
+ };
168
+
169
+ this.textRenderer.renderText(ctx, displayValue, bounds, textStyle);
170
+ }
171
+
172
+ // Render borders
173
+ if (style) {
174
+ const borders = this.cellStyleToBorders(style);
175
+ if (borders.top || borders.right || borders.bottom || borders.left) {
176
+ this.gridRenderer.renderCellBorders(ctx, bounds, borders);
177
+ }
178
+ }
179
+ }
180
+ }
181
+
@@ -0,0 +1,238 @@
1
+ // Grid Renderer - Handles grid lines and cell borders
2
+
3
+ import type { CanvasTheme, Viewport, BorderStyle, CellBorders, Rect } from './types';
4
+
5
+ /**
6
+ * Renders grid lines and cell borders
7
+ */
8
+ export class GridRenderer {
9
+ private theme: CanvasTheme;
10
+
11
+ constructor(theme: CanvasTheme) {
12
+ this.theme = theme;
13
+ }
14
+
15
+ /**
16
+ * Render grid lines for the visible area
17
+ */
18
+ renderGridLines(
19
+ ctx: CanvasRenderingContext2D,
20
+ viewport: Viewport,
21
+ rowHeights: Map<number, number>,
22
+ colWidths: Map<number, number>,
23
+ defaultRowHeight: number,
24
+ defaultColWidth: number,
25
+ headerWidth: number,
26
+ headerHeight: number,
27
+ startRow: number,
28
+ endRow: number,
29
+ startCol: number,
30
+ endCol: number,
31
+ hiddenRows?: Set<number>,
32
+ hiddenCols?: Set<number>
33
+ ): void {
34
+ const { scrollTop, scrollLeft, width, height } = viewport;
35
+ const hidRows = hiddenRows ?? new Set<number>();
36
+ const hidCols = hiddenCols ?? new Set<number>();
37
+
38
+ ctx.strokeStyle = this.theme.gridLineColor;
39
+ ctx.lineWidth = this.theme.gridLineWidth;
40
+
41
+ // Calculate starting positions (skip hidden rows/cols)
42
+ let startY = headerHeight;
43
+ for (let r = 0; r < startRow; r++) {
44
+ if (!hidRows.has(r)) {
45
+ startY += rowHeights.get(r) ?? defaultRowHeight;
46
+ }
47
+ }
48
+ startY -= scrollTop;
49
+
50
+ let startX = headerWidth;
51
+ for (let c = 0; c < startCol; c++) {
52
+ if (!hidCols.has(c)) {
53
+ startX += colWidths.get(c) ?? defaultColWidth;
54
+ }
55
+ }
56
+ startX -= scrollLeft;
57
+
58
+ ctx.beginPath();
59
+
60
+ // Draw horizontal lines (row separators) - skip hidden rows
61
+ let y = startY;
62
+ for (let row = startRow; row <= endRow; row++) {
63
+ if (hidRows.has(row)) continue;
64
+ // Snap to pixel grid for crisp lines
65
+ const lineY = Math.round(y) + 0.5;
66
+ ctx.moveTo(headerWidth, lineY);
67
+ ctx.lineTo(width, lineY);
68
+ y += rowHeights.get(row) ?? defaultRowHeight;
69
+ }
70
+
71
+ // Draw vertical lines (column separators) - skip hidden columns
72
+ let x = startX;
73
+ for (let col = startCol; col <= endCol; col++) {
74
+ if (hidCols.has(col)) continue;
75
+ // Snap to pixel grid for crisp lines
76
+ const lineX = Math.round(x) + 0.5;
77
+ ctx.moveTo(lineX, headerHeight);
78
+ ctx.lineTo(lineX, height);
79
+ x += colWidths.get(col) ?? defaultColWidth;
80
+ }
81
+
82
+ ctx.stroke();
83
+ }
84
+
85
+ /**
86
+ * Parse a CSS border string into a BorderStyle object
87
+ */
88
+ parseBorderString(border: string | undefined): BorderStyle | undefined {
89
+ if (!border || border === 'none') {
90
+ return undefined;
91
+ }
92
+
93
+ // Parse formats like "1px solid #000000" or "2px dashed red"
94
+ const parts = border.split(' ');
95
+ if (parts.length < 2) {
96
+ return undefined;
97
+ }
98
+
99
+ const widthStr = parts[0];
100
+ const style = parts[1] as 'solid' | 'dashed' | 'dotted';
101
+ const color = parts[2] ?? '#000000';
102
+
103
+ const width = parseInt(widthStr, 10);
104
+ if (isNaN(width)) {
105
+ return undefined;
106
+ }
107
+
108
+ return { width, style, color };
109
+ }
110
+
111
+ /**
112
+ * Render borders for a single cell
113
+ */
114
+ renderCellBorders(
115
+ ctx: CanvasRenderingContext2D,
116
+ bounds: Rect,
117
+ borders: CellBorders
118
+ ): void {
119
+ const { x, y, width, height } = bounds;
120
+
121
+ // Render each border if specified
122
+ if (borders.top) {
123
+ this.renderBorderLine(ctx, x, y, x + width, y, borders.top);
124
+ }
125
+
126
+ if (borders.right) {
127
+ this.renderBorderLine(ctx, x + width, y, x + width, y + height, borders.right);
128
+ }
129
+
130
+ if (borders.bottom) {
131
+ this.renderBorderLine(ctx, x, y + height, x + width, y + height, borders.bottom);
132
+ }
133
+
134
+ if (borders.left) {
135
+ this.renderBorderLine(ctx, x, y, x, y + height, borders.left);
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Render a single border line with the specified style
141
+ */
142
+ private renderBorderLine(
143
+ ctx: CanvasRenderingContext2D,
144
+ x1: number,
145
+ y1: number,
146
+ x2: number,
147
+ y2: number,
148
+ style: BorderStyle
149
+ ): void {
150
+ ctx.strokeStyle = style.color;
151
+ ctx.lineWidth = style.width;
152
+
153
+ // Set line dash pattern based on style
154
+ switch (style.style) {
155
+ case 'dashed':
156
+ ctx.setLineDash([4, 2]);
157
+ break;
158
+ case 'dotted':
159
+ ctx.setLineDash([1, 1]);
160
+ break;
161
+ case 'solid':
162
+ default:
163
+ ctx.setLineDash([]);
164
+ break;
165
+ }
166
+
167
+ ctx.beginPath();
168
+
169
+ // Snap to pixel grid for crisp lines
170
+ const snapX1 = Math.round(x1) + 0.5;
171
+ const snapY1 = Math.round(y1) + 0.5;
172
+ const snapX2 = Math.round(x2) + 0.5;
173
+ const snapY2 = Math.round(y2) + 0.5;
174
+
175
+ ctx.moveTo(snapX1, snapY1);
176
+ ctx.lineTo(snapX2, snapY2);
177
+ ctx.stroke();
178
+
179
+ // Reset line dash
180
+ ctx.setLineDash([]);
181
+ }
182
+
183
+ /**
184
+ * Render borders for all visible cells that have custom borders
185
+ */
186
+ renderAllCellBorders(
187
+ ctx: CanvasRenderingContext2D,
188
+ cellBorders: Map<string, CellBorders>,
189
+ viewport: Viewport,
190
+ rowHeights: Map<number, number>,
191
+ colWidths: Map<number, number>,
192
+ defaultRowHeight: number,
193
+ defaultColWidth: number,
194
+ headerWidth: number,
195
+ headerHeight: number,
196
+ startRow: number,
197
+ endRow: number,
198
+ startCol: number,
199
+ endCol: number
200
+ ): void {
201
+ const { scrollTop, scrollLeft } = viewport;
202
+
203
+ // Calculate starting positions
204
+ let startY = headerHeight;
205
+ for (let r = 0; r < startRow; r++) {
206
+ startY += rowHeights.get(r) ?? defaultRowHeight;
207
+ }
208
+ startY -= scrollTop;
209
+
210
+ let startX = headerWidth;
211
+ for (let c = 0; c < startCol; c++) {
212
+ startX += colWidths.get(c) ?? defaultColWidth;
213
+ }
214
+ startX -= scrollLeft;
215
+
216
+ // Iterate through visible cells and render borders
217
+ let y = startY;
218
+ for (let row = startRow; row < endRow; row++) {
219
+ const rowHeight = rowHeights.get(row) ?? defaultRowHeight;
220
+ let x = startX;
221
+
222
+ for (let col = startCol; col < endCol; col++) {
223
+ const colWidth = colWidths.get(col) ?? defaultColWidth;
224
+ const cellKey = `${row}:${col}`;
225
+ const borders = cellBorders.get(cellKey);
226
+
227
+ if (borders) {
228
+ this.renderCellBorders(ctx, { x, y, width: colWidth, height: rowHeight }, borders);
229
+ }
230
+
231
+ x += colWidth;
232
+ }
233
+
234
+ y += rowHeight;
235
+ }
236
+ }
237
+ }
238
+