feat(blog): live in-page filter via data-search attributes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
8423759f03
commit
468d4e460d
1 changed files with 62 additions and 0 deletions
|
|
@ -80,3 +80,65 @@
|
|||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
// Lives on /blog. No-ops gracefully on any other page (selectors return empty).
|
||||
const input = document.querySelector<HTMLInputElement>(".binput");
|
||||
if (input) {
|
||||
const rows = Array.from(
|
||||
document.querySelectorAll<HTMLElement>("[data-search]")
|
||||
);
|
||||
const featured = document.querySelector<HTMLElement>("[data-featured]");
|
||||
const yearHeads = Array.from(
|
||||
document.querySelectorAll<HTMLElement>("[data-year-head]")
|
||||
);
|
||||
const empty = document.querySelector<HTMLElement>("[data-empty]");
|
||||
const emptyQuery = empty?.querySelector<HTMLElement>("[data-empty-query]");
|
||||
|
||||
function applyFilter() {
|
||||
const raw = input!.value.toLowerCase().trim();
|
||||
const tokens = raw.length ? raw.split(/\s+/) : [];
|
||||
const isFiltering = tokens.length > 0;
|
||||
|
||||
let visibleCount = 0;
|
||||
for (const row of rows) {
|
||||
const haystack = row.dataset.search ?? "";
|
||||
const isFeatured = row === featured;
|
||||
let visible: boolean;
|
||||
if (!isFiltering) {
|
||||
visible = true;
|
||||
} else if (isFeatured) {
|
||||
// Featured hides whenever there's a query, regardless of match.
|
||||
visible = false;
|
||||
} else {
|
||||
visible = tokens.every((t) => haystack.includes(t));
|
||||
}
|
||||
row.classList.toggle("is-hidden", !visible);
|
||||
if (visible && !isFeatured) visibleCount += 1;
|
||||
}
|
||||
|
||||
// Hide year heads whose following rows are all hidden.
|
||||
for (const head of yearHeads) {
|
||||
let anyVisible = false;
|
||||
let node: Element | null = head.nextElementSibling;
|
||||
while (node && !node.matches("[data-year-head]")) {
|
||||
if (node.matches("[data-search]") && !node.classList.contains("is-hidden")) {
|
||||
anyVisible = true;
|
||||
break;
|
||||
}
|
||||
node = node.nextElementSibling;
|
||||
}
|
||||
head.classList.toggle("is-hidden", !anyVisible);
|
||||
}
|
||||
|
||||
// Toggle empty-state message.
|
||||
if (empty) {
|
||||
const showEmpty = isFiltering && visibleCount === 0;
|
||||
empty.hidden = !showEmpty;
|
||||
if (showEmpty && emptyQuery) emptyQuery.textContent = input!.value.trim();
|
||||
}
|
||||
}
|
||||
|
||||
input.addEventListener("input", applyFilter);
|
||||
}
|
||||
</script>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue