@wizzard-packages/react 0.1.1 → 0.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 +283 -1
- package/dist/index.cjs +612 -93
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +134 -23
- package/dist/index.d.ts +134 -23
- package/dist/index.js +611 -96
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,21 +1,102 @@
|
|
|
1
|
+
// src/factory.tsx
|
|
2
|
+
import {
|
|
3
|
+
shallowEqual as shallowEqual2
|
|
4
|
+
} from "@wizzard-packages/core";
|
|
5
|
+
|
|
1
6
|
// src/context/WizardContext.tsx
|
|
7
|
+
import {
|
|
8
|
+
WizardStore,
|
|
9
|
+
getByPath as getByPath2,
|
|
10
|
+
setByPath as setByPath2
|
|
11
|
+
} from "@wizzard-packages/core";
|
|
12
|
+
import { MemoryAdapter } from "@wizzard-packages/persistence";
|
|
2
13
|
import {
|
|
3
14
|
createContext,
|
|
15
|
+
useCallback,
|
|
4
16
|
useContext,
|
|
5
17
|
useEffect,
|
|
6
18
|
useMemo,
|
|
19
|
+
useRef,
|
|
7
20
|
useState,
|
|
8
|
-
|
|
9
|
-
useSyncExternalStore,
|
|
10
|
-
useRef
|
|
21
|
+
useSyncExternalStore
|
|
11
22
|
} from "react";
|
|
23
|
+
|
|
24
|
+
// src/internal/dependencies.ts
|
|
12
25
|
import {
|
|
13
|
-
WizardStore,
|
|
14
26
|
getByPath,
|
|
15
27
|
setByPath
|
|
16
28
|
} from "@wizzard-packages/core";
|
|
17
|
-
|
|
29
|
+
var applyStepDependencies = (config, store, baseData, changedPaths) => {
|
|
30
|
+
let currentData = { ...baseData };
|
|
31
|
+
const allClearedPaths = /* @__PURE__ */ new Set();
|
|
32
|
+
const snapshot = store.getSnapshot();
|
|
33
|
+
const nextCompletedSteps = new Set(snapshot.completedSteps);
|
|
34
|
+
const nextVisitedSteps = new Set(snapshot.visitedSteps);
|
|
35
|
+
let statusChanged = false;
|
|
36
|
+
const processDependencies = (paths) => {
|
|
37
|
+
const newlyClearedPaths = [];
|
|
38
|
+
config.steps.forEach((step) => {
|
|
39
|
+
const isDependent = step.dependsOn?.some(
|
|
40
|
+
(p) => paths.some((path) => path === p || p.startsWith(path + ".") || path.startsWith(p + "."))
|
|
41
|
+
);
|
|
42
|
+
if (!isDependent) return;
|
|
43
|
+
if (nextCompletedSteps.delete(step.id)) {
|
|
44
|
+
statusChanged = true;
|
|
45
|
+
}
|
|
46
|
+
if (nextVisitedSteps.delete(step.id)) {
|
|
47
|
+
statusChanged = true;
|
|
48
|
+
}
|
|
49
|
+
if (!step.clearData) return;
|
|
50
|
+
if (typeof step.clearData === "function") {
|
|
51
|
+
const patch = step.clearData(currentData, paths);
|
|
52
|
+
Object.keys(patch).forEach((key) => {
|
|
53
|
+
if (currentData[key] !== patch[key]) {
|
|
54
|
+
currentData[key] = patch[key];
|
|
55
|
+
newlyClearedPaths.push(key);
|
|
56
|
+
allClearedPaths.add(key);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
} else {
|
|
60
|
+
const pathsToClear = Array.isArray(step.clearData) ? step.clearData : [step.clearData];
|
|
61
|
+
pathsToClear.forEach((p) => {
|
|
62
|
+
const val = getByPath(currentData, p);
|
|
63
|
+
if (val !== void 0) {
|
|
64
|
+
currentData = setByPath(currentData, p, void 0);
|
|
65
|
+
newlyClearedPaths.push(p);
|
|
66
|
+
allClearedPaths.add(p);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (newlyClearedPaths.length > 0) {
|
|
72
|
+
processDependencies(newlyClearedPaths);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
processDependencies(changedPaths);
|
|
76
|
+
return {
|
|
77
|
+
newData: currentData,
|
|
78
|
+
hasClearing: allClearedPaths.size > 0,
|
|
79
|
+
clearedPaths: Array.from(allClearedPaths),
|
|
80
|
+
statusChanged,
|
|
81
|
+
nextCompletedSteps,
|
|
82
|
+
nextVisitedSteps
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/context/WizardContext.tsx
|
|
18
87
|
import { jsx } from "react/jsx-runtime";
|
|
88
|
+
var UNSET = /* @__PURE__ */ Symbol("wizard_unset");
|
|
89
|
+
var shallowEqual = (a, b) => {
|
|
90
|
+
if (a === b) return true;
|
|
91
|
+
if (!a || !b) return false;
|
|
92
|
+
const aKeys = Object.keys(a);
|
|
93
|
+
const bKeys = Object.keys(b);
|
|
94
|
+
if (aKeys.length !== bKeys.length) return false;
|
|
95
|
+
for (const key of aKeys) {
|
|
96
|
+
if (!Object.is(a[key], b[key])) return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
};
|
|
19
100
|
var WizardStateContext = createContext(void 0);
|
|
20
101
|
var WizardActionsContext = createContext(void 0);
|
|
21
102
|
var WizardStoreContext = createContext(void 0);
|
|
@@ -35,9 +116,10 @@ function WizardProvider({
|
|
|
35
116
|
return localConfig.persistence?.adapter || new MemoryAdapter();
|
|
36
117
|
}, [localConfig.persistence?.adapter]);
|
|
37
118
|
const persistenceMode = localConfig.persistence?.mode || "onStepChange";
|
|
38
|
-
const
|
|
119
|
+
const META_KEY2 = "__wizzard_meta__";
|
|
39
120
|
const snapshot = useSyncExternalStore(
|
|
40
121
|
storeRef.current.subscribe,
|
|
122
|
+
storeRef.current.getSnapshot,
|
|
41
123
|
storeRef.current.getSnapshot
|
|
42
124
|
);
|
|
43
125
|
const {
|
|
@@ -151,83 +233,23 @@ function WizardProvider({
|
|
|
151
233
|
const idx = activeStepsIndexMap2.get(currentStepId2) ?? -1;
|
|
152
234
|
if (idx > 0) await goToStep(activeSteps2[idx - 1].id);
|
|
153
235
|
}, [goToStep]);
|
|
154
|
-
const
|
|
155
|
-
(
|
|
156
|
-
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
let statusChanged = false;
|
|
162
|
-
const processDependencies = (changedPaths) => {
|
|
163
|
-
const newlyClearedPaths = [];
|
|
164
|
-
localConfig.steps.forEach((step) => {
|
|
165
|
-
const isDependent = step.dependsOn?.some(
|
|
166
|
-
(p) => changedPaths.some(
|
|
167
|
-
(path) => path === p || p.startsWith(path + ".") || path.startsWith(p + ".")
|
|
168
|
-
)
|
|
169
|
-
);
|
|
170
|
-
if (isDependent) {
|
|
171
|
-
if (nextComp.delete(step.id)) {
|
|
172
|
-
statusChanged = true;
|
|
173
|
-
}
|
|
174
|
-
if (nextVis.delete(step.id)) {
|
|
175
|
-
statusChanged = true;
|
|
176
|
-
}
|
|
177
|
-
if (step.clearData) {
|
|
178
|
-
if (typeof step.clearData === "function") {
|
|
179
|
-
const patch = step.clearData(currentData, changedPaths);
|
|
180
|
-
Object.keys(patch).forEach((key) => {
|
|
181
|
-
if (currentData[key] !== patch[key]) {
|
|
182
|
-
currentData[key] = patch[key];
|
|
183
|
-
newlyClearedPaths.push(key);
|
|
184
|
-
allClearedPaths.add(key);
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
} else {
|
|
188
|
-
const pathsToClear = Array.isArray(step.clearData) ? step.clearData : [step.clearData];
|
|
189
|
-
pathsToClear.forEach((p) => {
|
|
190
|
-
const val = getByPath(currentData, p);
|
|
191
|
-
if (val !== void 0) {
|
|
192
|
-
currentData = setByPath(currentData, p, void 0);
|
|
193
|
-
newlyClearedPaths.push(p);
|
|
194
|
-
allClearedPaths.add(p);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
});
|
|
201
|
-
if (newlyClearedPaths.length > 0) {
|
|
202
|
-
processDependencies(newlyClearedPaths);
|
|
203
|
-
}
|
|
204
|
-
};
|
|
205
|
-
processDependencies(paths);
|
|
236
|
+
const setData = useCallback(
|
|
237
|
+
(path, value, options) => {
|
|
238
|
+
const { stepsMap: stepsMap2, currentStepId: currentStepId2 } = stateRef.current;
|
|
239
|
+
const prevData = storeRef.current.getSnapshot().data;
|
|
240
|
+
if (getByPath2(prevData, path) === value) return;
|
|
241
|
+
const baseData = setByPath2(prevData, path, value);
|
|
242
|
+
const { newData, hasClearing, statusChanged, nextCompletedSteps, nextVisitedSteps } = applyStepDependencies(localConfig, storeRef.current, baseData, [path]);
|
|
206
243
|
if (statusChanged) {
|
|
207
244
|
storeRef.current.dispatch({
|
|
208
245
|
type: "SET_COMPLETED_STEPS",
|
|
209
|
-
payload: { steps:
|
|
246
|
+
payload: { steps: nextCompletedSteps }
|
|
210
247
|
});
|
|
211
248
|
storeRef.current.dispatch({
|
|
212
249
|
type: "SET_VISITED_STEPS",
|
|
213
|
-
payload: { steps:
|
|
250
|
+
payload: { steps: nextVisitedSteps }
|
|
214
251
|
});
|
|
215
252
|
}
|
|
216
|
-
return {
|
|
217
|
-
newData: currentData,
|
|
218
|
-
hasClearing: allClearedPaths.size > 0,
|
|
219
|
-
clearedPaths: Array.from(allClearedPaths)
|
|
220
|
-
};
|
|
221
|
-
},
|
|
222
|
-
[localConfig.steps]
|
|
223
|
-
);
|
|
224
|
-
const setData = useCallback(
|
|
225
|
-
(path, value, options) => {
|
|
226
|
-
const { stepsMap: stepsMap2, currentStepId: currentStepId2 } = stateRef.current;
|
|
227
|
-
const prevData = storeRef.current.getSnapshot().data;
|
|
228
|
-
if (getByPath(prevData, path) === value) return;
|
|
229
|
-
const baseData = setByPath(prevData, path, value);
|
|
230
|
-
const { newData, hasClearing } = handleStepDependencies([path], baseData);
|
|
231
253
|
if (!hasClearing) {
|
|
232
254
|
storeRef.current.dispatch({
|
|
233
255
|
type: "SET_DATA",
|
|
@@ -261,13 +283,23 @@ function WizardProvider({
|
|
|
261
283
|
}
|
|
262
284
|
}
|
|
263
285
|
},
|
|
264
|
-
[localConfig, validateStep
|
|
286
|
+
[localConfig, validateStep]
|
|
265
287
|
);
|
|
266
288
|
const updateData = useCallback(
|
|
267
289
|
(data2, options) => {
|
|
268
290
|
const prev = storeRef.current.getSnapshot().data;
|
|
269
291
|
const baseData = options?.replace ? data2 : { ...prev, ...data2 };
|
|
270
|
-
const { newData } =
|
|
292
|
+
const { newData, statusChanged, nextCompletedSteps, nextVisitedSteps } = applyStepDependencies(localConfig, storeRef.current, baseData, Object.keys(data2));
|
|
293
|
+
if (statusChanged) {
|
|
294
|
+
storeRef.current.dispatch({
|
|
295
|
+
type: "SET_COMPLETED_STEPS",
|
|
296
|
+
payload: { steps: nextCompletedSteps }
|
|
297
|
+
});
|
|
298
|
+
storeRef.current.dispatch({
|
|
299
|
+
type: "SET_VISITED_STEPS",
|
|
300
|
+
payload: { steps: nextVisitedSteps }
|
|
301
|
+
});
|
|
302
|
+
}
|
|
271
303
|
storeRef.current.update(newData, Object.keys(data2));
|
|
272
304
|
if (options?.persist) {
|
|
273
305
|
if (storeRef.current.save) {
|
|
@@ -275,7 +307,7 @@ function WizardProvider({
|
|
|
275
307
|
}
|
|
276
308
|
}
|
|
277
309
|
},
|
|
278
|
-
[
|
|
310
|
+
[localConfig]
|
|
279
311
|
);
|
|
280
312
|
const reset = useCallback(() => {
|
|
281
313
|
storeRef.current.setInitialData(initialData || {});
|
|
@@ -370,7 +402,7 @@ function WizardProvider({
|
|
|
370
402
|
reset,
|
|
371
403
|
setData,
|
|
372
404
|
updateData,
|
|
373
|
-
getData: (p, d) =>
|
|
405
|
+
getData: (p, d) => getByPath2(storeRef.current.getSnapshot().data, p, d),
|
|
374
406
|
updateConfig: (nc) => {
|
|
375
407
|
setLocalConfig((prev) => ({ ...prev, ...nc }));
|
|
376
408
|
}
|
|
@@ -421,7 +453,7 @@ function WizardProvider({
|
|
|
421
453
|
useEffect(() => {
|
|
422
454
|
if (hasHydratedRef.current) return;
|
|
423
455
|
hasHydratedRef.current = true;
|
|
424
|
-
const meta = persistenceAdapter.getStep(
|
|
456
|
+
const meta = persistenceAdapter.getStep(META_KEY2);
|
|
425
457
|
if (meta) {
|
|
426
458
|
if (meta.currentStepId) {
|
|
427
459
|
storeRef.current.dispatch({
|
|
@@ -483,21 +515,37 @@ function useWizardState() {
|
|
|
483
515
|
function useWizardValue(path, options) {
|
|
484
516
|
const store = useContext(WizardStoreContext);
|
|
485
517
|
if (!store) throw new Error("useWizardValue must be used within a WizardProvider");
|
|
486
|
-
const lastStateRef = useRef(
|
|
487
|
-
const lastValueRef = useRef(
|
|
518
|
+
const lastStateRef = useRef(UNSET);
|
|
519
|
+
const lastValueRef = useRef(UNSET);
|
|
520
|
+
const isEqual = typeof options === "function" ? options : options?.isEqual;
|
|
521
|
+
const isEqualRef = useRef(isEqual);
|
|
522
|
+
isEqualRef.current = isEqual;
|
|
488
523
|
const getSnapshot = useCallback(() => {
|
|
489
524
|
const data = store.getSnapshot().data;
|
|
490
|
-
if (data === lastStateRef.current
|
|
491
|
-
|
|
492
|
-
|
|
525
|
+
if (data === lastStateRef.current && lastValueRef.current !== UNSET) {
|
|
526
|
+
return lastValueRef.current;
|
|
527
|
+
}
|
|
528
|
+
const value = getByPath2(data, path);
|
|
529
|
+
if (lastValueRef.current !== UNSET && (isEqualRef.current || Object.is)(lastValueRef.current, value)) {
|
|
493
530
|
lastStateRef.current = data;
|
|
494
531
|
return lastValueRef.current;
|
|
495
532
|
}
|
|
496
533
|
lastStateRef.current = data;
|
|
497
534
|
lastValueRef.current = value;
|
|
498
535
|
return value;
|
|
499
|
-
}, [store, path
|
|
500
|
-
return useSyncExternalStore(store.subscribe, getSnapshot);
|
|
536
|
+
}, [store, path]);
|
|
537
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
538
|
+
}
|
|
539
|
+
function useWizardField(path, options) {
|
|
540
|
+
const value = useWizardValue(path, options);
|
|
541
|
+
const { setData } = useWizardActions();
|
|
542
|
+
const setValue = useCallback(
|
|
543
|
+
(next) => {
|
|
544
|
+
setData(path, next);
|
|
545
|
+
},
|
|
546
|
+
[setData, path]
|
|
547
|
+
);
|
|
548
|
+
return [value, setValue];
|
|
501
549
|
}
|
|
502
550
|
function useWizardError(path) {
|
|
503
551
|
const store = useContext(WizardStoreContext);
|
|
@@ -510,22 +558,33 @@ function useWizardError(path) {
|
|
|
510
558
|
}
|
|
511
559
|
return void 0;
|
|
512
560
|
}, [store, path]);
|
|
513
|
-
return useSyncExternalStore(store.subscribe, getSnapshot);
|
|
561
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
514
562
|
}
|
|
515
563
|
function useWizardSelector(selector, options) {
|
|
516
564
|
const store = useContext(WizardStoreContext);
|
|
517
565
|
if (!store) throw new Error("useWizardSelector must be used within a WizardProvider");
|
|
518
|
-
const
|
|
566
|
+
const selectorRef = useRef(selector);
|
|
567
|
+
selectorRef.current = selector;
|
|
568
|
+
const isEqual = typeof options === "function" ? options : options?.isEqual;
|
|
569
|
+
const isEqualRef = useRef(isEqual);
|
|
570
|
+
isEqualRef.current = isEqual;
|
|
571
|
+
const lastStateRef = useRef(UNSET);
|
|
572
|
+
const lastResultRef = useRef(UNSET);
|
|
519
573
|
const getSnapshot = useCallback(() => {
|
|
520
574
|
const full = store.getSnapshot();
|
|
521
|
-
|
|
522
|
-
|
|
575
|
+
if (full === lastStateRef.current && lastResultRef.current !== UNSET) {
|
|
576
|
+
return lastResultRef.current;
|
|
577
|
+
}
|
|
578
|
+
const res = selectorRef.current(full);
|
|
579
|
+
if (lastResultRef.current !== UNSET && (isEqualRef.current || Object.is)(lastResultRef.current, res)) {
|
|
580
|
+
lastStateRef.current = full;
|
|
523
581
|
return lastResultRef.current;
|
|
524
582
|
}
|
|
583
|
+
lastStateRef.current = full;
|
|
525
584
|
lastResultRef.current = res;
|
|
526
585
|
return res;
|
|
527
|
-
}, [store
|
|
528
|
-
return useSyncExternalStore(store.subscribe, getSnapshot);
|
|
586
|
+
}, [store]);
|
|
587
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
529
588
|
}
|
|
530
589
|
function useWizardActions() {
|
|
531
590
|
const context = useContext(WizardActionsContext);
|
|
@@ -558,6 +617,44 @@ function useWizardContext() {
|
|
|
558
617
|
[stateProps, actions, data, allErrors, errors, store]
|
|
559
618
|
);
|
|
560
619
|
}
|
|
620
|
+
function useWizardCurrentStep() {
|
|
621
|
+
return useWizardSelector((s) => s.currentStep);
|
|
622
|
+
}
|
|
623
|
+
function useWizardSteps() {
|
|
624
|
+
return useWizardSelector((s) => s.activeSteps);
|
|
625
|
+
}
|
|
626
|
+
function useWizardMeta() {
|
|
627
|
+
return useWizardSelector(
|
|
628
|
+
(s) => ({
|
|
629
|
+
currentStepId: s.currentStepId,
|
|
630
|
+
currentStepIndex: s.currentStepIndex,
|
|
631
|
+
isFirstStep: s.isFirstStep,
|
|
632
|
+
isLastStep: s.isLastStep,
|
|
633
|
+
isLoading: s.isLoading,
|
|
634
|
+
isBusy: s.isBusy,
|
|
635
|
+
isDirty: s.isDirty,
|
|
636
|
+
progress: s.progress,
|
|
637
|
+
activeStepsCount: s.activeStepsCount,
|
|
638
|
+
goToStepResult: s.goToStepResult
|
|
639
|
+
}),
|
|
640
|
+
{ isEqual: shallowEqual }
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
function useWizardAllErrors() {
|
|
644
|
+
return useWizardSelector((s) => s.errors, {
|
|
645
|
+
isEqual: shallowEqual
|
|
646
|
+
});
|
|
647
|
+
}
|
|
648
|
+
function useWizardFlatErrors() {
|
|
649
|
+
const allErrors = useWizardAllErrors();
|
|
650
|
+
return useMemo(() => {
|
|
651
|
+
const flat = {};
|
|
652
|
+
Object.values(allErrors).forEach((stepErrors) => {
|
|
653
|
+
Object.assign(flat, stepErrors);
|
|
654
|
+
});
|
|
655
|
+
return flat;
|
|
656
|
+
}, [allErrors]);
|
|
657
|
+
}
|
|
561
658
|
|
|
562
659
|
// src/hooks/useWizard.ts
|
|
563
660
|
var useWizard = () => {
|
|
@@ -570,9 +667,18 @@ function createWizardFactory() {
|
|
|
570
667
|
const WizardProvider2 = ({
|
|
571
668
|
config,
|
|
572
669
|
initialData,
|
|
670
|
+
initialStepId,
|
|
573
671
|
children
|
|
574
672
|
}) => {
|
|
575
|
-
return /* @__PURE__ */ jsx2(
|
|
673
|
+
return /* @__PURE__ */ jsx2(
|
|
674
|
+
WizardProvider,
|
|
675
|
+
{
|
|
676
|
+
config,
|
|
677
|
+
initialData,
|
|
678
|
+
initialStepId,
|
|
679
|
+
children
|
|
680
|
+
}
|
|
681
|
+
);
|
|
576
682
|
};
|
|
577
683
|
const useWizard2 = () => {
|
|
578
684
|
return useWizard();
|
|
@@ -583,9 +689,17 @@ function createWizardFactory() {
|
|
|
583
689
|
const useWizardValue2 = (path, options) => {
|
|
584
690
|
return useWizardValue(path, options);
|
|
585
691
|
};
|
|
692
|
+
const useWizardField2 = (path, options) => {
|
|
693
|
+
const value = useWizardValue2(path, options);
|
|
694
|
+
const { setData } = useWizardActions2();
|
|
695
|
+
return [value, (next) => setData(path, next)];
|
|
696
|
+
};
|
|
586
697
|
const useWizardSelector2 = (selector, options) => {
|
|
587
698
|
return useWizardSelector(selector, options);
|
|
588
699
|
};
|
|
700
|
+
const useWizardShallowSelector = (selector) => {
|
|
701
|
+
return useWizardSelector2(selector, shallowEqual2);
|
|
702
|
+
};
|
|
589
703
|
const useWizardError2 = (path) => {
|
|
590
704
|
return useWizardError(path);
|
|
591
705
|
};
|
|
@@ -604,7 +718,9 @@ function createWizardFactory() {
|
|
|
604
718
|
useWizard: useWizard2,
|
|
605
719
|
useWizardContext: useWizardContext2,
|
|
606
720
|
useWizardValue: useWizardValue2,
|
|
721
|
+
useWizardField: useWizardField2,
|
|
607
722
|
useWizardSelector: useWizardSelector2,
|
|
723
|
+
useWizardShallowSelector,
|
|
608
724
|
useWizardError: useWizardError2,
|
|
609
725
|
useWizardActions: useWizardActions2,
|
|
610
726
|
useWizardState: useWizardState2,
|
|
@@ -635,21 +751,420 @@ var WizardStepRenderer = ({
|
|
|
635
751
|
return content;
|
|
636
752
|
};
|
|
637
753
|
|
|
754
|
+
// src/store.ts
|
|
755
|
+
import {
|
|
756
|
+
WizardStore as WizardStore2,
|
|
757
|
+
getByPath as getByPath3,
|
|
758
|
+
setByPath as setByPath3
|
|
759
|
+
} from "@wizzard-packages/core";
|
|
760
|
+
import { MemoryAdapter as MemoryAdapter2 } from "@wizzard-packages/persistence";
|
|
761
|
+
import { useCallback as useCallback2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
762
|
+
var UNSET2 = /* @__PURE__ */ Symbol("wizard_store_unset");
|
|
763
|
+
var META_KEY = "__wizzard_meta__";
|
|
764
|
+
var applyStoredMeta = (store, adapter) => {
|
|
765
|
+
const meta = adapter.getStep(META_KEY);
|
|
766
|
+
if (!meta) return;
|
|
767
|
+
if (meta.currentStepId) {
|
|
768
|
+
store.dispatch({
|
|
769
|
+
type: "SET_CURRENT_STEP_ID",
|
|
770
|
+
payload: { stepId: meta.currentStepId }
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
if (meta.visited) {
|
|
774
|
+
store.dispatch({
|
|
775
|
+
type: "SET_VISITED_STEPS",
|
|
776
|
+
payload: { steps: new Set(meta.visited) }
|
|
777
|
+
});
|
|
778
|
+
}
|
|
779
|
+
if (meta.completed) {
|
|
780
|
+
store.dispatch({
|
|
781
|
+
type: "SET_COMPLETED_STEPS",
|
|
782
|
+
payload: { steps: new Set(meta.completed) }
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
if (meta.history) {
|
|
786
|
+
store.dispatch({
|
|
787
|
+
type: "SET_HISTORY",
|
|
788
|
+
payload: { history: meta.history }
|
|
789
|
+
});
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
var ensureInitialStep = (store, activeSteps, initialStepId) => {
|
|
793
|
+
const snapshot = store.getSnapshot();
|
|
794
|
+
if (snapshot.currentStepId || activeSteps.length === 0) return;
|
|
795
|
+
const startId = initialStepId && activeSteps.some((s) => s.id === initialStepId) ? initialStepId : activeSteps[0].id;
|
|
796
|
+
store.dispatch({
|
|
797
|
+
type: "SET_CURRENT_STEP_ID",
|
|
798
|
+
payload: { stepId: startId }
|
|
799
|
+
});
|
|
800
|
+
if (snapshot.history.length === 0) {
|
|
801
|
+
store.dispatch({
|
|
802
|
+
type: "SET_HISTORY",
|
|
803
|
+
payload: { history: [startId] }
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
const currentVisited = new Set(snapshot.visitedSteps);
|
|
807
|
+
if (!currentVisited.has(startId)) {
|
|
808
|
+
currentVisited.add(startId);
|
|
809
|
+
store.dispatch({
|
|
810
|
+
type: "SET_VISITED_STEPS",
|
|
811
|
+
payload: { steps: currentVisited }
|
|
812
|
+
});
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
var resolveAndSetActiveSteps = async (store, data, initialStepId) => {
|
|
816
|
+
const resolved = await store.resolveActiveSteps(data);
|
|
817
|
+
store.dispatch({
|
|
818
|
+
type: "SET_ACTIVE_STEPS",
|
|
819
|
+
payload: { steps: resolved }
|
|
820
|
+
});
|
|
821
|
+
ensureInitialStep(store, resolved, initialStepId);
|
|
822
|
+
const snapshot = store.getSnapshot();
|
|
823
|
+
if (resolved.length > 0 && snapshot.isLoading) {
|
|
824
|
+
store.updateMeta({ isLoading: false });
|
|
825
|
+
}
|
|
826
|
+
};
|
|
827
|
+
var createWizardActions = (store, config, persistenceAdapter, initialData) => {
|
|
828
|
+
let currentConfig = config;
|
|
829
|
+
const validationDebounceRef = { current: null };
|
|
830
|
+
const stepsMap = /* @__PURE__ */ new Map();
|
|
831
|
+
currentConfig.steps.forEach((step) => stepsMap.set(step.id, step));
|
|
832
|
+
const resolveActiveStepsHelper = (data) => store.resolveActiveSteps(data);
|
|
833
|
+
const validateStep = (stepId) => store.validateStep(stepId);
|
|
834
|
+
const setData = (path, value, options) => {
|
|
835
|
+
const prevData = store.getSnapshot().data;
|
|
836
|
+
if (getByPath3(prevData, path) === value) return;
|
|
837
|
+
const baseData = setByPath3(prevData, path, value);
|
|
838
|
+
const { newData, hasClearing, statusChanged, nextCompletedSteps, nextVisitedSteps } = applyStepDependencies(currentConfig, store, baseData, [path]);
|
|
839
|
+
if (statusChanged) {
|
|
840
|
+
store.dispatch({
|
|
841
|
+
type: "SET_COMPLETED_STEPS",
|
|
842
|
+
payload: { steps: nextCompletedSteps }
|
|
843
|
+
});
|
|
844
|
+
store.dispatch({
|
|
845
|
+
type: "SET_VISITED_STEPS",
|
|
846
|
+
payload: { steps: nextVisitedSteps }
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
if (!hasClearing) {
|
|
850
|
+
store.dispatch({
|
|
851
|
+
type: "SET_DATA",
|
|
852
|
+
payload: {
|
|
853
|
+
path,
|
|
854
|
+
value,
|
|
855
|
+
options: { ...options, __from_set_data__: true }
|
|
856
|
+
}
|
|
857
|
+
});
|
|
858
|
+
} else {
|
|
859
|
+
store.dispatch({
|
|
860
|
+
type: "UPDATE_DATA",
|
|
861
|
+
payload: {
|
|
862
|
+
data: newData,
|
|
863
|
+
options: { replace: true, __from_set_data__: true, path }
|
|
864
|
+
}
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
const snapshot = store.getSnapshot();
|
|
868
|
+
const currentStepId = snapshot.currentStepId;
|
|
869
|
+
if (currentStepId) {
|
|
870
|
+
store.deleteError(currentStepId, path);
|
|
871
|
+
const step = stepsMap.get(currentStepId);
|
|
872
|
+
const mode = step?.validationMode || currentConfig.validationMode || "onStepChange";
|
|
873
|
+
if (mode === "onChange") {
|
|
874
|
+
const debounceMs = options?.debounceValidation ?? currentConfig.validationDebounceTime ?? 300;
|
|
875
|
+
if (validationDebounceRef.current) {
|
|
876
|
+
clearTimeout(validationDebounceRef.current);
|
|
877
|
+
}
|
|
878
|
+
validationDebounceRef.current = setTimeout(() => {
|
|
879
|
+
void validateStep(currentStepId);
|
|
880
|
+
}, debounceMs);
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
const updateData = (data, options) => {
|
|
885
|
+
const prev = store.getSnapshot().data;
|
|
886
|
+
const baseData = options?.replace ? data : { ...prev, ...data };
|
|
887
|
+
const { newData, statusChanged, nextCompletedSteps, nextVisitedSteps } = applyStepDependencies(
|
|
888
|
+
currentConfig,
|
|
889
|
+
store,
|
|
890
|
+
baseData,
|
|
891
|
+
Object.keys(data)
|
|
892
|
+
);
|
|
893
|
+
if (statusChanged) {
|
|
894
|
+
store.dispatch({
|
|
895
|
+
type: "SET_COMPLETED_STEPS",
|
|
896
|
+
payload: { steps: nextCompletedSteps }
|
|
897
|
+
});
|
|
898
|
+
store.dispatch({
|
|
899
|
+
type: "SET_VISITED_STEPS",
|
|
900
|
+
payload: { steps: nextVisitedSteps }
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
store.update(newData, Object.keys(data));
|
|
904
|
+
if (options?.persist) {
|
|
905
|
+
store.save(store.getSnapshot().currentStepId);
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
const reset = () => {
|
|
909
|
+
store.setInitialData(initialData || {});
|
|
910
|
+
store.update(initialData || {});
|
|
911
|
+
store.updateErrors({});
|
|
912
|
+
store.dispatch({
|
|
913
|
+
type: "SET_VISITED_STEPS",
|
|
914
|
+
payload: { steps: /* @__PURE__ */ new Set() }
|
|
915
|
+
});
|
|
916
|
+
store.dispatch({
|
|
917
|
+
type: "SET_COMPLETED_STEPS",
|
|
918
|
+
payload: { steps: /* @__PURE__ */ new Set() }
|
|
919
|
+
});
|
|
920
|
+
store.dispatch({
|
|
921
|
+
type: "SET_ERROR_STEPS",
|
|
922
|
+
payload: { steps: /* @__PURE__ */ new Set() }
|
|
923
|
+
});
|
|
924
|
+
const snapshot = store.getSnapshot();
|
|
925
|
+
if (snapshot.activeSteps.length > 0) {
|
|
926
|
+
const startId = snapshot.activeSteps[0].id;
|
|
927
|
+
store.dispatch({
|
|
928
|
+
type: "SET_CURRENT_STEP_ID",
|
|
929
|
+
payload: { stepId: startId }
|
|
930
|
+
});
|
|
931
|
+
store.dispatch({
|
|
932
|
+
type: "SET_HISTORY",
|
|
933
|
+
payload: { history: [startId] }
|
|
934
|
+
});
|
|
935
|
+
} else {
|
|
936
|
+
store.dispatch({
|
|
937
|
+
type: "SET_CURRENT_STEP_ID",
|
|
938
|
+
payload: { stepId: "" }
|
|
939
|
+
});
|
|
940
|
+
store.dispatch({
|
|
941
|
+
type: "SET_HISTORY",
|
|
942
|
+
payload: { history: [] }
|
|
943
|
+
});
|
|
944
|
+
}
|
|
945
|
+
persistenceAdapter.clear();
|
|
946
|
+
currentConfig.analytics?.onEvent("wizard_reset", { data: initialData });
|
|
947
|
+
};
|
|
948
|
+
const goToStep = (stepId, providedActiveSteps, options) => {
|
|
949
|
+
return store.goToStep(stepId, {
|
|
950
|
+
validate: options?.validate ?? true,
|
|
951
|
+
providedActiveSteps
|
|
952
|
+
});
|
|
953
|
+
};
|
|
954
|
+
const goToNextStep = async () => {
|
|
955
|
+
const snapshot = store.getSnapshot();
|
|
956
|
+
const currentStepId = snapshot.currentStepId;
|
|
957
|
+
if (!currentStepId) return;
|
|
958
|
+
const step = stepsMap.get(currentStepId);
|
|
959
|
+
const shouldVal = step?.autoValidate ?? currentConfig.autoValidate ?? !!step?.validationAdapter;
|
|
960
|
+
if (shouldVal) {
|
|
961
|
+
const ok = await validateStep(currentStepId);
|
|
962
|
+
if (!ok) return;
|
|
963
|
+
}
|
|
964
|
+
const resolvedSteps = await resolveActiveStepsHelper(snapshot.data);
|
|
965
|
+
const idx = resolvedSteps.findIndex((s) => s.id === currentStepId);
|
|
966
|
+
if (idx !== -1 && idx < resolvedSteps.length - 1) {
|
|
967
|
+
const nextStepId = resolvedSteps[idx + 1].id;
|
|
968
|
+
const success = await goToStep(nextStepId, resolvedSteps, {
|
|
969
|
+
validate: false
|
|
970
|
+
});
|
|
971
|
+
if (success) {
|
|
972
|
+
const currentSnapshot = store.getSnapshot();
|
|
973
|
+
if (!currentSnapshot.errorSteps.has(currentStepId)) {
|
|
974
|
+
const nextComp = new Set(currentSnapshot.completedSteps);
|
|
975
|
+
nextComp.add(currentStepId);
|
|
976
|
+
store.dispatch({
|
|
977
|
+
type: "SET_COMPLETED_STEPS",
|
|
978
|
+
payload: { steps: nextComp }
|
|
979
|
+
});
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
};
|
|
984
|
+
const goToPrevStep = async () => {
|
|
985
|
+
const snapshot = store.getSnapshot();
|
|
986
|
+
const idx = snapshot.activeSteps.findIndex((s) => s.id === snapshot.currentStepId);
|
|
987
|
+
if (idx > 0) {
|
|
988
|
+
await goToStep(snapshot.activeSteps[idx - 1].id);
|
|
989
|
+
}
|
|
990
|
+
};
|
|
991
|
+
const setStepData = (_stepId, data) => {
|
|
992
|
+
const next = { ...store.getSnapshot().data, ...data };
|
|
993
|
+
store.update(next, Object.keys(data));
|
|
994
|
+
};
|
|
995
|
+
const handleStepChange = (field, value) => {
|
|
996
|
+
if (store.getSnapshot().currentStepId) {
|
|
997
|
+
setData(field, value);
|
|
998
|
+
}
|
|
999
|
+
};
|
|
1000
|
+
const validateAll = async () => {
|
|
1001
|
+
const result = await store.validateAll();
|
|
1002
|
+
return result;
|
|
1003
|
+
};
|
|
1004
|
+
const save = (ids) => {
|
|
1005
|
+
if (ids === true) {
|
|
1006
|
+
currentConfig.steps.forEach((s) => store.save(s.id));
|
|
1007
|
+
} else if (!ids) {
|
|
1008
|
+
store.save();
|
|
1009
|
+
} else {
|
|
1010
|
+
(Array.isArray(ids) ? ids : [ids]).forEach((id) => store.save(id));
|
|
1011
|
+
}
|
|
1012
|
+
};
|
|
1013
|
+
const clearStorage = () => persistenceAdapter.clear();
|
|
1014
|
+
const getData = (path, defaultValue) => {
|
|
1015
|
+
return getByPath3(store.getSnapshot().data, path, defaultValue);
|
|
1016
|
+
};
|
|
1017
|
+
const updateConfig = (nextConfig) => {
|
|
1018
|
+
currentConfig = { ...currentConfig, ...nextConfig };
|
|
1019
|
+
stepsMap.clear();
|
|
1020
|
+
currentConfig.steps.forEach((step) => stepsMap.set(step.id, step));
|
|
1021
|
+
store.updateMeta({ config: currentConfig });
|
|
1022
|
+
};
|
|
1023
|
+
return {
|
|
1024
|
+
goToNextStep,
|
|
1025
|
+
goToPrevStep,
|
|
1026
|
+
goToStep,
|
|
1027
|
+
setStepData,
|
|
1028
|
+
handleStepChange,
|
|
1029
|
+
validateStep,
|
|
1030
|
+
validateAll,
|
|
1031
|
+
save,
|
|
1032
|
+
clearStorage,
|
|
1033
|
+
reset,
|
|
1034
|
+
setData,
|
|
1035
|
+
updateData,
|
|
1036
|
+
getData,
|
|
1037
|
+
updateConfig
|
|
1038
|
+
};
|
|
1039
|
+
};
|
|
1040
|
+
var createWizardStore = (options) => {
|
|
1041
|
+
const initialData = options.initialData || {};
|
|
1042
|
+
const store = new WizardStore2(initialData, options.config.middlewares);
|
|
1043
|
+
const persistenceAdapter = options.config.persistence?.adapter || new MemoryAdapter2();
|
|
1044
|
+
store.injectPersistence(persistenceAdapter);
|
|
1045
|
+
store.dispatch({
|
|
1046
|
+
type: "INIT",
|
|
1047
|
+
payload: { data: initialData, config: options.config }
|
|
1048
|
+
});
|
|
1049
|
+
store.hydrate();
|
|
1050
|
+
applyStoredMeta(store, persistenceAdapter);
|
|
1051
|
+
void resolveAndSetActiveSteps(store, store.getSnapshot().data, options.initialStepId);
|
|
1052
|
+
return {
|
|
1053
|
+
store,
|
|
1054
|
+
actions: createWizardActions(store, options.config, persistenceAdapter, initialData)
|
|
1055
|
+
};
|
|
1056
|
+
};
|
|
1057
|
+
var useWizardStoreState = (store) => {
|
|
1058
|
+
return useSyncExternalStore2(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
1059
|
+
};
|
|
1060
|
+
var useWizardStoreValue = (store, path, options) => {
|
|
1061
|
+
const lastStateRef = useRef2(UNSET2);
|
|
1062
|
+
const lastValueRef = useRef2(UNSET2);
|
|
1063
|
+
const isEqual = typeof options === "function" ? options : options?.isEqual;
|
|
1064
|
+
const isEqualRef = useRef2(isEqual);
|
|
1065
|
+
isEqualRef.current = isEqual;
|
|
1066
|
+
const getSnapshot = useCallback2(() => {
|
|
1067
|
+
const data = store.getSnapshot().data;
|
|
1068
|
+
if (data === lastStateRef.current && lastValueRef.current !== UNSET2) {
|
|
1069
|
+
return lastValueRef.current;
|
|
1070
|
+
}
|
|
1071
|
+
const value = getByPath3(data, path);
|
|
1072
|
+
if (lastValueRef.current !== UNSET2 && (isEqualRef.current || Object.is)(lastValueRef.current, value)) {
|
|
1073
|
+
lastStateRef.current = data;
|
|
1074
|
+
return lastValueRef.current;
|
|
1075
|
+
}
|
|
1076
|
+
lastStateRef.current = data;
|
|
1077
|
+
lastValueRef.current = value;
|
|
1078
|
+
return value;
|
|
1079
|
+
}, [store, path]);
|
|
1080
|
+
return useSyncExternalStore2(store.subscribe, getSnapshot, getSnapshot);
|
|
1081
|
+
};
|
|
1082
|
+
var useWizardStoreField = (store, setData, path, options) => {
|
|
1083
|
+
const value = useWizardStoreValue(store, path, options);
|
|
1084
|
+
const setValue = useCallback2(
|
|
1085
|
+
(next) => {
|
|
1086
|
+
setData(path, next);
|
|
1087
|
+
},
|
|
1088
|
+
[setData, path]
|
|
1089
|
+
);
|
|
1090
|
+
return [value, setValue];
|
|
1091
|
+
};
|
|
1092
|
+
var useWizardStoreError = (store, path) => {
|
|
1093
|
+
const getSnapshot = useCallback2(() => {
|
|
1094
|
+
const errors = store.getSnapshot().errors;
|
|
1095
|
+
for (const [_, stepErrors] of Object.entries(errors)) {
|
|
1096
|
+
const typed = stepErrors;
|
|
1097
|
+
if (typed[path]) return typed[path];
|
|
1098
|
+
}
|
|
1099
|
+
return void 0;
|
|
1100
|
+
}, [store, path]);
|
|
1101
|
+
return useSyncExternalStore2(store.subscribe, getSnapshot, getSnapshot);
|
|
1102
|
+
};
|
|
1103
|
+
var useWizardStoreSelector = (store, selector, options) => {
|
|
1104
|
+
const selectorRef = useRef2(selector);
|
|
1105
|
+
selectorRef.current = selector;
|
|
1106
|
+
const isEqual = typeof options === "function" ? options : options?.isEqual;
|
|
1107
|
+
const isEqualRef = useRef2(isEqual);
|
|
1108
|
+
isEqualRef.current = isEqual;
|
|
1109
|
+
const lastStateRef = useRef2(UNSET2);
|
|
1110
|
+
const lastResultRef = useRef2(UNSET2);
|
|
1111
|
+
const getSnapshot = useCallback2(() => {
|
|
1112
|
+
const full = store.getSnapshot();
|
|
1113
|
+
if (full === lastStateRef.current && lastResultRef.current !== UNSET2) {
|
|
1114
|
+
return lastResultRef.current;
|
|
1115
|
+
}
|
|
1116
|
+
const res = selectorRef.current(full);
|
|
1117
|
+
if (lastResultRef.current !== UNSET2 && (isEqualRef.current || Object.is)(lastResultRef.current, res)) {
|
|
1118
|
+
lastStateRef.current = full;
|
|
1119
|
+
return lastResultRef.current;
|
|
1120
|
+
}
|
|
1121
|
+
lastStateRef.current = full;
|
|
1122
|
+
lastResultRef.current = res;
|
|
1123
|
+
return res;
|
|
1124
|
+
}, [store]);
|
|
1125
|
+
return useSyncExternalStore2(store.subscribe, getSnapshot, getSnapshot);
|
|
1126
|
+
};
|
|
1127
|
+
var createWizardHooks = (store, actions) => ({
|
|
1128
|
+
useWizardState: () => useWizardStoreState(store),
|
|
1129
|
+
useWizardValue: (path, options) => useWizardStoreValue(store, path, options),
|
|
1130
|
+
useWizardField: (path, options) => {
|
|
1131
|
+
if (!actions) {
|
|
1132
|
+
throw new Error("useWizardField requires actions. Pass actions to createWizardHooks.");
|
|
1133
|
+
}
|
|
1134
|
+
return useWizardStoreField(store, actions.setData, path, options);
|
|
1135
|
+
},
|
|
1136
|
+
useWizardError: (path) => useWizardStoreError(store, path),
|
|
1137
|
+
useWizardSelector: (selector, options) => useWizardStoreSelector(store, selector, options)
|
|
1138
|
+
});
|
|
1139
|
+
|
|
638
1140
|
// src/index.ts
|
|
639
|
-
import { WizardStore as
|
|
1141
|
+
import { WizardStore as WizardStore3 } from "@wizzard-packages/core";
|
|
640
1142
|
import { loggerMiddleware } from "@wizzard-packages/middleware";
|
|
641
1143
|
export {
|
|
642
1144
|
WizardProvider,
|
|
643
1145
|
WizardStepRenderer,
|
|
644
|
-
|
|
1146
|
+
WizardStore3 as WizardStore,
|
|
645
1147
|
createWizardFactory,
|
|
1148
|
+
createWizardHooks,
|
|
1149
|
+
createWizardStore,
|
|
646
1150
|
loggerMiddleware,
|
|
647
1151
|
useWizard,
|
|
648
1152
|
useWizardActions,
|
|
1153
|
+
useWizardAllErrors,
|
|
649
1154
|
useWizardContext,
|
|
1155
|
+
useWizardCurrentStep,
|
|
650
1156
|
useWizardError,
|
|
1157
|
+
useWizardField,
|
|
1158
|
+
useWizardFlatErrors,
|
|
1159
|
+
useWizardMeta,
|
|
651
1160
|
useWizardSelector,
|
|
652
1161
|
useWizardState,
|
|
1162
|
+
useWizardSteps,
|
|
1163
|
+
useWizardStoreError,
|
|
1164
|
+
useWizardStoreField,
|
|
1165
|
+
useWizardStoreSelector,
|
|
1166
|
+
useWizardStoreState,
|
|
1167
|
+
useWizardStoreValue,
|
|
653
1168
|
useWizardValue
|
|
654
1169
|
};
|
|
655
1170
|
//# sourceMappingURL=index.js.map
|