mirror of
https://github.com/waynesutton/markdown-site.git
synced 2026-01-12 04:09:14 +00:00
Update: Blog page featured layout ui, mobile menu padding and font change
This commit is contained in:
89
src/components/BlogHeroCard.tsx
Normal file
89
src/components/BlogHeroCard.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import { Link } from "react-router-dom";
|
||||
import { format, parseISO } from "date-fns";
|
||||
|
||||
interface BlogHeroCardProps {
|
||||
slug: string;
|
||||
title: string;
|
||||
description: string;
|
||||
date: string;
|
||||
tags: string[];
|
||||
readTime?: string;
|
||||
image?: string;
|
||||
excerpt?: string;
|
||||
authorName?: string;
|
||||
authorImage?: string;
|
||||
}
|
||||
|
||||
// Hero card component for featured blog post on /blog page
|
||||
// Displays as a large card with image on left, content on right (like Giga.ai/news)
|
||||
export default function BlogHeroCard({
|
||||
slug,
|
||||
title,
|
||||
description,
|
||||
date,
|
||||
tags,
|
||||
readTime,
|
||||
image,
|
||||
excerpt,
|
||||
authorName,
|
||||
authorImage,
|
||||
}: BlogHeroCardProps) {
|
||||
return (
|
||||
<Link to={`/${slug}`} className="blog-hero-card">
|
||||
{/* Hero image on the left */}
|
||||
{image && (
|
||||
<div className="blog-hero-image-wrapper">
|
||||
<img
|
||||
src={image}
|
||||
alt={title}
|
||||
className="blog-hero-image"
|
||||
loading="eager"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Content on the right */}
|
||||
<div className="blog-hero-content">
|
||||
{/* Tags displayed as labels */}
|
||||
{tags.length > 0 && (
|
||||
<div className="blog-hero-tags">
|
||||
{tags.slice(0, 2).map((tag) => (
|
||||
<span key={tag} className="blog-hero-tag">
|
||||
{tag}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Date */}
|
||||
<time className="blog-hero-date">
|
||||
{format(parseISO(date), "MMM d, yyyy").toUpperCase()}
|
||||
</time>
|
||||
|
||||
{/* Title */}
|
||||
<h2 className="blog-hero-title">{title}</h2>
|
||||
|
||||
{/* Description or excerpt */}
|
||||
<p className="blog-hero-excerpt">{excerpt || description}</p>
|
||||
|
||||
{/* Author info and read more */}
|
||||
<div className="blog-hero-footer">
|
||||
{authorName && (
|
||||
<div className="blog-hero-author">
|
||||
{authorImage && (
|
||||
<img
|
||||
src={authorImage}
|
||||
alt={authorName}
|
||||
className="blog-hero-author-image"
|
||||
/>
|
||||
)}
|
||||
<span className="blog-hero-author-name">{authorName}</span>
|
||||
</div>
|
||||
)}
|
||||
{readTime && <span className="blog-hero-read-time">{readTime}</span>}
|
||||
<span className="blog-hero-read-more">Read more</span>
|
||||
</div>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
@@ -221,10 +221,12 @@ export default function Layout({ children }: LayoutProps) {
|
||||
</nav>
|
||||
</MobileMenu>
|
||||
|
||||
{/* Use wider layout for stats page, normal layout for other pages */}
|
||||
{/* Use wider layout for stats and blog pages, normal layout for other pages */}
|
||||
<main
|
||||
className={
|
||||
location.pathname === "/stats" ? "main-content-wide" : "main-content"
|
||||
location.pathname === "/stats" || location.pathname === "/blog"
|
||||
? "main-content-wide"
|
||||
: "main-content"
|
||||
}
|
||||
>
|
||||
{children}
|
||||
|
||||
@@ -16,6 +16,8 @@ interface Post {
|
||||
interface PostListProps {
|
||||
posts: Post[];
|
||||
viewMode?: "list" | "cards";
|
||||
columns?: 2 | 3; // Number of columns for card view (default: 3)
|
||||
showExcerpts?: boolean; // Show excerpts in card view (default: true)
|
||||
}
|
||||
|
||||
// Group posts by year
|
||||
@@ -33,7 +35,12 @@ function groupByYear(posts: Post[]): Record<string, Post[]> {
|
||||
);
|
||||
}
|
||||
|
||||
export default function PostList({ posts, viewMode = "list" }: PostListProps) {
|
||||
export default function PostList({
|
||||
posts,
|
||||
viewMode = "list",
|
||||
columns = 3,
|
||||
showExcerpts = true,
|
||||
}: PostListProps) {
|
||||
// Sort posts by date descending
|
||||
const sortedPosts = [...posts].sort(
|
||||
(a, b) => new Date(b.date).getTime() - new Date(a.date).getTime()
|
||||
@@ -41,8 +48,11 @@ export default function PostList({ posts, viewMode = "list" }: PostListProps) {
|
||||
|
||||
// Card view: render all posts in a grid
|
||||
if (viewMode === "cards") {
|
||||
// Apply column class for 2 or 3 columns
|
||||
const cardGridClass =
|
||||
columns === 2 ? "post-cards post-cards-2col" : "post-cards";
|
||||
return (
|
||||
<div className="post-cards">
|
||||
<div className={cardGridClass}>
|
||||
{sortedPosts.map((post) => (
|
||||
<Link key={post._id} to={`/${post.slug}`} className="post-card">
|
||||
{/* Thumbnail image displayed as square using object-fit: cover */}
|
||||
@@ -58,7 +68,8 @@ export default function PostList({ posts, viewMode = "list" }: PostListProps) {
|
||||
)}
|
||||
<div className="post-card-content">
|
||||
<h3 className="post-card-title">{post.title}</h3>
|
||||
{(post.excerpt || post.description) && (
|
||||
{/* Only show excerpt if showExcerpts is true */}
|
||||
{showExcerpts && (post.excerpt || post.description) && (
|
||||
<p className="post-card-excerpt">
|
||||
{post.excerpt || post.description}
|
||||
</p>
|
||||
|
||||
Reference in New Issue
Block a user