mirror of
https://github.com/LibreChat-AI/librechat.ai.git
synced 2026-03-27 10:48:32 +07:00
* 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.
99 lines
3.3 KiB
TypeScript
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
|