Files
pulse-signage/src/web/public/js/table/table-sort.js
T
2026-07-26 20:26:01 +01:00

197 lines
6.9 KiB
JavaScript

(function () {
function getTableSortState(table) {
if (!table._tableSortState) {
table._tableSortState = {
sortKey: '',
direction: 'asc'
};
}
return table._tableSortState;
}
function ensureSortableHeaderIndicator(headerCell) {
var indicator = headerCell.querySelector && headerCell.querySelector('.table-sort-indicator');
if (indicator) {
return indicator;
}
indicator = document.createElement('i');
indicator.className = 'table-sort-indicator bi bi-arrow-down-up ms-1';
indicator.setAttribute('aria-hidden', 'true');
headerCell.appendChild(indicator);
return indicator;
}
function updateSortableHeaderState(table) {
var state = getTableSortState(table);
var currentUrl = new URL(window.location.href);
var activeSortKey = String(currentUrl.searchParams.get('sort') || '').trim();
var activeDirection = String(currentUrl.searchParams.get('direction') || '').trim().toLowerCase() === 'desc' ? 'desc' : 'asc';
var headerCells = table.tHead && table.tHead.rows && table.tHead.rows.length ? Array.prototype.slice.call(table.tHead.rows[0].cells || []) : [];
state.sortKey = activeSortKey;
state.direction = activeDirection;
headerCells.forEach(function (headerCell) {
if (!headerCell) {
return;
}
var sortKey = String(headerCell.getAttribute && headerCell.getAttribute('data-table-sort-key') || '').trim();
var sortable = Boolean(sortKey) && String(headerCell.getAttribute('data-sortable') || '').toLowerCase() !== 'false';
headerCell.classList.remove('sort-asc', 'sort-desc', 'sortable', 'unsortable');
headerCell.removeAttribute('aria-sort');
if (!sortable) {
headerCell.classList.add('unsortable');
var hiddenIndicator = headerCell.querySelector && headerCell.querySelector('.table-sort-indicator');
if (hiddenIndicator) {
hiddenIndicator.style.display = 'none';
}
return;
}
var indicator = ensureSortableHeaderIndicator(headerCell);
indicator.style.display = '';
indicator.className = 'table-sort-indicator bi ms-1';
headerCell.classList.add('sortable');
headerCell.setAttribute('role', 'button');
headerCell.setAttribute('tabindex', '0');
if (sortKey === activeSortKey) {
headerCell.classList.add(activeDirection === 'desc' ? 'sort-desc' : 'sort-asc');
headerCell.setAttribute('aria-sort', activeDirection === 'desc' ? 'descending' : 'ascending');
if (activeDirection === 'desc') {
indicator.classList.add('bi-caret-down-fill');
} else {
indicator.classList.add('bi-caret-up-fill');
}
return;
}
headerCell.setAttribute('aria-sort', 'none');
indicator.classList.add('bi-arrow-down-up');
});
}
function buildSortedUrl(sortKey) {
var currentUrl = new URL(window.location.href);
var nextUrl = new URL(window.location.href);
var currentSortKey = String(currentUrl.searchParams.get('sort') || '').trim();
var currentDirection = String(currentUrl.searchParams.get('direction') || '').trim().toLowerCase() === 'desc' ? 'desc' : 'asc';
if (currentSortKey === sortKey) {
nextUrl.searchParams.set('direction', currentDirection === 'asc' ? 'desc' : 'asc');
} else {
nextUrl.searchParams.set('sort', sortKey);
nextUrl.searchParams.set('direction', 'asc');
}
nextUrl.searchParams.delete('page');
return nextUrl;
}
function updateUrlState(nextUrl) {
if (window.history && window.history.replaceState) {
window.history.replaceState({}, document.title, nextUrl.pathname + nextUrl.search + nextUrl.hash);
}
}
function refreshSortedTable(table, nextUrl, requestSequence, sequenceId) {
var container = table ? (table.closest('[data-table-search-container]') || table.closest('.card') || null) : null;
fetch(nextUrl.toString(), {
method: 'GET',
headers: {
'Accept': 'text/html, application/xhtml+xml',
'X-Requested-With': 'XMLHttpRequest'
},
credentials: 'same-origin',
cache: 'no-store'
}).then(function (response) {
if (!response.ok) {
throw new Error('Unable to load sorted results.');
}
return response.text();
}).then(function (text) {
if (sequenceId !== requestSequence.value) {
return;
}
var responseDocument = new DOMParser().parseFromString(text || '', 'text/html');
updateUrlState(nextUrl);
if (window.replaceTableResults && replaceTableResults(container, responseDocument)) {
if (typeof window.initSortableTables === 'function') {
window.initSortableTables(container || document);
}
if (typeof window.initTableSearches === 'function') {
window.initTableSearches(container || document);
}
if (typeof window.initLocalDateTimes === 'function') {
window.initLocalDateTimes(container || document);
}
}
}).catch(function () {
window.location.assign(nextUrl.toString());
});
}
function initSortableTables(root) {
var scope = root && root.querySelectorAll ? root : document;
Array.prototype.forEach.call(scope.querySelectorAll('table[data-table-searchable]'), function (table) {
var headerRow = table.tHead && table.tHead.rows && table.tHead.rows.length ? table.tHead.rows[0] : null;
if (!headerRow) {
return;
}
if (table.getAttribute('data-table-sort-bound') === 'true') {
updateSortableHeaderState(table);
return;
}
table.setAttribute('data-table-sort-bound', 'true');
var requestSequence = { value: 0 };
Array.prototype.forEach.call(headerRow.cells, function (headerCell) {
if (!headerCell) {
return;
}
var sortKey = String(headerCell.getAttribute && headerCell.getAttribute('data-table-sort-key') || '').trim();
var isSortable = Boolean(sortKey) && String(headerCell.getAttribute('data-sortable') || '').toLowerCase() !== 'false';
if (!isSortable) {
return;
}
if (headerCell.getAttribute('data-table-sort-bound') === 'true') {
return;
}
headerCell.setAttribute('data-table-sort-bound', 'true');
ensureSortableHeaderIndicator(headerCell);
headerCell.addEventListener('click', function () {
var nextUrl = buildSortedUrl(sortKey);
requestSequence.value += 1;
var sequenceId = requestSequence.value;
refreshSortedTable(table, nextUrl, requestSequence, sequenceId);
});
headerCell.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
headerCell.click();
}
});
});
updateSortableHeaderState(table);
});
}
window.initSortableTables = initSortableTables;
initSortableTables();
}());