Files
librechat.ai/components/blog/BlogCard.tsx
Danny Avila a0a74501c9 chore: bump dev packages, linting, logos (#521)
* chore: upgrade eslint to v9

* chore: update package dependencies in package.json and pnpm-lock.yaml

- Added `minimatch` and `serialize-javascript` dependencies with updated versions.
- Upgraded `ajv` to version 6.14.0.
- Removed outdated dependencies from pnpm-lock.yaml for better package management.

* feat: add Stripe logos to Companies section

- Introduced new company entry for Stripe in the Companies component, including both light and dark logo variants.
- Updated the Companies array to display 10 logos instead of 8.
- Adjusted TypeScript environment reference to point to the development types directory.
2026-03-02 18:18:50 -05:00

99 lines
3.3 KiB
TypeScript

import { useState, useEffect } from 'react'
import Image from 'next/image'
import { useRouter } from 'next/router'
import type { Page, MdxFile } from 'nextra'
import { Author } from '../Author/Authors'
import { Video } from '../Video'
const BlogCard = ({
page,
handleTagClick,
selectedTags = [],
}: {
page: MdxFile & Page
handleTagClick: (tag: string) => void
selectedTags?: string[]
}) => {
const router = useRouter()
const [cardWidth, setCardWidth] = useState(0)
const [maxDescriptionLength, setMaxDescriptionLength] = useState(160)
const handleCardClick = () => {
router.push(page.route)
}
useEffect(() => {
setMaxDescriptionLength(cardWidth > 260 ? 145 : 46) // Adjust maxLength based on card width
}, [cardWidth])
useEffect(() => {
const updateCardWidth = () => {
setCardWidth(document.querySelector('.blog-card')?.clientWidth ?? 0)
}
window.addEventListener('resize', updateCardWidth)
updateCardWidth()
return () => {
window.removeEventListener('resize', updateCardWidth)
}
}, [])
const truncateDescription = (text) => {
if (text.length > maxDescriptionLength) {
return text.slice(0, maxDescriptionLength) + '...'
}
return text
}
return (
<div className="bg-popover rounded-lg shadow-md overflow-hidden blog-card">
<div
className="relative h-52 md:h-64 mb-1 overflow-hidden scale-100 transition-transform hover:scale-105 cursor-pointer"
onClick={handleCardClick}
style={{ transformOrigin: 'bottom center' }}
>
{page.frontMatter?.ogVideo ? (
<Video src={page.frontMatter.ogVideo} gifStyle className="object-cover size-full mt-0" />
) : page.frontMatter?.ogImage ? (
<Image
src={page.frontMatter.gif ?? page.frontMatter.ogImage}
width={1200}
height={675}
className="object-cover absolute top-0 left-0 size-full"
alt={page.frontMatter?.title ?? 'Blog post image'}
unoptimized={
page.frontMatter.gif !== undefined || page.frontMatter.ogImage?.endsWith('.gif')
}
/>
) : null}
</div>
<div className="p-4 pt-2 h-56 overflow-hidden relative">
<div className="items-center justify-between mb-2">
{page.frontMatter?.tags?.map((tag) => (
<span
key={tag}
className={`cursor-pointer text-xs py-1 px-2 bg-background/80 shadow-md rounded-md mx-1 ${
selectedTags.includes(tag) ? 'bg-gray-700/20' : ''
}`}
onClick={() => handleTagClick(tag)}
>
{tag}
</span>
))}
</div>
{/* Modified title and description to be clickable */}
<div className="mb-2 mx-1 cursor-pointer" onClick={handleCardClick}>
<h2 className="font-mono text-xl mb-2 font-bold">
{page.meta?.title || page.frontMatter?.title || page.name}
</h2>
<div>{truncateDescription(page.frontMatter?.description || '')}</div>
</div>
<div className="flex items-center justify-between absolute bottom-4 inset-x-4">
<Author authorid={page.frontMatter?.authorid} />
<span className="text-sm opacity-60">{page.frontMatter?.date}</span>
</div>
</div>
</div>
)
}
export default BlogCard