diff --git a/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx b/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx index 7bbd289..adb6692 100644 --- a/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx +++ b/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx @@ -1,26 +1,63 @@ -import React, { useRef } from 'react'; -import { useCardAnimation, UseCardAnimationOptions } from '@/hooks/useCardAnimation'; +"use client"; -export interface TimelinePhoneViewItem { - [key: string]: any; +import React, { memo } from "react"; +import MediaContent from "@/components/shared/MediaContent"; +import CardStackTextBox from "../../CardStackTextBox"; +import { usePhoneAnimations, type TimelinePhoneViewItem } from "../../hooks/usePhoneAnimations"; +import { useCardAnimation } from "../../hooks/useCardAnimation"; +import { cls } from "@/lib/utils"; +import type { LucideIcon } from "lucide-react"; +import type { ButtonConfig, ButtonAnimationType, TitleSegment, CardAnimationType } from "../../types"; +import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; + +interface PhoneFrameProps { + imageSrc?: string; + videoSrc?: string; + imageAlt?: string; + videoAriaLabel?: string; + phoneRef: (el: HTMLDivElement | null) => void; + className?: string; } -export interface TimelinePhoneViewProps { - children: React.ReactNode; +const PhoneFrame = memo(({ + imageSrc, + videoSrc, + imageAlt, + videoAriaLabel, + phoneRef, + className = "", +}: PhoneFrameProps) => ( +
+ +
+)); + +PhoneFrame.displayName = "PhoneFrame"; + +interface TimelinePhoneViewProps { + items: TimelinePhoneViewItem[]; showTextBox?: boolean; showDivider?: boolean; - title?: string; - titleSegments?: Array<{ type: 'text'; content: string } | { type: 'image'; src: string; alt?: string }>; - description?: string; + 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; - ariaLabel?: string; + tagIcon?: LucideIcon; + tagAnimation?: ButtonAnimationType; + buttons?: ButtonConfig[]; + buttonAnimation?: ButtonAnimationType; + animationType: CardAnimationType; + textboxLayout: TextboxLayout; + useInvertedBackground?: InvertedBackground; className?: string; containerClassName?: string; textBoxClassName?: string; @@ -30,28 +67,209 @@ export interface TimelinePhoneViewProps { buttonContainerClassName?: string; buttonClassName?: string; buttonTextClassName?: string; + desktopContainerClassName?: string; + mobileContainerClassName?: string; + desktopContentClassName?: string; + desktopWrapperClassName?: string; + mobileWrapperClassName?: string; + phoneFrameClassName?: string; + mobilePhoneFrameClassName?: string; + titleImageWrapperClassName?: string; + titleImageClassName?: string; + ariaLabel?: string; } -export const TimelinePhoneView: React.FC = ({ - children, - containerClassName = '', - ...props -}) => { - const containerRef = useRef(null); - const itemRefs = useRef<(HTMLDivElement | null)[]>([]); - - const animationOptions: UseCardAnimationOptions = { - containerRef, - itemRefs, - }; - - const { } = useCardAnimation(animationOptions); +const TimelinePhoneView = ({ + items, + showTextBox = true, + showDivider = false, + title, + titleSegments, + description, + tag, + tagIcon, + tagAnimation, + buttons, + buttonAnimation, + animationType, + textboxLayout, + useInvertedBackground, + className = "", + containerClassName = "", + textBoxClassName = "", + titleClassName = "", + descriptionClassName = "", + tagClassName = "", + buttonContainerClassName = "", + buttonClassName = "", + buttonTextClassName = "", + desktopContainerClassName = "", + mobileContainerClassName = "", + desktopContentClassName = "", + desktopWrapperClassName = "", + mobileWrapperClassName = "", + phoneFrameClassName = "", + mobilePhoneFrameClassName = "", + titleImageWrapperClassName = "", + titleImageClassName = "", + ariaLabel = "Timeline phone view section", +}: TimelinePhoneViewProps) => { + const { imageRefs, mobileImageRefs } = usePhoneAnimations(items); + const { itemRefs: contentRefs } = useCardAnimation({ + animationType, + itemCount: items.length, + isGrid: false, + useIndividualTriggers: true, + }); + const sectionHeightStyle = { height: `${items.length * 100}vh` }; return ( -
- {children} -
+
+
+ {showTextBox && ( +
+ +
+ )} + {showDivider && ( +
+ )} +
+
+ {items.map((item, index) => ( +
+
{ contentRefs.current[index] = el; }} + className={desktopWrapperClassName} + > + {item.content} +
+
+ ))} +
+
+ {items.map((item, itemIndex) => ( +
+
+ { + if (imageRefs.current) { + imageRefs.current[itemIndex * 2] = el; + } + }} + className={cls("w-20 2xl:w-25 h-[70vh]", phoneFrameClassName)} + /> + { + if (imageRefs.current) { + imageRefs.current[itemIndex * 2 + 1] = el; + } + }} + className={cls("w-20 2xl:w-25 h-[70vh]", phoneFrameClassName)} + /> +
+
+ ))} +
+
+
+ {items.map((item, itemIndex) => ( +
+
+ {item.content} +
+
+ { + if (mobileImageRefs.current) { + mobileImageRefs.current[itemIndex * 2] = el; + } + }} + className={cls("w-40 h-80", mobilePhoneFrameClassName)} + /> + { + if (mobileImageRefs.current) { + mobileImageRefs.current[itemIndex * 2 + 1] = el; + } + }} + className={cls("w-40 h-80", mobilePhoneFrameClassName)} + /> +
+
+ ))} +
+
+
); }; -export default TimelinePhoneView; +TimelinePhoneView.displayName = "TimelinePhoneView"; + +export default memo(TimelinePhoneView);