Add trade fair column settings modal with visibility controls
This commit is contained in:
@@ -2,11 +2,19 @@
|
||||
const tableBody = document.getElementById('tradeFairTableBody');
|
||||
const searchInput = document.getElementById('tradeFairSearchInput');
|
||||
const meta = document.getElementById('tradeFairMeta');
|
||||
const columnSettingsBtn = document.getElementById('tradeFairColumnSettingsBtn');
|
||||
const columnsModal = document.getElementById('tradeFairColumnsModal');
|
||||
const columnsModalBackdrop = document.getElementById('tradeFairColumnsModalBackdrop');
|
||||
const columnsModalClose = document.getElementById('tradeFairColumnsModalClose');
|
||||
const columnsModalDone = document.getElementById('tradeFairColumnsDoneBtn');
|
||||
const columnsModalReset = document.getElementById('tradeFairColumnsResetBtn');
|
||||
const columnsList = document.getElementById('tradeFairColumnsList');
|
||||
const sortButtons = Array.from(document.querySelectorAll('.bookmark-subpage__sort[data-trade-sort]'));
|
||||
const table = tableBody ? tableBody.closest('table') : null;
|
||||
const headerRow = table ? table.querySelector('thead tr') : null;
|
||||
const SORT_STATE_KEY = 'fb_trade_fairs_sort_v1';
|
||||
const COLUMN_ORDER_STATE_KEY = 'fb_trade_fairs_columns_v1';
|
||||
const COLUMN_VISIBILITY_STATE_KEY = 'fb_trade_fairs_column_visibility_v1';
|
||||
const LAST_OPEN_STATE_KEY = 'fb_trade_fairs_last_open_v1';
|
||||
const DEFAULT_COLUMN_ORDER = [
|
||||
'tage_bis_start',
|
||||
@@ -33,12 +41,14 @@
|
||||
}
|
||||
|
||||
const headerCellsByKey = new Map();
|
||||
const columnLabelByKey = new Map();
|
||||
sortButtons.forEach((button) => {
|
||||
const key = button.dataset.tradeSort;
|
||||
const th = button.closest('th');
|
||||
if (!key || !th) {
|
||||
return;
|
||||
}
|
||||
columnLabelByKey.set(key, button.textContent.trim());
|
||||
th.dataset.columnKey = key;
|
||||
th.draggable = true;
|
||||
headerCellsByKey.set(key, th);
|
||||
@@ -586,8 +596,6 @@
|
||||
rang: index + 1
|
||||
}));
|
||||
|
||||
const HIDDEN_FREE_FAIRS_COUNT = TRADE_FAIRS.length - EFFECTIVE_TRADE_FAIRS.length;
|
||||
|
||||
const SORT_TYPES = {
|
||||
tage_bis_start: 'number',
|
||||
rang: 'number',
|
||||
@@ -613,6 +621,7 @@
|
||||
let searchTerm = '';
|
||||
let draggedColumnKey = null;
|
||||
let lastOpenedByTradeFair = {};
|
||||
let columnVisibility = Object.fromEntries(DEFAULT_COLUMN_ORDER.map((key) => [key, true]));
|
||||
|
||||
function toNumber(value) {
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
@@ -807,6 +816,251 @@
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeColumnVisibility(rawState) {
|
||||
if (!rawState || typeof rawState !== 'object' || Array.isArray(rawState)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const normalized = {};
|
||||
DEFAULT_COLUMN_ORDER.forEach((key) => {
|
||||
const value = rawState[key];
|
||||
normalized[key] = value !== false;
|
||||
});
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function loadColumnVisibility() {
|
||||
try {
|
||||
const raw = localStorage.getItem(COLUMN_VISIBILITY_STATE_KEY);
|
||||
if (!raw) {
|
||||
return;
|
||||
}
|
||||
const parsed = JSON.parse(raw);
|
||||
const normalized = normalizeColumnVisibility(parsed);
|
||||
if (!normalized) {
|
||||
return;
|
||||
}
|
||||
columnVisibility = normalized;
|
||||
} catch (_error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function persistColumnVisibility() {
|
||||
try {
|
||||
localStorage.setItem(COLUMN_VISIBILITY_STATE_KEY, JSON.stringify(columnVisibility));
|
||||
} catch (_error) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
function isColumnVisible(columnKey) {
|
||||
return columnVisibility[columnKey] !== false;
|
||||
}
|
||||
|
||||
function getVisibleColumnOrder() {
|
||||
const order = getColumnOrderFromHeader().filter((columnKey) => isColumnVisible(columnKey));
|
||||
if (order.length) {
|
||||
return order;
|
||||
}
|
||||
return ['messe'];
|
||||
}
|
||||
|
||||
function ensureValidSortColumn() {
|
||||
if (isColumnVisible(sortKey)) {
|
||||
return;
|
||||
}
|
||||
const visibleColumns = getVisibleColumnOrder();
|
||||
if (visibleColumns.includes('rang')) {
|
||||
sortKey = 'rang';
|
||||
sortDirection = 'asc';
|
||||
persistSortState();
|
||||
return;
|
||||
}
|
||||
sortKey = visibleColumns[0] || 'messe';
|
||||
sortDirection = 'asc';
|
||||
persistSortState();
|
||||
}
|
||||
|
||||
function applyColumnVisibilityToHeader() {
|
||||
headerCellsByKey.forEach((th, key) => {
|
||||
const visible = isColumnVisible(key);
|
||||
th.hidden = !visible;
|
||||
th.style.display = visible ? '' : 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function getColumnLabel(columnKey) {
|
||||
const fromBase = sortButtons.find((button) => button.dataset.tradeSort === columnKey)?.dataset.baseLabel;
|
||||
if (fromBase) {
|
||||
return fromBase;
|
||||
}
|
||||
return columnLabelByKey.get(columnKey) || columnKey;
|
||||
}
|
||||
|
||||
function moveColumnInOrder(columnKey, direction) {
|
||||
const order = getColumnOrderFromHeader();
|
||||
const index = order.indexOf(columnKey);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
const targetIndex = direction < 0 ? index - 1 : index + 1;
|
||||
if (targetIndex < 0 || targetIndex >= order.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
const nextOrder = [...order];
|
||||
const [item] = nextOrder.splice(index, 1);
|
||||
nextOrder.splice(targetIndex, 0, item);
|
||||
applyColumnOrder(nextOrder);
|
||||
persistColumnOrder();
|
||||
applyColumnVisibilityToHeader();
|
||||
render();
|
||||
renderColumnSettingsList();
|
||||
}
|
||||
|
||||
function setColumnVisibility(columnKey, visible) {
|
||||
const nextState = {
|
||||
...columnVisibility,
|
||||
[columnKey]: visible
|
||||
};
|
||||
const visibleCount = DEFAULT_COLUMN_ORDER.filter((key) => nextState[key] !== false).length;
|
||||
if (visibleCount === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
columnVisibility = nextState;
|
||||
persistColumnVisibility();
|
||||
applyColumnVisibilityToHeader();
|
||||
ensureValidSortColumn();
|
||||
updateSortButtonState();
|
||||
render();
|
||||
return true;
|
||||
}
|
||||
|
||||
function renderColumnSettingsList() {
|
||||
if (!columnsList) {
|
||||
return;
|
||||
}
|
||||
|
||||
const order = getColumnOrderFromHeader();
|
||||
columnsList.innerHTML = '';
|
||||
order.forEach((columnKey, index) => {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'bookmark-columns-modal__item';
|
||||
|
||||
const label = document.createElement('label');
|
||||
label.className = 'bookmark-columns-modal__toggle';
|
||||
const checkbox = document.createElement('input');
|
||||
checkbox.type = 'checkbox';
|
||||
checkbox.checked = isColumnVisible(columnKey);
|
||||
checkbox.addEventListener('change', () => {
|
||||
const ok = setColumnVisibility(columnKey, checkbox.checked);
|
||||
if (!ok) {
|
||||
checkbox.checked = true;
|
||||
return;
|
||||
}
|
||||
renderColumnSettingsList();
|
||||
});
|
||||
const text = document.createElement('span');
|
||||
text.textContent = getColumnLabel(columnKey);
|
||||
label.append(checkbox, text);
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'bookmark-columns-modal__item-actions';
|
||||
const upButton = document.createElement('button');
|
||||
upButton.type = 'button';
|
||||
upButton.className = 'bookmark-columns-modal__move';
|
||||
upButton.textContent = '↑';
|
||||
upButton.title = 'Nach oben';
|
||||
upButton.disabled = index === 0;
|
||||
upButton.addEventListener('click', () => {
|
||||
moveColumnInOrder(columnKey, -1);
|
||||
});
|
||||
|
||||
const downButton = document.createElement('button');
|
||||
downButton.type = 'button';
|
||||
downButton.className = 'bookmark-columns-modal__move';
|
||||
downButton.textContent = '↓';
|
||||
downButton.title = 'Nach unten';
|
||||
downButton.disabled = index === order.length - 1;
|
||||
downButton.addEventListener('click', () => {
|
||||
moveColumnInOrder(columnKey, 1);
|
||||
});
|
||||
|
||||
actions.append(upButton, downButton);
|
||||
item.append(label, actions);
|
||||
columnsList.appendChild(item);
|
||||
});
|
||||
}
|
||||
|
||||
function closeColumnSettingsModal() {
|
||||
if (!columnsModal) {
|
||||
return;
|
||||
}
|
||||
columnsModal.hidden = true;
|
||||
}
|
||||
|
||||
function openColumnSettingsModal() {
|
||||
if (!columnsModal) {
|
||||
return;
|
||||
}
|
||||
renderColumnSettingsList();
|
||||
columnsModal.hidden = false;
|
||||
}
|
||||
|
||||
function resetColumnSettings() {
|
||||
applyColumnOrder(DEFAULT_COLUMN_ORDER);
|
||||
columnVisibility = Object.fromEntries(DEFAULT_COLUMN_ORDER.map((key) => [key, true]));
|
||||
persistColumnOrder();
|
||||
persistColumnVisibility();
|
||||
applyColumnVisibilityToHeader();
|
||||
ensureValidSortColumn();
|
||||
updateSortButtonState();
|
||||
render();
|
||||
renderColumnSettingsList();
|
||||
}
|
||||
|
||||
function setupColumnSettingsModal() {
|
||||
if (!columnSettingsBtn || !columnsModal) {
|
||||
return;
|
||||
}
|
||||
|
||||
columnSettingsBtn.addEventListener('click', () => {
|
||||
openColumnSettingsModal();
|
||||
});
|
||||
|
||||
if (columnsModalBackdrop) {
|
||||
columnsModalBackdrop.addEventListener('click', () => {
|
||||
closeColumnSettingsModal();
|
||||
});
|
||||
}
|
||||
|
||||
if (columnsModalClose) {
|
||||
columnsModalClose.addEventListener('click', () => {
|
||||
closeColumnSettingsModal();
|
||||
});
|
||||
}
|
||||
|
||||
if (columnsModalDone) {
|
||||
columnsModalDone.addEventListener('click', () => {
|
||||
closeColumnSettingsModal();
|
||||
});
|
||||
}
|
||||
|
||||
if (columnsModalReset) {
|
||||
columnsModalReset.addEventListener('click', () => {
|
||||
resetColumnSettings();
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.key === 'Escape' && columnsModal && !columnsModal.hidden) {
|
||||
closeColumnSettingsModal();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeLastOpenedState(rawState) {
|
||||
if (!rawState || typeof rawState !== 'object' || Array.isArray(rawState)) {
|
||||
return {};
|
||||
@@ -1007,6 +1261,7 @@
|
||||
clearDropMarkers();
|
||||
persistColumnOrder();
|
||||
render();
|
||||
renderColumnSettingsList();
|
||||
});
|
||||
|
||||
th.addEventListener('dragend', () => {
|
||||
@@ -1183,14 +1438,14 @@
|
||||
const normalizedRows = EFFECTIVE_TRADE_FAIRS.map(normalizeRow);
|
||||
const filtered = normalizedRows.filter((row) => matchesSearch(row, searchTerm));
|
||||
const sorted = sortRows(filtered);
|
||||
const columnOrder = getColumnOrderFromHeader();
|
||||
const visibleColumnOrder = getVisibleColumnOrder();
|
||||
|
||||
tableBody.innerHTML = '';
|
||||
|
||||
if (!sorted.length) {
|
||||
const emptyRow = document.createElement('tr');
|
||||
const emptyCell = document.createElement('td');
|
||||
emptyCell.colSpan = columnOrder.length;
|
||||
emptyCell.colSpan = visibleColumnOrder.length;
|
||||
emptyCell.textContent = 'Keine Messe zum Suchbegriff gefunden.';
|
||||
emptyRow.appendChild(emptyCell);
|
||||
tableBody.appendChild(emptyRow);
|
||||
@@ -1198,7 +1453,7 @@
|
||||
sorted.forEach((row) => {
|
||||
const tr = document.createElement('tr');
|
||||
const rowCells = createRowCells(row);
|
||||
columnOrder.forEach((columnKey) => {
|
||||
visibleColumnOrder.forEach((columnKey) => {
|
||||
const cell = rowCells[columnKey] || createCell('k.A.', columnKey);
|
||||
tr.appendChild(cell);
|
||||
});
|
||||
@@ -1209,10 +1464,7 @@
|
||||
|
||||
const activeSortButton = sortButtons.find((button) => button.dataset.tradeSort === sortKey);
|
||||
const sortLabel = activeSortButton?.dataset.baseLabel || sortKey;
|
||||
const hiddenInfo = HIDDEN_FREE_FAIRS_COUNT > 0
|
||||
? ` | ${HIDDEN_FREE_FAIRS_COUNT} kostenlose Messe${HIDDEN_FREE_FAIRS_COUNT === 1 ? '' : 'n'} ausgeblendet`
|
||||
: '';
|
||||
meta.textContent = `${sorted.length} von ${EFFECTIVE_TRADE_FAIRS.length} Messen${hiddenInfo} | Sortierung: ${sortLabel} (${sortDirection === 'asc' ? 'aufsteigend' : 'absteigend'})`;
|
||||
meta.textContent = `${sorted.length} von ${EFFECTIVE_TRADE_FAIRS.length} Messen | Sortierung: ${sortLabel} (${sortDirection === 'asc' ? 'aufsteigend' : 'absteigend'})`;
|
||||
}
|
||||
|
||||
sortButtons.forEach((button) => {
|
||||
@@ -1248,8 +1500,12 @@
|
||||
|
||||
lastOpenedByTradeFair = loadLastOpenedState();
|
||||
loadColumnOrder();
|
||||
loadColumnVisibility();
|
||||
applyColumnVisibilityToHeader();
|
||||
setupColumnDragAndDrop();
|
||||
setupColumnSettingsModal();
|
||||
loadSortState();
|
||||
ensureValidSortColumn();
|
||||
updateSortButtonState();
|
||||
render();
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user