Format links page

Group links by date and style them like cards.
This commit is contained in:
Hornwitser 2025-02-16 23:54:10 +01:00
parent 8a1313eb6e
commit 9f15da0835
2 changed files with 127 additions and 14 deletions

View file

@ -12,6 +12,7 @@ interface LinkData {
description?: string,
read?: string,
author?: string,
quote?: string,
}
interface Data {
links: LinkData[];
@ -23,13 +24,81 @@ interface Data {
function Link(props: { link: LinkData }) {
const link = props.link;
return <>
<a href={link.url}>{link.title}</a>
{" "}
{link.tags.join(", ")}
<hgroup>
<h4>
<a href={link.url}>{link.title}</a>
{link.author ? <span class="no-break"> {link.author}</span> : null}
</h4>
<p>
{link.tags.join(", ")}
{link.read ? <>
, read: <time>{link.read}</time>
</> : null}
</p>
</hgroup>
{link.quote ? <blockquote>
{link.quote}
</blockquote> : null}
{link.description ? <p>
{link.description}
</p> : null}
{link.altUrls ? <p>
Also available at {link.altUrls.map(url => <a href={url}>{url}</a>)}.
</p> : null}
{link.related ? <p>
Related <a href={link.related}>{link.related}</a>.
</p> : null}
{link.via ? <p>
Via <a href={link.via}>{link.via}</a>.
</p> : null}
</>
}
const data: Data = eval(`(${readFileSync("src/content/links.jsonc", "utf8")})`);
data.links.pop(); // Remove template at the end
function compare(a: string, b: string) {
return Number(a > b) - Number(b > a);
}
function* groupBy<T, K>(items: Iterable<T>, keyFn: (item: T) => K) {
let oldKey: K = Symbol() as any;
let group: T[] | undefined = undefined;
for (const item of items) {
let newKey = keyFn(item);
if (!Object.is(newKey, oldKey)) {
if (group) {
yield [oldKey, group] as [K, T[]];
}
group = [];
oldKey = newKey;
}
group!.push(item);
}
if (group) {
yield [oldKey, group] as [K, T[]];
}
}
const byDate = data.links.filter(link => link.read).sort((a, b) => -compare(a.read!, b.read!));
const byMonth = groupBy(byDate, link => link.read!.slice(0, 7));
const byYearMonth = [...groupBy(byMonth, month => month[0].slice(0, 4))];
const other = data.links.filter(link => !link.read);
const monthNames = [
"", // padding to make mapping start at 1
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
];
const title = "Links!";
export const links: Page = {
title,
@ -37,8 +106,27 @@ export const links: Page = {
content: <BasePage title={title}>
<main>
<h1>{title}</h1>
<ul>
{ data.links.map(link => <li><Link link={link} /></li>)}
<p>
Here be interesting things I've read, watched, listened to or otherwise found useful as resources over the years. These are ordered by when I read them as that seems most practical to me.
</p>
{byYearMonth.map(([year, months]) => <section>
<h2>{year}</h2>
{months.map(([yearMonth, links]) => <section>
<h3>{monthNames[Number.parseInt(yearMonth.slice(5))]}</h3>
<ul role="list">
{links.map(link => <li class="link">
<Link link={link} />
</li>)}
</ul>
</section>)}
</section>)}
<h2>
Resources and other things
</h2>
<ul role="list">
{other.map(link => <li class="link">
<Link link={link} />
</li>)}
</ul>
</main>
</BasePage>