Tables with a header row can be sorted by clicking a column header
Listing tables on company, taxonomy and generic list pages get column headers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YDRjtmAiNpo5mK5N3d7QwZ
This commit is contained in:
+12
-5
@@ -72,14 +72,21 @@ small, .meta, .note, .crumbs { color: var(--muted); }
|
||||
|
||||
table { border-collapse: collapse; }
|
||||
.specs, .list { width: 100%; clear: both; margin: 16px 0; background: var(--panel); }
|
||||
.specs th, .specs td, .list td { padding: 6px 10px; border: 1px solid var(--line); text-align: left; vertical-align: top; }
|
||||
.specs th, .specs td, .list th, .list td { padding: 6px 10px; border: 1px solid var(--line); text-align: left; vertical-align: top; }
|
||||
.specs th { width: 30%; font-size: 0.8rem; letter-spacing: 0.5px; color: var(--muted); }
|
||||
.list { table-layout: fixed; }
|
||||
.list td:first-child { width: 45%; }
|
||||
.list td:nth-child(2) { width: 12%; }
|
||||
.list td:nth-child(4) { width: 15%; }
|
||||
.list :is(th, td):first-child { width: 45%; }
|
||||
.list :is(th, td):nth-child(2) { width: 12%; }
|
||||
.list :is(th, td):nth-child(4) { width: 15%; }
|
||||
.list { font-size: 0.95rem; }
|
||||
@media (max-width: 600px) { .list td:nth-child(4) { display: none; } }
|
||||
@media (max-width: 600px) { .list :is(th, td):nth-child(4) { display: none; } }
|
||||
.list thead th { font-size: 0.8rem; letter-spacing: 0.5px; color: var(--muted); }
|
||||
.content th { padding: 6px 10px; text-align: left; }
|
||||
.sortable th { cursor: pointer; user-select: none; white-space: nowrap; }
|
||||
.sortable th:hover, .sortable th:focus-visible { color: var(--text, inherit); text-decoration: underline; }
|
||||
.sortable th::after { content: " ↕"; opacity: 0.4; }
|
||||
.sortable th[aria-sort="ascending"]::after { content: " ↑"; opacity: 1; }
|
||||
.sortable th[aria-sort="descending"]::after { content: " ↓"; opacity: 1; }
|
||||
|
||||
.gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 16px; }
|
||||
.gallery figure, .thumbs a, .card, .comments li {
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// Tables with a header row can be re-ordered by clicking a column header.
|
||||
(function () {
|
||||
function headerRow(table) {
|
||||
if (table.tHead && table.tHead.rows.length) return table.tHead.rows[table.tHead.rows.length - 1];
|
||||
var first = table.rows[0];
|
||||
if (!first || first.cells.length < 2) return null;
|
||||
for (var i = 0; i < first.cells.length; i++) if (first.cells[i].tagName !== "TH") return null;
|
||||
return first;
|
||||
}
|
||||
|
||||
function value(row, col) {
|
||||
var cell = row.cells[col];
|
||||
return cell ? cell.textContent.trim() : "";
|
||||
}
|
||||
|
||||
function number(text) {
|
||||
return /^[-+]?[\d.,\s]*\d[\d.,\s]*\S{0,3}$/.test(text) ? parseFloat(text.replace(/[\s,]/g, "")) : NaN;
|
||||
}
|
||||
|
||||
function sort(table, head, th) {
|
||||
var col = th.cellIndex, down = th.getAttribute("aria-sort") === "ascending";
|
||||
var rows = [];
|
||||
for (var b = 0; b < table.tBodies.length; b++)
|
||||
for (var r = 0; r < table.tBodies[b].rows.length; r++)
|
||||
if (table.tBodies[b].rows[r] !== head) rows.push(table.tBodies[b].rows[r]);
|
||||
if (!rows.length) return;
|
||||
var numeric = rows.every(function (row) { var v = value(row, col); return !v || !isNaN(number(v)); });
|
||||
var compare = new Intl.Collator(undefined, { numeric: true, sensitivity: "base" }).compare;
|
||||
rows.map(function (row, i) { return { row: row, text: value(row, col), i: i }; })
|
||||
.sort(function (a, b) {
|
||||
if (!a.text || !b.text) return a.text || b.text ? (a.text ? -1 : 1) : a.i - b.i; // empty cells last
|
||||
var d = numeric ? number(a.text) - number(b.text) : compare(a.text, b.text);
|
||||
return (down ? -d : d) || a.i - b.i;
|
||||
})
|
||||
.forEach(function (item) { rows[0].parentNode.appendChild(item.row); });
|
||||
for (var i = 0; i < head.cells.length; i++) head.cells[i].removeAttribute("aria-sort");
|
||||
th.setAttribute("aria-sort", down ? "descending" : "ascending");
|
||||
}
|
||||
|
||||
document.querySelectorAll("table").forEach(function (table) {
|
||||
var head = headerRow(table);
|
||||
if (!head || table.querySelector("[rowspan], [colspan]")) return;
|
||||
table.classList.add("sortable");
|
||||
Array.prototype.forEach.call(head.cells, function (th) {
|
||||
th.tabIndex = 0;
|
||||
th.title = "Sort by this column";
|
||||
th.addEventListener("click", function () { sort(table, head, th); });
|
||||
th.addEventListener("keydown", function (e) {
|
||||
if (e.key === "Enter" || e.key === " ") { e.preventDefault(); sort(table, head, th); }
|
||||
});
|
||||
});
|
||||
});
|
||||
})();
|
||||
@@ -1,14 +1,22 @@
|
||||
{{/* table of pages: machines get company/year/type columns; companies are a plain list */}}
|
||||
{{ if eq (index . 0).Section "companies" }}
|
||||
{{/* table of pages: machines get company/year/type columns; companies are a plain list.
|
||||
Takes the pages, or (dict "pages" … "head" true) to add the column headers (sortable). */}}
|
||||
{{ $pages := . }}{{ $head := false }}
|
||||
{{ if reflect.IsMap . }}{{ $pages = .pages }}{{ $head = .head }}{{ end }}
|
||||
{{ if eq (index $pages 0).Section "companies" }}
|
||||
<ul class="columns">
|
||||
{{ range . }}
|
||||
{{ range $pages }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .Title }}</a> <small>({{ len .Params.machines }})</small></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
{{ else }}
|
||||
<table class="list">
|
||||
{{ if $head }}
|
||||
<thead>
|
||||
<tr><th>Name</th><th>Year</th><th>{{ if eq (index $pages 0).Section "software" }}Tags{{ else }}Type{{ end }}</th><th>Origin</th></tr>
|
||||
</thead>
|
||||
{{ end }}
|
||||
<tbody>
|
||||
{{ range . }}
|
||||
{{ range $pages }}
|
||||
<tr>
|
||||
<td><a href="{{ .RelPermalink }}">{{ .Title }}</a></td>
|
||||
<td>{{ partial "terms.html" (dict "page" . "taxonomy" "years") }}</td>
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
{{ with resources.Get "css/main.css" | minify | fingerprint }}
|
||||
<link rel="stylesheet" href="{{ .RelPermalink }}">
|
||||
{{ end }}
|
||||
{{ with resources.Get "js/sortTables.js" | minify | fingerprint }}
|
||||
<script src="{{ .RelPermalink }}" defer></script>
|
||||
{{ end }}
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<h2>Systems</h2>
|
||||
{{ $pages := slice }}
|
||||
{{ range . }}{{ with site.GetPage . }}{{ $pages = $pages | append . }}{{ end }}{{ end }}
|
||||
{{ partial "page-list.html" $pages }}
|
||||
{{ partial "page-list.html" (dict "pages" $pages "head" true) }}
|
||||
{{ end }}
|
||||
</article>
|
||||
{{ end }}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{{ define "main" }}
|
||||
<h1>{{ .Title }}</h1>
|
||||
<div class="content">{{ .Content }}</div>
|
||||
{{ partial "page-list.html" .RegularPages.ByTitle }}
|
||||
{{ partial "page-list.html" (dict "pages" .RegularPages.ByTitle "head" true) }}
|
||||
{{ end }}
|
||||
|
||||
+1
-1
@@ -3,6 +3,6 @@
|
||||
<h1>{{ .Title }}</h1>
|
||||
{{ range .Pages.GroupBy "Section" }}
|
||||
<h2>{{ with site.GetPage (printf "/%s" .Key) }}{{ .Title }}{{ end }} <small>({{ len .Pages }})</small></h2>
|
||||
{{ partial "page-list.html" .Pages.ByTitle }}
|
||||
{{ partial "page-list.html" (dict "pages" .Pages.ByTitle "head" true) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
Reference in New Issue
Block a user