Files
website-astro-stefanimhoff.de/src/pages/[...slug].astro
2024-12-27 12:39:32 +01:00

258 lines
6.5 KiB
Plaintext

---
import type { CollectionEntry } from 'astro:content';
type Journal = CollectionEntry<'journal'>;
import { getCollection } from 'astro:content';
import { site } from '../data/site';
import { formatPosts, sortByDate } from '../utils';
import { dateToFormat, dateToISO, wordCount } from '../utils';
import Banner from '../components/Banner.astro';
import GridLayout from '../layouts/GridLayout.astro';
import Image from '../components/Image.astro';
import ListItem from '../components/ListItem.astro';
import OrderedList from '../components/OrderedList.astro';
import PageTitle from '../components/PageTitle.astro';
import Pagination from '../components/Pagination.astro';
import Tag from '../components/Tag.astro';
import TextLink from '../components/TextLink.astro';
import { mapping } from '../mdx-components';
export async function getStaticPaths() {
const journalEntries = await getCollection('journal', ({ data }: Journal) => !data.draft);
const numberOfPages = journalEntries.length;
const formattedJournalEntries: Journal[] = formatPosts(journalEntries, { sortOrder: 'asc' });
return formattedJournalEntries.map((entry, index) => ({
params: { slug: entry.slug },
props: {
entry,
next:
index + 1 === numberOfPages
? { slug: null, data: null }
: formattedJournalEntries[index + 1],
prev: index === 0 ? { slug: null, data: null } : formattedJournalEntries[index - 1],
},
}));
}
const { entry, prev, next } = Astro.props;
const {
Content,
remarkPluginFrontmatter: { minutesRead },
} = await entry.render();
const seriesEntries: Journal[] = await getCollection('journal', ({ data }: Journal) => {
return data.series === entry.data.series && !data.draft;
});
seriesEntries.sort(sortByDate).reverse();
const title = entry.data.title;
const description = entry.data.description;
const schema = JSON.stringify({
'@context': 'https://schema.org',
'@type': 'Article',
headline: entry.data.title,
description: entry.data.description,
datePublished: entry.data.date,
dateModified: entry.data.updated,
image: [
entry.data.cover
? site.url + entry.data.cover
: `${site.url}/assets/images/branding/og/bonsai.jpg`,
],
author: [
{
'@type': 'Person',
name: entry.data.author || site.author,
url: `${site.url}/about`,
},
],
mainEntityOfPage: {
'@type': 'webPage',
id: `${site.url}/${entry.slug}/}`,
},
});
---
<GridLayout
backLink="/journal/"
cover={entry.data.cover}
description={description}
grid="fullsize"
innerGrid
nofollow={entry.data.nofollow}
noindex={entry.data.noindex}
title={title}
>
<PageTitle slot="title" class="!text-6">
{entry.data.title}
</PageTitle>
{
entry.data.cover && (
<Image
alt={entry.data.title}
class="col-span-full aspect-video h-auto !mbe-0 xl:col-start-1 xl:col-end-14 3xl:col-end-13 print:hidden [&_img]:!w-full [&_img]:!max-w-none [&_picture]:!w-full [&_picture]:!max-w-none"
loading="eager"
src={entry.data.cover}
/>
)
}
<aside
class:list={[
'col-start-2 col-end-18 md:col-start-5 md:col-end-15 xl:col-start-15 xl:col-end-18 xl:row-start-2 3xl:col-start-14 3xl:col-end-18',
{ 'row-start-3': entry.data.cover },
]}
>
<div class="leading-none mbe-6">
<em>By</em>{' '}
<TextLink href="/about/">{entry.data.author}</TextLink>
</div>
<div class="leading-tight">
<time datetime={dateToISO(entry.data.date)}>{dateToFormat(entry.data.date)}</time>
</div>
<div class="leading-none mbe-6">
{wordCount(entry.body)}{' '}
<span class="italic">words</span> • {Math.round(minutesRead)}{' '}
<span class="italic">min read</span>
</div>
<div class="flex flex-wrap gap-y-3">
{
entry.data.tags
.sort((a: string, b: string) => a.localeCompare(b))
.map((t: string) => <Tag href={`/tag/${t}/`}>{t}</Tag>)
}
</div>
</aside>
<div
class="journal-post col-start-2 col-end-18 md:col-start-5 md:col-end-15 xl:col-start-6 xl:col-end-14 3xl:col-start-7 3xl:col-end-13"
>
{
entry.data.series && seriesEntries.length > 1 && (
<Banner>
<OrderedList class="!mbe-0 !pis-7">
{seriesEntries.map((item) => (
<ListItem class="!text-[0.85em]">
{entry.slug === item.slug ? (
<>
<i>{item.data.title}</i>
📍
</>
) : (
<TextLink href={`/${item.slug}/`}>{item.data.title}</TextLink>
)}
</ListItem>
))}
</OrderedList>
</Banner>
)
}
<Content components={mapping} />
{
entry.data.updated && (
<footer class="mbs-10">
<b>Last Updated:</b>
<time datetime={dateToISO(entry.data.updated)}>
{dateToFormat(entry.data.updated)}
</time>
</footer>
)
}
</div>
<Pagination
nextText="Next"
nextUrl={next.slug ? `/${next.slug}/` : undefined}
previousText="Previous"
previousUrl={prev.slug ? `/${prev.slug}/` : undefined}
/>
</GridLayout>
<script type="application/ld+json" set:html={schema} />
<style is:global>
.journal-post {
& > h2 {
@apply text-4 mbe-9;
}
& > h3,
& > h4,
& > h5,
& > h6 {
@apply text-3;
}
& h2 + h3,
& p + h2,
& p + h3,
& p + h4,
& p + h5,
& p + h6 {
@apply !mbs-12;
}
& > pre {
@apply md:-mie-[5.55vw] md:-mis-[5.55vw];
}
& > figure.figure-wide {
@apply md:-mie-[16.65vw] md:-mis-[16.65vw] xl:-mie-[11.1vw] xl:-mis-[11.1vw] [&_img]:w-full [&_img]:max-w-none;
}
& > figure.figure-fullsize {
@apply -mie-[5.55vw] -mis-[5.55vw] md:-mie-[22.2vw] md:-mis-[22.2vw] xl:-mie-[27.75vw] xl:-mis-[27.75vw] 3xl:-mie-[33.3vw] 3xl:-mis-[33.3vw] [&_img]:w-full [&_img]:max-w-none;
}
& > figure .image-shadow,
& > figure p {
@apply m-0;
}
/** Footnotes */
.footnotes {
@apply relative pis-[2rem];
}
.footnotes ol {
@apply m-0 list-none p-0 [counter-reset:item];
}
.footnotes li::before {
@apply absolute align-super text-[smaller] font-thin opacity-60 content-[counter(item)] pbs-[0.15rem] inline-start-0 [counter-increment:item];
}
.footnotes .data-footnote-backref {
@apply font-light text-accent no-underline;
}
.footnotes p {
@apply mbe-0;
}
.footnotes p a {
@apply break-words;
}
.footnotes :target {
@apply bg-black/5 p-5 dark:bg-white/5;
}
[data-footnote-ref] {
@apply relative rounded-full font-light not-italic !text-shibui-400 no-underline mis-[0.2em] dark:!text-shibui-100;
}
p:hover [data-footnote-ref],
p:focus [data-footnote-ref],
[data-footnote-ref]:hover,
[data-footnote-ref]:focus {
@apply relative rounded-full font-light not-italic !text-accent no-underline mis-[0.2em];
}
}
</style>