feat: use astro files for slides

This commit is contained in:
Henrique Ramos 2025-02-01 18:26:43 -03:00
parent 8ca43b3785
commit 0614e2b7ef
8 changed files with 182 additions and 47 deletions

View File

@ -1,14 +0,0 @@
import { defineCollection, z } from "astro:content";
import { glob } from "astro/loaders"; // Not available with legacy API
const slides = defineCollection({
loader: glob({ pattern: "**/*.md", base: "./src/slides" }),
schema: z.object({
title: z.string(),
description: z.string(),
authors: z.array(z.string()),
publishedAt: z.coerce.date(),
}),
});
export const collections = { slides };

View File

@ -5,7 +5,7 @@ export interface Props {
description?: string;
}
import Layout from "./BaseLayout.astro";
import "@themes/hnrq.scss";
import "@theme";
const { title, authors, description } = Astro.props;
---

View File

@ -1,21 +1,17 @@
---
import SlideLayout from "@layouts/SlideLayout.astro";
import { getCollection, render } from "astro:content";
import { getSlides } from "@utils/getSlides";
export const getStaticPaths = async () =>
(await getCollection("slides")).map((slide) => ({
params: { id: slide.id },
props: { slide },
}));
export const getStaticPaths = () =>
getSlides().map((slide) => ({ params: { id: slide.id }, props: { slide } }));
const { slide } = Astro.props;
const { Content } = await render(slide);
---
<SlideLayout
title={slide.data.title}
authors={slide.data.authors}
description={slide.data.description}
title={slide.title}
authors={slide.authors}
description={slide.description}
>
<Content />
<slide.default />
</SlideLayout>

View File

@ -1,10 +1,8 @@
---
import Layout from "@layouts/BaseLayout.astro";
import { getCollection } from "astro:content";
import { getSlides } from "@utils/getSlides";
const slides = (await getCollection("slides")).sort((c1, c2) =>
c1.data.title > c2.data.title ? -1 : 1
);
const slides = getSlides().sort((c1, c2) => (c1.title > c2.title ? -1 : 1));
---
<Layout title="Home">
@ -13,21 +11,21 @@ const slides = (await getCollection("slides")).sort((c1, c2) =>
{
slides.map((slide) => (
<a href={`/${slide.id}`}>
<h2>{slide.data.title}</h2>
<p>{slide.data.description}</p>
<small>{slide.data.publishedAt.toLocaleDateString()}</small>
<h2>{slide.title}</h2>
<p>{slide.description}</p>
<small>{new Date(slide.publishedAt).toLocaleDateString()}</small>
</a>
))
}
</Layout>
<style lang="scss">
@use "@themes/hnrq";
<style>
@import "@theme";
body {
background-color: hnrq.$backgroundColor;
color: hnrq.$mainColor;
font-family: hnrq.$mainFont;
background-color: var(--background);
color: var(--main-color);
font-family: var(--main-font);
box-sizing: border-box;
display: flex;
flex-direction: column;
@ -43,17 +41,17 @@ const slides = (await getCollection("slides")).sort((c1, c2) =>
h4,
h5,
h6 {
font-family: hnrq.$headingFont;
font-family: var(--heading-font);
}
a {
padding: 2rem;
color: hnrq.$mainColor;
color: var(--main-color);
text-decoration: none;
transition: all 0.2s ease-in-out;
transition: filter 0.2s ease-in-out;
background-color: var(--background);
&:hover {
background: hnrq.$mainColor;
color: hnrq.$backgroundColor;
filter: invert(1);
}
}
</style>

View File

@ -0,0 +1,92 @@
---
export const title = "CSS Flexbox"
export const authors = ["Henrique Ramos"]
export const publishedAt = "2025-01-27"
export const description = "Do you even flex?"
---
<section>
<h2>What is Flexbox?</h2>
<ul>
<li>One-dimensional layout model</li>
<li>Distributes space along a single direction</li>
<li>Powerful alignment capabilities</li>
</ul>
</section>
<section>
<h2>Basic Flexbox Container</h2>
<div class="demo">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
<pre><code data-trim>
.container {'{'}
display: flex;
{'}'}
</code></pre>
</section>
<section>
<h2>justify-content</h2>
<div class="demo" style="justify-content: space-between">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
<pre><code data-trim>
.container {'{'}
justify-content: space-between;
{'}'}
</code></pre>
</section>
<section>
<h2>align-items</h2>
<div class="demo" style="align-items: center; height: 300px">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
<pre><code data-trim>
.container {'{'}
align-items: center;
{'}'}
</code></pre>
</section>
<section>
<h2>flex-direction</h2>
<div class="demo" style="flex-direction: column">
<div class="box">1</div>
<div class="box">2</div>
<div class="box">3</div>
</div>
<pre><code data-trim>
.container {'{'}
flex-direction: column;
{'}'}
</code></pre>
</section>
<style>
.demo {
display: flex;
margin: 20px;
padding: 20px;
border: 2px solid #fff;
min-height: 200px;
}
.box {
width: 100px;
height: 100px;
background: #4a9eff;
margin: 10px;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
</style>

View File

@ -1,8 +1,8 @@
---
title: "Finite State Machines with XState for Game Development using Three.js"
authors: ["Henrique Ramos"]
publishedAt: 2024-12-13
description: "Using XState Finite State Machines to coordinate character actions in a Three.js game"
export const title = "Finite State Machines with XState for Game Development using Three.js"
export const authors = ["Henrique Ramos"]
export const publishedAt = "2024-12-13"
export const description = "Using XState Finite State Machines to coordinate character actions in a Three.js game"
---
<section>

View File

@ -0,0 +1,38 @@
import type { AstroInstance } from "astro";
type Opts<T extends Record<string, unknown>> = {
files: Record<string, T>;
requiredFields?: string[];
};
/**
* Get all Astro pages from a given path.
* @param opts - The options for the function.
* @param opts.files - The files to get the Astro pages from. Use import.meta.glob eager: true.
* @param opts.requiredFields - The required fields for the Astro pages.
* @returns The Astro pages in the root of the given path, or looks for index.astro files in subdirectories (single level).
*/
const getAstroPages = <T extends Record<string, unknown> & AstroInstance>({
files,
requiredFields = [],
}: Opts<T>) =>
Object.values(files).map((module) => {
if (!requiredFields.every((field) => module[field as keyof T])) {
throw new Error(
`Missing required fields for ${module.file}: ${requiredFields
.filter((field) => !module[field as keyof T])
.join(", ")}`,
);
}
return {
id: (
module.file
.split("/")
.at(module.file.includes("index.astro") ? -2 : -1) ?? ""
).replace(".astro", ""),
...module,
};
});
export default getAstroPages;

25
src/utils/getSlides.ts Normal file
View File

@ -0,0 +1,25 @@
import type { AstroInstance } from "astro";
import getAstroPages from "./getAstroPages";
type Slide = AstroInstance & {
[key: string]: unknown;
title: string;
description: string;
authors: string[];
publishedAt: string;
};
const slideRequiredFields = ["title", "description", "authors", "publishedAt"];
/**
* Get all slides from the slides directory.
* @returns The slides.
*/
export const getSlides = () =>
getAstroPages<Slide>({
files: import.meta.glob<true, string, Slide>(
["@slides/**/index.astro", "@slides/*.astro"],
{ eager: true },
),
requiredFields: slideRequiredFields,
});