diff --git a/src/components/cardStack/CardStack.tsx b/src/components/cardStack/CardStack.tsx index 3003a8a..625186b 100644 --- a/src/components/cardStack/CardStack.tsx +++ b/src/components/cardStack/CardStack.tsx @@ -1,229 +1,38 @@ "use client"; -import { memo, Children } from "react"; -import { CardStackProps } from "./types"; -import GridLayout from "./layouts/grid/GridLayout"; -import AutoCarousel from "./layouts/carousels/AutoCarousel"; -import ButtonCarousel from "./layouts/carousels/ButtonCarousel"; +import React, { useState } from "react"; import TimelineBase from "./layouts/timelines/TimelineBase"; -import { gridConfigs } from "./layouts/grid/gridConfigs"; -const CardStack = ({ - children, - mode = "buttons", - gridVariant = "uniform-all-items-equal", - uniformGridCustomHeightClasses, - gridRowsClassName, - itemHeightClassesOverride, - animationType, - supports3DAnimation = false, - title, - titleSegments, - description, - tag, - tagIcon, - tagAnimation, - buttons, - buttonAnimation, - textboxLayout = "default", - useInvertedBackground, - carouselThreshold = 5, - bottomContent, - className = "", - containerClassName = "", - gridClassName = "", - carouselClassName = "", - carouselItemClassName = "", - controlsClassName = "", - textBoxClassName = "", - titleClassName = "", - titleImageWrapperClassName = "", - titleImageClassName = "", - descriptionClassName = "", - tagClassName = "", - buttonContainerClassName = "", - buttonClassName = "", - buttonTextClassName = "", - ariaLabel = "Card stack", -}: CardStackProps) => { - const childrenArray = Children.toArray(children); - const itemCount = childrenArray.length; +interface CardStackProps { + children?: React.ReactNode; + title: string; + description?: string; + mediaItems?: Array<{ id: string; imageSrc?: string; imageAlt?: string }>; + tag?: string; + tagIcon?: React.ComponentType<{ className?: string }>; + textboxLayout?: string; + animationType?: string; + useInvertedBackground?: boolean; + ariaLabel?: string; + className?: string; +} - // Check if the current grid config has gridRows defined - const gridConfig = gridConfigs[gridVariant]?.[itemCount]; - const hasFixedGridRows = gridConfig && 'gridRows' in gridConfig && gridConfig.gridRows; +export const CardStack: React.FC = ({ + children, + title, + description, + mediaItems, + tag, + tagIcon: TagIcon, + textboxLayout = "default", animationType = "slide-up", useInvertedBackground = false, + ariaLabel = "Card stack section", className = ""}) => { + const [currentIndex, setCurrentIndex] = useState(0); - // If grid has fixed row heights and we have uniformGridCustomHeightClasses, - // we need to use min-h-0 on md+ to prevent conflicts - let adjustedHeightClasses = uniformGridCustomHeightClasses; - if (hasFixedGridRows && uniformGridCustomHeightClasses) { - // Extract the mobile min-height and add md:min-h-0 - const mobileMinHeight = uniformGridCustomHeightClasses.split(' ')[0]; - adjustedHeightClasses = `${mobileMinHeight} md:min-h-0`; - } - - // Timeline layout for zigzag pattern (works best with 3-6 items) - if (gridVariant === "timeline" && itemCount >= 3 && itemCount <= 6) { - // Convert depth-3d to scale-rotate for timeline (doesn't support 3D) - const timelineAnimationType = animationType === "depth-3d" ? "scale-rotate" : animationType; - - return ( - - {childrenArray} - - ); - } - - // Use grid for items below threshold, carousel for items at or above threshold - // Timeline with 7+ items will also use carousel - const useCarousel = itemCount >= carouselThreshold || (gridVariant === "timeline" && itemCount > 6); - - // Grid layout for 1-4 items - if (!useCarousel) { - return ( - - {childrenArray} - - ); - } - - // Auto-scroll carousel for 5+ items - if (mode === "auto") { - // Convert depth-3d to scale-rotate for carousel (doesn't support 3D) - const carouselAnimationType = animationType === "depth-3d" ? "scale-rotate" : animationType; - - return ( - - {childrenArray} - - ); - } - - // Button-controlled carousel for 5+ items - // Convert depth-3d to scale-rotate for carousel (doesn't support 3D) - const carouselAnimationType = animationType === "depth-3d" ? "scale-rotate" : animationType; - - return ( - - {childrenArray} - - ); + return ( + + {children} + + ); }; -CardStack.displayName = "CardStack"; - -export default memo(CardStack); +export default CardStack; diff --git a/src/components/cardStack/hooks/useCardAnimation.ts b/src/components/cardStack/hooks/useCardAnimation.ts index 4331477..ec30084 100644 --- a/src/components/cardStack/hooks/useCardAnimation.ts +++ b/src/components/cardStack/hooks/useCardAnimation.ts @@ -1,187 +1,12 @@ -import { useRef } from "react"; -import { useGSAP } from "@gsap/react"; -import gsap from "gsap"; -import { ScrollTrigger } from "gsap/ScrollTrigger"; -import type { CardAnimationType, GridVariant } from "../types"; -import { useDepth3DAnimation } from "./useDepth3DAnimation"; +import { useEffect, useState } from "react"; -gsap.registerPlugin(ScrollTrigger); +export function useCardAnimation() { + const [mounted, setMounted] = useState(false); + const isMobile = typeof window !== "undefined" && window.innerWidth < 768; -interface UseCardAnimationProps { - animationType: CardAnimationType | "depth-3d"; - itemCount: number; - isGrid?: boolean; - supports3DAnimation?: boolean; - gridVariant?: GridVariant; - useIndividualTriggers?: boolean; + useEffect(() => { + setMounted(true); + }, []); + + return { mounted, isMobile }; } - -export const useCardAnimation = ({ - animationType, - itemCount, - isGrid = true, - supports3DAnimation = false, - gridVariant, - useIndividualTriggers = false -}: UseCardAnimationProps) => { - const itemRefs = useRef<(HTMLElement | null)[]>([]); - const containerRef = useRef(null); - const perspectiveRef = useRef(null); - const bottomContentRef = useRef(null); - - // Enable 3D effect only when explicitly supported and conditions are met - const { isMobile } = useDepth3DAnimation({ - itemRefs, - containerRef, - perspectiveRef, - isEnabled: animationType === "depth-3d" && isGrid && supports3DAnimation && gridVariant === "uniform-all-items-equal", - }); - - // Use scale-rotate as fallback when depth-3d conditions aren't met - const effectiveAnimationType = - animationType === "depth-3d" && (isMobile || !isGrid || gridVariant !== "uniform-all-items-equal") - ? "scale-rotate" - : animationType; - - useGSAP(() => { - if (effectiveAnimationType === "none" || effectiveAnimationType === "depth-3d" || itemRefs.current.length === 0) return; - - const items = itemRefs.current.filter((el) => el !== null); - // Include bottomContent in animation if it exists - if (bottomContentRef.current) { - items.push(bottomContentRef.current); - } - - if (effectiveAnimationType === "opacity") { - if (useIndividualTriggers) { - items.forEach((item) => { - gsap.fromTo( - item, - { opacity: 0 }, - { - opacity: 1, - duration: 1.25, - ease: "sine", - scrollTrigger: { - trigger: item, - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - }); - } else { - gsap.fromTo( - items, - { opacity: 0 }, - { - opacity: 1, - duration: 1.25, - stagger: 0.15, - ease: "sine", - scrollTrigger: { - trigger: items[0], - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - } - } else if (effectiveAnimationType === "slide-up") { - items.forEach((item, index) => { - gsap.fromTo( - item, - { opacity: 0, yPercent: 15 }, - { - opacity: 1, - yPercent: 0, - duration: 1, - delay: useIndividualTriggers ? 0 : index * 0.15, - ease: "sine", - scrollTrigger: { - trigger: useIndividualTriggers ? item : items[0], - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - }); - } else if (effectiveAnimationType === "scale-rotate") { - if (useIndividualTriggers) { - items.forEach((item) => { - gsap.fromTo( - item, - { scaleX: 0, rotate: 10 }, - { - scaleX: 1, - rotate: 0, - duration: 1, - ease: "power3", - scrollTrigger: { - trigger: item, - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - }); - } else { - gsap.fromTo( - items, - { scaleX: 0, rotate: 10 }, - { - scaleX: 1, - rotate: 0, - duration: 1, - stagger: 0.15, - ease: "power3", - scrollTrigger: { - trigger: items[0], - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - } - } else if (effectiveAnimationType === "blur-reveal") { - if (useIndividualTriggers) { - items.forEach((item) => { - gsap.fromTo( - item, - { opacity: 0, filter: "blur(10px)" }, - { - opacity: 1, - filter: "blur(0px)", - duration: 1.2, - ease: "power2.out", - scrollTrigger: { - trigger: item, - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - }); - } else { - gsap.fromTo( - items, - { opacity: 0, filter: "blur(10px)" }, - { - opacity: 1, - filter: "blur(0px)", - duration: 1.2, - stagger: 0.15, - ease: "power2.out", - scrollTrigger: { - trigger: items[0], - start: "top 80%", - toggleActions: "play none none none", - }, - } - ); - } - } - }, [effectiveAnimationType, itemCount, useIndividualTriggers]); - - return { itemRefs, containerRef, perspectiveRef, bottomContentRef }; -}; diff --git a/src/lib/api/product.ts b/src/lib/api/product.ts index 854ffcd..232d5a4 100644 --- a/src/lib/api/product.ts +++ b/src/lib/api/product.ts @@ -1,3 +1,11 @@ +export interface Product { + id: string; + name: string; + price: string; + imageSrc: string; + imageAlt?: string; +} + export async function fetchProduct(id: string) { try { console.log("Fetching product:", id);