diff --git a/src/components/cardStack/layouts/timelines/TimelineCardStack.tsx b/src/components/cardStack/layouts/timelines/TimelineCardStack.tsx index 8c18c99..5332539 100644 --- a/src/components/cardStack/layouts/timelines/TimelineCardStack.tsx +++ b/src/components/cardStack/layouts/timelines/TimelineCardStack.tsx @@ -1,27 +1,147 @@ -'use client'; +"use client"; -import React from 'react'; -import { - ButtonConfig, - ButtonAnimationType, - TitleSegment, -} from '@/components/cardStack/types'; +import React, { useEffect, useRef, memo, Children } from "react"; +import { gsap } from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import CardStackTextBox from "../../CardStackTextBox"; +import { cls } from "@/lib/utils"; +import type { LucideIcon } from "lucide-react"; +import type { ButtonConfig, ButtonAnimationType, TitleSegment } from "../../types"; +import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; + +gsap.registerPlugin(ScrollTrigger); interface TimelineCardStackProps { - items: any[]; - className?: string; + children: React.ReactNode; + title: string; + titleSegments?: TitleSegment[]; + description: string; + tag?: string; + tagIcon?: LucideIcon; + tagAnimation?: ButtonAnimationType; + buttons?: ButtonConfig[]; + buttonAnimation?: ButtonAnimationType; + textboxLayout: TextboxLayout; + useInvertedBackground?: InvertedBackground; + className?: string; + containerClassName?: string; + textBoxClassName?: string; + titleClassName?: string; + titleImageWrapperClassName?: string; + titleImageClassName?: string; + descriptionClassName?: string; + tagClassName?: string; + buttonContainerClassName?: string; + buttonClassName?: string; + buttonTextClassName?: string; + ariaLabel?: string; } -const TimelineCardStack: React.FC = ({ items, className = '' }) => { - return ( -
- {items.map((item, index) => ( -
- {item.title} -
- ))} -
- ); +const TimelineCardStack = ({ + children, + title, + titleSegments, + description, + tag, + tagIcon, + tagAnimation, + buttons, + buttonAnimation, + textboxLayout, + useInvertedBackground, + className = "", + containerClassName = "", + textBoxClassName = "", + titleClassName = "", + titleImageWrapperClassName = "", + titleImageClassName = "", + descriptionClassName = "", + tagClassName = "", + buttonContainerClassName = "", + buttonClassName = "", + buttonTextClassName = "", + ariaLabel = "Timeline section", +}: TimelineCardStackProps) => { + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + const childrenArray = Children.toArray(children); + + useEffect(() => { + const ctx = gsap.context(() => { + itemRefs.current.forEach((ref, position) => { + if (!ref) return; + + const isLast = position === itemRefs.current.length - 1; + + const timeline = gsap.timeline({ + scrollTrigger: { + trigger: ref, + start: "center center", + end: "+=100%", + scrub: true, + }, + }); + + timeline.set(ref, { willChange: "opacity" }).to(ref, { + ease: "none", + opacity: isLast ? 1 : 0, + }); + }); + }); + + return () => { + ctx.revert(); + }; + }, [childrenArray.length]); + + return ( +
+
+ +
+ {Children.map(childrenArray, (child, index) => ( +
{ + itemRefs.current[index] = el; + }} + className="!sticky w-full card backdrop-blur-xs rounded-theme-capped h-[140vw] md:h-[75vh] top-[25vw] md:top-[12.5vh]" + > + {child} +
+ ))} +
+
+
+ ); }; -export default TimelineCardStack; +TimelineCardStack.displayName = "TimelineCardStack"; + +export default memo(TimelineCardStack);