From 14398cadd704816dc9c94527228ea6bc5d8c19b0 Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 4 Mar 2026 19:07:09 +0000 Subject: [PATCH] Switch to version 1: modified src/components/sections/blog/BlogCardThree.tsx --- .../sections/blog/BlogCardThree.tsx | 468 ++++++++++-------- 1 file changed, 270 insertions(+), 198 deletions(-) diff --git a/src/components/sections/blog/BlogCardThree.tsx b/src/components/sections/blog/BlogCardThree.tsx index e1b4544..9e1f326 100644 --- a/src/components/sections/blog/BlogCardThree.tsx +++ b/src/components/sections/blog/BlogCardThree.tsx @@ -1,216 +1,288 @@ "use client"; -import React, { useEffect, useRef } from 'react'; -import gsap from 'gsap'; -import { ScrollTrigger } from 'gsap/ScrollTrigger'; -import TimelineHorizontalCardStack from '@/components/cardStack/layouts/timelines/TimelineHorizontalCardStack'; +import { memo } from "react"; +import Image from "next/image"; +import CardStack from "@/components/cardStack/CardStack"; +import Tag from "@/components/shared/Tag"; +import MediaContent from "@/components/shared/MediaContent"; +import OverlayArrowButton from "@/components/shared/OverlayArrowButton"; +import { cls, shouldUseInvertedText } from "@/lib/utils"; +import { useTheme } from "@/providers/themeProvider/ThemeProvider"; +import type { BlogPost } from "@/lib/api/blog"; +import type { LucideIcon } from "lucide-react"; +import type { ButtonConfig, CardAnimationType, TitleSegment, ButtonAnimationType } from "@/components/cardStack/types"; +import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; -gsap.registerPlugin(ScrollTrigger); +type BlogCard = BlogPost; interface BlogCardThreeProps { - blogs: Array<{ - id: string; - category: string; + blogs: BlogCard[]; + carouselMode?: "auto" | "buttons"; + uniformGridCustomHeightClasses?: string; + animationType: CardAnimationType; title: string; - excerpt: string; - imageSrc: string; - imageAlt?: string; - authorName: string; - authorAvatar: string; - date: string; - onBlogClick?: () => void; - }>; - carouselMode?: 'auto' | 'buttons'; - uniformGridCustomHeightClasses?: string; - animationType: 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; - title: string; - titleSegments?: Array<{ type: 'text'; content: string } | { type: 'image'; src: string; alt?: string }>; - description: string; - tag?: string; - tagIcon?: React.ComponentType; - tagAnimation?: 'none' | 'opacity' | 'slide-up' | 'blur-reveal'; - buttons?: Array<{ text: string; onClick?: () => void; href?: string }>; - buttonAnimation?: 'none' | 'opacity' | 'slide-up' | 'blur-reveal'; - textboxLayout: 'default' | 'split' | 'split-actions' | 'split-description' | 'inline-image'; - useInvertedBackground: boolean; - ariaLabel?: string; - className?: string; - containerClassName?: string; - cardClassName?: string; - imageWrapperClassName?: string; - imageClassName?: string; - categoryClassName?: string; - cardTitleClassName?: string; - excerptClassName?: string; - authorContainerClassName?: string; - authorAvatarClassName?: string; - authorNameClassName?: string; - dateClassName?: string; - textBoxTitleImageClassName?: string; - textBoxDescriptionClassName?: string; - gridClassName?: string; - carouselClassName?: string; - controlsClassName?: string; - textBoxClassName?: string; - textBoxTagClassName?: string; - textBoxButtonContainerClassName?: string; - textBoxButtonClassName?: string; - textBoxButtonTextClassName?: string; + titleSegments?: TitleSegment[]; + description: string; + tag?: string; + tagIcon?: LucideIcon; + tagAnimation?: ButtonAnimationType; + buttons?: ButtonConfig[]; + buttonAnimation?: ButtonAnimationType; + textboxLayout: TextboxLayout; + useInvertedBackground: InvertedBackground; + ariaLabel?: string; + className?: string; + containerClassName?: string; + cardClassName?: string; + cardContentClassName?: string; + categoryTagClassName?: string; + cardTitleClassName?: string; + excerptClassName?: string; + authorContainerClassName?: string; + authorAvatarClassName?: string; + authorNameClassName?: string; + dateClassName?: string; + mediaWrapperClassName?: string; + mediaClassName?: string; + textBoxTitleClassName?: string; + textBoxTitleImageWrapperClassName?: string; + textBoxTitleImageClassName?: string; + textBoxDescriptionClassName?: string; + gridClassName?: string; + carouselClassName?: string; + controlsClassName?: string; + textBoxClassName?: string; + textBoxTagClassName?: string; + textBoxButtonContainerClassName?: string; + textBoxButtonClassName?: string; + textBoxButtonTextClassName?: string; } -const BlogCardThree: React.FC = ({ - blogs, - carouselMode = 'buttons', - uniformGridCustomHeightClasses = 'min-h-95 2xl:min-h-105', - animationType, - title, - titleSegments, - description, - tag, - tagIcon: TagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout, - useInvertedBackground, - ariaLabel = 'Blog section', - className = '', - containerClassName = '', - cardClassName = '', - imageWrapperClassName = '', - imageClassName = '', - categoryClassName = '', - cardTitleClassName = '', - excerptClassName = '', - authorContainerClassName = '', - authorAvatarClassName = '', - authorNameClassName = '', - dateClassName = '', - textBoxTitleImageClassName = '', - textBoxDescriptionClassName = '', - gridClassName = '', - carouselClassName = '', - controlsClassName = '', - textBoxClassName = '', - textBoxTagClassName = '', - textBoxButtonContainerClassName = '', - textBoxButtonClassName = '', - textBoxButtonTextClassName = '', -}) => { - const sectionRef = useRef(null); - const cardsRef = useRef<(HTMLDivElement | null)[]>([]); +interface BlogCardItemProps { + blog: BlogCard; + useInvertedBackground: boolean; + cardClassName?: string; + cardContentClassName?: string; + categoryTagClassName?: string; + cardTitleClassName?: string; + excerptClassName?: string; + authorContainerClassName?: string; + authorAvatarClassName?: string; + authorNameClassName?: string; + dateClassName?: string; + mediaWrapperClassName?: string; + mediaClassName?: string; +} - useEffect(() => { - if (animationType === 'none' || !sectionRef.current) return; +const BlogCardItem = memo(({ + blog, + useInvertedBackground, + cardClassName = "", + cardContentClassName = "", + categoryTagClassName = "", + cardTitleClassName = "", + excerptClassName = "", + authorContainerClassName = "", + authorAvatarClassName = "", + authorNameClassName = "", + dateClassName = "", + mediaWrapperClassName = "", + mediaClassName = "", +}: BlogCardItemProps) => { + const theme = useTheme(); + const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle); - const cards = cardsRef.current.filter((card): card is HTMLDivElement => card !== null); - if (cards.length === 0) return; + return ( +
+
+ - cards.forEach((card, index) => { - gsap.set(card, { opacity: 0, y: 20 }); +

+ {blog.title} +

- ScrollTrigger.create({ - trigger: card, - onEnter: () => { - gsap.to(card, { - opacity: 1, - y: 0, - duration: 0.6, - delay: index * 0.1, - ease: 'power2.out', - }); - }, - }); - }); +

+ {blog.excerpt} +

- return () => { - cards.forEach(() => ScrollTrigger.getAll().forEach(trigger => trigger.kill())); - }; - }, [animationType]); + {(blog.authorName || blog.date) && ( +
+ {blog.authorAvatar && ( + {blog.authorName + )} + {blog.authorAvatar ? ( +
+ {blog.authorName && ( +

+ {blog.authorName} +

+ )} + {blog.date && ( +

+ {blog.date} +

+ )} +
+ ) : ( + <> + {blog.authorName && ( +

+ {blog.authorName} +

+ )} + {blog.date && ( +

+ {blog.date} +

+ )} + + )} +
+ )} +
- const gridVariant = blogs.length <= 4 ? 'uniform-all-items-equal' : 'uniform-all-items-equal'; - const mode = blogs.length >= 5 ? 'auto' : carouselMode; +
+ + +
+
+ ); +}); - const blogChildren = blogs.map((blog, index) => ( -
{ - cardsRef.current[index] = el; - }} - className={`cursor-pointer group ${cardClassName}`} - onClick={blog.onBlogClick} - > - {/* Image wrapper with overlay arrow */} -
- {blog.imageAlt -
-
-
-
+BlogCardItem.displayName = "BlogCardItem"; - {/* Category badge */} -
- {blog.category} -
- - {/* Title */} -

- {blog.title} -

- - {/* Excerpt */} -

- {blog.excerpt} -

- - {/* Author info */} -
- {blog.authorName} -
-

- {blog.authorName} -

-

{blog.date}

-
-
-
- )); - - return ( -
- -
- ); +const BlogCardThree = ({ + blogs = [], + carouselMode = "buttons", + uniformGridCustomHeightClasses = "min-h-none", + animationType, + title, + titleSegments, + description, + tag, + tagIcon, + tagAnimation, + buttons, + buttonAnimation, + textboxLayout, + useInvertedBackground, + ariaLabel = "Blog section", + className = "", + containerClassName = "", + cardClassName = "", + cardContentClassName = "", + categoryTagClassName = "", + cardTitleClassName = "", + excerptClassName = "", + authorContainerClassName = "", + authorAvatarClassName = "", + authorNameClassName = "", + dateClassName = "", + mediaWrapperClassName = "", + mediaClassName = "", + textBoxTitleClassName = "", + textBoxTitleImageWrapperClassName = "", + textBoxTitleImageClassName = "", + textBoxDescriptionClassName = "", + gridClassName = "", + carouselClassName = "", + controlsClassName = "", + textBoxClassName = "", + textBoxTagClassName = "", + textBoxButtonContainerClassName = "", + textBoxButtonClassName = "", + textBoxButtonTextClassName = "", +}: BlogCardThreeProps) => { + return ( + + {blogs.map((blog) => ( + + ))} + + ); }; -export default BlogCardThree; \ No newline at end of file +BlogCardThree.displayName = "BlogCardThree"; + +export default BlogCardThree;