argent-grid 0.1.0 → 0.3.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 (122) hide show
  1. package/.github/workflows/ci.yml +69 -0
  2. package/.github/workflows/pages.yml +6 -12
  3. package/.storybook/main.ts +20 -0
  4. package/.storybook/preview.ts +18 -0
  5. package/.storybook/tsconfig.json +24 -0
  6. package/AGENTS.md +70 -27
  7. package/README.md +51 -34
  8. package/angular.json +66 -0
  9. package/biome.json +66 -0
  10. package/demo-app/e2e/selection-screenshot.spec.ts +20 -0
  11. package/docs/AG-GRID-COMPARISON.md +725 -0
  12. package/docs/CELL-RENDERER-GUIDE.md +241 -0
  13. package/docs/CONTEXT-MENU-GUIDE.md +371 -0
  14. package/docs/LIVE-DATA-OPTIMIZATIONS.md +497 -0
  15. package/docs/PERFORMANCE-OPTIMIZATIONS-PHASE1.md +162 -0
  16. package/docs/PERFORMANCE-REVIEW.md +571 -0
  17. package/docs/RESEARCH-STATUS.md +234 -0
  18. package/docs/STATE-PERSISTENCE-GUIDE.md +370 -0
  19. package/docs/STORYBOOK-REFACTOR.md +215 -0
  20. package/docs/STORYBOOK-STATUS.md +156 -0
  21. package/docs/TEST-COVERAGE-REPORT.md +276 -0
  22. package/docs/THEME-API-GUIDE.md +445 -0
  23. package/docs/THEME-API-PLAN.md +364 -0
  24. package/e2e/advanced.spec.ts +109 -0
  25. package/e2e/argentgrid.spec.ts +65 -0
  26. package/e2e/benchmark.spec.ts +52 -0
  27. package/e2e/cell-renderers.spec.ts +152 -0
  28. package/e2e/debug-streaming.spec.ts +31 -0
  29. package/e2e/dnd.spec.ts +73 -0
  30. package/e2e/screenshots.spec.ts +52 -0
  31. package/e2e/theming.spec.ts +35 -0
  32. package/e2e/visual.spec.ts +112 -0
  33. package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
  34. package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
  35. package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
  36. package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
  37. package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
  38. package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
  39. package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
  40. package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
  41. package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
  42. package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
  43. package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
  44. package/package.json +21 -7
  45. package/plan.md +56 -28
  46. package/playwright.config.ts +38 -0
  47. package/setup-vitest.ts +10 -13
  48. package/src/lib/argent-grid.module.ts +10 -12
  49. package/src/lib/components/argent-grid.component.css +281 -321
  50. package/src/lib/components/argent-grid.component.html +295 -207
  51. package/src/lib/components/argent-grid.component.spec.ts +120 -160
  52. package/src/lib/components/argent-grid.component.ts +1193 -290
  53. package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
  54. package/src/lib/components/argent-grid.selection.spec.ts +132 -0
  55. package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
  56. package/src/lib/components/set-filter/set-filter.component.ts +307 -0
  57. package/src/lib/directives/ag-grid-compatibility.directive.ts +16 -26
  58. package/src/lib/directives/click-outside.directive.ts +19 -0
  59. package/src/lib/rendering/canvas-renderer.spec.ts +513 -0
  60. package/src/lib/rendering/canvas-renderer.ts +456 -452
  61. package/src/lib/rendering/live-data-handler.ts +110 -0
  62. package/src/lib/rendering/live-data-optimizations.ts +133 -0
  63. package/src/lib/rendering/render/blit.spec.ts +16 -27
  64. package/src/lib/rendering/render/blit.ts +48 -36
  65. package/src/lib/rendering/render/cells.spec.ts +132 -0
  66. package/src/lib/rendering/render/cells.ts +167 -28
  67. package/src/lib/rendering/render/column-utils.ts +95 -0
  68. package/src/lib/rendering/render/hit-test.ts +50 -0
  69. package/src/lib/rendering/render/index.ts +88 -76
  70. package/src/lib/rendering/render/lines.ts +53 -47
  71. package/src/lib/rendering/render/primitives.ts +423 -0
  72. package/src/lib/rendering/render/theme.spec.ts +8 -12
  73. package/src/lib/rendering/render/theme.ts +7 -10
  74. package/src/lib/rendering/render/types.ts +3 -2
  75. package/src/lib/rendering/render/walk.spec.ts +35 -38
  76. package/src/lib/rendering/render/walk.ts +94 -64
  77. package/src/lib/rendering/utils/damage-tracker.spec.ts +8 -7
  78. package/src/lib/rendering/utils/damage-tracker.ts +6 -18
  79. package/src/lib/rendering/utils/index.ts +1 -1
  80. package/src/lib/services/grid.service.set-filter.spec.ts +219 -0
  81. package/src/lib/services/grid.service.spec.ts +1241 -201
  82. package/src/lib/services/grid.service.ts +1204 -235
  83. package/src/lib/themes/parts/color-schemes.ts +132 -0
  84. package/src/lib/themes/parts/icon-sets.ts +258 -0
  85. package/src/lib/themes/theme-builder.ts +347 -0
  86. package/src/lib/themes/theme-quartz.ts +72 -0
  87. package/src/lib/themes/types.ts +238 -0
  88. package/src/lib/types/ag-grid-types.ts +573 -14
  89. package/src/public-api.ts +39 -9
  90. package/src/stories/Advanced.stories.ts +249 -0
  91. package/src/stories/ArgentGrid.stories.ts +301 -0
  92. package/src/stories/Benchmark.stories.ts +76 -0
  93. package/src/stories/CellRenderers.stories.ts +395 -0
  94. package/src/stories/Filtering.stories.ts +292 -0
  95. package/src/stories/Grouping.stories.ts +290 -0
  96. package/src/stories/Streaming.stories.ts +57 -0
  97. package/src/stories/Theming.stories.ts +137 -0
  98. package/src/stories/Tooltips.stories.ts +381 -0
  99. package/src/stories/benchmark-wrapper.component.ts +355 -0
  100. package/src/stories/story-utils.ts +88 -0
  101. package/src/stories/streaming-wrapper.component.ts +441 -0
  102. package/tsconfig.json +1 -0
  103. package/tsconfig.storybook.json +10 -0
  104. package/vitest.config.ts +9 -9
  105. package/demo-app/README.md +0 -70
  106. package/demo-app/angular.json +0 -78
  107. package/demo-app/e2e/benchmark.spec.ts +0 -53
  108. package/demo-app/e2e/demo-page.spec.ts +0 -77
  109. package/demo-app/e2e/grid-features.spec.ts +0 -269
  110. package/demo-app/package-lock.json +0 -14023
  111. package/demo-app/package.json +0 -36
  112. package/demo-app/playwright-test-menu.js +0 -19
  113. package/demo-app/playwright.config.ts +0 -23
  114. package/demo-app/src/app/app.component.ts +0 -10
  115. package/demo-app/src/app/app.config.ts +0 -13
  116. package/demo-app/src/app/app.routes.ts +0 -7
  117. package/demo-app/src/app/demo-page/demo-page.component.css +0 -313
  118. package/demo-app/src/app/demo-page/demo-page.component.html +0 -124
  119. package/demo-app/src/app/demo-page/demo-page.component.ts +0 -366
  120. package/demo-app/src/index.html +0 -19
  121. package/demo-app/src/main.ts +0 -6
  122. package/demo-app/tsconfig.json +0 -31
@@ -4,9 +4,17 @@
4
4
  * Handles drawing of individual cells with prep/draw cycle optimization.
5
5
  */
6
6
 
7
- import { Column, IRowNode, ColDef, GridApi } from '../../types/ag-grid-types';
8
- import { CellDrawContext, ColumnPrepResult, GridTheme } from './types';
7
+ import { ColDef, Column, GridApi, IRowNode } from '../../types/ag-grid-types';
8
+ import {
9
+ drawBadge,
10
+ drawButton,
11
+ drawCheckbox,
12
+ drawProgressBar,
13
+ drawRating,
14
+ drawSparkline,
15
+ } from './primitives';
9
16
  import { getFontFromTheme } from './theme';
17
+ import { CellDrawContext, ColumnPrepResult, GridTheme } from './types';
10
18
 
11
19
  /**
12
20
  * Get value from object using path (e.g. 'pivotData.NY.salary')
@@ -14,8 +22,8 @@ import { getFontFromTheme } from './theme';
14
22
  export function getValueByPath(obj: any, path: string): any {
15
23
  if (!path || !obj) return undefined;
16
24
  if (!path.includes('.')) return obj[path];
17
-
18
- return path.split('.').reduce((acc, part) => acc && acc[part], obj);
25
+
26
+ return path.split('.').reduce((acc, part) => acc?.[part], obj);
19
27
  }
20
28
 
21
29
  // ============================================================================
@@ -75,7 +83,7 @@ export function drawCell<TData = any>(
75
83
  prep: ColumnPrepResult<TData>,
76
84
  context: CellDrawContext<TData>
77
85
  ): void {
78
- const { x, y, width, height, value, formattedValue, column, rowNode } = context;
86
+ const { rowNode } = context;
79
87
 
80
88
  // Draw cell background
81
89
  drawCellBackground(ctx, context);
@@ -109,23 +117,92 @@ export function drawCellBackground<TData = any>(
109
117
  }
110
118
 
111
119
  /**
112
- * Draw cell content (text)
120
+ * Draw cell content (text or specialized renderer)
113
121
  */
114
122
  export function drawCellContent<TData = any>(
115
123
  ctx: CanvasRenderingContext2D,
116
- prep: ColumnPrepResult<TData>,
124
+ _prep: ColumnPrepResult<TData>,
117
125
  context: CellDrawContext<TData>
118
126
  ): void {
119
- const { x, y, width, height, formattedValue, theme } = context;
127
+ const { x, y, width, height, value, formattedValue, theme, colDef, rowNode, api } = context;
128
+
129
+ // 1. Check for dedicated checkbox renderer or internal selection column
130
+ if (colDef?.cellRenderer === 'checkbox' || context.column.colId === 'ag-Grid-SelectionColumn') {
131
+ const isChecked = colDef?.cellRenderer === 'checkbox' ? !!value : !!rowNode?.selected;
132
+ const size = 14;
133
+ const bx = Math.floor(x + (width - size) / 2);
134
+ const by = Math.floor(y + (height - size) / 2);
135
+
136
+ drawCheckbox(ctx, bx, by, size, isChecked, theme);
137
+ return; // Dedicated checkbox column only shows checkbox
138
+ }
139
+
140
+ // 2. Check for sparkline
141
+ if (colDef?.sparklineOptions) {
142
+ drawSparkline(ctx, value, x, y, width, height, colDef.sparklineOptions);
143
+ return;
144
+ }
145
+
146
+ // 3. Check for progress bar
147
+ if (colDef?.progressOptions) {
148
+ drawProgressBar(ctx, Number(value), x, y, width, height, colDef.progressOptions);
149
+ return;
150
+ }
151
+
152
+ // 4. Check for badge
153
+ if (colDef?.badgeOptions) {
154
+ drawBadge(ctx, String(value ?? ''), x, y, width, height, colDef.badgeOptions);
155
+ return;
156
+ }
157
+
158
+ // 5. Check for button
159
+ if (colDef?.buttonOptions) {
160
+ const opts = colDef.buttonOptions;
161
+ const label =
162
+ typeof opts.label === 'function'
163
+ ? opts.label({
164
+ value,
165
+ data: rowNode?.data,
166
+ node: rowNode!,
167
+ colDef: colDef!,
168
+ api: api!,
169
+ })
170
+ : opts.label;
171
+ drawButton(ctx, label, x, y, width, height, opts);
172
+ return;
173
+ }
120
174
 
175
+ // 6. Check for rating
176
+ if (colDef?.cellRenderer === 'rating' || colDef?.ratingOptions) {
177
+ drawRating(ctx, Number(value), x, y, width, height, colDef?.ratingOptions);
178
+ return;
179
+ }
180
+
181
+ // 7. Default: Text rendering
121
182
  if (!formattedValue) return;
122
183
 
123
184
  // Calculate text position with padding
124
185
  const textX = x + theme.cellPadding;
125
186
  const textY = y + height / 2; // Centered vertically
126
187
 
188
+ // Handle cellStyle color
189
+ let textColor = theme.textCell;
190
+ if (colDef?.cellStyle) {
191
+ const style =
192
+ typeof colDef.cellStyle === 'function'
193
+ ? colDef.cellStyle({
194
+ value,
195
+ data: rowNode?.data,
196
+ node: rowNode!,
197
+ column: context.column,
198
+ api: api!,
199
+ })
200
+ : colDef.cellStyle;
201
+ if (style?.color) textColor = style.color;
202
+ }
203
+
127
204
  // Set text properties
128
- ctx.fillStyle = theme.textCell;
205
+ ctx.fillStyle = textColor;
129
206
  ctx.textBaseline = 'middle';
130
207
 
131
208
  // Truncate text if needed
@@ -142,10 +219,10 @@ export function drawCellContent<TData = any>(
142
219
  */
143
220
  export function drawGroupIndicators<TData = any>(
144
221
  ctx: CanvasRenderingContext2D,
145
- prep: ColumnPrepResult<TData>,
222
+ _prep: ColumnPrepResult<TData>,
146
223
  context: CellDrawContext<TData>
147
224
  ): void {
148
- const { x, y, width, height, column, rowNode, theme, isEvenRow } = context;
225
+ const { x, y, height, column, rowNode, theme } = context;
149
226
 
150
227
  if (!rowNode) return;
151
228
 
@@ -174,7 +251,7 @@ export function drawGroupIndicators<TData = any>(
174
251
  const size = theme.groupIndicatorSize;
175
252
  const centerX = Math.floor(indicatorX + size / 2);
176
253
  const centerY = Math.floor(indicatorY);
177
-
254
+
178
255
  // Horizontal line
179
256
  ctx.moveTo(Math.floor(indicatorX), centerY);
180
257
  ctx.lineTo(Math.floor(indicatorX + size), centerY);
@@ -212,7 +289,7 @@ export function truncateText(
212
289
 
213
290
  while (start < end) {
214
291
  const mid = Math.floor((start + end) / 2);
215
- const truncated = text.slice(0, mid) + '...';
292
+ const truncated = `${text.slice(0, mid)}...`;
216
293
 
217
294
  if (ctx.measureText(truncated).width <= maxWidth) {
218
295
  start = mid + 1;
@@ -221,16 +298,13 @@ export function truncateText(
221
298
  }
222
299
  }
223
300
 
224
- return text.slice(0, Math.max(0, start - 1)) + '...';
301
+ return `${text.slice(0, Math.max(0, start - 1))}...`;
225
302
  }
226
303
 
227
304
  /**
228
305
  * Measure text width
229
306
  */
230
- export function measureText(
231
- ctx: CanvasRenderingContext2D,
232
- text: string
233
- ): number {
307
+ export function measureText(ctx: CanvasRenderingContext2D, text: string): number {
234
308
  return ctx.measureText(text).width;
235
309
  }
236
310
 
@@ -240,7 +314,7 @@ export function measureText(
240
314
  export function calculateColumnWidth<TData = any>(
241
315
  ctx: CanvasRenderingContext2D,
242
316
  column: Column,
243
- colDef: ColDef<TData> | null,
317
+ _colDef: ColDef<TData> | null,
244
318
  theme: GridTheme,
245
319
  sampleData: any[],
246
320
  maxRows: number = 100
@@ -278,6 +352,19 @@ export function calculateColumnWidth<TData = any>(
278
352
  /**
279
353
  * Get formatted cell value
280
354
  */
355
+ /**
356
+ * Strip HTML tags from string
357
+ * Supports basic cellRenderer that returns HTML strings
358
+ * Note: Only plain text is rendered - colors, backgrounds, etc. are NOT supported
359
+ */
360
+ export function stripHtmlTags(html: string): string {
361
+ if (!html) return '';
362
+ return html
363
+ .replace(/<[^>]*>/g, '')
364
+ .replace(/\s+/g, ' ')
365
+ .trim();
366
+ }
367
+
281
368
  export function getFormattedValue<TData = any>(
282
369
  value: any,
283
370
  colDef: ColDef<TData> | null,
@@ -289,6 +376,27 @@ export function getFormattedValue<TData = any>(
289
376
  return '';
290
377
  }
291
378
 
379
+ // Use custom cellRenderer if provided
380
+ if (colDef && typeof colDef.cellRenderer === 'function') {
381
+ try {
382
+ const result = colDef.cellRenderer({
383
+ value,
384
+ data,
385
+ node: rowNode,
386
+ colDef,
387
+ api,
388
+ });
389
+ // Handle both string and Promise<string> returns
390
+ if (typeof result === 'string') {
391
+ return stripHtmlTags(result);
392
+ }
393
+ // For async renderers, return value as string (will be updated on next render)
394
+ return String(value);
395
+ } catch (e) {
396
+ console.warn('Cell renderer error:', e);
397
+ }
398
+ }
399
+
292
400
  // Use custom formatter if provided
293
401
  if (colDef && typeof colDef.valueFormatter === 'function') {
294
402
  try {
@@ -311,6 +419,42 @@ export function getFormattedValue<TData = any>(
311
419
  // BATCH CELL RENDERING
312
420
  // ============================================================================
313
421
 
422
+ /**
423
+ * Get the value for a cell, respecting valueGetter if present
424
+ */
425
+ export function getCellValue<TData = any>(
426
+ column: Column,
427
+ colDef: ColDef<TData> | null,
428
+ rowNode: IRowNode<TData>,
429
+ api: GridApi<TData>
430
+ ): any {
431
+ // 1. Prioritize valueGetter
432
+ if (colDef?.valueGetter) {
433
+ if (typeof colDef.valueGetter === 'function') {
434
+ try {
435
+ return colDef.valueGetter({
436
+ data: rowNode.data,
437
+ node: rowNode,
438
+ colDef,
439
+ api,
440
+ column,
441
+ context: api.getGridOption('context'),
442
+ } as any);
443
+ } catch (e) {
444
+ console.warn('Value getter error:', e);
445
+ }
446
+ }
447
+ // Note: String expressions for valueGetter are not supported in the canvas renderer yet
448
+ }
449
+
450
+ // 2. Fallback to field
451
+ if (column.field) {
452
+ return getValueByPath(rowNode.data, column.field);
453
+ }
454
+
455
+ return undefined;
456
+ }
457
+
314
458
  /**
315
459
  * Render all cells in a row
316
460
  */
@@ -337,14 +481,8 @@ export function renderRow<TData = any>(
337
481
  if (!prep) continue;
338
482
 
339
483
  const x = getCellX(column);
340
- const value = column.field ? getValueByPath(rowNode.data, column.field) : undefined;
341
- const formattedValue = getFormattedValue(
342
- value,
343
- prep.colDef,
344
- rowNode.data,
345
- rowNode,
346
- api
347
- );
484
+ const value = getCellValue(column, prep.colDef, rowNode, api);
485
+ const formattedValue = getFormattedValue(value, prep.colDef, rowNode.data, rowNode, api);
348
486
 
349
487
  const context: CellDrawContext<TData> = {
350
488
  ctx,
@@ -362,8 +500,9 @@ export function renderRow<TData = any>(
362
500
  isSelected: options.isSelected || rowNode.selected,
363
501
  isHovered: options.isHovered || false,
364
502
  isEvenRow,
503
+ api,
365
504
  };
366
505
 
367
506
  drawCell(ctx, prep, context);
368
507
  }
369
- }
508
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Column Utilities for Canvas Renderer
3
+ *
4
+ * Helper functions for column management and definition lookup.
5
+ */
6
+
7
+ import { ColDef, Column, ColumnGroup, GridApi } from '../../types/ag-grid-types';
8
+
9
+ /**
10
+ * Check if a column or column group is visible, respecting columnGroupShow
11
+ */
12
+ export function isColumnVisible(item: Column | ColumnGroup): boolean {
13
+ if (item.columnGroupShow) {
14
+ const parent = item.parent;
15
+ if (parent) {
16
+ if (item.columnGroupShow === 'open') {
17
+ return parent.expanded;
18
+ }
19
+ if (item.columnGroupShow === 'closed') {
20
+ return !parent.expanded;
21
+ }
22
+ }
23
+ }
24
+
25
+ if ('children' in item) {
26
+ return item.children.some((child) => isColumnVisible(child));
27
+ }
28
+ return item.visible;
29
+ }
30
+
31
+ /**
32
+ * Find the Column Definition for a given Column
33
+ */
34
+ export function getColumnDef<TData = any>(
35
+ column: Column,
36
+ gridApi: GridApi<TData>
37
+ ): ColDef<TData> | null {
38
+ const allDefs = gridApi.getColumnDefs();
39
+ if (!allDefs) return null;
40
+
41
+ const targetId = column.colId;
42
+ const targetField = column.field?.toString();
43
+
44
+ for (const def of allDefs) {
45
+ if ('children' in def) {
46
+ const found = def.children.find((c) => {
47
+ const cDef = c as ColDef;
48
+ return (
49
+ cDef.colId === targetId ||
50
+ cDef.field?.toString() === targetId ||
51
+ (targetField && cDef.field?.toString() === targetField)
52
+ );
53
+ });
54
+ if (found) return found as ColDef<TData>;
55
+ } else {
56
+ const cDef = def as ColDef;
57
+ if (
58
+ cDef.colId === targetId ||
59
+ cDef.field?.toString() === targetId ||
60
+ (targetField && cDef.field?.toString() === targetField)
61
+ ) {
62
+ return def as ColDef<TData>;
63
+ }
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+
69
+ /**
70
+ * Get X position for a column
71
+ */
72
+ export function getColumnX(
73
+ targetCol: Column,
74
+ columnPositions: Map<string, number>,
75
+ scrollLeft: number,
76
+ leftPinnedWidth: number,
77
+ rightPinnedWidth: number,
78
+ viewportWidth: number
79
+ ): number {
80
+ // Use cached column position (O(1) lookup)
81
+ const baseX = columnPositions.get(targetCol.colId) || 0;
82
+
83
+ // Adjust for pinned columns and scroll position
84
+ if (targetCol.pinned === 'left') {
85
+ return Math.floor(baseX);
86
+ } else if (targetCol.pinned === 'right') {
87
+ // When right-pinned, we need to know the offset from the right edge
88
+ // Our positions are accumulated from left to right.
89
+ // We need to find where the right-pinned section starts.
90
+ const rightPinnedStartX = Math.floor(viewportWidth - rightPinnedWidth);
91
+ return rightPinnedStartX + Math.floor(baseX - (viewportWidth - rightPinnedWidth));
92
+ } else {
93
+ return Math.floor(leftPinnedWidth - scrollLeft + (baseX - leftPinnedWidth));
94
+ }
95
+ }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Hit Testing for Canvas Renderer
3
+ *
4
+ * Utilities for detecting which grid element is under a given coordinate.
5
+ */
6
+
7
+ import { Column } from '../../types/ag-grid-types';
8
+ import { HitTestResult } from './types';
9
+ import { getColumnAtX, getRowAtY } from './walk';
10
+
11
+ /**
12
+ * Perform a hit test on the grid
13
+ */
14
+ export function performHitTest(
15
+ canvasX: number,
16
+ canvasY: number,
17
+ rowHeight: number,
18
+ scrollTop: number,
19
+ scrollLeft: number,
20
+ viewportWidth: number,
21
+ columns: Column[],
22
+ availableWidth?: number
23
+ ): HitTestResult {
24
+ // Use walker utility for row detection
25
+ const rowIndex = getRowAtY(canvasY, rowHeight, scrollTop);
26
+
27
+ // Use walker utility for column detection
28
+ const result = getColumnAtX(columns, canvasX, scrollLeft, viewportWidth, availableWidth);
29
+
30
+ return {
31
+ rowIndex,
32
+ columnIndex: result.index,
33
+ column: result.column,
34
+ rowNode: null, // Should be populated by caller if needed
35
+ hitArea: result.column ? 'cell' : 'empty',
36
+ };
37
+ }
38
+
39
+ /**
40
+ * Get the horizontal offset of a center column
41
+ */
42
+ export function getCenterColumnOffset(targetCol: Column, columns: Column[]): number {
43
+ const centerColumns = columns.filter((c) => !c.pinned);
44
+ let offset = 0;
45
+ for (const col of centerColumns) {
46
+ if (col === targetCol) return offset;
47
+ offset += col.width;
48
+ }
49
+ return offset;
50
+ }
@@ -4,102 +4,114 @@
4
4
  * Exports all rendering-related modules.
5
5
  */
6
6
 
7
- // Types (base definitions)
8
- export {
9
- Rectangle,
10
- Point,
11
- Size,
12
- ScrollPosition,
13
- PositionedColumn,
14
- ColumnWalkCallback,
15
- RowWalkCallback,
16
- CellWalkCallback,
17
- CellDrawContext,
18
- ColumnPrepResult,
19
- BlitResult,
20
- BufferPair,
21
- DamageType,
22
- DirtyRegions,
23
- GridTheme,
24
- PartialTheme,
25
- RenderState,
26
- VisibleRange,
27
- HitTestResult,
28
- GridMouseEvent,
29
- } from './types';
30
-
31
- // Theme (re-export the DEFAULT_THEME and utilities)
32
- export {
33
- DEFAULT_THEME,
34
- DARK_THEME,
35
- THEME_PRESETS,
36
- mergeTheme,
37
- getFontFromTheme,
38
- getRowTheme,
39
- getCellBackgroundColor,
40
- getThemePreset,
41
- createTheme,
42
- } from './theme';
43
-
44
- // Walker functions
45
- export {
46
- walkColumns,
47
- getPositionedColumns,
48
- getPinnedWidths,
49
- walkRows,
50
- getVisibleRowRange,
51
- getRowY,
52
- walkCells,
53
- getColumnAtX,
54
- getColumnIndex,
55
- getTotalColumnWidth,
56
- getRowAtY,
57
- isRowVisible,
58
- calculateVisibleRange,
59
- } from './walk';
60
-
7
+ // Live data optimizations
8
+ export { LiveDataOptimizations } from '../live-data-optimizations';
61
9
  // Blitting optimization
62
10
  export {
63
- MIN_BLIT_DELTA,
64
- MAX_BLIT_DELTA_RATIO,
65
- shouldBlit,
66
- calculateBlit,
11
+ BlitState,
67
12
  blitLastFrame,
13
+ calculateBlit,
68
14
  createBufferPair,
69
- swapBuffers,
70
15
  displayBuffer,
16
+ MAX_BLIT_DELTA_RATIO,
17
+ MIN_BLIT_DELTA,
71
18
  resizeBufferPair,
72
- BlitState,
19
+ shouldBlit,
20
+ swapBuffers,
73
21
  } from './blit';
74
-
75
22
  // Cell rendering (explicit exports to avoid conflicts)
76
23
  export {
77
- prepColumn,
78
- prepColumns,
24
+ calculateColumnWidth,
79
25
  drawCell,
80
26
  drawCellBackground,
81
27
  drawCellContent,
82
28
  drawGroupIndicators,
83
- truncateText,
84
- measureText,
85
- calculateColumnWidth,
29
+ getCellValue,
86
30
  getFormattedValue,
87
31
  getValueByPath,
32
+ measureText,
33
+ prepColumn,
34
+ prepColumns,
88
35
  renderRow,
36
+ truncateText,
89
37
  } from './cells';
90
-
38
+ // Column utilities
39
+ export * from './column-utils';
40
+ // Hit testing
41
+ export * from './hit-test';
91
42
  // Grid lines
92
43
  export {
93
- drawCrispLine,
94
- drawHorizontalLine,
95
- drawVerticalLine,
96
- drawRowLines,
97
- drawColumnLines,
98
- getColumnBorderPositions,
99
- drawGridLines,
100
44
  drawBorder,
101
45
  drawCellSelectionBorder,
102
- drawRangeSelectionBorder,
46
+ drawColumnLines,
47
+ drawCrispLine,
48
+ drawGridLines,
49
+ drawHorizontalLine,
103
50
  drawPinnedRegionBorders,
104
51
  drawPinnedRegionShadows,
105
- } from './lines';
52
+ drawRangeSelectionBorder,
53
+ drawRowLines,
54
+ drawVerticalLine,
55
+ getColumnBorderPositions,
56
+ } from './lines';
57
+ // Rendering primitives
58
+ export {
59
+ drawBadge,
60
+ drawButton,
61
+ drawCheckbox,
62
+ drawGroupIndicator,
63
+ drawProgressBar,
64
+ drawRating,
65
+ drawSparkline,
66
+ } from './primitives';
67
+ // Theme (re-export the DEFAULT_THEME and utilities)
68
+ export {
69
+ createTheme,
70
+ DARK_THEME,
71
+ DEFAULT_THEME,
72
+ getCellBackgroundColor,
73
+ getFontFromTheme,
74
+ getRowTheme,
75
+ getThemePreset,
76
+ mergeTheme,
77
+ THEME_PRESETS,
78
+ } from './theme';
79
+ // Types (base definitions)
80
+ export {
81
+ BlitResult,
82
+ BufferPair,
83
+ CellDrawContext,
84
+ CellWalkCallback,
85
+ ColumnPrepResult,
86
+ ColumnWalkCallback,
87
+ DamageType,
88
+ DirtyRegions,
89
+ GridMouseEvent,
90
+ GridTheme,
91
+ HitTestResult,
92
+ PartialTheme,
93
+ Point,
94
+ PositionedColumn,
95
+ Rectangle,
96
+ RenderState,
97
+ RowWalkCallback,
98
+ ScrollPosition,
99
+ Size,
100
+ VisibleRange,
101
+ } from './types';
102
+ // Walker functions
103
+ export {
104
+ calculateVisibleRange,
105
+ getColumnAtX,
106
+ getColumnIndex,
107
+ getPinnedWidths,
108
+ getPositionedColumns,
109
+ getRowAtY,
110
+ getRowY,
111
+ getTotalColumnWidth,
112
+ getVisibleRowRange,
113
+ isRowVisible,
114
+ walkCells,
115
+ walkColumns,
116
+ walkRows,
117
+ } from './walk';