journal-media-vue/public/sortable-table.js
gcch 11fa3d1558
Some checks failed
ci/woodpecker/push/publish_instable Pipeline failed
2025-03-03
2025-03-03 17:28:33 +01:00

167 lines
4.3 KiB
JavaScript

/*
* This content is licensed according to the W3C Software License at
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
*
* File: sortable-table.js
*
* Desc: Adds sorting to a HTML data table that implements ARIA Authoring Practices
*/
"use strict";
class SortableTable {
constructor(tableNode) {
this.tableNode = tableNode;
this.columnHeaders = tableNode.querySelectorAll("thead th");
this.sortColumns = [];
for (var i = 0; i < this.columnHeaders.length; i++) {
var ch = this.columnHeaders[i];
var buttonNode = ch.querySelector("button");
if (buttonNode) {
this.sortColumns.push(i);
buttonNode.setAttribute("data-column-index", i);
buttonNode.addEventListener("click", this.handleClick.bind(this));
}
}
this.optionCheckbox = document.querySelector(
"input[type=\"checkbox\"][value=\"show-unsorted-icon\"]",
);
if (this.optionCheckbox) {
this.optionCheckbox.addEventListener(
"change",
this.handleOptionChange.bind(this),
);
if (this.optionCheckbox.checked) {
this.tableNode.classList.add("show-unsorted-icon");
}
}
}
handleClick(event) {
var tgt = event.currentTarget;
this.setColumnHeaderSort(tgt.getAttribute("data-column-index"));
}
handleOptionChange(event) {
var tgt = event.currentTarget;
if (tgt.checked) {
this.tableNode.classList.add("show-unsorted-icon");
} else {
this.tableNode.classList.remove("show-unsorted-icon");
}
}
/* EVENT HANDLERS */
setColumnHeaderSort(columnIndex) {
if (typeof columnIndex === "string") {
columnIndex = parseInt(columnIndex);
}
for (var i = 0; i < this.columnHeaders.length; i++) {
var ch = this.columnHeaders[i];
var buttonNode = ch.querySelector("button");
if (i === columnIndex) {
var value = ch.getAttribute("aria-sort");
if (value === "descending") {
ch.setAttribute("aria-sort", "ascending");
this.sortColumn(
columnIndex,
"ascending",
ch.classList.contains("num"),
);
} else {
ch.setAttribute("aria-sort", "descending");
this.sortColumn(
columnIndex,
"descending",
ch.classList.contains("num"),
);
}
} else {
if (ch.hasAttribute("aria-sort") && buttonNode) {
ch.removeAttribute("aria-sort");
}
}
}
}
sortColumn(columnIndex, sortValue, isNumber) {
function compareValues(a, b) {
if (sortValue === "ascending") {
if (a.value === b.value) {
return 0;
} else {
if (isNumber) {
return a.value - b.value;
} else {
return a.value < b.value ? -1 : 1;
}
}
} else {
if (a.value === b.value) {
return 0;
} else {
if (isNumber) {
return b.value - a.value;
} else {
return a.value > b.value ? -1 : 1;
}
}
}
}
if (typeof isNumber !== "boolean") {
isNumber = false;
}
var tbodyNode = this.tableNode.querySelector("tbody");
var rowNodes = [];
var dataCells = [];
var rowNode = tbodyNode.firstElementChild;
var index = 0;
while (rowNode) {
rowNodes.push(rowNode);
var rowCells = rowNode.querySelectorAll("th, td");
var dataCell = rowCells[columnIndex];
var data = {};
data.index = index;
data.value = dataCell.textContent.toLowerCase().trim();
if (isNumber) {
data.value = parseFloat(data.value);
}
dataCells.push(data);
rowNode = rowNode.nextElementSibling;
index += 1;
}
dataCells.sort(compareValues);
// remove rows
while (tbodyNode.firstChild) {
tbodyNode.removeChild(tbodyNode.lastChild);
}
// add sorted rows
for (var i = 0; i < dataCells.length; i += 1) {
tbodyNode.appendChild(rowNodes[dataCells[i].index]);
}
}
}
// Initialize sortable table buttons
window.addEventListener("load", function() {
var sortableTables = document.querySelectorAll("table.sortable");
for (var i = 0; i < sortableTables.length; i++) {
new SortableTable(sortableTables[i]);
}
});