Skip to content

Commit

Permalink
Add categories to blog
Browse files Browse the repository at this point in the history
  • Loading branch information
djfarrelly committed Nov 1, 2024
1 parent 5251e31 commit 647316e
Show file tree
Hide file tree
Showing 31 changed files with 319 additions and 159 deletions.
38 changes: 38 additions & 0 deletions components/Blog/BlogHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { Rss } from "react-feather";
import Dropdown from "../Dropdown";
import { BLOG_CATEGORIES } from "./index";

export default function BlogHeader({ description }: { description: string }) {
const categoryLinks = Object.keys(BLOG_CATEGORIES).map((k) => ({
href: `/blog/category/${k}`,
text: BLOG_CATEGORIES[k],
}));
return (
<div className="flex flex-col lg:flex-row gap-2 lg:gap-4 items-start lg:items-center">
<div className="flex flex-row items-center justify-between w-full lg:w-auto">
<h2 className="font-bold text-base text-white lg:border-r border-carbon-600/50 pr-4">
Blog
</h2>
<Dropdown
title="Categories"
items={categoryLinks}
className="block lg:hidden"
/>
</div>
<div className="flex flex-row gap-2 lg:gap-4 items-center">
<p className="text-carbon-200 text-sm">{description}</p>
<a
href="/api/rss.xml"
className="hidden md:block py-1 rounded-md transition-all text-carbon-300 hover:text-white border border-transparent hover:border-carbon-200/30"
>
<Rss className="h-4" />
</a>
</div>
<Dropdown
title="Categories"
items={categoryLinks}
className="hidden lg:block"
/>
</div>
);
}
42 changes: 42 additions & 0 deletions components/Blog/BlogPostList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import Image from "next/image";
import { RiCalendarLine } from "@remixicon/react";
import Tags from "src/shared/Blog/Tags";
import { type BlogPost } from "./index";

export default function BlogPostList({ posts }: { posts: BlogPost[] }) {
return (
<ul className="grid grid-cols-1 md:grid-cols-2 gap-x-8 lg:gap-x-4 xl:gap-x-8 lg:grid-cols-3 gap-y-20">
{posts.map((post) => (
<li key={post.slug}>
<a
href={post.redirect ?? `/blog/${post.slug}`}
className="group flex flex-col rounded-lg ease-out transition-all "
>
{post.image && (
<div className="flex rounded-lg shadow group-hover:scale-105 transition-all">
{/* We use 720 as the responsive view goes full width at 720px viewport width */}
<Image
className="rounded-lg"
src={post.image}
alt={`Featured image for ${post.heading} blog post`}
width={720}
height={720 / 2}
/>
</div>
)}
<div className="pt-4 xl:pt-6 xl:py-4">
<h2 className="text-base text-basis xl:text-lg text-white mb-1 group-hover:text-link transition-all">
{post.heading}
</h2>
<p className="text-muted text-sm font-medium mb-4 mt-2 flex items-center gap-1">
<RiCalendarLine className="h-3 w-3" />
{post.humanDate} <Tags tags={post.tags || []} />
</p>
<p className="text-subtle text-sm">{post.subtitle}</p>
</div>
</a>
</li>
))}
</ul>
);
}
19 changes: 19 additions & 0 deletions components/Blog/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { type MDXFileMetadata } from "src/utils/markdown";

export type BlogPost = {
heading: string;
subtitle: string;
author?: string;
image: string;
date: string;
humanDate: string;
tags?: string[];
hide?: boolean;
} & MDXFileMetadata;

// Slug -> Formatted title
export const BLOG_CATEGORIES = {
"product-updates": "Product updates",
"company-news": "Company news",
engineering: "Engineering",
};
62 changes: 62 additions & 0 deletions components/Dropdown.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use client";
import { useState } from "react";
import { RiArrowDownSLine } from "@remixicon/react";

export default function Dropdown({
title,
items,
className = "",
}: {
title: React.ReactNode;
items: {
href: string;
text: string;
target?: string;
}[];
className?: string;
}) {
const [open, setOpen] = useState(false);
/**
* NOTE - This uses md: prefixes to make the button work on hover on desktop and click on mobile
*/
return (
<div
className={`group relative z-20 border border-subtle text-basis rounded-md text-sm ${className}`}
>
<div
className="flex flex-row flex-nowrap items-center justify-start text-nowrap gap-2 px-3 py-1 rounded-md group-hover:bg-canvasSubtle"
onClick={() => setOpen(!open)}
>
{title}
<RiArrowDownSLine
className={`h-4 w-4 transition-all ${
open ? "rotate-180 md:rotate-0" : ""
}`}
/>
</div>
<div
// Only show on hover when non-mobile (> md)
className={`absolute right-0 w-full min-w-min md:hidden md:group-hover:block ${
open ? "block" : "hidden"
}`}
>
<div className="bg-transparent h-2">
{/* transparent element to persist hover */}
</div>
<ul className="flex flex-col gap-2 px-3 py-2 bg-surfaceBase border border-subtle rounded-md text-sm">
{items.map((item, idx) => (
<li className="" key={idx}>
<a
href={item.href}
target={item.target}
className="font-medium text-nowrap text-basis/90 hover:text-primary-intense"
>
{item.text}
</a>
</li>
))}
</ul>
</div>
</div>
);
}
61 changes: 17 additions & 44 deletions components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import classNames from "src/utils/classNames";
import Container from "src/shared/layout/Container";
import Menu, { type MenuProps } from "./Nav/Menu";
import { productLinks, resourcesLinks } from "./Nav/links";
import Dropdown from "./Dropdown";

// Manual count of stars on GitHub for now
// Run pnpm run github:stars to get the latest count
Expand Down Expand Up @@ -200,50 +201,22 @@ const repos = [
];

function OpenSourceButton({ className = "" }: { className?: string }) {
const [open, setOpen] = useState(false);
/**
* NOTE - This uses md: prefixes to make the button work on hover on desktop and click on mobile
*/
const items = repos.map((repo) => ({
href: `https://github.com/${repo}`,
text: repo,
target: "_blank",
}));
return (
<div
className={`group relative border border-subtle text-basis rounded-md text-sm ${className}`}
>
<div
className="flex flex-row flex-nowrap items-center justify-start text-nowrap gap-2 px-3 py-1 rounded-md group-hover:bg-canvasSubtle"
onClick={() => setOpen(!open)}
>
<span className="hidden lg:inline">Open Source</span>
<RiGithubFill className="h-4 w-4 shrink-0" />
<span>{(GITHUB_STARS / 1000).toFixed(1)}K</span>
<RiArrowDownSLine
className={`h-4 w-4 transition-all ${
open ? "rotate-180 md:rotate-0" : ""
}`}
/>
</div>
<div
// Only show on hover when non-mobile (> md)
className={`absolute right-0 w-full min-w-min md:hidden md:group-hover:block ${
open ? "block" : "hidden"
}`}
>
<div className="bg-transparent h-2">
{/* transparent element to persist hover */}
</div>
<ul className="flex flex-col gap-2 px-3 py-2 bg-surfaceBase border border-subtle rounded-md text-sm">
{repos.map((repo, idx) => (
<li className="" key={idx}>
<a
href={`https://github.com/${repo}`}
target="_blank"
className="font-medium text-nowrap text-basis/90 hover:text-primary-intense"
>
{repo}
</a>
</li>
))}
</ul>
</div>
</div>
<Dropdown
title={
<>
<span className="hidden lg:inline">Open Source</span>
<RiGithubFill className="h-4 w-4 shrink-0" />
<span>{(GITHUB_STARS / 1000).toFixed(1)}K</span>
</>
}
items={items}
className={className}
/>
);
}
71 changes: 12 additions & 59 deletions pages/blog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ import {
loadMarkdownFilesMetadata,
type MDXFileMetadata,
} from "../utils/markdown";
import BlogHeader from "src/components/Blog/BlogHeader";
import BlogPostList from "src/components/Blog/BlogPostList";
import { type BlogPost } from "src/components/Blog";
// import { LaunchWeekBanner } from "./index";

export default function BlogLayout(props) {
export default function BlogIndex(props) {
const router = useRouter();
const { showHidden } = router.query;

Expand All @@ -37,7 +40,7 @@ export default function BlogLayout(props) {
return (
<>
<Head>
<title>Inngest Product & Engineering blog</title>
<title>Inngest - Product & Engineering blog</title>
<meta name="description" content={description}></meta>
<meta
property="og:title"
Expand All @@ -52,18 +55,8 @@ export default function BlogLayout(props) {
{/* <LaunchWeekBanner urlRef="blog-feed-banner" /> */}

<Container className="pt-8">
<div className="flex flex-col lg:flex-row gap-2 lg:gap-4 items-start lg:items-center">
<h2 className="font-bold text-base text-white lg:border-r border-carbon-600/50 pr-4">
Blog
</h2>
<p className="text-carbon-200 text-sm">{description}</p>
<a
href="/api/rss.xml"
className="py-1 rounded-md transition-all text-carbon-300 hover:text-white border border-transparent hover:border-carbon-200/30"
>
<Rss className="h-4" />
</a>
</div>
<BlogHeader description={description} />

<div className="pt-16">
{focus && (
<a
Expand Down Expand Up @@ -109,39 +102,7 @@ export default function BlogLayout(props) {
</a>
)}

<ul className="grid grid-cols-1 md:grid-cols-2 gap-x-8 lg:gap-x-4 xl:gap-x-8 lg:grid-cols-3 gap-y-20">
{rest.map((item) => (
<li key={item.slug}>
<a
href={item.redirect ?? `/blog/${item.slug}`}
className="group flex flex-col rounded-lg ease-out transition-all "
>
{item.image && (
<div className="flex rounded-lg shadow group-hover:scale-105 transition-all">
{/* We use 720 as the responsive view goes full width at 720px viewport width */}
<Image
className="rounded-lg"
src={item.image}
alt={`Featured image for ${item.heading} blog post`}
width={720}
height={720 / 2}
/>
</div>
)}
<div className="pt-4 xl:pt-6 xl:py-4">
<h2 className="text-base text-basis xl:text-lg text-white mb-1 group-hover:text-link transition-all">
{item.heading}
</h2>
<p className="text-muted text-sm font-medium mb-4 mt-2 flex items-center gap-1">
<RiCalendarLine className="h-3 w-3" />
{item.humanDate} <Tags tags={item.tags || []} />
</p>
<p className="text-subtle text-sm">{item.subtitle}</p>
</div>
</a>
</li>
))}
</ul>
<BlogPostList posts={rest} />
</div>
</Container>
<Footer />
Expand All @@ -150,21 +111,13 @@ export default function BlogLayout(props) {
);
}

export type BlogPost = {
heading: string;
subtitle: string;
author?: string;
image: string;
date: string;
humanDate: string;
tags?: string[];
hide?: boolean;
} & MDXFileMetadata;

// This function also gets called at build time to generate specific content.
export async function getStaticProps() {
const posts = await loadMarkdownFilesMetadata<BlogPost>("blog/_posts");
const content = posts.map((p) => JSON.stringify(p));
// If a post is set to featured=false, do not show on main blog feed
// This can be used for less important posts that may be directly linked to from other places
const filteredPosts = posts.filter((p) => p?.featured !== false);
const content = filteredPosts.map((p) => JSON.stringify(p));

return {
props: {
Expand Down
Loading

0 comments on commit 647316e

Please sign in to comment.