129 lines
4.1 KiB
JavaScript
129 lines
4.1 KiB
JavaScript
// SPDX-FileCopyrightText: 2022 Sarah Vaupel <sarah.vaupel@ifi.lmu.de>
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
import { Utility } from '../../core/utility';
|
|
import { TableIndices } from '../../lib/table/table';
|
|
import { FrontendTooltips } from '../../lib/tooltips/frontend-tooltips';
|
|
import { Translations } from '../../messages';
|
|
|
|
|
|
const CHECKRANGE_INITIALIZED_CLASS = 'checkrange--initialized';
|
|
const CHECKBOX_SELECTOR = '[type="checkbox"]';
|
|
|
|
|
|
@Utility({
|
|
selector: 'table:not([uw-no-check-all])',
|
|
})
|
|
export class CheckRange {
|
|
_lastCheckedCell = null;
|
|
_element;
|
|
_tableIndices;
|
|
_columns = new Array();
|
|
|
|
constructor(element) {
|
|
if(!element) {
|
|
throw new Error('Check Range Utility cannot be setup without an element');
|
|
}
|
|
|
|
this._element = element;
|
|
|
|
if (this._element.classList.contains(CHECKRANGE_INITIALIZED_CLASS))
|
|
return false;
|
|
|
|
this._tableIndices = new TableIndices(this._element);
|
|
|
|
this._gatherColumns();
|
|
|
|
let checkboxColumns = this._findCheckboxColumns();
|
|
|
|
checkboxColumns.forEach(columnId => this._setUpShiftClickOnColumn(columnId));
|
|
|
|
this._element.classList.add(CHECKRANGE_INITIALIZED_CLASS);
|
|
}
|
|
|
|
_setUpShiftClickOnColumn(columnId) {
|
|
if (!this._columns || columnId < 0 || columnId >= this._columns.length) return;
|
|
let column = this._columns[columnId];
|
|
let language = document.documentElement.lang;
|
|
let toolTipMessage = Translations.getTranslation('checkrangeTooltip', language);
|
|
FrontendTooltips.addToolTip(column[0], toolTipMessage);
|
|
column.forEach(el => el.addEventListener('click', (ev) => {
|
|
|
|
if(ev.shiftKey && this.lastCheckedCell !== null) {
|
|
let lastClickedIndex = this._tableIndices.rowIndex(this._lastCheckedCell);
|
|
let currentCellIndex = this._tableIndices.rowIndex(el);
|
|
let cell = this._columns[columnId][currentCellIndex];
|
|
if(currentCellIndex > lastClickedIndex)
|
|
this._handleCellsInBetween(cell, lastClickedIndex, currentCellIndex, columnId);
|
|
else
|
|
this._handleCellsInBetween(cell, currentCellIndex, lastClickedIndex, columnId);
|
|
} else {
|
|
this._lastCheckedCell = el;
|
|
}
|
|
}));
|
|
}
|
|
|
|
_handleCellsInBetween(cell, firstRowIndex, lastRowIndex, columnId) {
|
|
if(this._isChecked(cell)) {
|
|
this._uncheckMultipleCells(firstRowIndex, lastRowIndex, columnId);
|
|
} else {
|
|
this._checkMultipleCells(firstRowIndex, lastRowIndex, columnId);
|
|
}
|
|
}
|
|
|
|
_checkMultipleCells(firstRowIndex, lastRowIndex, columnId) {
|
|
for(let i=firstRowIndex; i<=lastRowIndex; i++) {
|
|
let cell = this._columns[columnId][i];
|
|
if (cell.tagName !== 'TH') {
|
|
cell.querySelector(CHECKBOX_SELECTOR).checked = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
_uncheckMultipleCells(firstRowIndex, lastRowIndex, columnId) {
|
|
for(let i=firstRowIndex; i<=lastRowIndex; i++) {
|
|
let cell = this._columns[columnId][i];
|
|
if (cell.tagName !== 'TH') {
|
|
cell.querySelector(CHECKBOX_SELECTOR).checked = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
_isChecked(cell) {
|
|
return cell.querySelector(CHECKBOX_SELECTOR).checked;
|
|
}
|
|
|
|
|
|
_gatherColumns() {
|
|
for (const rowIndex of Array(this._tableIndices.maxRow + 1).keys()) {
|
|
for (const colIndex of Array(this._tableIndices.maxCol + 1).keys()) {
|
|
|
|
const cell = this._tableIndices.getCell(rowIndex, colIndex);
|
|
|
|
if (!cell)
|
|
continue;
|
|
|
|
if (!this._columns[colIndex])
|
|
this._columns[colIndex] = new Array();
|
|
|
|
this._columns[colIndex][rowIndex] = cell;
|
|
}
|
|
}
|
|
}
|
|
|
|
_findCheckboxColumns() {
|
|
let checkboxColumnIds = new Array();
|
|
this._columns.forEach((col, i) => {
|
|
if (this._isCheckboxColumn(col)) {
|
|
checkboxColumnIds.push(i);
|
|
}
|
|
});
|
|
return checkboxColumnIds;
|
|
}
|
|
|
|
_isCheckboxColumn(col) {
|
|
return col.every(cell => cell.tagName == 'TH' || cell.querySelector(CHECKBOX_SELECTOR))
|
|
&& col.some(cell => cell.querySelector(CHECKBOX_SELECTOR));
|
|
}
|
|
}
|