wizzard-stepper-react 1.3.1 โ†’ 1.5.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/README.md CHANGED
@@ -5,7 +5,8 @@ A flexible, headless, and strictly typed multi-step wizard library for React. Bu
5
5
  ## Features
6
6
 
7
7
  - ๐Ÿง  **Headless Architecture**: Full control over UI. You bring the components, we provide the logic.
8
- - ๐Ÿ”Œ **Adapter Pattern**: Built-in adapters for **Zod**, **Yup** validation, and **LocalStorage/URL/Memory** persistence.
8
+ - ๐Ÿ”Œ **Adapter Pattern**: Zero-dependency adapters for **Zod**, **Yup** validation. No hard dependencies on these libraries in the core.
9
+ - ๐Ÿ—๏ธ **Complex Data**: Built-in support for nested objects and arrays using dot notation (`users[0].name`).
9
10
  - ๐Ÿ›ก๏ธ **Strictly Typed**: Built with TypeScript generics for type safety across steps.
10
11
  - ๐Ÿ”€ **Conditional Steps**: Dynamic pipelines where steps can be skipped based on data.
11
12
  - ๐Ÿ’พ **Persistence**: Auto-save progress to LocalStorage or custom stores.
@@ -21,51 +22,61 @@ yarn add wizzard-stepper-react
21
22
  pnpm add wizzard-stepper-react
22
23
  ```
23
24
 
24
- ## Quick Start (Native Forms)
25
+ ## Usage
25
26
 
26
- ```tsx
27
- import { WizardProvider, useWizard, IWizardConfig } from 'wizzard-stepper-react';
27
+ ### 1. Basic Usage (Compatible & Simple)
28
28
 
29
- // 1. Define Config
30
- const config: IWizardConfig = {
31
- steps: [
32
- { id: 'personal', label: 'Personal Info' },
33
- { id: 'contact', label: 'Contact Details' },
34
- ],
35
- };
29
+ The quickest way to get started. Types are flexible (`any`).
30
+
31
+ ```tsx
32
+ import { WizardProvider, useWizard } from 'wizzard-stepper-react';
36
33
 
37
- // 2. Create Steps
38
34
  const Step1 = () => {
39
- const { handleStepChange, wizardData } = useWizard<{ name: string }>();
35
+ const { wizardData, handleStepChange } = useWizard();
40
36
  return (
41
37
  <input
42
- value={wizardData.name || ''}
43
- onChange={e => handleStepChange('name', e.target.value)}
38
+ value={wizardData.name}
39
+ onChange={(e) => handleStepChange('name', e.target.value)}
44
40
  />
45
41
  );
46
42
  };
47
43
 
48
- // 3. Wrap in Provider
49
- export default function App() {
50
- return (
51
- <WizardProvider config={config}>
52
- <WizardContent />
53
- </WizardProvider>
54
- );
44
+ const App = () => (
45
+ <WizardProvider>
46
+ <Step1 />
47
+ </WizardProvider>
48
+ );
49
+ ```
50
+
51
+ ### 2. Strict Usage (Factory Pattern ๐Ÿญ)
52
+
53
+ For production apps, use the factory pattern to get perfect type inference.
54
+
55
+ **`wizards/my-wizard.ts`**
56
+ ```typescript
57
+ import { createWizardFactory } from 'wizzard-stepper-react';
58
+
59
+ interface MySchema {
60
+ name: string;
61
+ age: number;
55
62
  }
56
63
 
57
- const WizardContent = () => {
58
- const { currentStep, goToNextStep } = useWizard();
59
- if(!currentStep) return null;
60
- return (
61
- <div>
62
- {currentStep.id === 'personal' && <Step1 />}
63
- <button onClick={goToNextStep}>Next</button>
64
- </div>
65
- )
64
+ export const { WizardProvider, useWizard } = createWizardFactory<MySchema>();
65
+ ```
66
+
67
+ **`components/MyForm.tsx`**
68
+ ```tsx
69
+ import { useWizard } from '../wizards/my-wizard';
70
+
71
+ const Step1 = () => {
72
+ const { wizardData } = useWizard();
73
+ // โœ… wizardData is strictly typed as MySchema
74
+ // โœ… Autocomplete works for wizardData.name
66
75
  }
67
76
  ```
68
77
 
78
+ See [MIGRATION.md](./MIGRATION.md) for details on switching to strict mode.
79
+
69
80
  ## Integration with React Hook Form + Zod
70
81
 
71
82
  ```tsx
@@ -97,12 +108,94 @@ const config = {
97
108
  {
98
109
  id: 'step1',
99
110
  label: 'Email',
100
- validationAdapter: new ZodAdapter(schema) // Blocks 'Next' if invalid
111
+ // Zero-dependency: works with any Zod version
112
+ validationAdapter: new ZodAdapter(schema)
101
113
  }
102
114
  ]
103
115
  }
104
116
  ```
105
117
 
118
+ ## Complex Data (Arrays & Objects)
119
+
120
+ The library provides `setData` and `getData` helpers that support deep paths using dot notation and array brackets.
121
+
122
+ ```tsx
123
+ const { setData, wizardData } = useWizard<MyData>();
124
+
125
+ // Set nested object property
126
+ setData('user.profile.name', 'John');
127
+
128
+ // Set array item property
129
+ setData('items[0].value', 'New Value');
130
+
131
+ // Get with default value
132
+ const name = getData('user.profile.name', 'Anonymous');
133
+
134
+ // ๐Ÿ†• Bulk Update (Autofill)
135
+ const autoFillParams = () => {
136
+ // Merges into existing data
137
+ updateData({
138
+ name: 'John Doe',
139
+ email: 'john@example.com'
140
+ });
141
+ };
142
+ ```
143
+
144
+ ## Performance & Optimization ๐Ÿš€
145
+
146
+ For large forms (e.g., 50+ array items), using `useWizard` context can cause performance issues because it triggers a re-render on every keystroke. To solve this, we provide **granular hooks** that allow components to subscribe only to the specific data they need.
147
+
148
+ ### 1. Use `useWizardValue` for Granular Updates
149
+
150
+ Instead of reading the whole `wizardData`, subscribe to a single field. The component will only re-render when *that specific field* changes.
151
+
152
+ ```tsx
153
+ // โœ… FAST: Only re-renders when "users[0].name" changes
154
+ const NameInput = () => {
155
+ // Subscribe to specific path
156
+ const name = useWizardValue('users[0].name');
157
+ const { setData } = useWizardActions(); // Component actions don't trigger re-renders
158
+
159
+ return <input value={name} onChange={e => setData('users[0].name', e.target.value)} />;
160
+ }
161
+
162
+ // โŒ SLOW: Re-renders on ANY change in the form
163
+ const NameInputSlow = () => {
164
+ const { wizardData, setData } = useWizard();
165
+ return <input value={wizardData.users[0].name} ... />;
166
+ }
167
+ ```
168
+
169
+ ### 2. Use `useWizardSelector` for Lists
170
+
171
+ When rendering lists, avoid passing the whole `children` array to the parent component. Instead, select only IDs and let child components fetch their own data.
172
+
173
+ ```tsx
174
+ const ChildrenList = () => {
175
+ // โœ… Only re-renders when the list LENGTH changes or IDs change
176
+ const childIds = useWizardSelector(state => state.children.map(c => c.id));
177
+
178
+ return (
179
+ <div>
180
+ {childIds.map((id, index) => (
181
+ // Pass ID/Index, NOT the data object
182
+ <ChildRow key={id} index={index} />
183
+ ))}
184
+ </div>
185
+ );
186
+ }
187
+ ```
188
+
189
+ ### 3. Debounced Validation
190
+
191
+ For heavy validation schemas, you can debounce validation to keep the UI responsive.
192
+
193
+ ```tsx
194
+ setData('field.path', value, {
195
+ debounceValidation: 300 // Wait 300ms before running Zod/Yup validation
196
+ });
197
+ ```
198
+
106
199
  ## Conditional Steps
107
200
 
108
201
  Steps can be dynamically included based on the wizard's state.
@@ -137,29 +230,141 @@ const config: IWizardConfig = {
137
230
  }
138
231
  ```
139
232
 
233
+ ## Advanced Features ๐ŸŒŸ
234
+
235
+ ### 1. Step Renderer (Declarative UI)
236
+
237
+ Instead of manual switch statements with `currentStep.id`, trust the renderer!
238
+
239
+ ```typescript
240
+ // Define component in config
241
+ const steps = [
242
+ { id: 'step1', label: 'Start', component: Step1Component },
243
+ { id: 'step2', label: 'End', component: Step2Component },
244
+ ];
245
+
246
+ // Render
247
+ const App = () => (
248
+ <WizardProvider config={{ steps }}>
249
+ <WizardStepRenderer
250
+ wrapper={({ children }) => (
251
+ <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }}>
252
+ {children}
253
+ </motion.div>
254
+ )}
255
+ />
256
+ </WizardProvider>
257
+ );
258
+ ```
259
+
260
+ ### 2. Routing Integration
261
+
262
+ Sync wizard state with URL using `onStepChange`.
263
+
264
+ ```tsx
265
+ const navigate = useNavigate();
266
+
267
+ const config: IWizardConfig = {
268
+ // 1. Sync State -> URL
269
+ onStepChange: (prev, next, data) => {
270
+ navigate(`/wizard/${next}`);
271
+ // Optional: Send event to Analytics
272
+ trackEvent('wizard_step', { step: next });
273
+ },
274
+ steps: [...]
275
+ };
276
+
277
+ // 2. Sync URL -> State (Initial Load)
278
+ const { stepId } = useParams();
279
+
280
+ return <WizardProvider config={config} initialStepId={stepId}>...</WizardProvider>;
281
+ ```
282
+
283
+ ### 3. Granular Persistence
284
+
285
+ By default, the wizard uses `MemoryAdapter` (RAM only). You can enable `LocalStorage` globally, but override it for sensitive steps.
286
+
287
+ ```tsx
288
+ const config: IWizardConfig = {
289
+ // Global: Persist everything to LocalStorage
290
+ persistence: { adapter: new LocalStorageAdapter('wizard_') },
291
+ steps: [
292
+ {
293
+ id: 'public',
294
+ label: 'Public Info',
295
+ // Inherits global adapter (LocalStorage)
296
+ },
297
+ {
298
+ id: 'sensitive',
299
+ label: 'Credit Card',
300
+ // Override: Store strictly in memory (cleared on refresh)
301
+ persistenceAdapter: new MemoryAdapter()
302
+ }
303
+ ]
304
+ };
305
+ ```
306
+
140
307
  ## API Reference
141
308
 
142
309
  ### `IWizardConfig<T>`
310
+
143
311
  - `steps`: Array of step configurations.
144
312
  - `persistence`: Configuration for state persistence.
145
313
  - `autoValidate`: (obj) Global validation setting.
146
314
 
147
315
  ### `useWizard<T>()`
316
+
148
317
  - `activeSteps`: Steps that match conditions.
149
318
  - `currentStep`: The currently active step object.
150
- - `wizardData`: The global state object.
151
- - `handleStepChange(key, value)`: Helper to update state.
319
+ - `wizardData`: The global state object (subscribe cautiously!).
320
+ - `setData(path, value, options?)`: Update state. Options: `{ debounceValidation: number }`.
321
+ - `getData(path, defaultValue?)`: Retrieve nested data.
322
+ - `handleStepChange(key, value)`: simple helper to update top-level state.
152
323
  - `goToNextStep()`: Validates and moves next.
153
324
  - `goToStep(id)`: Jumps to specific step.
154
325
  - `allErrors`: Map of validation errors.
155
326
 
327
+ ### New Performance Hooks
328
+
329
+ #### `useWizardValue<T>(path: string)`
330
+
331
+ Subscribes to a specific data path. Re-renders **only** when that value changes.
332
+
333
+ #### `useWizardError(path: string)`
334
+
335
+ Subscribes to validation errors for a specific path. Highly recommended for individual inputs.
336
+
337
+ #### `useWizardSelector<T>(selector: (state: T) => any)`
338
+
339
+ Create a custom subscription to the wizard state. Ideal for derived state or lists.
340
+
341
+ #### `useWizardActions()`
342
+
343
+ Returns object with actions (`setData`, `goToNextStep`, etc.) **without** subscribing to state changes. Use this in components that trigger updates but don't need to render data.
344
+
156
345
  ## Demos
157
346
 
158
347
  Check out the [Live Demo](https://ZizzX.github.io/wizzard-stepper-react/), [NPM](https://www.npmjs.com/package/wizzard-stepper-react) or the [source code](https://github.com/ZizzX/wizzard-stepper-react-demo) for a complete implementation featuring:
348
+
159
349
  - **Tailwind CSS v4** UI overhaul.
160
350
  - **React Hook Form + Zod** integration.
161
351
  - **Formik + Yup** integration.
162
352
  - **Conditional Routing** logic.
353
+ - **Advanced Features Demo**: (`/advanced`) showcasing:
354
+ - **Autofill**: `updateData` global merge.
355
+ - **Declarative Rendering**: `<WizardStepRenderer />`.
356
+ - **Granular Persistence**: Hybrid Memory/LocalStorage.
357
+
358
+ ## Advanced Demo Guide ๐Ÿงช
359
+
360
+ Visit `/advanced` in the demo to try:
361
+
362
+ 1. **Autofill**: Click "Magic Autofill" to test `updateData()`. It instantly populates the form (merged with existing data).
363
+ 2. **Hybrid Persistence**:
364
+ * **Step 1 (Identity)**: Refreshes persist (LocalStorage).
365
+ * **Step 2 (Security)**: Refreshes CLEAR data (MemoryAdapter).
366
+ 3. **Declarative UI**: The steps are rendered using `<WizardStepRenderer />` with Framer Motion animations, defined in the config!
163
367
 
164
368
  ## License
369
+
165
370
  MIT