diff --git a/frontend/src/lib/storage-manager/storage-manager.js b/frontend/src/lib/storage-manager/storage-manager.js index cb82a0006..8297627fa 100644 --- a/frontend/src/lib/storage-manager/storage-manager.js +++ b/frontend/src/lib/storage-manager/storage-manager.js @@ -50,6 +50,26 @@ export class StorageManager { } } + remove(key, options) { + if (options && options.lifetime !== undefined && !Object.values(LIFETIME).includes(options.lifetime)) { + throw new Error('StorageManager.load called with unsupported lifetime option'); + } + + const lifetime = options && options.lifetime !== undefined ? options.lifetime : LIFETIME.INFINITE; + + switch (lifetime) { + case LIFETIME.INFINITE: { + var val = this.getFromLocalStorage(); + + delete val[key]; + + return this.saveToLocalStorage(val); + } + default: + console.error('StorageManager.load cannot load item with unsupported lifetime'); + } + } + getFromLocalStorage() { const state = JSON.parse(window.localStorage.getItem(this.namespace)); if (state === null) { @@ -67,5 +87,4 @@ export class StorageManager { clearLocalStorage() { window.localStorage.removeItem(this.namespace); } - } diff --git a/frontend/src/utils/hide-columns/hide-columns.js b/frontend/src/utils/hide-columns/hide-columns.js index e10627a71..c4a1f6f0f 100644 --- a/frontend/src/utils/hide-columns/hide-columns.js +++ b/frontend/src/utils/hide-columns/hide-columns.js @@ -15,6 +15,7 @@ const TABLE_HIDER_VISIBLE_CLASS = 'table-hider--visible'; const TABLE_PILL_CLASS = 'table-pill'; const CELL_HIDDEN_CLASS = 'hide-columns--hidden-cell'; +const CELL_ORIGINAL_COLSPAN = 'uw-hide-column-original-colspan'; @Utility({ selector: `[${HIDE_COLUMNS_CONTAINER_IDENT}] table`, @@ -121,7 +122,7 @@ export class HideColumns { // reposition hider on each window resize event window.addEventListener('resize', () => this.repositionHider(hider)); - this.updateColumnDisplay(th.cellIndex, preHidden); + this.updateColumnDisplay(this.colIndex(th), preHidden); this.updateHider(hider, preHidden); if (preHidden) { @@ -135,22 +136,46 @@ export class HideColumns { switchColumnDisplay(th, hider) { const hidden = !this.isHiddenColumn(th); + const originalColspan = Math.max(1, th.getAttribute(CELL_ORIGINAL_COLSPAN)) || 1; + const colspan = Math.max(1, th.colSpan) || 1; + const columnIndex = this.colIndex(th); - this.updateColumnDisplay(th.cellIndex, hidden); + for (var i = 0; i < Math.max(colspan, originalColspan); i++) { + this.updateColumnDisplay(columnIndex + i, hidden); + } this.updateHider(hider, hidden); // persist new hidden setting for column - this._storageManager.save(this.getStorageKey(th), hidden); + if (hidden && this.isEmptyColumn(columnIndex)) { + this._storageManager.remove(this.getStorageKey(th)); + } else { + this._storageManager.save(this.getStorageKey(th), hidden); + } } updateColumnDisplay(columnIndex, hidden) { this._element.getElementsByTagName('tr').forEach(row => { - const cell = row.cells[columnIndex]; + const cell = this.getCol(row, columnIndex); + if (cell) { + const originalColspan = cell.getAttribute(CELL_ORIGINAL_COLSPAN); + const colspan = Math.max(1, cell.colSpan) || 1; + if (hidden) { - cell.classList.add(CELL_HIDDEN_CLASS); + if (colspan > 1) { + if (!originalColspan) { + cell.setAttribute(CELL_ORIGINAL_COLSPAN, colspan); + } + cell.colSpan--; + } else { + cell.classList.add(CELL_HIDDEN_CLASS); + } } else { - cell.classList.remove(CELL_HIDDEN_CLASS); + if (cell.classList.contains(CELL_HIDDEN_CLASS)) { + cell.classList.remove(CELL_HIDDEN_CLASS); + } else if (originalColspan && colspan < originalColspan) { + cell.colSpan++; + } } } }); @@ -215,20 +240,16 @@ export class HideColumns { // check for unique table header ident from backend (if not present, use cell index as fallback) let thIdent = th.getAttribute(TABLE_HEADER_IDENT); if (!thIdent) { - thIdent = th.cellIndex; + thIdent = this.colIndex(th); } return `${handlerIdent}__${tIdent}__${thIdent}`; } isEmptyColumn(columnIndex) { - for (let row of this._element.getElementsByTagName('TR')) { - if (row.children.length <= columnIndex) { - return; - } - - const cell = row.children[columnIndex]; - if (cell.tagName === 'TH') + for (let row of this._element.getElementsByTagName('tr')) { + const cell = this.getCol(row, columnIndex); + if (cell.matches('th')) continue; if (cell.querySelector('.table__td-content')) { for (let child of cell.children) { @@ -244,10 +265,47 @@ export class HideColumns { isHiddenColumn(th) { const hidden = this._storageManager.load(this.getStorageKey(th)), - emptyColumn = this.isEmptyColumn(th.cellIndex); + emptyColumn = this.isEmptyColumn(this.colIndex(th)); return hidden === true || hidden === undefined && emptyColumn; } + colSpan(cell) { + if (!cell) + return 1; + + const originalColspan = cell.getAttribute(CELL_ORIGINAL_COLSPAN); + const colspan = Math.max(1, cell.colSpan) || 1; + + return originalColspan ? Math.max(colspan, originalColspan) : colspan; + } + + colIndex(cell) { + if (!cell) + return 0; + + const rowParent = cell.closest('tr'); + + if (!rowParent) + return 0; + + var i = 0; + for (const sibling of Array.from(rowParent.cells).slice(0, cell.cellIndex)) { + i += this.colSpan(sibling); + } + + return i; + } + + getCol(row, columnIndex) { + var c = 0; + + for (const cell of row.cells) { + c += cell ? this.colSpan(cell) : 1; + + if (columnIndex < c) + return cell; + } + } } function isEmptyElement(element) {