Skip to content

Commit 55588df

Browse files
spike-rabbitMarkColeman1
authored andcommitted
perf: only calculate render offset once for all rows (#210)
1 parent 881add3 commit 55588df

File tree

3 files changed

+84
-166
lines changed

3 files changed

+84
-166
lines changed

projects/swimlane/ngx-datatable/src/lib/components/body/body.component.spec.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -144,20 +144,4 @@ describe('DataTableBodyComponent', () => {
144144
expect(rows[1].classes['row-disabled']).toBeTrue();
145145
});
146146
});
147-
148-
describe('Summary row', () => {
149-
it('should not return custom styles for a bottom summary row if a scrollbar mode is off', () => {
150-
const styles = component.bottomSummaryRowsStyles();
151-
expect(styles).toBeFalsy();
152-
});
153-
154-
it('should return custom styles for a bottom summary row if a scrollbar mode is on', () => {
155-
component.rowHeight = 50;
156-
component.scrollbarV = true;
157-
component.virtualization = true;
158-
component.rows = [{ num: 1 }, { num: 2 }, { num: 3 }, { num: 4 }];
159-
const styles = component.bottomSummaryRowsStyles();
160-
expect(styles).toBeDefined();
161-
});
162-
});
163147
});

projects/swimlane/ngx-datatable/src/lib/components/body/body.component.ts

Lines changed: 84 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
import { ScrollerComponent } from './scroller.component';
1919
import { columnGroupWidths, columnsByPin } from '../../utils/column';
2020
import { RowHeightCache } from '../../utils/row-height-cache';
21-
import { NgStyle, NgTemplateOutlet } from '@angular/common';
21+
import { NgTemplateOutlet } from '@angular/common';
2222
import { DatatableGroupHeaderDirective } from './body-group-header.directive';
2323
import { DatatableRowDetailDirective } from '../row-detail/row-detail.directive';
2424
import { DataTableBodyRowComponent } from './body-row.component';
@@ -118,83 +118,84 @@ import { Keys } from '../../utils/keys';
118118
</datatable-body-row>
119119
</ng-template>
120120
121-
@for (group of rowsToRender(); track rowTrackingFn(i, group); let i = $index) {
122-
@let disabled = isRow(group) && disableRowCheck && disableRowCheck(group);
123-
<!-- $any(group) is needed as the typing is broken and the feature as well. See #147. -->
124-
<!-- FIXME: This has to be revisited and fixed. -->
125-
<datatable-row-wrapper
126-
[attr.hidden]="
127-
ghostLoadingIndicator && (!rowCount || !virtualization || !scrollbarV) ? true : null
128-
"
129-
[groupedRows]="groupedRows"
130-
[innerWidth]="innerWidth"
131-
[ngStyle]="rowsStyles()[i]"
132-
[rowDetail]="rowDetail"
133-
[groupHeader]="groupHeader"
134-
[offsetX]="offsetX"
135-
[detailRowHeight]="getDetailRowHeight(group && $any(group)[i], i)"
136-
[groupHeaderRowHeight]="getGroupHeaderRowHeight(group && $any(group)[i], i)"
137-
[row]="group"
138-
[disabled]="disabled"
139-
[expanded]="getRowExpanded(group)"
140-
[rowIndex]="getRowIndex(group && $any(group)[i])?.index ?? 0"
141-
[selected]="selected"
142-
(rowContextmenu)="rowContextmenu.emit($event)"
143-
>
144-
@if (rowDefTemplate) {
145-
<ng-container
146-
*rowDefInternal="
147-
{
148-
template: rowDefTemplate,
149-
rowTemplate: bodyRow,
150-
row: group,
151-
index: i
152-
};
153-
disabled: disabled
154-
"
155-
/>
156-
} @else {
157-
@if (isRow(group)) {
121+
<div [style.transform]="renderOffset()">
122+
@for (group of rowsToRender(); track rowTrackingFn(i, group); let i = $index) {
123+
@let disabled = isRow(group) && disableRowCheck && disableRowCheck(group);
124+
<!-- $any(group) is needed as the typing is broken and the feature as well. See #147. -->
125+
<!-- FIXME: This has to be revisited and fixed. -->
126+
<datatable-row-wrapper
127+
[attr.hidden]="
128+
ghostLoadingIndicator && (!rowCount || !virtualization || !scrollbarV) ? true : null
129+
"
130+
[groupedRows]="groupedRows"
131+
[innerWidth]="innerWidth"
132+
[style.width]="groupedRows ? columnGroupWidths.total : undefined"
133+
[rowDetail]="rowDetail"
134+
[groupHeader]="groupHeader"
135+
[offsetX]="offsetX"
136+
[detailRowHeight]="getDetailRowHeight(group && $any(group)[i], i)"
137+
[groupHeaderRowHeight]="getGroupHeaderRowHeight(group && $any(group)[i], i)"
138+
[row]="group"
139+
[disabled]="disabled"
140+
[expanded]="getRowExpanded(group)"
141+
[rowIndex]="getRowIndex(group && $any(group)[i])?.index ?? 0"
142+
[selected]="selected"
143+
(rowContextmenu)="rowContextmenu.emit($event)"
144+
>
145+
@if (rowDefTemplate) {
158146
<ng-container
159-
[ngTemplateOutlet]="bodyRow"
160-
[ngTemplateOutletContext]="{
161-
row: group,
162-
index: i,
163-
disabled
164-
}"
165-
></ng-container>
147+
*rowDefInternal="
148+
{
149+
template: rowDefTemplate,
150+
rowTemplate: bodyRow,
151+
row: group,
152+
index: i
153+
};
154+
disabled: disabled
155+
"
156+
/>
157+
} @else {
158+
@if (isRow(group)) {
159+
<ng-container
160+
[ngTemplateOutlet]="bodyRow"
161+
[ngTemplateOutletContext]="{
162+
row: group,
163+
index: i,
164+
disabled
165+
}"
166+
></ng-container>
167+
}
166168
}
167-
}
168169
169-
@if (isGroup(group)) {
170-
<!-- The row typecast is due to angular compiler acting weird. It is obvious that it is of type TRow, but the compiler does not understand. -->
171-
@for (row of group.value; track rowTrackingFn(i, row); let i = $index) {
172-
@let disabled = disableRowCheck && disableRowCheck(row);
173-
<ng-container
174-
[ngTemplateOutlet]="bodyRow"
175-
[ngTemplateOutletContext]="{
176-
row,
177-
groupedRows: group?.value,
178-
index: i,
179-
disabled
180-
}"
181-
></ng-container>
170+
@if (isGroup(group)) {
171+
<!-- The row typecast is due to angular compiler acting weird. It is obvious that it is of type TRow, but the compiler does not understand. -->
172+
@for (row of group.value; track rowTrackingFn(i, row); let i = $index) {
173+
@let disabled = disableRowCheck && disableRowCheck(row);
174+
<ng-container
175+
[ngTemplateOutlet]="bodyRow"
176+
[ngTemplateOutletContext]="{
177+
row,
178+
groupedRows: group?.value,
179+
index: i,
180+
disabled
181+
}"
182+
></ng-container>
183+
}
182184
}
183-
}
184-
</datatable-row-wrapper>
185-
}
186-
@if (summaryRow && summaryPosition === 'bottom') {
187-
<datatable-summary-row
188-
role="row"
189-
[ngStyle]="bottomSummaryRowsStyles()"
190-
[rowHeight]="summaryHeight"
191-
[innerWidth]="innerWidth"
192-
[rows]="rows"
193-
[columns]="columns"
194-
>
195-
</datatable-summary-row>
196-
}
185+
</datatable-row-wrapper>
186+
}
187+
</div>
197188
</datatable-scroller>
189+
@if (summaryRow && summaryPosition === 'bottom') {
190+
<datatable-summary-row
191+
role="row"
192+
[rowHeight]="summaryHeight"
193+
[innerWidth]="innerWidth"
194+
[rows]="rows"
195+
[columns]="columns"
196+
>
197+
</datatable-summary-row>
198+
}
198199
}
199200
@if (!rows?.length && !loadingIndicator && !ghostLoadingIndicator) {
200201
<datatable-scroller
@@ -219,7 +220,6 @@ import { Keys } from '../../utils/keys';
219220
ScrollerComponent,
220221
DataTableSummaryRowComponent,
221222
DataTableRowWrapperComponent,
222-
NgStyle,
223223
DatatableRowDefInternalDirective,
224224
DataTableBodyRowComponent,
225225
DraggableDirective,
@@ -650,77 +650,18 @@ export class DataTableBodyComponent<TRow extends Row = any> implements OnInit, O
650650
};
651651

652652
/**
653-
* Calculates the styles for the row so that the rows can be moved in 2D space
654-
* during virtual scroll inside the DOM. In the below case the Y position is
655-
* manipulated. As an example, if the height of row 0 is 30 px and row 1 is
656-
* 100 px then following styles are generated:
657-
*
658-
* transform: translate3d(0px, 0px, 0px); -> row0
659-
* transform: translate3d(0px, 30px, 0px); -> row1
660-
* transform: translate3d(0px, 130px, 0px); -> row2
661-
*
662-
* Row heights have to be calculated based on the row heights cache as we wont
663-
* be able to determine which row is of what height before hand. In the above
664-
* case the positionY of the translate3d for row2 would be the sum of all the
665-
* heights of the rows before it (i.e. row0 and row1).
666-
*
667-
* @returns the CSS3 style to be applied
668-
*/
669-
rowsStyles = computed(() => {
670-
const rowsStyles: NgStyle['ngStyle'][] = [];
671-
this.rowsToRender().forEach((rows, index) => {
672-
const styles: NgStyle['ngStyle'] = {};
673-
674-
// only add styles for the group if there is a group
675-
if (this.groupedRows) {
676-
styles.width = this.columnGroupWidths.total;
677-
}
678-
679-
if (this.scrollbarV && this.virtualization) {
680-
let idx = 0;
681-
682-
if (Array.isArray(rows)) {
683-
// Get the latest row rowindex in a group
684-
const row = rows[rows.length - 1];
685-
// The group row, which has always a numeric index
686-
idx = row ? this.getRowIndex(row).index : 0;
687-
} else {
688-
if (rows) {
689-
// normal rows always have a numeric index
690-
idx = this.getRowIndex(rows).index;
691-
} else {
692-
// When ghost cells are enabled use index to get the position of them
693-
idx = this.indexes().first + index;
694-
}
695-
}
696-
697-
// const pos = idx * rowHeight;
698-
// The position of this row would be the sum of all row heights
699-
// until the previous row position.
700-
styles.transform = `translateY(${this.rowHeightsCache().query(idx - 1)}px)`;
701-
}
702-
rowsStyles.push(styles);
703-
});
704-
return rowsStyles;
705-
});
706-
707-
/**
708-
* Calculate bottom summary row offset for scrollbar mode.
709-
* For more information about cache and offset calculation
710-
* see description for `rowsStyles` signal
711-
*
712-
* @returns the CSS3 style to be applied
653+
* Calculates the offset of the rendered rows.
654+
* As virtual rows are not shown, we have to move all rendered rows
655+
* by the total size of previous non-rendered rows.
656+
* If each row has a size of 10px and the first 10 rows are not rendered due to scroll,
657+
* then we have a renderOffset of 100px.
713658
*/
714-
bottomSummaryRowsStyles = computed(() => {
715-
if (!this.scrollbarV || !this.rows.length || !this.rowsToRender()) {
716-
return null;
659+
renderOffset = computed(() => {
660+
if (this.scrollbarV && this.virtualization) {
661+
return `translateY(${this.rowHeightsCache().query(this.indexes().first - 1)}px)`;
662+
} else {
663+
return '';
717664
}
718-
719-
const pos = this.rowHeightsCache().query(this.rows.length - 1);
720-
return {
721-
transform: `translateY(${pos}px)`,
722-
position: 'absolute'
723-
};
724665
});
725666

726667
/**

projects/swimlane/ngx-datatable/src/lib/components/datatable.component.scss

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@
2222
.datatable-body {
2323
overflow-y: auto;
2424
}
25-
&.virtualized {
26-
.datatable-body {
27-
.datatable-row-wrapper {
28-
position: absolute;
29-
}
30-
}
31-
}
3225
}
3326

3427
.datatable-body-cell,

0 commit comments

Comments
 (0)