summaryrefslogtreecommitdiff
path: root/src/components
diff options
context:
space:
mode:
authorJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-21 00:37:34 -0300
committerJoão Augusto Costa Branco Marado Torres <torres.dev@disroot.org>2025-07-21 00:37:34 -0300
commitc530d30a74bb521b1c22f0e2a468d9b69c6a2e8e (patch)
treec9230fdf28aa8865ac2066450b84009bc9da0168 /src/components
parent25375fccfe4477022c0b354c6e1018ff5a07df95 (diff)
feat: blog listing style
Signed-off-by: João Augusto Costa Branco Marado Torres <torres.dev@disroot.org>
Diffstat (limited to 'src/components')
-rw-r--r--src/components/DateSelector.astro172
-rw-r--r--src/components/templates/SimplePostList.astro91
2 files changed, 171 insertions, 92 deletions
diff --git a/src/components/DateSelector.astro b/src/components/DateSelector.astro
index d57919e..ab10fd1 100644
--- a/src/components/DateSelector.astro
+++ b/src/components/DateSelector.astro
@@ -1,6 +1,8 @@
---
+import { defined } from "@utils/anonymous";
+
interface Props {
- date: Date;
+ date?: Date;
years: number[];
months: number[];
days?: number[];
@@ -8,9 +10,10 @@ interface Props {
const { date, years, months, days } = Astro.props;
-const y = date.getFullYear();
-const m = date.getMonth() + 1;
-const d = date.getDate();
+const dateParts = Astro.params.date?.split("/").map(Number);
+const y = dateParts?.[0];
+const m = dateParts?.[1];
+const d = dateParts?.[2];
let yI = 0;
let mI = 0;
let dI = 0;
@@ -19,23 +22,31 @@ const list = new Intl.ListFormat("pt-PT", { type: "unit", style: "narrow" });
const pad = (n: number) => String(n).padStart(2, "0");
---
-<nav>
+<nav class="mute small">
<span role="list">
Anos:{" "}
{
- list.formatToParts(years.map((y) =>
- new Intl.DateTimeFormat("pt-PT", { year: "2-digit" }).format(
- new Date(
- Date.UTC(
- y,
- 0,
- 1,
- date.getTimezoneOffset() / 60,
- date.getTimezoneOffset() % 60,
+ list.formatToParts(
+ years.sort().map((y) =>
+ new Intl.DateTimeFormat("pt-PT", { year: "numeric" }).format(
+ new Date(
+ Date.UTC(
+ y,
+ 0,
+ 3,
+ ...[
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() / 60,
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() % 60,
+ ].filter(defined),
+ ),
),
- ),
- )
- )).map(({ type, value }: { type: string; value: string }) => {
+ )
+ ),
+ ).map(({ type, value }: { type: string; value: string }) => {
switch (type) {
case "element": {
const year = years[yI++];
@@ -53,67 +64,39 @@ const pad = (n: number) => String(n).padStart(2, "0");
})
}
</span>
- <br />
- <span role="list">
- Meses:{" "}
- {
- list.formatToParts(months.map((m) =>
- new Intl.DateTimeFormat("pt-PT", { month: "short" }).format(
- new Date(
- Date.UTC(
- y,
- m - 1,
- 1,
- date.getTimezoneOffset() / 60,
- date.getTimezoneOffset() % 60,
- ),
- ),
- )
- )).map(({ type, value }: { type: string; value: string }) => {
- switch (type) {
- case "element": {
- const month = months[mI++];
- return (
- <span role="listitem"><a
- class:list={[{ active: month === m }]}
- href={`/blog/${y}/${pad(month)}`}
- >{value}</a></span>
- );
- }
- case "literal": {
- return <span>{value}</span>;
- }
- }
- })
- }
- </span>
{
- days &&
- (
+ y && (
<><br /><span role="list">
- Dias:{" "}
+ Meses:{" "}
{
- list.formatToParts(days.map((d) => {
- return new Intl.DateTimeFormat("pt-PT", { day: "numeric" })
- .format(
+ list.formatToParts(
+ months.sort().map((m) =>
+ new Intl.DateTimeFormat("pt-PT", { month: "short" }).format(
new Date(
Date.UTC(
y,
m - 1,
- d,
- date.getTimezoneOffset() / 60,
- date.getTimezoneOffset() % 60,
+ 3,
+ ...[
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() / 60,
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() % 60,
+ ].filter(defined),
),
),
- );
- })).map(({ type, value }: { type: string; value: string }) => {
+ )
+ ),
+ ).map(({ type, value }: { type: string; value: string }) => {
switch (type) {
case "element": {
- const day = days[dI++];
+ const month = months[mI++];
return (
<span role="listitem"><a
- class:list={[{ active: day === d }]}
- href={`/blog/${y}/${pad(m)}/${pad(d)}`}
+ class:list={[{ active: month === m }]}
+ href={`/blog/${y}/${pad(month)}`}
>{value}</a></span>
);
}
@@ -126,10 +109,69 @@ const pad = (n: number) => String(n).padStart(2, "0");
</span></>
)
}
+ {
+ days &&
+ (
+ <><br /><span role="list">
+ Dias:{" "}
+ {
+ y && m &&
+ list.formatToParts(
+ days.sort().map((d) => {
+ return new Intl.DateTimeFormat("pt-PT", {
+ day: "numeric",
+ })
+ .format(
+ new Date(
+ Date.UTC(
+ y,
+ m - 1,
+ d,
+ ...[
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() / 60,
+ date === undefined
+ ? undefined
+ : date.getTimezoneOffset() % 60,
+ ].filter(defined),
+ ),
+ ),
+ );
+ }),
+ ).map(
+ ({ type, value }: { type: string; value: string }) => {
+ switch (type) {
+ case "element": {
+ const day = days[dI++];
+ return (
+ <span role="listitem"><a
+ class:list={[{ active: day === d }]}
+ href={`/blog/${y}/${pad(m)}/${pad(day)}`}
+ >{value}</a></span>
+ );
+ }
+ case "literal": {
+ return <span>{value}</span>;
+ }
+ }
+ },
+ )
+ }
+ </span></>
+ )
+ }
</nav>
<style>
+ nav {
+ padding-block: calc(var(--size-2) * 1em);
+ }
a.active {
font-weight: bolder;
}
+
+ a:hover {
+ color: var(--color-active);
+ }
</style>
diff --git a/src/components/templates/SimplePostList.astro b/src/components/templates/SimplePostList.astro
index 164b32b..6d05a1f 100644
--- a/src/components/templates/SimplePostList.astro
+++ b/src/components/templates/SimplePostList.astro
@@ -1,25 +1,45 @@
---
import Date from "@components/organisms/Date.astro";
import KeywordsList from "@components/organisms/KeywordsList.astro";
-import { getFirstUserID, getLastUpdate } from "@lib/collection/helpers";
-import type { Original } from "@lib/collection/schemas";
-import type { z } from "astro:content";
+import {
+ getFirstUserID,
+ getLastUpdate,
+ getTranslationOriginal,
+ isMicro,
+ isTranslation,
+} from "@lib/collection/helpers";
import type { CollectionEntry } from "astro:content";
interface Props {
- posts: (CollectionEntry<"blog"> & { data: z.infer<typeof Original> })[];
+ posts: CollectionEntry<"blog">[];
+ small?: boolean;
+ dateOptions?: Intl.DateTimeFormatOptions;
}
-const { posts } = Astro.props;
+const {
+ posts,
+ small = false,
+ dateOptions = { year: "numeric", month: "long", day: "numeric" },
+} = Astro.props;
---
-<ol>
+<ol class:list={{ "remove-nums": !small }}>
{
- await Promise.all(posts.map(async (post) => {
- const { id, data } = post;
- const { title, description, lang, keywords } = data;
- const { name, email, entity } = await getFirstUserID(post);
- const display = name ?? email ?? entity;
- return (
+ await Promise.all(
+ posts.map(
+ async (post) => {
+ const { id, data } = post;
+ const { title, lang } = data;
+ const description = isMicro(post)
+ ? post.rendered?.html
+ : ("description" in data ? data.description : undefined);
+ const keywords = isTranslation(post)
+ ? await getTranslationOriginal(post).then((x) =>
+ x?.data?.keywords
+ )
+ : ("keywords" in data ? data.keywords : undefined);
+ const { name, email, entity } = await getFirstUserID(post);
+ const display = name ?? email ?? entity;
+ return (
<li>
<article
itemprop="blogPost"
@@ -29,36 +49,39 @@ const { posts } = Astro.props;
<h3>
<a href={`/blog/read/${id}`} itemprop="headline name">{title}</a>
</h3>
- <div itemprop="abstract">
- {
- description &&
- description.split("\n\n").map((paragraph) => (
- <p class="small">{paragraph}</p>
- ))
- }
- </div>
+ {
+ description && (
+ <div itemprop="abstract">
+ {
+ isMicro(post)
+ ? <Fragment set:html={description} />
+ : description.split("\n\n").map((
+ paragraph,
+ ) => <p class:list={{ small }}>{paragraph}</p>)
+ }
+ </div>
+ )
+ }
<footer class="small">
<Date
date={getLastUpdate(post)}
locales={lang}
- options={{
- year: "numeric",
- month: "long",
- day: "numeric",
- }}
+ options={dateOptions}
itemprop="dateModified"
/><span
itemprop="author"
itemscope
itemtype="https://schema.org/Person"
><span itemprop="alternateName">{display}</span></span>
- <KeywordsList {keywords} />
+ {Array.isArray(keywords) && <KeywordsList {keywords} />}
</footer>
</article>
</li>
);
- }))
+ },
+ ),
+ )
}
</ol>
@@ -82,6 +105,20 @@ const { posts } = Astro.props;
}
}
}
+
+ & > li:not(:first-of-type) {
+ border-block-start: 1px solid var(--color-dark);
+ }
+ }
+
+ ol.remove-nums {
+ margin-inline-start: 0;
+ & > li {
+ margin-inline-end: calc(var(--size-7) * 1em);
+ & > article {
+ padding-inline-end: 0;
+ }
+ }
}
@media (width >= 40rem) {