@true-directive/base
Version:
The set of base classes for the TrueDirective Grid
580 lines (579 loc) • 22.6 kB
JavaScript
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 };