Skip to content

Commit f7346d3

Browse files
committed
✨ added a dotted stepper
1 parent bd04247 commit f7346d3

File tree

1 file changed

+141
-0
lines changed

1 file changed

+141
-0
lines changed
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
'use client'
2+
3+
import { motion } from 'framer-motion'
4+
import { CheckCircle, Circle } from 'lucide-react'
5+
import React, { useState } from 'react'
6+
7+
interface FieldProps {
8+
name: string
9+
type: string
10+
placeholder: string
11+
}
12+
13+
interface StepProps {
14+
label: string
15+
fields?: FieldProps[]
16+
}
17+
18+
const steps: StepProps[] = [
19+
{ label: 'Introduction' },
20+
{
21+
label: 'Personal Information',
22+
fields: [
23+
{ name: 'name', type: 'text', placeholder: 'Name' },
24+
{ name: 'email', type: 'email', placeholder: 'Email' },
25+
],
26+
},
27+
{
28+
label: 'Address Details',
29+
fields: [
30+
{ name: 'address', type: 'text', placeholder: 'Address' },
31+
{ name: 'city', type: 'text', placeholder: 'City' },
32+
{ name: 'country', type: 'text', placeholder: 'Country' },
33+
],
34+
},
35+
{ label: 'Review & Submit' },
36+
]
37+
38+
const StepIndicator: React.FC<{
39+
currentStep: number
40+
steps: StepProps[]
41+
totalSteps: number
42+
}> = ({ currentStep, steps, totalSteps }) => (
43+
<div className="relative w-full">
44+
<div className="flex items-center justify-between">
45+
{steps.map((step, index) => (
46+
<React.Fragment key={step.label}>
47+
<div className="flex flex-col items-center">
48+
<motion.div
49+
className={`z-10 flex h-10 w-10 items-center justify-center rounded-full ${
50+
index <= currentStep ? 'bg-red-500 text-white' : 'bg-gray-200'
51+
}`}
52+
initial={false}
53+
animate={{ scale: index === currentStep ? 1.2 : 1 }}
54+
>
55+
{index < currentStep ? (
56+
<CheckCircle size={18} />
57+
) : (
58+
<Circle size={18} fill="currentColor" />
59+
)}
60+
</motion.div>
61+
</div>
62+
{index < steps.length - 1 && (
63+
<div className="relative flex-grow">
64+
<div className="absolute -top-1 h-1.5 w-full bg-gray-100" />
65+
<motion.div
66+
className="absolute -top-1 h-1.5 w-full rounded-full bg-red-500"
67+
initial={{ width: '0%' }}
68+
animate={{
69+
width: index < currentStep ? '100%' : '0%',
70+
}}
71+
transition={{ duration: 0.3 }}
72+
/>
73+
</div>
74+
)}
75+
</React.Fragment>
76+
))}
77+
</div>
78+
</div>
79+
)
80+
81+
const StepContent: React.FC = () => {
82+
return (
83+
<div className="my-4 flex min-h-[30vh] w-full items-center justify-center rounded-lg border bg-gray-100 text-center">
84+
Stepper Content
85+
</div>
86+
)
87+
}
88+
89+
const ButtonClasses =
90+
'rounded-2xl bg-red-500 px-2 py-1 text-sm font-medium text-white'
91+
92+
const NavigationButtons: React.FC<{
93+
currentStep: number
94+
totalSteps: number
95+
handlePrev: () => void
96+
handleNext: () => void
97+
}> = ({ currentStep, totalSteps, handlePrev, handleNext }) => (
98+
<div className="flex justify-end gap-3">
99+
{currentStep === 0 ? null : (
100+
<button onClick={handlePrev} className={ButtonClasses}>
101+
Previous
102+
</button>
103+
)}
104+
{currentStep === totalSteps - 1 ? null : (
105+
<button onClick={handleNext} className={ButtonClasses}>
106+
Next
107+
</button>
108+
)}
109+
</div>
110+
)
111+
112+
const Stepper: React.FC = () => {
113+
const [currentStep, setCurrentStep] = useState(1)
114+
115+
const handleNext = () => {
116+
setCurrentStep((prev) => Math.min(prev + 1, steps.length - 1))
117+
}
118+
119+
const handlePrev = () => {
120+
setCurrentStep((prev) => Math.max(prev - 1, 0))
121+
}
122+
123+
return (
124+
<div className="mx-auto w-full max-w-2xl p-6">
125+
<StepIndicator
126+
currentStep={currentStep}
127+
steps={steps}
128+
totalSteps={steps.length}
129+
/>
130+
<StepContent />
131+
<NavigationButtons
132+
currentStep={currentStep}
133+
totalSteps={steps.length}
134+
handlePrev={handlePrev}
135+
handleNext={handleNext}
136+
/>
137+
</div>
138+
)
139+
}
140+
141+
export default Stepper

0 commit comments

Comments
 (0)