Skip to content

Commit 644fee5

Browse files
committed
feat: Implement hybrid cell pooling for Grid Rows (#8992)
1 parent b7f2d9e commit 644fee5

2 files changed

Lines changed: 110 additions & 33 deletions

File tree

src/grid/Body.mjs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ class GridBody extends Container {
7777
* @reactive
7878
*/
7979
bufferRowRange_: 3,
80+
/**
81+
* The pool size for recyclable cells.
82+
* Auto-calculated based on mounted columns range.
83+
* @member {Number} cellPoolSize_=20
84+
* @protected
85+
* @reactive
86+
*/
87+
cellPoolSize_: 20,
8088
/**
8189
* Define which model field contains the value of colspan definitions
8290
* @member {String} colspanField='colspan'
@@ -671,7 +679,16 @@ class GridBody extends Container {
671679
* @returns {String}
672680
*/
673681
getCellId(rowIndex, dataField) {
674-
return this.getRowId(rowIndex) + '__' + dataField
682+
let me = this,
683+
column = me.getColumn(dataField),
684+
columnIndex = me.getColumn(dataField, true),
685+
rowId = me.getRowId(rowIndex);
686+
687+
if (column.hideMode === 'removeDom') {
688+
return `${rowId}__cell-${columnIndex % me.cellPoolSize}`
689+
}
690+
691+
return `${rowId}__${dataField}`
675692
}
676693

677694
/**
@@ -1051,12 +1068,12 @@ class GridBody extends Container {
10511068
*/
10521069
updateMountedAndVisibleColumns() {
10531070
let me = this,
1054-
{bufferColumnRange, columnPositions, mountedColumns, visibleColumns} = me,
1071+
{bufferColumnRange, cellPoolSize, columnPositions, mountedColumns, visibleColumns} = me,
10551072
i = 0,
10561073
countColumns = columnPositions.getCount(),
10571074
endIndex = countColumns - 1,
10581075
x = me.scrollLeft,
1059-
column, startIndex;
1076+
column, newPoolSize, startIndex;
10601077

10611078
if (countColumns < 1) {
10621079
return
@@ -1082,7 +1099,14 @@ class GridBody extends Container {
10821099
startIndex = Math.max(0, visibleColumns[0] - bufferColumnRange);
10831100
endIndex = Math.min(countColumns - 1, visibleColumns[1] + bufferColumnRange);
10841101

1085-
me.mountedColumns = [startIndex, endIndex]
1102+
if (endIndex - startIndex >= cellPoolSize) {
1103+
newPoolSize = endIndex - startIndex + 5;
1104+
}
1105+
1106+
me.set({
1107+
cellPoolSize : newPoolSize || cellPoolSize,
1108+
mountedColumns: [startIndex, endIndex]
1109+
})
10861110
}
10871111
}
10881112

src/grid/Row.mjs

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -239,46 +239,99 @@ class Row extends Component {
239239

240240
vdom.cls = rowCls;
241241

242-
for (i=0; i < countColumns; i++) {
243-
isMounted = i >= mountedColumns[0] && i <= mountedColumns[1];
244-
column = columns.getAt(i);
242+
// Pass 1: Render Pooled Cells (hideMode === 'removeDom')
243+
// We only render these if they are within the mounted range.
244+
// This loop is O(Viewport) - highly efficient.
245+
for (i=mountedColumns[0]; i <= mountedColumns[1]; i++) {
246+
column = columns.getAt(i);
247+
248+
// Sanity check for bounds (e.g. if column count changed)
249+
if (!column) continue;
250+
251+
if (column.hideMode === 'removeDom') {
252+
cellConfig = me.applyRendererOutput({
253+
cellId : `${me.id}__cell-${i % gridBody.cellPoolSize}`,
254+
column,
255+
columnIndex: i,
256+
record,
257+
rowIndex,
258+
silent
259+
});
260+
261+
if (column.dock) {
262+
cellConfig.cls = ['neo-locked', ...cellConfig.cls || []]
263+
}
245264

246-
if (!isMounted && column.hideMode === 'removeDom') {
247-
continue
248-
}
265+
columnPosition = gridBody.columnPositions.get(column.dataField);
249266

250-
cellConfig = me.applyRendererOutput({column, columnIndex: i, record, rowIndex, silent});
267+
if (!columnPosition) {
268+
continue
269+
}
251270

252-
if (column.dock) {
253-
cellConfig.cls = ['neo-locked', ...cellConfig.cls || []]
254-
}
271+
cellConfig.style = {
272+
...cellConfig.style,
273+
left : columnPosition.x + 'px',
274+
width: columnPosition.width + 'px'
275+
};
255276

256-
columnPosition = gridBody.columnPositions.get(column.dataField);
277+
// Happens during a column header drag OP, when leaving the painted range
278+
if (columnPosition.hidden) {
279+
cellConfig.style.visibility = 'hidden'
280+
}
257281

258-
if (!columnPosition) {
259-
continue
282+
vdom.cn.push(cellConfig)
260283
}
284+
}
261285

262-
cellConfig.style = {
263-
...cellConfig.style,
264-
left : columnPosition.x + 'px',
265-
width: columnPosition.width + 'px'
266-
};
286+
// Pass 2: Render Permanent Cells (hideMode !== 'removeDom')
287+
// We MUST render these even if they are off-screen to preserve their DOM state (e.g. Canvas context).
288+
// This loop is O(TotalColumns), but typically few columns use this mode.
289+
for (i=0; i < countColumns; i++) {
290+
column = columns.getAt(i);
291+
292+
if (column.hideMode !== 'removeDom') {
293+
isMounted = i >= mountedColumns[0] && i <= mountedColumns[1];
294+
295+
cellConfig = me.applyRendererOutput({
296+
cellId : `${me.id}__${column.dataField}`,
297+
column,
298+
columnIndex: i,
299+
record,
300+
rowIndex,
301+
silent
302+
});
303+
304+
if (column.dock) {
305+
cellConfig.cls = ['neo-locked', ...cellConfig.cls || []]
306+
}
267307

268-
// Happens during a column header drag OP, when leaving the painted range
269-
if (isMounted) {
270-
if (columnPosition.hidden) {
271-
cellConfig.style.visibility = 'hidden'
308+
columnPosition = gridBody.columnPositions.get(column.dataField);
309+
310+
if (!columnPosition) {
311+
continue
272312
}
273-
} else {
274-
if (column.hideMode === 'visibility') {
275-
cellConfig.style.visibility = 'hidden'
276-
} else if (column.hideMode === 'display') {
277-
cellConfig.style.display = 'none'
313+
314+
cellConfig.style = {
315+
...cellConfig.style,
316+
left : columnPosition.x + 'px',
317+
width: columnPosition.width + 'px'
318+
};
319+
320+
// Visibility Logic
321+
if (isMounted) {
322+
if (columnPosition.hidden) {
323+
cellConfig.style.visibility = 'hidden'
324+
}
325+
} else {
326+
if (column.hideMode === 'visibility') {
327+
cellConfig.style.visibility = 'hidden'
328+
} else if (column.hideMode === 'display') {
329+
cellConfig.style.display = 'none'
330+
}
278331
}
279-
}
280332

281-
vdom.cn.push(cellConfig)
333+
vdom.cn.push(cellConfig)
334+
}
282335
}
283336

284337
!silent && me.update()

0 commit comments

Comments
 (0)