import { useEffect, useRef } from "react"; import { gsap } from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import Button from "@/components/ui/Button"; import TextAnimation from "@/components/ui/TextAnimation"; import InfoCardMarquee from "@/components/ui/InfoCardMarquee"; import AnimatedBarChart from "@/components/ui/AnimatedBarChart"; import ChecklistTimeline from "@/components/ui/ChecklistTimeline"; import MediaStack from "@/components/ui/MediaStack"; import { cls } from "@/lib/utils"; import type { LucideIcon } from "lucide-react"; gsap.registerPlugin(ScrollTrigger); type IconInput = string | LucideIcon; type FeatureItem = { title: string; description: string; primaryButton?: { text: string; href: string }; secondaryButton?: { text: string; href: string } } & ( | { bentoComponent: "info-card-marquee"; infoCards: { icon: IconInput; label: string; value: string }[] } | { bentoComponent: "animated-bar-chart" } | { bentoComponent: "checklist-timeline"; heading: string; subheading: string; checklistItems: [{ label: string; detail: string }, { label: string; detail: string }, { label: string; detail: string }]; completedLabel: string } | { bentoComponent: "media-stack"; mediaItems: [{ imageSrc?: string; videoSrc?: string }, { imageSrc?: string; videoSrc?: string }, { imageSrc?: string; videoSrc?: string }] } ); const getBentoComponent = (item: FeatureItem) => { switch (item.bentoComponent) { case "info-card-marquee": return ; case "animated-bar-chart": return ; case "checklist-timeline": return ; case "media-stack": return ; } }; interface FeaturesAlternatingBentoProps { tag: string; title: string; description: string; primaryButton?: { text: string; href: string }; secondaryButton?: { text: string; href: string }; items: FeatureItem[]; } const FeaturesAlternatingBento = ({ tag, title, description, primaryButton, secondaryButton, items, }: FeaturesAlternatingBentoProps) => { const itemRefs = useRef<(HTMLDivElement | null)[]>([]); useEffect(() => { const ctx = gsap.context(() => { itemRefs.current.forEach((ref, position) => { if (!ref) return; const isLast = position === itemRefs.current.length - 1; gsap.timeline({ scrollTrigger: { trigger: ref, start: "center center", end: "+=100%", scrub: true, }, }) .set(ref, { willChange: "opacity" }) .to(ref, { ease: "none", opacity: isLast ? 1 : 0, }); }); }); return () => ctx.revert(); }, [items.length]); return (

{tag}

{(primaryButton || secondaryButton) && (
{primaryButton &&
)}
{items.map((item, index) => (
{ itemRefs.current[index] = el; }} className={cls("sticky top-[25vw] md:top-[12.5vh] h-[140vw] md:h-[75vh] flex flex-col gap-6 md:gap-10 p-6 md:p-10 card rounded", index % 2 === 0 ? "md:flex-row" : "md:flex-row-reverse")} >

{index + 1}

{item.title}

{item.description}

{(item.primaryButton || item.secondaryButton) && (
{item.primaryButton &&
)}
{getBentoComponent(item)}
))}
); }; export default FeaturesAlternatingBento;