@moontra/moonui-pro 2.6.2 → 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 +85 -4
- package/dist/index.mjs +543 -12
- package/package.json +1 -1
- package/src/components/credit-card-input/index.tsx +406 -0
- package/src/components/form-wizard/index.tsx +2 -2
- package/src/components/index.ts +7 -1
- package/src/components/multi-step-form/index.tsx +3 -3
- package/src/components/phone-number-input/index.tsx +335 -0
- package/src/components/quiz-form/index.tsx +3 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@moontra/moonui-pro",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Premium React components for MoonUI - Advanced UI library with 50+ pro components including performance, interactive, and gesture components",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.mjs",
|
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import React, { useState, useRef, useEffect } from "react"
|
|
4
|
+
import { cn } from "../../lib/utils"
|
|
5
|
+
import { Input } from "../ui/input"
|
|
6
|
+
import { Label } from "../ui/label"
|
|
7
|
+
import { CreditCard, Lock } from "lucide-react"
|
|
8
|
+
import { motion, AnimatePresence } from "framer-motion"
|
|
9
|
+
|
|
10
|
+
export interface CreditCardInputProps extends Omit<React.InputHTMLAttributes<HTMLInputElement>, 'onChange' | 'value'> {
|
|
11
|
+
value?: {
|
|
12
|
+
number: string
|
|
13
|
+
expiry: string
|
|
14
|
+
cvc: string
|
|
15
|
+
name?: string
|
|
16
|
+
}
|
|
17
|
+
onChange?: (value: {
|
|
18
|
+
number: string
|
|
19
|
+
expiry: string
|
|
20
|
+
cvc: string
|
|
21
|
+
name?: string
|
|
22
|
+
}) => void
|
|
23
|
+
showCardPreview?: boolean
|
|
24
|
+
showCardType?: boolean
|
|
25
|
+
showSecurityBadge?: boolean
|
|
26
|
+
autoFormat?: boolean
|
|
27
|
+
validateOnChange?: boolean
|
|
28
|
+
labels?: {
|
|
29
|
+
number?: string
|
|
30
|
+
expiry?: string
|
|
31
|
+
cvc?: string
|
|
32
|
+
name?: string
|
|
33
|
+
}
|
|
34
|
+
placeholders?: {
|
|
35
|
+
number?: string
|
|
36
|
+
expiry?: string
|
|
37
|
+
cvc?: string
|
|
38
|
+
name?: string
|
|
39
|
+
}
|
|
40
|
+
errors?: {
|
|
41
|
+
number?: string
|
|
42
|
+
expiry?: string
|
|
43
|
+
cvc?: string
|
|
44
|
+
name?: string
|
|
45
|
+
}
|
|
46
|
+
className?: string
|
|
47
|
+
inputClassName?: string
|
|
48
|
+
labelClassName?: string
|
|
49
|
+
errorClassName?: string
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Kart tipi tespiti için regex patternleri
|
|
53
|
+
const cardPatterns = {
|
|
54
|
+
visa: /^4/,
|
|
55
|
+
mastercard: /^5[1-5]/,
|
|
56
|
+
amex: /^3[47]/,
|
|
57
|
+
discover: /^6(?:011|5)/,
|
|
58
|
+
diners: /^3(?:0[0-5]|[68])/,
|
|
59
|
+
jcb: /^35/,
|
|
60
|
+
unionpay: /^62/
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Kart numarası formatlama fonksiyonu
|
|
64
|
+
function formatCardNumber(value: string, cardType?: string): string {
|
|
65
|
+
const v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
|
|
66
|
+
const matches = cardType === 'amex'
|
|
67
|
+
? v.match(/\d{1,4}/g)
|
|
68
|
+
: v.match(/\d{1,4}/g)
|
|
69
|
+
return matches ? matches.join(' ') : v
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Expiry formatlama fonksiyonu
|
|
73
|
+
function formatExpiry(value: string): string {
|
|
74
|
+
const v = value.replace(/\s+/g, '').replace(/[^0-9]/gi, '')
|
|
75
|
+
if (v.length >= 2) {
|
|
76
|
+
return v.slice(0, 2) + (v.length > 2 ? '/' + v.slice(2, 4) : '')
|
|
77
|
+
}
|
|
78
|
+
return v
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Kart tipi tespiti
|
|
82
|
+
function detectCardType(number: string): string | null {
|
|
83
|
+
const cleanNumber = number.replace(/\s+/g, '')
|
|
84
|
+
for (const [type, pattern] of Object.entries(cardPatterns)) {
|
|
85
|
+
if (pattern.test(cleanNumber)) {
|
|
86
|
+
return type
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return null
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Luhn algoritması ile kart numarası doğrulama
|
|
93
|
+
function validateCardNumber(number: string): boolean {
|
|
94
|
+
const cleanNumber = number.replace(/\s+/g, '')
|
|
95
|
+
if (!/^\d+$/.test(cleanNumber)) return false
|
|
96
|
+
|
|
97
|
+
let sum = 0
|
|
98
|
+
let isEven = false
|
|
99
|
+
|
|
100
|
+
for (let i = cleanNumber.length - 1; i >= 0; i--) {
|
|
101
|
+
let digit = parseInt(cleanNumber.charAt(i), 10)
|
|
102
|
+
|
|
103
|
+
if (isEven) {
|
|
104
|
+
digit *= 2
|
|
105
|
+
if (digit > 9) {
|
|
106
|
+
digit -= 9
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
sum += digit
|
|
111
|
+
isEven = !isEven
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return sum % 10 === 0
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Expiry doğrulama
|
|
118
|
+
function validateExpiry(expiry: string): boolean {
|
|
119
|
+
const parts = expiry.split('/')
|
|
120
|
+
if (parts.length !== 2) return false
|
|
121
|
+
|
|
122
|
+
const month = parseInt(parts[0], 10)
|
|
123
|
+
const year = parseInt('20' + parts[1], 10)
|
|
124
|
+
|
|
125
|
+
if (month < 1 || month > 12) return false
|
|
126
|
+
|
|
127
|
+
const now = new Date()
|
|
128
|
+
const expiryDate = new Date(year, month - 1)
|
|
129
|
+
|
|
130
|
+
return expiryDate > now
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export const MoonUICreditCardInputPro = React.forwardRef<HTMLDivElement, CreditCardInputProps>(({
|
|
134
|
+
value = { number: '', expiry: '', cvc: '', name: '' },
|
|
135
|
+
onChange,
|
|
136
|
+
showCardPreview = true,
|
|
137
|
+
showCardType = true,
|
|
138
|
+
showSecurityBadge = true,
|
|
139
|
+
autoFormat = true,
|
|
140
|
+
validateOnChange = false,
|
|
141
|
+
labels = {},
|
|
142
|
+
placeholders = {},
|
|
143
|
+
errors = {},
|
|
144
|
+
className,
|
|
145
|
+
inputClassName,
|
|
146
|
+
labelClassName,
|
|
147
|
+
errorClassName,
|
|
148
|
+
disabled,
|
|
149
|
+
required,
|
|
150
|
+
...props
|
|
151
|
+
}, ref) => {
|
|
152
|
+
const [cardType, setCardType] = useState<string | null>(null)
|
|
153
|
+
const [focused, setFocused] = useState<string | null>(null)
|
|
154
|
+
const [localErrors, setLocalErrors] = useState<typeof errors>({})
|
|
155
|
+
|
|
156
|
+
const numberRef = useRef<HTMLInputElement>(null)
|
|
157
|
+
const expiryRef = useRef<HTMLInputElement>(null)
|
|
158
|
+
const cvcRef = useRef<HTMLInputElement>(null)
|
|
159
|
+
const nameRef = useRef<HTMLInputElement>(null)
|
|
160
|
+
|
|
161
|
+
// Kart tipini tespit et
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
const detectedType = detectCardType(value.number)
|
|
164
|
+
setCardType(detectedType)
|
|
165
|
+
}, [value.number])
|
|
166
|
+
|
|
167
|
+
// Doğrulama
|
|
168
|
+
useEffect(() => {
|
|
169
|
+
if (!validateOnChange) return
|
|
170
|
+
|
|
171
|
+
const newErrors: typeof errors = {}
|
|
172
|
+
|
|
173
|
+
if (value.number && !validateCardNumber(value.number)) {
|
|
174
|
+
newErrors.number = 'Invalid card number'
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (value.expiry && !validateExpiry(value.expiry)) {
|
|
178
|
+
newErrors.expiry = 'Invalid expiry date'
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (value.cvc && (value.cvc.length < 3 || value.cvc.length > 4)) {
|
|
182
|
+
newErrors.cvc = 'Invalid CVC'
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
setLocalErrors(newErrors)
|
|
186
|
+
}, [value, validateOnChange])
|
|
187
|
+
|
|
188
|
+
const handleChange = (field: keyof typeof value, newValue: string) => {
|
|
189
|
+
let formattedValue = newValue
|
|
190
|
+
|
|
191
|
+
if (autoFormat) {
|
|
192
|
+
if (field === 'number') {
|
|
193
|
+
formattedValue = formatCardNumber(newValue, cardType || undefined)
|
|
194
|
+
// Maksimum uzunluk kontrolü
|
|
195
|
+
const maxLength = cardType === 'amex' ? 18 : 19 // Boşluklarla birlikte
|
|
196
|
+
if (formattedValue.length > maxLength) return
|
|
197
|
+
} else if (field === 'expiry') {
|
|
198
|
+
formattedValue = formatExpiry(newValue)
|
|
199
|
+
if (formattedValue.length > 5) return
|
|
200
|
+
} else if (field === 'cvc') {
|
|
201
|
+
formattedValue = newValue.replace(/\D/g, '')
|
|
202
|
+
const maxLength = cardType === 'amex' ? 4 : 3
|
|
203
|
+
if (formattedValue.length > maxLength) return
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
onChange?.({
|
|
208
|
+
...value,
|
|
209
|
+
[field]: formattedValue
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
// Otomatik geçiş
|
|
213
|
+
if (autoFormat) {
|
|
214
|
+
if (field === 'number' && formattedValue.length === (cardType === 'amex' ? 18 : 19)) {
|
|
215
|
+
expiryRef.current?.focus()
|
|
216
|
+
} else if (field === 'expiry' && formattedValue.length === 5) {
|
|
217
|
+
cvcRef.current?.focus()
|
|
218
|
+
} else if (field === 'cvc' && formattedValue.length === (cardType === 'amex' ? 4 : 3)) {
|
|
219
|
+
nameRef.current?.focus()
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const displayErrors = { ...localErrors, ...errors }
|
|
225
|
+
|
|
226
|
+
return (
|
|
227
|
+
<div ref={ref} className={cn("space-y-4", className)} {...props}>
|
|
228
|
+
{showCardPreview && (
|
|
229
|
+
<motion.div
|
|
230
|
+
className="relative w-full max-w-md mx-auto h-48 rounded-xl bg-gradient-to-r from-gray-700 to-gray-900 p-6 text-white shadow-xl"
|
|
231
|
+
initial={{ opacity: 0, y: -20 }}
|
|
232
|
+
animate={{ opacity: 1, y: 0 }}
|
|
233
|
+
transition={{ duration: 0.3 }}
|
|
234
|
+
>
|
|
235
|
+
<div className="flex justify-between items-start mb-8">
|
|
236
|
+
<div className="text-lg font-semibold">
|
|
237
|
+
{cardType ? cardType.toUpperCase() : 'CARD'}
|
|
238
|
+
</div>
|
|
239
|
+
<CreditCard className="w-8 h-8" />
|
|
240
|
+
</div>
|
|
241
|
+
|
|
242
|
+
<div className="space-y-4">
|
|
243
|
+
<div className="text-xl font-mono tracking-wider">
|
|
244
|
+
{value.number || '•••• •••• •••• ••••'}
|
|
245
|
+
</div>
|
|
246
|
+
|
|
247
|
+
<div className="flex justify-between">
|
|
248
|
+
<div>
|
|
249
|
+
<div className="text-xs opacity-70">NAME</div>
|
|
250
|
+
<div className="text-sm uppercase">
|
|
251
|
+
{value.name || 'YOUR NAME'}
|
|
252
|
+
</div>
|
|
253
|
+
</div>
|
|
254
|
+
|
|
255
|
+
<div>
|
|
256
|
+
<div className="text-xs opacity-70">EXPIRES</div>
|
|
257
|
+
<div className="text-sm">
|
|
258
|
+
{value.expiry || 'MM/YY'}
|
|
259
|
+
</div>
|
|
260
|
+
</div>
|
|
261
|
+
</div>
|
|
262
|
+
</div>
|
|
263
|
+
</motion.div>
|
|
264
|
+
)}
|
|
265
|
+
|
|
266
|
+
<div className="space-y-4">
|
|
267
|
+
{/* Kart Numarası */}
|
|
268
|
+
<div className="space-y-1">
|
|
269
|
+
<Label htmlFor="card-number" className={labelClassName}>
|
|
270
|
+
{labels.number || 'Card Number'}
|
|
271
|
+
{required && <span className="text-destructive ml-1">*</span>}
|
|
272
|
+
</Label>
|
|
273
|
+
<div className="relative">
|
|
274
|
+
<Input
|
|
275
|
+
ref={numberRef}
|
|
276
|
+
id="card-number"
|
|
277
|
+
type="text"
|
|
278
|
+
inputMode="numeric"
|
|
279
|
+
value={value.number}
|
|
280
|
+
onChange={(e) => handleChange('number', e.target.value)}
|
|
281
|
+
onFocus={() => setFocused('number')}
|
|
282
|
+
onBlur={() => setFocused(null)}
|
|
283
|
+
placeholder={placeholders.number || '1234 5678 9012 3456'}
|
|
284
|
+
className={cn(
|
|
285
|
+
"pr-20",
|
|
286
|
+
displayErrors.number && "border-destructive",
|
|
287
|
+
inputClassName
|
|
288
|
+
)}
|
|
289
|
+
disabled={disabled}
|
|
290
|
+
/>
|
|
291
|
+
{showCardType && cardType && (
|
|
292
|
+
<div className="absolute right-3 top-1/2 -translate-y-1/2 text-sm font-medium text-muted-foreground">
|
|
293
|
+
{cardType.toUpperCase()}
|
|
294
|
+
</div>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
{displayErrors.number && (
|
|
298
|
+
<p className={cn("text-sm text-destructive", errorClassName)}>
|
|
299
|
+
{displayErrors.number}
|
|
300
|
+
</p>
|
|
301
|
+
)}
|
|
302
|
+
</div>
|
|
303
|
+
|
|
304
|
+
{/* Expiry ve CVC */}
|
|
305
|
+
<div className="grid grid-cols-2 gap-4">
|
|
306
|
+
<div className="space-y-1">
|
|
307
|
+
<Label htmlFor="card-expiry" className={labelClassName}>
|
|
308
|
+
{labels.expiry || 'Expiry Date'}
|
|
309
|
+
{required && <span className="text-destructive ml-1">*</span>}
|
|
310
|
+
</Label>
|
|
311
|
+
<Input
|
|
312
|
+
ref={expiryRef}
|
|
313
|
+
id="card-expiry"
|
|
314
|
+
type="text"
|
|
315
|
+
inputMode="numeric"
|
|
316
|
+
value={value.expiry}
|
|
317
|
+
onChange={(e) => handleChange('expiry', e.target.value)}
|
|
318
|
+
onFocus={() => setFocused('expiry')}
|
|
319
|
+
onBlur={() => setFocused(null)}
|
|
320
|
+
placeholder={placeholders.expiry || 'MM/YY'}
|
|
321
|
+
className={cn(
|
|
322
|
+
displayErrors.expiry && "border-destructive",
|
|
323
|
+
inputClassName
|
|
324
|
+
)}
|
|
325
|
+
disabled={disabled}
|
|
326
|
+
/>
|
|
327
|
+
{displayErrors.expiry && (
|
|
328
|
+
<p className={cn("text-sm text-destructive", errorClassName)}>
|
|
329
|
+
{displayErrors.expiry}
|
|
330
|
+
</p>
|
|
331
|
+
)}
|
|
332
|
+
</div>
|
|
333
|
+
|
|
334
|
+
<div className="space-y-1">
|
|
335
|
+
<Label htmlFor="card-cvc" className={labelClassName}>
|
|
336
|
+
{labels.cvc || 'CVC'}
|
|
337
|
+
{required && <span className="text-destructive ml-1">*</span>}
|
|
338
|
+
</Label>
|
|
339
|
+
<div className="relative">
|
|
340
|
+
<Input
|
|
341
|
+
ref={cvcRef}
|
|
342
|
+
id="card-cvc"
|
|
343
|
+
type="text"
|
|
344
|
+
inputMode="numeric"
|
|
345
|
+
value={value.cvc}
|
|
346
|
+
onChange={(e) => handleChange('cvc', e.target.value)}
|
|
347
|
+
onFocus={() => setFocused('cvc')}
|
|
348
|
+
onBlur={() => setFocused(null)}
|
|
349
|
+
placeholder={placeholders.cvc || cardType === 'amex' ? '1234' : '123'}
|
|
350
|
+
className={cn(
|
|
351
|
+
displayErrors.cvc && "border-destructive",
|
|
352
|
+
inputClassName
|
|
353
|
+
)}
|
|
354
|
+
disabled={disabled}
|
|
355
|
+
/>
|
|
356
|
+
{showSecurityBadge && (
|
|
357
|
+
<Lock className="absolute right-3 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
|
|
358
|
+
)}
|
|
359
|
+
</div>
|
|
360
|
+
{displayErrors.cvc && (
|
|
361
|
+
<p className={cn("text-sm text-destructive", errorClassName)}>
|
|
362
|
+
{displayErrors.cvc}
|
|
363
|
+
</p>
|
|
364
|
+
)}
|
|
365
|
+
</div>
|
|
366
|
+
</div>
|
|
367
|
+
|
|
368
|
+
{/* Kart Sahibi Adı */}
|
|
369
|
+
<div className="space-y-1">
|
|
370
|
+
<Label htmlFor="card-name" className={labelClassName}>
|
|
371
|
+
{labels.name || 'Cardholder Name'}
|
|
372
|
+
</Label>
|
|
373
|
+
<Input
|
|
374
|
+
ref={nameRef}
|
|
375
|
+
id="card-name"
|
|
376
|
+
type="text"
|
|
377
|
+
value={value.name}
|
|
378
|
+
onChange={(e) => handleChange('name', e.target.value)}
|
|
379
|
+
onFocus={() => setFocused('name')}
|
|
380
|
+
onBlur={() => setFocused(null)}
|
|
381
|
+
placeholder={placeholders.name || 'John Doe'}
|
|
382
|
+
className={cn(
|
|
383
|
+
displayErrors.name && "border-destructive",
|
|
384
|
+
inputClassName
|
|
385
|
+
)}
|
|
386
|
+
disabled={disabled}
|
|
387
|
+
/>
|
|
388
|
+
{displayErrors.name && (
|
|
389
|
+
<p className={cn("text-sm text-destructive", errorClassName)}>
|
|
390
|
+
{displayErrors.name}
|
|
391
|
+
</p>
|
|
392
|
+
)}
|
|
393
|
+
</div>
|
|
394
|
+
</div>
|
|
395
|
+
|
|
396
|
+
{showSecurityBadge && (
|
|
397
|
+
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
398
|
+
<Lock className="w-4 h-4" />
|
|
399
|
+
<span>Your payment information is secure and encrypted</span>
|
|
400
|
+
</div>
|
|
401
|
+
)}
|
|
402
|
+
</div>
|
|
403
|
+
)
|
|
404
|
+
})
|
|
405
|
+
|
|
406
|
+
MoonUICreditCardInputPro.displayName = "MoonUICreditCardInputPro"
|
|
@@ -10,7 +10,7 @@ import { FormWizardProps } from "./types"
|
|
|
10
10
|
import { Card, CardContent } from "../ui/card"
|
|
11
11
|
import { Separator } from "../ui/separator"
|
|
12
12
|
|
|
13
|
-
export const
|
|
13
|
+
export const MoonUIFormWizardPro = React.forwardRef<HTMLDivElement, FormWizardProps>(({
|
|
14
14
|
steps,
|
|
15
15
|
currentStep = 0,
|
|
16
16
|
onStepChange,
|
|
@@ -93,7 +93,7 @@ export const FormWizardPro = React.forwardRef<HTMLDivElement, FormWizardProps>((
|
|
|
93
93
|
)
|
|
94
94
|
})
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
MoonUIFormWizardPro.displayName = "MoonUIFormWizardPro"
|
|
97
97
|
|
|
98
98
|
// Export types and hooks
|
|
99
99
|
export type { FormWizardProps, WizardStep, WizardStepContentProps } from "./types"
|
package/src/components/index.ts
CHANGED
|
@@ -93,4 +93,10 @@ export * from "./form-wizard"
|
|
|
93
93
|
export * from "./multi-step-form"
|
|
94
94
|
|
|
95
95
|
// Quiz Form
|
|
96
|
-
export * from "./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"
|
|
@@ -4,7 +4,7 @@ import React from "react"
|
|
|
4
4
|
import { useForm, FormProvider, UseFormReturn, FieldValues, DefaultValues } from "react-hook-form"
|
|
5
5
|
import { zodResolver } from "@hookform/resolvers/zod"
|
|
6
6
|
import { z } from "zod"
|
|
7
|
-
import {
|
|
7
|
+
import { MoonUIFormWizardPro, FormWizardProps, WizardStep } from "../form-wizard"
|
|
8
8
|
import { Alert, AlertDescription } from "../ui/alert"
|
|
9
9
|
import { AlertCircle } from "lucide-react"
|
|
10
10
|
|
|
@@ -92,7 +92,7 @@ function createStepSchema(fields: MultiStepFormField[]): z.ZodSchema<any> {
|
|
|
92
92
|
return z.object(shape)
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
-
export function
|
|
95
|
+
export function MoonUIMultiStepFormPro<TFormData extends FieldValues = FieldValues>({
|
|
96
96
|
steps,
|
|
97
97
|
onSubmit,
|
|
98
98
|
defaultValues,
|
|
@@ -209,7 +209,7 @@ export function MultiStepFormPro<TFormData extends FieldValues = FieldValues>({
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
return (
|
|
212
|
-
<
|
|
212
|
+
<MoonUIFormWizardPro
|
|
213
213
|
{...wizardProps}
|
|
214
214
|
steps={wizardSteps}
|
|
215
215
|
onComplete={handleComplete}
|