library sorting
This commit is contained in:
parent
61297a924e
commit
e5ed6a3bb4
|
|
@ -404,7 +404,9 @@ rules = do
|
|||
let ts = fromMaybe [] (lookupStringList "tags" meta)
|
||||
return $ any (\t -> t == p || (p ++ "/") `isPrefixOf` t) ts
|
||||
|
||||
portalList name p = listField name essayCtx $ do
|
||||
itemCtx = dateField "date-iso" "%Y-%m-%d" <> essayCtx
|
||||
|
||||
portalList name p = listField name itemCtx $ do
|
||||
essays <- loadAll (allEssays .&&. hasNoVersion)
|
||||
posts <- loadAll ("content/blog/*.md" .&&. hasNoVersion)
|
||||
fiction <- loadAll ("content/fiction/*.md" .&&. hasNoVersion)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,54 @@
|
|||
font-size: var(--text-size-small);
|
||||
color: var(--text-muted);
|
||||
margin-top: 0.25rem;
|
||||
margin-bottom: 1.25rem;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
SORT CONTROLS
|
||||
============================================================ */
|
||||
|
||||
.library-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.6rem;
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
|
||||
.library-controls-label {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-faint);
|
||||
}
|
||||
|
||||
.library-controls-options {
|
||||
display: flex;
|
||||
gap: 0.3rem;
|
||||
}
|
||||
|
||||
.library-sort-btn {
|
||||
font-family: var(--font-sans);
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
background: none;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 2px;
|
||||
padding: 0.15em 0.55em;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.1s, color 0.1s;
|
||||
}
|
||||
|
||||
.library-sort-btn:hover {
|
||||
border-color: var(--border-muted);
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.library-sort-btn.is-active {
|
||||
border-color: var(--text-muted);
|
||||
color: var(--text);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
PORTAL SECTIONS
|
||||
============================================================ */
|
||||
|
|
|
|||
|
|
@ -2,11 +2,20 @@
|
|||
<h1 class="page-title">Library</h1>
|
||||
<p class="library-intro">Everything on this site, organized by portal.</p>
|
||||
|
||||
<div class="library-controls">
|
||||
<span class="library-controls-label">Sort by</span>
|
||||
<div class="library-controls-options" role="group" aria-label="Sort order">
|
||||
<button class="library-sort-btn" data-sort="date">date</button>
|
||||
<button class="library-sort-btn" data-sort="title">title</button>
|
||||
<button class="library-sort-btn" data-sort="score">epistemic effort</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
$if(research-entries)$
|
||||
<section class="library-section">
|
||||
<h2 id="research"><a href="/research/">Research</a></h2>
|
||||
<ul class="library-list">$for(research-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -20,7 +29,7 @@ $if(nonfiction-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="nonfiction"><a href="/nonfiction/">Nonfiction</a></h2>
|
||||
<ul class="library-list">$for(nonfiction-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -34,7 +43,7 @@ $if(fiction-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="fiction"><a href="/fiction/">Fiction</a></h2>
|
||||
<ul class="library-list">$for(fiction-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -48,7 +57,7 @@ $if(poetry-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="poetry"><a href="/poetry/">Poetry</a></h2>
|
||||
<ul class="library-list">$for(poetry-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -62,7 +71,7 @@ $if(music-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="music"><a href="/music/">Music</a></h2>
|
||||
<ul class="library-list">$for(music-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -76,7 +85,7 @@ $if(ai-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="ai"><a href="/ai/">AI</a></h2>
|
||||
<ul class="library-list">$for(ai-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -90,7 +99,7 @@ $if(tech-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="tech"><a href="/tech/">Tech</a></h2>
|
||||
<ul class="library-list">$for(tech-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -104,7 +113,7 @@ $if(miscellany-entries)$
|
|||
<section class="library-section">
|
||||
<h2 id="miscellany"><a href="/miscellany/">Miscellany</a></h2>
|
||||
<ul class="library-list">$for(miscellany-entries)$
|
||||
<li class="library-entry">
|
||||
<li class="library-entry" data-date="$date-iso$"$if(overall-score)$ data-score="$overall-score$"$endif$>
|
||||
<div class="library-entry-header">
|
||||
<a class="library-entry-title" href="$url$">$title$</a>
|
||||
<span class="library-entry-date">$date-created$</span>
|
||||
|
|
@ -115,3 +124,62 @@ $if(miscellany-entries)$
|
|||
$endif$
|
||||
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
var STORAGE_KEY = 'library-sort';
|
||||
var DEFAULT = 'date';
|
||||
var MODES = { date: 1, title: 1, score: 1 };
|
||||
|
||||
function titleOf(entry) {
|
||||
var el = entry.querySelector('.library-entry-title');
|
||||
return el ? el.textContent.trim().toLowerCase() : '';
|
||||
}
|
||||
|
||||
function compare(a, b, mode) {
|
||||
if (mode === 'title') {
|
||||
return titleOf(a).localeCompare(titleOf(b));
|
||||
}
|
||||
if (mode === 'score') {
|
||||
// Entries without a score sink to the bottom; ties broken by date desc.
|
||||
var hasA = a.dataset.score !== undefined;
|
||||
var hasB = b.dataset.score !== undefined;
|
||||
if (hasA && !hasB) return -1;
|
||||
if (!hasA && hasB) return 1;
|
||||
if (hasA && hasB) {
|
||||
var diff = Number(b.dataset.score) - Number(a.dataset.score);
|
||||
if (diff !== 0) return diff;
|
||||
}
|
||||
// fall through to date-desc tiebreak
|
||||
}
|
||||
// date desc (ISO strings sort lexicographically)
|
||||
var da = a.dataset.date || '';
|
||||
var db = b.dataset.date || '';
|
||||
if (da < db) return 1;
|
||||
if (da > db) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
function applySort(mode) {
|
||||
if (!MODES[mode]) mode = DEFAULT;
|
||||
document.querySelectorAll('.library-list').forEach(function (list) {
|
||||
var entries = Array.prototype.slice.call(list.querySelectorAll('.library-entry'));
|
||||
entries.sort(function (a, b) { return compare(a, b, mode); });
|
||||
entries.forEach(function (el) { list.appendChild(el); });
|
||||
});
|
||||
document.querySelectorAll('.library-sort-btn').forEach(function (btn) {
|
||||
btn.classList.toggle('is-active', btn.dataset.sort === mode);
|
||||
});
|
||||
try { localStorage.setItem(STORAGE_KEY, mode); } catch (e) {}
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
var saved;
|
||||
try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) {}
|
||||
applySort(saved || DEFAULT);
|
||||
|
||||
document.querySelectorAll('.library-sort-btn').forEach(function (btn) {
|
||||
btn.addEventListener('click', function () { applySort(btn.dataset.sort); });
|
||||
});
|
||||
});
|
||||
}());
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Reference in New Issue