react-wizard-engine 0.1.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/CHANGELOG.md +12 -0
- package/LICENSE +21 -0
- package/README.md +100 -0
- package/dist/components-provider-bC3_zfyb.d.cts +29 -0
- package/dist/components-provider-bC3_zfyb.d.ts +29 -0
- package/dist/index.cjs +2349 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1083 -0
- package/dist/index.d.ts +1083 -0
- package/dist/index.js +2242 -0
- package/dist/index.js.map +1 -0
- package/dist/shadcn/index.cjs +311 -0
- package/dist/shadcn/index.cjs.map +1 -0
- package/dist/shadcn/index.d.cts +78 -0
- package/dist/shadcn/index.d.ts +78 -0
- package/dist/shadcn/index.js +270 -0
- package/dist/shadcn/index.js.map +1 -0
- package/package.json +101 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,2349 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
"use strict";
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
|
+
};
|
|
11
|
+
var __copyProps = (to, from, except, desc) => {
|
|
12
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
13
|
+
for (let key of __getOwnPropNames(from))
|
|
14
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
15
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
20
|
+
|
|
21
|
+
// src/index.ts
|
|
22
|
+
var src_exports = {};
|
|
23
|
+
__export(src_exports, {
|
|
24
|
+
TypedEmitter: () => TypedEmitter,
|
|
25
|
+
WizardActiveProgressStrategy: () => WizardActiveProgressStrategy,
|
|
26
|
+
WizardActiveStepRule: () => WizardActiveStepRule,
|
|
27
|
+
WizardBack: () => WizardBack,
|
|
28
|
+
WizardCategory: () => WizardCategory,
|
|
29
|
+
WizardCategoryDirection: () => WizardCategoryDirection,
|
|
30
|
+
WizardCompleteEvent: () => WizardCompleteEvent,
|
|
31
|
+
WizardCompleteTreeState: () => WizardCompleteTreeState,
|
|
32
|
+
WizardComponentsProvider: () => WizardComponentsProvider,
|
|
33
|
+
WizardConfig: () => WizardConfig,
|
|
34
|
+
WizardDefaultInitializer: () => WizardDefaultInitializer,
|
|
35
|
+
WizardDefaultVisibilityStrategy: () => WizardDefaultVisibilityStrategy,
|
|
36
|
+
WizardEngine: () => WizardEngine,
|
|
37
|
+
WizardEngineContext: () => WizardEngineContext,
|
|
38
|
+
WizardEngineRefCapture: () => WizardEngineRefCapture,
|
|
39
|
+
WizardError: () => WizardError,
|
|
40
|
+
WizardEvent: () => WizardEvent,
|
|
41
|
+
WizardEventType: () => WizardEventType,
|
|
42
|
+
WizardExitEvent: () => WizardExitEvent,
|
|
43
|
+
WizardExitTreeState: () => WizardExitTreeState,
|
|
44
|
+
WizardHeader: () => WizardHeader,
|
|
45
|
+
WizardInitializationError: () => WizardInitializationError,
|
|
46
|
+
WizardInitializer: () => WizardInitializer,
|
|
47
|
+
WizardInitializerService: () => WizardInitializerService,
|
|
48
|
+
WizardLastActiveVisibilityStrategy: () => WizardLastActiveVisibilityStrategy,
|
|
49
|
+
WizardListener: () => WizardListener,
|
|
50
|
+
WizardLog: () => WizardLog,
|
|
51
|
+
WizardNavigation: () => WizardNavigation,
|
|
52
|
+
WizardNavigationCancelledEvent: () => WizardNavigationCancelledEvent,
|
|
53
|
+
WizardNavigationEndEvent: () => WizardNavigationEndEvent,
|
|
54
|
+
WizardNavigationError: () => WizardNavigationError,
|
|
55
|
+
WizardNavigationErrorEvent: () => WizardNavigationErrorEvent,
|
|
56
|
+
WizardNavigationIgnoredEvent: () => WizardNavigationIgnoredEvent,
|
|
57
|
+
WizardNavigationService: () => WizardNavigationService,
|
|
58
|
+
WizardNavigationStartEvent: () => WizardNavigationStartEvent,
|
|
59
|
+
WizardNext: () => WizardNext,
|
|
60
|
+
WizardPassedPrevCategoriesRule: () => WizardPassedPrevCategoriesRule,
|
|
61
|
+
WizardPassedPrevStepsRule: () => WizardPassedPrevStepsRule,
|
|
62
|
+
WizardProgressStrategy: () => WizardProgressStrategy,
|
|
63
|
+
WizardProvider: () => WizardProvider,
|
|
64
|
+
WizardRail: () => WizardRail,
|
|
65
|
+
WizardResolveEndEvent: () => WizardResolveEndEvent,
|
|
66
|
+
WizardResolveStartEvent: () => WizardResolveStartEvent,
|
|
67
|
+
WizardResolverError: () => WizardResolverError,
|
|
68
|
+
WizardRule: () => WizardRule,
|
|
69
|
+
WizardScrollEndEvent: () => WizardScrollEndEvent,
|
|
70
|
+
WizardScrollStartEvent: () => WizardScrollStartEvent,
|
|
71
|
+
WizardScrollStrategy: () => WizardScrollStrategy,
|
|
72
|
+
WizardShownActiveCategoryRule: () => WizardShownActiveCategoryRule,
|
|
73
|
+
WizardSingleActiveCategoryRule: () => WizardSingleActiveCategoryRule,
|
|
74
|
+
WizardSmoothScrollStrategy: () => WizardSmoothScrollStrategy,
|
|
75
|
+
WizardStateService: () => WizardStateService,
|
|
76
|
+
WizardStep: () => WizardStep,
|
|
77
|
+
WizardStepHideEvent: () => WizardStepHideEvent,
|
|
78
|
+
WizardStepShowEvent: () => WizardStepShowEvent,
|
|
79
|
+
WizardStepper: () => WizardStepper,
|
|
80
|
+
WizardStepperDot: () => WizardStepperDot,
|
|
81
|
+
WizardStore: () => WizardStore,
|
|
82
|
+
WizardTreeStateBuilder: () => WizardTreeStateBuilder,
|
|
83
|
+
WizardVisibilityStrategy: () => WizardVisibilityStrategy,
|
|
84
|
+
buildWizardBranchState: () => buildWizardBranchState,
|
|
85
|
+
buildWizardCategoryState: () => buildWizardCategoryState,
|
|
86
|
+
buildWizardTreeState: () => buildWizardTreeState,
|
|
87
|
+
composeWizardProviders: () => composeWizardProviders,
|
|
88
|
+
scrollToStep: () => scrollToStep,
|
|
89
|
+
useWizard: () => useWizard,
|
|
90
|
+
useWizardCategoriesView: () => useWizardCategoriesView,
|
|
91
|
+
useWizardComponents: () => useWizardComponents,
|
|
92
|
+
useWizardEngineRef: () => useWizardEngineRef,
|
|
93
|
+
useWizardEvent: () => useWizardEvent,
|
|
94
|
+
useWizardStep: () => useWizardStep,
|
|
95
|
+
withConfig: () => withConfig,
|
|
96
|
+
withInitializer: () => withInitializer,
|
|
97
|
+
withListener: () => withListener,
|
|
98
|
+
withProgressStrategy: () => withProgressStrategy,
|
|
99
|
+
withScrollContainer: () => withScrollContainer,
|
|
100
|
+
withScrollStrategy: () => withScrollStrategy,
|
|
101
|
+
withVisibilityStrategy: () => withVisibilityStrategy,
|
|
102
|
+
wizardDefaultBranch: () => wizardDefaultBranch,
|
|
103
|
+
wizardDefaultConfig: () => wizardDefaultConfig,
|
|
104
|
+
wizardDefaultState: () => wizardDefaultState,
|
|
105
|
+
wizardRules: () => wizardRules
|
|
106
|
+
});
|
|
107
|
+
module.exports = __toCommonJS(src_exports);
|
|
108
|
+
|
|
109
|
+
// src/enums/wizard-event-type.enum.ts
|
|
110
|
+
var WizardEventType = /* @__PURE__ */ ((WizardEventType2) => {
|
|
111
|
+
WizardEventType2["Complete"] = "complete";
|
|
112
|
+
WizardEventType2["Exit"] = "exit";
|
|
113
|
+
WizardEventType2["NavigationCancelled"] = "navigation_cancelled";
|
|
114
|
+
WizardEventType2["NavigationEnd"] = "navigation_end";
|
|
115
|
+
WizardEventType2["NavigationError"] = "navigation_error";
|
|
116
|
+
WizardEventType2["NavigationIgnored"] = "navigation_ignored";
|
|
117
|
+
WizardEventType2["NavigationStart"] = "navigation_start";
|
|
118
|
+
WizardEventType2["ResolveEnd"] = "resolve_end";
|
|
119
|
+
WizardEventType2["ResolveStart"] = "resolve_start";
|
|
120
|
+
WizardEventType2["ScrollEnd"] = "scroll_end";
|
|
121
|
+
WizardEventType2["ScrollStart"] = "scroll_start";
|
|
122
|
+
WizardEventType2["StepHide"] = "step_hide";
|
|
123
|
+
WizardEventType2["StepShow"] = "step_show";
|
|
124
|
+
return WizardEventType2;
|
|
125
|
+
})(WizardEventType || {});
|
|
126
|
+
|
|
127
|
+
// src/core/utils/general/last.ts
|
|
128
|
+
var last = (arr) => arr[arr.length - 1];
|
|
129
|
+
|
|
130
|
+
// src/core/utils/general/to-array.ts
|
|
131
|
+
var toArray = (arr) => Array.isArray(arr) ? arr : [arr];
|
|
132
|
+
|
|
133
|
+
// src/core/utils/state/get-step-id.ts
|
|
134
|
+
var getStepId = (stepState) => {
|
|
135
|
+
const branchesId = stepState.branches.join(",");
|
|
136
|
+
return `${stepState.htmlIndex}:${branchesId}.${stepState.categoryId}.${stepState.id}`;
|
|
137
|
+
};
|
|
138
|
+
var getStepStateId = (stepState) => {
|
|
139
|
+
return `${+stepState.isShow}${+stepState.isCompleted}${+stepState.isSkipped}${+stepState.isActive}`;
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// src/core/utils/state/get-shape-id.ts
|
|
143
|
+
var getShapeId = (state, prefix = "") => {
|
|
144
|
+
const id = state.map(getStepId).join("|");
|
|
145
|
+
return prefix !== "" && prefix !== void 0 ? `@${prefix}@${id}` : id;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// src/core/utils/state/get-state-id.ts
|
|
149
|
+
var getStateId = (state, prefix = "") => {
|
|
150
|
+
const id = state.map(getStepStateId).join("|");
|
|
151
|
+
return prefix ? `@${prefix}@${id}` : id;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// src/core/builders/build-tree-state.ts
|
|
155
|
+
function buildWizardBranchState(id, categories) {
|
|
156
|
+
const shownCategories = [];
|
|
157
|
+
const activeCategories = [];
|
|
158
|
+
const completedCategories = [];
|
|
159
|
+
const skippedCategories = [];
|
|
160
|
+
const passedCategories = [];
|
|
161
|
+
for (const category of categories) {
|
|
162
|
+
if (category.isActive) activeCategories.push(category);
|
|
163
|
+
if (category.isShow) shownCategories.push(category);
|
|
164
|
+
if (category.isCompleted) completedCategories.push(category);
|
|
165
|
+
if (category.isSkipped) skippedCategories.push(category);
|
|
166
|
+
if (category.isPassed) passedCategories.push(category);
|
|
167
|
+
}
|
|
168
|
+
const activeCategory = activeCategories[0] ?? shownCategories[0];
|
|
169
|
+
const getShownCategoryIndex = (category) => shownCategories.indexOf(category);
|
|
170
|
+
const activeCategoryIndex = getShownCategoryIndex(activeCategory);
|
|
171
|
+
const firstCategory = shownCategories[0];
|
|
172
|
+
const lastCategory = last(shownCategories);
|
|
173
|
+
const prevCategory = activeCategoryIndex > 0 ? shownCategories[activeCategoryIndex - 1] ?? null : null;
|
|
174
|
+
const nextCategory = activeCategoryIndex >= 0 ? shownCategories[activeCategoryIndex + 1] ?? null : null;
|
|
175
|
+
const getShownCategories = (fromIndex, toIndex) => shownCategories.slice(fromIndex, toIndex + 1);
|
|
176
|
+
return {
|
|
177
|
+
activeCategories,
|
|
178
|
+
activeCategory,
|
|
179
|
+
activeCategoryIndex,
|
|
180
|
+
categories: [...categories],
|
|
181
|
+
completedCategories,
|
|
182
|
+
firstCategory,
|
|
183
|
+
getShownCategories,
|
|
184
|
+
getShownCategoryIndex,
|
|
185
|
+
id,
|
|
186
|
+
lastCategory,
|
|
187
|
+
nextCategory,
|
|
188
|
+
passedCategories,
|
|
189
|
+
prevCategory,
|
|
190
|
+
shownCategories,
|
|
191
|
+
skippedCategories
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function buildWizardCategoryState(id, htmlIndex, steps) {
|
|
195
|
+
const branches = steps[0]?.branches ?? [];
|
|
196
|
+
const shownSteps = [];
|
|
197
|
+
const activeSteps = [];
|
|
198
|
+
const completedSteps = [];
|
|
199
|
+
const skippedSteps = [];
|
|
200
|
+
const passedSteps = [];
|
|
201
|
+
for (const step of steps) {
|
|
202
|
+
if (step.isActive) activeSteps.push(step);
|
|
203
|
+
if (step.isShow) shownSteps.push(step);
|
|
204
|
+
if (step.isSkipped) skippedSteps.push(step);
|
|
205
|
+
if (step.isCompleted) completedSteps.push(step);
|
|
206
|
+
if (step.isSkipped || step.isCompleted) passedSteps.push(step);
|
|
207
|
+
}
|
|
208
|
+
const isActive = activeSteps.length > 0;
|
|
209
|
+
const isShow = shownSteps.length > 0;
|
|
210
|
+
const skippedAt = shownSteps.findIndex((step) => step.isSkipped);
|
|
211
|
+
const isSkipped = skippedAt !== -1;
|
|
212
|
+
const completedShownSteps = shownSteps.filter((step) => step.isCompleted || step.isSkipped);
|
|
213
|
+
const isCompleted = completedShownSteps.length === shownSteps.length && shownSteps.length > 0;
|
|
214
|
+
const isPassed = isCompleted || isSkipped;
|
|
215
|
+
const lastActiveStep = last(activeSteps);
|
|
216
|
+
const activeStepIndex = lastActiveStep ? shownSteps.indexOf(lastActiveStep) : -1;
|
|
217
|
+
const firstStep = shownSteps[0];
|
|
218
|
+
const lastStep = last(shownSteps);
|
|
219
|
+
const firstActiveStep = activeSteps[0];
|
|
220
|
+
const prevStep = activeStepIndex > 0 ? shownSteps[activeStepIndex - 1] ?? null : null;
|
|
221
|
+
const nextStep = activeStepIndex >= 0 ? shownSteps[activeStepIndex + 1] ?? null : null;
|
|
222
|
+
return {
|
|
223
|
+
activeStepIndex,
|
|
224
|
+
activeSteps,
|
|
225
|
+
branches: [...branches],
|
|
226
|
+
completedSteps,
|
|
227
|
+
firstActiveStep,
|
|
228
|
+
firstStep,
|
|
229
|
+
htmlIndex,
|
|
230
|
+
id,
|
|
231
|
+
isActive,
|
|
232
|
+
isCompleted,
|
|
233
|
+
isPassed,
|
|
234
|
+
isShow,
|
|
235
|
+
isSkipped,
|
|
236
|
+
lastActiveStep,
|
|
237
|
+
lastStep,
|
|
238
|
+
nextStep,
|
|
239
|
+
passedSteps,
|
|
240
|
+
prevStep,
|
|
241
|
+
shownSteps,
|
|
242
|
+
skippedAt,
|
|
243
|
+
skippedSteps,
|
|
244
|
+
steps: [...steps]
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function buildWizardTreeState(state, activeBranchId) {
|
|
248
|
+
const stepMap = {};
|
|
249
|
+
const categoryOrder = [];
|
|
250
|
+
const stepsByCategory = {};
|
|
251
|
+
for (const step of state) {
|
|
252
|
+
stepMap[step.id] = step;
|
|
253
|
+
if (!stepsByCategory[step.categoryId]) {
|
|
254
|
+
categoryOrder.push(step.categoryId);
|
|
255
|
+
stepsByCategory[step.categoryId] = [];
|
|
256
|
+
}
|
|
257
|
+
stepsByCategory[step.categoryId].push(step);
|
|
258
|
+
}
|
|
259
|
+
const categoryMap = {};
|
|
260
|
+
const categoriesByBranch = {};
|
|
261
|
+
for (const [index, categoryId] of categoryOrder.entries()) {
|
|
262
|
+
const categoryState = buildWizardCategoryState(
|
|
263
|
+
categoryId,
|
|
264
|
+
index,
|
|
265
|
+
stepsByCategory[categoryId]
|
|
266
|
+
);
|
|
267
|
+
categoryMap[categoryId] = categoryState;
|
|
268
|
+
for (const branchId of categoryState.branches) {
|
|
269
|
+
(categoriesByBranch[branchId] ??= []).push(categoryState);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
const branchMap = {};
|
|
273
|
+
const branches = [];
|
|
274
|
+
for (const branchId of Object.keys(categoriesByBranch)) {
|
|
275
|
+
const branchState = buildWizardBranchState(branchId, categoriesByBranch[branchId]);
|
|
276
|
+
branchMap[branchId] = branchState;
|
|
277
|
+
branches.push(branchState);
|
|
278
|
+
}
|
|
279
|
+
const activeBranch = pickActiveBranch(branches, branchMap, activeBranchId);
|
|
280
|
+
const activeCategory = activeBranch.activeCategory;
|
|
281
|
+
const activeSteps = activeCategory.activeSteps;
|
|
282
|
+
const lastActiveStep = last(activeSteps);
|
|
283
|
+
return {
|
|
284
|
+
activeBranch,
|
|
285
|
+
activeCategory,
|
|
286
|
+
activeSteps,
|
|
287
|
+
branches,
|
|
288
|
+
branchMap,
|
|
289
|
+
categoryMap,
|
|
290
|
+
id: getStateId(state),
|
|
291
|
+
lastActiveStep,
|
|
292
|
+
state: [...state],
|
|
293
|
+
stepMap
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
function pickActiveBranch(branches, branchMap, activeBranchId) {
|
|
297
|
+
if (branches.length === 1) {
|
|
298
|
+
return branches[0];
|
|
299
|
+
}
|
|
300
|
+
const activeBranches = branches.filter((branch) => branch.activeCategories.length > 0);
|
|
301
|
+
if (activeBranches.length === 1) {
|
|
302
|
+
return activeBranches[0];
|
|
303
|
+
}
|
|
304
|
+
if (activeBranchId && branchMap[activeBranchId]) {
|
|
305
|
+
return branchMap[activeBranchId];
|
|
306
|
+
}
|
|
307
|
+
return activeBranches[0] ?? branches[0];
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// src/core/builders/wizard-default.const.ts
|
|
311
|
+
var wizardDefaultState = {
|
|
312
|
+
isActive: false,
|
|
313
|
+
isCompleted: false,
|
|
314
|
+
isShow: true,
|
|
315
|
+
isSkipped: false
|
|
316
|
+
};
|
|
317
|
+
var wizardDefaultBranch = "main";
|
|
318
|
+
|
|
319
|
+
// src/core/errors/wizard.error.ts
|
|
320
|
+
var WizardError = class extends Error {
|
|
321
|
+
name = "WizardError";
|
|
322
|
+
constructor(message) {
|
|
323
|
+
super(message);
|
|
324
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
var WizardInitializationError = class extends WizardError {
|
|
328
|
+
name = "WizardInitializationError";
|
|
329
|
+
};
|
|
330
|
+
var WizardNavigationError = class extends WizardError {
|
|
331
|
+
name = "WizardNavigationError";
|
|
332
|
+
};
|
|
333
|
+
var WizardResolverError = class extends WizardError {
|
|
334
|
+
name = "WizardResolverError";
|
|
335
|
+
};
|
|
336
|
+
|
|
337
|
+
// src/core/rules/wizard-rule.ts
|
|
338
|
+
var WizardRule = class {
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
// src/core/rules/wizard-active-step.rule.ts
|
|
342
|
+
var WizardActiveStepRule = class extends WizardRule {
|
|
343
|
+
verify({ lastActiveStep }) {
|
|
344
|
+
if (!lastActiveStep) {
|
|
345
|
+
throw new WizardError("[WizardActiveStepRule]: There are no available active step in tree state.");
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
// src/core/rules/wizard-passed-prev-categories.rule.ts
|
|
351
|
+
var WizardPassedPrevCategoriesRule = class extends WizardRule {
|
|
352
|
+
verify({ activeBranch, activeCategory }) {
|
|
353
|
+
const prevCategories = activeBranch.shownCategories.slice(0, activeBranch.activeCategoryIndex);
|
|
354
|
+
const hasUnpassedPrevCategories = prevCategories.some((prevCategory) => !prevCategory.isPassed);
|
|
355
|
+
if (hasUnpassedPrevCategories) {
|
|
356
|
+
const prevCategoryNames = prevCategories.map((prevCategory) => prevCategory.id).join(",");
|
|
357
|
+
throw new WizardError(
|
|
358
|
+
`[WizardPassedPrevCategoriesRule]: Active category "${activeCategory.id}" has neither completed nor skipped prev categories. Please, skip or complete categories: "${prevCategoryNames}".`
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
// src/core/rules/wizard-passed-prev-steps.rule.ts
|
|
365
|
+
var WizardPassedPrevStepsRule = class extends WizardRule {
|
|
366
|
+
verify({ activeCategory }) {
|
|
367
|
+
const prevSteps = activeCategory.shownSteps.slice(0, activeCategory.activeStepIndex);
|
|
368
|
+
const prevUnpassedSteps = prevSteps.filter((prevStep) => !prevStep.isCompleted && !prevStep.isSkipped);
|
|
369
|
+
if (prevUnpassedSteps.length) {
|
|
370
|
+
const prevStepNames = prevUnpassedSteps.map((prevStep) => prevStep.id).join(",");
|
|
371
|
+
throw new WizardError(
|
|
372
|
+
`[WizardPassedPrevStepsRule]: Last active step "${activeCategory.lastActiveStep.id}" has not completed steps before. Please, complete or hide steps: "${prevStepNames}".`
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
// src/core/rules/wizard-shown-active-category.rule.ts
|
|
379
|
+
var WizardShownActiveCategoryRule = class extends WizardRule {
|
|
380
|
+
verify({ activeCategory }) {
|
|
381
|
+
if (!activeCategory.isShow) {
|
|
382
|
+
throw new WizardError(
|
|
383
|
+
`[WizardShownActiveCategoryRule]: Active category is hidden. Please, show category "${activeCategory.id}" to be able to activate it.`
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
// src/core/rules/wizard-single-active-category.rule.ts
|
|
390
|
+
var WizardSingleActiveCategoryRule = class extends WizardRule {
|
|
391
|
+
verify({ activeBranch }) {
|
|
392
|
+
if (activeBranch.activeCategories.length > 1) {
|
|
393
|
+
const activeCategoryNames = activeBranch.activeCategories.map((c) => c.id).join(",");
|
|
394
|
+
throw new WizardError(
|
|
395
|
+
`[WizardSingleActiveCategoryRule]: There are 2 active categories at the same time: ${activeCategoryNames}.`
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
// src/core/rules/wizard-rules.ts
|
|
402
|
+
var wizardRules = [
|
|
403
|
+
new WizardActiveStepRule(),
|
|
404
|
+
new WizardSingleActiveCategoryRule(),
|
|
405
|
+
new WizardShownActiveCategoryRule(),
|
|
406
|
+
new WizardPassedPrevStepsRule(),
|
|
407
|
+
new WizardPassedPrevCategoriesRule()
|
|
408
|
+
];
|
|
409
|
+
|
|
410
|
+
// src/core/state/wizard-tree.state.ts
|
|
411
|
+
var WizardCompleteTreeState = class {
|
|
412
|
+
constructor(originTree) {
|
|
413
|
+
this.originTree = originTree;
|
|
414
|
+
}
|
|
415
|
+
originTree;
|
|
416
|
+
state = "complete";
|
|
417
|
+
};
|
|
418
|
+
var WizardExitTreeState = class {
|
|
419
|
+
constructor(originTree) {
|
|
420
|
+
this.originTree = originTree;
|
|
421
|
+
}
|
|
422
|
+
originTree;
|
|
423
|
+
state = "exit";
|
|
424
|
+
};
|
|
425
|
+
|
|
426
|
+
// src/core/state/project-to-tree.ts
|
|
427
|
+
var projectToTree = (target) => target instanceof WizardExitTreeState || target instanceof WizardCompleteTreeState ? target.originTree : target;
|
|
428
|
+
|
|
429
|
+
// src/core/builders/wizard-tree-state.builder.ts
|
|
430
|
+
var WizardTreeStateBuilder = class _WizardTreeStateBuilder {
|
|
431
|
+
branch;
|
|
432
|
+
state;
|
|
433
|
+
constructor(treeState, branch) {
|
|
434
|
+
this.state = treeState.state.map((step) => ({ ...step }));
|
|
435
|
+
this.branch = branch ?? treeState.activeBranch.id;
|
|
436
|
+
}
|
|
437
|
+
static validate(tree) {
|
|
438
|
+
wizardRules.forEach((rule) => rule.verify(tree));
|
|
439
|
+
return tree;
|
|
440
|
+
}
|
|
441
|
+
/** Activate completed steps + the next step after the last completed one. */
|
|
442
|
+
activateCategory(categoryId) {
|
|
443
|
+
const shown = this.getCategoryStepStates(categoryId).filter((s) => s.isShow);
|
|
444
|
+
for (let i = 0; i < shown.length; i++) {
|
|
445
|
+
const step = shown[i];
|
|
446
|
+
const prev = shown[i - 1];
|
|
447
|
+
step.isActive = step.isCompleted || (prev?.isCompleted ?? true);
|
|
448
|
+
}
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
/** Activate first shown step of a category; deactivate the rest. */
|
|
452
|
+
activateFirstStep(categoryId) {
|
|
453
|
+
const shown = this.getCategoryStepStates(categoryId).filter((step) => step.isShow);
|
|
454
|
+
if (shown.length === 0) return this;
|
|
455
|
+
shown[0].isActive = true;
|
|
456
|
+
for (let i = 1; i < shown.length; i++) {
|
|
457
|
+
shown[i].isActive = false;
|
|
458
|
+
}
|
|
459
|
+
return this;
|
|
460
|
+
}
|
|
461
|
+
// ================
|
|
462
|
+
// Step actions
|
|
463
|
+
// ================
|
|
464
|
+
activateStep(step) {
|
|
465
|
+
this.getStepState(step).isActive = true;
|
|
466
|
+
return this;
|
|
467
|
+
}
|
|
468
|
+
build() {
|
|
469
|
+
const tree = buildWizardTreeState(this.state, this.branch);
|
|
470
|
+
_WizardTreeStateBuilder.validate(tree);
|
|
471
|
+
return Object.freeze(tree);
|
|
472
|
+
}
|
|
473
|
+
/** Build a terminal complete tree (skips rule validation since the tree is sealed). */
|
|
474
|
+
buildComplete() {
|
|
475
|
+
const tree = buildWizardTreeState(this.state, this.branch);
|
|
476
|
+
return Object.freeze(new WizardCompleteTreeState(tree));
|
|
477
|
+
}
|
|
478
|
+
/** Build a terminal exit tree (skips rule validation since the tree is sealed). */
|
|
479
|
+
buildExit() {
|
|
480
|
+
const tree = buildWizardTreeState(this.state, this.branch);
|
|
481
|
+
return Object.freeze(new WizardExitTreeState(tree));
|
|
482
|
+
}
|
|
483
|
+
completeCategory(categoryId) {
|
|
484
|
+
this.getCategoryStepStates(categoryId).forEach((step) => step.isCompleted = true);
|
|
485
|
+
return this;
|
|
486
|
+
}
|
|
487
|
+
// ================
|
|
488
|
+
// Category actions
|
|
489
|
+
// ================
|
|
490
|
+
completeStep(step) {
|
|
491
|
+
this.getStepState(step).isCompleted = true;
|
|
492
|
+
return this;
|
|
493
|
+
}
|
|
494
|
+
deactivateCategory(categoryId) {
|
|
495
|
+
this.getCategoryStepStates(categoryId).forEach((step) => step.isActive = false);
|
|
496
|
+
return this;
|
|
497
|
+
}
|
|
498
|
+
deactivateStep(step) {
|
|
499
|
+
this.getStepState(step).isActive = false;
|
|
500
|
+
return this;
|
|
501
|
+
}
|
|
502
|
+
getCategoryStepStates(categoryId) {
|
|
503
|
+
return this.state.filter((step) => step.categoryId === categoryId);
|
|
504
|
+
}
|
|
505
|
+
/** Reset every step in the tree to `wizardDefaultState`. */
|
|
506
|
+
getResetTree() {
|
|
507
|
+
this.state.forEach((step) => Object.assign(step, wizardDefaultState));
|
|
508
|
+
const firstStep = this.state[0];
|
|
509
|
+
if (firstStep) {
|
|
510
|
+
firstStep.isActive = true;
|
|
511
|
+
}
|
|
512
|
+
return Object.freeze(buildWizardTreeState(this.state, this.branch));
|
|
513
|
+
}
|
|
514
|
+
getStepState(step) {
|
|
515
|
+
const found = this.state.find((s) => s.id === step);
|
|
516
|
+
if (!found) {
|
|
517
|
+
throw new Error(`[WizardTreeStateBuilder] Unknown step "${step}"`);
|
|
518
|
+
}
|
|
519
|
+
return found;
|
|
520
|
+
}
|
|
521
|
+
hideCategory(categoryId) {
|
|
522
|
+
this.getCategoryStepStates(categoryId).forEach((step) => step.isShow = false);
|
|
523
|
+
return this;
|
|
524
|
+
}
|
|
525
|
+
hideStep(step) {
|
|
526
|
+
this.getStepState(step).isShow = false;
|
|
527
|
+
return this;
|
|
528
|
+
}
|
|
529
|
+
/** Reset category steps from `from` index onward; deactivate all but first. */
|
|
530
|
+
resetCategory(categoryId, from = 0) {
|
|
531
|
+
const slice = this.getCategoryStepStates(categoryId).slice(from);
|
|
532
|
+
slice.forEach((step, index) => {
|
|
533
|
+
step.isActive = step.isActive && index === 0;
|
|
534
|
+
step.isCompleted = false;
|
|
535
|
+
});
|
|
536
|
+
return this;
|
|
537
|
+
}
|
|
538
|
+
showCategory(categoryId) {
|
|
539
|
+
this.getCategoryStepStates(categoryId).forEach((step) => step.isShow = true);
|
|
540
|
+
return this;
|
|
541
|
+
}
|
|
542
|
+
// ================
|
|
543
|
+
// Build / reset
|
|
544
|
+
// ================
|
|
545
|
+
showStep(step) {
|
|
546
|
+
this.getStepState(step).isShow = true;
|
|
547
|
+
return this;
|
|
548
|
+
}
|
|
549
|
+
skipCategory(categoryId) {
|
|
550
|
+
this.getCategoryStepStates(categoryId).forEach((step) => step.isSkipped = true);
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
skipCategoryUncompleted(categoryId) {
|
|
554
|
+
this.getCategoryStepStates(categoryId).filter((step) => !step.isCompleted).forEach((step) => step.isSkipped = true);
|
|
555
|
+
return this;
|
|
556
|
+
}
|
|
557
|
+
uncompleteStep(step) {
|
|
558
|
+
this.getStepState(step).isCompleted = false;
|
|
559
|
+
return this;
|
|
560
|
+
}
|
|
561
|
+
unskipCategory(categoryId) {
|
|
562
|
+
this.getCategoryStepStates(categoryId).forEach((step) => step.isSkipped = false);
|
|
563
|
+
return this;
|
|
564
|
+
}
|
|
565
|
+
};
|
|
566
|
+
|
|
567
|
+
// src/core/compose-wizard-providers.ts
|
|
568
|
+
function composeWizardProviders(...mods) {
|
|
569
|
+
const props = {
|
|
570
|
+
config: {},
|
|
571
|
+
initializers: [],
|
|
572
|
+
listeners: [],
|
|
573
|
+
strategies: {}
|
|
574
|
+
};
|
|
575
|
+
for (const mod of mods) {
|
|
576
|
+
mod(props);
|
|
577
|
+
}
|
|
578
|
+
return props;
|
|
579
|
+
}
|
|
580
|
+
var withConfig = (options) => (props) => {
|
|
581
|
+
props.config = { ...props.config, ...options };
|
|
582
|
+
};
|
|
583
|
+
var withInitializer = (initializer) => (props) => {
|
|
584
|
+
props.initializers.push(initializer);
|
|
585
|
+
};
|
|
586
|
+
var withListener = (listener) => (props) => {
|
|
587
|
+
props.listeners.push(listener);
|
|
588
|
+
};
|
|
589
|
+
var withScrollStrategy = (strategy) => (props) => {
|
|
590
|
+
props.strategies.scroll = strategy;
|
|
591
|
+
};
|
|
592
|
+
var withScrollContainer = (container) => (props) => {
|
|
593
|
+
props.scrollContainer = container;
|
|
594
|
+
};
|
|
595
|
+
var withProgressStrategy = (strategy) => (props) => {
|
|
596
|
+
props.strategies.progress = strategy;
|
|
597
|
+
};
|
|
598
|
+
var withVisibilityStrategy = (strategy) => (props) => {
|
|
599
|
+
props.strategies.visibility = strategy;
|
|
600
|
+
};
|
|
601
|
+
|
|
602
|
+
// src/core/events/wizard-events.ts
|
|
603
|
+
var WizardEvent = class {
|
|
604
|
+
/** @param navigation - Specific navigation data associated with event */
|
|
605
|
+
constructor(navigation) {
|
|
606
|
+
this.navigation = navigation;
|
|
607
|
+
}
|
|
608
|
+
navigation;
|
|
609
|
+
};
|
|
610
|
+
var WizardCompleteEvent = class extends WizardEvent {
|
|
611
|
+
constructor(navigation) {
|
|
612
|
+
super(navigation);
|
|
613
|
+
this.navigation = navigation;
|
|
614
|
+
}
|
|
615
|
+
navigation;
|
|
616
|
+
type = "complete" /* Complete */;
|
|
617
|
+
};
|
|
618
|
+
var WizardExitEvent = class extends WizardEvent {
|
|
619
|
+
type = "exit" /* Exit */;
|
|
620
|
+
};
|
|
621
|
+
var WizardNavigationCancelledEvent = class extends WizardEvent {
|
|
622
|
+
constructor(navigation, reason) {
|
|
623
|
+
super(navigation);
|
|
624
|
+
this.navigation = navigation;
|
|
625
|
+
this.reason = reason;
|
|
626
|
+
}
|
|
627
|
+
navigation;
|
|
628
|
+
reason;
|
|
629
|
+
type = "navigation_cancelled" /* NavigationCancelled */;
|
|
630
|
+
};
|
|
631
|
+
var WizardNavigationEndEvent = class extends WizardEvent {
|
|
632
|
+
constructor(navigation) {
|
|
633
|
+
super(navigation);
|
|
634
|
+
this.navigation = navigation;
|
|
635
|
+
}
|
|
636
|
+
navigation;
|
|
637
|
+
type = "navigation_end" /* NavigationEnd */;
|
|
638
|
+
};
|
|
639
|
+
var WizardNavigationErrorEvent = class extends WizardEvent {
|
|
640
|
+
constructor(navigation) {
|
|
641
|
+
super(navigation);
|
|
642
|
+
this.navigation = navigation;
|
|
643
|
+
}
|
|
644
|
+
navigation;
|
|
645
|
+
type = "navigation_error" /* NavigationError */;
|
|
646
|
+
};
|
|
647
|
+
var WizardNavigationIgnoredEvent = class extends WizardEvent {
|
|
648
|
+
constructor(navigation, reason) {
|
|
649
|
+
super(navigation);
|
|
650
|
+
this.navigation = navigation;
|
|
651
|
+
this.reason = reason;
|
|
652
|
+
}
|
|
653
|
+
navigation;
|
|
654
|
+
reason;
|
|
655
|
+
type = "navigation_ignored" /* NavigationIgnored */;
|
|
656
|
+
};
|
|
657
|
+
var WizardNavigationStartEvent = class extends WizardEvent {
|
|
658
|
+
constructor(navigation) {
|
|
659
|
+
super(navigation);
|
|
660
|
+
this.navigation = navigation;
|
|
661
|
+
}
|
|
662
|
+
navigation;
|
|
663
|
+
type = "navigation_start" /* NavigationStart */;
|
|
664
|
+
};
|
|
665
|
+
var WizardResolveEndEvent = class extends WizardEvent {
|
|
666
|
+
constructor(navigation) {
|
|
667
|
+
super(navigation);
|
|
668
|
+
this.navigation = navigation;
|
|
669
|
+
}
|
|
670
|
+
navigation;
|
|
671
|
+
type = "resolve_end" /* ResolveEnd */;
|
|
672
|
+
};
|
|
673
|
+
var WizardResolveStartEvent = class extends WizardEvent {
|
|
674
|
+
constructor(navigation) {
|
|
675
|
+
super(navigation);
|
|
676
|
+
this.navigation = navigation;
|
|
677
|
+
}
|
|
678
|
+
navigation;
|
|
679
|
+
type = "resolve_start" /* ResolveStart */;
|
|
680
|
+
};
|
|
681
|
+
var WizardScrollEndEvent = class extends WizardEvent {
|
|
682
|
+
constructor(navigation) {
|
|
683
|
+
super(navigation);
|
|
684
|
+
this.navigation = navigation;
|
|
685
|
+
}
|
|
686
|
+
navigation;
|
|
687
|
+
type = "scroll_end" /* ScrollEnd */;
|
|
688
|
+
};
|
|
689
|
+
var WizardScrollStartEvent = class extends WizardEvent {
|
|
690
|
+
constructor(navigation) {
|
|
691
|
+
super(navigation);
|
|
692
|
+
this.navigation = navigation;
|
|
693
|
+
}
|
|
694
|
+
navigation;
|
|
695
|
+
type = "scroll_start" /* ScrollStart */;
|
|
696
|
+
};
|
|
697
|
+
var WizardStepHideEvent = class extends WizardEvent {
|
|
698
|
+
constructor(navigation) {
|
|
699
|
+
super(navigation);
|
|
700
|
+
this.navigation = navigation;
|
|
701
|
+
}
|
|
702
|
+
navigation;
|
|
703
|
+
type = "step_hide" /* StepHide */;
|
|
704
|
+
};
|
|
705
|
+
var WizardStepShowEvent = class extends WizardEvent {
|
|
706
|
+
constructor(navigation) {
|
|
707
|
+
super(navigation);
|
|
708
|
+
this.navigation = navigation;
|
|
709
|
+
}
|
|
710
|
+
navigation;
|
|
711
|
+
type = "step_show" /* StepShow */;
|
|
712
|
+
};
|
|
713
|
+
|
|
714
|
+
// src/core/initializers/wizard-initializer.ts
|
|
715
|
+
var WizardInitializer = class {
|
|
716
|
+
};
|
|
717
|
+
|
|
718
|
+
// src/core/initializers/wizard-default.initializer.ts
|
|
719
|
+
var WizardDefaultInitializer = class extends WizardInitializer {
|
|
720
|
+
getState(tree) {
|
|
721
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
722
|
+
if (tree.activeSteps.length > 0) {
|
|
723
|
+
return builder.build();
|
|
724
|
+
}
|
|
725
|
+
const firstUnpassed = tree.activeBranch.shownCategories.find((category) => !category.isPassed) ?? tree.activeCategory;
|
|
726
|
+
return builder.activateCategory(firstUnpassed.id).build();
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
|
|
730
|
+
// src/core/listeners/wizard-listener.ts
|
|
731
|
+
var WizardListener = class {
|
|
732
|
+
onDestroy(_tree) {
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
onEvent(_event) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
onInit(_tree) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
onTreeChange(_tree) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
// src/core/navigation/initializer-service.ts
|
|
747
|
+
var WizardInitializerService = class {
|
|
748
|
+
constructor(wizardLog, wizardInitializers) {
|
|
749
|
+
this.wizardLog = wizardLog;
|
|
750
|
+
this.wizardInitializers = wizardInitializers;
|
|
751
|
+
}
|
|
752
|
+
wizardLog;
|
|
753
|
+
wizardInitializers;
|
|
754
|
+
async getInitialState(state, activeBranch) {
|
|
755
|
+
const htmlTree = buildWizardTreeState(state, activeBranch);
|
|
756
|
+
this.wizardLog.trace("HTML tree", htmlTree);
|
|
757
|
+
try {
|
|
758
|
+
this.validateHtmlTree(htmlTree);
|
|
759
|
+
this.validateActiveBranch(htmlTree, activeBranch);
|
|
760
|
+
let tree = htmlTree;
|
|
761
|
+
for (const initializer of this.wizardInitializers) {
|
|
762
|
+
tree = await initializer.getState(tree, htmlTree);
|
|
763
|
+
this.wizardLog.trace("Initializer", initializer, tree);
|
|
764
|
+
}
|
|
765
|
+
return tree;
|
|
766
|
+
} catch (e) {
|
|
767
|
+
this.wizardLog.error(e);
|
|
768
|
+
return htmlTree;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
validateActiveBranch(htmlTree, activeBranch) {
|
|
772
|
+
if (!activeBranch) return;
|
|
773
|
+
if (!htmlTree.branchMap[activeBranch]) {
|
|
774
|
+
const branchIds = htmlTree.branches.map((branch) => branch.id).join(",");
|
|
775
|
+
throw new WizardError(
|
|
776
|
+
`[WizardInitializerService02]: There is no branch with id "${activeBranch}". List of available branches: ${branchIds}.`
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
validateHtmlTree(htmlTree) {
|
|
781
|
+
const seen = /* @__PURE__ */ new Set();
|
|
782
|
+
const duplicates = [];
|
|
783
|
+
for (const s of htmlTree.state) {
|
|
784
|
+
if (seen.has(s.id)) duplicates.push(s.id);
|
|
785
|
+
else seen.add(s.id);
|
|
786
|
+
}
|
|
787
|
+
if (duplicates.length > 0) {
|
|
788
|
+
throw new WizardInitializationError(
|
|
789
|
+
`[WizardInitializerService01]: There are steps with the same ID: ${duplicates.join(",")}.`
|
|
790
|
+
);
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
|
|
795
|
+
// src/core/navigation/typed-emitter.ts
|
|
796
|
+
var TypedEmitter = class {
|
|
797
|
+
map = /* @__PURE__ */ new Map();
|
|
798
|
+
dispose() {
|
|
799
|
+
this.map.clear();
|
|
800
|
+
}
|
|
801
|
+
emit(event) {
|
|
802
|
+
const specific = this.map.get(event.type);
|
|
803
|
+
if (specific) {
|
|
804
|
+
for (const handler of [...specific]) handler(event);
|
|
805
|
+
}
|
|
806
|
+
const wildcard = this.map.get("*");
|
|
807
|
+
if (wildcard) {
|
|
808
|
+
for (const handler of [...wildcard]) handler(event);
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
on(type, handler) {
|
|
812
|
+
let set = this.map.get(type);
|
|
813
|
+
if (!set) {
|
|
814
|
+
set = /* @__PURE__ */ new Set();
|
|
815
|
+
this.map.set(type, set);
|
|
816
|
+
}
|
|
817
|
+
set.add(handler);
|
|
818
|
+
return () => {
|
|
819
|
+
set.delete(handler);
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
};
|
|
823
|
+
|
|
824
|
+
// src/core/navigation/wizard-navigation.ts
|
|
825
|
+
var WizardNavigation = class _WizardNavigation {
|
|
826
|
+
constructor(from, to) {
|
|
827
|
+
this.from = from;
|
|
828
|
+
this.to = to;
|
|
829
|
+
const toTree = projectToTree(to);
|
|
830
|
+
this.id = `${from.id}=>${toTree.id}`;
|
|
831
|
+
this.direction = _WizardNavigation.getDirection(from, toTree);
|
|
832
|
+
this.isSkipping = _WizardNavigation.isSkipping(from, toTree, this.direction);
|
|
833
|
+
this.isCategoryChanging = _WizardNavigation.isCategoryChanging(from, toTree);
|
|
834
|
+
this.isScrollChanging = _WizardNavigation.isScrollChanging(from, toTree);
|
|
835
|
+
this.isExit = to instanceof WizardExitTreeState;
|
|
836
|
+
this.isComplete = to instanceof WizardCompleteTreeState;
|
|
837
|
+
}
|
|
838
|
+
from;
|
|
839
|
+
to;
|
|
840
|
+
direction;
|
|
841
|
+
id;
|
|
842
|
+
isCategoryChanging;
|
|
843
|
+
isComplete;
|
|
844
|
+
isExit;
|
|
845
|
+
isScrollChanging;
|
|
846
|
+
isSkipping;
|
|
847
|
+
static getDirection(from, to) {
|
|
848
|
+
if (_WizardNavigation.isCategoryChanging(from, to)) {
|
|
849
|
+
return from.activeCategory.htmlIndex > to.activeCategory.htmlIndex ? "back" : "next";
|
|
850
|
+
}
|
|
851
|
+
if (from.activeCategory.activeStepIndex === to.activeCategory.activeStepIndex) {
|
|
852
|
+
return to.activeCategory.id === to.activeBranch.firstCategory.id ? "back" : "next";
|
|
853
|
+
}
|
|
854
|
+
return from.activeCategory.activeStepIndex > to.activeCategory.activeStepIndex ? "back" : "next";
|
|
855
|
+
}
|
|
856
|
+
static isCategoryChanging(from, to) {
|
|
857
|
+
return from.activeCategory.id !== to.activeCategory.id;
|
|
858
|
+
}
|
|
859
|
+
static isScrollChanging(from, to) {
|
|
860
|
+
const isSamePosition = from.activeCategory.activeStepIndex === to.activeCategory.activeStepIndex;
|
|
861
|
+
const isSameStep = from.activeCategory.lastActiveStep.id === to.activeCategory.lastActiveStep.id;
|
|
862
|
+
return !isSameStep || !isSamePosition;
|
|
863
|
+
}
|
|
864
|
+
/**
|
|
865
|
+
* Navigation counted as skipped when Category "A" was "active" and not "skipped" before transition
|
|
866
|
+
* and became "inactive" and "skipped" after transition.
|
|
867
|
+
*/
|
|
868
|
+
static isSkipping(from, to, direction = _WizardNavigation.getDirection(from, to)) {
|
|
869
|
+
if (direction === "back") {
|
|
870
|
+
return false;
|
|
871
|
+
}
|
|
872
|
+
const isPrevCategoryAlreadySkipped = from.activeCategory.isSkipped;
|
|
873
|
+
const isPrevCategorySkipped = to.categoryMap[from.activeCategory.id].isSkipped;
|
|
874
|
+
return !isPrevCategoryAlreadySkipped && isPrevCategorySkipped;
|
|
875
|
+
}
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
// src/core/navigation/navigation-service.ts
|
|
879
|
+
var WizardNavigationService = class {
|
|
880
|
+
constructor(wizardStore, wizardConfig, wizardStateService, wizardScrollStrategy) {
|
|
881
|
+
this.wizardStore = wizardStore;
|
|
882
|
+
this.wizardConfig = wizardConfig;
|
|
883
|
+
this.wizardStateService = wizardStateService;
|
|
884
|
+
this.wizardScrollStrategy = wizardScrollStrategy;
|
|
885
|
+
}
|
|
886
|
+
wizardStore;
|
|
887
|
+
wizardConfig;
|
|
888
|
+
wizardStateService;
|
|
889
|
+
wizardScrollStrategy;
|
|
890
|
+
get isNavigating() {
|
|
891
|
+
return !!this.inFlight;
|
|
892
|
+
}
|
|
893
|
+
disposed = false;
|
|
894
|
+
emitter = new TypedEmitter();
|
|
895
|
+
inFlight = null;
|
|
896
|
+
prevSuccessId = null;
|
|
897
|
+
async completeWizard() {
|
|
898
|
+
const tree = this.wizardStore.getTreeSnapshot();
|
|
899
|
+
const to = new WizardCompleteTreeState(tree);
|
|
900
|
+
return this.navigate(to);
|
|
901
|
+
}
|
|
902
|
+
dispose() {
|
|
903
|
+
this.disposed = true;
|
|
904
|
+
if (this.inFlight) {
|
|
905
|
+
this.inFlight.abort.abort();
|
|
906
|
+
this.inFlight = null;
|
|
907
|
+
}
|
|
908
|
+
this.emitter.dispose();
|
|
909
|
+
}
|
|
910
|
+
async exitWizard() {
|
|
911
|
+
try {
|
|
912
|
+
const tree = this.wizardStore.getTreeSnapshot();
|
|
913
|
+
const to = new WizardExitTreeState(tree);
|
|
914
|
+
return this.navigate(to);
|
|
915
|
+
} catch {
|
|
916
|
+
this.emitter.emit(new WizardExitEvent());
|
|
917
|
+
return true;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
async hideCategory(categoryId) {
|
|
921
|
+
const to = this.wizardStateService.hideCategory(categoryId);
|
|
922
|
+
return this.hide(this.projectTree(to));
|
|
923
|
+
}
|
|
924
|
+
async hideStep(step) {
|
|
925
|
+
const tree = this.wizardStore.getTreeSnapshot();
|
|
926
|
+
const to = toArray(step).reduce((acc, id) => {
|
|
927
|
+
const accTree = this.projectTree(acc);
|
|
928
|
+
return this.wizardStateService.hideStep(id, accTree);
|
|
929
|
+
}, tree);
|
|
930
|
+
return this.hide(this.projectTree(to));
|
|
931
|
+
}
|
|
932
|
+
async navigate(to) {
|
|
933
|
+
if (this.disposed) return false;
|
|
934
|
+
const fromTree = this.wizardStore.getTreeSnapshot();
|
|
935
|
+
const navigation = new WizardNavigation(fromTree, to);
|
|
936
|
+
if (navigation.id === this.prevSuccessId && !navigation.isExit && !navigation.isComplete) {
|
|
937
|
+
this.emitter.emit(new WizardNavigationIgnoredEvent(navigation, "same_navigation"));
|
|
938
|
+
return false;
|
|
939
|
+
}
|
|
940
|
+
if (this.inFlight) {
|
|
941
|
+
const prior = this.inFlight;
|
|
942
|
+
prior.abort.abort();
|
|
943
|
+
this.inFlight = null;
|
|
944
|
+
this.emitter.emit(new WizardNavigationCancelledEvent(prior.navigation, "new_navigation"));
|
|
945
|
+
}
|
|
946
|
+
const abort = new AbortController();
|
|
947
|
+
this.inFlight = { abort, navigation };
|
|
948
|
+
try {
|
|
949
|
+
this.emitter.emit(new WizardNavigationStartEvent(navigation));
|
|
950
|
+
this.emitter.emit(new WizardResolveStartEvent(navigation));
|
|
951
|
+
try {
|
|
952
|
+
await this.runResolverChain(navigation, abort.signal);
|
|
953
|
+
} catch (e) {
|
|
954
|
+
if (this.isAbortReason(e, abort.signal)) {
|
|
955
|
+
return false;
|
|
956
|
+
}
|
|
957
|
+
if (e instanceof WizardResolverError) {
|
|
958
|
+
this.emitter.emit(new WizardNavigationCancelledEvent(navigation, "resolver"));
|
|
959
|
+
return false;
|
|
960
|
+
}
|
|
961
|
+
this.emitter.emit(new WizardNavigationErrorEvent(navigation));
|
|
962
|
+
console.error(e);
|
|
963
|
+
throw e;
|
|
964
|
+
}
|
|
965
|
+
if (abort.signal.aborted) return false;
|
|
966
|
+
this.emitter.emit(new WizardResolveEndEvent(navigation));
|
|
967
|
+
this.commitTree(navigation, to);
|
|
968
|
+
this.emitVisibilityFlips(navigation, fromTree, to);
|
|
969
|
+
this.emitter.emit(new WizardNavigationEndEvent(navigation));
|
|
970
|
+
if (navigation.isExit) this.emitter.emit(new WizardExitEvent(navigation));
|
|
971
|
+
if (navigation.isComplete) this.emitter.emit(new WizardCompleteEvent(navigation));
|
|
972
|
+
if (!navigation.isExit && !navigation.isComplete && navigation.isScrollChanging) {
|
|
973
|
+
this.emitter.emit(new WizardScrollStartEvent(navigation));
|
|
974
|
+
await this.wizardScrollStrategy.onNavigate(navigation);
|
|
975
|
+
this.emitter.emit(new WizardScrollEndEvent(navigation));
|
|
976
|
+
}
|
|
977
|
+
this.prevSuccessId = navigation.id;
|
|
978
|
+
return true;
|
|
979
|
+
} finally {
|
|
980
|
+
if (this.inFlight && this.inFlight.navigation === navigation) {
|
|
981
|
+
this.inFlight = null;
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
on(type, handler) {
|
|
986
|
+
return this.emitter.on(type, handler);
|
|
987
|
+
}
|
|
988
|
+
async showCategory(categoryId) {
|
|
989
|
+
const to = this.wizardStateService.showCategory(categoryId);
|
|
990
|
+
return this.show(to);
|
|
991
|
+
}
|
|
992
|
+
async showStep(step) {
|
|
993
|
+
const tree = this.wizardStore.getTreeSnapshot();
|
|
994
|
+
const to = toArray(step).reduce(
|
|
995
|
+
(acc, id) => this.wizardStateService.showStep(id, acc),
|
|
996
|
+
tree
|
|
997
|
+
);
|
|
998
|
+
return this.show(to);
|
|
999
|
+
}
|
|
1000
|
+
// --- internals ---
|
|
1001
|
+
commitTree(navigation, to) {
|
|
1002
|
+
if (navigation.isExit || navigation.isComplete) {
|
|
1003
|
+
const tree = this.projectTree(to);
|
|
1004
|
+
this.wizardStore.setState(tree);
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
this.wizardStore.setState(to);
|
|
1008
|
+
}
|
|
1009
|
+
emitVisibilityFlips(navigation, from, to) {
|
|
1010
|
+
const toTree = this.projectTree(to);
|
|
1011
|
+
for (const stepId of Object.keys(from.stepMap)) {
|
|
1012
|
+
const before = from.stepMap[stepId];
|
|
1013
|
+
const after = toTree.stepMap[stepId];
|
|
1014
|
+
if (!before || !after) continue;
|
|
1015
|
+
if (before.isShow === after.isShow) continue;
|
|
1016
|
+
if (after.isShow) {
|
|
1017
|
+
this.emitter.emit(new WizardStepShowEvent(navigation));
|
|
1018
|
+
} else {
|
|
1019
|
+
this.emitter.emit(new WizardStepHideEvent(navigation));
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
async hide(to) {
|
|
1024
|
+
const fromTree = this.wizardStore.getTreeSnapshot();
|
|
1025
|
+
const navigation = new WizardNavigation(fromTree, to);
|
|
1026
|
+
this.wizardStore.setState(to);
|
|
1027
|
+
this.emitter.emit(new WizardStepHideEvent(navigation));
|
|
1028
|
+
if (navigation.isScrollChanging) {
|
|
1029
|
+
await this.wizardScrollStrategy.onToggle(navigation);
|
|
1030
|
+
}
|
|
1031
|
+
return true;
|
|
1032
|
+
}
|
|
1033
|
+
async invokeResolver(resolver, navigation, signal) {
|
|
1034
|
+
if (!resolver) return;
|
|
1035
|
+
if (signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
1036
|
+
const abortPromise = new Promise((_, reject) => {
|
|
1037
|
+
const onAbort = () => reject(new DOMException("aborted", "AbortError"));
|
|
1038
|
+
if (signal.aborted) {
|
|
1039
|
+
onAbort();
|
|
1040
|
+
} else {
|
|
1041
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
const result = await Promise.race([Promise.resolve(resolver(navigation)), abortPromise]);
|
|
1045
|
+
if (signal.aborted) throw new DOMException("aborted", "AbortError");
|
|
1046
|
+
if (!result) {
|
|
1047
|
+
throw new WizardResolverError(`Resolver rejected navigation ${navigation.id}`);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
isAbortReason(error, signal) {
|
|
1051
|
+
if (!signal.aborted) return false;
|
|
1052
|
+
if (error instanceof DOMException && error.name === "AbortError") return true;
|
|
1053
|
+
return false;
|
|
1054
|
+
}
|
|
1055
|
+
projectTree(to) {
|
|
1056
|
+
if (to instanceof WizardExitTreeState || to instanceof WizardCompleteTreeState) {
|
|
1057
|
+
return to.originTree;
|
|
1058
|
+
}
|
|
1059
|
+
return to;
|
|
1060
|
+
}
|
|
1061
|
+
async runResolverChain(navigation, signal) {
|
|
1062
|
+
const config = this.wizardConfig.getOptions();
|
|
1063
|
+
if (navigation.direction === "next" && navigation.isCategoryChanging && !navigation.isSkipping) {
|
|
1064
|
+
await this.invokeResolver(config.resolveCategoryComplete, navigation, signal);
|
|
1065
|
+
}
|
|
1066
|
+
if (navigation.isCategoryChanging) {
|
|
1067
|
+
await this.invokeResolver(config.resolveCategoryChange, navigation, signal);
|
|
1068
|
+
}
|
|
1069
|
+
if (navigation.isCategoryChanging) {
|
|
1070
|
+
await this.invokeResolver(config.resolveCategoryEnter, navigation, signal);
|
|
1071
|
+
}
|
|
1072
|
+
await this.invokeResolver(config.resolveStepChange, navigation, signal);
|
|
1073
|
+
}
|
|
1074
|
+
async show(to) {
|
|
1075
|
+
const fromTree = this.wizardStore.getTreeSnapshot();
|
|
1076
|
+
const toTree = this.projectTree(to);
|
|
1077
|
+
const navigation = new WizardNavigation(fromTree, toTree);
|
|
1078
|
+
this.wizardStore.setState(toTree);
|
|
1079
|
+
this.emitter.emit(new WizardStepShowEvent(navigation));
|
|
1080
|
+
if (navigation.isScrollChanging) {
|
|
1081
|
+
await this.wizardScrollStrategy.onToggle(navigation);
|
|
1082
|
+
}
|
|
1083
|
+
return true;
|
|
1084
|
+
}
|
|
1085
|
+
};
|
|
1086
|
+
|
|
1087
|
+
// src/core/navigation/state-service.ts
|
|
1088
|
+
var WizardStateService = class {
|
|
1089
|
+
constructor(wizardStore, wizardConfig) {
|
|
1090
|
+
this.wizardStore = wizardStore;
|
|
1091
|
+
this.wizardConfig = wizardConfig;
|
|
1092
|
+
}
|
|
1093
|
+
wizardStore;
|
|
1094
|
+
wizardConfig;
|
|
1095
|
+
completeCategory(category, tree = this.currentTree()) {
|
|
1096
|
+
const { activeBranch } = tree;
|
|
1097
|
+
const { activeCategory, nextCategory } = activeBranch;
|
|
1098
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1099
|
+
builder.completeCategory(category);
|
|
1100
|
+
if (activeCategory.id === category && nextCategory) {
|
|
1101
|
+
return builder.deactivateCategory(category).activateCategory(nextCategory.id).build();
|
|
1102
|
+
}
|
|
1103
|
+
if (activeCategory.id === category && !nextCategory) {
|
|
1104
|
+
return builder.buildComplete();
|
|
1105
|
+
}
|
|
1106
|
+
return builder.build();
|
|
1107
|
+
}
|
|
1108
|
+
goToCategory(categoryId, tree = this.currentTree()) {
|
|
1109
|
+
const { activeBranch, activeCategory, branchMap, categoryMap } = tree;
|
|
1110
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1111
|
+
const toCategory = categoryMap[categoryId];
|
|
1112
|
+
const toCategoryFirstBranch = branchMap[toCategory.branches[0]];
|
|
1113
|
+
const toCategoryBranch = toCategory.branches.includes(activeBranch.id) ? activeBranch : toCategoryFirstBranch;
|
|
1114
|
+
const toCategoryIndex = toCategoryBranch.getShownCategoryIndex(toCategory);
|
|
1115
|
+
const activeCategoryIndex = toCategoryBranch.getShownCategoryIndex(activeCategory);
|
|
1116
|
+
if (activeBranch !== toCategoryBranch && toCategoryIndex > activeCategoryIndex) {
|
|
1117
|
+
throw new WizardError(
|
|
1118
|
+
`[State02]: Trying to switch to category "${categoryId}" in different branch "${toCategoryBranch.id}" from current active branch "${activeBranch.id}", but new category has uncompleted prev category in new branch.`
|
|
1119
|
+
);
|
|
1120
|
+
}
|
|
1121
|
+
if (toCategoryIndex > activeCategoryIndex) {
|
|
1122
|
+
activeBranch.getShownCategories(activeCategoryIndex, toCategoryIndex - 1).forEach((mid) => builder.skipCategoryUncompleted(mid.id));
|
|
1123
|
+
}
|
|
1124
|
+
if (this.wizardConfig.getOptions().isBackResetCompleted && toCategoryIndex < activeCategoryIndex) {
|
|
1125
|
+
activeBranch.getShownCategories(toCategoryIndex + 1, activeCategoryIndex).forEach((mid) => builder.resetCategory(mid.id));
|
|
1126
|
+
}
|
|
1127
|
+
builder.deactivateCategory(activeCategory.id);
|
|
1128
|
+
if (this.wizardConfig.getOptions().isShowFirstStepOnCategoryChange) {
|
|
1129
|
+
return builder.activateFirstStep(categoryId).build();
|
|
1130
|
+
}
|
|
1131
|
+
return builder.activateCategory(categoryId).build();
|
|
1132
|
+
}
|
|
1133
|
+
hideCategory(categoryId, tree = this.currentTree()) {
|
|
1134
|
+
const { activeBranch, activeCategory } = tree;
|
|
1135
|
+
const projected = activeCategory.id === categoryId ? this.projectShownCategory(categoryId, tree) : tree;
|
|
1136
|
+
const builder = new WizardTreeStateBuilder(projected, activeBranch.id);
|
|
1137
|
+
return builder.hideCategory(categoryId).build();
|
|
1138
|
+
}
|
|
1139
|
+
hideStep(step, tree = this.currentTree()) {
|
|
1140
|
+
const { activeBranch, lastActiveStep } = tree;
|
|
1141
|
+
const projected = lastActiveStep.id === step ? this.projectShownStep(step, tree) : tree;
|
|
1142
|
+
const builder = new WizardTreeStateBuilder(projected, activeBranch.id);
|
|
1143
|
+
return builder.hideStep(step).deactivateStep(step).build();
|
|
1144
|
+
}
|
|
1145
|
+
next(branch, tree = this.currentTree()) {
|
|
1146
|
+
const { activeBranch, branchMap } = tree;
|
|
1147
|
+
const nextBranch = branch ? branchMap[branch] : activeBranch;
|
|
1148
|
+
const builder = new WizardTreeStateBuilder(tree, branch ?? activeBranch.id);
|
|
1149
|
+
const activeCategory = nextBranch.activeCategory;
|
|
1150
|
+
const activeStep = activeCategory.lastActiveStep;
|
|
1151
|
+
const nextCategory = nextBranch.nextCategory;
|
|
1152
|
+
const nextStep = activeCategory.nextStep;
|
|
1153
|
+
if (activeBranch.activeCategory !== nextBranch.activeCategory) {
|
|
1154
|
+
throw new WizardError(
|
|
1155
|
+
`[State01]: In order to move to the "${branch}" branch, you need to have common category between 2 branches`
|
|
1156
|
+
);
|
|
1157
|
+
}
|
|
1158
|
+
builder.unskipCategory(activeCategory.id).completeStep(activeStep.id);
|
|
1159
|
+
if (nextStep) {
|
|
1160
|
+
return builder.activateStep(nextStep.id).build();
|
|
1161
|
+
}
|
|
1162
|
+
if (nextCategory) {
|
|
1163
|
+
builder.deactivateCategory(activeCategory.id);
|
|
1164
|
+
if (this.wizardConfig.getOptions().isShowFirstStepOnCategoryChange) {
|
|
1165
|
+
return builder.activateFirstStep(nextCategory.id).build();
|
|
1166
|
+
}
|
|
1167
|
+
return builder.activateCategory(nextCategory.id).build();
|
|
1168
|
+
}
|
|
1169
|
+
return builder.buildComplete();
|
|
1170
|
+
}
|
|
1171
|
+
nextCategory(branch, tree = this.currentTree()) {
|
|
1172
|
+
const { activeBranch, branchMap } = tree;
|
|
1173
|
+
const nextBranch = branch ? branchMap[branch] : activeBranch;
|
|
1174
|
+
const builder = new WizardTreeStateBuilder(tree, branch ?? activeBranch.id);
|
|
1175
|
+
const activeCategory = nextBranch.activeCategory;
|
|
1176
|
+
const activeStep = activeCategory.lastActiveStep;
|
|
1177
|
+
const nextCategory = nextBranch.nextCategory;
|
|
1178
|
+
if (activeBranch.activeCategory !== nextBranch.activeCategory) {
|
|
1179
|
+
throw new WizardError(
|
|
1180
|
+
`[State01]: In order to move to the "${branch}" branch, you need to have common category between 2 branches`
|
|
1181
|
+
);
|
|
1182
|
+
}
|
|
1183
|
+
builder.deactivateCategory(activeCategory.id).completeStep(activeStep.id);
|
|
1184
|
+
if (nextCategory) {
|
|
1185
|
+
builder.skipCategoryUncompleted(activeCategory.id);
|
|
1186
|
+
if (this.wizardConfig.getOptions().isShowFirstStepOnCategoryChange) {
|
|
1187
|
+
return builder.activateFirstStep(nextCategory.id).build();
|
|
1188
|
+
}
|
|
1189
|
+
return builder.activateCategory(nextCategory.id).build();
|
|
1190
|
+
}
|
|
1191
|
+
return builder.buildComplete();
|
|
1192
|
+
}
|
|
1193
|
+
prev(tree = this.currentTree()) {
|
|
1194
|
+
const { activeBranch } = tree;
|
|
1195
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1196
|
+
const activeCategory = activeBranch.activeCategory;
|
|
1197
|
+
const activeStep = activeCategory.lastActiveStep;
|
|
1198
|
+
const prevCategory = activeBranch.prevCategory;
|
|
1199
|
+
const prevStep = activeCategory.prevStep;
|
|
1200
|
+
if (this.wizardConfig.getOptions().isBackResetCompleted) {
|
|
1201
|
+
builder.uncompleteStep(activeStep.id);
|
|
1202
|
+
}
|
|
1203
|
+
if (prevStep) {
|
|
1204
|
+
return builder.deactivateStep(activeStep.id).build();
|
|
1205
|
+
}
|
|
1206
|
+
if (prevCategory) {
|
|
1207
|
+
return builder.deactivateCategory(activeCategory.id).activateCategory(prevCategory.id).build();
|
|
1208
|
+
}
|
|
1209
|
+
return builder.buildExit();
|
|
1210
|
+
}
|
|
1211
|
+
prevCategory(tree = this.currentTree()) {
|
|
1212
|
+
const { activeBranch } = tree;
|
|
1213
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1214
|
+
const activeCategory = activeBranch.activeCategory;
|
|
1215
|
+
const prevCategory = activeBranch.prevCategory;
|
|
1216
|
+
if (prevCategory && this.wizardConfig.getOptions().isBackResetCompleted) {
|
|
1217
|
+
builder.resetCategory(activeCategory.id);
|
|
1218
|
+
}
|
|
1219
|
+
if (prevCategory) {
|
|
1220
|
+
return builder.deactivateCategory(activeCategory.id).activateCategory(prevCategory.id).build();
|
|
1221
|
+
}
|
|
1222
|
+
return builder.buildExit();
|
|
1223
|
+
}
|
|
1224
|
+
resetCategory(category, from = 0, tree = this.currentTree()) {
|
|
1225
|
+
const { activeCategory } = tree;
|
|
1226
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1227
|
+
builder.resetCategory(category, from);
|
|
1228
|
+
if (activeCategory.id === category) {
|
|
1229
|
+
return builder.activateStep(activeCategory.firstStep.id).build();
|
|
1230
|
+
}
|
|
1231
|
+
return builder.build();
|
|
1232
|
+
}
|
|
1233
|
+
resetTree(tree = this.currentTree()) {
|
|
1234
|
+
const reset = new WizardTreeStateBuilder(tree).getResetTree();
|
|
1235
|
+
return new WizardTreeStateBuilder(reset, tree.activeBranch.id).activateStep(reset.activeCategory.firstStep.id).build();
|
|
1236
|
+
}
|
|
1237
|
+
showCategory(categoryId, tree = this.currentTree()) {
|
|
1238
|
+
const { activeBranch } = tree;
|
|
1239
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1240
|
+
const activeCategoryIndex = activeBranch.categories.indexOf(activeBranch.activeCategory);
|
|
1241
|
+
const categoryIndex = activeBranch.categories.findIndex((category) => category.id === categoryId);
|
|
1242
|
+
const isBeforeActiveCategory = activeCategoryIndex > categoryIndex;
|
|
1243
|
+
builder.showCategory(categoryId);
|
|
1244
|
+
if (isBeforeActiveCategory) builder.skipCategory(categoryId);
|
|
1245
|
+
return builder.build();
|
|
1246
|
+
}
|
|
1247
|
+
showStep(step, tree = this.currentTree()) {
|
|
1248
|
+
const { activeCategory, lastActiveStep, stepMap } = tree;
|
|
1249
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1250
|
+
const stepState = stepMap[step];
|
|
1251
|
+
const isInActiveCategory = activeCategory.steps.some((s) => s.id === step);
|
|
1252
|
+
const isBeforeLastActiveStep = lastActiveStep.htmlIndex > stepState.htmlIndex;
|
|
1253
|
+
builder.showStep(step);
|
|
1254
|
+
if (isBeforeLastActiveStep) builder.completeStep(step);
|
|
1255
|
+
if (isInActiveCategory && isBeforeLastActiveStep) builder.activateStep(step);
|
|
1256
|
+
return builder.build();
|
|
1257
|
+
}
|
|
1258
|
+
skipCategory(category, tree = this.currentTree()) {
|
|
1259
|
+
const { activeBranch } = tree;
|
|
1260
|
+
const { activeCategory, nextCategory } = activeBranch;
|
|
1261
|
+
const builder = new WizardTreeStateBuilder(tree);
|
|
1262
|
+
builder.skipCategory(category);
|
|
1263
|
+
if (activeCategory.id === category && nextCategory) {
|
|
1264
|
+
return builder.deactivateCategory(category).activateCategory(nextCategory.id).build();
|
|
1265
|
+
}
|
|
1266
|
+
if (activeCategory.id === category && !nextCategory) {
|
|
1267
|
+
return builder.buildComplete();
|
|
1268
|
+
}
|
|
1269
|
+
return builder.build();
|
|
1270
|
+
}
|
|
1271
|
+
currentTree() {
|
|
1272
|
+
return this.wizardStore.getTreeSnapshot();
|
|
1273
|
+
}
|
|
1274
|
+
projectShownCategory(categoryId, tree) {
|
|
1275
|
+
const { activeBranch } = tree;
|
|
1276
|
+
const result = categoryId === activeBranch.firstCategory.id ? this.nextCategory(null, tree) : this.prevCategory(tree);
|
|
1277
|
+
return result;
|
|
1278
|
+
}
|
|
1279
|
+
projectShownStep(step, tree) {
|
|
1280
|
+
const { activeBranch, activeCategory, stepMap } = tree;
|
|
1281
|
+
const stepState = stepMap[step];
|
|
1282
|
+
const isFirstCategory = stepState.categoryId === activeBranch.firstCategory.id;
|
|
1283
|
+
const isFirstStep = step === activeCategory.firstStep.id;
|
|
1284
|
+
const result = isFirstCategory && isFirstStep ? this.next(null, tree) : this.prev(tree);
|
|
1285
|
+
return result;
|
|
1286
|
+
}
|
|
1287
|
+
};
|
|
1288
|
+
|
|
1289
|
+
// src/core/strategies/progress/wizard-progress.strategy.ts
|
|
1290
|
+
var WizardProgressStrategy = class {
|
|
1291
|
+
};
|
|
1292
|
+
|
|
1293
|
+
// src/core/strategies/progress/wizard-active-progress.strategy.ts
|
|
1294
|
+
var WizardActiveProgressStrategy = class extends WizardProgressStrategy {
|
|
1295
|
+
getProgress(tree) {
|
|
1296
|
+
const { activeCategory, categoryMap } = tree;
|
|
1297
|
+
const progress = {};
|
|
1298
|
+
for (const category of Object.keys(categoryMap)) {
|
|
1299
|
+
const categoryState = categoryMap[category];
|
|
1300
|
+
if (categoryState.htmlIndex !== activeCategory.htmlIndex) {
|
|
1301
|
+
progress[category] = categoryState.htmlIndex < activeCategory.htmlIndex ? 100 : 0;
|
|
1302
|
+
continue;
|
|
1303
|
+
}
|
|
1304
|
+
progress[category] = categoryState.shownSteps.length > 0 ? (categoryState.activeSteps.length - 1) * 100 / categoryState.shownSteps.length : 0;
|
|
1305
|
+
}
|
|
1306
|
+
return progress;
|
|
1307
|
+
}
|
|
1308
|
+
};
|
|
1309
|
+
|
|
1310
|
+
// src/core/strategies/scroll/wizard-scroll.strategy.ts
|
|
1311
|
+
var WizardScrollStrategy = class {
|
|
1312
|
+
// Thin re-export so subclasses keep the existing call site.
|
|
1313
|
+
static projectToTree = projectToTree;
|
|
1314
|
+
container;
|
|
1315
|
+
/** Called when toggling step/category visibility (instant scroll by default). */
|
|
1316
|
+
onToggle(navigation) {
|
|
1317
|
+
const tree = projectToTree(navigation.to);
|
|
1318
|
+
if (!tree) return Promise.resolve(false);
|
|
1319
|
+
const isFirstStepOfCategory = tree.lastActiveStep?.id === tree.activeCategory.firstStep?.id;
|
|
1320
|
+
return scrollToStep(isFirstStepOfCategory ? null : tree.lastActiveStep?.id, "auto", this.container);
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Imperative scroll-to-top of the configured container (or window). Useful
|
|
1324
|
+
* for surfacing top-of-page state — e.g. an error banner — after a failed
|
|
1325
|
+
* submit, without re-running the navigation pipeline.
|
|
1326
|
+
*/
|
|
1327
|
+
scrollToTop(behavior = "smooth") {
|
|
1328
|
+
return scrollToStep(null, behavior, this.container);
|
|
1329
|
+
}
|
|
1330
|
+
setContainer(container) {
|
|
1331
|
+
this.container = container;
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
function scrollToStep(stepId, behavior, container) {
|
|
1335
|
+
if (typeof document === "undefined") return Promise.resolve(false);
|
|
1336
|
+
const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)").matches : false;
|
|
1337
|
+
const effectiveBehavior = reducedMotion ? "auto" : behavior;
|
|
1338
|
+
if (!stepId) {
|
|
1339
|
+
const containerEl = resolveScrollContainer(container);
|
|
1340
|
+
if (containerEl) {
|
|
1341
|
+
containerEl.scrollTo({ behavior: effectiveBehavior, top: 0 });
|
|
1342
|
+
} else if (typeof window !== "undefined") {
|
|
1343
|
+
window.scrollTo({ behavior: effectiveBehavior, top: 0 });
|
|
1344
|
+
}
|
|
1345
|
+
return Promise.resolve(true);
|
|
1346
|
+
}
|
|
1347
|
+
const element = document.getElementById(stepId);
|
|
1348
|
+
if (!element) return Promise.resolve(false);
|
|
1349
|
+
element.scrollIntoView({ behavior: effectiveBehavior, block: "start" });
|
|
1350
|
+
return Promise.resolve(true);
|
|
1351
|
+
}
|
|
1352
|
+
function resolveScrollContainer(c) {
|
|
1353
|
+
if (!c) return null;
|
|
1354
|
+
if (typeof c === "function") return c();
|
|
1355
|
+
if ("current" in c) return c.current;
|
|
1356
|
+
return c;
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// src/core/strategies/scroll/wizard-smooth-scroll.strategy.ts
|
|
1360
|
+
var WizardSmoothScrollStrategy = class extends WizardScrollStrategy {
|
|
1361
|
+
constructor(wizardConfig) {
|
|
1362
|
+
super();
|
|
1363
|
+
this.wizardConfig = wizardConfig;
|
|
1364
|
+
}
|
|
1365
|
+
wizardConfig;
|
|
1366
|
+
onNavigate(navigation) {
|
|
1367
|
+
const tree = WizardScrollStrategy.projectToTree(navigation.to);
|
|
1368
|
+
if (!tree) return Promise.resolve(false);
|
|
1369
|
+
const isFirstStepOfCategory = tree.lastActiveStep?.id === tree.activeCategory.firstStep?.id;
|
|
1370
|
+
const stepId = isFirstStepOfCategory ? null : tree.lastActiveStep?.id;
|
|
1371
|
+
const useInstant = navigation.isCategoryChanging || (this.wizardConfig?.isMobile ?? false);
|
|
1372
|
+
return scrollToStep(stepId, useInstant ? "auto" : "smooth", this.container);
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1375
|
+
|
|
1376
|
+
// src/core/strategies/visibility/wizard-visibility.strategy.ts
|
|
1377
|
+
var WizardVisibilityStrategy = class {
|
|
1378
|
+
};
|
|
1379
|
+
|
|
1380
|
+
// src/core/strategies/visibility/wizard-default-visibility.strategy.ts
|
|
1381
|
+
var WizardDefaultVisibilityStrategy = class extends WizardVisibilityStrategy {
|
|
1382
|
+
constructor(wizardConfig) {
|
|
1383
|
+
super();
|
|
1384
|
+
this.wizardConfig = wizardConfig;
|
|
1385
|
+
}
|
|
1386
|
+
wizardConfig;
|
|
1387
|
+
isStepVisible(step, tree) {
|
|
1388
|
+
const isShowStep = step.isActive && step.isShow;
|
|
1389
|
+
const isLastActiveStep = step.id === tree.lastActiveStep?.id;
|
|
1390
|
+
return this.wizardConfig.isMobile ? isShowStep && isLastActiveStep : isShowStep;
|
|
1391
|
+
}
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
// src/core/strategies/visibility/wizard-last-active-visibility.strategy.ts
|
|
1395
|
+
var WizardLastActiveVisibilityStrategy = class extends WizardVisibilityStrategy {
|
|
1396
|
+
isStepVisible(step, tree) {
|
|
1397
|
+
const isShowStep = step.isActive && step.isShow;
|
|
1398
|
+
const isLastActiveStep = step.id === tree.lastActiveStep?.id;
|
|
1399
|
+
return isShowStep && isLastActiveStep;
|
|
1400
|
+
}
|
|
1401
|
+
};
|
|
1402
|
+
|
|
1403
|
+
// src/core/wizard-default.config.ts
|
|
1404
|
+
var passResolver = () => true;
|
|
1405
|
+
var wizardDefaultConfig = {
|
|
1406
|
+
backBtnClass: "",
|
|
1407
|
+
backBtnText: "Back",
|
|
1408
|
+
doneDotText: "Finish",
|
|
1409
|
+
enableTracing: "none",
|
|
1410
|
+
finishText: "Finish",
|
|
1411
|
+
headerI18n: null,
|
|
1412
|
+
isActiveCategoryClickReset: true,
|
|
1413
|
+
isBackResetCompleted: true,
|
|
1414
|
+
isDoneDot: false,
|
|
1415
|
+
isShowFirstStepOnCategoryChange: false,
|
|
1416
|
+
isShowWizardHeader: true,
|
|
1417
|
+
isShowWizardHeaderSteps: true,
|
|
1418
|
+
nextBtnClass: "",
|
|
1419
|
+
nextBtnText: "Next",
|
|
1420
|
+
nextCategoryText: "Continue",
|
|
1421
|
+
resolveCategoryChange: passResolver,
|
|
1422
|
+
resolveCategoryComplete: passResolver,
|
|
1423
|
+
resolveCategoryEnter: passResolver,
|
|
1424
|
+
resolveStepChange: passResolver,
|
|
1425
|
+
silenceErrors: false,
|
|
1426
|
+
stickyHeader: true
|
|
1427
|
+
};
|
|
1428
|
+
|
|
1429
|
+
// src/core/wizard-config.ts
|
|
1430
|
+
var WizardConfig = class {
|
|
1431
|
+
/**
|
|
1432
|
+
* Whether the wizard is rendering on a mobile viewport. Used by the default
|
|
1433
|
+
* visibility and scroll strategies. The React adapter (Phase 15) updates this
|
|
1434
|
+
* via a media-query subscription; in plain Node (tests/SSR), it stays `false`.
|
|
1435
|
+
*/
|
|
1436
|
+
isMobile = false;
|
|
1437
|
+
get options() {
|
|
1438
|
+
return this.current;
|
|
1439
|
+
}
|
|
1440
|
+
current;
|
|
1441
|
+
disposed = false;
|
|
1442
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1443
|
+
notifyScheduled = false;
|
|
1444
|
+
constructor(overrides = {}) {
|
|
1445
|
+
this.current = { ...wizardDefaultConfig, ...overrides };
|
|
1446
|
+
}
|
|
1447
|
+
dispose() {
|
|
1448
|
+
this.disposed = true;
|
|
1449
|
+
this.listeners.clear();
|
|
1450
|
+
}
|
|
1451
|
+
getOptions() {
|
|
1452
|
+
return this.current;
|
|
1453
|
+
}
|
|
1454
|
+
setOptions(options) {
|
|
1455
|
+
if (this.disposed) return this;
|
|
1456
|
+
let changed = false;
|
|
1457
|
+
for (const k of Object.keys(options)) {
|
|
1458
|
+
if (!Object.is(options[k], this.current[k])) {
|
|
1459
|
+
changed = true;
|
|
1460
|
+
break;
|
|
1461
|
+
}
|
|
1462
|
+
}
|
|
1463
|
+
if (!changed) return this;
|
|
1464
|
+
this.current = { ...this.current, ...options };
|
|
1465
|
+
this.scheduleNotify();
|
|
1466
|
+
return this;
|
|
1467
|
+
}
|
|
1468
|
+
subscribe(listener) {
|
|
1469
|
+
if (this.disposed) return () => {
|
|
1470
|
+
};
|
|
1471
|
+
this.listeners.add(listener);
|
|
1472
|
+
return () => {
|
|
1473
|
+
this.listeners.delete(listener);
|
|
1474
|
+
};
|
|
1475
|
+
}
|
|
1476
|
+
scheduleNotify() {
|
|
1477
|
+
if (this.notifyScheduled) return;
|
|
1478
|
+
this.notifyScheduled = true;
|
|
1479
|
+
queueMicrotask(() => {
|
|
1480
|
+
this.notifyScheduled = false;
|
|
1481
|
+
if (this.disposed) return;
|
|
1482
|
+
const snapshot = this.current;
|
|
1483
|
+
for (const listener of [...this.listeners]) {
|
|
1484
|
+
listener(snapshot);
|
|
1485
|
+
}
|
|
1486
|
+
});
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
// src/core/wizard-log.ts
|
|
1491
|
+
var WizardLog = class {
|
|
1492
|
+
constructor(wizardConfig) {
|
|
1493
|
+
this.wizardConfig = wizardConfig;
|
|
1494
|
+
}
|
|
1495
|
+
wizardConfig;
|
|
1496
|
+
error(e) {
|
|
1497
|
+
if (!(e instanceof WizardError)) {
|
|
1498
|
+
console.error(e);
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
if (this.wizardConfig.getOptions().silenceErrors) {
|
|
1502
|
+
return;
|
|
1503
|
+
}
|
|
1504
|
+
console.error(e);
|
|
1505
|
+
}
|
|
1506
|
+
trace(...args) {
|
|
1507
|
+
if (this.wizardConfig.getOptions().enableTracing !== "none") {
|
|
1508
|
+
console.log(...args);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
};
|
|
1512
|
+
|
|
1513
|
+
// src/core/wizard-store.ts
|
|
1514
|
+
var WizardStore = class {
|
|
1515
|
+
constructor(storagePrefix) {
|
|
1516
|
+
this.storagePrefix = storagePrefix;
|
|
1517
|
+
this.initOnce = new Promise((resolve) => {
|
|
1518
|
+
this.resolveInitOnce = resolve;
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
storagePrefix;
|
|
1522
|
+
initOnce;
|
|
1523
|
+
get shapeId() {
|
|
1524
|
+
return this._shapeId ?? "";
|
|
1525
|
+
}
|
|
1526
|
+
_shapeId = null;
|
|
1527
|
+
current = null;
|
|
1528
|
+
disposed = false;
|
|
1529
|
+
initialTree = null;
|
|
1530
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1531
|
+
resolveInitOnce;
|
|
1532
|
+
dispose() {
|
|
1533
|
+
this.disposed = true;
|
|
1534
|
+
this.listeners.clear();
|
|
1535
|
+
}
|
|
1536
|
+
getTreeSnapshot() {
|
|
1537
|
+
if (!this.current) {
|
|
1538
|
+
throw new WizardInitializationError("[WizardStore] getTreeSnapshot called before init");
|
|
1539
|
+
}
|
|
1540
|
+
return this.current;
|
|
1541
|
+
}
|
|
1542
|
+
init(initialTree) {
|
|
1543
|
+
if (this.disposed) return;
|
|
1544
|
+
if (this.initialTree) {
|
|
1545
|
+
console.warn("[WizardStore] init() called more than once; ignoring subsequent call.");
|
|
1546
|
+
return;
|
|
1547
|
+
}
|
|
1548
|
+
this.initialTree = initialTree;
|
|
1549
|
+
this._shapeId = getShapeId(initialTree.state, this.storagePrefix);
|
|
1550
|
+
this.setState(initialTree);
|
|
1551
|
+
this.resolveInitOnce(initialTree);
|
|
1552
|
+
}
|
|
1553
|
+
setState(tree) {
|
|
1554
|
+
if (this.disposed) return;
|
|
1555
|
+
if (!this.initialTree) {
|
|
1556
|
+
throw new WizardInitializationError("[WizardStore] setState called before init");
|
|
1557
|
+
}
|
|
1558
|
+
if (this.current === tree) return;
|
|
1559
|
+
this.current = tree;
|
|
1560
|
+
for (const listener of [...this.listeners]) {
|
|
1561
|
+
listener();
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
subscribe = (listener) => {
|
|
1565
|
+
this.listeners.add(listener);
|
|
1566
|
+
return () => {
|
|
1567
|
+
this.listeners.delete(listener);
|
|
1568
|
+
};
|
|
1569
|
+
};
|
|
1570
|
+
};
|
|
1571
|
+
|
|
1572
|
+
// src/core/wizard-engine.ts
|
|
1573
|
+
var WizardEngine = class {
|
|
1574
|
+
get configOptions() {
|
|
1575
|
+
return this.config.options;
|
|
1576
|
+
}
|
|
1577
|
+
get isInitialized() {
|
|
1578
|
+
return this.initialized;
|
|
1579
|
+
}
|
|
1580
|
+
get lastActiveStep() {
|
|
1581
|
+
return this.tree.lastActiveStep;
|
|
1582
|
+
}
|
|
1583
|
+
get progress() {
|
|
1584
|
+
return this.progressStrategy.getProgress(this.tree);
|
|
1585
|
+
}
|
|
1586
|
+
get shapeId() {
|
|
1587
|
+
return this.store.shapeId;
|
|
1588
|
+
}
|
|
1589
|
+
get steps() {
|
|
1590
|
+
return this.tree.stepMap;
|
|
1591
|
+
}
|
|
1592
|
+
get tree() {
|
|
1593
|
+
return this.store.getTreeSnapshot();
|
|
1594
|
+
}
|
|
1595
|
+
committed = false;
|
|
1596
|
+
// commitRegistration() entered
|
|
1597
|
+
config;
|
|
1598
|
+
disposed = false;
|
|
1599
|
+
initialBranch;
|
|
1600
|
+
initialized = false;
|
|
1601
|
+
// store.init() ran successfully
|
|
1602
|
+
initialState;
|
|
1603
|
+
initService;
|
|
1604
|
+
listeners;
|
|
1605
|
+
log;
|
|
1606
|
+
// ------------- snapshots -------------
|
|
1607
|
+
navService;
|
|
1608
|
+
pendingPostInitTasks = [];
|
|
1609
|
+
progressStrategy;
|
|
1610
|
+
scrollStrategy;
|
|
1611
|
+
stateService;
|
|
1612
|
+
store;
|
|
1613
|
+
visibilityStrategy;
|
|
1614
|
+
constructor(options) {
|
|
1615
|
+
this.config = new WizardConfig(options.config ?? {});
|
|
1616
|
+
const storagePrefix = options.config?.storagePrefix ?? void 0;
|
|
1617
|
+
this.store = new WizardStore(storagePrefix);
|
|
1618
|
+
this.log = new WizardLog(this.config);
|
|
1619
|
+
this.scrollStrategy = options.strategies?.scroll ?? new WizardSmoothScrollStrategy(this.config);
|
|
1620
|
+
if (options.scrollContainer !== void 0) {
|
|
1621
|
+
this.scrollStrategy.setContainer(options.scrollContainer);
|
|
1622
|
+
}
|
|
1623
|
+
this.visibilityStrategy = options.strategies?.visibility ?? new WizardDefaultVisibilityStrategy(this.config);
|
|
1624
|
+
this.progressStrategy = options.strategies?.progress ?? new WizardActiveProgressStrategy();
|
|
1625
|
+
this.stateService = new WizardStateService(this.store, this.config);
|
|
1626
|
+
this.navService = new WizardNavigationService(
|
|
1627
|
+
this.store,
|
|
1628
|
+
this.config,
|
|
1629
|
+
this.stateService,
|
|
1630
|
+
this.scrollStrategy
|
|
1631
|
+
);
|
|
1632
|
+
const initializers = options.initializers ?? [new WizardDefaultInitializer()];
|
|
1633
|
+
this.initService = new WizardInitializerService(this.log, initializers);
|
|
1634
|
+
this.listeners = options.listeners ?? [];
|
|
1635
|
+
this.initialState = options.state;
|
|
1636
|
+
this.initialBranch = options.activeBranch;
|
|
1637
|
+
}
|
|
1638
|
+
// ------------- React adapter wiring -------------
|
|
1639
|
+
back() {
|
|
1640
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.prev()));
|
|
1641
|
+
}
|
|
1642
|
+
backCategory() {
|
|
1643
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.prevCategory()));
|
|
1644
|
+
}
|
|
1645
|
+
async commitRegistration() {
|
|
1646
|
+
if (this.disposed) return;
|
|
1647
|
+
if (this.committed) return;
|
|
1648
|
+
this.committed = true;
|
|
1649
|
+
const tree = await this.initService.getInitialState(this.initialState, this.initialBranch);
|
|
1650
|
+
if (this.disposed) {
|
|
1651
|
+
for (const task of this.pendingPostInitTasks.splice(0)) task.resolve(false);
|
|
1652
|
+
return;
|
|
1653
|
+
}
|
|
1654
|
+
this.store.init(tree);
|
|
1655
|
+
this.initialized = true;
|
|
1656
|
+
for (const listener of this.listeners) {
|
|
1657
|
+
try {
|
|
1658
|
+
listener.onInit(tree);
|
|
1659
|
+
} catch (e) {
|
|
1660
|
+
this.log.error(e);
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
this.store.subscribe(() => {
|
|
1664
|
+
const current = this.store.getTreeSnapshot();
|
|
1665
|
+
for (const listener of this.listeners) {
|
|
1666
|
+
try {
|
|
1667
|
+
listener.onTreeChange(current);
|
|
1668
|
+
} catch (e) {
|
|
1669
|
+
this.log.error(e);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1673
|
+
this.navService.on("*", (event) => {
|
|
1674
|
+
for (const listener of this.listeners) {
|
|
1675
|
+
try {
|
|
1676
|
+
listener.onEvent(event);
|
|
1677
|
+
} catch (e) {
|
|
1678
|
+
this.log.error(e);
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1681
|
+
});
|
|
1682
|
+
for (const task of this.pendingPostInitTasks.splice(0)) {
|
|
1683
|
+
task.run();
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
// ------------- bootstrap -------------
|
|
1687
|
+
completeWizard() {
|
|
1688
|
+
return this.runOrQueue(() => this.navService.completeWizard());
|
|
1689
|
+
}
|
|
1690
|
+
// ------------- imperative API -------------
|
|
1691
|
+
dispose() {
|
|
1692
|
+
if (this.disposed) return;
|
|
1693
|
+
this.disposed = true;
|
|
1694
|
+
for (const task of this.pendingPostInitTasks.splice(0)) task.resolve(false);
|
|
1695
|
+
const lastTree = this.initialized ? this.store.getTreeSnapshot() : null;
|
|
1696
|
+
for (const listener of this.listeners) {
|
|
1697
|
+
try {
|
|
1698
|
+
listener.onDestroy(lastTree);
|
|
1699
|
+
} catch (e) {
|
|
1700
|
+
this.log.error(e);
|
|
1701
|
+
}
|
|
1702
|
+
}
|
|
1703
|
+
this.navService.dispose();
|
|
1704
|
+
this.store.dispose();
|
|
1705
|
+
this.config.dispose();
|
|
1706
|
+
}
|
|
1707
|
+
exitWizard() {
|
|
1708
|
+
return this.runOrQueue(() => this.navService.exitWizard());
|
|
1709
|
+
}
|
|
1710
|
+
getTreeSnapshot = () => this.store.getTreeSnapshot();
|
|
1711
|
+
go(tree) {
|
|
1712
|
+
return this.runOrQueue(() => this.navService.navigate(tree));
|
|
1713
|
+
}
|
|
1714
|
+
goToCategory(category) {
|
|
1715
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.goToCategory(category)));
|
|
1716
|
+
}
|
|
1717
|
+
hideCategory(category) {
|
|
1718
|
+
return this.runOrQueue(() => this.navService.hideCategory(category));
|
|
1719
|
+
}
|
|
1720
|
+
hideStep(step) {
|
|
1721
|
+
return this.runOrQueue(() => this.navService.hideStep(step));
|
|
1722
|
+
}
|
|
1723
|
+
isStepVisible(stepId) {
|
|
1724
|
+
const tree = this.store.getTreeSnapshot();
|
|
1725
|
+
const step = tree.stepMap[stepId];
|
|
1726
|
+
if (!step) return false;
|
|
1727
|
+
return this.visibilityStrategy.isStepVisible(step, tree);
|
|
1728
|
+
}
|
|
1729
|
+
next(branch) {
|
|
1730
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.next(branch ?? null)));
|
|
1731
|
+
}
|
|
1732
|
+
nextCategory(branch) {
|
|
1733
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.nextCategory(branch ?? null)));
|
|
1734
|
+
}
|
|
1735
|
+
on(type, handler) {
|
|
1736
|
+
return this.navService.on(type, handler);
|
|
1737
|
+
}
|
|
1738
|
+
resetActiveCategory(from = 0) {
|
|
1739
|
+
return this.runOrQueue(
|
|
1740
|
+
() => this.navService.navigate(this.stateService.resetCategory(this.tree.activeCategory.id, from))
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
resetCategory(category, from = 0) {
|
|
1744
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.resetCategory(category, from)));
|
|
1745
|
+
}
|
|
1746
|
+
resetWizard() {
|
|
1747
|
+
return this.runOrQueue(() => this.navService.navigate(this.stateService.resetTree()));
|
|
1748
|
+
}
|
|
1749
|
+
scrollToTop(behavior = "smooth") {
|
|
1750
|
+
return this.scrollStrategy.scrollToTop(behavior);
|
|
1751
|
+
}
|
|
1752
|
+
showCategory(category) {
|
|
1753
|
+
return this.runOrQueue(() => this.navService.showCategory(category));
|
|
1754
|
+
}
|
|
1755
|
+
showStep(step) {
|
|
1756
|
+
return this.runOrQueue(() => this.navService.showStep(step));
|
|
1757
|
+
}
|
|
1758
|
+
skipActiveCategory() {
|
|
1759
|
+
return this.runOrQueue(
|
|
1760
|
+
() => this.navService.navigate(this.stateService.skipCategory(this.tree.activeCategory.id))
|
|
1761
|
+
);
|
|
1762
|
+
}
|
|
1763
|
+
skipCategory(category) {
|
|
1764
|
+
return this.runOrQueue(
|
|
1765
|
+
() => this.navService.navigate(this.stateService.skipCategory(category ?? this.tree.activeCategory.id))
|
|
1766
|
+
);
|
|
1767
|
+
}
|
|
1768
|
+
subscribe = (listener) => this.store.subscribe(listener);
|
|
1769
|
+
toggleCategory(category, show) {
|
|
1770
|
+
return show ? this.showCategory(category) : this.hideCategory(category);
|
|
1771
|
+
}
|
|
1772
|
+
// ------------- lifecycle -------------
|
|
1773
|
+
toggleStep(step, show) {
|
|
1774
|
+
return show ? this.showStep(step) : this.hideStep(step);
|
|
1775
|
+
}
|
|
1776
|
+
// ------------- internal -------------
|
|
1777
|
+
runOrQueue(action) {
|
|
1778
|
+
if (this.disposed) {
|
|
1779
|
+
return Promise.resolve(false);
|
|
1780
|
+
}
|
|
1781
|
+
if (!this.initialized) {
|
|
1782
|
+
return new Promise((resolve, reject) => {
|
|
1783
|
+
this.pendingPostInitTasks.push({
|
|
1784
|
+
resolve,
|
|
1785
|
+
run: () => {
|
|
1786
|
+
action().then(resolve, reject);
|
|
1787
|
+
}
|
|
1788
|
+
});
|
|
1789
|
+
});
|
|
1790
|
+
}
|
|
1791
|
+
return action();
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
|
|
1795
|
+
// src/enums/wizard-category-direction.enum.ts
|
|
1796
|
+
var WizardCategoryDirection = /* @__PURE__ */ ((WizardCategoryDirection2) => {
|
|
1797
|
+
WizardCategoryDirection2["Backward"] = "backward";
|
|
1798
|
+
WizardCategoryDirection2["Forward"] = "forward";
|
|
1799
|
+
return WizardCategoryDirection2;
|
|
1800
|
+
})(WizardCategoryDirection || {});
|
|
1801
|
+
|
|
1802
|
+
// src/react/components-provider.tsx
|
|
1803
|
+
var import_react = require("react");
|
|
1804
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
1805
|
+
var FallbackButton = (props) => {
|
|
1806
|
+
const { children, asChild: _asChild, variant: _variant, size: _size, ...rest } = props;
|
|
1807
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { ...rest, type: props.type ?? "button", children });
|
|
1808
|
+
};
|
|
1809
|
+
var FallbackBackArrowIcon = ({ className }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { className, width: "16", height: "16", viewBox: "0 0 16 16", fill: "currentColor", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M10 4 L6 8 L10 12", stroke: "currentColor", strokeWidth: "1.5", fill: "none" }) });
|
|
1810
|
+
var defaults = { Button: FallbackButton, BackArrowIcon: FallbackBackArrowIcon };
|
|
1811
|
+
var WizardComponentsContext = (0, import_react.createContext)(defaults);
|
|
1812
|
+
function WizardComponentsProvider(props) {
|
|
1813
|
+
const { Button, BackArrowIcon, children } = props;
|
|
1814
|
+
const value = {
|
|
1815
|
+
Button: Button ?? defaults.Button,
|
|
1816
|
+
BackArrowIcon: BackArrowIcon ?? defaults.BackArrowIcon
|
|
1817
|
+
};
|
|
1818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WizardComponentsContext.Provider, { value, children });
|
|
1819
|
+
}
|
|
1820
|
+
function useWizardComponents() {
|
|
1821
|
+
return (0, import_react.useContext)(WizardComponentsContext);
|
|
1822
|
+
}
|
|
1823
|
+
|
|
1824
|
+
// src/react/use-wizard.ts
|
|
1825
|
+
var import_react3 = require("react");
|
|
1826
|
+
|
|
1827
|
+
// src/react/context.ts
|
|
1828
|
+
var import_react2 = require("react");
|
|
1829
|
+
var WizardEngineContext = (0, import_react2.createContext)(null);
|
|
1830
|
+
|
|
1831
|
+
// src/react/use-wizard.ts
|
|
1832
|
+
function useWizard() {
|
|
1833
|
+
const engine = (0, import_react3.useContext)(WizardEngineContext);
|
|
1834
|
+
if (!engine) {
|
|
1835
|
+
throw new WizardError("useWizard must be used inside <WizardProvider>");
|
|
1836
|
+
}
|
|
1837
|
+
(0, import_react3.useSyncExternalStore)(engine.subscribe, engine.getTreeSnapshot, engine.getTreeSnapshot);
|
|
1838
|
+
return engine;
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
// src/react/components/wizard-action-button.tsx
|
|
1842
|
+
var import_react_slot = require("@radix-ui/react-slot");
|
|
1843
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1844
|
+
function WizardActionButton(props) {
|
|
1845
|
+
const { Button } = useWizardComponents();
|
|
1846
|
+
if (props.asChild) {
|
|
1847
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react_slot.Slot, { className: props.className, onClick: props.onClick, children: props.children });
|
|
1848
|
+
}
|
|
1849
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Button, { className: props.className, onClick: props.onClick, type: "button", variant: props.variant, children: props.children ?? props.defaultLabel });
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
// src/react/components/wizard-back.tsx
|
|
1853
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
1854
|
+
function WizardBack(props) {
|
|
1855
|
+
const wizard = useWizard();
|
|
1856
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
1857
|
+
WizardActionButton,
|
|
1858
|
+
{
|
|
1859
|
+
asChild: props.asChild,
|
|
1860
|
+
className: props.className,
|
|
1861
|
+
defaultLabel: "Back",
|
|
1862
|
+
onClick: () => void wizard.back(),
|
|
1863
|
+
variant: "ghost",
|
|
1864
|
+
children: props.children
|
|
1865
|
+
}
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// src/react/components/wizard-category.tsx
|
|
1870
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
1871
|
+
function WizardCategory(props) {
|
|
1872
|
+
const wizard = useWizard();
|
|
1873
|
+
if (wizard.tree.activeCategory.id !== props.categoryId) return null;
|
|
1874
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: props.children });
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// src/utils/cn.ts
|
|
1878
|
+
var import_clsx = require("clsx");
|
|
1879
|
+
var import_tailwind_merge = require("tailwind-merge");
|
|
1880
|
+
function cn(...inputs) {
|
|
1881
|
+
return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(...inputs));
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
// src/react/components/wizard-stepper-dot.tsx
|
|
1885
|
+
var import_lucide_react = require("lucide-react");
|
|
1886
|
+
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1887
|
+
function WizardStepperDot(props) {
|
|
1888
|
+
const { clickable = false, isLast = false, label, onClick, progress, state } = props;
|
|
1889
|
+
const isInteractive = clickable && !!onClick;
|
|
1890
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("li", { className: "relative flex flex-1 items-center", "data-state": state, children: [
|
|
1891
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1892
|
+
"button",
|
|
1893
|
+
{
|
|
1894
|
+
"aria-current": state === "active" ? "step" : void 0,
|
|
1895
|
+
"aria-label": label ?? state,
|
|
1896
|
+
className: cn(
|
|
1897
|
+
"relative flex size-3 shrink-0 items-center justify-center rounded-full border-2 transition-colors",
|
|
1898
|
+
state === "pending" && "border-border bg-background",
|
|
1899
|
+
state === "active" && "border-success-text bg-success-text ring-2 ring-success-border/40 ring-offset-2 ring-offset-background",
|
|
1900
|
+
state === "completed" && "border-success-border bg-success-text",
|
|
1901
|
+
state === "skipped" && "border-border bg-background opacity-60",
|
|
1902
|
+
isInteractive && "cursor-pointer hover:bg-success-bg",
|
|
1903
|
+
!isInteractive && "cursor-default"
|
|
1904
|
+
),
|
|
1905
|
+
disabled: !isInteractive,
|
|
1906
|
+
onClick: isInteractive ? onClick : void 0,
|
|
1907
|
+
type: "button",
|
|
1908
|
+
children: state === "completed" && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react.CheckIcon, { className: "size-2 text-background" })
|
|
1909
|
+
}
|
|
1910
|
+
),
|
|
1911
|
+
!isLast && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { "aria-hidden": true, className: "relative ml-1 mr-1 h-px flex-1 bg-border", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1912
|
+
"span",
|
|
1913
|
+
{
|
|
1914
|
+
className: "absolute inset-y-0 left-0 bg-success-border transition-[width]",
|
|
1915
|
+
style: { width: `${Math.max(0, Math.min(100, progress))}%` }
|
|
1916
|
+
}
|
|
1917
|
+
) }),
|
|
1918
|
+
label && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
1919
|
+
"span",
|
|
1920
|
+
{
|
|
1921
|
+
className: cn(
|
|
1922
|
+
"absolute -top-6 left-0 -translate-x-[calc(50%-0.375rem)] text-sm font-semibold whitespace-nowrap",
|
|
1923
|
+
state === "active" && "text-success-text",
|
|
1924
|
+
state !== "active" && "text-muted-foreground"
|
|
1925
|
+
),
|
|
1926
|
+
children: label
|
|
1927
|
+
}
|
|
1928
|
+
)
|
|
1929
|
+
] });
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
// src/react/components/wizard-stepper.tsx
|
|
1933
|
+
var import_jsx_runtime6 = require("react/jsx-runtime");
|
|
1934
|
+
function WizardStepper(props) {
|
|
1935
|
+
const { canAccessCategory, categories, doneDot, headerI18n, onCategoryClick, progress } = props;
|
|
1936
|
+
const lastIdx = categories.length - 1;
|
|
1937
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("ol", { "aria-label": "Wizard progress", className: "relative flex flex-1 items-center pt-6", children: [
|
|
1938
|
+
categories.map((category, idx) => {
|
|
1939
|
+
const isLast = idx === lastIdx && !doneDot;
|
|
1940
|
+
const clickable = canAccessCategory?.(category) ?? false;
|
|
1941
|
+
return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
|
|
1942
|
+
WizardStepperDot,
|
|
1943
|
+
{
|
|
1944
|
+
clickable,
|
|
1945
|
+
isLast,
|
|
1946
|
+
label: headerI18n?.[category.id],
|
|
1947
|
+
onClick: onCategoryClick ? () => onCategoryClick(category) : void 0,
|
|
1948
|
+
progress: progress[category.id] ?? 0,
|
|
1949
|
+
state: getDotState(category, progress[category.id] ?? 0)
|
|
1950
|
+
},
|
|
1951
|
+
category.id
|
|
1952
|
+
);
|
|
1953
|
+
}),
|
|
1954
|
+
doneDot && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(WizardStepperDot, { isLast: true, label: doneDot.text, progress: 0, state: "pending" })
|
|
1955
|
+
] });
|
|
1956
|
+
}
|
|
1957
|
+
function getDotState(category, progress) {
|
|
1958
|
+
if (category.isActive) return "active";
|
|
1959
|
+
if (progress >= 100) return "completed";
|
|
1960
|
+
if (category.isSkipped) return "skipped";
|
|
1961
|
+
return "pending";
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// src/react/components/wizard-header.tsx
|
|
1965
|
+
var import_jsx_runtime7 = require("react/jsx-runtime");
|
|
1966
|
+
function WizardHeader() {
|
|
1967
|
+
const wizard = useWizard();
|
|
1968
|
+
const tree = wizard.tree;
|
|
1969
|
+
const config = wizard.configOptions;
|
|
1970
|
+
if (!config.isShowWizardHeader) return null;
|
|
1971
|
+
const oneCategory = tree.activeBranch.shownCategories.length === 1 && !config.isDoneDot;
|
|
1972
|
+
const stepHeaderEnabled = true;
|
|
1973
|
+
const showSteps = config.isShowWizardHeaderSteps && stepHeaderEnabled && !oneCategory;
|
|
1974
|
+
const canAccessCategory = (category) => {
|
|
1975
|
+
if (config.isBackResetCompleted) return false;
|
|
1976
|
+
if (category.isCompleted) return true;
|
|
1977
|
+
const idx = tree.activeBranch.shownCategories.indexOf(category);
|
|
1978
|
+
return !!tree.activeBranch.shownCategories[idx - 1]?.isCompleted;
|
|
1979
|
+
};
|
|
1980
|
+
const onCategoryClick = (category) => {
|
|
1981
|
+
if (config.isBackResetCompleted) return;
|
|
1982
|
+
const isCategoryStart = category.activeSteps.length === 1;
|
|
1983
|
+
if (config.isActiveCategoryClickReset && category.isActive && !isCategoryStart) {
|
|
1984
|
+
void wizard.resetCategory(category.id);
|
|
1985
|
+
return;
|
|
1986
|
+
}
|
|
1987
|
+
if (!category.isActive && category.isCompleted) {
|
|
1988
|
+
void wizard.goToCategory(category.id);
|
|
1989
|
+
return;
|
|
1990
|
+
}
|
|
1991
|
+
if (!category.isActive && canAccessCategory(category)) {
|
|
1992
|
+
void wizard.goToCategory(category.id);
|
|
1993
|
+
}
|
|
1994
|
+
};
|
|
1995
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
|
|
1996
|
+
"header",
|
|
1997
|
+
{
|
|
1998
|
+
className: cn(
|
|
1999
|
+
"relative mb-8 flex items-center gap-4 border-b border-border bg-background px-4 py-3",
|
|
2000
|
+
config.stickyHeader && "sticky top-0 z-10"
|
|
2001
|
+
),
|
|
2002
|
+
children: [
|
|
2003
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(WizardBack, {}),
|
|
2004
|
+
showSteps && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
|
|
2005
|
+
WizardStepper,
|
|
2006
|
+
{
|
|
2007
|
+
canAccessCategory,
|
|
2008
|
+
categories: tree.activeBranch.shownCategories,
|
|
2009
|
+
doneDot: config.isDoneDot ? { text: config.doneDotText } : void 0,
|
|
2010
|
+
headerI18n: config.headerI18n,
|
|
2011
|
+
onCategoryClick,
|
|
2012
|
+
progress: wizard.progress
|
|
2013
|
+
}
|
|
2014
|
+
)
|
|
2015
|
+
]
|
|
2016
|
+
}
|
|
2017
|
+
);
|
|
2018
|
+
}
|
|
2019
|
+
|
|
2020
|
+
// src/react/components/wizard-next.tsx
|
|
2021
|
+
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
2022
|
+
function WizardNext(props) {
|
|
2023
|
+
const wizard = useWizard();
|
|
2024
|
+
const defaultLabel = !props.asChild && props.children === void 0 ? getNextLabel(wizard) : "";
|
|
2025
|
+
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
2026
|
+
WizardActionButton,
|
|
2027
|
+
{
|
|
2028
|
+
asChild: props.asChild,
|
|
2029
|
+
className: props.className,
|
|
2030
|
+
defaultLabel,
|
|
2031
|
+
onClick: () => void wizard.next(),
|
|
2032
|
+
children: props.children
|
|
2033
|
+
}
|
|
2034
|
+
);
|
|
2035
|
+
}
|
|
2036
|
+
function getNextLabel(wizard) {
|
|
2037
|
+
const config = wizard.configOptions;
|
|
2038
|
+
const tree = wizard.tree;
|
|
2039
|
+
const activeStep = tree.lastActiveStep;
|
|
2040
|
+
const category = tree.categoryMap[activeStep.categoryId];
|
|
2041
|
+
const isLastStep = category.lastStep.id === activeStep.id;
|
|
2042
|
+
const isLastCategory = tree.activeBranch.lastCategory.id === category.id;
|
|
2043
|
+
if (!isLastStep) return config.nextBtnText;
|
|
2044
|
+
return isLastCategory ? config.finishText : config.nextCategoryText;
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
// src/react/components/wizard-rail.tsx
|
|
2048
|
+
var import_lucide_react2 = require("lucide-react");
|
|
2049
|
+
|
|
2050
|
+
// src/react/hooks/use-wizard-categories-view.ts
|
|
2051
|
+
var EMPTY_SUB_STEPS = [];
|
|
2052
|
+
function useWizardCategoriesView() {
|
|
2053
|
+
const wizard = useWizard();
|
|
2054
|
+
return wizard.tree.activeBranch.shownCategories.map((category) => projectCategory(category));
|
|
2055
|
+
}
|
|
2056
|
+
function getCategoryState(category) {
|
|
2057
|
+
if (category.isActive) return "active";
|
|
2058
|
+
if (category.isCompleted) return "completed";
|
|
2059
|
+
if (category.isSkipped) return "skipped";
|
|
2060
|
+
return "pending";
|
|
2061
|
+
}
|
|
2062
|
+
function projectCategory(category) {
|
|
2063
|
+
const shown = category.shownSteps;
|
|
2064
|
+
const subSteps = shown.length > 1 ? shown.slice().sort((a, b) => a.htmlIndex - b.htmlIndex).map((step) => ({ id: step.id, isActive: step.isActive })) : EMPTY_SUB_STEPS;
|
|
2065
|
+
return {
|
|
2066
|
+
id: category.id,
|
|
2067
|
+
isActive: category.isActive,
|
|
2068
|
+
state: getCategoryState(category),
|
|
2069
|
+
subSteps
|
|
2070
|
+
};
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
// src/react/components/wizard-rail.tsx
|
|
2074
|
+
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
2075
|
+
function WizardRail(props) {
|
|
2076
|
+
const { canAccessCategory, className, headerI18n, subStepLabels } = props;
|
|
2077
|
+
const wizard = useWizard();
|
|
2078
|
+
const view = useWizardCategoriesView();
|
|
2079
|
+
const labels = headerI18n ?? wizard.configOptions.headerI18n;
|
|
2080
|
+
const isClickable = (category) => canAccessCategory?.(category) ?? defaultClickable(category.state);
|
|
2081
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("nav", { "aria-label": "Wizard steps", className: cn("flex flex-col gap-1", className), children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ol", { className: "relative flex flex-col gap-1", children: view.map((category, idx) => {
|
|
2082
|
+
const label = labels?.[category.id] ?? String(category.id);
|
|
2083
|
+
const clickable = isClickable(category);
|
|
2084
|
+
const isLast = idx === view.length - 1;
|
|
2085
|
+
const isPassed = category.state === "completed" || category.state === "skipped";
|
|
2086
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("li", { className: "relative", children: [
|
|
2087
|
+
!isLast && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2088
|
+
"span",
|
|
2089
|
+
{
|
|
2090
|
+
"aria-hidden": true,
|
|
2091
|
+
className: cn(
|
|
2092
|
+
"pointer-events-none absolute top-7 -bottom-2 left-5 w-px -translate-x-1/2 transition-colors duration-200",
|
|
2093
|
+
isPassed ? "bg-success-border" : "bg-border"
|
|
2094
|
+
)
|
|
2095
|
+
}
|
|
2096
|
+
),
|
|
2097
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsxs)(
|
|
2098
|
+
"button",
|
|
2099
|
+
{
|
|
2100
|
+
"aria-current": category.isActive ? "step" : void 0,
|
|
2101
|
+
className: cn(
|
|
2102
|
+
"group flex w-full items-center gap-3 rounded-md px-3 py-2 text-left text-sm font-medium transition-[color,background-color,transform] duration-200 ease-(--ease-out-strong)",
|
|
2103
|
+
category.state === "active" && "bg-success-bg/60 text-success-text",
|
|
2104
|
+
category.state === "completed" && "text-foreground",
|
|
2105
|
+
category.state === "skipped" && "text-muted-foreground opacity-70",
|
|
2106
|
+
category.state === "pending" && "text-muted-foreground",
|
|
2107
|
+
clickable ? "cursor-pointer hover:bg-foreground/[0.06] motion-safe:active:scale-[0.97]" : "cursor-default"
|
|
2108
|
+
),
|
|
2109
|
+
"data-state": category.state,
|
|
2110
|
+
disabled: !clickable,
|
|
2111
|
+
onClick: () => {
|
|
2112
|
+
void wizard.goToCategory(category.id);
|
|
2113
|
+
},
|
|
2114
|
+
type: "button",
|
|
2115
|
+
children: [
|
|
2116
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)(RailDot, { state: category.state }),
|
|
2117
|
+
/* @__PURE__ */ (0, import_jsx_runtime9.jsx)("span", { children: label })
|
|
2118
|
+
]
|
|
2119
|
+
}
|
|
2120
|
+
),
|
|
2121
|
+
category.isActive && category.subSteps.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("ol", { className: "mt-1 ml-7 flex flex-col gap-1 border-l border-border pl-3", children: category.subSteps.map((step) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2122
|
+
"li",
|
|
2123
|
+
{
|
|
2124
|
+
"aria-current": step.isActive ? "step" : void 0,
|
|
2125
|
+
className: cn(
|
|
2126
|
+
"rounded px-2 py-1 text-sm",
|
|
2127
|
+
step.isActive ? "text-success-text font-medium" : "text-muted-foreground"
|
|
2128
|
+
),
|
|
2129
|
+
children: subStepLabels?.[step.id] ?? String(step.id)
|
|
2130
|
+
},
|
|
2131
|
+
step.id
|
|
2132
|
+
)) })
|
|
2133
|
+
] }, category.id);
|
|
2134
|
+
}) }) });
|
|
2135
|
+
}
|
|
2136
|
+
function defaultClickable(state) {
|
|
2137
|
+
return state !== "pending";
|
|
2138
|
+
}
|
|
2139
|
+
function RailDot({ state }) {
|
|
2140
|
+
return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
|
|
2141
|
+
"span",
|
|
2142
|
+
{
|
|
2143
|
+
"aria-hidden": true,
|
|
2144
|
+
className: cn(
|
|
2145
|
+
"flex size-4 shrink-0 items-center justify-center rounded-full border-2 transition-colors",
|
|
2146
|
+
state === "pending" && "border-border bg-background",
|
|
2147
|
+
state === "active" && "border-success-border bg-success-bg",
|
|
2148
|
+
state === "completed" && "border-success-border bg-success-text",
|
|
2149
|
+
state === "skipped" && "border-border bg-background opacity-60"
|
|
2150
|
+
),
|
|
2151
|
+
children: state === "completed" && /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_lucide_react2.CheckIcon, { className: "size-2.5 text-background motion-safe:animate-in motion-safe:fade-in motion-safe:zoom-in-75 motion-safe:duration-200" })
|
|
2152
|
+
}
|
|
2153
|
+
);
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
// src/react/components/wizard-step.tsx
|
|
2157
|
+
var import_react4 = require("react");
|
|
2158
|
+
var import_jsx_runtime10 = require("react/jsx-runtime");
|
|
2159
|
+
function WizardStep(props) {
|
|
2160
|
+
const engine = (0, import_react4.useContext)(WizardEngineContext);
|
|
2161
|
+
if (!engine) {
|
|
2162
|
+
throw new WizardError("WizardStep must be used inside <WizardProvider>");
|
|
2163
|
+
}
|
|
2164
|
+
const getSnapshot = (0, import_react4.useCallback)(() => engine.isStepVisible(props.id), [engine, props.id]);
|
|
2165
|
+
const isVisible = (0, import_react4.useSyncExternalStore)(engine.subscribe, getSnapshot, getSnapshot);
|
|
2166
|
+
if (!isVisible) return null;
|
|
2167
|
+
return /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
2168
|
+
"div",
|
|
2169
|
+
{
|
|
2170
|
+
className: "motion-safe:animate-in motion-safe:fade-in motion-safe:slide-in-from-bottom-1 motion-safe:duration-300 motion-safe:ease-(--ease-out-strong)",
|
|
2171
|
+
id: props.id,
|
|
2172
|
+
children: props.children
|
|
2173
|
+
}
|
|
2174
|
+
);
|
|
2175
|
+
}
|
|
2176
|
+
|
|
2177
|
+
// src/react/provider.tsx
|
|
2178
|
+
var import_react5 = require("react");
|
|
2179
|
+
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
2180
|
+
function WizardProvider(props) {
|
|
2181
|
+
const { activeBranch, children, config, initializers, listeners, scrollContainer, state, strategies } = props;
|
|
2182
|
+
const [bootOptions] = (0, import_react5.useState)(() => ({
|
|
2183
|
+
activeBranch,
|
|
2184
|
+
config,
|
|
2185
|
+
initializers,
|
|
2186
|
+
listeners,
|
|
2187
|
+
scrollContainer,
|
|
2188
|
+
state,
|
|
2189
|
+
strategies
|
|
2190
|
+
}));
|
|
2191
|
+
const [engine, setEngine] = (0, import_react5.useState)(null);
|
|
2192
|
+
(0, import_react5.useEffect)(() => {
|
|
2193
|
+
const newEngine = new WizardEngine(bootOptions);
|
|
2194
|
+
let cancelled = false;
|
|
2195
|
+
newEngine.commitRegistration().then(() => {
|
|
2196
|
+
if (!cancelled) setEngine(newEngine);
|
|
2197
|
+
});
|
|
2198
|
+
return () => {
|
|
2199
|
+
cancelled = true;
|
|
2200
|
+
newEngine.dispose();
|
|
2201
|
+
setEngine((prev) => prev === newEngine ? null : prev);
|
|
2202
|
+
};
|
|
2203
|
+
}, [bootOptions]);
|
|
2204
|
+
if (!engine) {
|
|
2205
|
+
return null;
|
|
2206
|
+
}
|
|
2207
|
+
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)(WizardEngineContext.Provider, { value: engine, children });
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
// src/react/use-wizard-engine-ref.ts
|
|
2211
|
+
var import_react6 = require("react");
|
|
2212
|
+
function useWizardEngineRef() {
|
|
2213
|
+
return (0, import_react6.useRef)(null);
|
|
2214
|
+
}
|
|
2215
|
+
function WizardEngineRefCapture({
|
|
2216
|
+
engineRef
|
|
2217
|
+
}) {
|
|
2218
|
+
const wizard = useWizard();
|
|
2219
|
+
(0, import_react6.useEffect)(() => {
|
|
2220
|
+
engineRef.current = wizard;
|
|
2221
|
+
return () => {
|
|
2222
|
+
engineRef.current = null;
|
|
2223
|
+
};
|
|
2224
|
+
}, [engineRef, wizard]);
|
|
2225
|
+
return null;
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
// src/react/use-wizard-event.ts
|
|
2229
|
+
var import_react7 = require("react");
|
|
2230
|
+
function useWizardEvent(type, handler) {
|
|
2231
|
+
const engine = (0, import_react7.useContext)(WizardEngineContext);
|
|
2232
|
+
if (!engine) {
|
|
2233
|
+
throw new WizardError("useWizardEvent must be used inside <WizardProvider>");
|
|
2234
|
+
}
|
|
2235
|
+
const handlerRef = (0, import_react7.useRef)(handler);
|
|
2236
|
+
(0, import_react7.useEffect)(() => {
|
|
2237
|
+
handlerRef.current = handler;
|
|
2238
|
+
});
|
|
2239
|
+
(0, import_react7.useEffect)(() => {
|
|
2240
|
+
return engine.on(type, (event) => handlerRef.current(event));
|
|
2241
|
+
}, [engine, type]);
|
|
2242
|
+
}
|
|
2243
|
+
|
|
2244
|
+
// src/react/use-wizard-step.ts
|
|
2245
|
+
var import_react8 = require("react");
|
|
2246
|
+
function useWizardStep(stepId) {
|
|
2247
|
+
const engine = (0, import_react8.useContext)(WizardEngineContext);
|
|
2248
|
+
if (!engine) {
|
|
2249
|
+
throw new WizardError("useWizardStep must be used inside <WizardProvider>");
|
|
2250
|
+
}
|
|
2251
|
+
const lastRef = (0, import_react8.useRef)(null);
|
|
2252
|
+
const getSnapshot = (0, import_react8.useCallback)(() => {
|
|
2253
|
+
const tree = engine.getTreeSnapshot();
|
|
2254
|
+
const next = tree.stepMap[stepId] ?? null;
|
|
2255
|
+
const prev = lastRef.current;
|
|
2256
|
+
if (prev !== null && next !== null && prev.isActive === next.isActive && prev.isShow === next.isShow && prev.isCompleted === next.isCompleted && prev.isSkipped === next.isSkipped) {
|
|
2257
|
+
return prev;
|
|
2258
|
+
}
|
|
2259
|
+
lastRef.current = next;
|
|
2260
|
+
return next;
|
|
2261
|
+
}, [engine, stepId]);
|
|
2262
|
+
return (0, import_react8.useSyncExternalStore)(engine.subscribe, getSnapshot, getSnapshot);
|
|
2263
|
+
}
|
|
2264
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
2265
|
+
0 && (module.exports = {
|
|
2266
|
+
TypedEmitter,
|
|
2267
|
+
WizardActiveProgressStrategy,
|
|
2268
|
+
WizardActiveStepRule,
|
|
2269
|
+
WizardBack,
|
|
2270
|
+
WizardCategory,
|
|
2271
|
+
WizardCategoryDirection,
|
|
2272
|
+
WizardCompleteEvent,
|
|
2273
|
+
WizardCompleteTreeState,
|
|
2274
|
+
WizardComponentsProvider,
|
|
2275
|
+
WizardConfig,
|
|
2276
|
+
WizardDefaultInitializer,
|
|
2277
|
+
WizardDefaultVisibilityStrategy,
|
|
2278
|
+
WizardEngine,
|
|
2279
|
+
WizardEngineContext,
|
|
2280
|
+
WizardEngineRefCapture,
|
|
2281
|
+
WizardError,
|
|
2282
|
+
WizardEvent,
|
|
2283
|
+
WizardEventType,
|
|
2284
|
+
WizardExitEvent,
|
|
2285
|
+
WizardExitTreeState,
|
|
2286
|
+
WizardHeader,
|
|
2287
|
+
WizardInitializationError,
|
|
2288
|
+
WizardInitializer,
|
|
2289
|
+
WizardInitializerService,
|
|
2290
|
+
WizardLastActiveVisibilityStrategy,
|
|
2291
|
+
WizardListener,
|
|
2292
|
+
WizardLog,
|
|
2293
|
+
WizardNavigation,
|
|
2294
|
+
WizardNavigationCancelledEvent,
|
|
2295
|
+
WizardNavigationEndEvent,
|
|
2296
|
+
WizardNavigationError,
|
|
2297
|
+
WizardNavigationErrorEvent,
|
|
2298
|
+
WizardNavigationIgnoredEvent,
|
|
2299
|
+
WizardNavigationService,
|
|
2300
|
+
WizardNavigationStartEvent,
|
|
2301
|
+
WizardNext,
|
|
2302
|
+
WizardPassedPrevCategoriesRule,
|
|
2303
|
+
WizardPassedPrevStepsRule,
|
|
2304
|
+
WizardProgressStrategy,
|
|
2305
|
+
WizardProvider,
|
|
2306
|
+
WizardRail,
|
|
2307
|
+
WizardResolveEndEvent,
|
|
2308
|
+
WizardResolveStartEvent,
|
|
2309
|
+
WizardResolverError,
|
|
2310
|
+
WizardRule,
|
|
2311
|
+
WizardScrollEndEvent,
|
|
2312
|
+
WizardScrollStartEvent,
|
|
2313
|
+
WizardScrollStrategy,
|
|
2314
|
+
WizardShownActiveCategoryRule,
|
|
2315
|
+
WizardSingleActiveCategoryRule,
|
|
2316
|
+
WizardSmoothScrollStrategy,
|
|
2317
|
+
WizardStateService,
|
|
2318
|
+
WizardStep,
|
|
2319
|
+
WizardStepHideEvent,
|
|
2320
|
+
WizardStepShowEvent,
|
|
2321
|
+
WizardStepper,
|
|
2322
|
+
WizardStepperDot,
|
|
2323
|
+
WizardStore,
|
|
2324
|
+
WizardTreeStateBuilder,
|
|
2325
|
+
WizardVisibilityStrategy,
|
|
2326
|
+
buildWizardBranchState,
|
|
2327
|
+
buildWizardCategoryState,
|
|
2328
|
+
buildWizardTreeState,
|
|
2329
|
+
composeWizardProviders,
|
|
2330
|
+
scrollToStep,
|
|
2331
|
+
useWizard,
|
|
2332
|
+
useWizardCategoriesView,
|
|
2333
|
+
useWizardComponents,
|
|
2334
|
+
useWizardEngineRef,
|
|
2335
|
+
useWizardEvent,
|
|
2336
|
+
useWizardStep,
|
|
2337
|
+
withConfig,
|
|
2338
|
+
withInitializer,
|
|
2339
|
+
withListener,
|
|
2340
|
+
withProgressStrategy,
|
|
2341
|
+
withScrollContainer,
|
|
2342
|
+
withScrollStrategy,
|
|
2343
|
+
withVisibilityStrategy,
|
|
2344
|
+
wizardDefaultBranch,
|
|
2345
|
+
wizardDefaultConfig,
|
|
2346
|
+
wizardDefaultState,
|
|
2347
|
+
wizardRules
|
|
2348
|
+
});
|
|
2349
|
+
//# sourceMappingURL=index.cjs.map
|