UNPKG

@true-directive/base

Version:

The set of base classes for the TrueDirective Grid

580 lines (579 loc) 22.6 kB
import { CellPosition } from './cell-position.class'; import { CellRange } from './cell-range.class'; import { Keys } from '../common/keys.class'; import { SelectionMode } from './enums'; /** * Выделенные данные в таблице. * Содержит одну ячейку, на которой установлена фокусировка и набор * прямоугольных областей */ var Selection = /** @class */ (function () { function Selection() { this._lastFocusedField = null; this._focusedCell = null; /** * Список выделенных областей */ this.ranges = []; } Object.defineProperty(Selection.prototype, "lastRange", { /** * Последняя выделенная область * @return Последняя выделенная область, если есть (иначе null) */ get: function () { if (this.ranges.length === 0) { return null; } return this.ranges[this.ranges.length - 1]; }, enumerable: true, configurable: true }); Object.defineProperty(Selection.prototype, "focusedCell", { /** * Позиция ячейки, на которой находится фокус */ get: function () { return this._focusedCell; }, set: function (cp) { var changed = false; if (cp === null || !cp.equals(this._focusedCell)) { changed = true; } this._focusedCell = cp; if (changed) { this.focusChangedEvent(this._focusedCell); } }, enumerable: true, configurable: true }); /** * Добавление области в список выделенных областей * @param range Область */ Selection.prototype.addRange = function (range) { this.ranges.push(range); }; /** * Очистка выделенного. */ Selection.prototype.clear = function () { var oldFocused = this.focusedCell; var oldRangesLength = this.ranges.length; this.focusedCell = null; this.ranges.splice(0, this.ranges.length); if (oldFocused !== null || oldRangesLength !== 0) { return true; } return false; }; /** * Очистка выделенного и излучение события об изменении выделенного */ Selection.prototype.clearAll = function () { if (this.clear()) { this.selectionChangedEvent(null); } }; /** * Начало выделения области пользователем * @param pos Позиция ячейки, с которой начато выделение * @param ctrl Нажат ли Ctrl. Если true, то новая область будет добавлена к * имеющемся. Иначе сначала производится очистка областей. */ Selection.prototype.startSelect = function (pos, ctrl) { if (ctrl === void 0) { ctrl = false; } // Если нажат контрол, то не сбрасываем области, а добавляем.. if (!ctrl) { this.ranges.splice(0, this.ranges.length); } this.focusedCell = pos; this.addRange(new CellRange(pos)); this.selectionChangedEvent(pos); }; Selection.prototype.proceedToSelect = function (pos, scrollToPos) { if (scrollToPos === void 0) { scrollToPos = false; } if (this.lastRange === null) { return; // Some kind of error.. } // Extend the range // Of course, the last var res = this.lastRange.extend(pos); // Scroll to this position is not necessary if (res) { this.selectionChangedEvent(scrollToPos ? pos : null); } return res; }; /** * User finished selection * @param sm Current grid's selectionMode * @return If selection has been changed */ Selection.prototype.endSelect = function (sm) { var changed = false; var res = true; while (res) { res = false; for (var i = this.ranges.length - 1; i > 0; i--) { var del = false; var range = this.ranges[i]; // Check if there is any such range before. for (var j = i - 1; j >= 0; j--) { var prev_range = this.ranges[j]; if (range.equals(prev_range, sm)) { changed = true; del = true; this.ranges.splice(j, 1); break; } } if (del) { res = true; break; } } } // Event if (changed) { this.selectionChangedEvent(null); } return changed; }; /** * Last position of the last range. * @return CellPosition */ Selection.prototype.getLastPos = function () { if (!this.lastRange) { return null; } if (this.lastRange.toCell) { return this.lastRange.toCell; } else { return this.lastRange.fromCell; } }; Selection.prototype.cellPosition = function (row, rowIndex, fieldName, keyField) { var keyValue = null; if (keyField !== '') { keyValue = row[keyField]; } return new CellPosition(row, rowIndex, fieldName, keyValue); }; Selection.prototype.findRow = function (rows, row, keyField) { if (keyField === void 0) { keyField = ''; } if (!rows) { return -1; } var fi = rows.indexOf(row); if (fi < 0 && keyField !== '') { var foundByKey = rows.find(function (r) { return r[keyField] === row[keyField]; }); if (foundByKey) { fi = rows.indexOf(foundByKey); } } return fi; }; /** * Updating indices of the selected rows. * @param rows Source rows list * @param resultRows Resulting rows list * @param keyField Key field name * @return Returns true if indices was changed */ Selection.prototype.updateSelectionIndices = function (rows, resultRows, keyField) { if (keyField === void 0) { keyField = ''; } if (!resultRows) { return true; } // Ничего не изменилось? var changed = false; if (this.focusedCell) { var r = this.focusedCell.row; var fName = this.focusedCell.fieldName; var fi = this.findRow(resultRows, r, keyField); if (fi < 0) { this._lastFocusedField = fName; this.focusedCell.rowIndex = -1; if (this.findRow(rows, r, keyField) < 0) { // Строки нет в исходном наборе this.focusedCell = null; changed = true; } } else { this.focusedCell = this.cellPosition(resultRows[fi], fi, fName, keyField); } } var res = true; while (res) { res = false; var i0 = 0; for (var i = i0; i < this.ranges.length; i++) { var range = this.ranges[i]; var ii = range.fromCell.rowIndex; var ri = this.findRow(resultRows, range.fromCell.row, keyField); // Удаляем, если строка не найдена if (ri < 0) { var found = this.findRow(rows, range.fromCell.row, keyField); if (found < 0) { // Нет в исходном наборе строк. Можно удалить. this.ranges.splice(i, 1); changed = true; i0 = i + 1; res = true; break; } else { // Область не найдена в результирующем наборе строк. // Но есть в исходном. // Поэтому она останется невидимой range.fromCell.rowIndex = -1; changed = true; continue; } } if (range.fromCell.rowIndex !== ri) { range.fromCell.rowIndex = ri; changed = true; } // Если конец области та же самая ячейка, что и начало, то // всё уже сделано. if (range.toCell && range.toCell !== range.fromCell) { var hh = range.toCell.rowIndex - ii; range.toCell.rowIndex = ri + hh; if (range.toCell.rowIndex >= resultRows.length) { range.toCell.rowIndex = resultRows.length - 1; changed = true; } if (range.toCell.row !== resultRows[range.toCell.rowIndex]) { range.toCell.row = resultRows[range.toCell.rowIndex]; changed = true; } } } } return changed; }; // Выделить заданную строку Selection.prototype.selectRow = function (layouts, r, ri, fieldName, keyField) { if (fieldName === void 0) { fieldName = ''; } if (keyField === void 0) { keyField = ''; } if (!fieldName) { fieldName = layouts[0].columns[0].fieldName; } var newPos = this.cellPosition(r, ri, fieldName, keyField); this.clear(); this.focusedCell = newPos; this.addRange(new CellRange(newPos)); this.selectionChangedEvent(newPos); return newPos; }; // Выделить первую строку Selection.prototype.selectFirstRow = function (layouts, rows) { if (!rows || rows.length === 0) { return null; } var newPos = this.selectRow(layouts, rows[0], 0, this._lastFocusedField); this._lastFocusedField = null; this.selectionChangedEvent(newPos); return newPos; }; // Изменение выделенного в соответствии с заданной клавишей Selection.prototype.move = function (layouts, // Список частей грида, чтобы можно было переместиться между ними settings, // Настройки rows, // Отображаемый список строк pageCapacity, // Количество строк, вмещаемых в страницу keyEvent) { var keyCode = keyEvent.keyCode; var shift = keyEvent.shiftKey; var ctrl = keyEvent.ctrlKey; // Ничего не выделено - при нажатии Down - выделяем первую строчку if (!this.focusedCell) { if (keyCode === Keys.DOWN && !shift) { return this.selectFirstRow(layouts, rows); } } var pos; if (shift && settings.canSelectRange() && keyCode !== Keys.TAB) { pos = this.getLastPos(); } else { pos = this.focusedCell; } var newPos = this.movePosition(layouts, pos, rows, pageCapacity, keyCode, shift, ctrl, settings.keyField); if (newPos) { if (shift && settings.canSelectRange() && keyCode !== Keys.TAB) { this.proceedToSelect(newPos, true); } else { // Если вызовем очистку, то событие изменения фокуса сработает два раза this.ranges.splice(0, this.ranges.length); this.focusedCell = newPos; this.ranges.push(new CellRange(newPos)); this.selectionChangedEvent(newPos); } return newPos; } return null; }; // Возващает новую позицию относительно given position Selection.prototype.movePosition = function (layouts, cellPos, rows, pageCapacity, keyCode, shift, ctrl, keyField) { if (!cellPos) { return null; } var ri = cellPos.rowIndex; var f = cellPos.fieldName; var res = false; if (keyCode === Keys.UP && ri > 0) { ri--; res = true; } if (keyCode === Keys.DOWN && ri < (rows.length - 1)) { ri++; res = true; } if (keyCode === Keys.PAGE_DOWN) { ri += pageCapacity.downRowCount; if (ri >= rows.length) { ri = rows.length - 1; } res = true; } if (keyCode === Keys.PAGE_UP) { ri -= pageCapacity.upRowCount; if (ri < 0) { ri = 0; } res = true; } var newF = f; if (keyCode === Keys.RIGHT) { newF = this.nextLayoutField(layouts, f); res = f !== newF; } if (keyCode === Keys.TAB && !shift) { newF = this.nextLayoutField(layouts, f); if (newF !== f) { res = true; } else { if (ri < (rows.length - 1)) { newF = this.firstField(layouts, f); ri++; res = true; } } } if (keyCode === Keys.TAB && shift) { newF = this.prevLayoutField(layouts, f); if (newF !== f) { res = true; } else { if (ri > 0) { newF = this.lastField(layouts, f); ri--; res = true; } } } if (keyCode === Keys.LEFT) { newF = this.prevLayoutField(layouts, f); res = f !== newF; } if (keyCode === Keys.HOME) { if (ctrl) { // В начало таблицы if (ri > 0) { ri = 0; res = true; } } else { // В начало строки newF = this.firstField(layouts, f); res = f !== newF; } } if (keyCode === Keys.END) { // В конец таблицы if (ctrl) { if (ri < (rows.length - 1)) { ri = rows.length - 1; res = true; } } else { // Последнее поле newF = this.lastField(layouts, f); res = f !== newF; } } if (res) { return this.cellPosition(rows[ri], ri, newF, keyField); } return null; }; // Ищет колонку по лэйаутам Selection.prototype.findField = function (layouts, fieldName) { for (var i = 0; i < layouts.length; i++) { for (var j = 0; j < layouts[i].columns.length; j++) { var col = layouts[i].columns[j]; if (col.fieldName === fieldName) { return { layout: i, index: j }; } } } return null; }; // Колонка, следующая за заданной. Сквозь все лэйауты // Если следующего нет, возвращает заданное поле Selection.prototype.nextLayoutField = function (layouts, fieldName) { var res = fieldName; var cPos = this.findField(layouts, fieldName); if (!cPos) { return res; } // Следуюшая колонка этой области if (cPos.index < (layouts[cPos.layout].columns.length - 1)) { res = layouts[cPos.layout].columns[cPos.index + 1].fieldName; } else { // Перебираемся в следующую область if (cPos.layout < (layouts.length - 1) && layouts[cPos.layout + 1].columns.length > 0) { res = layouts[cPos.layout + 1].columns[0].fieldName; } } return res; }; // Колонка, предшествуюшая заданной. Сквозь все лэйауты // Если следующего нет, возвращает заданное поле Selection.prototype.prevLayoutField = function (layouts, fieldName) { var res = fieldName; var cPos = this.findField(layouts, fieldName); if (!cPos) { return res; } if (cPos.index > 0) { // Предыдущая колонка этой области res = layouts[cPos.layout].columns[cPos.index - 1].fieldName; } else { if (cPos.layout > 0 && layouts[cPos.layout - 1].columns.length > 0) { // Перебираемся в следующую область var prevL = layouts[cPos.layout - 1]; res = prevL.columns[prevL.columns.length - 1].fieldName; } } return res; }; // Поле самой первой колонки // Если каким-то чудом нет ни одной колонки - возвращаем fieldName из аргументов Selection.prototype.firstField = function (layouts, fieldName) { for (var i = 0; i < layouts.length; i++) { if (layouts[i].columns.length > 0) { return layouts[i].columns[0].fieldName; } } return fieldName; }; // Поле самой последней колонки // Если каким-то чудом нет ни одной колонки - возвращаем fieldName из аргументов Selection.prototype.lastField = function (layouts, fieldName) { for (var i = layouts.length - 1; i >= 0; i--) { if (layouts[i].columns.length > 0) { return layouts[i].columns[layouts[i].columns.length - 1].fieldName; } } return fieldName; }; Selection.prototype.isSingleCellSelected = function () { if (this.focusedCell !== null && this.ranges.length === 1) { if (this.focusedCell.rowIndex === this.ranges[0].fromCell.rowIndex) { if (this.focusedCell.fieldName === this.ranges[0].fromCell.fieldName) { if (this.ranges[0].toCell === null) { return true; } } } } return false; }; Selection.prototype.selectionChangedEvent = function (cp) { }; Selection.prototype.focusChangedEvent = function (cp) { }; Selection.prototype.columnIndex = function (lc, fieldName) { var c = lc.find(function (col) { return col.fieldName === fieldName; }); return lc.indexOf(c); }; Object.defineProperty(Selection.prototype, "focusedRow", { /** * The row containing a focused cell. */ get: function () { if (this.focusedCell) { return this.focusedCell.row; } return null; }, enumerable: true, configurable: true }); /** * Выделена ли ячейка в заданной позиции ячейки * @param lc Список колонок (по лэйаутам) * @param pos Позиция ячейки * @param st Настройки грида * @return Да или нет */ Selection.prototype.isSelected = function (lc, pos, st) { if (!pos || pos.rowIndex < 0 || lc.length < 1) { return false; } var ii = this.columnIndex(lc, pos.fieldName); for (var _i = 0, _a = this.ranges; _i < _a.length; _i++) { var range = _a[_i]; if (pos.rowIndex < range.fromRow || pos.rowIndex > range.toRow) { continue; } var i1 = this.columnIndex(lc, range.fromField); var i2 = this.columnIndex(lc, range.toField); if (range.fromRow === range.toRow && i1 === i2) { // Одна ячейка if (st.selectionMode === SelectionMode.ROW || st.selectionMode === SelectionMode.ROW_AND_RANGE) { // Значит выделена вся строка return true; } } if (i2 < i1) { var t = i1; i1 = i2; i2 = t; } if (ii >= i1 && ii <= i2) { return true; } } return false; }; /** * Returns value of column in the focused cell * @param c Column * @return Value */ Selection.prototype.focusedValue = function (c) { var v = null; if (this.focusedRow) { v = this.focusedRow[c.fieldName]; } return v; }; return Selection; }()); export { Selection };