174 lines
5.3 KiB
TypeScript
174 lines
5.3 KiB
TypeScript
"use client";
|
|
|
|
import { motion } from "framer-motion";
|
|
import { Check, ChevronLeft, ChevronRight, Circle } from "lucide-react";
|
|
import React, { useState } from "react";
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
interface TimelineSectionProps {
|
|
className?: string;
|
|
}
|
|
|
|
interface StepProps {
|
|
title: string;
|
|
imageSrc: string;
|
|
description: string;
|
|
}
|
|
|
|
const steps: StepProps[] = [
|
|
{
|
|
title: "Introduction",
|
|
imageSrc: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-1.svg",
|
|
description:
|
|
"Welcome to our step-by-step guide. This introduction will help you get started with the process and understand what to expect.",
|
|
},
|
|
{
|
|
title: "Personal Information",
|
|
imageSrc: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-2.svg",
|
|
description:
|
|
"In this section, we'll collect some basic information about you to personalize your experience and provide better recommendations.",
|
|
},
|
|
{
|
|
title: "Address Details",
|
|
imageSrc: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-3.svg",
|
|
description:
|
|
"Please provide your address details so we can deliver your products to the right location and calculate shipping costs.",
|
|
},
|
|
{
|
|
title: "Review & Submit",
|
|
imageSrc: "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-4.svg",
|
|
description:
|
|
"Take a moment to review all the information you've provided before finalizing your submission. You can go back to any step.",
|
|
},
|
|
];
|
|
|
|
const ProcessBar: React.FC<{
|
|
currentStep: number;
|
|
steps: StepProps[];
|
|
}> = ({ currentStep, steps }) => (
|
|
<div className="relative w-full scale-75">
|
|
<div className="flex items-center justify-between">
|
|
{steps.map((step, index) => (
|
|
<React.Fragment key={step.title}>
|
|
<div className="flex flex-col items-center">
|
|
<motion.div
|
|
className={cn(
|
|
"z-10 flex h-8 w-8 items-center justify-center rounded-full",
|
|
index <= currentStep
|
|
? "bg-primary text-primary-foreground"
|
|
: "bg-muted text-muted-foreground"
|
|
)}
|
|
animate={{ scale: 1.02 }}
|
|
>
|
|
{index < currentStep ? (
|
|
<Check size={17} />
|
|
) : (
|
|
<Circle size={17} className="text-current" />
|
|
)}
|
|
</motion.div>
|
|
</div>
|
|
{index < steps.length - 1 && (
|
|
<div className="relative grow">
|
|
<div className="absolute -top-1 h-1.5 w-full bg-muted" />
|
|
<motion.div
|
|
className="absolute -top-1 h-1.5 w-full bg-primary"
|
|
initial={{ width: "0%" }}
|
|
animate={{
|
|
width: index < currentStep ? "100%" : "0%",
|
|
}}
|
|
transition={{ duration: 0.5, ease: "easeInOut" }}
|
|
/>
|
|
</div>
|
|
)}
|
|
</React.Fragment>
|
|
))}
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const StepperContent: React.FC<{ step: StepProps }> = ({ step }) => {
|
|
return (
|
|
<div className="my-4 flex min-h-[400px] w-full flex-col items-center justify-center overflow-hidden rounded-2xl bg-muted p-6 text-center">
|
|
<motion.div
|
|
key={step.imageSrc}
|
|
initial={{ opacity: 0, y: -10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3 }}
|
|
className="flex h-[200px] w-[500px] items-center justify-center overflow-hidden"
|
|
>
|
|
<img
|
|
src={step.imageSrc}
|
|
alt={step.title}
|
|
className="w-[500px]"
|
|
onError={(e) => {
|
|
e.currentTarget.src = "https://deifkwefumgah.cloudfront.net/shadcnblocks/block/placeholder-1.svg";
|
|
}}
|
|
/>
|
|
</motion.div>
|
|
<p className="text-body mt-6 px-10 leading-snug tracking-tight text-muted-foreground">
|
|
{step.description}
|
|
</p>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const NavButtons: React.FC<{
|
|
currentStep: number;
|
|
totalSteps: number;
|
|
handlePrev: () => void;
|
|
handleNext: () => void;
|
|
}> = ({ handlePrev, handleNext }) => (
|
|
<div className="flex w-full justify-end gap-3 tracking-tight">
|
|
<Button
|
|
onClick={handlePrev}
|
|
variant="outline"
|
|
>
|
|
<ChevronLeft size={20} />
|
|
Previous
|
|
</Button>
|
|
|
|
<Button
|
|
onClick={handleNext}
|
|
variant="outline"
|
|
>
|
|
Next
|
|
<ChevronRight size={20} />
|
|
</Button>
|
|
</div>
|
|
);
|
|
|
|
const TimelineSection: React.FC<TimelineSectionProps> = ({ className }) => {
|
|
const [currentStep, setCurrentStep] = useState(1);
|
|
|
|
const handleNext = () => {
|
|
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1));
|
|
};
|
|
|
|
const handlePrev = () => {
|
|
setCurrentStep((prev) => Math.max(prev - 1, 0));
|
|
};
|
|
|
|
return (
|
|
<section className={cn("py-32", className)}>
|
|
<div className="container mx-auto flex max-w-2xl flex-col items-center gap-10 md:gap-0">
|
|
<h2 className="text-heading-2 mb-10 text-center font-semibold tracking-tight text-foreground">
|
|
Stepper
|
|
</h2>
|
|
<ProcessBar currentStep={currentStep} steps={steps} />
|
|
<StepperContent step={steps[currentStep]} />
|
|
<NavButtons
|
|
handlePrev={handlePrev}
|
|
handleNext={handleNext}
|
|
currentStep={currentStep}
|
|
totalSteps={steps.length}
|
|
/>
|
|
</div>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export { TimelineSection };
|