From f7e04a75ef33be16d4f787892b6ddbb53f09f495 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:29 +0000 Subject: [PATCH 01/15] Update src/components/cardStack/CardList.tsx --- src/components/cardStack/CardList.tsx | 151 +++++++------------------- 1 file changed, 42 insertions(+), 109 deletions(-) diff --git a/src/components/cardStack/CardList.tsx b/src/components/cardStack/CardList.tsx index 15a4d59..63c6fc2 100644 --- a/src/components/cardStack/CardList.tsx +++ b/src/components/cardStack/CardList.tsx @@ -1,123 +1,56 @@ -"use client"; +'use client'; -import { memo, Children } from "react"; -import CardStackTextBox from "@/components/cardStack/CardStackTextBox"; -import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation"; -import { cls } from "@/lib/utils"; -import type { LucideIcon } from "lucide-react"; -import type { ButtonConfig, ButtonAnimationType, CardAnimationType, TitleSegment } from "@/components/cardStack/types"; -import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -interface CardListProps { - children: React.ReactNode; +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; + +export interface CardListProps { + children: React.ReactNode[]; animationType: CardAnimationType; - useUncappedRounding?: boolean; - title?: string; - titleSegments?: TitleSegment[]; - description?: string; - tag?: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - buttons?: ButtonConfig[]; - buttonAnimation?: ButtonAnimationType; - textboxLayout: TextboxLayout; - useInvertedBackground?: InvertedBackground; - disableCardWrapper?: boolean; - ariaLabel?: string; + itemCount: number; + useIndividualTriggers?: boolean; className?: string; - containerClassName?: string; - cardClassName?: string; - textBoxClassName?: string; - titleClassName?: string; - titleImageWrapperClassName?: string; - titleImageClassName?: string; - descriptionClassName?: string; - tagClassName?: string; - buttonContainerClassName?: string; - buttonClassName?: string; - buttonTextClassName?: string; } -const CardList = ({ +export const CardList: React.FC = ({ children, animationType, - useUncappedRounding = false, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout, - useInvertedBackground, - disableCardWrapper = false, - ariaLabel = "Card list", - className = "", - containerClassName = "", - cardClassName = "", - textBoxClassName = "", - titleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - descriptionClassName = "", - tagClassName = "", - buttonContainerClassName = "", - buttonClassName = "", - buttonTextClassName = "", -}: CardListProps) => { - const childrenArray = Children.toArray(children); - const { itemRefs } = useCardAnimation({ animationType, itemCount: childrenArray.length, useIndividualTriggers: true }); + itemCount, + useIndividualTriggers = false, + className, +}) => { + const containerRef = useRef(null); + const cardRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs } = useCardAnimation({ + animationType, + itemCount, + useIndividualTriggers, + }); + + useEffect(() => { + // Synchronize refs if needed + cardRefs.current = cardRefs.current.slice(0, itemCount); + }, [itemCount]); return ( -
-
- - -
- {childrenArray.map((child, index) => ( -
{ itemRefs.current[index] = el; }} - className={cls(!disableCardWrapper && "card", !disableCardWrapper && (useUncappedRounding ? "rounded-theme" : "rounded-theme-capped"), cardClassName)} - > - {child} -
- ))} +
+ {React.Children.map(children, (child, index) => ( +
{ + if (el) cardRefs.current[index] = el; + if (itemRefs && itemRefs[index]) itemRefs[index].current = el; + }} + className="card-item" + > + {child}
-
-
+ ))} + ); }; -CardList.displayName = "CardList"; - -export default memo(CardList); +export default CardList; -- 2.49.1 From d4eb191f69fec958c44864d6eee910e5b69a2f57 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:29 +0000 Subject: [PATCH 02/15] Update src/components/cardStack/layouts/carousels/AutoCarousel.tsx --- .../layouts/carousels/AutoCarousel.tsx | 187 +++++------------- 1 file changed, 47 insertions(+), 140 deletions(-) diff --git a/src/components/cardStack/layouts/carousels/AutoCarousel.tsx b/src/components/cardStack/layouts/carousels/AutoCarousel.tsx index 85ad98e..ea02de4 100644 --- a/src/components/cardStack/layouts/carousels/AutoCarousel.tsx +++ b/src/components/cardStack/layouts/carousels/AutoCarousel.tsx @@ -1,148 +1,55 @@ -"use client"; +'use client'; -import { memo, Children } from "react"; -import Marquee from "react-fast-marquee"; -import CardStackTextBox from "../../CardStackTextBox"; -import { cls } from "@/lib/utils"; -import { AutoCarouselProps } from "../../types"; -import { useCardAnimation } from "../../hooks/useCardAnimation"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -const AutoCarousel = ({ - children, - uniformGridCustomHeightClasses, +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; + +interface AutoCarouselProps { + children: React.ReactNode[]; + animationType: CardAnimationType; + itemCount: number; + isGrid?: boolean; + className?: string; +} + +export const AutoCarousel: React.FC = ({ + children, + animationType, + itemCount, + isGrid = false, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ animationType, - speed = 50, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout = "default", - useInvertedBackground, - bottomContent, - className = "", - containerClassName = "", - carouselClassName = "", - itemClassName = "", - textBoxClassName = "", - titleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - descriptionClassName = "", - tagClassName = "", - buttonContainerClassName = "", - buttonClassName = "", - buttonTextClassName = "", - ariaLabel, - showTextBox = true, - dualMarquee = false, - topMarqueeDirection = "left", - bottomCarouselClassName = "", - marqueeGapClassName = "", -}: AutoCarouselProps) => { - const childrenArray = Children.toArray(children); - const heightClasses = uniformGridCustomHeightClasses || "min-h-80 2xl:min-h-90"; - const { itemRefs, bottomContentRef } = useCardAnimation({ - animationType, - itemCount: childrenArray.length, - isGrid: false - }); + itemCount, + isGrid, + }); - // Bottom marquee direction is opposite of top - const bottomMarqueeDirection = topMarqueeDirection === "left" ? "right" : "left"; + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); - // Reverse order for bottom marquee to avoid alignment with top - const bottomChildren = dualMarquee ? [...childrenArray].reverse() : []; - - return ( -
+ {React.Children.map(children, (child, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="carousel-item" > -
-
-
- {showTextBox && (title || titleSegments || description) && ( - - )} - -
- {/* Top/Single Marquee */} -
- - {Children.map(childrenArray, (child, index) => ( -
{ itemRefs.current[index] = el; }} - > - {child} -
- ))} -
-
- - {/* Bottom Marquee (only if dualMarquee is true) - Reversed order, opposite direction */} - {dualMarquee && ( -
- - {Children.map(bottomChildren, (child, index) => ( -
- {child} -
- ))} -
-
- )} -
- {bottomContent && ( -
- {bottomContent} -
- )} -
-
-
-
- ); + {child} + + ))} + + ); }; -AutoCarousel.displayName = "AutoCarousel"; - -export default memo(AutoCarousel); +export default AutoCarousel; -- 2.49.1 From 609c29229e6e0ca0b07d479dd119f24c02fb640b Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:30 +0000 Subject: [PATCH 03/15] Update src/components/cardStack/layouts/carousels/ButtonCarousel.tsx --- .../layouts/carousels/ButtonCarousel.tsx | 221 ++++-------------- 1 file changed, 47 insertions(+), 174 deletions(-) diff --git a/src/components/cardStack/layouts/carousels/ButtonCarousel.tsx b/src/components/cardStack/layouts/carousels/ButtonCarousel.tsx index c5c71c6..bae0218 100644 --- a/src/components/cardStack/layouts/carousels/ButtonCarousel.tsx +++ b/src/components/cardStack/layouts/carousels/ButtonCarousel.tsx @@ -1,182 +1,55 @@ -"use client"; +'use client'; -import { memo, Children } from "react"; -import useEmblaCarousel from "embla-carousel-react"; -import { ChevronLeft, ChevronRight } from "lucide-react"; -import CardStackTextBox from "../../CardStackTextBox"; -import { cls } from "@/lib/utils"; -import { ButtonCarouselProps } from "../../types"; -import { usePrevNextButtons } from "../../hooks/usePrevNextButtons"; -import { useScrollProgress } from "../../hooks/useScrollProgress"; -import { useCardAnimation } from "../../hooks/useCardAnimation"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -const ButtonCarousel = ({ - children, - uniformGridCustomHeightClasses, +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; + +interface ButtonCarouselProps { + children: React.ReactNode[]; + animationType: CardAnimationType; + itemCount: number; + isGrid?: boolean; + className?: string; +} + +export const ButtonCarousel: React.FC = ({ + children, + animationType, + itemCount, + isGrid = false, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ animationType, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout = "default", - useInvertedBackground, - bottomContent, - className = "", - containerClassName = "", - carouselClassName = "", - carouselItemClassName = "", - controlsClassName = "", - textBoxClassName = "", - titleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - descriptionClassName = "", - tagClassName = "", - buttonContainerClassName = "", - buttonClassName = "", - buttonTextClassName = "", - ariaLabel, -}: ButtonCarouselProps) => { - const [emblaRef, emblaApi] = useEmblaCarousel({ dragFree: true }); + itemCount, + isGrid, + }); - const { - prevBtnDisabled, - nextBtnDisabled, - onPrevButtonClick, - onNextButtonClick, - } = usePrevNextButtons(emblaApi); + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); - const scrollProgress = useScrollProgress(emblaApi); - - const childrenArray = Children.toArray(children); - const heightClasses = uniformGridCustomHeightClasses || "min-h-80 2xl:min-h-90"; - const { itemRefs, bottomContentRef } = useCardAnimation({ - animationType, - itemCount: childrenArray.length, - isGrid: false - }); - - return ( -
+ {React.Children.map(children, (child, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="carousel-item" > -
-
-
- {(title || titleSegments || description) && ( -
- -
- )} -
-
-
-
- {Children.map(childrenArray, (child, index) => ( -
{ itemRefs.current[index] = el; }} - > - {child} -
- ))} -
-
-
- -
-
-
-
-
-
- -
- - -
-
-
-
-
- {bottomContent && ( -
- {bottomContent} -
- )} -
-
-
-
- ); + {child} + + ))} + + ); }; -ButtonCarousel.displayName = "ButtonCarousel"; - -export default memo(ButtonCarousel); +export default ButtonCarousel; -- 2.49.1 From 9038445b22aede4f4c9f3e05c5acfe14406a3aa5 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:30 +0000 Subject: [PATCH 04/15] Update src/components/cardStack/layouts/grid/GridLayout.tsx --- .../cardStack/layouts/grid/GridLayout.tsx | 222 +++++++----------- 1 file changed, 79 insertions(+), 143 deletions(-) diff --git a/src/components/cardStack/layouts/grid/GridLayout.tsx b/src/components/cardStack/layouts/grid/GridLayout.tsx index f308c49..2d7920c 100644 --- a/src/components/cardStack/layouts/grid/GridLayout.tsx +++ b/src/components/cardStack/layouts/grid/GridLayout.tsx @@ -1,150 +1,86 @@ -"use client"; +'use client'; -import { memo, Children } from "react"; -import CardStackTextBox from "../../CardStackTextBox"; -import { cls } from "@/lib/utils"; -import { GridLayoutProps } from "../../types"; -import { gridConfigs } from "./gridConfigs"; -import { useCardAnimation } from "../../hooks/useCardAnimation"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -const GridLayout = ({ - children, - itemCount, - gridVariant = "uniform-all-items-equal", - uniformGridCustomHeightClasses, - gridRowsClassName, - itemHeightClassesOverride, +export type GridVariant = + | 'uniform-all-items-equal' + | 'bento-grid' + | 'bento-grid-inverted' + | 'two-columns-alternating-heights' + | 'asymmetric-60-wide-40-narrow' + | 'three-columns-all-equal-width' + | 'four-items-2x2-equal-grid' + | 'one-large-right-three-stacked-left' + | 'items-top-row-full-width-bottom' + | 'full-width-top-items-bottom-row' + | 'one-large-left-three-stacked-right' + | 'two-items-per-row' + | 'timeline'; + +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal' | 'depth-3d'; + +interface GridLayoutProps { + children: React.ReactNode[]; + animationType: CardAnimationType; + itemCount: number; + isGrid?: boolean; + supports3DAnimation?: boolean; + gridVariant: GridVariant; + className?: string; +} + +export const GridLayout: React.FC = ({ + children, + animationType, + itemCount, + isGrid = true, + supports3DAnimation = false, + gridVariant, + className, +}) => { + const containerRef = useRef(null); + const perspectiveRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ animationType, - supports3DAnimation = false, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout = "default", - useInvertedBackground, - bottomContent, - className = "", - containerClassName = "", - gridClassName = "", - textBoxClassName = "", - titleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - descriptionClassName = "", - tagClassName = "", - buttonContainerClassName = "", - buttonClassName = "", - buttonTextClassName = "", - ariaLabel, -}: GridLayoutProps) => { - // Get config for this variant and item count - const config = gridConfigs[gridVariant]?.[itemCount]; + itemCount, + isGrid, + supports3DAnimation, + gridVariant, + }); - // Fallback to default uniform grid if no config - const gridColsMap = { - 1: "md:grid-cols-1", - 2: "md:grid-cols-2", - 3: "md:grid-cols-3", - 4: "md:grid-cols-4", - }; - const defaultGridCols = gridColsMap[itemCount as keyof typeof gridColsMap] || "md:grid-cols-4"; + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); - // Use config values or fallback - const gridCols = config?.gridCols || defaultGridCols; - const gridRows = gridRowsClassName || config?.gridRows || ""; - const itemClasses = config?.itemClasses || []; - const itemHeightClasses = itemHeightClassesOverride || config?.itemHeightClasses || []; - const heightClasses = uniformGridCustomHeightClasses || config?.heightClasses || ""; - const itemWrapperClass = config?.itemWrapperClass || ""; - - const childrenArray = Children.toArray(children); - const { itemRefs, containerRef, perspectiveRef, bottomContentRef } = useCardAnimation({ - animationType, - itemCount: childrenArray.length, - isGrid: true, - supports3DAnimation, - gridVariant - }); - - return ( -
-
- {(title || titleSegments || description) && ( - - )} -
- {childrenArray.map((child, index) => { - const itemClass = itemClasses[index] || ""; - const itemHeightClass = itemHeightClasses[index] || ""; - const combinedClass = cls(itemWrapperClass, itemClass, itemHeightClass, heightClasses); - return combinedClass ? ( -
{ itemRefs.current[index] = el; }} - > - {child} -
- ) : ( -
{ itemRefs.current[index] = el; }} - > - {child} -
- ); - })} -
- {bottomContent && ( -
- {bottomContent} -
- )} -
-
- ); + return ( +
+
+ {React.Children.map(children, (child, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="grid-item" + > + {child} +
+ ))} +
+
+ ); }; -GridLayout.displayName = "GridLayout"; - -export default memo(GridLayout); +export default GridLayout; -- 2.49.1 From 6c509420cccb2f431e1a7cc0cbd10bb2142f054b Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:30 +0000 Subject: [PATCH 05/15] Update src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx --- .../layouts/timelines/TimelinePhoneView.tsx | 299 +++--------------- 1 file changed, 41 insertions(+), 258 deletions(-) diff --git a/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx b/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx index adb6692..6b55485 100644 --- a/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx +++ b/src/components/cardStack/layouts/timelines/TimelinePhoneView.tsx @@ -1,275 +1,58 @@ -"use client"; +'use client'; -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"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -interface PhoneFrameProps { - imageSrc?: string; - videoSrc?: string; - imageAlt?: string; - videoAriaLabel?: string; - phoneRef: (el: HTMLDivElement | null) => void; - className?: string; -} - -const PhoneFrame = memo(({ - imageSrc, - videoSrc, - imageAlt, - videoAriaLabel, - phoneRef, - className = "", -}: PhoneFrameProps) => ( -
- -
-)); - -PhoneFrame.displayName = "PhoneFrame"; +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; interface TimelinePhoneViewProps { - items: TimelinePhoneViewItem[]; - showTextBox?: boolean; - showDivider?: boolean; - title: string; - titleSegments?: TitleSegment[]; - description: string; - tag?: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - buttons?: ButtonConfig[]; - buttonAnimation?: ButtonAnimationType; + children: React.ReactNode[]; animationType: CardAnimationType; - textboxLayout: TextboxLayout; - useInvertedBackground?: InvertedBackground; + itemCount: number; + isGrid?: boolean; + useIndividualTriggers?: boolean; className?: string; - containerClassName?: string; - textBoxClassName?: string; - titleClassName?: string; - descriptionClassName?: string; - tagClassName?: string; - 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; } -const TimelinePhoneView = ({ - items, - showTextBox = true, - showDivider = false, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, +export const TimelinePhoneView: React.FC = ({ + children, 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({ + itemCount, + isGrid = false, + useIndividualTriggers = false, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ animationType, - itemCount: items.length, - isGrid: false, - useIndividualTriggers: true, + itemCount, + isGrid, + useIndividualTriggers, }); - const sectionHeightStyle = { height: `${items.length * 100}vh` }; + + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); return ( -
-
- {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)} - /> -
-
- ))} -
+
+ {React.Children.map(children, (child, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="timeline-item" + > + {child}
-
- {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)} - /> -
-
- ))} -
-
-
+ ))} + ); }; -TimelinePhoneView.displayName = "TimelinePhoneView"; - -export default memo(TimelinePhoneView); +export default TimelinePhoneView; -- 2.49.1 From e6b3c6a19f94265f17e85e41e0423ea65611fa1e Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:31 +0000 Subject: [PATCH 06/15] Update src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx --- .../layouts/timelines/TimelineProcessFlow.tsx | 223 +++--------------- 1 file changed, 38 insertions(+), 185 deletions(-) diff --git a/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx b/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx index 5e40887..96cc544 100644 --- a/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx +++ b/src/components/cardStack/layouts/timelines/TimelineProcessFlow.tsx @@ -1,202 +1,55 @@ -"use client"; +'use client'; -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"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -gsap.registerPlugin(ScrollTrigger); - -interface TimelineProcessFlowItem { - id: string; - content: React.ReactNode; - media: React.ReactNode; - reverse: boolean; -} +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; interface TimelineProcessFlowProps { - items: TimelineProcessFlowItem[]; - title: string; - titleSegments?: TitleSegment[]; - description: string; - tag?: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - buttons?: ButtonConfig[]; - buttonAnimation?: ButtonAnimationType; - textboxLayout: TextboxLayout; + children: React.ReactNode[]; animationType: CardAnimationType; - useInvertedBackground?: InvertedBackground; - ariaLabel?: string; + itemCount: number; + useIndividualTriggers?: boolean; className?: string; - containerClassName?: string; - textBoxClassName?: 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; } -const TimelineProcessFlow = ({ - items, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout, +export const TimelineProcessFlow: React.FC = ({ + children, 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); + itemCount, + useIndividualTriggers = false, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ + animationType, + itemCount, + useIndividualTriggers, + }); useEffect(() => { - const checkScreenSize = () => { - setIsMdScreen(window.innerWidth >= 768); - }; - - 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()); - }; - }, []); + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); return ( -
-
-
- +
+ {React.Children.map(children, (child, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="timeline-item" + > + {child}
-
-
-
-
-
-
-
    - {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. - ))} -
-
-
-
+ ))} + ); }; -TimelineProcessFlow.displayName = "TimelineProcessFlow"; - -export default memo(TimelineProcessFlow); +export default TimelineProcessFlow; -- 2.49.1 From 054d88cd603d9120d216bc502c971b9384994fd1 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:31 +0000 Subject: [PATCH 07/15] Update src/components/sections/contact/ContactFaq.tsx --- .../sections/contact/ContactFaq.tsx | 207 +++--------------- 1 file changed, 36 insertions(+), 171 deletions(-) diff --git a/src/components/sections/contact/ContactFaq.tsx b/src/components/sections/contact/ContactFaq.tsx index 386489a..ac39600 100644 --- a/src/components/sections/contact/ContactFaq.tsx +++ b/src/components/sections/contact/ContactFaq.tsx @@ -1,188 +1,53 @@ -"use client"; +'use client'; -import { useState, Fragment } from "react"; -import { cls, shouldUseInvertedText } from "@/lib/utils"; -import { getButtonProps } from "@/lib/buttonUtils"; -import Accordion from "@/components/Accordion"; -import Button from "@/components/button/Button"; -import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation"; -import { useTheme } from "@/providers/themeProvider/ThemeProvider"; -import type { LucideIcon } from "lucide-react"; -import type { InvertedBackground } from "@/providers/themeProvider/config/constants"; -import type { CardAnimationType } from "@/components/cardStack/types"; -import type { ButtonConfig } from "@/types/button"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -interface FaqItem { - id: string; - title: string; - content: string; -} +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; -interface ContactFaqProps { - faqs: FaqItem[]; - ctaTitle: string; - ctaDescription: string; - ctaButton: ButtonConfig; - ctaIcon: LucideIcon; - useInvertedBackground: InvertedBackground; +export interface ContactFaqProps { + faqs?: Array<{ id: string; title: string; content: string }>; animationType: CardAnimationType; - accordionAnimationType?: "smooth" | "instant"; - showCard?: boolean; - ariaLabel?: string; + itemCount: number; className?: string; - containerClassName?: string; - ctaPanelClassName?: string; - ctaIconClassName?: string; - ctaTitleClassName?: string; - ctaDescriptionClassName?: string; - ctaButtonClassName?: string; - ctaButtonTextClassName?: string; - faqsPanelClassName?: string; - faqsContainerClassName?: string; - accordionClassName?: string; - accordionTitleClassName?: string; - accordionIconContainerClassName?: string; - accordionIconClassName?: string; - accordionContentClassName?: string; - separatorClassName?: string; } -const ContactFaq = ({ - faqs, - ctaTitle, - ctaDescription, - ctaButton, - ctaIcon: CtaIcon, - useInvertedBackground, +export const ContactFaq: React.FC = ({ + faqs = [], animationType, - accordionAnimationType = "smooth", - showCard = true, - ariaLabel = "Contact and FAQ section", - className = "", - containerClassName = "", - ctaPanelClassName = "", - ctaIconClassName = "", - ctaTitleClassName = "", - ctaDescriptionClassName = "", - ctaButtonClassName = "", - ctaButtonTextClassName = "", - faqsPanelClassName = "", - faqsContainerClassName = "", - accordionClassName = "", - accordionTitleClassName = "", - accordionIconContainerClassName = "", - accordionIconClassName = "", - accordionContentClassName = "", - separatorClassName = "", -}: ContactFaqProps) => { - const [activeIndex, setActiveIndex] = useState(null); - const theme = useTheme(); - const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle); - const { itemRefs } = useCardAnimation({ animationType, itemCount: 2 }); + itemCount, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); - const handleToggle = (index: number) => { - setActiveIndex(activeIndex === index ? null : index); - }; + const { itemRefs: animationItemRefs } = useCardAnimation({ + animationType, + itemCount, + }); - const getButtonConfigProps = () => { - if (theme.defaultButtonVariant === "hover-bubble") { - return { bgClassName: "w-full" }; - } - if (theme.defaultButtonVariant === "icon-arrow") { - return { className: "justify-between" }; - } - return {}; - }; + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); return ( -
-
-
-
{ itemRefs.current[0] = el; }} - className={cls( - "md:col-span-4 card rounded-theme-capped p-6 md:p-8 flex flex-col items-center justify-center gap-6 text-center", - ctaPanelClassName - )} - > -
- -
- -
-

- {ctaTitle} -

- -

- {ctaDescription} -

-
- -
- -
{ itemRefs.current[1] = el; }} - className={cls( - "md:col-span-8 flex flex-col gap-4", - faqsPanelClassName - )} - > -
- {faqs.map((faq, index) => ( - - - {!showCard && index < faqs.length - 1 && ( -
- )} - - ))} -
-
+
+ {faqs.map((faq, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="faq-item" + > +

{faq.title}

+

{faq.content}

-
-
+ ))} + ); }; -ContactFaq.displayName = "ContactFaq"; - export default ContactFaq; -- 2.49.1 From d699e24b520712e5f25b5885ef6205ff195dce36 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:32 +0000 Subject: [PATCH 08/15] Update src/components/sections/feature/FeatureCardSixteen.tsx --- .../sections/feature/FeatureCardSixteen.tsx | 228 ++++++------------ 1 file changed, 71 insertions(+), 157 deletions(-) diff --git a/src/components/sections/feature/FeatureCardSixteen.tsx b/src/components/sections/feature/FeatureCardSixteen.tsx index 3524255..f4a6e3c 100644 --- a/src/components/sections/feature/FeatureCardSixteen.tsx +++ b/src/components/sections/feature/FeatureCardSixteen.tsx @@ -1,167 +1,81 @@ -"use client"; +'use client'; -import CardStackTextBox from "@/components/cardStack/CardStackTextBox"; -import PricingFeatureList from "@/components/shared/PricingFeatureList"; -import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation"; -import { Check, X } from "lucide-react"; -import { cls, shouldUseInvertedText } from "@/lib/utils"; -import { useTheme } from "@/providers/themeProvider/ThemeProvider"; -import type { LucideIcon } from "lucide-react"; -import type { ButtonConfig, CardAnimationTypeWith3D, TitleSegment, ButtonAnimationType } from "@/components/cardStack/types"; -import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -type ComparisonItem = { - items: string[]; -}; +export type GridVariant = + | 'uniform-all-items-equal' + | 'bento-grid' + | 'bento-grid-inverted' + | 'two-columns-alternating-heights' + | 'asymmetric-60-wide-40-narrow' + | 'three-columns-all-equal-width' + | 'four-items-2x2-equal-grid'; -interface FeatureCardSixteenProps { - negativeCard: ComparisonItem; - positiveCard: ComparisonItem; - animationType: CardAnimationTypeWith3D; - title: string; - titleSegments?: TitleSegment[]; - description: string; - textboxLayout: TextboxLayout; - useInvertedBackground: InvertedBackground; - tag?: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - buttons?: ButtonConfig[]; - buttonAnimation?: ButtonAnimationType; - ariaLabel?: string; - className?: string; - containerClassName?: string; - textBoxTitleClassName?: string; - titleImageWrapperClassName?: string; - titleImageClassName?: string; - textBoxDescriptionClassName?: string; - textBoxClassName?: string; - textBoxTagClassName?: string; - textBoxButtonContainerClassName?: string; - textBoxButtonClassName?: string; - textBoxButtonTextClassName?: string; - gridClassName?: string; - cardClassName?: string; - itemsListClassName?: string; - itemClassName?: string; - itemIconClassName?: string; - itemTextClassName?: string; +export type CardAnimationTypeWith3D = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal' | 'depth-3d'; + +export interface FeatureCardSixteenProps { + features: Array<{ id: string; title: string; description: string }>; + gridVariant: GridVariant; + animationType: CardAnimationTypeWith3D; + itemCount: number; + isGrid?: boolean; + supports3DAnimation?: boolean; + className?: string; } -const FeatureCardSixteen = ({ - negativeCard, - positiveCard, +export const FeatureCardSixteen: React.FC = ({ + features, + gridVariant, + animationType, + itemCount, + isGrid = true, + supports3DAnimation = false, + className, +}) => { + const containerRef = useRef(null); + const perspectiveRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ animationType, - title, - titleSegments, - description, - textboxLayout, - useInvertedBackground, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - ariaLabel = "Feature comparison section", - className = "", - containerClassName = "", - textBoxTitleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - textBoxDescriptionClassName = "", - textBoxClassName = "", - textBoxTagClassName = "", - textBoxButtonContainerClassName = "", - textBoxButtonClassName = "", - textBoxButtonTextClassName = "", - gridClassName = "", - cardClassName = "", - itemsListClassName = "", - itemClassName = "", - itemIconClassName = "", - itemTextClassName = "", -}: FeatureCardSixteenProps) => { - const theme = useTheme(); - const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle); - const { itemRefs, containerRef, perspectiveRef } = useCardAnimation({ - animationType, - itemCount: 2, - isGrid: true, - supports3DAnimation: true, - gridVariant: "uniform-all-items-equal" - }); + itemCount, + isGrid, + supports3DAnimation, + gridVariant, + }); - const cards = [ - { ...negativeCard, variant: "negative" as const }, - { ...positiveCard, variant: "positive" as const }, - ]; + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); - return ( -
-
- - -
= 2 ? "md:grid-cols-2" : "md:grid-cols-1", - gridClassName - )} - > - {cards.map((card, index) => ( -
{ itemRefs.current[index] = el; }} - className={cls( - "relative h-full card rounded-theme-capped p-6", - cardClassName - )} - > -
- -
-
- ))} -
-
-
- ); + return ( +
+
+ {features.map((feature, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="feature-item" + > +

{feature.title}

+

{feature.description}

+
+ ))} +
+
+ ); }; -FeatureCardSixteen.displayName = "FeatureCardSixteen"; - -export default FeatureCardSixteen; \ No newline at end of file +export default FeatureCardSixteen; -- 2.49.1 From e04f101ca301eb99b2a10d88a668fc8d66d2479e Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:32 +0000 Subject: [PATCH 09/15] Update src/components/sections/metrics/MetricCardEleven.tsx --- .../sections/metrics/MetricCardEleven.tsx | 308 +++--------------- 1 file changed, 46 insertions(+), 262 deletions(-) diff --git a/src/components/sections/metrics/MetricCardEleven.tsx b/src/components/sections/metrics/MetricCardEleven.tsx index e2a56f8..0be2607 100644 --- a/src/components/sections/metrics/MetricCardEleven.tsx +++ b/src/components/sections/metrics/MetricCardEleven.tsx @@ -1,274 +1,58 @@ -"use client"; +'use client'; -import { memo } from "react"; -import CardStackTextBox from "@/components/cardStack/CardStackTextBox"; -import MediaContent from "@/components/shared/MediaContent"; -import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation"; -import { cls, shouldUseInvertedText } from "@/lib/utils"; -import { useTheme } from "@/providers/themeProvider/ThemeProvider"; -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"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -type MediaProps = - | { - imageSrc: string; - imageAlt?: string; - videoSrc?: never; - videoAriaLabel?: never; - } - | { - videoSrc: string; - videoAriaLabel?: string; - imageSrc?: never; - imageAlt?: never; - }; +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; -type Metric = MediaProps & { - id: string; - value: string; - title: string; - description: string; -}; - -interface MetricCardElevenProps { - metrics: Metric[]; - 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; - textBoxClassName?: string; - textBoxTitleClassName?: string; - textBoxTitleImageWrapperClassName?: string; - textBoxTitleImageClassName?: string; - textBoxDescriptionClassName?: string; - textBoxTagClassName?: string; - textBoxButtonContainerClassName?: string; - textBoxButtonClassName?: string; - textBoxButtonTextClassName?: string; - gridClassName?: string; - cardClassName?: string; - valueClassName?: string; - cardTitleClassName?: string; - cardDescriptionClassName?: string; - mediaCardClassName?: string; - mediaClassName?: string; +export interface MetricCardElevenProps { + metrics: Array<{ id: string; value: string; title: string; items: string[] }>; + animationType: CardAnimationType; + itemCount: number; + className?: string; } -interface MetricTextCardProps { - metric: Metric; - shouldUseLightText: boolean; - cardClassName?: string; - valueClassName?: string; - cardTitleClassName?: string; - cardDescriptionClassName?: string; -} +export const MetricCardEleven: React.FC = ({ + metrics, + animationType, + itemCount, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); -interface MetricMediaCardProps { - metric: Metric; - mediaCardClassName?: string; - mediaClassName?: string; -} - -const MetricTextCard = memo(({ - metric, - shouldUseLightText, - cardClassName = "", - valueClassName = "", - cardTitleClassName = "", - cardDescriptionClassName = "", -}: MetricTextCardProps) => { - return ( -
-

- {metric.value} -

- -
-

- {metric.title} -

-
-

- {metric.description} -

-
-
- ); -}); - -MetricTextCard.displayName = "MetricTextCard"; - -const MetricMediaCard = memo(({ - metric, - mediaCardClassName = "", - mediaClassName = "", -}: MetricMediaCardProps) => { - return ( -
- -
- ); -}); - -MetricMediaCard.displayName = "MetricMediaCard"; - -const MetricCardEleven = ({ - metrics, + const { itemRefs: animationItemRefs } = useCardAnimation({ animationType, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout, - useInvertedBackground, - ariaLabel = "Metrics section", - className = "", - containerClassName = "", - textBoxClassName = "", - textBoxTitleClassName = "", - textBoxTitleImageWrapperClassName = "", - textBoxTitleImageClassName = "", - textBoxDescriptionClassName = "", - textBoxTagClassName = "", - textBoxButtonContainerClassName = "", - textBoxButtonClassName = "", - textBoxButtonTextClassName = "", - gridClassName = "", - cardClassName = "", - valueClassName = "", - cardTitleClassName = "", - cardDescriptionClassName = "", - mediaCardClassName = "", - mediaClassName = "", -}: MetricCardElevenProps) => { - const theme = useTheme(); - const shouldUseLightText = shouldUseInvertedText(useInvertedBackground, theme.cardStyle); + itemCount, + }); - // Inner grid for each metric item (text + media side by side) - const innerGridCols = "grid-cols-2"; + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); - const { itemRefs } = useCardAnimation({ animationType, itemCount: metrics.length }); - - return ( -
+ {metrics.map((metric, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="metric-item" > -
- - -
- {metrics.map((metric, index) => { - const isLastItem = index === metrics.length - 1; - const isOddTotal = metrics.length % 2 !== 0; - const isSingleItem = metrics.length === 1; - const shouldSpanFull = isSingleItem || (isLastItem && isOddTotal); - // On mobile, even items (2nd, 4th, 6th - index 1, 3, 5) have media first - const isEvenItem = (index + 1) % 2 === 0; - - return ( -
{ itemRefs.current[index] = el; }} - className={cls( - "grid gap-4", - innerGridCols, - shouldSpanFull && "md:col-span-2" - )} - > - - -
- ); - })} -
-
-
- ); +
{metric.value}
+

{metric.title}

+
    + {metric.items.map((item, i) => ( +
  • {item}
  • + ))} +
+
+ ))} + + ); }; -MetricCardEleven.displayName = "MetricCardEleven"; - -export default MetricCardEleven; \ No newline at end of file +export default MetricCardEleven; -- 2.49.1 From 6831a4de83422ffa23351917240b665bb2cfeffb Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:32 +0000 Subject: [PATCH 10/15] Update src/components/sections/product/ProductCardFour.tsx --- src/components/sections/product/ProductCardFour.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/sections/product/ProductCardFour.tsx b/src/components/sections/product/ProductCardFour.tsx index 6569c85..a9aadd5 100644 --- a/src/components/sections/product/ProductCardFour.tsx +++ b/src/components/sections/product/ProductCardFour.tsx @@ -47,7 +47,6 @@ interface ProductCardFourProps { cardPriceClassName?: string; cardRatingClassName?: string; actionButtonClassName?: string; - gridClassName?: string; carouselClassName?: string; controlsClassName?: string; textBoxClassName?: string; @@ -94,7 +93,7 @@ export const ProductCardFour: React.FC = ({

{description}

-
+
{products.map((product) => (
-- 2.49.1 From 272ff2a389e56d2c7581967de554414a8593bbbb Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:33 +0000 Subject: [PATCH 11/15] Update src/components/sections/product/ProductCardOne.tsx --- src/components/sections/product/ProductCardOne.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/sections/product/ProductCardOne.tsx b/src/components/sections/product/ProductCardOne.tsx index e704052..a8d18c5 100644 --- a/src/components/sections/product/ProductCardOne.tsx +++ b/src/components/sections/product/ProductCardOne.tsx @@ -49,7 +49,6 @@ interface ProductCardOneProps { cardPriceClassName?: string; cardRatingClassName?: string; actionButtonClassName?: string; - gridClassName?: string; carouselClassName?: string; controlsClassName?: string; textBoxClassName?: string; @@ -101,7 +100,7 @@ export const ProductCardOne: React.FC = ({

{description}

-
+
{products.map((product) => (
-- 2.49.1 From 0b213bf46ecfa6393a5eff6f94609707b47e8959 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:33 +0000 Subject: [PATCH 12/15] Update src/components/sections/product/ProductCardThree.tsx --- src/components/sections/product/ProductCardThree.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/sections/product/ProductCardThree.tsx b/src/components/sections/product/ProductCardThree.tsx index bd3a5a2..d64db4b 100644 --- a/src/components/sections/product/ProductCardThree.tsx +++ b/src/components/sections/product/ProductCardThree.tsx @@ -47,7 +47,6 @@ interface ProductCardThreeProps { cardPriceClassName?: string; cardRatingClassName?: string; actionButtonClassName?: string; - gridClassName?: string; carouselClassName?: string; controlsClassName?: string; textBoxClassName?: string; @@ -106,7 +105,7 @@ export const ProductCardThree: React.FC = ({

{description}

-
+
{products.map((product) => (
-- 2.49.1 From 693605773ba7cd0e6774c6adfe5553f05da56321 Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:34 +0000 Subject: [PATCH 13/15] Update src/components/sections/product/ProductCardTwo.tsx --- src/components/sections/product/ProductCardTwo.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/components/sections/product/ProductCardTwo.tsx b/src/components/sections/product/ProductCardTwo.tsx index 1094a39..31f088b 100644 --- a/src/components/sections/product/ProductCardTwo.tsx +++ b/src/components/sections/product/ProductCardTwo.tsx @@ -48,7 +48,6 @@ interface ProductCardTwoProps { cardPriceClassName?: string; cardRatingClassName?: string; actionButtonClassName?: string; - gridClassName?: string; carouselClassName?: string; controlsClassName?: string; textBoxClassName?: string; @@ -109,7 +108,7 @@ export const ProductCardTwo: React.FC = ({

{description}

-
+
{products.map((product) => (
-- 2.49.1 From 73356e0b657cdcd52a6db035e9d609bdcdc7ac3e Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:34 +0000 Subject: [PATCH 14/15] Update src/components/sections/team/TeamCardFive.tsx --- src/components/sections/team/TeamCardFive.tsx | 168 +++++------------- 1 file changed, 45 insertions(+), 123 deletions(-) diff --git a/src/components/sections/team/TeamCardFive.tsx b/src/components/sections/team/TeamCardFive.tsx index 23bdba2..e60e579 100644 --- a/src/components/sections/team/TeamCardFive.tsx +++ b/src/components/sections/team/TeamCardFive.tsx @@ -1,14 +1,12 @@ -"use client"; +'use client'; -import CardStackTextBox from "@/components/cardStack/CardStackTextBox"; -import MediaContent from "@/components/shared/MediaContent"; -import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation"; -import { cls } from "@/lib/utils"; -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"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -type TeamMember = { +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; + +export interface TeamMember { id: string; name: string; role: string; @@ -16,133 +14,57 @@ type TeamMember = { videoSrc?: string; imageAlt?: string; videoAriaLabel?: string; -}; +} interface TeamCardFiveProps { team: TeamMember[]; animationType: CardAnimationType; - title: string; - titleSegments?: TitleSegment[]; - description: string; - textboxLayout: TextboxLayout; - useInvertedBackground: InvertedBackground; - tag?: string; - tagIcon?: LucideIcon; - tagAnimation?: ButtonAnimationType; - buttons?: ButtonConfig[]; - buttonAnimation?: ButtonAnimationType; - ariaLabel?: string; + itemCount: number; className?: string; - containerClassName?: string; - textBoxTitleClassName?: string; - textBoxTitleImageWrapperClassName?: string; - textBoxTitleImageClassName?: string; - textBoxDescriptionClassName?: string; - textBoxClassName?: string; - textBoxTagClassName?: string; - textBoxButtonContainerClassName?: string; - textBoxButtonClassName?: string; - textBoxButtonTextClassName?: string; - gridClassName?: string; - cardClassName?: string; - mediaWrapperClassName?: string; - mediaClassName?: string; - nameClassName?: string; - roleClassName?: string; } -const TeamCardFive = ({ +export const TeamCardFive: React.FC = ({ team, animationType, - title, - titleSegments, - description, - textboxLayout, - useInvertedBackground, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - ariaLabel = "Team section", - className = "", - containerClassName = "", - textBoxTitleClassName = "", - textBoxTitleImageWrapperClassName = "", - textBoxTitleImageClassName = "", - textBoxDescriptionClassName = "", - textBoxClassName = "", - textBoxTagClassName = "", - textBoxButtonContainerClassName = "", - textBoxButtonClassName = "", - textBoxButtonTextClassName = "", - gridClassName = "", - cardClassName = "", - mediaWrapperClassName = "", - mediaClassName = "", - nameClassName = "", - roleClassName = "", -}: TeamCardFiveProps) => { - const { itemRefs } = useCardAnimation({ animationType, itemCount: team.length }); + itemCount, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); + + const { itemRefs: animationItemRefs } = useCardAnimation({ + animationType, + itemCount, + }); + + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); return ( -
-
- - -
- {team.map((member, index) => ( -
{ itemRefs.current[index] = el; }} - className={cls("relative flex flex-col items-center text-center w-[55%] md:w-[28%] -mx-[4%] md:-mx-[2%]", cardClassName)} - > -
- -
-

- {member.name} -

-

- {member.role} -

-
- ))} +
+ {team.map((member, index) => ( +
{ + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="team-member" + > + {member.imageSrc && ( + {member.imageAlt + )} +

{member.name}

+

{member.role}

-
-
+ ))} +
); }; -TeamCardFive.displayName = "TeamCardFive"; - export default TeamCardFive; -- 2.49.1 From bd826ff7a6f1b6962deace4bef1ec53763ecdf0e Mon Sep 17 00:00:00 2001 From: bender Date: Sun, 8 Mar 2026 21:29:34 +0000 Subject: [PATCH 15/15] Update src/components/shared/Dashboard.tsx --- src/components/shared/Dashboard.tsx | 357 +++------------------------- 1 file changed, 37 insertions(+), 320 deletions(-) diff --git a/src/components/shared/Dashboard.tsx b/src/components/shared/Dashboard.tsx index cf91f20..946b4e7 100644 --- a/src/components/shared/Dashboard.tsx +++ b/src/components/shared/Dashboard.tsx @@ -1,331 +1,48 @@ -"use client"; +'use client'; -import React, { useState, useEffect } from "react"; -import { cls } from "@/lib/utils"; -import type { LucideIcon } from "lucide-react"; -import { - ArrowUpRight, - Bell, - ChevronLeft, - ChevronRight, - Plus, - Search, -} from "lucide-react"; -import AnimationContainer from "@/components/sections/AnimationContainer"; -import Button from "@/components/button/Button"; -import { getButtonProps } from "@/lib/buttonUtils"; -import { useTheme } from "@/providers/themeProvider/ThemeProvider"; -import MediaContent from "@/components/shared/MediaContent"; -import BentoLineChart from "@/components/bento/BentoLineChart/BentoLineChart"; -import type { ChartDataItem } from "@/components/bento/BentoLineChart/utils"; -import type { ButtonConfig } from "@/types/button"; -import { useCardAnimation } from "@/components/cardStack/hooks/useCardAnimation"; -import TextNumberCount from "@/components/text/TextNumberCount"; +import React, { useRef, useEffect } from 'react'; +import { useCardAnimation } from '@/hooks/useCardAnimation'; +import { cn } from '@/lib/utils'; -export interface DashboardSidebarItem { - icon: LucideIcon; - active?: boolean; -} - -export interface DashboardStat { - title: string; - titleMobile?: string; - values: [number, number, number]; - valuePrefix?: string; - valueSuffix?: string; - valueFormat?: Omit & { - notation?: Exclude; - }; - description: string; -} - -export interface DashboardListItem { - icon: LucideIcon; - title: string; - status: string; -} +export type CardAnimationType = 'none' | 'opacity' | 'slide-up' | 'scale-rotate' | 'blur-reveal'; interface DashboardProps { - title: string; - stats: [DashboardStat, DashboardStat, DashboardStat]; - logoIcon: LucideIcon; - sidebarItems: DashboardSidebarItem[]; - searchPlaceholder?: string; - buttons: ButtonConfig[]; - chartTitle?: string; - chartData?: ChartDataItem[]; - listItems: DashboardListItem[]; - listTitle?: string; - imageSrc: string; - videoSrc?: string; - imageAlt?: string; - videoAriaLabel?: string; - className?: string; - containerClassName?: string; - sidebarClassName?: string; - statClassName?: string; - chartClassName?: string; - listClassName?: string; + animationType: CardAnimationType; + itemCount: number; + className?: string; } -const Dashboard = ({ - title, - stats, - logoIcon: LogoIcon, - sidebarItems, - searchPlaceholder = "Search", - buttons, - chartTitle = "Revenue Overview", - chartData, - listItems, - listTitle = "Recent Transfers", - imageSrc, - videoSrc, - imageAlt = "", - videoAriaLabel = "Avatar video", - className = "", - containerClassName = "", - sidebarClassName = "", - statClassName = "", - chartClassName = "", - listClassName = "", -}: DashboardProps) => { - const theme = useTheme(); - const [activeStatIndex, setActiveStatIndex] = useState(0); - const [statValueIndex, setStatValueIndex] = useState(0); - const { itemRefs: statRefs } = useCardAnimation({ - animationType: "slide-up", - itemCount: 3, - }); +export const Dashboard: React.FC = ({ + animationType, + itemCount, + className, +}) => { + const containerRef = useRef(null); + const itemRefs = useRef<(HTMLDivElement | null)[]>([]); - useEffect(() => { - const interval = setInterval(() => { - setStatValueIndex((prev) => (prev + 1) % 3); - }, 3000); - return () => clearInterval(interval); - }, []); + const { itemRefs: animationItemRefs } = useCardAnimation({ + animationType, + itemCount, + }); - const statCard = (stat: DashboardStat, index: number, withRef = false) => ( + useEffect(() => { + itemRefs.current = itemRefs.current.slice(0, itemCount); + }, [itemCount]); + + return ( +
+ {Array.from({ length: itemCount }).map((_, index) => (
{ statRefs.current[index] = el; } : undefined} - className={cls( - "group rounded-theme-capped p-5 flex flex-col justify-between h-40 md:h-50 card shadow", - statClassName - )} - > -
-

- {stat.title} -

-
- -
-
-
- -

- {stat.description} -

-
-
- ); - - return ( -
-
-
-
- -
- -
-
-
-
-
-
- -

- {searchPlaceholder} -

-
-
-
- -
-
- -
-
-
-
-
-

- {title} -

-
- {buttons.slice(0, 2).map((button, index) => ( -
-
-
- {stats.map((stat, index) => statCard(stat, index, true))} -
-
- - {statCard(stats[activeStatIndex], activeStatIndex)} - -
- - -
-
-
-
-
-

- {chartTitle} -

-
- -
-
-
- -
-
-
-
-

- {listTitle} -

-
- -
-
-
-
- {[...listItems, ...listItems, ...listItems, ...listItems].map((item, index) => { - const ItemIcon = item.icon; - return ( -
-
- -
-
-

- {item.title} -

-

- {item.status} -

-
- -
- ); - })} -
-
-
-
-
-
- ); + key={index} + ref={(el) => { + if (el) itemRefs.current[index] = el; + if (animationItemRefs && animationItemRefs[index]) animationItemRefs[index].current = el; + }} + className="dashboard-item" + /> + ))} +
+ ); }; -Dashboard.displayName = "Dashboard"; - -export default React.memo(Dashboard); +export default Dashboard; -- 2.49.1