This commit is contained in:
Henry Hiles 2023-06-21 11:23:43 -04:00
parent c0a63a4a97
commit 2c103f70ae
18 changed files with 205 additions and 84 deletions

View file

@ -15,6 +15,6 @@
"@types/marked": "^5.0.0", "@types/marked": "^5.0.0",
"astro": "^2.6.4", "astro": "^2.6.4",
"astro-icon": "^0.8.1", "astro-icon": "^0.8.1",
"marked": "^5.1.0" "micromark": "^3.2.0"
} }
} }

10
pnpm-lock.yaml generated
View file

@ -6,7 +6,7 @@ specifiers:
'@types/marked': ^5.0.0 '@types/marked': ^5.0.0
astro: ^2.6.4 astro: ^2.6.4
astro-icon: ^0.8.1 astro-icon: ^0.8.1
marked: ^5.1.0 micromark: ^3.2.0
dependencies: dependencies:
',mmarked': link:@types/,mmarked ',mmarked': link:@types/,mmarked
@ -14,7 +14,7 @@ dependencies:
'@types/marked': 5.0.0 '@types/marked': 5.0.0
astro: 2.6.4 astro: 2.6.4
astro-icon: 0.8.1 astro-icon: 0.8.1
marked: 5.1.0 micromark: 3.2.0
packages: packages:
@ -1868,12 +1868,6 @@ packages:
resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==} resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
dev: false dev: false
/marked/5.1.0:
resolution: {integrity: sha512-z3/nBe7aTI8JDszlYLk7dDVNpngjw0o1ZJtrA9kIfkkHcIF+xH7mO23aISl4WxP83elU+MFROgahqdpd05lMEQ==}
engines: {node: '>= 18'}
hasBin: true
dev: false
/mdast-util-definitions/5.1.2: /mdast-util-definitions/5.1.2:
resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==} resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==}
dependencies: dependencies:

View file

@ -2,27 +2,56 @@
import styles from "../styles/BlogPost.module.css" import styles from "../styles/BlogPost.module.css"
import Divider from "../components/Divider.astro" import Divider from "../components/Divider.astro"
import type { CollectionEntry } from "astro:content" import { CollectionEntry, getCollection } from "astro:content"
interface Props { interface Props {
post: CollectionEntry<"blog"> post: CollectionEntry<"blog">
standalone?: Boolean
} }
const { data: post, render } = Astro.props.post const { post: postEntry, standalone = true } = Astro.props
const { data: post, render } = postEntry
const { Content } = await render() const { Content } = await render()
const pubDate = new Intl.DateTimeFormat("en-US", { const pubDate = new Intl.DateTimeFormat("en-US", {
month: "long", month: "long",
day: "numeric", day: "numeric",
year: "numeric" year: "numeric"
}).format(post.pubDate) }).format(post.pubDate)
const blog = await getCollection("blog")
const index = blog.findIndex((blogPost) => blogPost.id === postEntry.id)
const prev = blog[index - 1]
const next = blog[index + 1]
--- ---
<section class={styles.jumbo}> <section class={`${styles.jumbo} ${standalone ? styles.standalone : ""}`}>
<section class={styles.top}>
{standalone && <a href="/blog">&lt; Go Back</a>}
<span class={styles.date}>{pubDate}</span> <span class={styles.date}>{pubDate}</span>
</section>
<h2 class={styles.title}> <h2 class={styles.title}>
{post.title} {post.title}
</h2> </h2>
<Divider /> <Divider />
<article class={styles.description}> <article class={styles.description}>
<Content /> {standalone ? <Content /> : post.description}
</article> </article>
{standalone ? "" : <span class={styles.more}>Read More</span>}
{
standalone ? (
<section class={styles.links}>
{prev ? (
<a href={`/blog/${prev.slug}`}>&lt; {prev.data.title}</a>
) : (
<span>&lt; Previous Article</span>
)}
{next ? (
<a href={`/blog/${next.slug}`}>{next.data.title} &gt;</a>
) : (
<span>Next Article &gt;</span>
)}
</section>
) : (
""
)
}
</section> </section>

View file

@ -1,6 +0,0 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
---
# **Test**

View file

@ -1,6 +0,0 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
---
# **Test**

View file

@ -1,6 +0,0 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
---
# **Test**

View file

@ -1,6 +0,0 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
---
# **Test**

View file

@ -1,6 +0,0 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
---
# **Test**

View file

@ -0,0 +1,10 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
category: "other"
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
---
Lorem ipsum dolor sit **amet**, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit **amet**, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

View file

@ -0,0 +1,8 @@
---
title: "Welcome to my blog!"
pubDate: 1687027268667
category: "other"
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

View file

@ -1,4 +1,8 @@
--- ---
title: "Welcome to my blog!" title: "Welcome to my blog!"
pubDate: 1687027268667 pubDate: 1687027268667
category: "other"
description: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
--- ---
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

View file

@ -3,24 +3,26 @@ import { rssSchema } from "@astrojs/rss"
const blogCollection = defineCollection({ const blogCollection = defineCollection({
type: "content", type: "content",
schema: rssSchema schema: rssSchema.extend({
category: z.enum(["web", "bot", "linux", "other"])
})
}) })
const projectsCollection = defineCollection({ const projectsCollection = defineCollection({
type: "content", type: "content",
schema: z.object({ schema: z.object({
name: z.string(), name: z.string(),
github: z.string().optional(), github: z.string().url().optional(),
overview: z.string(), overview: z.string(),
customLink: z customLink: z
.object({ .object({
name: z.string(), name: z.string(),
url: z.string() url: z.string().url()
}) })
.optional(), .optional(),
mainImage: z.string(), mainImage: z.string(),
thumbImage: z.string(), thumbImage: z.string(),
demoLink: z.string().optional() demoLink: z.string().url().optional()
}) })
}) })

View file

@ -12,6 +12,23 @@ const posts = await getCollection("blog")
<div class={styles.container}> <div class={styles.container}>
<aside class={styles.sidebar}> <aside class={styles.sidebar}>
<input type="text" placeholder="Search..." id="search" /> <input type="text" placeholder="Search..." id="search" />
<section class={styles.radios}>
<input type="radio" name="category" id="all" checked />
<label for="all">All</label>
<input type="radio" name="category" id="linux" />
<label for="linux">Linux</label>
<input type="radio" name="category" id="web" />
<label for="web">Web Development</label>
<input type="radio" name="category" id="bot" />
<label for="bot">Discord Bot Development</label>
<input type="radio" name="category" id="other" />
<label for="other">Other</label>
</section>
</aside> </aside>
<div class={styles.right}> <div class={styles.right}>
<article class={styles.description}> <article class={styles.description}>
@ -29,9 +46,9 @@ const posts = await getCollection("blog")
class={styles.link} class={styles.link}
href={`/blog/${post.slug}`} href={`/blog/${post.slug}`}
data-title={post.data.title} data-title={post.data.title}
data-tags="" data-category={post.data.category}
> >
<BlogPost post={post} /> <BlogPost post={post} standalone={false} />
</a> </a>
)) ))
} }
@ -40,23 +57,63 @@ const posts = await getCollection("blog")
</Layout> </Layout>
<script> <script>
let category = "all"
const searchBar = document.querySelector("#search") as HTMLInputElement const searchBar = document.querySelector("#search") as HTMLInputElement
const posts = document.querySelectorAll( const posts = document.querySelectorAll(
"[data-title]" "[data-title]"
) as NodeListOf<HTMLElement> ) as NodeListOf<HTMLElement>
const radios = document.querySelectorAll('input[type="radio"]')
const urlParams = new URLSearchParams(location.search)
const set = (name: string, value: string) => {
urlParams.set(name, value)
history.replaceState({}, "", `?${urlParams}`)
}
radios.forEach((radio) =>
radio.addEventListener("click", ({ target }) => {
if (target instanceof HTMLInputElement) {
set("category", target.id)
category = target.id
filterByCategory()
}
})
)
const search = () => const search = () =>
posts.forEach((post) => filter(
post.setAttribute( (post) =>
"aria-hidden", !!post.dataset.title
post.dataset.title
?.toLowerCase() ?.toLowerCase()
?.includes(searchBar.value.toLowerCase()) ?.includes(searchBar.value.toLowerCase())
? "false"
: "true"
)
) )
searchBar?.addEventListener("input", search) const filterByCategory = () =>
addEventListener("load", search) filter((post) =>
category == "all" ? true : post.dataset.category == category
)
const filter = (condition: (element: HTMLElement) => boolean) =>
posts.forEach((post) =>
post.setAttribute("aria-hidden", (!condition(post)).toString())
)
searchBar?.addEventListener("input", () => {
set("search", searchBar.value)
search()
})
addEventListener("load", () => {
searchBar.value = urlParams.get("search") ?? ""
Array.from(radios).find((radio) => {
if (
radio instanceof HTMLInputElement &&
radio.id == urlParams.get("category")
) {
radio.checked = true
category = radio.id
}
})
search()
filterByCategory()
})
</script> </script>

View file

@ -1,6 +1,6 @@
import rss from "@astrojs/rss" import rss from "@astrojs/rss"
import { getCollection } from "astro:content" import { getCollection } from "astro:content"
import { marked } from "marked" import { micromark } from "micromark"
export const get = async (context) => { export const get = async (context) => {
const blog = await getCollection("blog") const blog = await getCollection("blog")
@ -11,10 +11,7 @@ export const get = async (context) => {
site: context.site, site: context.site,
items: blog.map((post) => ({ items: blog.map((post) => ({
link: `/blog/${post.slug}/`, link: `/blog/${post.slug}/`,
content: marked.parse(post.body, { content: micromark(post.body),
mangle: false,
headerIds: false
}),
...post.data ...post.data
})), })),
customData: "<language>en-us</language>", customData: "<language>en-us</language>",

View file

@ -1,9 +1,7 @@
.link { .link {
display: flex; text-decoration: none;
width: 100%; width: 100%;
color: unset; color: unset;
text-decoration: none;
justify-content: center;
} }
.description { .description {
@ -35,6 +33,7 @@
.sidebar { .sidebar {
display: flex; display: flex;
position: sticky; position: sticky;
gap: 1rem;
top: 8rem; top: 8rem;
height: 80vh; height: 80vh;
flex-direction: column; flex-direction: column;
@ -47,6 +46,21 @@
width: 100%; width: 100%;
} }
[aria-hidden="true"] { [aria-hidden="true"],
.radios [type="radio"] {
display: none; display: none;
} }
.radios :checked + label {
color: var(--primary);
font-weight: bold;
}
label {
font-size: 1.3rem;
}
.radios {
display: flex;
flex-direction: column;
}

View file

@ -1,15 +1,38 @@
.jumbo { .jumbo {
display: flex; display: flex;
padding: 1rem 1.5rem;
flex-direction: column; flex-direction: column;
position: relative;
align-items: center; align-items: center;
} }
.jumbo.standalone {
max-width: 80rem;
}
.more {
margin-top: 1.5rem;
padding: 1rem;
border: 2px solid var(--primary);
border-radius: 20rem;
backdrop-filter: blur(2rem);
background: rgb(0 0 0 / 0.3);
box-shadow: 0 0 2rem black;
transition: scale 0.2s;
}
.more:hover {
scale: 1.05;
text-decoration: underline;
}
.date { .date {
position: absolute; color: var(--muted);
top: 1rem; }
right: 1rem;
color: hsl(37 10% 58%); .top {
display: flex;
justify-content: space-between;
width: 100%;
} }
.title { .title {
@ -18,9 +41,9 @@
text-align: center; text-align: center;
} }
.description p { .description {
font-size: 1.3rem; font-size: 1.3rem;
max-width: 40rem; max-width: 60rem;
text-align: justify; text-align: justify;
} }
@ -37,8 +60,20 @@
font-size: 1.6rem; font-size: 1.6rem;
} }
.description :is(strong, em) { .links {
display: block;
width: 100%; width: 100%;
text-align: justify; display: flex;
justify-content: space-between;
}
.links * {
font-size: 1.2rem;
}
.links a {
color: white;
}
.links span {
color: var(--muted);
} }

View file

@ -3,8 +3,8 @@
background: rgb(0 0 0 / 0.1); background: rgb(0 0 0 / 0.1);
border: 2px solid var(--primary); border: 2px solid var(--primary);
backdrop-filter: blur(2rem); backdrop-filter: blur(2rem);
border-radius: 2rem; border-radius: 1.5rem;
padding: 2rem; padding: 1.5rem;
text-decoration: none; text-decoration: none;
white-space: nowrap; white-space: nowrap;
height: 100%; height: 100%;

View file

@ -2,8 +2,9 @@
color-scheme: dark light; color-scheme: dark light;
scroll-behavior: smooth; scroll-behavior: smooth;
--primary: #4e94e4; --primary: hsl(212, 74%, 60%);
--secondary: #354b5f; --secondary: hsl(209, 28%, 29%);
--muted: hsl(0, 0%, 54%);
} }
body { body {
@ -40,7 +41,7 @@ main {
align-items: center; align-items: center;
} }
:is(article, section, aside):not(section *, nav *, header *) { :is(article, section, aside):not(section *, nav *, header *, aside *) {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-radius: 1rem; border-radius: 1rem;
@ -52,7 +53,7 @@ main {
} }
:is(article, section, aside):not(section *, nav *, header *):hover { :is(article, section, aside):not(section *, nav *, header *):hover {
scale: 102%; scale: 101.5%;
} }
@media (min-width: 500px) { @media (min-width: 500px) {