Skip to content

Commit

Permalink
Make the class reference table sortable (#28)
Browse files Browse the repository at this point in the history
The main table's headers are now clickable to sort columns in ascending or descending order.
String columns are sorted alphabetically, description columns are sorted by MISSING vs. OK, and numeric columns are sorted by relative percentages.
A custom JS sorting implementation is used, both to avoid a large dependency and to handle the unique format of these table entries.
The cursor when hovering over table headers indicates that they can be interacted with.
  • Loading branch information
TechnoPorg authored Oct 11, 2023
1 parent 4fc119a commit c9714e9
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 0 deletions.
1 change: 1 addition & 0 deletions layouts/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<title>{{ .Site.Title }}</title>
<link rel="icon" href="favicon.png">
<link rel="stylesheet" href="main.css">
<script src="sorttable.js"></script>
</head>
<body>
{{ .Content }}
Expand Down
1 change: 1 addition & 0 deletions static/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ th {
position: sticky;
z-index: 1; /* Show on top of table cells. */
top: 0; /* Stick to the top of the screen. */
cursor: pointer; /* Visually hint that headers can be interacted with. */
}
th:first-child {
border-left: 1px solid var(--table-sticky-background-color); /* Fixes left border during scroll; must have a valid color, transparent doesn't work. */
Expand Down
102 changes: 102 additions & 0 deletions static/sorttable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
// Make the class reference table sortable in ascending or descending order
// when a header is clicked.


// Helper function to return the content from the idx-th cell of row tr
const getCellValue = (tr, idx) => tr.children[idx].textContent;

// Array sorting functions for different columns used by the comparer

// Compares the Desc. and Brief Desc. columns
// "MISSING" comes first in ascending order
const descriptionComparer = function(v1, v2) {
if(v1 == v2) return 0
if(v1 == "OK") return 1
return -1
}

// Compares the Name and Docs URL columns using a basic string sort
const stringComparer = (new Intl.Collator()).compare

// Compares the Overall column by completion percentage
const overallComparer = function(v1, v2) {
return Number(v1.replace("%", "")) - Number(v2.replace("%", ""))
}

// Compares the other columns (constructors, methods, members, etc.)
// by the percentage they're finished.
// If two have the same percentage, they're compared by denominator size.
const fractionComparer = (asc) => function(v1, v2) {
if(v1 == v2) return 0

// Always send 0/0 values to the bottom
// The "asc" parameter is needed for that purpose.
if(v1 == "0/0") {
return asc ? 1 : -1
}

if(v2 == "0/0") {
return asc ? -1 : 1
}

var v1fraction = v1.split("/")
var v2fraction = v2.split("/")

var v1decimal = Number(v1fraction[0]) / Number(v1fraction[1])
var v2decimal = Number(v2fraction[0]) / Number(v2fraction[1])
if(v1decimal == v2decimal) return v1fraction[1] - v2fraction[1]

return v1decimal - v2decimal
}

// Returns a function responsible for sorting a specific table column
// (column = column object, asc = ascending order?).
const comparer = function(column, asc) {

// This is used by the array.sort() function...
return function(a, b) {
const colIdx = Array.from(column.parentNode.children).indexOf(column)
const colName = column.textContent

// Select a function based on which column is being called.
var columnComparer
switch(colName) {
case "Brief Desc.":
case "Desc.":
columnComparer = descriptionComparer
break
case "Name":
case "Docs URL":
columnComparer = stringComparer
break
case "Overall":
columnComparer = overallComparer
break
default:
columnComparer = fractionComparer(column.asc)
break
}

// Switch the order of the values depending on whether it's an ascending or descending sort.
return columnComparer(getCellValue(asc ? a : b, colIdx), getCellValue(asc ? b : a, colIdx));
}
};

const SKIP_END_ROWS = 5 // The number of footer rows generated by doc_status.py

// Set up event listeners that will sort the table when headers are clicked.
window.onload = function()
{
document.querySelectorAll('th')
.forEach(th =>
th.addEventListener('click', (() =>
{
const table = th.closest('table');
const tbody = table.querySelector('tbody')
const trows = Array.from(tbody.querySelectorAll('tr'))
trows.slice(0, -SKIP_END_ROWS)
.sort(comparer(th, th.asc = !th.asc)) // Give each column an individual sort direction
.concat(trows.splice(-SKIP_END_ROWS)) // Don't sort the last rows, as they are not class reference entries.
.forEach(tr => tbody.appendChild(tr) );
})));
}

0 comments on commit c9714e9

Please sign in to comment.