wizzard-stepper-react 1.7.0 → 1.7.2

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.js CHANGED
@@ -1,921 +1 @@
1
- var __defProp = Object.defineProperty;
2
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
- var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
-
5
- // src/context/WizardContext.tsx
6
- import {
7
- createContext,
8
- useContext,
9
- useEffect,
10
- useMemo,
11
- useState,
12
- useCallback,
13
- useSyncExternalStore,
14
- useRef,
15
- useTransition
16
- } from "react";
17
-
18
- // src/adapters/persistence/MemoryAdapter.ts
19
- var MemoryAdapter = class {
20
- constructor() {
21
- __publicField(this, "storage", {});
22
- }
23
- saveStep(stepId, data) {
24
- this.storage[stepId] = data;
25
- }
26
- getStep(stepId) {
27
- return this.storage[stepId];
28
- }
29
- clear() {
30
- this.storage = {};
31
- }
32
- };
33
-
34
- // src/utils/data.ts
35
- var pathCache = /* @__PURE__ */ new Map();
36
- function toPath(path) {
37
- if (!path) return [];
38
- if (pathCache.has(path)) {
39
- return pathCache.get(path);
40
- }
41
- let keys;
42
- if (path.includes("[")) {
43
- keys = path.replace(/\[(\d+)\]/g, ".$1").split(".").filter(Boolean);
44
- } else {
45
- keys = path.split(".").filter(Boolean);
46
- }
47
- pathCache.set(path, keys);
48
- return keys;
49
- }
50
- function getByPath(obj, path, defaultValue) {
51
- if (!path || obj === void 0 || obj === null) return defaultValue ?? obj;
52
- if (!path.includes(".") && !path.includes("[")) {
53
- const val = obj[path];
54
- return val !== void 0 ? val : defaultValue;
55
- }
56
- const keys = toPath(path);
57
- let result = obj;
58
- for (let i = 0; i < keys.length; i++) {
59
- if (result === void 0 || result === null) return defaultValue;
60
- result = result[keys[i]];
61
- }
62
- return result !== void 0 ? result : defaultValue;
63
- }
64
- function setByPath(obj, path, value) {
65
- if (!path) return value;
66
- if (!path.includes(".") && !path.includes("[")) {
67
- if (Array.isArray(obj)) {
68
- const copy = [...obj];
69
- copy[path] = value;
70
- return copy;
71
- }
72
- return { ...obj, [path]: value };
73
- }
74
- const keys = toPath(path);
75
- if (keys.length === 0) return value;
76
- const root = Array.isArray(obj) ? [...obj] : { ...obj };
77
- let current = root;
78
- for (let i = 0; i < keys.length - 1; i++) {
79
- const key = keys[i];
80
- const nextKey = keys[i + 1];
81
- const existing = current[key];
82
- let nextLevel;
83
- if (existing && typeof existing === "object") {
84
- nextLevel = Array.isArray(existing) ? [...existing] : { ...existing };
85
- } else {
86
- const isNumeric = /^\d+$/.test(nextKey);
87
- nextLevel = isNumeric ? [] : {};
88
- }
89
- current[key] = nextLevel;
90
- current = nextLevel;
91
- }
92
- const lastKey = keys[keys.length - 1];
93
- current[lastKey] = value;
94
- return root;
95
- }
96
- function shallowEqual(a, b) {
97
- if (Object.is(a, b)) return true;
98
- if (typeof a !== "object" || a === null || typeof b !== "object" || b === null) {
99
- return false;
100
- }
101
- const keysA = Object.keys(a);
102
- const keysB = Object.keys(b);
103
- if (keysA.length !== keysB.length) return false;
104
- for (let i = 0; i < keysA.length; i++) {
105
- const key = keysA[i];
106
- if (!Object.prototype.hasOwnProperty.call(b, key) || !Object.is(a[key], b[key])) {
107
- return false;
108
- }
109
- }
110
- return true;
111
- }
112
-
113
- // src/context/WizardContext.tsx
114
- import { jsx } from "react/jsx-runtime";
115
- var WizardStateContext = createContext(
116
- void 0
117
- );
118
- var WizardActionsContext = createContext(
119
- void 0
120
- );
121
- var WizardStore = class {
122
- constructor(initialData) {
123
- __publicField(this, "state");
124
- __publicField(this, "listeners", /* @__PURE__ */ new Set());
125
- __publicField(this, "errorsMap", /* @__PURE__ */ new Map());
126
- __publicField(this, "subscribe", (listener) => {
127
- this.listeners.add(listener);
128
- return () => this.listeners.delete(listener);
129
- });
130
- this.state = {
131
- data: initialData,
132
- errors: {}
133
- };
134
- }
135
- getSnapshot() {
136
- return this.state;
137
- }
138
- update(newData) {
139
- this.state = { ...this.state, data: newData };
140
- this.notify();
141
- }
142
- // Sync internal Map to external Object (for backward compat)
143
- syncErrors() {
144
- const newErrorsObj = {};
145
- for (const [stepId, fieldErrors] of this.errorsMap.entries()) {
146
- if (fieldErrors.size > 0) {
147
- newErrorsObj[stepId] = Object.fromEntries(fieldErrors);
148
- }
149
- }
150
- this.state = { ...this.state, errors: newErrorsObj };
151
- this.notify();
152
- }
153
- // Update from Object (Legacy/State setter)
154
- updateErrors(newErrors) {
155
- this.errorsMap.clear();
156
- for (const [stepId, fieldErrors] of Object.entries(newErrors)) {
157
- const stepMap = /* @__PURE__ */ new Map();
158
- for (const [field, msg] of Object.entries(fieldErrors)) {
159
- stepMap.set(field, msg);
160
- }
161
- if (stepMap.size > 0) this.errorsMap.set(stepId, stepMap);
162
- }
163
- this.state = { ...this.state, errors: newErrors };
164
- this.notify();
165
- }
166
- // Optimize: Update Step Errors directly (O(1) Map updated, then Sync)
167
- setStepErrors(stepId, errors) {
168
- if (!errors || Object.keys(errors).length === 0) {
169
- if (this.errorsMap.has(stepId)) {
170
- this.errorsMap.delete(stepId);
171
- this.syncErrors();
172
- return true;
173
- }
174
- return false;
175
- }
176
- const stepMap = /* @__PURE__ */ new Map();
177
- for (const [field, msg] of Object.entries(errors)) {
178
- stepMap.set(field, msg);
179
- }
180
- this.errorsMap.set(stepId, stepMap);
181
- this.syncErrors();
182
- return true;
183
- }
184
- // Fast Delete (O(1))
185
- deleteError(stepId, path) {
186
- const stepErrors = this.errorsMap.get(stepId);
187
- if (!stepErrors) return false;
188
- if (stepErrors.has(path)) {
189
- stepErrors.delete(path);
190
- if (stepErrors.size === 0) {
191
- this.errorsMap.delete(stepId);
192
- }
193
- this.syncErrors();
194
- return true;
195
- }
196
- return false;
197
- }
198
- notify() {
199
- this.listeners.forEach((l) => l());
200
- }
201
- };
202
- function WizardProvider({
203
- config,
204
- initialData,
205
- initialStepId,
206
- children
207
- }) {
208
- const [currentStepId, setCurrentStepId] = useState("");
209
- const currentStepIdRef = useRef(currentStepId);
210
- useEffect(() => {
211
- currentStepIdRef.current = currentStepId;
212
- }, [currentStepId]);
213
- const [visitedSteps, setVisitedSteps] = useState(/* @__PURE__ */ new Set());
214
- const [completedSteps, setCompletedSteps] = useState(/* @__PURE__ */ new Set());
215
- const [errorSteps, setErrorSteps] = useState(/* @__PURE__ */ new Set());
216
- const [, setAllErrorsState] = useState({});
217
- const [isLoading, setIsLoading] = useState(true);
218
- const [isPending, startTransition] = useTransition();
219
- const storeRef = useRef(new WizardStore(initialData || {}));
220
- const persistenceAdapter = useMemo(() => {
221
- return config.persistence?.adapter || new MemoryAdapter();
222
- }, [config.persistence?.adapter]);
223
- const persistenceMode = config.persistence?.mode || "onStepChange";
224
- const stepsMap = useMemo(() => {
225
- const map = /* @__PURE__ */ new Map();
226
- config.steps.forEach((step) => map.set(step.id, step));
227
- return map;
228
- }, [config.steps]);
229
- const lastActiveStepsRef = useRef([]);
230
- const activeSteps = useSyncExternalStore(
231
- storeRef.current.subscribe,
232
- useCallback(() => {
233
- const currentData = storeRef.current.getSnapshot().data;
234
- const nextActiveSteps = config.steps.filter((step) => {
235
- if (step.condition) {
236
- return step.condition(currentData);
237
- }
238
- return true;
239
- });
240
- const prevIds = lastActiveStepsRef.current.map((s) => s.id).join(".");
241
- const nextIds = nextActiveSteps.map((s) => s.id).join(".");
242
- if (prevIds === nextIds && lastActiveStepsRef.current.length > 0) {
243
- return lastActiveStepsRef.current;
244
- }
245
- lastActiveStepsRef.current = nextActiveSteps;
246
- return nextActiveSteps;
247
- }, [config.steps])
248
- );
249
- const activeStepsIndexMap = useMemo(() => {
250
- const map = /* @__PURE__ */ new Map();
251
- activeSteps.forEach((s, i) => map.set(s.id, i));
252
- return map;
253
- }, [activeSteps]);
254
- useEffect(() => {
255
- if (!currentStepId && activeSteps.length > 0) {
256
- if (initialStepId) {
257
- const target = activeSteps.find((s) => s.id === initialStepId);
258
- if (target) {
259
- setCurrentStepId(target.id);
260
- } else {
261
- setCurrentStepId(activeSteps[0].id);
262
- }
263
- } else {
264
- setCurrentStepId(activeSteps[0].id);
265
- }
266
- setIsLoading(false);
267
- }
268
- }, [activeSteps, currentStepId, initialStepId]);
269
- const stateRef = useRef({
270
- config,
271
- stepsMap,
272
- activeSteps,
273
- activeStepsIndexMap,
274
- visitedSteps,
275
- completedSteps,
276
- persistenceMode,
277
- persistenceAdapter,
278
- currentStepId
279
- });
280
- stateRef.current = {
281
- config,
282
- stepsMap,
283
- activeSteps,
284
- activeStepsIndexMap,
285
- visitedSteps,
286
- completedSteps,
287
- persistenceMode,
288
- persistenceAdapter,
289
- currentStepId
290
- };
291
- const currentStep = useMemo(
292
- () => stepsMap.get(currentStepId) || null,
293
- [stepsMap, currentStepId]
294
- );
295
- const currentStepIndex = useMemo(
296
- () => activeStepsIndexMap.get(currentStepId) ?? -1,
297
- [activeStepsIndexMap, currentStepId]
298
- );
299
- const isFirstStep = currentStepIndex === 0;
300
- const isLastStep = currentStepIndex === activeSteps.length - 1;
301
- const META_KEY = "__wizzard_meta__";
302
- const hydrate = useCallback(() => {
303
- setIsLoading(true);
304
- const { persistenceAdapter: persistenceAdapter2, config: config2 } = stateRef.current;
305
- const metaFn = persistenceAdapter2.getStep(META_KEY);
306
- if (metaFn) {
307
- if (metaFn.currentStepId) setCurrentStepId(metaFn.currentStepId);
308
- if (metaFn.visited) setVisitedSteps(new Set(metaFn.visited));
309
- if (metaFn.completed) setCompletedSteps(new Set(metaFn.completed));
310
- }
311
- const loadedData = {};
312
- config2.steps.forEach((step) => {
313
- const stepData = persistenceAdapter2.getStep(step.id);
314
- if (stepData) {
315
- Object.assign(loadedData, stepData);
316
- }
317
- });
318
- if (Object.keys(loadedData).length > 0) {
319
- const currentData = storeRef.current.getSnapshot().data;
320
- const newData = { ...currentData, ...loadedData };
321
- storeRef.current.update(newData);
322
- }
323
- setIsLoading(false);
324
- }, []);
325
- useEffect(() => {
326
- hydrate();
327
- }, [hydrate]);
328
- const saveData = useCallback(
329
- (mode, stepId, data) => {
330
- const { stepsMap: stepsMap2, persistenceAdapter: persistenceAdapter2, persistenceMode: globalMode } = stateRef.current;
331
- const stepConfig = stepsMap2.get(stepId);
332
- const stepAdapter = stepConfig?.persistenceAdapter;
333
- const stepMode = stepConfig?.persistenceMode;
334
- const adapterToUse = stepAdapter || persistenceAdapter2;
335
- const modeToUse = stepMode || globalMode;
336
- if (mode === modeToUse || mode === "manual") {
337
- adapterToUse.saveStep(stepId, data);
338
- }
339
- },
340
- []
341
- );
342
- const validationTimeoutRef = useRef(
343
- null
344
- );
345
- const validateStep = useCallback(
346
- async (stepId, data) => {
347
- const { stepsMap: stepsMap2 } = stateRef.current;
348
- const step = stepsMap2.get(stepId);
349
- if (!step || !step.validationAdapter) {
350
- return true;
351
- }
352
- const result = await step.validationAdapter.validate(data);
353
- if (result.isValid) {
354
- const changed = storeRef.current.setStepErrors(stepId, null);
355
- if (changed) {
356
- setAllErrorsState(storeRef.current.getSnapshot().errors);
357
- setErrorSteps((prev) => {
358
- const next = new Set(prev);
359
- next.delete(stepId);
360
- return next;
361
- });
362
- }
363
- return true;
364
- } else {
365
- storeRef.current.setStepErrors(stepId, result.errors || null);
366
- setAllErrorsState(storeRef.current.getSnapshot().errors);
367
- setErrorSteps((prev) => {
368
- const next = new Set(prev);
369
- next.add(stepId);
370
- return next;
371
- });
372
- return false;
373
- }
374
- },
375
- []
376
- );
377
- const setStepData = useCallback(
378
- (stepId, data) => {
379
- const { persistenceMode: persistenceMode2 } = stateRef.current;
380
- const prevData = storeRef.current.getSnapshot().data;
381
- const newData = { ...prevData, ...data };
382
- storeRef.current.update(newData);
383
- const stepConfig = stateRef.current.stepsMap.get(stepId);
384
- const effectiveMode = stepConfig?.persistenceMode || persistenceMode2;
385
- if (effectiveMode === "onChange") {
386
- saveData("onChange", stepId, newData);
387
- }
388
- },
389
- [saveData]
390
- );
391
- const setData = useCallback(
392
- (path, value, options) => {
393
- const { persistenceMode: persistenceMode2, stepsMap: stepsMap2 } = stateRef.current;
394
- const prevData = storeRef.current.getSnapshot().data;
395
- const currentValue = getByPath(prevData, path);
396
- if (currentValue === value) return;
397
- const newData = setByPath(prevData, path, value);
398
- storeRef.current.update(newData);
399
- const activeStepId = stateRef.current.currentStepId;
400
- const stepConfig = stepsMap2.get(activeStepId);
401
- if (activeStepId) {
402
- const wasDeleted = storeRef.current.deleteError(activeStepId, path);
403
- if (wasDeleted) {
404
- if (!storeRef.current.errorsMap.has(activeStepId)) {
405
- setErrorSteps((prev) => {
406
- const next = new Set(prev);
407
- next.delete(activeStepId);
408
- return next;
409
- });
410
- }
411
- }
412
- }
413
- if (activeStepId && storeRef.current.getSnapshot().errors[activeStepId]) {
414
- startTransition(() => {
415
- setAllErrorsState(storeRef.current.getSnapshot().errors);
416
- });
417
- }
418
- const { config: config2 } = stateRef.current;
419
- const mode = stepConfig?.validationMode ?? config2.validationMode ?? "onStepChange";
420
- if (mode === "onChange") {
421
- if (options?.debounceValidation) {
422
- if (validationTimeoutRef.current)
423
- clearTimeout(validationTimeoutRef.current);
424
- validationTimeoutRef.current = setTimeout(() => {
425
- try {
426
- if (activeStepId) {
427
- validateStep(activeStepId, newData).catch((err) => {
428
- console.error("[Wizard] Debounced validation failed:", err);
429
- });
430
- }
431
- } catch (e) {
432
- console.error("[Wizard] Error starting validation:", e);
433
- }
434
- }, options.debounceValidation);
435
- } else {
436
- if (activeStepId) validateStep(activeStepId, newData);
437
- }
438
- }
439
- const effectivePersistenceMode = stepConfig?.persistenceMode ?? persistenceMode2;
440
- if (effectivePersistenceMode === "onChange" && activeStepId) {
441
- saveData("onChange", activeStepId, newData);
442
- }
443
- },
444
- [saveData, validateStep]
445
- );
446
- const updateData = useCallback(
447
- (data, options) => {
448
- const { config: config2 } = stateRef.current;
449
- const prevData = storeRef.current.getSnapshot().data;
450
- const newData = options?.replace ? data : { ...prevData, ...data };
451
- storeRef.current.update(newData);
452
- if (options?.persist) {
453
- config2.steps.forEach((step) => {
454
- saveData("manual", step.id, newData);
455
- });
456
- }
457
- },
458
- [saveData]
459
- );
460
- const getData = useCallback((path, defaultValue) => {
461
- return getByPath(storeRef.current.getSnapshot().data, path, defaultValue);
462
- }, []);
463
- const handleStepChange = useCallback(
464
- (field, value) => {
465
- const { currentStepId: currentStepId2 } = stateRef.current;
466
- if (!currentStepId2) return;
467
- setData(field, value);
468
- },
469
- [setData]
470
- );
471
- const validateAll = useCallback(async () => {
472
- const { activeSteps: activeSteps2 } = stateRef.current;
473
- const currentData = storeRef.current.getSnapshot().data;
474
- const validationResults = await Promise.all(
475
- activeSteps2.map((step) => validateStep(step.id, currentData))
476
- );
477
- const isValid = validationResults.every(Boolean);
478
- const finalErrors = storeRef.current.getSnapshot().errors;
479
- return { isValid, errors: finalErrors };
480
- }, [validateStep]);
481
- const goToStep = useCallback(
482
- async (stepId) => {
483
- const {
484
- activeStepsIndexMap: activeStepsIndexMap2,
485
- currentStepId: currentStepId2,
486
- config: config2,
487
- persistenceMode: persistenceMode2,
488
- visitedSteps: visitedSteps2,
489
- completedSteps: completedSteps2,
490
- persistenceAdapter: persistenceAdapter2,
491
- stepsMap: stepsMap2
492
- } = stateRef.current;
493
- const targetIndex = activeStepsIndexMap2.get(stepId) ?? -1;
494
- if (targetIndex === -1) return false;
495
- const currentData = storeRef.current.getSnapshot().data;
496
- const currentStepIndex2 = activeStepsIndexMap2.get(currentStepId2) ?? -1;
497
- const currentStep2 = stepsMap2.get(currentStepId2);
498
- if (targetIndex > currentStepIndex2) {
499
- const shouldValidate = currentStep2?.autoValidate ?? config2.autoValidate ?? false;
500
- if (shouldValidate && currentStepId2) {
501
- const isValid = await validateStep(currentStepId2, currentData);
502
- if (!isValid) return false;
503
- }
504
- }
505
- if (currentStep2 && currentStepId2) {
506
- const effectivePersistenceMode = currentStep2.persistenceMode ?? persistenceMode2;
507
- if (effectivePersistenceMode === "onStepChange") {
508
- saveData("onStepChange", currentStepId2, currentData);
509
- }
510
- }
511
- const nextVisited = new Set(visitedSteps2);
512
- if (currentStepId2) nextVisited.add(currentStepId2);
513
- setVisitedSteps(nextVisited);
514
- setCurrentStepId(stepId);
515
- if (persistenceMode2 !== "manual") {
516
- persistenceAdapter2.saveStep(META_KEY, {
517
- currentStepId: stepId,
518
- visited: Array.from(nextVisited),
519
- completed: Array.from(completedSteps2)
520
- });
521
- }
522
- if (config2.onStepChange) {
523
- config2.onStepChange(currentStepId2 || null, stepId, currentData);
524
- }
525
- window.scrollTo(0, 0);
526
- return true;
527
- },
528
- [saveData, validateStep]
529
- );
530
- const goToNextStep = useCallback(async () => {
531
- const { activeSteps: activeSteps2, activeStepsIndexMap: activeStepsIndexMap2, currentStepId: currentStepId2, completedSteps: completedSteps2, persistenceMode: persistenceMode2 } = stateRef.current;
532
- const currentStepIndex2 = activeStepsIndexMap2.get(currentStepId2) ?? -1;
533
- if (currentStepIndex2 === -1 || currentStepIndex2 === activeSteps2.length - 1) return;
534
- const nextStep = activeSteps2[currentStepIndex2 + 1];
535
- if (nextStep) {
536
- const success = await goToStep(nextStep.id);
537
- if (success) {
538
- const nextCompleted = new Set(completedSteps2).add(currentStepId2);
539
- setCompletedSteps(nextCompleted);
540
- if (persistenceMode2 !== "manual") {
541
- persistenceAdapter.saveStep(META_KEY, {
542
- currentStepId: nextStep.id,
543
- visited: Array.from(new Set(visitedSteps).add(currentStepId2)),
544
- completed: Array.from(nextCompleted)
545
- });
546
- }
547
- }
548
- }
549
- }, [goToStep]);
550
- const goToPrevStep = useCallback(() => {
551
- const { activeSteps: activeSteps2, activeStepsIndexMap: activeStepsIndexMap2, currentStepId: currentStepId2 } = stateRef.current;
552
- const currentStepIndex2 = activeStepsIndexMap2.get(currentStepId2) ?? -1;
553
- if (currentStepIndex2 <= 0) return;
554
- const prevStep = activeSteps2[currentStepIndex2 - 1];
555
- if (prevStep) {
556
- goToStep(prevStep.id);
557
- }
558
- }, [goToStep]);
559
- const clearStorage = useCallback(() => {
560
- stateRef.current.persistenceAdapter.clear();
561
- }, []);
562
- const save = useCallback(
563
- (stepIds) => {
564
- const { config: config2, currentStepId: currentStepId2 } = stateRef.current;
565
- const data = storeRef.current.getSnapshot().data;
566
- if (stepIds === true) {
567
- config2.steps.forEach((step) => {
568
- saveData("manual", step.id, data);
569
- });
570
- return;
571
- }
572
- if (!stepIds) {
573
- if (currentStepId2) {
574
- saveData("manual", currentStepId2, data);
575
- }
576
- return;
577
- }
578
- const ids = Array.isArray(stepIds) ? stepIds : [stepIds];
579
- ids.forEach((id) => {
580
- saveData("manual", id, data);
581
- });
582
- },
583
- [saveData]
584
- );
585
- const stateValue = useMemo(
586
- () => ({
587
- currentStep,
588
- currentStepIndex,
589
- isFirstStep,
590
- isLastStep,
591
- isLoading,
592
- isPending,
593
- activeSteps,
594
- visitedSteps,
595
- completedSteps,
596
- errorSteps,
597
- store: storeRef.current
598
- }),
599
- [
600
- currentStep,
601
- currentStepIndex,
602
- isFirstStep,
603
- isLastStep,
604
- isLoading,
605
- isPending,
606
- activeSteps,
607
- visitedSteps,
608
- completedSteps,
609
- errorSteps
610
- ]
611
- );
612
- const actionsValue = useMemo(
613
- () => ({
614
- goToNextStep,
615
- goToPrevStep,
616
- goToStep,
617
- setStepData,
618
- handleStepChange,
619
- validateStep: (sid) => validateStep(sid, storeRef.current.getSnapshot().data),
620
- validateAll,
621
- save,
622
- clearStorage,
623
- setData,
624
- updateData,
625
- getData
626
- }),
627
- [
628
- goToNextStep,
629
- goToPrevStep,
630
- goToStep,
631
- setStepData,
632
- handleStepChange,
633
- validateStep,
634
- validateAll,
635
- save,
636
- clearStorage,
637
- setData,
638
- updateData,
639
- getData
640
- ]
641
- );
642
- return /* @__PURE__ */ jsx(WizardStateContext.Provider, { value: stateValue, children: /* @__PURE__ */ jsx(WizardActionsContext.Provider, { value: actionsValue, children }) });
643
- }
644
- function useWizardState() {
645
- const context = useContext(WizardStateContext);
646
- if (!context) {
647
- throw new Error("useWizardState must be used within a WizardProvider");
648
- }
649
- return context;
650
- }
651
- function useWizardValue(path, options) {
652
- const { store } = useWizardState();
653
- const lastStateRef = useRef(null);
654
- const lastValueRef = useRef(null);
655
- const isEqual = options?.isEqual || Object.is;
656
- const getSnapshot = useCallback(() => {
657
- const fullState = store.getSnapshot();
658
- const data = fullState.data;
659
- if (data === lastStateRef.current) {
660
- return lastValueRef.current;
661
- }
662
- const value = getByPath(data, path);
663
- if (lastValueRef.current !== void 0 && isEqual(lastValueRef.current, value)) {
664
- lastStateRef.current = data;
665
- return lastValueRef.current;
666
- }
667
- lastStateRef.current = data;
668
- lastValueRef.current = value;
669
- return value;
670
- }, [store, path, isEqual]);
671
- return useSyncExternalStore(store.subscribe, getSnapshot);
672
- }
673
- function useWizardError(path) {
674
- const { store } = useWizardState();
675
- const lastStateRef = useRef(null);
676
- const lastValueRef = useRef(null);
677
- const getSnapshot = useCallback(() => {
678
- const fullState = store.getSnapshot();
679
- const errors = fullState.errors;
680
- if (errors === lastStateRef.current) {
681
- return lastValueRef.current;
682
- }
683
- let foundError;
684
- Object.values(errors).forEach((stepErrors) => {
685
- const typedStepErrors = stepErrors;
686
- if (typedStepErrors[path]) foundError = typedStepErrors[path];
687
- });
688
- lastStateRef.current = errors;
689
- lastValueRef.current = foundError;
690
- return foundError;
691
- }, [store, path]);
692
- return useSyncExternalStore(store.subscribe, getSnapshot);
693
- }
694
- function useWizardSelector(selector, options) {
695
- const { store } = useWizardState();
696
- const lastStateRef = useRef(null);
697
- const lastResultRef = useRef(null);
698
- const isEqual = options?.isEqual || Object.is;
699
- const getSnapshot = useCallback(() => {
700
- const fullState = store.getSnapshot();
701
- if (fullState === lastStateRef.current) {
702
- return lastResultRef.current;
703
- }
704
- const result = selector(fullState.data);
705
- if (lastResultRef.current !== null && isEqual(lastResultRef.current, result)) {
706
- lastStateRef.current = fullState;
707
- return lastResultRef.current;
708
- }
709
- lastStateRef.current = fullState;
710
- lastResultRef.current = result;
711
- return result;
712
- }, [store, selector, isEqual]);
713
- return useSyncExternalStore(store.subscribe, getSnapshot);
714
- }
715
- function useWizardActions() {
716
- const context = useContext(WizardActionsContext);
717
- if (!context) {
718
- throw new Error("useWizardActions must be used within a WizardProvider");
719
- }
720
- return context;
721
- }
722
- function useWizardContext() {
723
- const state = useWizardState();
724
- const actions = useWizardActions();
725
- const wizardData = useWizardSelector((s) => s);
726
- const allErrors = useSyncExternalStore(state.store.subscribe, () => state.store.getSnapshot().errors);
727
- return useMemo(
728
- () => ({
729
- ...state,
730
- ...actions,
731
- wizardData,
732
- allErrors
733
- }),
734
- [state, actions, wizardData, allErrors]
735
- );
736
- }
737
-
738
- // src/components/WizardStepRenderer.tsx
739
- import { useMemo as useMemo2 } from "react";
740
- import { jsx as jsx2 } from "react/jsx-runtime";
741
- var WizardStepRenderer = ({
742
- wrapper: Wrapper
743
- }) => {
744
- const { currentStep } = useWizardContext();
745
- const StepComponent = useMemo2(() => {
746
- if (!currentStep?.component) return null;
747
- return currentStep.component;
748
- }, [currentStep]);
749
- if (!currentStep || !StepComponent) {
750
- return null;
751
- }
752
- const content = /* @__PURE__ */ jsx2(StepComponent, {});
753
- if (Wrapper) {
754
- return /* @__PURE__ */ jsx2(Wrapper, { children: content }, currentStep.id);
755
- }
756
- return content;
757
- };
758
-
759
- // src/hooks/useWizard.ts
760
- var useWizard = () => {
761
- return useWizardContext();
762
- };
763
-
764
- // src/factory.tsx
765
- import { jsx as jsx3 } from "react/jsx-runtime";
766
- function createWizardFactory() {
767
- const WizardProvider2 = ({
768
- config,
769
- initialData,
770
- children
771
- }) => {
772
- return /* @__PURE__ */ jsx3(
773
- WizardProvider,
774
- {
775
- config,
776
- initialData,
777
- children
778
- }
779
- );
780
- };
781
- const useWizard2 = () => {
782
- return useWizard();
783
- };
784
- const useWizardContext2 = () => {
785
- return useWizardContext();
786
- };
787
- const useWizardValue2 = (path, options) => {
788
- return useWizardValue(path, options);
789
- };
790
- const useWizardSelector2 = (selector, options) => {
791
- return useWizardSelector(selector, options);
792
- };
793
- const useWizardError2 = (path) => {
794
- return useWizardError(path);
795
- };
796
- const useWizardActions2 = () => {
797
- return useWizardActions();
798
- };
799
- const useWizardState2 = () => {
800
- return useWizardState();
801
- };
802
- const createStep = (config) => config;
803
- return {
804
- WizardProvider: WizardProvider2,
805
- useWizard: useWizard2,
806
- useWizardContext: useWizardContext2,
807
- useWizardValue: useWizardValue2,
808
- useWizardSelector: useWizardSelector2,
809
- useWizardError: useWizardError2,
810
- useWizardActions: useWizardActions2,
811
- useWizardState: useWizardState2,
812
- createStep
813
- };
814
- }
815
-
816
- // src/adapters/persistence/LocalStorageAdapter.ts
817
- var LocalStorageAdapter = class {
818
- constructor(prefix = "wizard_") {
819
- __publicField(this, "prefix");
820
- this.prefix = prefix;
821
- }
822
- getKey(stepId) {
823
- return `${this.prefix}${stepId}`;
824
- }
825
- saveStep(stepId, data) {
826
- if (typeof window === "undefined") return;
827
- try {
828
- localStorage.setItem(this.getKey(stepId), JSON.stringify(data));
829
- } catch (error) {
830
- console.warn("LocalStorageAdapter: Failed to save step", error);
831
- }
832
- }
833
- getStep(stepId) {
834
- if (typeof window === "undefined") return void 0;
835
- try {
836
- const item = localStorage.getItem(this.getKey(stepId));
837
- return item ? JSON.parse(item) : void 0;
838
- } catch (error) {
839
- console.warn("LocalStorageAdapter: Failed to get step", error);
840
- return void 0;
841
- }
842
- }
843
- clear() {
844
- if (typeof window === "undefined") return;
845
- Object.keys(localStorage).forEach((key) => {
846
- if (key.startsWith(this.prefix)) {
847
- localStorage.removeItem(key);
848
- }
849
- });
850
- }
851
- };
852
-
853
- // src/adapters/validation/ZodAdapter.ts
854
- var ZodAdapter = class {
855
- constructor(schema) {
856
- __publicField(this, "schema");
857
- this.schema = schema;
858
- }
859
- async validate(data) {
860
- const result = await this.schema.safeParseAsync(data);
861
- if (result.success) {
862
- return { isValid: true };
863
- }
864
- const errors = {};
865
- if (result.error) {
866
- result.error.issues.forEach((err) => {
867
- const path = err.path.join(".");
868
- errors[path] = err.message;
869
- });
870
- }
871
- return { isValid: false, errors };
872
- }
873
- };
874
-
875
- // src/adapters/validation/YupAdapter.ts
876
- var YupAdapter = class {
877
- constructor(schema) {
878
- __publicField(this, "schema");
879
- this.schema = schema;
880
- }
881
- async validate(data) {
882
- try {
883
- await this.schema.validate(data, { abortEarly: false });
884
- return { isValid: true };
885
- } catch (err) {
886
- if (err && typeof err === "object" && "inner" in err) {
887
- const yupError = err;
888
- const errors = {};
889
- yupError.inner.forEach((error) => {
890
- if (error.path) {
891
- errors[error.path] = error.message;
892
- }
893
- });
894
- return { isValid: false, errors };
895
- }
896
- throw err;
897
- }
898
- }
899
- };
900
- export {
901
- LocalStorageAdapter,
902
- MemoryAdapter,
903
- WizardProvider,
904
- WizardStepRenderer,
905
- WizardStore,
906
- YupAdapter,
907
- ZodAdapter,
908
- createWizardFactory,
909
- getByPath,
910
- setByPath,
911
- shallowEqual,
912
- toPath,
913
- useWizard,
914
- useWizardActions,
915
- useWizardContext,
916
- useWizardError,
917
- useWizardSelector,
918
- useWizardState,
919
- useWizardValue
920
- };
921
- //# sourceMappingURL=index.js.map
1
+ var qe=Object.defineProperty;var Ne=(t,e,r)=>e in t?qe(t,e,{enumerable:!0,configurable:!0,writable:!0,value:r}):t[e]=r;var x=(t,e,r)=>Ne(t,typeof e!="symbol"?e+"":e,r);import{createContext as we,useContext as Pe,useEffect as te,useMemo as M,useState as k,useCallback as m,useSyncExternalStore as L,useRef as P,useTransition as Fe}from"react";var $=class{constructor(){x(this,"storage",{})}saveStep(e,r){this.storage[e]=r}getStep(e){return this.storage[e]}clear(){this.storage={}}};var ee=new Map;function xe(t){if(!t)return[];if(ee.has(t))return ee.get(t);let e;return t.includes("[")?e=t.replace(/\[(\d+)\]/g,".$1").split(".").filter(Boolean):e=t.split(".").filter(Boolean),ee.set(t,e),e}function J(t,e,r){if(!e||t===void 0||t===null)return r??t;if(!e.includes(".")&&!e.includes("[")){let c=t[e];return c!==void 0?c:r}let a=xe(e),s=t;for(let c=0;c<a.length;c++){if(s==null)return r;s=s[a[c]]}return s!==void 0?s:r}function ze(t,e,r){if(!e)return r;if(!e.includes(".")&&!e.includes("[")){if(Array.isArray(t)){let S=[...t];return S[e]=r,S}return{...t,[e]:r}}let a=xe(e);if(a.length===0)return r;let s=Array.isArray(t)?[...t]:{...t},c=s;for(let S=0;S<a.length-1;S++){let h=a[S],f=a[S+1],v=c[h],b;v&&typeof v=="object"?b=Array.isArray(v)?[...v]:{...v}:b=/^\d+$/.test(f)?[]:{},c[h]=b,c=b}let T=a[a.length-1];return c[T]=r,s}function He(t,e){if(Object.is(t,e))return!0;if(typeof t!="object"||t===null||typeof e!="object"||e===null)return!1;let r=Object.keys(t),a=Object.keys(e);if(r.length!==a.length)return!1;for(let s=0;s<r.length;s++){let c=r[s];if(!Object.prototype.hasOwnProperty.call(e,c)||!Object.is(t[c],e[c]))return!1}return!0}import{jsx as We}from"react/jsx-runtime";var be=we(void 0),Ce=we(void 0),re=class{constructor(e){x(this,"state");x(this,"listeners",new Set);x(this,"errorsMap",new Map);x(this,"subscribe",e=>(this.listeners.add(e),()=>this.listeners.delete(e)));this.state={data:e,errors:{}}}getSnapshot(){return this.state}update(e){this.state={...this.state,data:e},this.notify()}syncErrors(){let e={};for(let[r,a]of this.errorsMap.entries())a.size>0&&(e[r]=Object.fromEntries(a));this.state={...this.state,errors:e},this.notify()}updateErrors(e){this.errorsMap.clear();for(let[r,a]of Object.entries(e)){let s=new Map;for(let[c,T]of Object.entries(a))s.set(c,T);s.size>0&&this.errorsMap.set(r,s)}this.state={...this.state,errors:e},this.notify()}setStepErrors(e,r){if(!r||Object.keys(r).length===0)return this.errorsMap.has(e)?(this.errorsMap.delete(e),this.syncErrors(),!0):!1;let a=new Map;for(let[s,c]of Object.entries(r))a.set(s,c);return this.errorsMap.set(e,a),this.syncErrors(),!0}deleteError(e,r){let a=this.errorsMap.get(e);return a&&a.has(r)?(a.delete(r),a.size===0&&this.errorsMap.delete(e),this.syncErrors(),!0):!1}notify(){this.listeners.forEach(e=>e())}};function Ee({config:t,initialData:e,initialStepId:r,children:a}){let[s,c]=k(""),T=P(s);te(()=>{T.current=s},[s]);let[S,h]=k(new Set),[f,v]=k(new Set),[b,j]=k(new Set),[,Z]=k({}),[ae,G]=k(!0),[oe,Be]=Fe(),l=P(new re(e||{})),H=M(()=>t.persistence?.adapter||new $,[t.persistence?.adapter]),ie=t.persistence?.mode||"onStepChange",q=M(()=>{let n=new Map;return t.steps.forEach(i=>n.set(i.id,i)),n},[t.steps]),N=P([]),z=L(l.current.subscribe,m(()=>{let n=l.current.getSnapshot().data,i=t.steps.filter(d=>d.condition?d.condition(n):!0),o=N.current.map(d=>d.id).join("."),p=i.map(d=>d.id).join(".");return o===p&&N.current.length>0?N.current:(N.current=i,i)},[t.steps])),F=M(()=>{let n=new Map;return z.forEach((i,o)=>n.set(i.id,o)),n},[z]);te(()=>{if(!s&&z.length>0){if(r){let n=z.find(i=>i.id===r);c(n?n.id:z[0].id)}else c(z[0].id);G(!1)}},[z,s,r]);let I=P({config:t,stepsMap:q,activeSteps:z,activeStepsIndexMap:F,visitedSteps:S,completedSteps:f,persistenceMode:ie,persistenceAdapter:H,currentStepId:s});I.current={config:t,stepsMap:q,activeSteps:z,activeStepsIndexMap:F,visitedSteps:S,completedSteps:f,persistenceMode:ie,persistenceAdapter:H,currentStepId:s};let ce=M(()=>q.get(s)||null,[q,s]),_=M(()=>F.get(s)??-1,[F,s]),de=_===0,pe=_===z.length-1,Q="__wizzard_meta__",ue=m(()=>{G(!0);let{persistenceAdapter:n,config:i}=I.current,o=n.getStep(Q);o&&(o.currentStepId&&c(o.currentStepId),o.visited&&h(new Set(o.visited)),o.completed&&v(new Set(o.completed)));let p={};if(i.steps.forEach(d=>{let u=n.getStep(d.id);u&&Object.assign(p,u)}),Object.keys(p).length>0){let u={...l.current.getSnapshot().data,...p};l.current.update(u)}G(!1)},[]);te(()=>{ue()},[ue]);let W=m((n,i,o)=>{let{stepsMap:p,persistenceAdapter:d,persistenceMode:u}=I.current,y=p.get(i),w=y?.persistenceAdapter,g=y?.persistenceMode;(n===(g||u)||n==="manual")&&(w||d).saveStep(i,o)},[]),X=P(null),C=m(async(n,i)=>{let{stepsMap:o}=I.current,p=o.get(n);if(!p||!p.validationAdapter)return!0;let d=await p.validationAdapter.validate(i);return d.isValid?(l.current.setStepErrors(n,null)&&(Z(l.current.getSnapshot().errors),j(y=>{let w=new Set(y);return w.delete(n),w})),!0):(l.current.setStepErrors(n,d.errors||null),Z(l.current.getSnapshot().errors),j(u=>{let y=new Set(u);return y.add(n),y}),!1)},[]),le=m((n,i)=>{let{persistenceMode:o}=I.current,d={...l.current.getSnapshot().data,...i};l.current.update(d),(I.current.stepsMap.get(n)?.persistenceMode||o)==="onChange"&&W("onChange",n,d)},[W]),K=m((n,i,o)=>{let{persistenceMode:p,stepsMap:d}=I.current,u=l.current.getSnapshot().data;if(J(u,n)===i)return;let w=ze(u,n,i);l.current.update(w);let g=I.current.currentStepId,A=d.get(g);g&&l.current.deleteError(g,n)&&(l.current.errorsMap.has(g)||j(Y=>{let U=new Set(Y);return U.delete(g),U})),g&&l.current.getSnapshot().errors[g]&&Be(()=>{Z(l.current.getSnapshot().errors)});let{config:V}=I.current;(A?.validationMode??V.validationMode??"onStepChange")==="onChange"&&(o?.debounceValidation?(X.current&&clearTimeout(X.current),X.current=setTimeout(()=>{try{g&&C(g,w).catch(E=>{console.error("[Wizard] Debounced validation failed:",E)})}catch(E){console.error("[Wizard] Error starting validation:",E)}},o.debounceValidation)):g&&C(g,w)),(A?.persistenceMode??p)==="onChange"&&g&&W("onChange",g,w)},[W,C]),Se=m((n,i)=>{let{config:o}=I.current,p=l.current.getSnapshot().data,d=i?.replace?n:{...p,...n};l.current.update(d),i?.persist&&o.steps.forEach(u=>{W("manual",u.id,d)})},[W]),fe=m((n,i)=>J(l.current.getSnapshot().data,n,i),[]),ge=m((n,i)=>{let{currentStepId:o}=I.current;o&&K(n,i)},[K]),me=m(async()=>{let{activeSteps:n}=I.current,i=l.current.getSnapshot().data,p=(await Promise.all(n.map(u=>C(u.id,i)))).every(Boolean),d=l.current.getSnapshot().errors;return{isValid:p,errors:d}},[C]),R=m(async n=>{let{activeStepsIndexMap:i,currentStepId:o,config:p,persistenceMode:d,visitedSteps:u,completedSteps:y,persistenceAdapter:w,stepsMap:g}=I.current,A=i.get(n)??-1;if(A===-1)return!1;let V=l.current.getSnapshot().data,ve=i.get(o)??-1,B=g.get(o);if(A>ve){let Y=B?.autoValidate??p.autoValidate??!1,U=B?.validationMode??p.validationMode??"onStepChange";if((Y||U==="onStepChange")&&o&&!await C(o,V))return!1}B&&o&&(B.persistenceMode??d)==="onStepChange"&&W("onStepChange",o,V);let E=new Set(u);return o&&E.add(o),h(E),c(n),d!=="manual"&&w.saveStep(Q,{currentStepId:n,visited:Array.from(E),completed:Array.from(y)}),p.onStepChange&&p.onStepChange(o||null,n,V),window.scrollTo(0,0),!0},[W,C]),he=m(async()=>{let{activeSteps:n,activeStepsIndexMap:i,currentStepId:o,completedSteps:p,persistenceMode:d}=I.current,u=i.get(o)??-1;if(u===-1||u===n.length-1)return;let y=n[u+1];if(y&&await R(y.id)){let g=new Set(p).add(o);v(g),d!=="manual"&&H.saveStep(Q,{currentStepId:y.id,visited:Array.from(new Set(S).add(o)),completed:Array.from(g)})}},[R]),Ie=m(()=>{let{activeSteps:n,activeStepsIndexMap:i,currentStepId:o}=I.current,p=i.get(o)??-1;if(p<=0)return;let d=n[p-1];d&&R(d.id)},[R]),ye=m(()=>{I.current.persistenceAdapter.clear()},[]),Te=m(n=>{let{config:i,currentStepId:o}=I.current,p=l.current.getSnapshot().data;if(n===!0){i.steps.forEach(u=>{W("manual",u.id,p)});return}if(!n){o&&W("manual",o,p);return}(Array.isArray(n)?n:[n]).forEach(u=>{W("manual",u,p)})},[W]),Le=M(()=>({currentStep:ce,currentStepIndex:_,isFirstStep:de,isLastStep:pe,isLoading:ae,isPending:oe,activeSteps:z,visitedSteps:S,completedSteps:f,errorSteps:b,store:l.current}),[ce,_,de,pe,ae,oe,z,S,f,b]),je=M(()=>({goToNextStep:he,goToPrevStep:Ie,goToStep:R,setStepData:le,handleStepChange:ge,validateStep:n=>C(n,l.current.getSnapshot().data),validateAll:me,save:Te,clearStorage:ye,setData:K,updateData:Se,getData:fe}),[he,Ie,R,le,ge,C,me,Te,ye,K,Se,fe]);return We(be.Provider,{value:Le,children:We(Ce.Provider,{value:je,children:a})})}function D(){let t=Pe(be);if(!t)throw new Error("useWizardState must be used within a WizardProvider");return t}function Me(t,e){let{store:r}=D(),a=P(null),s=P(null),c=e?.isEqual||Object.is,T=m(()=>{let h=r.getSnapshot().data;if(h===a.current)return s.current;let f=J(h,t);return s.current!==void 0&&c(s.current,f)?(a.current=h,s.current):(a.current=h,s.current=f,f)},[r,t,c]);return L(r.subscribe,T)}function Re(t){let{store:e}=D(),r=P(null),a=P(null),s=m(()=>{let T=e.getSnapshot().errors;if(T===r.current)return a.current;let S;return Object.values(T).forEach(h=>{let f=h;f[t]&&(S=f[t])}),r.current=T,a.current=S,S},[e,t]);return L(e.subscribe,s)}function ne(t,e){let{store:r}=D(),a=P(null),s=P(null),c=e?.isEqual||Object.is,T=m(()=>{let S=r.getSnapshot();if(S===a.current)return s.current;let h=t(S.data);return s.current!==null&&c(s.current,h)?(a.current=S,s.current):(a.current=S,s.current=h,h)},[r,t,c]);return L(r.subscribe,T)}function se(){let t=Pe(Ce);if(!t)throw new Error("useWizardActions must be used within a WizardProvider");return t}function O(){let t=D(),e=se(),r=ne(s=>s),a=L(t.store.subscribe,()=>t.store.getSnapshot().errors);return M(()=>({...t,...e,wizardData:r,allErrors:a}),[t,e,r,a])}import{useMemo as _e}from"react";import{jsx as Ae}from"react/jsx-runtime";var Ke=({wrapper:t})=>{let{currentStep:e}=O(),r=_e(()=>e?.component?e.component:null,[e]);if(!e||!r)return null;let a=Ae(r,{});return t?Ae(t,{children:a},e.id):a};var Ve=()=>O();import{jsx as Ue}from"react/jsx-runtime";function Ye(){return{WizardProvider:({config:f,initialData:v,children:b})=>Ue(Ee,{config:f,initialData:v,children:b}),useWizard:()=>Ve(),useWizardContext:()=>O(),useWizardValue:(f,v)=>Me(f,v),useWizardSelector:(f,v)=>ne(f,v),useWizardError:f=>Re(f),useWizardActions:()=>se(),useWizardState:()=>D(),createStep:f=>f}}var ke=class{constructor(e="wizard_"){x(this,"prefix");this.prefix=e}getKey(e){return`${this.prefix}${e}`}saveStep(e,r){if(!(typeof window>"u"))try{localStorage.setItem(this.getKey(e),JSON.stringify(r))}catch(a){console.warn("LocalStorageAdapter: Failed to save step",a)}}getStep(e){if(!(typeof window>"u"))try{let r=localStorage.getItem(this.getKey(e));return r?JSON.parse(r):void 0}catch(r){console.warn("LocalStorageAdapter: Failed to get step",r);return}}clear(){typeof window>"u"||Object.keys(localStorage).forEach(e=>{e.startsWith(this.prefix)&&localStorage.removeItem(e)})}};var De=class{constructor(e){x(this,"schema");this.schema=e}async validate(e){let r=await this.schema.safeParseAsync(e);if(r.success)return{isValid:!0};let a={};return r.error&&r.error.issues.forEach(s=>{let c=s.path.join(".");a[c]=s.message}),{isValid:!1,errors:a}}};var Oe=class{constructor(e){x(this,"schema");this.schema=e}async validate(e){try{return await this.schema.validate(e,{abortEarly:!1}),{isValid:!0}}catch(r){if(r&&typeof r=="object"&&"inner"in r){let a=r,s={};return a.inner.forEach(c=>{c.path&&(s[c.path]=c.message)}),{isValid:!1,errors:s}}throw r}}};export{ke as LocalStorageAdapter,$ as MemoryAdapter,Ee as WizardProvider,Ke as WizardStepRenderer,re as WizardStore,Oe as YupAdapter,De as ZodAdapter,Ye as createWizardFactory,J as getByPath,ze as setByPath,He as shallowEqual,xe as toPath,Ve as useWizard,se as useWizardActions,O as useWizardContext,Re as useWizardError,ne as useWizardSelector,D as useWizardState,Me as useWizardValue};