Format links page
Group links by date and style them like cards.
This commit is contained in:
parent
8a1313eb6e
commit
9f15da0835
2 changed files with 127 additions and 14 deletions
|
@ -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>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue