UNPKG

@true-directive/base

Version:

The set of base classes for the TrueDirective Grid

359 lines (358 loc) 14.8 kB
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 };