Files
website-astro-stefanimhoff.de/src/pages/[...slug].astro
2023-06-12 19:12:23 +02:00

249 lines
5.8 KiB
Plaintext

---
import cx from 'classnames';
import { getCollection } from 'astro:content';
import { site } from '../data/site';
import { formatPosts, sortByDate } from '../utils';
import { dateToFormat, dateToISO, wordCount } from '../utils';
import GridLayout from '../layouts/GridLayout.astro';
import PageTitle from '../components/PageTitle.astro';
import Pagination from '../components/Pagination.astro';
import Picture from '../components/Picture.astro';
import { Banner, ListItem, OrderedList, Tag, TextLink } from '../components';
import { mapping } from '../mdx-components';
export async function getStaticPaths() {
const journalEntries = await getCollection('journal');
const numberOfPages = journalEntries.length;
const formattedJournalEntries = 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 ? {} : formattedJournalEntries[index - 1],
},
}));
}
const { entry, prev, next } = Astro.props;
const {
Content,
remarkPluginFrontmatter: { minutesRead },
} = await entry.render();
const seriesEntries = await getCollection('journal', ({ data }) => {
return data.series === entry.data.series;
});
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 && (
<Picture
alt={entry.data.title}
aspect={1.6}
breakpoints={[300, 500, 700, 1000, 1300, 1500, 1800, 2000]}
class="col-span-full aspect-video h-auto !mbe-0 print:hidden xl:col-start-1 xl:col-end-14 3xl:col-end-13 [&_img]:!w-full [&_img]:!max-w-none [&_picture]:!w-full [&_picture]:!max-w-none"
format={['webp', 'avif']}
src={entry.data.cover}
/>
)
}
<aside
class={cx(
'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> • {minutesRead}
<span class="italic">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 && (
<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}/`}
previousText={'Next'}
previousUrl={prev.slug && `/${prev.slug}/`}
/>
</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 p-[0.1em] font-light !text-accent no-underline;
}
}
</style>