-
-
Notifications
You must be signed in to change notification settings - Fork 372
Expand file tree
/
Copy pathBlogCard.tsx
More file actions
98 lines (94 loc) · 2.95 KB
/
Copy pathBlogCard.tsx
File metadata and controls
98 lines (94 loc) · 2.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import { Link } from '@tanstack/react-router'
import { Card } from '~/components/Card'
import { CoverFallback } from '~/components/CoverFallback'
import {
formatAuthors,
formatPublishedDate,
getBlogLibraries,
} from '~/utils/blog-format'
import { getOptimizedImageUrl } from '~/utils/optimizedImage'
export type BlogCardPost = {
slug: string
title: string
published: string
excerpt: string
headerImage: string | undefined
authors: string[]
library: string | undefined
}
type BlogCardProps = {
post: BlogCardPost
showLibraryBadges?: boolean
}
export function BlogCard({ post, showLibraryBadges = true }: BlogCardProps) {
const { slug, title, published, excerpt, headerImage, authors, library } =
post
const blogLibraries = showLibraryBadges ? getBlogLibraries(library) : []
return (
<Card
as={Link}
to="/blog/$"
params={{ _splat: slug } as never}
className="relative flex flex-col justify-between overflow-hidden transition-all hover:shadow-sm hover:border-blue-500"
>
{blogLibraries.length ? (
<div className="absolute right-3 top-3 z-10 flex flex-wrap justify-end gap-1">
{blogLibraries.map((blogLibrary) => (
<div
key={blogLibrary.id}
className={`rounded-md px-2 py-1 text-xs font-black uppercase shadow-sm ${blogLibrary.bgStyle} ${blogLibrary.badgeTextStyle ?? 'text-white'}`}
>
{blogLibrary.name.replace('TanStack ', '')}
</div>
))}
</div>
) : null}
{headerImage ? (
<div className="aspect-video overflow-hidden bg-gray-100 dark:bg-gray-800">
<img
src={getOptimizedImageUrl(headerImage, {
fit: 'cover',
format: 'auto',
quality: 80,
width: 800,
})}
alt=""
loading="lazy"
decoding="async"
className="w-full h-full object-cover"
/>
</div>
) : (
<CoverFallback
slug={slug}
library={library}
className="aspect-video w-full"
/>
)}
<div className="p-4 md:p-8 flex flex-col gap-4 flex-1 justify-between">
<div>
<div className="text-lg font-extrabold">{title}</div>
<div className="text-xs italic font-light mt-1">
by {formatAuthors(authors)}
{published ? (
<time dateTime={published} title={formatPublishedDate(published)}>
{' '}
on {formatPublishedDate(published)}
</time>
) : null}
</div>
{excerpt ? (
<p className="text-sm mt-4 text-gray-600 dark:text-gray-400 leading-7 line-clamp-4">
{excerpt}
</p>
) : null}
</div>
<div>
<div className="text-blue-500 uppercase font-black text-sm">
Read More
</div>
</div>
</div>
</Card>
)
}