@progress/kendo-angular-pivotgrid
Version:
PivotGrid package for Angular
129 lines (128 loc) • 6.04 kB
JavaScript
/**-----------------------------------------------------------------------------------------
* Copyright © 2025 Progress Software Corporation. All rights reserved.
* Licensed under commercial license. See LICENSE.md in the project root for more information
*-------------------------------------------------------------------------------------------*/
/**
* @hidden
*/
export class ScrollableTable {
element;
handlers;
options;
startRow = 0;
endRow;
startCol = 0;
endCol;
renderedRows;
renderedCols;
total;
totalCols;
rtl;
visibleRows;
visibleCols;
lastKnownScrollPosition = { top: 0, left: 0 };
offsetFirst = 0;
stretcher;
rect;
constructor(element, handlers, options) {
this.element = element;
this.handlers = handlers;
this.options = options;
this.initialize();
}
onNewData(recalculateSize = false) {
this.offsetFirst = this.startRow * this.options.itemHeight;
this.renderedRows = Math.min(this.options.renderedRows || Math.ceil(this.visibleRows * 3), this.total);
this.endRow = this.startRow + this.renderedRows;
this.renderedCols = Math.min(this.options.renderedCols || Math.ceil(this.visibleCols * 1.3), this.totalCols);
this.element.querySelector('table').style.transform = `translateY(${this.offsetFirst}px)`;
recalculateSize && this.recalculateSize();
}
destroy() {
this.element.removeEventListener('scroll', this.scrollHandler);
this.element.removeEventListener('scrollend', this.scrollEndHandler);
this.element.removeChild(this.stretcher);
}
initialize() {
this.rtl = this.options.rtl;
this.rect = this.element.getBoundingClientRect();
// visible rows and cols
this.visibleRows = Math.ceil(this.rect.height / this.options.itemHeight);
this.visibleCols = Math.ceil(this.rect.width / this.options.itemWidth);
// current totals
this.total = this.options.total;
this.totalCols = this.options.totalCols;
const totalHeight = this.total * this.options.itemHeight;
const totalWidth = this.totalCols * this.options.itemWidth;
// "page" size (rows and cols)
this.renderedRows = Math.min(this.options.renderedRows || Math.ceil(this.visibleRows * 3), this.total);
this.renderedCols = Math.min(this.options.renderedCols || Math.ceil(this.visibleCols * 1.3), this.totalCols);
// start and end row/col
this.startRow = 0;
this.startCol = 0;
this.endRow = this.startRow + this.renderedRows;
this.endCol = this.startCol + this.renderedCols;
// element that ensures correct scrolling dimensions of the container
this.stretcher = document.createElement('DIV');
this.stretcher.style.height = `${totalHeight}px`;
this.stretcher.style.width = `${totalWidth}px`;
this.element.appendChild(this.stretcher);
this.element.addEventListener('scroll', this.scrollHandler);
this.element.addEventListener('scrollend', this.scrollEndHandler);
}
scrollHandler = () => {
const verticalDir = this.element.scrollTop - this.lastKnownScrollPosition.top;
const horizontalDir = Math.abs(this.element.scrollLeft) - this.lastKnownScrollPosition.left;
if (this.options.rowVirtualization) {
if (verticalDir > 0) {
if (this.element.scrollTop > (this.renderedRows * this.options.itemHeight + this.offsetFirst - this.rect.height)) {
this.startRow = Math.floor(this.element.scrollTop / this.options.itemHeight);
this.rowVirtualizationUpdate();
}
}
else {
if (this.element.scrollTop <= this.offsetFirst) {
this.startRow = Math.max(0, Math.ceil(this.element.scrollTop / this.options.itemHeight) - Math.ceil(this.options.renderedRows / 3));
this.rowVirtualizationUpdate();
}
}
}
if (this.options.columnVirtualization) {
if (horizontalDir > 0) {
if (Math.abs(this.element.scrollLeft) - (Math.max(this.startCol - 1, 0) * (this.options.colWidth || 200)) > (this.options.colWidth || 200)) {
this.startCol = Math.min(Math.max(Math.floor(Math.abs(this.element.scrollLeft) / this.options.itemWidth) - 1, 0), this.totalCols - this.renderedCols);
this.handlers.onScroll();
}
}
else {
if (Math.abs(this.element.scrollLeft) <= (this.startCol + 1) * (this.options.colWidth || 200)) {
this.startCol = Math.min(Math.max(Math.floor(Math.abs(this.element.scrollLeft) / this.options.itemWidth) - 1, 0), this.totalCols - this.renderedCols);
this.handlers.onScroll();
}
}
}
this.lastKnownScrollPosition = {
top: this.element.scrollTop,
left: Math.abs(this.element.scrollLeft)
};
};
scrollEndHandler = () => {
this.handlers.onScrollEnd();
};
recalculateSize() {
const totalHeight = this.total * this.options.itemHeight;
const totalWidth = this.totalCols * this.options.itemWidth;
this.stretcher.style.height = `${totalHeight}px`;
this.stretcher.style.width = `${totalWidth}px`;
this.rect = this.element.getBoundingClientRect();
// visible rows and cols
this.visibleRows = Math.ceil(this.rect.height / this.options.itemHeight);
this.visibleCols = Math.ceil(this.rect.width / this.options.itemWidth);
}
rowVirtualizationUpdate() {
this.endRow = Math.min(this.startRow + this.renderedRows, this.total);
this.offsetFirst = this.startRow * this.options.itemHeight;
this.element.querySelector('table').style.transform = `translateY(${this.offsetFirst}px)`;
this.handlers.onScroll();
}
}