blog/src/components/PostList.astro

105 lines
2.2 KiB
Plaintext

---
import { getCollection, type CollectionEntry } from "astro:content";
import Timestamp from "./Timestamp.astro";
import { sortBy } from "lodash-es";
import TagList from "./TagList.astro";
interface Props {
basePath: string;
drafts?: "exclude" | "include" | "only";
filteredPosts?: Post[];
}
type Post = CollectionEntry<"posts">;
const { basePath, drafts: includeDrafts, filteredPosts } = Astro.props;
type FilterFn = (_: Post) => boolean;
function unreachable(): never {
throw new Error("unreachable");
}
function getFilter(): FilterFn {
switch (includeDrafts) {
case "exclude":
case undefined:
return (post: Post) => !post.data.draft;
case "include":
return (_: Post) => true;
case "only":
return (post: Post) => post.data.draft === true;
}
return unreachable();
}
const filter = getFilter();
let allPosts;
if (filteredPosts) allPosts = filteredPosts.filter(filter);
else allPosts = await getCollection("posts", filter);
const sortedPosts = sortBy(allPosts, (post) => -post.data.date);
---
<table class="postListing">
{
sortedPosts.map((post) => {
return (
<>
<tr class="row">
<td class="info">
<span class="spacer" />
<Timestamp timestamp={post.data.date} />
</td>
<td>
<div class="title">
<a href={`${basePath}/${post.slug}`} class="brand-colorlink">
{post.data.title}
</a>
</div>
<TagList tags={post.data.tags} />
</td>
</tr>
</>
);
})
}
</table>
<style lang="scss">
.postListing {
width: 100%;
border-spacing: 6px 10px;
td {
// padding-bottom: 10px;
// line-height: 1;
.title {
font-size: 12pt;
}
.summary {
padding-top: 4px;
font-size: 0.64em;
color: var(--smaller-text-color);
p {
display: inline;
}
}
}
td.info {
color: var(--smaller-text-color);
font-size: 0.75em;
white-space: nowrap;
text-align: right;
vertical-align: baseline;
.spacer {
font-size: 12pt;
}
}
}
</style>