-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Make the class reference table sortable (#28)
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
1 parent
4fc119a
commit c9714e9
Showing
3 changed files
with
104 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) ); | ||
}))); | ||
} |