2025-03-03
Some checks failed
ci/woodpecker/push/publish_instable Pipeline failed

This commit is contained in:
gcch 2025-03-03 17:28:18 +01:00
commit 11fa3d1558
38 changed files with 819 additions and 148 deletions

View file

@ -2,13 +2,13 @@
#app-loading {
position: fixed;
inset: 0;
text-align: center;
align-content: center;
z-index: 100;
background: salmon;
inset: 0;
align-content: center;
font-family: Banquise;
font-size: 2rem;
text-align: center;
background: salmon;
p {
width: 10ch;
@ -25,15 +25,19 @@
0% {
content: "";
}
25% {
content: ".";
}
50% {
content: "..";
}
75% {
content: "...";
}
100% {
content: "";
}

View file

@ -2,6 +2,7 @@
<path d="M50.4 78.5a75.1 75.1 0 0 0-28.5 6.9l24.2-65.7c.7-2 1.9-3.2 3.4-3.2h29c1.5 0 2.7 1.2 3.4 3.2l24.2 65.7s-11.6-7-28.5-7L67 45.5c-.4-1.7-1.6-2.8-2.9-2.8-1.3 0-2.5 1.1-2.9 2.7L50.4 78.5Zm-1.1 28.2Zm-4.2-20.2c-2 6.6-.6 15.8 4.2 20.2a17.5 17.5 0 0 1 .2-.7 5.5 5.5 0 0 1 5.7-4.5c2.8.1 4.3 1.5 4.7 4.7.2 1.1.2 2.3.2 3.5v.4c0 2.7.7 5.2 2.2 7.4a13 13 0 0 0 5.7 4.9v-.3l-.2-.3c-1.8-5.6-.5-9.5 4.4-12.8l1.5-1a73 73 0 0 0 3.2-2.2 16 16 0 0 0 6.8-11.4c.3-2 .1-4-.6-6l-.8.6-1.6 1a37 37 0 0 1-22.4 2.7c-5-.7-9.7-2-13.2-6.2Z" />
<style>
path { fill: #000; }
@media (prefers-color-scheme: dark) {
path { fill: #FFF; }
}

Before

Width:  |  Height:  |  Size: 749 B

After

Width:  |  Height:  |  Size: 750 B

Before After
Before After

96
public/sortable-table.css Normal file
View file

@ -0,0 +1,96 @@
.sr-only {
position: absolute;
top: -30em;
}
table.sortable td, table.sortable th {
width: 8em;
padding: 0.125em 0.25em;
}
table.sortable th {
position: relative;
border-bottom: thin solid #888;
font-weight: bold;
}
table.sortable th.no-sort {
padding-top: 0.35em;
}
table.sortable th:nth-child(5) {
width: 10em;
}
table.sortable th button {
cursor: pointer;
inset: 0;
display: inline;
width: 100%;
margin: 1px;
padding: 4px;
border: none;
font-size: 100%;
font-weight: bold;
text-align: left;
background: transparent;
outline: none;
}
table.sortable th button span {
position: absolute;
right: 4px;
}
table.sortable th[aria-sort="descending"] span::after {
content: "▼";
top: 0;
font-size: 100%;
color: currentcolor;
}
table.sortable th[aria-sort="ascending"] span::after {
content: "▲";
top: 0;
font-size: 100%;
color: currentcolor;
}
table.show-unsorted-icon th:not([aria-sort]) button span::after {
content: "♢";
position: relative;
top: -3px;
left: -4px;
font-size: 100%;
color: currentcolor;
}
table.sortable td.num {
text-align: right;
}
table.sortable tbody tr:nth-child(odd) {
background-color: #ddd;
}
/* Focus and hover styling */
table.sortable th button:focus, table.sortable th button:hover {
padding: 2px;
border: 2px solid currentcolor;
background-color: #e5f4ff;
}
table.sortable th button:focus span, table.sortable th button:hover span {
right: 2px;
}
table.sortable th:not([aria-sort]) button:focus span::after, table.sortable
th:not([aria-sort])
button:hover
span::after {
content: "▼";
top: 0;
font-size: 100%;
color: currentcolor;
}

167
public/sortable-table.js Normal file
View file

@ -0,0 +1,167 @@
/*
* 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]);
}
});