react-form-steps 1.0.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/LICENSE +21 -0
- package/README.md +542 -0
- package/assets/cli_demo.html +214 -0
- package/assets/cli_screenshot.png +0 -0
- package/dist/cli.js +857 -0
- package/dist/index.d.mts +129 -0
- package/dist/index.d.ts +129 -0
- package/dist/index.js +24 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +24 -0
- package/dist/index.mjs.map +1 -0
- package/dist/index.native.d.mts +129 -0
- package/dist/index.native.js +2 -0
- package/dist/index.native.js.map +1 -0
- package/package.json +78 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Coderkube Technologies
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,542 @@
|
|
|
1
|
+
# ๐ react-form-steps
|
|
2
|
+
|
|
3
|
+
A premium, production-ready multi-step form manager for **React** & **React Native**. Built on top of `react-hook-form` and `zod`, it handles complex data flows, file persistence, draft recovery, and smooth animations โ all with zero configuration.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/react-form-steps)
|
|
6
|
+
[](https://github.com/yourusername/react-form-steps/blob/main/LICENSE)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ๐ป Interactive CLI Scaffolding
|
|
11
|
+
|
|
12
|
+
`react-form-steps` ships with a powerful interactive CLI that generates complete multi-step wizard forms in seconds. No boilerplate โ just answer a few questions and start building.
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://github.com/user-attachments/assets/c555b874-a716-47b7-9474-c24daa24f09f">
|
|
16
|
+
<img src="assets/cli_screenshot.png" alt="Watch react-form-steps CLI Demo Video" width="800" style="border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.15);" />
|
|
17
|
+
</a>
|
|
18
|
+
<br />
|
|
19
|
+
<sub>๐ฌ <b>Click the terminal preview above to watch the interactive CLI setup demo!</b></sub>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npx react-form-steps
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
The CLI walks you through:
|
|
27
|
+
|
|
28
|
+
| Prompt | Options |
|
|
29
|
+
| :--- | :--- |
|
|
30
|
+
| **Platform** | Web (React) ยท React Native (Mobile) |
|
|
31
|
+
| **Language** | JavaScript ยท TypeScript |
|
|
32
|
+
| **Rendering Style** | Normal Inline Page ยท Popup / Modal Overlay |
|
|
33
|
+
| **State Strategy** | Normal (React Hook Form) ยท Redux (React Hook Form + Redux Toolkit) |
|
|
34
|
+
| **Steps** | 1โ10 steps |
|
|
35
|
+
| **Fields per Step** | 1โ10 fields (uniform across all steps) |
|
|
36
|
+
|
|
37
|
+
### Generated File Structure
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
./form-steps/
|
|
41
|
+
โโโ FormStepsWizard.tsx # Main wizard wrapper
|
|
42
|
+
โโโ Step1.tsx # Step 1 component
|
|
43
|
+
โโโ Step2.tsx # Step 2 component
|
|
44
|
+
โโโ Step3.tsx # Step 3 component
|
|
45
|
+
โโโ form-steps.css # (Web only) Prebuilt styles
|
|
46
|
+
โโโ formSlice.ts # (Redux only) Redux Toolkit slice
|
|
47
|
+
โโโ index.ts # Barrel export
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## ๐ Key Features
|
|
53
|
+
|
|
54
|
+
- ๐๏ธ **Simple Architecture** โ Use `<FormSteps>` and `<Step>` components to build forms in minutes.
|
|
55
|
+
- โ
**First-Class Validation** โ Optional Zod integration for per-step or global validation.
|
|
56
|
+
- ๐พ **Smart Persistence** โ Automatically saves drafts to `localStorage`, `sessionStorage`, `AsyncStorage`, or your custom database.
|
|
57
|
+
- ๐ **Base64 File Helper** โ Serializes `File` objects (images/PDFs) into drafts and restores them as real Files on resume.
|
|
58
|
+
- โจ **Native Animations** โ Beautiful built-in `slide` and `fade` transitions.
|
|
59
|
+
- ๐ **Analytics Built-in** โ Track user drop-off with `onStepEnter` and `onStepComplete` callbacks.
|
|
60
|
+
- ๐ **Edit Mode** โ Switch between "Create" and "Edit" modes with automatic field diffing.
|
|
61
|
+
- ๐จ **Fully Customizable** โ Render props for banners, sidebars, and complete UI control.
|
|
62
|
+
- ๐ฑ **Cross-Platform** โ First-class React Native support with platform-specific components.
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## ๐ฆ Installation
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Core dependencies
|
|
70
|
+
npm install react-form-steps react-hook-form @hookform/resolvers
|
|
71
|
+
|
|
72
|
+
# Optional: Add Zod for schema validation
|
|
73
|
+
npm install zod
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For **React Native**, also install:
|
|
77
|
+
```bash
|
|
78
|
+
npm install @react-native-async-storage/async-storage
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## ๐ Quick Start (Web)
|
|
84
|
+
|
|
85
|
+
### 1. Define Your Step Components
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
// steps/PersonalInfo.tsx
|
|
89
|
+
import { useFormSteps } from 'react-form-steps';
|
|
90
|
+
|
|
91
|
+
export function PersonalInfo() {
|
|
92
|
+
const { register, formState: { errors }, goBack, goNext } = useFormSteps();
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<div>
|
|
96
|
+
<h3>Personal Information</h3>
|
|
97
|
+
|
|
98
|
+
<div>
|
|
99
|
+
<label>Full Name</label>
|
|
100
|
+
<input
|
|
101
|
+
{...register('fullName', { required: 'Name is required' })}
|
|
102
|
+
placeholder="John Doe"
|
|
103
|
+
/>
|
|
104
|
+
{errors.fullName && <span>{errors.fullName.message}</span>}
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<div>
|
|
108
|
+
<label>Email Address</label>
|
|
109
|
+
<input
|
|
110
|
+
type="email"
|
|
111
|
+
{...register('email', { required: 'Email is required' })}
|
|
112
|
+
placeholder="john@example.com"
|
|
113
|
+
/>
|
|
114
|
+
{errors.email && <span>{errors.email.message}</span>}
|
|
115
|
+
</div>
|
|
116
|
+
|
|
117
|
+
<div>
|
|
118
|
+
<button type="submit">Next Step</button>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
// steps/AddressInfo.tsx
|
|
127
|
+
import { useFormSteps } from 'react-form-steps';
|
|
128
|
+
|
|
129
|
+
export function AddressInfo() {
|
|
130
|
+
const { register, formState: { errors }, goBack } = useFormSteps();
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div>
|
|
134
|
+
<h3>Address Details</h3>
|
|
135
|
+
|
|
136
|
+
<div>
|
|
137
|
+
<label>Street Address</label>
|
|
138
|
+
<input
|
|
139
|
+
{...register('street', { required: 'Street is required' })}
|
|
140
|
+
placeholder="123 Main St"
|
|
141
|
+
/>
|
|
142
|
+
{errors.street && <span>{errors.street.message}</span>}
|
|
143
|
+
</div>
|
|
144
|
+
|
|
145
|
+
<div>
|
|
146
|
+
<label>City</label>
|
|
147
|
+
<input
|
|
148
|
+
{...register('city', { required: 'City is required' })}
|
|
149
|
+
placeholder="New York"
|
|
150
|
+
/>
|
|
151
|
+
{errors.city && <span>{errors.city.message}</span>}
|
|
152
|
+
</div>
|
|
153
|
+
|
|
154
|
+
<div>
|
|
155
|
+
<button type="button" onClick={goBack}>Back</button>
|
|
156
|
+
<button type="submit">Submit</button>
|
|
157
|
+
</div>
|
|
158
|
+
</div>
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### 2. Compose the Wizard
|
|
164
|
+
|
|
165
|
+
```tsx
|
|
166
|
+
// App.tsx
|
|
167
|
+
import { FormSteps, Step } from 'react-form-steps';
|
|
168
|
+
import { PersonalInfo } from './steps/PersonalInfo';
|
|
169
|
+
import { AddressInfo } from './steps/AddressInfo';
|
|
170
|
+
|
|
171
|
+
function App() {
|
|
172
|
+
const handleSubmit = (payload: any, diff: any) => {
|
|
173
|
+
console.log('โ
Form submitted:', payload);
|
|
174
|
+
// Send to your API
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
<FormSteps
|
|
179
|
+
formKey="user-registration"
|
|
180
|
+
storageStrategy="localStorage"
|
|
181
|
+
transition="slide"
|
|
182
|
+
allowJump={true}
|
|
183
|
+
onSubmit={handleSubmit}
|
|
184
|
+
onStepEnter={(idx) => console.log('Entered step', idx)}
|
|
185
|
+
onStepComplete={(idx) => console.log('Completed step', idx)}
|
|
186
|
+
>
|
|
187
|
+
<Step label="Personal Info">
|
|
188
|
+
<PersonalInfo />
|
|
189
|
+
</Step>
|
|
190
|
+
|
|
191
|
+
<Step label="Address">
|
|
192
|
+
<AddressInfo />
|
|
193
|
+
</Step>
|
|
194
|
+
</FormSteps>
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export default App;
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## ๐ฑ Quick Start (React Native)
|
|
204
|
+
|
|
205
|
+
### 1. Define Your Step Components
|
|
206
|
+
|
|
207
|
+
```tsx
|
|
208
|
+
// steps/PersonalInfo.tsx
|
|
209
|
+
import React from 'react';
|
|
210
|
+
import { View, Text, TextInput, TouchableOpacity, StyleSheet } from 'react-native';
|
|
211
|
+
import { useFormSteps } from 'react-form-steps';
|
|
212
|
+
|
|
213
|
+
export function PersonalInfo() {
|
|
214
|
+
const { setValue, watch, formState: { errors }, goNext } = useFormSteps();
|
|
215
|
+
|
|
216
|
+
return (
|
|
217
|
+
<View style={styles.container}>
|
|
218
|
+
<Text style={styles.title}>Personal Information</Text>
|
|
219
|
+
|
|
220
|
+
<View style={styles.field}>
|
|
221
|
+
<Text style={styles.label}>Full Name</Text>
|
|
222
|
+
<TextInput
|
|
223
|
+
style={styles.input}
|
|
224
|
+
placeholder="John Doe"
|
|
225
|
+
value={watch('fullName') || ''}
|
|
226
|
+
onChangeText={(val) => setValue('fullName', val, { shouldValidate: true })}
|
|
227
|
+
/>
|
|
228
|
+
{errors.fullName && (
|
|
229
|
+
<Text style={styles.error}>{errors.fullName.message}</Text>
|
|
230
|
+
)}
|
|
231
|
+
</View>
|
|
232
|
+
|
|
233
|
+
<View style={styles.field}>
|
|
234
|
+
<Text style={styles.label}>Email</Text>
|
|
235
|
+
<TextInput
|
|
236
|
+
style={styles.input}
|
|
237
|
+
placeholder="john@example.com"
|
|
238
|
+
keyboardType="email-address"
|
|
239
|
+
value={watch('email') || ''}
|
|
240
|
+
onChangeText={(val) => setValue('email', val, { shouldValidate: true })}
|
|
241
|
+
/>
|
|
242
|
+
{errors.email && (
|
|
243
|
+
<Text style={styles.error}>{errors.email.message}</Text>
|
|
244
|
+
)}
|
|
245
|
+
</View>
|
|
246
|
+
|
|
247
|
+
<TouchableOpacity style={styles.button} onPress={goNext}>
|
|
248
|
+
<Text style={styles.buttonText}>Next Step</Text>
|
|
249
|
+
</TouchableOpacity>
|
|
250
|
+
</View>
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
const styles = StyleSheet.create({
|
|
255
|
+
container: { padding: 16 },
|
|
256
|
+
title: { fontSize: 20, fontWeight: '700', marginBottom: 16, color: '#1e293b' },
|
|
257
|
+
field: { marginBottom: 16 },
|
|
258
|
+
label: { fontSize: 13, fontWeight: '600', color: '#64748b', marginBottom: 6 },
|
|
259
|
+
input: { borderWidth: 1, borderColor: '#cbd5e1', borderRadius: 8, padding: 12, fontSize: 14 },
|
|
260
|
+
error: { color: '#ef4444', fontSize: 12, marginTop: 4 },
|
|
261
|
+
button: { backgroundColor: '#3b82f6', padding: 14, borderRadius: 8, alignItems: 'center', marginTop: 12 },
|
|
262
|
+
buttonText: { color: '#fff', fontSize: 14, fontWeight: '600' },
|
|
263
|
+
});
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### 2. Compose the Wizard
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
// App.tsx
|
|
270
|
+
import React from 'react';
|
|
271
|
+
import { SafeAreaView, ScrollView } from 'react-native';
|
|
272
|
+
import AsyncStorage from '@react-native-async-storage/async-storage';
|
|
273
|
+
import { FormSteps, Step } from 'react-form-steps';
|
|
274
|
+
import { PersonalInfo } from './steps/PersonalInfo';
|
|
275
|
+
import { AddressInfo } from './steps/AddressInfo';
|
|
276
|
+
|
|
277
|
+
export default function App() {
|
|
278
|
+
const handleSubmit = (payload: any) => {
|
|
279
|
+
console.log('โ
Form submitted:', payload);
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
return (
|
|
283
|
+
<SafeAreaView style={{ flex: 1 }}>
|
|
284
|
+
<ScrollView>
|
|
285
|
+
<FormSteps
|
|
286
|
+
formKey="native-registration"
|
|
287
|
+
asyncStorage={AsyncStorage}
|
|
288
|
+
transition="slide"
|
|
289
|
+
allowJump={true}
|
|
290
|
+
onSubmit={handleSubmit}
|
|
291
|
+
>
|
|
292
|
+
<Step label="Personal Info">
|
|
293
|
+
<PersonalInfo />
|
|
294
|
+
</Step>
|
|
295
|
+
|
|
296
|
+
<Step label="Address">
|
|
297
|
+
<AddressInfo />
|
|
298
|
+
</Step>
|
|
299
|
+
</FormSteps>
|
|
300
|
+
</ScrollView>
|
|
301
|
+
</SafeAreaView>
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## ๐ ๏ธ Advanced Features
|
|
309
|
+
|
|
310
|
+
### ๐พ Persistence & Draft Recovery
|
|
311
|
+
|
|
312
|
+
The library auto-saves user progress as they navigate between steps. If a user leaves and returns later, a customizable banner asks them to resume or start fresh.
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
<FormSteps
|
|
316
|
+
formKey="checkout-form"
|
|
317
|
+
storageStrategy="localStorage" // Web: localStorage or sessionStorage
|
|
318
|
+
asyncStorage={AsyncStorage} // React Native: AsyncStorage
|
|
319
|
+
draftTTL={86400} // Draft expires after 24 hours (in seconds)
|
|
320
|
+
Autofilldata={true} // Skip the banner, auto-resume silently
|
|
321
|
+
onDraftFound={(draft) => console.log('Draft found!', draft)}
|
|
322
|
+
onSubmit={handleSubmit}
|
|
323
|
+
>
|
|
324
|
+
{/* steps */}
|
|
325
|
+
</FormSteps>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### โ
Zod Schema Validation
|
|
329
|
+
|
|
330
|
+
Pass a Zod schema to any `<Step>` for per-step validation. The form will not advance until the schema passes:
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
import { z } from 'zod';
|
|
334
|
+
|
|
335
|
+
const contactSchema = z.object({
|
|
336
|
+
phone: z.string().min(10, 'Enter a valid phone number'),
|
|
337
|
+
address: z.string().min(5, 'Address is too short'),
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
<Step label="Contact Details" schema={contactSchema}>
|
|
341
|
+
<ContactForm />
|
|
342
|
+
</Step>
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
### ๐ Automatic File Persistence
|
|
346
|
+
|
|
347
|
+
Most libraries lose file uploads if the page refreshes. `react-form-steps` automatically converts `File` and `FileList` objects into Base64 strings for storage, and restores them as real `File` objects when the user resumes.
|
|
348
|
+
|
|
349
|
+
```tsx
|
|
350
|
+
// In your step component โ just use a file input normally:
|
|
351
|
+
<input type="file" {...register('avatar')} />
|
|
352
|
+
|
|
353
|
+
// The library automatically serializes & restores files from drafts!
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### ๐ Edit Mode
|
|
357
|
+
|
|
358
|
+
Pass `defaultValues` to switch to edit mode. The library automatically tracks which fields changed:
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
<FormSteps
|
|
362
|
+
formKey="edit-profile"
|
|
363
|
+
defaultValues={existingUserData} // Activates edit mode
|
|
364
|
+
onSubmit={(payload, changedFields) => {
|
|
365
|
+
console.log('Full payload:', payload);
|
|
366
|
+
console.log('Only changed fields:', changedFields);
|
|
367
|
+
// Send a PATCH request with only the changed fields
|
|
368
|
+
}}
|
|
369
|
+
>
|
|
370
|
+
{/* steps */}
|
|
371
|
+
</FormSteps>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### ๐๏ธ Redux Integration
|
|
375
|
+
|
|
376
|
+
Sync form state with your Redux store in real-time:
|
|
377
|
+
|
|
378
|
+
```tsx
|
|
379
|
+
import { useDispatch } from 'react-redux';
|
|
380
|
+
import { updateFormData } from './formSlice';
|
|
381
|
+
|
|
382
|
+
function App() {
|
|
383
|
+
const dispatch = useDispatch();
|
|
384
|
+
|
|
385
|
+
return (
|
|
386
|
+
<FormSteps
|
|
387
|
+
formKey="redux-form"
|
|
388
|
+
onDataChange={(data) => dispatch(updateFormData(data))}
|
|
389
|
+
onSubmit={handleSubmit}
|
|
390
|
+
>
|
|
391
|
+
{/* steps */}
|
|
392
|
+
</FormSteps>
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### ๐ Analytics & Tracking
|
|
398
|
+
|
|
399
|
+
Monitor conversion rates and user drop-off:
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
<FormSteps
|
|
403
|
+
onStepEnter={(idx) => analytics.track('Step Viewed', { step: idx })}
|
|
404
|
+
onStepComplete={(idx) => analytics.track('Step Completed', { step: idx })}
|
|
405
|
+
onSubmit={handleSubmit}
|
|
406
|
+
>
|
|
407
|
+
{/* steps */}
|
|
408
|
+
</FormSteps>
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
### โจ Transitions
|
|
412
|
+
|
|
413
|
+
Add smooth animations between steps:
|
|
414
|
+
|
|
415
|
+
```tsx
|
|
416
|
+
<FormSteps transition="slide"> {/* Slide from right */}
|
|
417
|
+
<FormSteps transition="fade"> {/* Fade in/out */}
|
|
418
|
+
<FormSteps transition="none"> {/* Instant (default) */}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
### ๐ Step Navigation
|
|
422
|
+
|
|
423
|
+
Control how users can navigate between steps:
|
|
424
|
+
|
|
425
|
+
```tsx
|
|
426
|
+
<FormSteps
|
|
427
|
+
allowJump={true} // Allow clicking step indicators to jump
|
|
428
|
+
unrestrictedNav={true} // Allow jumping even to incomplete steps
|
|
429
|
+
onSubmit={handleSubmit}
|
|
430
|
+
>
|
|
431
|
+
{/* steps */}
|
|
432
|
+
</FormSteps>
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
---
|
|
436
|
+
|
|
437
|
+
## ๐ API Reference
|
|
438
|
+
|
|
439
|
+
### `<FormSteps />` Props
|
|
440
|
+
|
|
441
|
+
| Prop | Type | Default | Description |
|
|
442
|
+
| :--- | :--- | :--- | :--- |
|
|
443
|
+
| `formKey` | `string` | โ | Unique key for draft storage. |
|
|
444
|
+
| `storageStrategy` | `'localStorage' \| 'sessionStorage' \| 'database' \| 'none'` | `'none'` | Where to persist drafts (Web). |
|
|
445
|
+
| `asyncStorage` | `{ getItem, setItem, removeItem }` | โ | Custom async storage adapter (React Native). |
|
|
446
|
+
| `Autofilldata` | `boolean` | `false` | Auto-resume drafts without prompting the user. |
|
|
447
|
+
| `draftTTL` | `number` | โ | Draft time-to-live in seconds. |
|
|
448
|
+
| `transition` | `'slide' \| 'fade' \| 'none'` | `'none'` | Animation between steps. |
|
|
449
|
+
| `allowJump` | `boolean` | `false` | Allow non-linear step navigation. |
|
|
450
|
+
| `unrestrictedNav` | `boolean` | `false` | Allow jumping to incomplete steps. |
|
|
451
|
+
| `defaultValues` | `any` | โ | Initial data (activates Edit Mode). |
|
|
452
|
+
| `onSubmit` | `(payload, diff) => void` | **Required** | Called on final step submission. |
|
|
453
|
+
| `onAutoSave` | `(step, data, merged) => Promise<void>` | โ | Custom auto-save callback (database strategy). |
|
|
454
|
+
| `onClearDraft` | `() => void` | โ | Called when draft is cleared (clean up DB). |
|
|
455
|
+
| `onDataChange` | `(data) => void` | โ | Sync state with Redux or external stores. |
|
|
456
|
+
| `onDraftFound` | `(draft) => void` | โ | Notified when a saved draft is detected. |
|
|
457
|
+
| `onStepEnter` | `(index) => void` | โ | Called when a step becomes active. |
|
|
458
|
+
| `onStepComplete` | `(index) => void` | โ | Called when a step is completed. |
|
|
459
|
+
| `bannerConfig` | `object` | โ | Customize default banner text and styles. |
|
|
460
|
+
| `renderDraftBanner` | `(props) => ReactNode` | โ | Render prop for fully custom draft banners. |
|
|
461
|
+
|
|
462
|
+
### `<Step />` Props
|
|
463
|
+
|
|
464
|
+
| Prop | Type | Default | Description |
|
|
465
|
+
| :--- | :--- | :--- | :--- |
|
|
466
|
+
| `label` | `string` | **Required** | Display name for the step. |
|
|
467
|
+
| `schema` | `ZodSchema` | โ | Zod schema for per-step validation. |
|
|
468
|
+
|
|
469
|
+
### `useFormSteps()` Hook
|
|
470
|
+
|
|
471
|
+
Returns the full `FormStepsContext` merged with `react-hook-form`'s `useFormContext()`:
|
|
472
|
+
|
|
473
|
+
| Value | Type | Description |
|
|
474
|
+
| :--- | :--- | :--- |
|
|
475
|
+
| `values` / `mergedData` | `any` | Current merged values of all steps. |
|
|
476
|
+
| `currentStep` | `number` | Index of the active step. |
|
|
477
|
+
| `steps` | `StepInfo[]` | Array of `{ index, label, status }`. |
|
|
478
|
+
| `isEditMode` | `boolean` | `true` when `defaultValues` is provided. |
|
|
479
|
+
| `isSubmitting` | `boolean` | `true` during `onSubmit` execution. |
|
|
480
|
+
| `changedFields` | `Record<string, boolean>` | Map of changed fields (edit mode only). |
|
|
481
|
+
| `goNext()` | `() => Promise<void>` | Validate current step and move forward. |
|
|
482
|
+
| `goBack()` | `() => void` | Move to previous step. |
|
|
483
|
+
| `goToStep(idx)` | `(index: number) => void` | Jump to a specific step. |
|
|
484
|
+
| `getAllErrors()` | `() => Record<number, any>` | Get validation errors across all steps. |
|
|
485
|
+
| `resumeDraft(draft)` | `(draft) => void` | Programmatically resume a draft. |
|
|
486
|
+
| `clearDraft()` | `() => void` | Clear saved draft and reset form. |
|
|
487
|
+
| `register` | `function` | Register input fields (from React Hook Form). |
|
|
488
|
+
| `watch` | `function` | Watch specific fields (from React Hook Form). |
|
|
489
|
+
| `setValue` | `function` | Set field values (from React Hook Form). |
|
|
490
|
+
| `handleSubmit` | `function` | Form submit handler (from React Hook Form). |
|
|
491
|
+
| `formState` | `object` | Full form state including `errors`, `isDirty`, etc. |
|
|
492
|
+
|
|
493
|
+
---
|
|
494
|
+
|
|
495
|
+
## ๐จ Customizing the Draft Banner
|
|
496
|
+
|
|
497
|
+
Don't like the default banner? Replace it entirely with your own UI:
|
|
498
|
+
|
|
499
|
+
```tsx
|
|
500
|
+
<FormSteps
|
|
501
|
+
formKey="my-form"
|
|
502
|
+
storageStrategy="localStorage"
|
|
503
|
+
renderDraftBanner={({ draft, resume, startFresh, dismiss }) => (
|
|
504
|
+
<div className="my-custom-banner">
|
|
505
|
+
<p>๐ We found your previous progress!</p>
|
|
506
|
+
<p>Last saved: {new Date(draft.savedAt).toLocaleString()}</p>
|
|
507
|
+
<button onClick={resume}>Continue Where I Left Off</button>
|
|
508
|
+
<button onClick={startFresh}>Start Over</button>
|
|
509
|
+
<button onClick={dismiss}>Dismiss</button>
|
|
510
|
+
</div>
|
|
511
|
+
)}
|
|
512
|
+
onSubmit={handleSubmit}
|
|
513
|
+
>
|
|
514
|
+
{/* steps */}
|
|
515
|
+
</FormSteps>
|
|
516
|
+
```
|
|
517
|
+
|
|
518
|
+
Or customize just the text using `bannerConfig`:
|
|
519
|
+
|
|
520
|
+
```tsx
|
|
521
|
+
<FormSteps
|
|
522
|
+
bannerConfig={{
|
|
523
|
+
title: 'Welcome Back!',
|
|
524
|
+
description: 'You have unsaved progress from your last visit.',
|
|
525
|
+
resumeLabel: 'Continue',
|
|
526
|
+
freshLabel: 'Start Over',
|
|
527
|
+
}}
|
|
528
|
+
onSubmit={handleSubmit}
|
|
529
|
+
>
|
|
530
|
+
{/* steps */}
|
|
531
|
+
</FormSteps>
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## ๐ค Contributing
|
|
537
|
+
|
|
538
|
+
Contributions are welcome! Please open an issue or submit a pull request.
|
|
539
|
+
|
|
540
|
+
## ๐ License
|
|
541
|
+
|
|
542
|
+
MIT ยฉ [CoderKube Technologies](https://coderkube.com)
|