argent-grid 0.2.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.
- package/AGENTS.md +70 -27
- package/e2e/advanced.spec.ts +1 -1
- package/e2e/benchmark.spec.ts +7 -7
- package/e2e/cell-renderers.spec.ts +152 -0
- package/e2e/debug-streaming.spec.ts +31 -0
- package/e2e/dnd.spec.ts +73 -0
- package/e2e/screenshots.spec.ts +1 -1
- package/e2e/visual.spec.ts +30 -9
- package/e2e/visual.spec.ts-snapshots/checkbox-renderer-mixed.png +0 -0
- package/e2e/visual.spec.ts-snapshots/debug.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-column-group-headers.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-default.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-empty-state.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-filter-popup.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-scroll-borders.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-sidebar-buttons.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-text-filter.png +0 -0
- package/e2e/visual.spec.ts-snapshots/grid-with-selection.png +0 -0
- package/e2e/visual.spec.ts-snapshots/rating-renderer-varied.png +0 -0
- package/package.json +5 -5
- package/plan.md +30 -34
- package/src/lib/components/argent-grid.component.css +258 -549
- package/src/lib/components/argent-grid.component.html +272 -306
- package/src/lib/components/argent-grid.component.ts +585 -135
- package/src/lib/components/argent-grid.regressions.spec.ts +301 -0
- package/src/lib/components/argent-grid.selection.spec.ts +2 -2
- package/src/lib/components/set-filter/set-filter.component.spec.ts +191 -0
- package/src/lib/components/set-filter/set-filter.component.ts +7 -2
- package/src/lib/rendering/canvas-renderer.spec.ts +148 -1
- package/src/lib/rendering/canvas-renderer.ts +177 -286
- package/src/lib/rendering/render/cells.ts +122 -5
- package/src/lib/rendering/render/column-utils.ts +27 -5
- package/src/lib/rendering/render/hit-test.ts +6 -11
- package/src/lib/rendering/render/index.ts +15 -6
- package/src/lib/rendering/render/lines.ts +12 -6
- package/src/lib/rendering/render/primitives.ts +269 -7
- package/src/lib/rendering/render/types.ts +2 -1
- package/src/lib/rendering/render/walk.ts +39 -19
- package/src/lib/services/grid.service.spec.ts +76 -0
- package/src/lib/services/grid.service.ts +451 -114
- package/src/lib/themes/theme-quartz.ts +2 -2
- package/src/lib/types/ag-grid-types.ts +500 -0
- package/src/stories/Advanced.stories.ts +78 -17
- package/src/stories/ArgentGrid.stories.ts +50 -26
- package/src/stories/Benchmark.stories.ts +17 -15
- package/src/stories/CellRenderers.stories.ts +205 -31
- package/src/stories/Filtering.stories.ts +56 -16
- package/src/stories/Grouping.stories.ts +86 -13
- package/src/stories/Streaming.stories.ts +57 -0
- package/src/stories/Theming.stories.ts +23 -10
- package/src/stories/Tooltips.stories.ts +381 -0
- package/src/stories/benchmark-wrapper.component.ts +69 -29
- package/src/stories/story-utils.ts +88 -0
- package/src/stories/streaming-wrapper.component.ts +441 -0
- package/tsconfig.json +1 -0
|
@@ -25,15 +25,18 @@ import {
|
|
|
25
25
|
export function walkColumns(
|
|
26
26
|
columns: Column[],
|
|
27
27
|
scrollX: number,
|
|
28
|
-
viewportWidth: number,
|
|
28
|
+
viewportWidth: number, // Total width of the container
|
|
29
29
|
leftPinnedWidth: number,
|
|
30
30
|
rightPinnedWidth: number,
|
|
31
|
-
callback: ColumnWalkCallback
|
|
31
|
+
callback: ColumnWalkCallback,
|
|
32
|
+
availableWidth?: number // Width excluding vertical scrollbar
|
|
32
33
|
): void {
|
|
33
34
|
const leftPinned = columns.filter((c) => c.pinned === 'left');
|
|
34
35
|
const rightPinned = columns.filter((c) => c.pinned === 'right');
|
|
35
36
|
const centerColumns = columns.filter((c) => !c.pinned);
|
|
36
37
|
|
|
38
|
+
const effectiveWidth = availableWidth ?? viewportWidth;
|
|
39
|
+
|
|
37
40
|
// 1. Left pinned columns (no scroll offset)
|
|
38
41
|
let x = 0;
|
|
39
42
|
for (const col of leftPinned) {
|
|
@@ -44,7 +47,7 @@ export function walkColumns(
|
|
|
44
47
|
|
|
45
48
|
// 2. Center columns (with scroll offset and clipping)
|
|
46
49
|
const centerStartX = Math.floor(leftPinnedWidth);
|
|
47
|
-
const centerEndX = Math.floor(
|
|
50
|
+
const centerEndX = Math.floor(effectiveWidth - rightPinnedWidth);
|
|
48
51
|
|
|
49
52
|
x = centerStartX - scrollX;
|
|
50
53
|
for (const col of centerColumns) {
|
|
@@ -79,7 +82,8 @@ export function getPositionedColumns(
|
|
|
79
82
|
scrollX: number,
|
|
80
83
|
viewportWidth: number,
|
|
81
84
|
leftPinnedWidth: number,
|
|
82
|
-
rightPinnedWidth: number
|
|
85
|
+
rightPinnedWidth: number,
|
|
86
|
+
availableWidth?: number
|
|
83
87
|
): PositionedColumn[] {
|
|
84
88
|
const result: PositionedColumn[] = [];
|
|
85
89
|
|
|
@@ -91,7 +95,8 @@ export function getPositionedColumns(
|
|
|
91
95
|
rightPinnedWidth,
|
|
92
96
|
(column, x, width, isPinned, pinSide) => {
|
|
93
97
|
result.push({ column, x, width, isPinned, pinSide });
|
|
94
|
-
}
|
|
98
|
+
},
|
|
99
|
+
availableWidth
|
|
95
100
|
);
|
|
96
101
|
|
|
97
102
|
return result;
|
|
@@ -148,9 +153,15 @@ export function getVisibleRowRange(
|
|
|
148
153
|
api?: GridApi
|
|
149
154
|
): { startRow: number; endRow: number } {
|
|
150
155
|
if (api) {
|
|
151
|
-
const startRow = Math.max(0, api.getRowAtY(scrollTop) - buffer);
|
|
152
|
-
const endRow = Math.min(
|
|
153
|
-
|
|
156
|
+
const startRow = Math.max(0, Math.min(totalRowCount - 1, api.getRowAtY(scrollTop)) - buffer);
|
|
157
|
+
const endRow = Math.min(
|
|
158
|
+
totalRowCount,
|
|
159
|
+
api.getRowAtY(Math.max(0, scrollTop + viewportHeight)) + buffer + 1
|
|
160
|
+
);
|
|
161
|
+
return {
|
|
162
|
+
startRow: Math.max(0, startRow),
|
|
163
|
+
endRow: Math.max(0, Math.min(totalRowCount, endRow)),
|
|
164
|
+
};
|
|
154
165
|
}
|
|
155
166
|
|
|
156
167
|
const startRow = Math.max(0, Math.floor(scrollTop / rowHeight) - buffer);
|
|
@@ -214,9 +225,11 @@ export function getColumnAtX(
|
|
|
214
225
|
columns: Column[],
|
|
215
226
|
x: number,
|
|
216
227
|
scrollX: number,
|
|
217
|
-
viewportWidth: number
|
|
228
|
+
viewportWidth: number,
|
|
229
|
+
availableWidth?: number
|
|
218
230
|
): { column: Column | null; index: number; localX: number } {
|
|
219
231
|
const { left: leftPinnedWidth, right: rightPinnedWidth } = getPinnedWidths(columns);
|
|
232
|
+
const effectiveWidth = availableWidth ?? viewportWidth;
|
|
220
233
|
|
|
221
234
|
const leftPinned = columns.filter((c) => c.pinned === 'left');
|
|
222
235
|
const rightPinned = columns.filter((c) => c.pinned === 'right');
|
|
@@ -235,8 +248,9 @@ export function getColumnAtX(
|
|
|
235
248
|
}
|
|
236
249
|
|
|
237
250
|
// Check right pinned
|
|
238
|
-
|
|
239
|
-
|
|
251
|
+
const centerEndX = Math.floor(effectiveWidth - rightPinnedWidth);
|
|
252
|
+
if (x > centerEndX) {
|
|
253
|
+
let colX = centerEndX;
|
|
240
254
|
for (let i = 0; i < rightPinned.length; i++) {
|
|
241
255
|
const col = rightPinned[i];
|
|
242
256
|
if (x < colX + col.width) {
|
|
@@ -271,7 +285,7 @@ export function getColumnIndex(columns: Column[], colId: string): number {
|
|
|
271
285
|
* Calculate total width of columns
|
|
272
286
|
*/
|
|
273
287
|
export function getTotalColumnWidth(columns: Column[]): number {
|
|
274
|
-
return columns.reduce((sum, col) => sum + col.width, 0);
|
|
288
|
+
return columns.reduce((sum, col) => sum + Math.floor(col.width), 0);
|
|
275
289
|
}
|
|
276
290
|
|
|
277
291
|
// ============================================================================
|
|
@@ -316,7 +330,8 @@ export function calculateVisibleRange(
|
|
|
316
330
|
viewportHeight: number,
|
|
317
331
|
rowHeight: number,
|
|
318
332
|
totalRowCount: number,
|
|
319
|
-
rowBuffer: number = 5
|
|
333
|
+
rowBuffer: number = 5,
|
|
334
|
+
availableWidth?: number
|
|
320
335
|
): VisibleRange {
|
|
321
336
|
const { startRow, endRow } = getVisibleRowRange(
|
|
322
337
|
scrollTop,
|
|
@@ -326,13 +341,15 @@ export function calculateVisibleRange(
|
|
|
326
341
|
rowBuffer
|
|
327
342
|
);
|
|
328
343
|
|
|
344
|
+
const effectiveWidth = availableWidth ?? viewportWidth;
|
|
345
|
+
|
|
329
346
|
// For columns, we just track indices
|
|
330
347
|
const centerColumns = columns.filter((c) => !c.pinned);
|
|
331
348
|
const leftPinned = columns.filter((c) => c.pinned === 'left');
|
|
332
349
|
const rightPinned = columns.filter((c) => c.pinned === 'right');
|
|
333
350
|
|
|
334
|
-
const leftPinnedWidth = leftPinned.reduce((sum, c) => sum + c.width, 0);
|
|
335
|
-
const rightPinnedWidth = rightPinned.reduce((sum, c) => sum + c.width, 0);
|
|
351
|
+
const leftPinnedWidth = leftPinned.reduce((sum, c) => sum + Math.floor(c.width), 0);
|
|
352
|
+
const rightPinnedWidth = rightPinned.reduce((sum, c) => sum + Math.floor(c.width), 0);
|
|
336
353
|
|
|
337
354
|
// Find first and last visible center column
|
|
338
355
|
let startColumnIndex = leftPinned.length;
|
|
@@ -341,21 +358,24 @@ export function calculateVisibleRange(
|
|
|
341
358
|
let x = leftPinnedWidth - scrollLeft;
|
|
342
359
|
for (let i = 0; i < centerColumns.length; i++) {
|
|
343
360
|
const col = centerColumns[i];
|
|
344
|
-
|
|
361
|
+
const width = Math.floor(col.width);
|
|
362
|
+
if (x + width > leftPinnedWidth) {
|
|
345
363
|
startColumnIndex = leftPinned.length + i;
|
|
346
364
|
break;
|
|
347
365
|
}
|
|
348
|
-
x +=
|
|
366
|
+
x += width;
|
|
349
367
|
}
|
|
350
368
|
|
|
351
369
|
x = leftPinnedWidth - scrollLeft;
|
|
370
|
+
const centerEndX = Math.floor(effectiveWidth - rightPinnedWidth);
|
|
352
371
|
for (let i = 0; i < centerColumns.length; i++) {
|
|
353
372
|
const col = centerColumns[i];
|
|
354
|
-
|
|
373
|
+
const width = Math.floor(col.width);
|
|
374
|
+
if (x > centerEndX) {
|
|
355
375
|
endColumnIndex = leftPinned.length + i;
|
|
356
376
|
break;
|
|
357
377
|
}
|
|
358
|
-
x +=
|
|
378
|
+
x += width;
|
|
359
379
|
}
|
|
360
380
|
|
|
361
381
|
// Add right pinned columns
|
|
@@ -2000,4 +2000,80 @@ describe('GridService', () => {
|
|
|
2000
2000
|
});
|
|
2001
2001
|
});
|
|
2002
2002
|
});
|
|
2003
|
+
|
|
2004
|
+
describe('Bug Fixes & Regression Protection', () => {
|
|
2005
|
+
it('should only emit optionChanged if the value actually changed', () => {
|
|
2006
|
+
const stateChangeSpy = vi.fn();
|
|
2007
|
+
api.setGridOption('rowHeight', 40); // Initial set
|
|
2008
|
+
service.gridStateChanged$.subscribe(stateChangeSpy);
|
|
2009
|
+
|
|
2010
|
+
api.setGridOption('rowHeight', 40); // Same value
|
|
2011
|
+
expect(stateChangeSpy).not.toHaveBeenCalled();
|
|
2012
|
+
|
|
2013
|
+
api.setGridOption('rowHeight', 50); // Different value
|
|
2014
|
+
expect(stateChangeSpy).toHaveBeenCalledWith(
|
|
2015
|
+
expect.objectContaining({
|
|
2016
|
+
type: 'optionChanged',
|
|
2017
|
+
key: 'rowHeight',
|
|
2018
|
+
value: 50,
|
|
2019
|
+
})
|
|
2020
|
+
);
|
|
2021
|
+
});
|
|
2022
|
+
|
|
2023
|
+
it('should respect groupDefaultExpanded option', () => {
|
|
2024
|
+
const groupingApi = service.createApi(
|
|
2025
|
+
[{ field: 'name', rowGroup: true }, { field: 'age' }],
|
|
2026
|
+
testRowData,
|
|
2027
|
+
{ groupDefaultExpanded: 1 }
|
|
2028
|
+
);
|
|
2029
|
+
|
|
2030
|
+
const rowCount = groupingApi.getDisplayedRowCount();
|
|
2031
|
+
// 2 groups (John, Jane, Bob are unique in testRowData name field) + 3 rows = 6 rows total if all expanded
|
|
2032
|
+
// Wait, testRowData has 3 unique names. So 3 groups + 3 rows = 6.
|
|
2033
|
+
expect(rowCount).toBe(6);
|
|
2034
|
+
|
|
2035
|
+
const firstNode = groupingApi.getDisplayedRowAtIndex(0);
|
|
2036
|
+
expect(firstNode?.group).toBe(true);
|
|
2037
|
+
expect(firstNode?.expanded).toBe(true);
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
it('should allow collapsing a group even when groupDefaultExpanded is set', () => {
|
|
2041
|
+
const groupingApi = service.createApi(
|
|
2042
|
+
[{ field: 'name', rowGroup: true }, { field: 'age' }],
|
|
2043
|
+
testRowData,
|
|
2044
|
+
{ groupDefaultExpanded: 1 }
|
|
2045
|
+
);
|
|
2046
|
+
|
|
2047
|
+
expect(groupingApi.getDisplayedRowCount()).toBe(6);
|
|
2048
|
+
|
|
2049
|
+
const firstGroup = groupingApi.getDisplayedRowAtIndex(0);
|
|
2050
|
+
expect(firstGroup?.group).toBe(true);
|
|
2051
|
+
expect(firstGroup?.expanded).toBe(true);
|
|
2052
|
+
|
|
2053
|
+
// Collapse it
|
|
2054
|
+
groupingApi.setRowNodeExpanded(firstGroup!, false);
|
|
2055
|
+
|
|
2056
|
+
expect(firstGroup?.expanded).toBe(false);
|
|
2057
|
+
// 3 groups + 3 rows - 1 child of first group = 5
|
|
2058
|
+
expect(groupingApi.getDisplayedRowCount()).toBe(5);
|
|
2059
|
+
});
|
|
2060
|
+
|
|
2061
|
+
it('should remove ag-Grid-AutoColumn when grouping is disabled', () => {
|
|
2062
|
+
api.addRowGroupColumn('name');
|
|
2063
|
+
expect(api.getAllColumns().find((c) => c.colId === 'ag-Grid-AutoColumn')).toBeDefined();
|
|
2064
|
+
|
|
2065
|
+
api.removeRowGroupColumn('name');
|
|
2066
|
+
expect(api.getAllColumns().find((c) => c.colId === 'ag-Grid-AutoColumn')).toBeUndefined();
|
|
2067
|
+
});
|
|
2068
|
+
|
|
2069
|
+
it('should remove ag-Grid-SelectionColumn when checkbox selection is disabled', () => {
|
|
2070
|
+
api.setColumnDefs([{ field: 'id', checkboxSelection: true }, { field: 'name' }]);
|
|
2071
|
+
expect(api.getAllColumns().find((c) => c.colId === 'ag-Grid-SelectionColumn')).toBeDefined();
|
|
2072
|
+
|
|
2073
|
+
api.setColumnDefs([{ field: 'id', checkboxSelection: false }, { field: 'name' }]);
|
|
2074
|
+
expect(
|
|
2075
|
+
api.getAllColumns().find((c) => c.colId === 'ag-Grid-SelectionColumn')
|
|
2076
|
+
).toBeUndefined();
|
|
2077
|
+
});
|
|
2078
|
+
});
|
|
2003
2079
|
});
|