← back to IWasCute
src/components/licensor/Stepper.tsx
140 lines
'use client'
import { motion } from 'framer-motion'
import { Check } from 'lucide-react'
import clsx from 'clsx'
const STEPS = [
{ label: 'Upload Photos', shortLabel: 'Upload' },
{ label: 'Copyright Info', shortLabel: 'Copyright' },
{ label: 'Likeness Release', shortLabel: 'Release' },
{ label: 'Review & Submit', shortLabel: 'Review' },
]
interface StepperProps {
currentStep: number
}
export default function Stepper({ currentStep }: StepperProps) {
return (
<div className="w-full px-4 py-6 md:px-8 md:py-8">
<div className="max-w-2xl mx-auto">
<div className="flex items-center justify-between relative">
{/* Connecting lines (behind circles) */}
<div
className="absolute top-5 left-0 right-0 h-0.5 mx-12 md:mx-16"
style={{ background: 'var(--color-cream-dark)' }}
/>
{/* Animated filled progress line */}
<div
className="absolute top-5 left-0 h-0.5 mx-12 md:mx-16 transition-all duration-700 ease-in-out"
style={{
background: 'var(--color-sage)',
right: 0,
width: `calc(${(currentStep / (STEPS.length - 1)) * 100}% - 6rem)`,
}}
/>
{STEPS.map((step, index) => {
const isCompleted = index < currentStep
const isActive = index === currentStep
const isUpcoming = index > currentStep
return (
<div
key={step.label}
className="relative flex flex-col items-center gap-2 z-10"
>
{/* Circle */}
<motion.div
initial={false}
animate={{
scale: isActive ? 1.12 : 1,
}}
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
className={clsx(
'w-10 h-10 rounded-full flex items-center justify-center',
'text-sm font-bold transition-colors duration-300',
'shadow-sm',
{
'text-white': isCompleted || isActive,
}
)}
style={{
background: isCompleted
? 'var(--color-sage-dark)'
: isActive
? 'var(--color-peach)'
: 'var(--color-cream)',
border: isUpcoming
? '2px solid var(--color-cream-dark)'
: 'none',
color: isUpcoming
? 'var(--color-warm-gray)'
: 'var(--color-brown)',
}}
aria-current={isActive ? 'step' : undefined}
aria-label={`Step ${index + 1}: ${step.label}${isCompleted ? ' (completed)' : isActive ? ' (current)' : ''}`}
>
{isCompleted ? (
<Check size={16} strokeWidth={3} />
) : (
<span>{index + 1}</span>
)}
</motion.div>
{/* Label */}
<span
className={clsx(
'text-xs font-medium tracking-wide text-center',
'hidden sm:block',
{
'font-semibold': isActive,
}
)}
style={{
color: isActive
? 'var(--color-brown)'
: isCompleted
? 'var(--color-sage-dark)'
: 'var(--color-warm-gray)',
}}
>
{step.label}
</span>
{/* Short label for mobile */}
<span
className={clsx(
'text-xs font-medium tracking-wide text-center',
'sm:hidden',
{
'font-semibold': isActive,
}
)}
style={{
color: isActive
? 'var(--color-brown)'
: isCompleted
? 'var(--color-sage-dark)'
: 'var(--color-warm-gray)',
}}
>
{step.shortLabel}
</span>
</div>
)
})}
</div>
{/* Step counter (mobile) */}
<p
className="mt-4 text-center text-xs sm:hidden"
style={{ color: 'var(--color-warm-gray)' }}
>
Step {currentStep + 1} of {STEPS.length}: {STEPS[currentStep].label}
</p>
</div>
</div>
)
}