@moontra/moonui-pro 2.6.1 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +304 -20
- package/dist/index.mjs +15467 -2135
- package/package.json +1 -1
- package/src/components/credit-card-input/index.tsx +406 -0
- package/src/components/form-wizard/form-wizard-context.tsx +248 -0
- package/src/components/form-wizard/form-wizard-navigation.tsx +118 -0
- package/src/components/form-wizard/form-wizard-progress.tsx +193 -0
- package/src/components/form-wizard/form-wizard-step.tsx +100 -0
- package/src/components/form-wizard/index.tsx +105 -0
- package/src/components/form-wizard/types.ts +76 -0
- package/src/components/index.ts +16 -1
- package/src/components/multi-step-form/index.tsx +223 -0
- package/src/components/phone-number-input/index.tsx +335 -0
- package/src/components/quiz-form/index.tsx +479 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { motion } from "framer-motion"
|
|
5
|
+
import { ChevronLeft, ChevronRight, Check, Loader2 } from "lucide-react"
|
|
6
|
+
import { Button } from "../ui/button"
|
|
7
|
+
import { cn } from "../../lib/utils"
|
|
8
|
+
import { useFormWizard } from "./form-wizard-context"
|
|
9
|
+
|
|
10
|
+
interface FormWizardNavigationProps {
|
|
11
|
+
className?: string
|
|
12
|
+
showStepIndicator?: boolean
|
|
13
|
+
previousText?: string
|
|
14
|
+
nextText?: string
|
|
15
|
+
completeText?: string
|
|
16
|
+
loadingText?: string
|
|
17
|
+
onPreviousClick?: () => void
|
|
18
|
+
onNextClick?: () => void
|
|
19
|
+
onCompleteClick?: () => void
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const FormWizardNavigation: React.FC<FormWizardNavigationProps> = ({
|
|
23
|
+
className,
|
|
24
|
+
showStepIndicator = true,
|
|
25
|
+
previousText = "Previous",
|
|
26
|
+
nextText = "Next",
|
|
27
|
+
completeText = "Complete",
|
|
28
|
+
loadingText = "Processing...",
|
|
29
|
+
onPreviousClick,
|
|
30
|
+
onNextClick,
|
|
31
|
+
onCompleteClick
|
|
32
|
+
}) => {
|
|
33
|
+
const {
|
|
34
|
+
steps,
|
|
35
|
+
currentStep,
|
|
36
|
+
isLoading,
|
|
37
|
+
canGoNext,
|
|
38
|
+
canGoPrevious,
|
|
39
|
+
goToNext,
|
|
40
|
+
goToPrevious,
|
|
41
|
+
completeWizard
|
|
42
|
+
} = useFormWizard()
|
|
43
|
+
|
|
44
|
+
const isLastStep = currentStep === steps.length - 1
|
|
45
|
+
const currentStepObj = steps[currentStep]
|
|
46
|
+
|
|
47
|
+
const handlePrevious = () => {
|
|
48
|
+
onPreviousClick?.()
|
|
49
|
+
goToPrevious()
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const handleNext = () => {
|
|
53
|
+
if (isLastStep) {
|
|
54
|
+
onCompleteClick?.()
|
|
55
|
+
completeWizard()
|
|
56
|
+
} else {
|
|
57
|
+
onNextClick?.()
|
|
58
|
+
goToNext()
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className={cn("flex items-center justify-between", className)}>
|
|
64
|
+
<div className="flex items-center gap-2">
|
|
65
|
+
<Button
|
|
66
|
+
type="button"
|
|
67
|
+
variant="outline"
|
|
68
|
+
onClick={handlePrevious}
|
|
69
|
+
disabled={!canGoPrevious || isLoading}
|
|
70
|
+
className={cn(
|
|
71
|
+
"transition-all duration-200",
|
|
72
|
+
!canGoPrevious && "invisible"
|
|
73
|
+
)}
|
|
74
|
+
>
|
|
75
|
+
<ChevronLeft className="w-4 h-4 mr-1" />
|
|
76
|
+
{previousText}
|
|
77
|
+
</Button>
|
|
78
|
+
|
|
79
|
+
{showStepIndicator && (
|
|
80
|
+
<motion.div
|
|
81
|
+
initial={{ opacity: 0, scale: 0.9 }}
|
|
82
|
+
animate={{ opacity: 1, scale: 1 }}
|
|
83
|
+
className="px-3 py-1 text-sm text-muted-foreground"
|
|
84
|
+
>
|
|
85
|
+
Step {currentStep + 1} of {steps.length}
|
|
86
|
+
{currentStepObj.isOptional && (
|
|
87
|
+
<span className="ml-1 text-xs">(Optional)</span>
|
|
88
|
+
)}
|
|
89
|
+
</motion.div>
|
|
90
|
+
)}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<Button
|
|
94
|
+
type="button"
|
|
95
|
+
onClick={handleNext}
|
|
96
|
+
disabled={isLoading || (!canGoNext && !isLastStep)}
|
|
97
|
+
className="min-w-[120px]"
|
|
98
|
+
>
|
|
99
|
+
{isLoading ? (
|
|
100
|
+
<>
|
|
101
|
+
<Loader2 className="w-4 h-4 mr-2 animate-spin" />
|
|
102
|
+
{loadingText}
|
|
103
|
+
</>
|
|
104
|
+
) : isLastStep ? (
|
|
105
|
+
<>
|
|
106
|
+
<Check className="w-4 h-4 mr-2" />
|
|
107
|
+
{completeText}
|
|
108
|
+
</>
|
|
109
|
+
) : (
|
|
110
|
+
<>
|
|
111
|
+
{nextText}
|
|
112
|
+
<ChevronRight className="w-4 h-4 ml-1" />
|
|
113
|
+
</>
|
|
114
|
+
)}
|
|
115
|
+
</Button>
|
|
116
|
+
</div>
|
|
117
|
+
)
|
|
118
|
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { motion } from "framer-motion"
|
|
5
|
+
import { CheckCircle, Circle, XCircle } from "lucide-react"
|
|
6
|
+
import { cn } from "../../lib/utils"
|
|
7
|
+
import { useFormWizard } from "./form-wizard-context"
|
|
8
|
+
import { FormWizardProps } from "./types"
|
|
9
|
+
|
|
10
|
+
interface FormWizardProgressProps {
|
|
11
|
+
className?: string
|
|
12
|
+
progressType?: FormWizardProps['progressType']
|
|
13
|
+
orientation?: FormWizardProps['orientation']
|
|
14
|
+
showStepNumbers?: boolean
|
|
15
|
+
showStepTitles?: boolean
|
|
16
|
+
stepIconPosition?: FormWizardProps['stepIconPosition']
|
|
17
|
+
completedStepIcon?: React.ReactNode
|
|
18
|
+
activeStepIcon?: React.ReactNode
|
|
19
|
+
errorStepIcon?: React.ReactNode
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const FormWizardProgress: React.FC<FormWizardProgressProps> = ({
|
|
23
|
+
className,
|
|
24
|
+
progressType = 'linear',
|
|
25
|
+
orientation = 'horizontal',
|
|
26
|
+
showStepNumbers = true,
|
|
27
|
+
showStepTitles = true,
|
|
28
|
+
stepIconPosition = 'top',
|
|
29
|
+
completedStepIcon = <CheckCircle className="w-5 h-5" />,
|
|
30
|
+
activeStepIcon,
|
|
31
|
+
errorStepIcon = <XCircle className="w-5 h-5" />
|
|
32
|
+
}) => {
|
|
33
|
+
const { steps, currentStep, isStepCompleted, isStepAccessible, error } = useFormWizard()
|
|
34
|
+
|
|
35
|
+
if (progressType === 'linear') {
|
|
36
|
+
return (
|
|
37
|
+
<div className={cn(
|
|
38
|
+
"relative",
|
|
39
|
+
orientation === 'vertical' ? "flex flex-col space-y-8" : "flex items-center justify-between",
|
|
40
|
+
className
|
|
41
|
+
)}>
|
|
42
|
+
{steps.map((step, index) => {
|
|
43
|
+
const isActive = index === currentStep
|
|
44
|
+
const isCompleted = isStepCompleted(index)
|
|
45
|
+
const isAccessible = isStepAccessible(index)
|
|
46
|
+
const hasError = isActive && error
|
|
47
|
+
|
|
48
|
+
const StepIcon = typeof step.icon === 'function'
|
|
49
|
+
? step.icon({ isActive, isCompleted })
|
|
50
|
+
: step.icon
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<React.Fragment key={step.id}>
|
|
54
|
+
<div className={cn(
|
|
55
|
+
"relative flex items-center",
|
|
56
|
+
orientation === 'vertical' ? "w-full" : "flex-1"
|
|
57
|
+
)}>
|
|
58
|
+
<motion.div
|
|
59
|
+
initial={{ scale: 0.8 }}
|
|
60
|
+
animate={{ scale: isActive ? 1.1 : 1 }}
|
|
61
|
+
className={cn(
|
|
62
|
+
"relative z-20 flex items-center justify-center rounded-full border-2 transition-all duration-300",
|
|
63
|
+
stepIconPosition === 'inside' ? "w-12 h-12" : "w-10 h-10",
|
|
64
|
+
isActive && "border-primary bg-primary text-primary-foreground shadow-lg",
|
|
65
|
+
isCompleted && !isActive && "border-primary bg-primary text-primary-foreground",
|
|
66
|
+
!isActive && !isCompleted && isAccessible && "border-muted-foreground/50 bg-background text-muted-foreground hover:border-muted-foreground",
|
|
67
|
+
!isAccessible && "border-muted bg-muted text-muted-foreground cursor-not-allowed opacity-50",
|
|
68
|
+
hasError && "border-destructive bg-destructive text-destructive-foreground"
|
|
69
|
+
)}
|
|
70
|
+
>
|
|
71
|
+
{hasError ? (
|
|
72
|
+
errorStepIcon
|
|
73
|
+
) : isCompleted && !isActive ? (
|
|
74
|
+
completedStepIcon
|
|
75
|
+
) : isActive && activeStepIcon ? (
|
|
76
|
+
activeStepIcon
|
|
77
|
+
) : StepIcon ? (
|
|
78
|
+
<span className="w-5 h-5 flex items-center justify-center">{StepIcon}</span>
|
|
79
|
+
) : showStepNumbers ? (
|
|
80
|
+
<span className="text-sm font-medium">{index + 1}</span>
|
|
81
|
+
) : (
|
|
82
|
+
<Circle className="w-4 h-4" />
|
|
83
|
+
)}
|
|
84
|
+
</motion.div>
|
|
85
|
+
|
|
86
|
+
{showStepTitles && (
|
|
87
|
+
<div className={cn(
|
|
88
|
+
"absolute whitespace-nowrap",
|
|
89
|
+
orientation === 'vertical'
|
|
90
|
+
? "left-16 top-1/2 -translate-y-1/2"
|
|
91
|
+
: "top-full mt-2 left-1/2 -translate-x-1/2 text-center"
|
|
92
|
+
)}>
|
|
93
|
+
<p className={cn(
|
|
94
|
+
"text-sm font-medium transition-colors",
|
|
95
|
+
isActive && "text-primary",
|
|
96
|
+
isCompleted && !isActive && "text-primary",
|
|
97
|
+
!isActive && !isCompleted && "text-muted-foreground"
|
|
98
|
+
)}>
|
|
99
|
+
{step.title}
|
|
100
|
+
</p>
|
|
101
|
+
{step.description && (
|
|
102
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
103
|
+
{step.description}
|
|
104
|
+
</p>
|
|
105
|
+
)}
|
|
106
|
+
</div>
|
|
107
|
+
)}
|
|
108
|
+
|
|
109
|
+
{index < steps.length - 1 && (
|
|
110
|
+
<div className={cn(
|
|
111
|
+
"absolute transition-all duration-500",
|
|
112
|
+
orientation === 'vertical'
|
|
113
|
+
? "top-12 left-5 w-0.5 h-8"
|
|
114
|
+
: "left-12 right-0 top-1/2 -translate-y-1/2 h-0.5",
|
|
115
|
+
isCompleted ? "bg-primary" : "bg-muted"
|
|
116
|
+
)} />
|
|
117
|
+
)}
|
|
118
|
+
</div>
|
|
119
|
+
</React.Fragment>
|
|
120
|
+
)
|
|
121
|
+
})}
|
|
122
|
+
</div>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (progressType === 'dots') {
|
|
127
|
+
return (
|
|
128
|
+
<div className={cn("flex items-center justify-center space-x-2", className)}>
|
|
129
|
+
{steps.map((_, index) => {
|
|
130
|
+
const isActive = index === currentStep
|
|
131
|
+
const isCompleted = isStepCompleted(index)
|
|
132
|
+
|
|
133
|
+
return (
|
|
134
|
+
<motion.div
|
|
135
|
+
key={index}
|
|
136
|
+
initial={{ scale: 0.8 }}
|
|
137
|
+
animate={{ scale: isActive ? 1.2 : 1 }}
|
|
138
|
+
className={cn(
|
|
139
|
+
"rounded-full transition-all duration-300",
|
|
140
|
+
isActive ? "w-3 h-3 bg-primary" : "w-2 h-2",
|
|
141
|
+
isCompleted && !isActive && "bg-primary/60",
|
|
142
|
+
!isActive && !isCompleted && "bg-muted"
|
|
143
|
+
)}
|
|
144
|
+
/>
|
|
145
|
+
)
|
|
146
|
+
})}
|
|
147
|
+
</div>
|
|
148
|
+
)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (progressType === 'circular') {
|
|
152
|
+
const progress = ((currentStep + 1) / steps.length) * 100
|
|
153
|
+
const circumference = 2 * Math.PI * 40 // radius = 40
|
|
154
|
+
const strokeDashoffset = circumference - (progress / 100) * circumference
|
|
155
|
+
|
|
156
|
+
return (
|
|
157
|
+
<div className={cn("relative w-32 h-32", className)}>
|
|
158
|
+
<svg className="w-full h-full -rotate-90">
|
|
159
|
+
<circle
|
|
160
|
+
cx="64"
|
|
161
|
+
cy="64"
|
|
162
|
+
r="40"
|
|
163
|
+
fill="none"
|
|
164
|
+
stroke="currentColor"
|
|
165
|
+
strokeWidth="8"
|
|
166
|
+
className="text-muted"
|
|
167
|
+
/>
|
|
168
|
+
<motion.circle
|
|
169
|
+
cx="64"
|
|
170
|
+
cy="64"
|
|
171
|
+
r="40"
|
|
172
|
+
fill="none"
|
|
173
|
+
stroke="currentColor"
|
|
174
|
+
strokeWidth="8"
|
|
175
|
+
strokeDasharray={circumference}
|
|
176
|
+
initial={{ strokeDashoffset: circumference }}
|
|
177
|
+
animate={{ strokeDashoffset }}
|
|
178
|
+
className="text-primary"
|
|
179
|
+
transition={{ duration: 0.5 }}
|
|
180
|
+
/>
|
|
181
|
+
</svg>
|
|
182
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
183
|
+
<div className="text-center">
|
|
184
|
+
<p className="text-2xl font-bold">{currentStep + 1}</p>
|
|
185
|
+
<p className="text-sm text-muted-foreground">of {steps.length}</p>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
</div>
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { motion, AnimatePresence } from "framer-motion"
|
|
5
|
+
import { cn } from "../../lib/utils"
|
|
6
|
+
import { useFormWizard } from "./form-wizard-context"
|
|
7
|
+
import { FormWizardProps } from "./types"
|
|
8
|
+
|
|
9
|
+
interface FormWizardStepProps {
|
|
10
|
+
className?: string
|
|
11
|
+
animationType?: FormWizardProps['animationType']
|
|
12
|
+
animationDuration?: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const animations = {
|
|
16
|
+
slide: {
|
|
17
|
+
initial: (direction: number) => ({
|
|
18
|
+
x: direction > 0 ? 1000 : -1000,
|
|
19
|
+
opacity: 0
|
|
20
|
+
}),
|
|
21
|
+
animate: {
|
|
22
|
+
x: 0,
|
|
23
|
+
opacity: 1
|
|
24
|
+
},
|
|
25
|
+
exit: (direction: number) => ({
|
|
26
|
+
x: direction < 0 ? 1000 : -1000,
|
|
27
|
+
opacity: 0
|
|
28
|
+
})
|
|
29
|
+
},
|
|
30
|
+
fade: {
|
|
31
|
+
initial: { opacity: 0 },
|
|
32
|
+
animate: { opacity: 1 },
|
|
33
|
+
exit: { opacity: 0 }
|
|
34
|
+
},
|
|
35
|
+
scale: {
|
|
36
|
+
initial: { opacity: 0, scale: 0.8 },
|
|
37
|
+
animate: { opacity: 1, scale: 1 },
|
|
38
|
+
exit: { opacity: 0, scale: 0.8 }
|
|
39
|
+
},
|
|
40
|
+
none: {
|
|
41
|
+
initial: {},
|
|
42
|
+
animate: {},
|
|
43
|
+
exit: {}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const FormWizardStep: React.FC<FormWizardStepProps> = ({
|
|
48
|
+
className,
|
|
49
|
+
animationType = 'slide',
|
|
50
|
+
animationDuration = 0.3
|
|
51
|
+
}) => {
|
|
52
|
+
const { steps, currentStep, goToNext, goToPrevious, goToStep, updateStepData, stepData } = useFormWizard()
|
|
53
|
+
const [direction, setDirection] = React.useState(0)
|
|
54
|
+
const previousStep = React.useRef(currentStep)
|
|
55
|
+
|
|
56
|
+
React.useEffect(() => {
|
|
57
|
+
setDirection(currentStep > previousStep.current ? 1 : -1)
|
|
58
|
+
previousStep.current = currentStep
|
|
59
|
+
}, [currentStep])
|
|
60
|
+
|
|
61
|
+
const currentStepObj = steps[currentStep]
|
|
62
|
+
const isFirstStep = currentStep === 0
|
|
63
|
+
const isLastStep = currentStep === steps.length - 1
|
|
64
|
+
|
|
65
|
+
const stepContentProps = {
|
|
66
|
+
currentStep,
|
|
67
|
+
totalSteps: steps.length,
|
|
68
|
+
goToNext,
|
|
69
|
+
goToPrevious,
|
|
70
|
+
goToStep,
|
|
71
|
+
isFirstStep,
|
|
72
|
+
isLastStep,
|
|
73
|
+
stepData: stepData[currentStepObj.id] || {},
|
|
74
|
+
updateStepData: (data: any) => updateStepData(currentStepObj.id, data)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const content = typeof currentStepObj.content === 'function'
|
|
78
|
+
? currentStepObj.content(stepContentProps)
|
|
79
|
+
: currentStepObj.content
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<AnimatePresence mode="wait" custom={direction}>
|
|
83
|
+
<motion.div
|
|
84
|
+
key={currentStep}
|
|
85
|
+
custom={direction}
|
|
86
|
+
variants={animations[animationType]}
|
|
87
|
+
initial="initial"
|
|
88
|
+
animate="animate"
|
|
89
|
+
exit="exit"
|
|
90
|
+
transition={{
|
|
91
|
+
duration: animationDuration,
|
|
92
|
+
ease: "easeInOut"
|
|
93
|
+
}}
|
|
94
|
+
className={cn("w-full", className)}
|
|
95
|
+
>
|
|
96
|
+
{content}
|
|
97
|
+
</motion.div>
|
|
98
|
+
</AnimatePresence>
|
|
99
|
+
)
|
|
100
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
import { FormWizardProvider } from "./form-wizard-context"
|
|
6
|
+
import { FormWizardProgress } from "./form-wizard-progress"
|
|
7
|
+
import { FormWizardStep } from "./form-wizard-step"
|
|
8
|
+
import { FormWizardNavigation } from "./form-wizard-navigation"
|
|
9
|
+
import { FormWizardProps } from "./types"
|
|
10
|
+
import { Card, CardContent } from "../ui/card"
|
|
11
|
+
import { Separator } from "../ui/separator"
|
|
12
|
+
|
|
13
|
+
export const MoonUIFormWizardPro = React.forwardRef<HTMLDivElement, FormWizardProps>(({
|
|
14
|
+
steps,
|
|
15
|
+
currentStep = 0,
|
|
16
|
+
onStepChange,
|
|
17
|
+
onComplete,
|
|
18
|
+
orientation = 'horizontal',
|
|
19
|
+
progressType = 'linear',
|
|
20
|
+
allowStepSkip = false,
|
|
21
|
+
allowBackNavigation = true,
|
|
22
|
+
validateOnStepChange = true,
|
|
23
|
+
animationType = 'slide',
|
|
24
|
+
animationDuration = 0.3,
|
|
25
|
+
showStepNumbers = true,
|
|
26
|
+
showStepTitles = true,
|
|
27
|
+
showProgressBar = true,
|
|
28
|
+
stepIconPosition = 'top',
|
|
29
|
+
completedStepIcon,
|
|
30
|
+
activeStepIcon,
|
|
31
|
+
errorStepIcon,
|
|
32
|
+
className,
|
|
33
|
+
progressClassName,
|
|
34
|
+
navigationClassName,
|
|
35
|
+
contentClassName,
|
|
36
|
+
stepClassName,
|
|
37
|
+
autoSave = false,
|
|
38
|
+
autoSaveDelay = 2000,
|
|
39
|
+
onAutoSave,
|
|
40
|
+
persistData = false,
|
|
41
|
+
storageKey = "form-wizard-data"
|
|
42
|
+
}, ref) => {
|
|
43
|
+
return (
|
|
44
|
+
<FormWizardProvider
|
|
45
|
+
steps={steps}
|
|
46
|
+
initialStep={currentStep}
|
|
47
|
+
onStepChange={onStepChange}
|
|
48
|
+
onComplete={onComplete}
|
|
49
|
+
validateOnStepChange={validateOnStepChange}
|
|
50
|
+
allowBackNavigation={allowBackNavigation}
|
|
51
|
+
allowStepSkip={allowStepSkip}
|
|
52
|
+
autoSave={autoSave}
|
|
53
|
+
autoSaveDelay={autoSaveDelay}
|
|
54
|
+
onAutoSave={onAutoSave}
|
|
55
|
+
persistData={persistData}
|
|
56
|
+
storageKey={storageKey}
|
|
57
|
+
>
|
|
58
|
+
<div ref={ref} className={cn("w-full", className)}>
|
|
59
|
+
{showProgressBar && (
|
|
60
|
+
<>
|
|
61
|
+
<FormWizardProgress
|
|
62
|
+
className={progressClassName}
|
|
63
|
+
progressType={progressType}
|
|
64
|
+
orientation={orientation}
|
|
65
|
+
showStepNumbers={showStepNumbers}
|
|
66
|
+
showStepTitles={showStepTitles}
|
|
67
|
+
stepIconPosition={stepIconPosition}
|
|
68
|
+
completedStepIcon={completedStepIcon}
|
|
69
|
+
activeStepIcon={activeStepIcon}
|
|
70
|
+
errorStepIcon={errorStepIcon}
|
|
71
|
+
/>
|
|
72
|
+
<Separator className="my-8" />
|
|
73
|
+
</>
|
|
74
|
+
)}
|
|
75
|
+
|
|
76
|
+
<Card className={cn("border-0 shadow-none", contentClassName)}>
|
|
77
|
+
<CardContent className="p-0">
|
|
78
|
+
<FormWizardStep
|
|
79
|
+
className={stepClassName}
|
|
80
|
+
animationType={animationType}
|
|
81
|
+
animationDuration={animationDuration}
|
|
82
|
+
/>
|
|
83
|
+
</CardContent>
|
|
84
|
+
</Card>
|
|
85
|
+
|
|
86
|
+
<Separator className="my-8" />
|
|
87
|
+
|
|
88
|
+
<FormWizardNavigation
|
|
89
|
+
className={navigationClassName}
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
92
|
+
</FormWizardProvider>
|
|
93
|
+
)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
MoonUIFormWizardPro.displayName = "MoonUIFormWizardPro"
|
|
97
|
+
|
|
98
|
+
// Export types and hooks
|
|
99
|
+
export type { FormWizardProps, WizardStep, WizardStepContentProps } from "./types"
|
|
100
|
+
export { useFormWizard } from "./form-wizard-context"
|
|
101
|
+
|
|
102
|
+
// Export sub-components for advanced usage
|
|
103
|
+
export { FormWizardProgress } from "./form-wizard-progress"
|
|
104
|
+
export { FormWizardStep } from "./form-wizard-step"
|
|
105
|
+
export { FormWizardNavigation } from "./form-wizard-navigation"
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { ReactNode } from "react"
|
|
2
|
+
|
|
3
|
+
export interface WizardStep {
|
|
4
|
+
id: string
|
|
5
|
+
title: string
|
|
6
|
+
description?: string
|
|
7
|
+
icon?: ReactNode | ((props: { isActive: boolean; isCompleted: boolean }) => ReactNode)
|
|
8
|
+
content: ReactNode | ((props: WizardStepContentProps) => ReactNode)
|
|
9
|
+
validation?: () => boolean | Promise<boolean>
|
|
10
|
+
isOptional?: boolean
|
|
11
|
+
isDisabled?: boolean | ((currentStep: number, steps: WizardStep[]) => boolean)
|
|
12
|
+
onEnter?: () => void | Promise<void>
|
|
13
|
+
onExit?: () => void | Promise<void>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WizardStepContentProps {
|
|
17
|
+
currentStep: number
|
|
18
|
+
totalSteps: number
|
|
19
|
+
goToNext: () => void
|
|
20
|
+
goToPrevious: () => void
|
|
21
|
+
goToStep: (step: number) => void
|
|
22
|
+
isFirstStep: boolean
|
|
23
|
+
isLastStep: boolean
|
|
24
|
+
stepData: any
|
|
25
|
+
updateStepData: (data: any) => void
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface FormWizardProps {
|
|
29
|
+
steps: WizardStep[]
|
|
30
|
+
currentStep?: number
|
|
31
|
+
onStepChange?: (step: number, previousStep: number) => void
|
|
32
|
+
onComplete?: (data: Record<string, any>) => void | Promise<void>
|
|
33
|
+
orientation?: 'horizontal' | 'vertical'
|
|
34
|
+
progressType?: 'linear' | 'circular' | 'dots' | 'custom'
|
|
35
|
+
allowStepSkip?: boolean
|
|
36
|
+
allowBackNavigation?: boolean
|
|
37
|
+
validateOnStepChange?: boolean
|
|
38
|
+
animationType?: 'slide' | 'fade' | 'scale' | 'none'
|
|
39
|
+
animationDuration?: number
|
|
40
|
+
showStepNumbers?: boolean
|
|
41
|
+
showStepTitles?: boolean
|
|
42
|
+
showProgressBar?: boolean
|
|
43
|
+
stepIconPosition?: 'top' | 'left' | 'inside'
|
|
44
|
+
completedStepIcon?: ReactNode
|
|
45
|
+
activeStepIcon?: ReactNode
|
|
46
|
+
errorStepIcon?: ReactNode
|
|
47
|
+
className?: string
|
|
48
|
+
progressClassName?: string
|
|
49
|
+
navigationClassName?: string
|
|
50
|
+
contentClassName?: string
|
|
51
|
+
stepClassName?: string
|
|
52
|
+
autoSave?: boolean
|
|
53
|
+
autoSaveDelay?: number
|
|
54
|
+
onAutoSave?: (stepId: string, data: any) => void | Promise<void>
|
|
55
|
+
persistData?: boolean
|
|
56
|
+
storageKey?: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface WizardContextValue {
|
|
60
|
+
steps: WizardStep[]
|
|
61
|
+
currentStep: number
|
|
62
|
+
stepData: Record<string, any>
|
|
63
|
+
isLoading: boolean
|
|
64
|
+
error: string | null
|
|
65
|
+
goToNext: () => void
|
|
66
|
+
goToPrevious: () => void
|
|
67
|
+
goToStep: (step: number) => void
|
|
68
|
+
updateStepData: (stepId: string, data: any) => void
|
|
69
|
+
validateCurrentStep: () => Promise<boolean>
|
|
70
|
+
completeWizard: () => void
|
|
71
|
+
resetWizard: () => void
|
|
72
|
+
isStepCompleted: (stepIndex: number) => boolean
|
|
73
|
+
isStepAccessible: (stepIndex: number) => boolean
|
|
74
|
+
canGoNext: boolean
|
|
75
|
+
canGoPrevious: boolean
|
|
76
|
+
}
|
package/src/components/index.ts
CHANGED
|
@@ -84,4 +84,19 @@ export * from "./file-upload"
|
|
|
84
84
|
export * from "./data-table"
|
|
85
85
|
|
|
86
86
|
// Sidebar
|
|
87
|
-
export * from "./sidebar"
|
|
87
|
+
export * from "./sidebar"
|
|
88
|
+
|
|
89
|
+
// Form Wizard
|
|
90
|
+
export * from "./form-wizard"
|
|
91
|
+
|
|
92
|
+
// Multi-Step Form
|
|
93
|
+
export * from "./multi-step-form"
|
|
94
|
+
|
|
95
|
+
// Quiz Form
|
|
96
|
+
export * from "./quiz-form"
|
|
97
|
+
|
|
98
|
+
// Credit Card Input
|
|
99
|
+
export * from "./credit-card-input"
|
|
100
|
+
|
|
101
|
+
// Phone Number Input
|
|
102
|
+
export * from "./phone-number-input"
|