Merge version_2 into main #14

Merged
bender merged 13 commits from version_2 into main 2026-03-12 04:05:08 +00:00
13 changed files with 303 additions and 1085 deletions

View File

@@ -1,5 +1,6 @@
"use client";
import React from 'react';
import { ThemeProvider } from '@/providers/themeProvider/ThemeProvider';
import NavbarStyleFullscreen from '@/components/navbar/NavbarStyleFullscreen/NavbarStyleFullscreen';
import HeroBillboardTestimonial from '@/components/sections/hero/HeroBillboardTestimonial';
@@ -12,233 +13,183 @@ import TestimonialCardFifteen from '@/components/sections/testimonial/Testimonia
import FaqSplitMedia from '@/components/sections/faq/FaqSplitMedia';
import ContactCTA from '@/components/sections/contact/ContactCTA';
import FooterBase from '@/components/sections/footer/FooterBase';
import { Sparkles, Star, Grid, Award, TrendingUp, Briefcase, Mail, HelpCircle, Shirt, Dumbbell, Activity, Zap, Smartphone, Cpu, Home, Sofa, Layout, Heart } from 'lucide-react';
import Link from 'next/link';
import { Sparkles, TrendingUp, Users, ArrowRight } from 'lucide-react';
export default function HomePage() {
const plan = {
theme: {
defaultButtonVariant: "text-stagger" as const,
defaultTextAnimation: "entrance-slide" as const,
borderRadius: "rounded" as const,
contentWidth: "medium" as const,
sizing: "medium" as const,
background: "circleGradient" as const,
cardStyle: "glass-elevated" as const,
primaryButtonStyle: "gradient" as const,
secondaryButtonStyle: "glass" as const,
headingFontWeight: "normal" as const,
},
};
const navItems = [
{ name: "Home", id: "/" },
{ name: "About", id: "about" },
{ name: "Services", id: "categories" },
{ name: "Contact", id: "contact" },
];
export default function Home() {
return (
<ThemeProvider
defaultButtonVariant="text-stagger"
defaultTextAnimation="entrance-slide"
borderRadius="rounded"
contentWidth="medium"
sizing="medium"
background="circleGradient"
cardStyle="glass-elevated"
primaryButtonStyle="gradient"
secondaryButtonStyle="glass"
headingFontWeight="normal"
defaultButtonVariant={plan.theme.defaultButtonVariant}
defaultTextAnimation={plan.theme.defaultTextAnimation}
borderRadius={plan.theme.borderRadius}
contentWidth={plan.theme.contentWidth}
sizing={plan.theme.sizing}
background={plan.theme.background}
cardStyle={plan.theme.cardStyle}
primaryButtonStyle={plan.theme.primaryButtonStyle}
secondaryButtonStyle={plan.theme.secondaryButtonStyle}
headingFontWeight={plan.theme.headingFontWeight}
>
<div id="nav" data-section="nav">
<NavbarStyleFullscreen
navItems={[
{ name: "Home", id: "/" },
{ name: "Fashion", id: "fashion" },
{ name: "Home & Decor", id: "home-category" },
{ name: "Gym", id: "gym" },
{ name: "Electronics", id: "electronics" },
]}
brandName="ZSMX Store"
bottomLeftText="Premium Multi-Category Store"
bottomRightText="hello@zsmxstore.com"
/>
<NavbarStyleFullscreen navItems={navItems} />
</div>
<div id="hero" data-section="hero">
<HeroBillboardTestimonial
title="Discover Your Perfect Style"
description="Explore our curated collection of fashion, home decor, fitness equipment, and premium electronics. Where quality meets elegance."
tag="Welcome to ZSMX Store"
tagIcon={Sparkles}
tagAnimation="slide-up"
background={{ variant: "radial-gradient" }}
imageSrc="http://img.b2bpic.net/free-photo/internationals-people-standing-cafe_1157-32402.jpg"
imageAlt="Premium multi-category product showcase"
mediaAnimation="slide-up"
tag="Testimonials"
tagIcon={Sparkles}
title="What Our Customers Say"
description="Hear from our satisfied clients about their experience with our products and services."
testimonials={[
{
name: "Sarah Mitchell", handle: "Fashion Enthusiast", testimonial: "Exceptional quality and stunning designs. ZSMX Store has become my go-to for everything.", rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg", imageAlt: "Sarah Mitchell"
},
name: "Sarah Johnson", handle: "@sarahj", testimonial: "Amazing product that transformed our workflow!", rating: 5,
imageSrc: "/placeholders/placeholder1.webp"},
{
name: "James Chen", handle: "Interior Designer", testimonial: "The home collection is absolutely exquisite. Premium pieces that transform any space.", rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg", imageAlt: "James Chen"
},
{
name: "Emma Rodriguez", handle: "Fitness Coach", testimonial: "Top-tier gym equipment. My clients and I love the durability and design.", rating: 5,
imageSrc: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg", imageAlt: "Emma Rodriguez"
}
name: "John Doe", handle: "@johndoe", testimonial: "Great support and excellent service. Highly recommended!", rating: 5,
imageSrc: "/placeholders/placeholder2.webp"},
]}
testimonialRotationInterval={5000}
buttons={[
{ text: "Shop Now", href: "fashion" },
{ text: "Explore Categories", href: "categories" }
{ text: "Get Started", href: "/" },
{ text: "Learn More", href: "categories" },
]}
buttonAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="products" data-section="products">
<ProductCardTwo
title="Featured Collection"
description="Hand-picked premium products across all categories. New arrivals updated daily."
tag="Best Sellers"
tagIcon={Star}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
gridVariant="bento-grid"
useInvertedBackground={false}
products={[
{
id: "fashion-1", brand: "LuxeStyle", name: "Premium Wool Overcoat", price: "$450.00", rating: 5, reviewCount: "342", imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg", imageAlt: "Premium Wool Overcoat"
},
id: "1", brand: "Premium", name: "Eclipse Motion Pro", price: "$150", rating: 5,
reviewCount: "128", imageSrc: "/placeholders/placeholder1.webp"},
{
id: "fashion-2", brand: "ElegantWear", name: "Designer Evening Gown", price: "$680.00", rating: 5, reviewCount: "289", imageSrc: "http://img.b2bpic.net/free-photo/store-customer-holding-shirt-body_482257-85803.jpg", imageAlt: "Designer Evening Gown"
},
id: "2", brand: "Standard", name: "Wave Dynamics", price: "$99", rating: 4,
reviewCount: "95", imageSrc: "/placeholders/placeholder2.webp"},
{
id: "fashion-3", brand: "ClassicThreads", name: "Italian Leather Shoes", price: "$395.00", rating: 4, reviewCount: "156", imageSrc: "http://img.b2bpic.net/free-photo/still-life-with-classic-shirts_23-2150828626.jpg", imageAlt: "Italian Leather Shoes"
},
{
id: "home-1", brand: "Luxehome", name: "Modern Sectional Sofa", price: "$1,299.00", rating: 5, reviewCount: "201", imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg", imageAlt: "Modern Sectional Sofa"
},
{
id: "home-2", brand: "DecorPremium", name: "Crystal Chandelier", price: "$850.00", rating: 5, reviewCount: "178", imageSrc: "http://img.b2bpic.net/free-photo/couch-with-cushions-glass-table_1203-764.jpg", imageAlt: "Crystal Chandelier"
},
{
id: "home-3", brand: "InteriorLux", name: "Turkish Area Rug", price: "$625.00", rating: 4, reviewCount: "124", imageSrc: "http://img.b2bpic.net/free-photo/cafe-with-coffee-tables-cosy-sofas-plants-shelves_140725-7785.jpg", imageAlt: "Turkish Area Rug"
}
]}
buttons={[
{ text: "View All Products", href: "fashion" }
id: "3", brand: "Elite", name: "Aurora Series", price: "$199", rating: 5,
reviewCount: "156", imageSrc: "/placeholders/placeholder3.webp"},
]}
gridVariant="three-columns-all-equal-width"
animationType="slide-up"
title="Featured Products"
description="Discover our latest collection of premium products"
textboxLayout="default"
useInvertedBackground={false}
/>
</div>
<div id="categories" data-section="categories">
<FeatureCardTen
title="Our Category Showcase"
description="Explore our diverse range of premium products across four expertly curated categories. Each collection represents the finest in quality and design."
tag="Categories"
tagIcon={Grid}
tagAnimation="slide-up"
features={[
{
id: "1", title: "Fast Performance", description: "Lightning-fast speeds optimized for your workflow", media: { imageSrc: "/placeholders/placeholder1.webp", imageAlt: "Performance" },
items: [
{ icon: TrendingUp, text: "10x faster processing" },
{ icon: Users, text: "Real-time collaboration" },
],
reverse: false,
},
{
id: "2", title: "Scalable Solutions", description: "Grow your business without limitations", media: { imageSrc: "/placeholders/placeholder2.webp", imageAlt: "Scalability" },
items: [
{ icon: ArrowRight, text: "Unlimited growth" },
{ icon: Sparkles, text: "Enterprise ready" },
],
reverse: true,
},
]}
title="Why Choose Us"
description="Powerful features designed for success"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
features={[
{
id: "1", title: "Fashion Excellence", description: "Premium apparel and accessories designed for those who appreciate style. From casual elegance to formal sophistication.", media: { imageSrc: "http://img.b2bpic.net/free-photo/bag-hanging-from-furniture-item-indoors_23-2151073505.jpg" },
items: [
{ icon: Shirt, text: "Designer Collections" },
{ icon: Sparkles, text: "Premium Fabrics" },
{ icon: Heart, text: "Timeless Styles" }
],
reverse: false
},
{
id: "2", title: "Home Furnishings", description: "Transform your living space with luxury home decor. Curated pieces that combine functionality with aesthetic elegance.", media: { imageSrc: "http://img.b2bpic.net/free-photo/beautiful-dried-flowers-table_23-2149591635.jpg" },
items: [
{ icon: Home, text: "Modern Design" },
{ icon: Sofa, text: "Premium Materials" },
{ icon: Layout, text: "Expert Curation" }
],
reverse: true
},
{
id: "3", title: "Fitness & Gym", description: "Professional-grade fitness equipment and apparel. Engineered for performance and durability in every workout.", media: { imageSrc: "http://img.b2bpic.net/free-photo/still-life-perfectly-ordered-fitness-gym-accessories_52683-100705.jpg" },
items: [
{ icon: Dumbbell, text: "Professional Equipment" },
{ icon: Activity, text: "Performance Gear" },
{ icon: Zap, text: "High Durability" }
],
reverse: false
},
{
id: "4", title: "Premium Electronics", description: "Latest technology and innovative gadgets. Cutting-edge devices that enhance your digital lifestyle.", media: { imageSrc: "http://img.b2bpic.net/free-photo/view-robotic-vacuum-cleaner-flat-surface_23-2151736769.jpg" },
items: [
{ icon: Smartphone, text: "Latest Technology" },
{ icon: Cpu, text: "Advanced Features" },
{ icon: Zap, text: "Top Performance" }
],
reverse: true
}
]}
/>
</div>
<div id="about" data-section="about">
<MetricSplitMediaAbout
title="Your Trusted Multi-Category Destination"
description="ZSMX Store is your premier destination for premium products across fashion, home, fitness, and electronics. We believe in delivering excellence through carefully curated collections, exceptional quality, and outstanding customer service. Our mission is to make luxury and quality accessible to everyone."
tag="About ZSMX"
tagIcon={Award}
tagAnimation="slide-up"
title="About Our Company"
description="We\'re dedicated to delivering excellence and innovation in everything we do."
metrics={[
{ value: "50k+", title: "Satisfied Customers" },
{ value: "10k+", title: "Premium Products" }
{ value: "10+", title: "Years Experience" },
{ value: "500+", title: "Happy Clients" },
{ value: "50M+", title: "Users Worldwide" },
{ value: "99.9%", title: "Uptime" },
]}
imageSrc="http://img.b2bpic.net/free-photo/modern-sauna-with-panoramic-windows-wooden-design_169016-70021.jpg"
imageAlt="ZSMX Store - Premium retail environment"
useInvertedBackground={true}
imageSrc="/placeholders/placeholder1.webp"
imageAlt="About us"
mediaAnimation="slide-up"
metricsAnimation="slide-up"
useInvertedBackground={false}
/>
</div>
<div id="metrics" data-section="metrics">
<MetricCardSeven
title="By The Numbers"
description="Trusted by thousands of customers worldwide. Our commitment to quality and service speaks for itself."
tag="Our Growth"
tagIcon={TrendingUp}
tagAnimation="slide-up"
textboxLayout="default"
animationType="slide-up"
useInvertedBackground={false}
metrics={[
{
id: "1", value: "98%", title: "Customer Satisfaction Rate", items: ["Premium quality guaranteed", "Expert curation", "Dedicated support team"]
id: "1", value: "7,000+", title: "Conversions", items: ["Increased by 45%", "Monthly growth"],
},
{
id: "2", value: "24/7", title: "Customer Support Available", items: ["Real-time assistance", "Expert consultations", "Fast responses"]
id: "2", value: "50,000+", title: "Active Users", items: ["Growing daily", "Engaged community"],
},
{
id: "3", value: "100%", title: "Authentic Products", items: ["Verified sources", "Quality assurance", "Brand authenticity"]
id: "3", value: "$2.5M", title: "Revenue", items: ["Year-over-year", "Consistent growth"],
},
{
id: "4", value: "Free", title: "Shipping On Orders Over $100", items: ["Fast delivery", "Tracking included", "Safe packaging"]
}
id: "4", value: "99.9%", title: "Uptime", items: ["24/7 monitoring", "Reliable service"],
},
]}
animationType="slide-up"
title="Performance Metrics"
description="See how we\'re making a difference"
textboxLayout="default"
useInvertedBackground={false}
/>
</div>
<div id="social-proof" data-section="social-proof">
<SocialProofOne
title="Trusted by Leading Brands & Retailers"
description="Partnered with premium brands worldwide to bring you authentic luxury products."
tag="Our Partners"
tagIcon={Briefcase}
tagAnimation="slide-up"
names={["Company A", "Company B", "Company C", "Company D", "Company E"]}
title="Trusted by Leading Companies"
description="Join thousands of businesses using our platform"
textboxLayout="default"
useInvertedBackground={false}
names={["LuxeStyle", "ElegantWear", "ClassicThreads", "Luxehome", "DecorPremium", "InteriorLux", "FitnessPro", "SportsTech"]}
speed={40}
showCard={true}
/>
</div>
<div id="testimonials" data-section="testimonials">
<TestimonialCardFifteen
testimonial="ZSMX Store has completely revolutionized how I shop online. The selection is incredible, the quality is unmatched, and the customer service is exceptional. I've purchased from all four categories and been amazed every single time."
testimonial="This platform has completely transformed how we manage our business. The support team is exceptional!"
rating={5}
author="Victoria Thompson, Premium Lifestyle Enthusiast"
author="Jane Smith"
avatars={[
{ src: "http://img.b2bpic.net/free-photo/portrait-confident-young-businessman-with-his-arms-crossed_23-2148176206.jpg", alt: "Customer 1" },
{ src: "http://img.b2bpic.net/free-photo/positive-confident-businessman-posing-outside_74855-1183.jpg", alt: "Customer 2" },
{ src: "http://img.b2bpic.net/free-photo/modern-businesswoman_23-2148012909.jpg", alt: "Customer 3" },
{ src: "http://img.b2bpic.net/free-photo/businessman-formal-wear-professional-corporate-concept_53876-71166.jpg", alt: "Customer 4" },
{ src: "http://img.b2bpic.net/free-photo/beautiful-business-woman-portrait_23-2149280717.jpg", alt: "Customer 5" },
{ src: "http://img.b2bpic.net/free-photo/portrait-outdoors-business-man-smiles_23-2148763856.jpg", alt: "Customer 6" }
{ src: "/placeholders/placeholder1.webp", alt: "Avatar 1" },
{ src: "/placeholders/placeholder2.webp", alt: "Avatar 2" },
{ src: "/placeholders/placeholder3.webp", alt: "Avatar 3" },
]}
ratingAnimation="slide-up"
avatarsAnimation="slide-up"
@@ -248,89 +199,67 @@ export default function HomePage() {
<div id="faq" data-section="faq">
<FaqSplitMedia
title="Frequently Asked Questions"
description="Find answers to common questions about our products, ordering, shipping, and customer service."
tag="Help Center"
tagIcon={HelpCircle}
tagAnimation="slide-up"
textboxLayout="default"
useInvertedBackground={false}
faqs={[
{
id: "1", title: "Are all products authentic and guaranteed?", content: "Yes, absolutely. We source directly from authorized distributors and verify authenticity of all products. Every item comes with our quality guarantee and certification of authenticity."
},
id: "1", title: "How do I get started?", content: "Getting started is easy. Sign up for an account, choose your plan, and start using our platform immediately."},
{
id: "2", title: "What is your return and exchange policy?", content: "We offer hassle-free returns and exchanges within 30 days of purchase. Items must be unused and in original packaging. Simply contact our customer service team to initiate the process."
},
id: "2", title: "What is your support policy?", content: "We offer 24/7 customer support via email, chat, and phone. Our average response time is under 2 hours."},
{
id: "3", title: "How long does shipping typically take?", content: "Standard shipping takes 5-7 business days. Express shipping options (2-3 days) are available for most orders. Orders over $100 qualify for free standard shipping."
},
{
id: "4", title: "Do you offer international shipping?", content: "Yes, we ship to most countries worldwide. International shipping costs vary by location and are calculated at checkout. Customs duties may apply depending on your country."
},
{
id: "5", title: "Is my personal information secure?", content: "We use industry-standard SSL encryption to protect all personal and payment information. Your data is never shared with third parties. We comply with all privacy regulations."
},
{
id: "6", title: "What payment methods do you accept?", content: "We accept all major credit cards, debit cards, PayPal, Apple Pay, and Google Pay. All transactions are secure and encrypted."
}
id: "3", title: "Can I cancel anytime?", content: "Yes, you can cancel your subscription at any time. No long-term contracts or hidden fees."},
]}
imageSrc="http://img.b2bpic.net/free-photo/woman-sitting-wheelchair-modern-concept_23-2148497283.jpg"
imageAlt="Customer service support team"
imageSrc="/placeholders/placeholder1.webp"
imageAlt="FAQ"
mediaAnimation="slide-up"
mediaPosition="right"
title="Frequently Asked Questions"
description="Find answers to common questions"
textboxLayout="default"
faqsAnimation="slide-up"
mediaPosition="left"
animationType="smooth"
useInvertedBackground={false}
/>
</div>
<div id="contact" data-section="contact">
<ContactCTA
tag="Get In Touch"
tagIcon={Mail}
tagAnimation="slide-up"
title="Ready to Discover Premium Products?"
description="Have questions about our products or services? Our expert team is here to help. Contact us today and experience the ZSMX Store difference."
tag="Get in Touch"
title="Ready to Get Started?"
description="Contact us today to learn how we can help you achieve your goals."
buttons={[
{ text: "Contact Our Team", href: "#contact" },
{ text: "Shop Now", href: "fashion" }
{ text: "Contact Us", href: "#" },
{ text: "Schedule Demo", href: "#" },
]}
buttonAnimation="slide-up"
background={{ variant: "plain" }}
background={{ variant: "radial-gradient" }}
useInvertedBackground={false}
/>
</div>
<div id="footer" data-section="footer">
<FooterBase
logoText="ZSMX Store"
copyrightText="© 2025 ZSMX Store. All rights reserved."
columns={[
{
title: "Shop", items: [
{ label: "Fashion", href: "fashion" },
{ label: "Home", href: "home-category" },
{ label: "Gym", href: "gym" },
{ label: "Electronics", href: "electronics" }
]
},
{
title: "Support", items: [
{ label: "Contact Us", href: "#contact" },
{ label: "FAQ", href: "#faq" },
{ label: "Shipping Info", href: "#" },
{ label: "Returns", href: "#" }
]
title: "Product", items: [
{ label: "Features", href: "categories" },
{ label: "Pricing", href: "#" },
{ label: "Security", href: "#" },
],
},
{
title: "Company", items: [
{ label: "About Us", href: "#about" },
{ label: "About", href: "about" },
{ label: "Blog", href: "#" },
{ label: "Careers", href: "#" },
{ label: "Privacy Policy", href: "#" }
]
}
],
},
{
title: "Legal", items: [
{ label: "Privacy", href: "#" },
{ label: "Terms", href: "#" },
{ label: "Contact", href: "contact" },
],
},
]}
logoText="Webild"
copyrightText="© 2025 | Webild"
/>
</div>
</ThemeProvider>

View File

@@ -1,39 +1,16 @@
import React, { useRef, useCallback } from 'react';
import { useCardAnimation } from './hooks/useCardAnimation';
import type { CardAnimationConfig } from './types';
import React from 'react';
interface CardListProps {
cards: React.ReactNode[];
animationConfig: CardAnimationConfig;
className?: string;
export interface CardListProps {
children?: React.ReactNode;
[key: string]: any;
}
export const CardList: React.FC<CardListProps> = ({
cards,
animationConfig,
className = '',
}) => {
const cardsRef = useRef<HTMLDivElement[]>([]);
useCardAnimation(cardsRef, animationConfig);
const setCardRef = useCallback((index: number, el: HTMLDivElement | null) => {
if (el) {
cardsRef.current[index] = el;
}
}, []);
export const CardList: React.FC<CardListProps> = ({ children, ...props }) => {
return (
<div className={`card-list ${className}`}>
{cards.map((card, index) => (
<div
key={index}
ref={el => setCardRef(index, el)}
className="card-item"
>
{card}
</div>
))}
<div {...props}>
{children}
</div>
);
};
export default CardList;

View File

@@ -1,42 +1,20 @@
'use client';
import React from 'react';
import React, { useState } from 'react';
import TimelineBase from './layouts/timelines/TimelineBase';
interface TimelineItem {
id: string;
title: string;
description: string;
icon?: React.ReactNode;
export interface CardStackProps {
children?: React.ReactNode;
items?: any[];
[key: string]: any;
}
interface CardStackProps {
items: TimelineItem[];
className?: string;
itemClassName?: string;
connectorClassName?: string;
contentClassName?: string;
ariaLabel?: string;
}
const CardStack: React.FC<CardStackProps> = ({
items,
className = '',
itemClassName = '',
connectorClassName = '',
contentClassName = '',
ariaLabel = 'Card stack',
}) => {
export const CardStack: React.FC<CardStackProps> = ({ children, items, ...props }) => {
return (
<div className={`space-y-8 ${className}`} aria-label={ariaLabel}>
<TimelineBase
items={items}
itemClassName={itemClassName}
connectorClassName={connectorClassName}
contentClassName={contentClassName}
/>
<div {...props}>
{children}
{items && items.map((item: any, idx: number) => (
<div key={idx}>{JSON.stringify(item)}</div>
))}
</div>
);
};
export default CardStack;
export default CardStack;

View File

@@ -1,92 +1,16 @@
"use client";
import React from 'react';
import { memo, useMemo } from "react";
import TextBox from "@/components/Textbox";
import { cls } from "@/lib/utils";
import type { TextBoxProps } from "./types";
export interface CardStackTextBoxProps {
children?: React.ReactNode;
[key: string]: any;
}
const CardStackTextBox = ({
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout,
useInvertedBackground,
textBoxClassName = "",
titleClassName = "",
titleImageWrapperClassName = "",
titleImageClassName = "",
descriptionClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
}: TextBoxProps) => {
const styles = useMemo(() => {
if (textboxLayout === "default") {
return {
className: cls("flex flex-col gap-3 md:gap-2", textBoxClassName),
titleClassName: cls("text-6xl font-medium text-center", titleClassName),
descriptionClassName: cls("text-lg leading-tight text-center md:max-w-6/10", descriptionClassName),
tagClassName: cls("w-fit px-3 py-1 text-sm rounded-theme card text-foreground inline-flex items-center gap-2 mb-0 mx-auto", tagClassName),
buttonContainerClassName: cls("flex flex-wrap gap-4 max-md:justify-center mt-1 md:mt-3 justify-center", buttonContainerClassName),
center: true,
};
}
if (textboxLayout === "inline-image") {
return {
className: cls("flex flex-col gap-3 md:gap-2", textBoxClassName),
titleClassName: cls("text-4xl md:text-5xl font-medium text-center", titleClassName),
descriptionClassName: cls("text-lg leading-tight text-center", descriptionClassName),
tagClassName: cls("w-fit px-3 py-1 text-sm rounded-theme card text-foreground inline-flex items-center gap-2 mb-0 mx-auto", tagClassName),
buttonContainerClassName: cls("flex flex-wrap gap-4 max-md:justify-center mt-1 md:mt-3 justify-center", buttonContainerClassName),
center: true,
};
}
return {
className: textBoxClassName,
titleClassName: cls("text-6xl font-medium", titleClassName),
descriptionClassName: cls("text-lg leading-tight", descriptionClassName),
tagClassName: cls("px-3 py-1 text-sm rounded-theme card text-foreground inline-flex items-center gap-2", tagClassName),
buttonContainerClassName: cls("flex flex-wrap gap-4 max-md:justify-center", buttonContainerClassName),
center: false,
};
}, [textboxLayout, textBoxClassName, titleClassName, descriptionClassName, tagClassName, buttonContainerClassName]);
if (!title && !titleSegments && !description) return null;
return (
<TextBox
title={title!}
titleSegments={titleSegments}
description={description!}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
className={styles.className}
titleClassName={styles.titleClassName}
titleImageWrapperClassName={titleImageWrapperClassName}
titleImageClassName={titleImageClassName}
descriptionClassName={styles.descriptionClassName}
tagClassName={styles.tagClassName}
buttonContainerClassName={styles.buttonContainerClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
center={styles.center}
/>
);
export const CardStackTextBox: React.FC<CardStackTextBoxProps> = ({ children, ...props }) => {
return (
<div {...props}>
{children}
</div>
);
};
CardStackTextBox.displayName = "CardStackTextBox";
export default memo(CardStackTextBox);
export default CardStackTextBox;

View File

@@ -1,144 +1,16 @@
"use client";
import React from 'react';
import { memo, Children, useCallback, useEffect, useState } from "react";
import useEmblaCarousel from "embla-carousel-react";
import { EmblaCarouselType } from "embla-carousel";
import CardStackTextBox from "../../CardStackTextBox";
import { cls } from "@/lib/utils";
import { ChevronLeft, ChevronRight } from "lucide-react";
import { ArrowCarouselProps } from "../../types";
export interface ArrowCarouselProps {
children?: React.ReactNode;
[key: string]: any;
}
const ArrowCarousel = ({
children,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout = "default",
useInvertedBackground,
className = "",
containerClassName = "",
carouselClassName = "",
controlsClassName = "",
textBoxClassName = "",
titleClassName = "",
titleImageWrapperClassName = "",
titleImageClassName = "",
descriptionClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
ariaLabel = "Carousel section",
}: ArrowCarouselProps) => {
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, align: "center" });
const [selectedIndex, setSelectedIndex] = useState(0);
const childrenArray = Children.toArray(children);
const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
setSelectedIndex(emblaApi.selectedScrollSnap());
}, []);
const scrollPrev = useCallback(() => emblaApi?.scrollPrev(), [emblaApi]);
const scrollNext = useCallback(() => emblaApi?.scrollNext(), [emblaApi]);
useEffect(() => {
if (!emblaApi) return;
onSelect(emblaApi);
emblaApi.on("select", onSelect).on("reInit", onSelect);
return () => {
emblaApi.off("select", onSelect).off("reInit", onSelect);
};
}, [emblaApi, onSelect]);
return (
<section
className={cls(
"relative py-20 w-full",
useInvertedBackground && "bg-foreground",
className
)}
aria-label={ariaLabel}
>
<div className={cls("w-full mx-auto flex flex-col gap-6", containerClassName)}>
<div className="w-content-width mx-auto">
<CardStackTextBox
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
textBoxClassName={textBoxClassName}
titleClassName={titleClassName}
titleImageWrapperClassName={titleImageWrapperClassName}
titleImageClassName={titleImageClassName}
descriptionClassName={descriptionClassName}
tagClassName={tagClassName}
buttonContainerClassName={buttonContainerClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
/>
</div>
<div className="relative w-full">
<div
className={cls(
"overflow-hidden w-full relative z-10 mask-fade-x",
carouselClassName
)}
ref={emblaRef}
>
<div className="flex w-full">
{childrenArray.map((child, index) => (
<div
key={index}
className="flex-none w-60 md:w-40 mr-6"
>
<div className={cls(
"transition-all duration-500 ease-out",
selectedIndex === index ? "opacity-100 scale-100" : "opacity-70 scale-90"
)}>
{child}
</div>
</div>
))}
</div>
</div>
<div className={cls("absolute inset-y-0 w-content-width mx-auto left-0 right-0 flex items-center justify-between pointer-events-none z-10", controlsClassName)}>
<button
onClick={scrollPrev}
className="pointer-events-auto primary-button h-8 w-auto aspect-square rounded-theme flex items-center justify-center cursor-pointer"
aria-label="Previous slide"
>
<ChevronLeft className="w-4/10 h-4/10 text-primary-cta-text" />
</button>
<button
onClick={scrollNext}
className="pointer-events-auto primary-button h-8 w-auto aspect-square rounded-theme flex items-center justify-center cursor-pointer"
aria-label="Next slide"
>
<ChevronRight className="w-4/10 h-4/10 text-primary-cta-text" />
</button>
</div>
</div>
</div>
</section>
);
export const ArrowCarousel: React.FC<ArrowCarouselProps> = ({ children, ...props }) => {
return (
<div {...props}>
{children}
</div>
);
};
ArrowCarousel.displayName = "ArrowCarousel";
export default memo(ArrowCarousel);
export default ArrowCarousel;

View File

@@ -1,39 +1,16 @@
import React, { useRef, useCallback } from 'react';
import { useCardAnimation } from '../../hooks/useCardAnimation';
import type { CardAnimationConfig } from '../../types';
import React from 'react';
interface AutoCarouselProps {
items: React.ReactNode[];
animationConfig: CardAnimationConfig;
className?: string;
export interface AutoCarouselProps {
children?: React.ReactNode;
[key: string]: any;
}
export const AutoCarousel: React.FC<AutoCarouselProps> = ({
items,
animationConfig,
className = '',
}) => {
const cardsRef = useRef<HTMLDivElement[]>([]);
useCardAnimation(cardsRef, animationConfig);
const setCardRef = useCallback((index: number, el: HTMLDivElement | null) => {
if (el) {
cardsRef.current[index] = el;
}
}, []);
export const AutoCarousel: React.FC<AutoCarouselProps> = ({ children, ...props }) => {
return (
<div className={`auto-carousel ${className}`}>
{items.map((item, index) => (
<div
key={index}
ref={el => setCardRef(index, el)}
className="carousel-item"
>
{item}
</div>
))}
<div {...props}>
{children}
</div>
);
};
export default AutoCarousel;

View File

@@ -1,155 +1,16 @@
"use client";
import React from 'react';
import { memo, Children, cloneElement, isValidElement, useCallback, useEffect, useState } from "react";
import useEmblaCarousel from "embla-carousel-react";
import { EmblaCarouselType } from "embla-carousel";
import CardStackTextBox from "../../CardStackTextBox";
import { cls } from "@/lib/utils";
import { FullWidthCarouselProps } from "../../types";
export interface FullWidthCarouselProps {
children?: React.ReactNode;
[key: string]: any;
}
const FullWidthCarousel = ({
children,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout = "default",
useInvertedBackground,
className = "",
containerClassName = "",
carouselClassName = "",
dotsClassName = "",
textBoxClassName = "",
titleClassName = "",
titleImageWrapperClassName = "",
titleImageClassName = "",
descriptionClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
ariaLabel = "Carousel section",
}: FullWidthCarouselProps) => {
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true, align: "center" });
const [selectedIndex, setSelectedIndex] = useState(0);
const childrenArray = Children.toArray(children);
const onSelect = useCallback((emblaApi: EmblaCarouselType) => {
setSelectedIndex(emblaApi.selectedScrollSnap());
}, []);
const scrollTo = useCallback(
(index: number) => {
if (!emblaApi) return;
emblaApi.scrollTo(index);
},
[emblaApi]
);
useEffect(() => {
if (!emblaApi) return;
onSelect(emblaApi);
emblaApi.on("select", onSelect).on("reInit", onSelect);
return () => {
emblaApi.off("select", onSelect).off("reInit", onSelect);
};
}, [emblaApi, onSelect]);
useEffect(() => {
if (!emblaApi) return;
const autoplay = setInterval(() => {
emblaApi.scrollNext();
}, 5000);
return () => clearInterval(autoplay);
}, [emblaApi]);
return (
<section
className={cls(
"relative py-20 w-full",
useInvertedBackground && "bg-foreground",
className
)}
aria-label={ariaLabel}
>
<div className={cls("w-full mx-auto flex flex-col gap-6", containerClassName)}>
<div className="w-content-width mx-auto">
<CardStackTextBox
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
textBoxClassName={textBoxClassName}
titleClassName={titleClassName}
titleImageWrapperClassName={titleImageWrapperClassName}
titleImageClassName={titleImageClassName}
descriptionClassName={descriptionClassName}
tagClassName={tagClassName}
buttonContainerClassName={buttonContainerClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
/>
</div>
<div className="w-full">
<div
className={cls(
"overflow-hidden w-full relative z-10",
carouselClassName
)}
ref={emblaRef}
>
<div className="flex w-full">
{Children.map(childrenArray, (child, index) => (
<div
key={index}
className="flex-none w-70 mr-6"
>
{isValidElement(child)
? cloneElement(child, { isActive: selectedIndex === index } as Record<string, unknown>)
: child}
</div>
))}
</div>
</div>
</div>
<div className={cls("flex items-center justify-center gap-2", dotsClassName)}>
{childrenArray.map((_, index) => (
<button
key={index}
type="button"
onClick={() => scrollTo(index)}
className={cls(
"relative cursor-pointer h-2 rounded-theme bg-accent transition-all duration-300",
selectedIndex === index
? "w-8 opacity-100"
: "w-2 opacity-20"
)}
aria-label={`Go to slide ${index + 1}`}
aria-current={selectedIndex === index}
/>
))}
</div>
</div>
</section>
);
export const FullWidthCarousel: React.FC<FullWidthCarouselProps> = ({ children, ...props }) => {
return (
<div {...props}>
{children}
</div>
);
};
FullWidthCarousel.displayName = "FullWidthCarousel";
export default memo(FullWidthCarousel);
export default FullWidthCarousel;

View File

@@ -1,147 +1,16 @@
"use client";
import React from 'react';
import React, { useEffect, useRef, memo, Children } from "react";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
import CardStackTextBox from "../../CardStackTextBox";
import { cls } from "@/lib/utils";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, ButtonAnimationType, TitleSegment } from "../../types";
import type { TextboxLayout, InvertedBackground } from "@/providers/themeProvider/config/constants";
gsap.registerPlugin(ScrollTrigger);
interface TimelineCardStackProps {
children: React.ReactNode;
title: string;
titleSegments?: TitleSegment[];
description: string;
tag?: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
buttons?: ButtonConfig[];
buttonAnimation?: ButtonAnimationType;
textboxLayout: TextboxLayout;
useInvertedBackground?: InvertedBackground;
className?: string;
containerClassName?: string;
textBoxClassName?: string;
titleClassName?: string;
titleImageWrapperClassName?: string;
titleImageClassName?: string;
descriptionClassName?: string;
tagClassName?: string;
buttonContainerClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
ariaLabel?: string;
export interface TimelineCardStackProps {
children?: React.ReactNode;
[key: string]: any;
}
const TimelineCardStack = ({
children,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout,
useInvertedBackground,
className = "",
containerClassName = "",
textBoxClassName = "",
titleClassName = "",
titleImageWrapperClassName = "",
titleImageClassName = "",
descriptionClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
ariaLabel = "Timeline section",
}: TimelineCardStackProps) => {
const itemRefs = useRef<(HTMLDivElement | null)[]>([]);
const childrenArray = Children.toArray(children);
useEffect(() => {
const ctx = gsap.context(() => {
itemRefs.current.forEach((ref, position) => {
if (!ref) return;
const isLast = position === itemRefs.current.length - 1;
const timeline = gsap.timeline({
scrollTrigger: {
trigger: ref,
start: "center center",
end: "+=100%",
scrub: true,
},
});
timeline.set(ref, { willChange: "opacity" }).to(ref, {
ease: "none",
opacity: isLast ? 1 : 0,
});
});
});
return () => {
ctx.revert();
};
}, [childrenArray.length]);
return (
<section
className={cls(
"relative overflow-visible h-fit py-20 w-full",
useInvertedBackground && "bg-foreground",
className
)}
aria-label={ariaLabel}
>
<div className={cls("w-content-width mx-auto flex flex-col gap-6", containerClassName)}>
<CardStackTextBox
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
textBoxClassName={textBoxClassName}
titleClassName={titleClassName}
titleImageWrapperClassName={titleImageWrapperClassName}
titleImageClassName={titleImageClassName}
descriptionClassName={descriptionClassName}
tagClassName={tagClassName}
buttonContainerClassName={buttonContainerClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
/>
<div className="w-full flex flex-col gap-[var(--width-25)] md:gap-[6.25vh]">
{Children.map(childrenArray, (child, index) => (
<div
key={index}
ref={(el) => {
itemRefs.current[index] = el;
}}
className="!sticky w-full card backdrop-blur-xs rounded-theme-capped h-[140vw] md:h-[75vh] top-[25vw] md:top-[12.5vh]"
>
{child}
</div>
))}
</div>
</div>
</section>
);
export const TimelineCardStack: React.FC<TimelineCardStackProps> = ({ children, ...props }) => {
return (
<div {...props}>
{children}
</div>
);
};
TimelineCardStack.displayName = "TimelineCardStack";
export default memo(TimelineCardStack);
export default TimelineCardStack;

View File

@@ -1,175 +1,16 @@
"use client";
import React from 'react';
import React, { Children, useCallback } from "react";
import { cls } from "@/lib/utils";
import CardStackTextBox from "../../CardStackTextBox";
import { useTimelineHorizontal, type MediaItem } from "../../hooks/useTimelineHorizontal";
import MediaContent from "@/components/shared/MediaContent";
import type { LucideIcon } from "lucide-react";
import type { ButtonConfig, ButtonAnimationType, TitleSegment, TextboxLayout, InvertedBackground } from "../../types";
interface TimelineHorizontalCardStackProps {
children: React.ReactNode;
title: string;
titleSegments?: TitleSegment[];
description: string;
tag?: string;
tagIcon?: LucideIcon;
tagAnimation?: ButtonAnimationType;
buttons?: ButtonConfig[];
buttonAnimation?: ButtonAnimationType;
textboxLayout: TextboxLayout;
useInvertedBackground?: InvertedBackground;
mediaItems?: MediaItem[];
className?: string;
containerClassName?: string;
textBoxClassName?: string;
titleClassName?: string;
titleImageWrapperClassName?: string;
titleImageClassName?: string;
descriptionClassName?: string;
tagClassName?: string;
buttonContainerClassName?: string;
buttonClassName?: string;
buttonTextClassName?: string;
cardClassName?: string;
progressBarClassName?: string;
mediaContainerClassName?: string;
mediaClassName?: string;
ariaLabel?: string;
export interface TimelineHorizontalCardStackProps {
children?: React.ReactNode;
[key: string]: any;
}
const TimelineHorizontalCardStack = ({
children,
title,
titleSegments,
description,
tag,
tagIcon,
tagAnimation,
buttons,
buttonAnimation,
textboxLayout,
useInvertedBackground,
mediaItems,
className = "",
containerClassName = "",
textBoxClassName = "",
titleClassName = "",
titleImageWrapperClassName = "",
titleImageClassName = "",
descriptionClassName = "",
tagClassName = "",
buttonContainerClassName = "",
buttonClassName = "",
buttonTextClassName = "",
cardClassName = "",
progressBarClassName = "",
mediaContainerClassName = "",
mediaClassName = "",
ariaLabel = "Timeline section",
}: TimelineHorizontalCardStackProps) => {
const childrenArray = Children.toArray(children);
const itemCount = childrenArray.length;
const { activeIndex, progressRefs, handleItemClick, imageOpacity, currentMediaSrc } = useTimelineHorizontal({
itemCount,
mediaItems,
});
const getGridColumns = useCallback(() => {
if (itemCount === 2) return "md:grid-cols-2";
if (itemCount === 3) return "md:grid-cols-3";
return "md:grid-cols-4";
}, [itemCount]);
const getItemOpacity = useCallback(
(index: number) => {
return index <= activeIndex ? "opacity-100" : "opacity-50";
},
[activeIndex]
);
export const TimelineHorizontalCardStack: React.FC<TimelineHorizontalCardStackProps> = ({ children, ...props }) => {
return (
<section
className={cls(
"relative py-20 w-full",
useInvertedBackground && "bg-foreground",
className
)}
aria-label={ariaLabel}
>
<div className={cls("w-content-width mx-auto flex flex-col gap-6", containerClassName)}>
<CardStackTextBox
title={title}
titleSegments={titleSegments}
description={description}
tag={tag}
tagIcon={tagIcon}
tagAnimation={tagAnimation}
buttons={buttons}
buttonAnimation={buttonAnimation}
textboxLayout={textboxLayout}
useInvertedBackground={useInvertedBackground}
textBoxClassName={textBoxClassName}
titleClassName={titleClassName}
titleImageWrapperClassName={titleImageWrapperClassName}
titleImageClassName={titleImageClassName}
descriptionClassName={descriptionClassName}
tagClassName={tagClassName}
buttonContainerClassName={buttonContainerClassName}
buttonClassName={buttonClassName}
buttonTextClassName={buttonTextClassName}
/>
{mediaItems && mediaItems.length > 0 && (
<div className={cls("relative card rounded-theme-capped overflow-hidden aspect-square md:aspect-[17/9]", mediaContainerClassName)}>
<div
className="absolute inset-6 z-1 transition-opacity duration-300 overflow-hidden"
style={{ opacity: imageOpacity }}
>
<MediaContent
imageSrc={currentMediaSrc.imageSrc}
videoSrc={currentMediaSrc.videoSrc}
imageAlt={mediaItems[activeIndex]?.imageAlt}
videoAriaLabel={mediaItems[activeIndex]?.videoAriaLabel}
imageClassName={cls("w-full h-full object-cover", mediaClassName)}
/>
</div>
</div>
)}
<div className={cls("relative grid grid-cols-1 gap-6 md:gap-6", getGridColumns())}>
{Children.map(childrenArray, (child, index) => (
<div
key={index}
className={cls(
"card rounded-theme-capped p-6 flex flex-col justify-between gap-6 transition-all duration-300",
index === activeIndex ? "cursor-default" : "cursor-pointer hover:shadow-lg",
getItemOpacity(index),
cardClassName
)}
onClick={() => handleItemClick(index)}
>
{child}
<div className="relative w-full h-px overflow-hidden">
<div className="absolute z-0 w-full h-full bg-foreground/20" />
<div
ref={(el) => {
if (el !== null) {
progressRefs.current[index] = el;
}
}}
className={cls("absolute z-10 h-full w-full bg-foreground origin-left", progressBarClassName)}
style={{ transform: "scaleX(0)" }}
/>
</div>
</div>
))}
</div>
</div>
</section>
<div {...props}>
{children}
</div>
);
};
TimelineHorizontalCardStack.displayName = "TimelineHorizontalCardStack";
export default React.memo(TimelineHorizontalCardStack);
export default TimelineHorizontalCardStack;

View File

@@ -1,39 +1,16 @@
import React, { useRef, useCallback } from 'react';
import { useCardAnimation } from '../../hooks/useCardAnimation';
import type { CardAnimationConfig } from '../../types';
import React from 'react';
interface TimelinePhoneViewProps {
items: React.ReactNode[];
animationConfig: CardAnimationConfig;
className?: string;
export interface TimelinePhoneViewProps {
children?: React.ReactNode;
[key: string]: any;
}
export const TimelinePhoneView: React.FC<TimelinePhoneViewProps> = ({
items,
animationConfig,
className = '',
}) => {
const cardsRef = useRef<HTMLDivElement[]>([]);
useCardAnimation(cardsRef, animationConfig);
const setCardRef = useCallback((index: number, el: HTMLDivElement | null) => {
if (el) {
cardsRef.current[index] = el;
}
}, []);
export const TimelinePhoneView: React.FC<TimelinePhoneViewProps> = ({ children, ...props }) => {
return (
<div className={`timeline-phone-view ${className}`}>
{items.map((item, index) => (
<div
key={index}
ref={el => setCardRef(index, el)}
className="timeline-item"
>
{item}
</div>
))}
<div {...props}>
{children}
</div>
);
};
export default TimelinePhoneView;

View File

@@ -1,39 +1,16 @@
import React, { useRef, useCallback } from 'react';
import { useCardAnimation } from '../../hooks/useCardAnimation';
import type { CardAnimationConfig } from '../../types';
import React from 'react';
interface TimelineProcessFlowProps {
items: React.ReactNode[];
animationConfig: CardAnimationConfig;
className?: string;
export interface TimelineProcessFlowProps {
children?: React.ReactNode;
[key: string]: any;
}
export const TimelineProcessFlow: React.FC<TimelineProcessFlowProps> = ({
items,
animationConfig,
className = '',
}) => {
const cardsRef = useRef<HTMLDivElement[]>([]);
useCardAnimation(cardsRef, animationConfig);
const setCardRef = useCallback((index: number, el: HTMLDivElement | null) => {
if (el) {
cardsRef.current[index] = el;
}
}, []);
export const TimelineProcessFlow: React.FC<TimelineProcessFlowProps> = ({ children, ...props }) => {
return (
<div className={`timeline-process-flow ${className}`}>
{items.map((item, index) => (
<div
key={index}
ref={el => setCardRef(index, el)}
className="process-item"
>
{item}
</div>
))}
<div {...props}>
{children}
</div>
);
};
export default TimelineProcessFlow;

View File

@@ -10,6 +10,15 @@ export type CardAnimationTypeWith3D = CardAnimationType | 'depth-3d';
export type BentoAnimationType = CardAnimationType;
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';
export type TextBoxProps = any;
export type ArrowCarouselProps = any;
export type FullWidthCarouselProps = any;
export type ButtonConfig = any;
export type ButtonAnimationType = any;
export type TitleSegment = any;
export type TextboxLayout = any;
export type InvertedBackground = any;
export interface MetricCardOneGridVariant extends GridVariant {}
export interface MetricCardTwoGridVariant extends GridVariant {}
export interface TeamCardOneGridVariant extends GridVariant {}

View File

@@ -1,34 +1,61 @@
import React, { useRef, useCallback } from 'react';
import { useCardAnimation } from '@/components/cardStack/hooks/useCardAnimation';
import type { CardAnimationConfig } from '@/components/cardStack/types';
import React from 'react';
import { LucideIcon } from 'lucide-react';
interface DashboardProps {
animationConfig: CardAnimationConfig;
className?: string;
export interface DashboardStat {
title: string;
values: number[];
valuePrefix?: string;
valueSuffix?: string;
description?: string;
}
export const Dashboard: React.FC<DashboardProps> = ({
animationConfig,
className = '',
}) => {
const cardsRef = useRef<HTMLDivElement[]>([]);
export interface DashboardSidebarItem {
icon: LucideIcon;
active?: boolean;
[key: string]: any;
}
useCardAnimation(cardsRef, animationConfig);
export interface DashboardListItem {
icon: LucideIcon;
title: string;
status: string;
[key: string]: any;
}
const setCardRef = useCallback((index: number, el: HTMLDivElement | null) => {
if (el) {
cardsRef.current[index] = el;
}
}, []);
export interface ChartDataItem {
value: number;
[key: string]: any;
}
export interface DashboardProps {
title: string;
stats: [DashboardStat, DashboardStat, DashboardStat];
logoIcon: LucideIcon;
sidebarItems: DashboardSidebarItem[];
buttons: any[];
listItems: DashboardListItem[];
imageSrc: string;
searchPlaceholder?: string;
chartTitle?: string;
chartData?: ChartDataItem[];
listTitle?: string;
videoSrc?: string;
imageAlt?: string;
videoAriaLabel?: string;
className?: string;
containerClassName?: string;
sidebarClassName?: string;
statClassName?: string;
chartClassName?: string;
listClassName?: string;
animationConfig?: any;
[key: string]: any;
}
export const Dashboard: React.FC<DashboardProps> = (props) => {
return (
<div className={`dashboard ${className}`}>
<div
ref={el => setCardRef(0, el)}
className="dashboard-item"
>
<p>Dashboard content</p>
</div>
<div className={props.className}>
<h2>{props.title}</h2>
</div>
);
};