@true-directive/base
Version:
The set of base classes for the TrueDirective Grid
359 lines (358 loc) • 14.8 kB
JavaScript
import { RenderMode } from './classes/enums';
/**
* Хранит высоты строк, пересчитывает позиции строк для рендреа
* при заданной позиции скролла
*/
var RowCalculator = /** @class */ (function () {
function RowCalculator(state) {
this.state = state;
// Стандартная выста строки
this._currentRH = null;
this.currentScrollPos = 0;
// Список реальных высот строк
this._rowHeights = null;
// Минимальная высота
this._minH = null;
// Актуальная общая высота фантомных строк
this.GHOST_START = 0; // До отображаемых строк
this.GHOST_END = 0; // После отображаемых строк
this.TOTAL_HEIGHT = 0;
// Сохраненные фантомные строки. Если позиция прокрутки не изменилась,
// то используем их
this._startRows = [];
this._endRows = [];
// Сохраненные высоты фантомных строк
this._startH = -1;
this._endH = -1;
// Для экстремально длинных списков
this._extraRows = [];
this._extraH = -1;
this.ghostMaxH = 1000000;
}
Object.defineProperty(RowCalculator.prototype, "currentRH", {
get: function () {
if (this._currentRH === null) {
return this.state.settings.rowHeight;
}
return this._currentRH;
},
enumerable: true,
configurable: true
});
Object.defineProperty(RowCalculator.prototype, "minH", {
get: function () {
return this._minH;
},
set: function (v) {
this._minH = v;
if (this._minH !== null &&
this.state.settings.fixedRowHeight) {
// Реально строка больше по высоте...
this._currentRH = this._minH;
}
else {
this._currentRH = this.state.settings.rowHeight;
}
},
enumerable: true,
configurable: true
});
RowCalculator.prototype.firstRowDY = function () {
var dy = -(this.currentScrollPos - this.GHOST_START);
return "translateY(" + dy + "px)";
};
RowCalculator.prototype.firstRowY = function () {
return "translateY(" + this.GHOST_START + "px)";
};
/**
* Список фантомных строк до и после отображаемых строк
* @param pos Позиция фантомных строк (start - в начале таблицы или end - в конце)
*/
RowCalculator.prototype.ghostRows = function (pos) {
var gs = this.GHOST_START;
var ge = this.GHOST_END;
var ex = 0;
if (gs > 30 * 280000) {
gs -= 30 * 280000;
ex = 30 * 280000;
}
var total;
if (pos === 'extra') {
total = ex;
if (total === this._extraH) {
return this._extraRows; // Используем сохраненные
}
}
else {
if (pos === 'start') {
total = gs;
if (total === this._startH) {
return this._startRows; // Используем сохраненные
}
}
else {
total = ge;
if (total === this._endH) {
return this._endRows; // Используем сохраненные
}
}
}
var rows = [];
var rowH = this.ghostMaxH; // Больше чем это IE не прожует
var i = total;
var j = 0;
while (i > 0) {
j++;
if (i > rowH) {
rows.push({ H: rowH, I: j });
i -= rowH;
}
else {
rows.push({ H: i, I: j });
i = 0;
}
}
if (pos === 'extra') {
this._extraH = total;
this._extraRows = rows;
return this._extraRows;
}
else {
if (pos === 'start') {
this._startH = total;
this._startRows = rows;
return this._startRows;
}
else {
this._endH = total;
this._endRows = rows;
return this._endRows;
}
}
};
/**
* Очистка. При любом изменении данных нужно очищать
*/
RowCalculator.prototype.clear = function () {
this._rowHeights = null;
};
/**
* Сохранение реальной высоты строки
* @param rowCount Общее количество строк
* @param index Индекс строки, высота которой сохраняется
* @param h Реальная высота строки
*/
RowCalculator.prototype.setRowHeight = function (rowCount, index, h) {
//
if (this._rowHeights === null) {
this._rowHeights = new Array(rowCount);
}
while (this._rowHeights.length <= index) {
this._rowHeights.push(null);
}
// Сохраняем минимальную высоту
if (this.minH === null || this.minH > h) {
this.minH = h;
}
this._rowHeights[index] = h;
};
/**
* Возврат реальной высоты строки
* @param index Индекс строки в списке отображаемых строк
* @return Реальная высота строки
*/
RowCalculator.prototype.getRowHeight = function (index) {
if (this.state.settings.fixedRowHeight) {
return this.currentRH;
}
if (this._rowHeights === null || this._rowHeights.length <= index) {
return this.currentRH;
}
var res = this._rowHeights[index];
if (res === undefined) {
return this.currentRH;
}
return res;
};
/**
* Возвращает позицию Y строки по заданному индексу
* @param ri Заданный индекс
*/
RowCalculator.prototype.getRowTop = function (ri) {
if (this.state.settings.fixedRowHeight) {
// Просто умножение
return ri * this.currentRH;
}
// Сложение всех предыдущих высот строк
var rowTop = 0;
for (var i = 0; i < ri; i++) {
rowTop += this.getRowHeight(i);
}
return rowTop;
};
/**
* Рассчитывает, сколько строк помещается на экране...
* @param rowIndex Index of the first rendered row
* @param viewPortHeight Высота вьюпорта
* @param rc Number of rows
* @return upRowCount - количество строк, между заданной и строкой, находящейся на одну страницу выше,
* downRowCount - между заданной и строкой, находящейся на одну страницу ниже
*/
RowCalculator.prototype.pageCapacity = function (rowIndex, viewPortHeight, rc) {
// Сколько строк помещается в странице
var res = { upRowCount: 0, downRowCount: 0 };
var dh = 0;
var i = rowIndex;
while (dh < viewPortHeight && i < rc) {
dh += this.getRowHeight(i);
i++; // На одну вниз
}
res.downRowCount = i - rowIndex - 1;
i = rowIndex;
dh = 0;
while (dh < viewPortHeight && i >= 0) {
i--; // На одну вверх
dh += this.getRowHeight(i);
}
res.upRowCount = rowIndex - i - 1;
return res;
};
/**
* Calculates render parameters
* @param rc Number of rows
* @param pos Vertical scroll position
* @param viewPortHeight Height of the visible area
* @param overWork Сколько дополнительных строк нужно рендерить помимо видимых
* @return Информация о рендеринге
*/
RowCalculator.prototype.renderInfo = function (rc, scrollPos, viewPortHeight, overWork) {
this.currentScrollPos = scrollPos;
if (scrollPos < 0) {
scrollPos = 0;
}
if (this.state.settings.renderMode === RenderMode.ALL) {
// Рендерим все строки
return {
beforeRows: 0, beforeHeight: 0,
renderRows: rc,
afterRows: 0, afterHeight: 0
};
}
var overWorkFwd = 4; // Рендерим на четыре строки больше перед видимой областью
var overWorkBack = 4; // И на четыре больше после видимой области
// Но если извне пришли настройки, то принимаем их:
if (overWork) {
overWorkFwd = overWork.fwd;
overWorkBack = overWork.back;
}
// Empty result:
var res = {
totalHeight: 0,
beforeRows: 0, beforeHeight: 0,
renderRows: 0, renderHeight: 0,
afterRows: 0, afterHeight: 0
};
if (rc === 0) {
return res;
}
// При фиксированной высоте строки - быстрые и простые арифметические действия
if (this.state.settings.fixedRowHeight) {
var HH = this.currentRH;
var i0 = Math.floor(scrollPos / HH) - overWorkBack;
var i1 = i0 + Math.floor(viewPortHeight / HH) + overWorkBack + overWorkFwd;
if (i1 >= rc) {
i0 = i0 - (i1 - rc + 1);
i1 = rc - 1;
}
if (i0 < 0) {
i0 = 0;
}
// From indices to numbers:
res.beforeRows = i0;
res.beforeHeight = i0 * HH;
res.renderRows = i1 - i0 + 1;
res.renderHeight = res.renderRows * HH;
res.afterRows = rc - i1 - 1;
res.afterHeight = res.afterRows * HH;
res.totalHeight = res.beforeHeight + res.renderHeight + res.afterHeight;
return res;
}
// Complex scenario...
var i = 0;
var afterCounter = 0;
var last10 = [];
while (i < rc) {
var hh = this.getRowHeight(i);
if ((res.totalHeight + hh) < scrollPos) {
res.beforeRows++;
res.beforeHeight += hh;
// Сохраним последние Х
if (last10.length >= overWorkBack) {
// Здесь можно сделать быстрее без shift. Но размер массива невелик..
last10.shift();
}
last10.push(hh);
}
else if (res.totalHeight < (scrollPos + viewPortHeight)) {
if (res.renderHeight === 0) {
// Только что переключились в эту область
// Перекидывем последние Х записей в рендер
var c10 = last10.length;
var h10 = last10.reduce(function (sum, h) { return sum + h; }, 0);
res.beforeRows -= c10;
res.beforeHeight -= h10;
res.renderRows += c10;
res.renderHeight += h10;
}
res.renderRows++;
res.renderHeight += hh;
}
else {
// Первые dy записей добавляем к рендеру всё же
if (afterCounter < overWorkFwd) {
res.renderRows++;
res.renderHeight += hh;
afterCounter++;
}
else {
res.afterRows++;
res.afterHeight += hh;
}
}
res.totalHeight += hh;
i++;
}
return res;
};
/**
* Обновление информации для рендеринга
* @param rc Количество строк
* @param pos Позиция скролла
* @param viewPortHeight Высота видимой области
* @param overWork Сколько дополнительных строк нужно рендерить помимо видимых
* @return True, если есть изменения и необходимо перерендерить страницу
*/
RowCalculator.prototype.updateRenderInfo = function (rc, pos, viewPortHeight, overWork) {
var ri = this.renderInfo(rc, pos, viewPortHeight, overWork);
this.state.setPage(ri.beforeRows, ri.renderRows); // Отображаемая страница
var pageChanged = false;
if (ri.beforeHeight !== this.GHOST_START || ri.afterHeight !== this.GHOST_END) {
this.GHOST_START = ri.beforeHeight;
this.GHOST_END = ri.afterHeight;
pageChanged = true;
}
this.TOTAL_HEIGHT = ri.totalHeight;
return pageChanged;
};
RowCalculator.prototype.trackGhostRowExtra = function (index, data) {
return data.H + '/' + data.I;
};
RowCalculator.prototype.trackGhostRowStart = function (index, data) {
return data.H + '/' + data.I;
};
RowCalculator.prototype.trackGhostRowEnd = function (index, data) {
return data.H + '/' + data.I;
};
return RowCalculator;
}());
export { RowCalculator };