wizzard-stepper-react 1.3.0 → 1.4.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,85 @@ 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
+
135
+ ## Performance & Optimization 🚀
136
+
137
+ 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.
138
+
139
+ ### 1. Use `useWizardValue` for Granular Updates
140
+
141
+ Instead of reading the whole `wizardData`, subscribe to a single field. The component will only re-render when *that specific field* changes.
142
+
143
+ ```tsx
144
+ // ✅ FAST: Only re-renders when "users[0].name" changes
145
+ const NameInput = () => {
146
+ // Subscribe to specific path
147
+ const name = useWizardValue('users[0].name');
148
+ const { setData } = useWizardActions(); // Component actions don't trigger re-renders
149
+
150
+ return <input value={name} onChange={e => setData('users[0].name', e.target.value)} />;
151
+ }
152
+
153
+ // ❌ SLOW: Re-renders on ANY change in the form
154
+ const NameInputSlow = () => {
155
+ const { wizardData, setData } = useWizard();
156
+ return <input value={wizardData.users[0].name} ... />;
157
+ }
158
+ ```
159
+
160
+ ### 2. Use `useWizardSelector` for Lists
161
+
162
+ 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.
163
+
164
+ ```tsx
165
+ const ChildrenList = () => {
166
+ // ✅ Only re-renders when the list LENGTH changes or IDs change
167
+ const childIds = useWizardSelector(state => state.children.map(c => c.id));
168
+
169
+ return (
170
+ <div>
171
+ {childIds.map((id, index) => (
172
+ // Pass ID/Index, NOT the data object
173
+ <ChildRow key={id} index={index} />
174
+ ))}
175
+ </div>
176
+ );
177
+ }
178
+ ```
179
+
180
+ ### 3. Debounced Validation
181
+
182
+ For heavy validation schemas, you can debounce validation to keep the UI responsive.
183
+
184
+ ```tsx
185
+ setData('field.path', value, {
186
+ debounceValidation: 300 // Wait 300ms before running Zod/Yup validation
187
+ });
188
+ ```
189
+
106
190
  ## Conditional Steps
107
191
 
108
192
  Steps can be dynamically included based on the wizard's state.
@@ -140,26 +224,50 @@ const config: IWizardConfig = {
140
224
  ## API Reference
141
225
 
142
226
  ### `IWizardConfig<T>`
227
+
143
228
  - `steps`: Array of step configurations.
144
229
  - `persistence`: Configuration for state persistence.
145
230
  - `autoValidate`: (obj) Global validation setting.
146
231
 
147
232
  ### `useWizard<T>()`
233
+
148
234
  - `activeSteps`: Steps that match conditions.
149
235
  - `currentStep`: The currently active step object.
150
- - `wizardData`: The global state object.
151
- - `handleStepChange(key, value)`: Helper to update state.
236
+ - `wizardData`: The global state object (subscribe cautiously!).
237
+ - `setData(path, value, options?)`: Update state. Options: `{ debounceValidation: number }`.
238
+ - `getData(path, defaultValue?)`: Retrieve nested data.
239
+ - `handleStepChange(key, value)`: simple helper to update top-level state.
152
240
  - `goToNextStep()`: Validates and moves next.
153
241
  - `goToStep(id)`: Jumps to specific step.
154
242
  - `allErrors`: Map of validation errors.
155
243
 
244
+ ### New Performance Hooks
245
+
246
+ #### `useWizardValue<T>(path: string)`
247
+
248
+ Subscribes to a specific data path. Re-renders **only** when that value changes.
249
+
250
+ #### `useWizardError(path: string)`
251
+
252
+ Subscribes to validation errors for a specific path. Highly recommended for individual inputs.
253
+
254
+ #### `useWizardSelector<T>(selector: (state: T) => any)`
255
+
256
+ Create a custom subscription to the wizard state. Ideal for derived state or lists.
257
+
258
+ #### `useWizardActions()`
259
+
260
+ 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.
261
+
156
262
  ## Demos
157
263
 
158
- Check out the [Live Demo](https://ZizzX.github.io/wizzard-stepper-react-demo/), [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:
264
+ 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:
265
+
159
266
  - **Tailwind CSS v4** UI overhaul.
160
267
  - **React Hook Form + Zod** integration.
161
268
  - **Formik + Yup** integration.
162
269
  - **Conditional Routing** logic.
163
270
 
164
271
  ## License
272
+
165
273
  MIT