Skip to content

Commit f3db570

Browse files
Add filename sort to web workflow file manager
1 parent d659a3b commit f3db570

File tree

2 files changed

+171
-2
lines changed

2 files changed

+171
-2
lines changed

supervisor/shared/web_workflow/static/directory.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
<h1><a href="/"><img src="/favicon.ico"/></a>&nbsp;<span id="path"></span></h1>
1212
<div id="usbwarning" style="display: none;">🛈 USB is using the storage. Only allowing reads. See <a href="https://learn.adafruit.com/circuitpython-essentials/circuitpython-storage">the CircuitPython Essentials: Storage guide</a> for details.</div>
1313
<template id="row"><tr><td></td><td></td><td><a></a></td><td></td><td><button class="delete">🗑️</button></td><td><a class="edit_link" href="">Edit</a></td></tr></template>
14-
<table>
15-
<thead><tr><th>Type</th><th>Size</th><th>Path</th><th>Modified</th><th></th></tr></thead>
14+
<table class="sortable">
15+
<thead><tr><th>Type</th><th>Size</th><th aria-sort="ascending"><button>Path<span aria-hidden="true"></span></button></th><th>Modified</th><th></th></tr></thead>
1616
<tbody></tbody>
1717
</table>
1818
<hr>

supervisor/shared/web_workflow/static/directory.js

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,172 @@
1+
/*
2+
* This content is licensed according to the W3C Software License at
3+
* https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
4+
*
5+
* File: sortable-table.js
6+
*
7+
* Desc: Adds sorting to a HTML data table that implements ARIA Authoring Practices
8+
*/
9+
10+
'use strict';
11+
12+
class SortableTable {
13+
constructor(tableNode) {
14+
this.tableNode = tableNode;
15+
16+
this.columnHeaders = tableNode.querySelectorAll('thead th');
17+
18+
this.sortColumns = [];
19+
20+
for (var i = 0; i < this.columnHeaders.length; i++) {
21+
var ch = this.columnHeaders[i];
22+
var buttonNode = ch.querySelector('button');
23+
if (buttonNode) {
24+
this.sortColumns.push(i);
25+
buttonNode.setAttribute('data-column-index', i);
26+
buttonNode.addEventListener('click', this.handleClick.bind(this));
27+
}
28+
}
29+
30+
this.optionCheckbox = document.querySelector(
31+
'input[type="checkbox"][value="show-unsorted-icon"]'
32+
);
33+
34+
if (this.optionCheckbox) {
35+
this.optionCheckbox.addEventListener(
36+
'change',
37+
this.handleOptionChange.bind(this)
38+
);
39+
if (this.optionCheckbox.checked) {
40+
this.tableNode.classList.add('show-unsorted-icon');
41+
}
42+
}
43+
}
44+
45+
setColumnHeaderSort(columnIndex) {
46+
if (typeof columnIndex === 'string') {
47+
columnIndex = parseInt(columnIndex);
48+
}
49+
50+
for (var i = 0; i < this.columnHeaders.length; i++) {
51+
var ch = this.columnHeaders[i];
52+
var buttonNode = ch.querySelector('button');
53+
if (i === columnIndex) {
54+
var value = ch.getAttribute('aria-sort');
55+
if (value === 'descending') {
56+
ch.setAttribute('aria-sort', 'ascending');
57+
this.sortColumn(
58+
columnIndex,
59+
'ascending',
60+
ch.classList.contains('num')
61+
);
62+
} else {
63+
ch.setAttribute('aria-sort', 'descending');
64+
this.sortColumn(
65+
columnIndex,
66+
'descending',
67+
ch.classList.contains('num')
68+
);
69+
}
70+
} else {
71+
if (ch.hasAttribute('aria-sort') && buttonNode) {
72+
ch.removeAttribute('aria-sort');
73+
}
74+
}
75+
}
76+
}
77+
78+
sortColumn(columnIndex, sortValue, isNumber) {
79+
function compareValues(a, b) {
80+
if (sortValue === 'ascending') {
81+
if (a.value === b.value) {
82+
return 0;
83+
} else {
84+
if (isNumber) {
85+
return a.value - b.value;
86+
} else {
87+
return a.value < b.value ? -1 : 1;
88+
}
89+
}
90+
} else {
91+
if (a.value === b.value) {
92+
return 0;
93+
} else {
94+
if (isNumber) {
95+
return b.value - a.value;
96+
} else {
97+
return a.value > b.value ? -1 : 1;
98+
}
99+
}
100+
}
101+
}
102+
103+
if (typeof isNumber !== 'boolean') {
104+
isNumber = false;
105+
}
106+
107+
var tbodyNode = this.tableNode.querySelector('tbody');
108+
var rowNodes = [];
109+
var dataCells = [];
110+
111+
var rowNode = tbodyNode.firstElementChild;
112+
113+
var index = 0;
114+
while (rowNode) {
115+
rowNodes.push(rowNode);
116+
var rowCells = rowNode.querySelectorAll('th, td');
117+
var dataCell = rowCells[columnIndex];
118+
119+
var data = {};
120+
data.index = index;
121+
data.value = dataCell.textContent.toLowerCase().trim();
122+
if (isNumber) {
123+
data.value = parseFloat(data.value);
124+
}
125+
dataCells.push(data);
126+
rowNode = rowNode.nextElementSibling;
127+
index += 1;
128+
}
129+
130+
dataCells.sort(compareValues);
131+
132+
// remove rows
133+
while (tbodyNode.firstChild) {
134+
tbodyNode.removeChild(tbodyNode.lastChild);
135+
}
136+
137+
// add sorted rows
138+
for (var i = 0; i < dataCells.length; i += 1) {
139+
tbodyNode.appendChild(rowNodes[dataCells[i].index]);
140+
}
141+
}
142+
143+
/* EVENT HANDLERS */
144+
145+
handleClick(event) {
146+
var tgt = event.currentTarget;
147+
this.setColumnHeaderSort(tgt.getAttribute('data-column-index'));
148+
}
149+
150+
handleOptionChange(event) {
151+
var tgt = event.currentTarget;
152+
153+
if (tgt.checked) {
154+
this.tableNode.classList.add('show-unsorted-icon');
155+
} else {
156+
this.tableNode.classList.remove('show-unsorted-icon');
157+
}
158+
}
159+
}
160+
161+
// Initialize sortable table buttons
162+
window.addEventListener('load', function () {
163+
var sortableTables = document.querySelectorAll('table.sortable');
164+
for (var i = 0; i < sortableTables.length; i++) {
165+
new SortableTable(sortableTables[i]);
166+
}
167+
});
168+
169+
1170
let new_directory_name = document.getElementById("name");
2171
let files = document.getElementById("files");
3172

0 commit comments

Comments
 (0)