fradrive/frontend/src/lib/table/table.js
2022-10-12 09:35:16 +02:00

189 lines
4.8 KiB
JavaScript

// SPDX-FileCopyrightText: 2022 Gregor Kleen <gregor.kleen@ifi.lmu.de>,Sarah Vaupel <sarah.vaupel@ifi.lmu.de>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
const DEBUG_MODE = /localhost/.test(window.location.href) ? 0 : 0;
import defer from 'lodash.defer';
class Overhang {
colSpan;
rowSpan;
cell;
constructor(colSpan, rowSpan, cell) {
this.colSpan = colSpan;
this.rowSpan = rowSpan;
this.cell = cell;
if (new.target === Overhang)
Object.freeze(this);
}
nextLine() {
return new Overhang(this.colSpan, Math.max(0, this.rowSpan - 1), this.cell);
}
reduceCol(n) {
if (this.colSpan > n)
return new Overhang(this.colSpan - n, this.rowSpan, this.cell);
else
return null;
}
isHole() {
return this.rowSpan <= 0;
}
}
const instanceCache = new Map();
export class TableIndices {
_table;
_cellToIndices = new Map();
_indicesToCell = new Array();
colSpan = cell => cell ? Math.max(1, cell.colSpan || 1) : 1;
rowSpan = cell => cell ? Math.max(1, cell.rowSpan || 1) : 1;
maxRow = 0;
maxCol = 0;
constructor(table, overrides) {
const prev = instanceCache.get(table);
if ( prev?.instance &&
overrides?.colSpan === prev.overrides?.colSpan &&
overrides?.rowSpan === prev.overrides?.rowSpan
) {
if (DEBUG_MODE > 0)
console.log('Reusing existing TableIndices', table, overrides, prev);
return prev.instance;
}
if (overrides && overrides.colSpan)
this.colSpan = overrides.colSpan;
if (overrides && overrides.rowSpan)
this.rowSpan = overrides.rowSpan;
this._table = table;
let currentOverhangs = new Array();
let currentRow = 0;
for (const rowParent of this._table.rows) {
let newOverhangs = new Array();
let cellBefore = 0;
for (const cell of rowParent.cells) {
let i;
for (i = 0; i < currentOverhangs.length; i++) {
const overhang = currentOverhangs[i];
if (overhang.isHole())
break;
else
newOverhangs.push(overhang.nextLine());
if (DEBUG_MODE > 1)
console.log('From overhang', overhang);
cellBefore += overhang.colSpan;
}
currentOverhangs = currentOverhangs.slice(i);
let remCols = this.colSpan(cell);
while (remCols > 0 && currentOverhangs[0]) {
let firstOverhang = currentOverhangs[0].reduceCol(this.colSpan(cell));
if (firstOverhang) {
if (DEBUG_MODE > 1)
console.log('Replace first overhang', remCols, currentOverhangs[0], firstOverhang);
currentOverhangs[0] = firstOverhang;
break;
} else {
if (DEBUG_MODE > 1)
console.log('Drop first overhang', remCols, currentOverhangs[0], firstOverhang);
remCols -= currentOverhangs[0].colSpan;
currentOverhangs.shift();
}
}
this._cellToIndices.set(cell, { row: currentRow, col: cellBefore });
let rows = range(currentRow, currentRow + this.rowSpan(cell));
let columns = range(cellBefore, cellBefore + this.colSpan(cell));
if (DEBUG_MODE > 1) {
console.log('Result', rows, columns);
cell.dataset.rows = JSON.stringify(rows);
cell.dataset.columns = JSON.stringify(columns);
}
for (const row of rows) {
for (const col of columns) {
if (!this._indicesToCell[row])
this._indicesToCell[row] = new Array();
this._indicesToCell[row][col] = cell;
this.maxRow = Math.max(row, this.maxRow);
this.maxCol = Math.max(col, this.maxCol);
}
}
newOverhangs.push(new Overhang(this.colSpan(cell), this.rowSpan(cell) - 1, cell));
if (DEBUG_MODE > 1)
console.log('From current cell', this.colSpan(cell));
cellBefore += this.colSpan(cell);
}
currentOverhangs = Array.from(newOverhangs);
currentRow++;
}
if (DEBUG_MODE > 1) {
console.log(this._cellToIndices);
console.table(this._indicesToCell);
}
instanceCache.set(table, { overrides: overrides, instance: this });
defer(() => { instanceCache.delete(table); } );
}
colIndex(cell) {
return this.getIndices(cell)?.col;
}
rowIndex(cell) {
return this.getIndices(cell)?.row;
}
getIndices(cell) {
const res = this._cellToIndices.get(cell);
if (DEBUG_MODE > 2)
console.log('getIndices', cell, res);
return res;
}
getCell(row, col) {
const cell = this._indicesToCell[row]?.[col];
if (DEBUG_MODE > 2)
console.log('getCell', row, col, cell);
return cell;
}
}
function range(from, to) {
return [...Array(to - from).keys()].map(n => n + from);
}