diff --git a/src/components/sections/blog/BlogCardTwo.tsx b/src/components/sections/blog/BlogCardTwo.tsx index b5e4142..c0b3f30 100644 --- a/src/components/sections/blog/BlogCardTwo.tsx +++ b/src/components/sections/blog/BlogCardTwo.tsx @@ -1,216 +1,241 @@ "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 Badge from "@/components/shared/Badge"; +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); - -interface BlogCardTwoProps { - blogs: Array<{ - id: string; - category: string; - 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; -} - -const BlogCardTwo: 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)[]>([]); - - useEffect(() => { - if (animationType === 'none' || !sectionRef.current) return; - - const cards = cardsRef.current.filter((card): card is HTMLDivElement => card !== null); - if (cards.length === 0) return; - - cards.forEach((card, index) => { - gsap.set(card, { opacity: 0, y: 20 }); - - ScrollTrigger.create({ - trigger: card, - onEnter: () => { - gsap.to(card, { - opacity: 1, - y: 0, - duration: 0.6, - delay: index * 0.1, - ease: 'power2.out', - }); - }, - }); - }); - - return () => { - cards.forEach(() => ScrollTrigger.getAll().forEach(trigger => trigger.kill())); - }; - }, [animationType]); - - 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 -
-
-
-
- - {/* Category badge */} -
- {blog.category} -
- - {/* Title */} -

- {blog.title} -

- - {/* Excerpt */} -

- {blog.excerpt} -

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

- {blog.authorName} -

-

{blog.date}

-
-
-
- )); - - return ( -
- -
- ); +type BlogCard = Omit & { + category: string | string[]; }; -export default BlogCardTwo; \ No newline at end of file +interface BlogCardTwoProps { + blogs: BlogCard[]; + carouselMode?: "auto" | "buttons"; + uniformGridCustomHeightClasses?: string; + animationType: CardAnimationType; + title: 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; + imageWrapperClassName?: string; + imageClassName?: string; + authorAvatarClassName?: string; + authorDateClassName?: string; + cardTitleClassName?: string; + excerptClassName?: string; + categoryClassName?: 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; +} + +interface BlogCardItemProps { + blog: BlogCard; + shouldUseLightText: boolean; + cardClassName?: string; + imageWrapperClassName?: string; + imageClassName?: string; + authorAvatarClassName?: string; + authorDateClassName?: string; + cardTitleClassName?: string; + excerptClassName?: string; + categoryClassName?: string; +} + +const BlogCardItem = memo(({ + blog, + shouldUseLightText, + cardClassName = "", + imageWrapperClassName = "", + imageClassName = "", + authorAvatarClassName = "", + authorDateClassName = "", + cardTitleClassName = "", + excerptClassName = "", + categoryClassName = "", +}: BlogCardItemProps) => { + return ( +
+
+ {blog.imageAlt + +
+ +
+
+
+ {blog.authorAvatar && ( + {blog.authorName} + )} +

+ {blog.authorName} • {blog.date} +

+
+ +

+ {blog.title} +

+ +

+ {blog.excerpt} +

+
+ +
+ {Array.isArray(blog.category) ? ( + blog.category.map((cat, index) => ( + + )) + ) : ( + + )} +
+
+
+ ); +}); + +BlogCardItem.displayName = "BlogCardItem"; + +const BlogCardTwo = ({ + blogs = [], + carouselMode = "buttons", + uniformGridCustomHeightClasses, + animationType, + title, + titleSegments, + description, + tag, + tagIcon, + tagAnimation, + buttons, + buttonAnimation, + textboxLayout, + useInvertedBackground, + ariaLabel = "Blog section", + className = "", + containerClassName = "", + cardClassName = "", + imageWrapperClassName = "", + imageClassName = "", + authorAvatarClassName = "", + authorDateClassName = "", + cardTitleClassName = "", + excerptClassName = "", + categoryClassName = "", + textBoxTitleClassName = "", + textBoxTitleImageWrapperClassName = "", + textBoxTitleImageClassName = "", + textBoxDescriptionClassName = "", + gridClassName = "", + carouselClassName = "", + controlsClassName = "", + textBoxClassName = "", + textBoxTagClassName = "", + textBoxButtonContainerClassName = "", + textBoxButtonClassName = "", + textBoxButtonTextClassName = "", +}: BlogCardTwoProps) => { + const theme = useTheme(); + const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle); + + return ( + + {blogs.map((blog) => ( + + ))} + + ); +}; + +BlogCardTwo.displayName = "BlogCardTwo"; + +export default BlogCardTwo;