From e8b2a950ad141059e01cc9ea741d6a36a46a684c Mon Sep 17 00:00:00 2001 From: bender Date: Wed, 4 Mar 2026 19:07:07 +0000 Subject: [PATCH] Switch to version 1: modified src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx --- .../layouts/timelines/TimelineProcessFlow.tsx | 222 +++++++++++++++--- 1 file changed, 186 insertions(+), 36 deletions(-) diff --git a/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx b/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx index 50818b2..5e40887 100644 --- a/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx +++ b/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx @@ -1,52 +1,202 @@ -import React, { useRef } from 'react'; -import { useCardAnimation, UseCardAnimationOptions } from '@/hooks/useCardAnimation'; +"use client"; -export interface TimelineProcessFlowProps { - children: React.ReactNode; - title?: string; - titleSegments?: Array<{ type: 'text'; content: string } | { type: 'image'; src: string; alt?: string }>; - description?: string; +import React, { useEffect, useRef, memo, useState } from "react"; +import { gsap } from "gsap"; +import { ScrollTrigger } from "gsap/ScrollTrigger"; +import CardStackTextBox from "../../CardStackTextBox"; +import { useCardAnimation } from "../../hooks/useCardAnimation"; +import { cls } from "@/lib/utils"; +import type { LucideIcon } from "lucide-react"; +import type { ButtonConfig, ButtonAnimationType, CardAnimationType, TitleSegment } from "../../types"; +import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; + +gsap.registerPlugin(ScrollTrigger); + +interface TimelineProcessFlowItem { + id: string; + content: React.ReactNode; + media: React.ReactNode; + reverse: boolean; +} + +interface TimelineProcessFlowProps { + items: TimelineProcessFlowItem[]; + title: string; + titleSegments?: TitleSegment[]; + description: string; tag?: string; - tagIcon?: any; - tagAnimation?: string; - buttons?: Array<{ text: string; onClick?: () => void; href?: string }>; - buttonAnimation?: string; - textboxLayout?: string; - animationType?: string; - useInvertedBackground?: boolean; + tagIcon?: LucideIcon; + tagAnimation?: ButtonAnimationType; + buttons?: ButtonConfig[]; + buttonAnimation?: ButtonAnimationType; + textboxLayout: TextboxLayout; + animationType: CardAnimationType; + useInvertedBackground?: InvertedBackground; ariaLabel?: string; className?: string; containerClassName?: string; textBoxClassName?: string; - titleClassName?: string; - descriptionClassName?: string; - tagClassName?: string; - buttonContainerClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; + textBoxTitleClassName?: string; + textBoxDescriptionClassName?: string; + textBoxTagClassName?: string; + textBoxButtonContainerClassName?: string; + textBoxButtonClassName?: string; + textBoxButtonTextClassName?: string; + itemClassName?: string; + mediaWrapperClassName?: string; + numberClassName?: string; + contentWrapperClassName?: string; gapClassName?: string; + titleImageWrapperClassName?: string; + titleImageClassName?: string; } -export const TimelineProcessFlow: React.FC = ({ - children, - containerClassName = '', - ...props -}) => { - const containerRef = useRef(null); - const itemRefs = useRef<(HTMLDivElement | null)[]>([]); +const TimelineProcessFlow = ({ + items, + title, + titleSegments, + description, + tag, + tagIcon, + tagAnimation, + buttons, + buttonAnimation, + textboxLayout, + animationType, + useInvertedBackground, + ariaLabel = "Timeline process flow section", + className = "", + containerClassName = "", + textBoxClassName = "", + textBoxTitleClassName = "", + textBoxDescriptionClassName = "", + textBoxTagClassName = "", + textBoxButtonContainerClassName = "", + textBoxButtonClassName = "", + textBoxButtonTextClassName = "", + itemClassName = "", + mediaWrapperClassName = "", + numberClassName = "", + contentWrapperClassName = "", + gapClassName = "", + titleImageWrapperClassName = "", + titleImageClassName = "", +}: TimelineProcessFlowProps) => { + const processLineRef = useRef(null); + const { itemRefs } = useCardAnimation({ animationType, itemCount: items.length, useIndividualTriggers: true }); + const [isMdScreen, setIsMdScreen] = useState(false); - const animationOptions: UseCardAnimationOptions = { - containerRef, - itemRefs, - }; + useEffect(() => { + const checkScreenSize = () => { + setIsMdScreen(window.innerWidth >= 768); + }; - const { } = useCardAnimation(animationOptions); + checkScreenSize(); + window.addEventListener('resize', checkScreenSize); + + return () => window.removeEventListener('resize', checkScreenSize); + }, []); + + useEffect(() => { + if (!processLineRef.current) return; + + gsap.fromTo( + processLineRef.current, + { yPercent: -100 }, + { + yPercent: 0, + ease: "none", + scrollTrigger: { + trigger: ".timeline-line", + start: "top center", + end: "bottom center", + scrub: true, + }, + } + ); + + return () => { + ScrollTrigger.getAll().forEach((trigger) => trigger.kill()); + }; + }, []); return ( -
- {children} -
+
+
+
+ +
+
+
+
+
+
+
+
    + {items.map((item, index) => ( +
  1. { + itemRefs.current[index] = el; + }} + className={cls( + "relative z-10 w-full flex flex-col gap-6 md:gap-0 md:flex-row justify-between", + item.reverse && "flex-col md:flex-row-reverse", + itemClassName + )} + > +
    + {item.media} +
    +
    +

    {item.id}

    +
    +
    + {item.content} +
    +
  2. + ))} +
+
+
+
); }; -export default TimelineProcessFlow; +TimelineProcessFlow.displayName = "TimelineProcessFlow"; + +export default memo(TimelineProcessFlow);