@jfdevelops/multi-step-form-core 1.0.0-alpha.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internals.cjs +10 -0
- package/dist/_internals.cjs.map +1 -0
- package/dist/_internals.mjs +7 -0
- package/dist/_internals.mjs.map +1 -0
- package/dist/errors/invalid-key.d.ts +18 -0
- package/dist/index.cjs +673 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.mjs +668 -0
- package/dist/index.mjs.map +1 -0
- package/dist/internals/index.d.ts +2 -0
- package/dist/internals/step-schema.d.ts +28 -0
- package/dist/observable.d.ts +21 -0
- package/dist/schema.d.ts +22 -0
- package/dist/steps/fields.d.ts +20 -0
- package/dist/steps/index.d.ts +4 -0
- package/dist/steps/schema.d.ts +138 -0
- package/dist/steps/types.d.ts +295 -0
- package/dist/steps/utils.d.ts +21 -0
- package/dist/storage.d.ts +28 -0
- package/dist/subscribable.d.ts +10 -0
- package/dist/types-C4Mgnku6.cjs +949 -0
- package/dist/types-C4Mgnku6.cjs.map +1 -0
- package/dist/types-CYf1_lBe.js +953 -0
- package/dist/types-CYf1_lBe.js.map +1 -0
- package/dist/utils/casing.d.ts +38 -0
- package/dist/utils/field-types.d.ts +6 -0
- package/dist/utils/helper-fns.d.ts +8 -0
- package/dist/utils/helpers.d.ts +8 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/invariant.d.ts +1 -0
- package/dist/utils/logger.d.ts +50 -0
- package/dist/utils/path.d.ts +141 -0
- package/dist/utils/types.d.ts +34 -0
- package/dist/utils/validator.d.ts +74 -0
- package/package.json +40 -0
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const _internals = require("./types-C4Mgnku6.cjs");
|
|
4
|
+
exports.MultiStepFormStepSchemaInternal = _internals.MultiStepFormStepSchemaInternal;
|
|
5
|
+
exports.isValidStepKey = _internals.isValidStepKey;
|
|
6
|
+
Object.defineProperty(exports, "path", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: () => _internals.path
|
|
9
|
+
});
|
|
10
|
+
//# sourceMappingURL=_internals.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_internals.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_internals.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export declare namespace InvalidKeyError {
|
|
2
|
+
type defaultMessages = {
|
|
3
|
+
invalid: string;
|
|
4
|
+
valid: string;
|
|
5
|
+
};
|
|
6
|
+
type keys<TInvalid extends unknown[], TValid extends unknown[]> = {
|
|
7
|
+
invalid: TInvalid;
|
|
8
|
+
valid: TValid;
|
|
9
|
+
};
|
|
10
|
+
type options<TInvalid extends unknown[], TValid extends unknown[]> = {
|
|
11
|
+
messages: defaultMessages;
|
|
12
|
+
keys: keys<TInvalid, TValid>;
|
|
13
|
+
formatter: Intl.ListFormat;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export declare class InvalidKeyError<invalidKeys extends unknown[], validKeys extends unknown[]> extends Error {
|
|
17
|
+
constructor(invalidKeys: invalidKeys, validKeys: validKeys, message?: string | ((options: InvalidKeyError.options<invalidKeys, validKeys>) => string));
|
|
18
|
+
}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,673 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const _internals = require("./types-C4Mgnku6.cjs");
|
|
4
|
+
const CASING_TYPES = [
|
|
5
|
+
"sentence",
|
|
6
|
+
"title",
|
|
7
|
+
"camel",
|
|
8
|
+
"lower",
|
|
9
|
+
"upper",
|
|
10
|
+
"pascal",
|
|
11
|
+
"snake",
|
|
12
|
+
"screaming-snake",
|
|
13
|
+
"flat",
|
|
14
|
+
"kebab"
|
|
15
|
+
];
|
|
16
|
+
const DEFAULT_CASING = "title";
|
|
17
|
+
function changeCasing(input, type) {
|
|
18
|
+
const words = input.replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[-_]+/g, " ").trim().split(/\s+/).map((w) => w.toLowerCase());
|
|
19
|
+
switch (type) {
|
|
20
|
+
case "sentence":
|
|
21
|
+
return words[0].charAt(0).toUpperCase() + words[0].slice(1) + (words.length > 1 ? " " + words.slice(1).join(" ") : "");
|
|
22
|
+
case "title":
|
|
23
|
+
return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
|
|
24
|
+
case "camel":
|
|
25
|
+
return words[0] + words.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
26
|
+
case "pascal":
|
|
27
|
+
return words.map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join("");
|
|
28
|
+
case "lower":
|
|
29
|
+
return words.join(" ");
|
|
30
|
+
case "upper":
|
|
31
|
+
return words.join(" ").toUpperCase();
|
|
32
|
+
case "snake":
|
|
33
|
+
return words.join("_");
|
|
34
|
+
case "screaming-snake":
|
|
35
|
+
return words.join("_").toUpperCase();
|
|
36
|
+
case "kebab":
|
|
37
|
+
return words.join("-");
|
|
38
|
+
case "flat":
|
|
39
|
+
return words.join("");
|
|
40
|
+
default:
|
|
41
|
+
return input;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function quote(str, quoteChar = '"') {
|
|
45
|
+
const startsWithQuote = str.startsWith(quoteChar);
|
|
46
|
+
const endsWithQuote = str.endsWith(quoteChar);
|
|
47
|
+
if (startsWithQuote && endsWithQuote) {
|
|
48
|
+
return str;
|
|
49
|
+
}
|
|
50
|
+
const trimmed = str.replace(/^['"]|['"]$/g, "");
|
|
51
|
+
return `${quoteChar}${trimmed}${quoteChar}`;
|
|
52
|
+
}
|
|
53
|
+
function isCasingValid(value) {
|
|
54
|
+
if (typeof value !== "string") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
const isValid = CASING_TYPES.includes(value);
|
|
58
|
+
return isValid;
|
|
59
|
+
}
|
|
60
|
+
function setCasingType(input, fallback = DEFAULT_CASING) {
|
|
61
|
+
if (isCasingValid(input)) {
|
|
62
|
+
return input;
|
|
63
|
+
}
|
|
64
|
+
return fallback;
|
|
65
|
+
}
|
|
66
|
+
const FIELD_TYPES = [
|
|
67
|
+
"string",
|
|
68
|
+
"string.phone",
|
|
69
|
+
"string.email",
|
|
70
|
+
"string.time",
|
|
71
|
+
"number",
|
|
72
|
+
"number.counter",
|
|
73
|
+
"date",
|
|
74
|
+
"dateTime",
|
|
75
|
+
"boolean.switch"
|
|
76
|
+
];
|
|
77
|
+
const DEFAULT_FIELD_TYPE = "string";
|
|
78
|
+
function isFieldType(value) {
|
|
79
|
+
return typeof value === "string" && FIELD_TYPES.includes(value);
|
|
80
|
+
}
|
|
81
|
+
const WINDOW_UNDEFINED_MESSAGE = '"window" in undefined. No storage is available';
|
|
82
|
+
const DEFAULT_STORAGE_KEY = "MultiStepForm";
|
|
83
|
+
class MultiStepFormStorage {
|
|
84
|
+
key;
|
|
85
|
+
store;
|
|
86
|
+
data;
|
|
87
|
+
log;
|
|
88
|
+
shouldRunActions;
|
|
89
|
+
throwWhenUndefined;
|
|
90
|
+
constructor(config) {
|
|
91
|
+
const { key, data, store, throwWhenUndefined = false } = config;
|
|
92
|
+
this.log = new _internals.MultiStepFormLogger({
|
|
93
|
+
prefix: DEFAULT_STORAGE_KEY
|
|
94
|
+
});
|
|
95
|
+
this.key = key;
|
|
96
|
+
this.data = data;
|
|
97
|
+
this.throwWhenUndefined = throwWhenUndefined;
|
|
98
|
+
if (store) {
|
|
99
|
+
this.store = store;
|
|
100
|
+
this.shouldRunActions = true;
|
|
101
|
+
if (typeof window === "undefined") {
|
|
102
|
+
this.shouldRunActions = false;
|
|
103
|
+
this.log.warn(WINDOW_UNDEFINED_MESSAGE);
|
|
104
|
+
}
|
|
105
|
+
} else if (typeof window !== "undefined") {
|
|
106
|
+
this.store = window.localStorage;
|
|
107
|
+
this.shouldRunActions = true;
|
|
108
|
+
} else {
|
|
109
|
+
this.shouldRunActions = false;
|
|
110
|
+
this.log.warn(WINDOW_UNDEFINED_MESSAGE);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
throwOnEmptyStore() {
|
|
114
|
+
if (!this.throwWhenUndefined) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
_internals.invariant(this.store, () => {
|
|
118
|
+
if (this.shouldRunActions) {
|
|
119
|
+
return WINDOW_UNDEFINED_MESSAGE;
|
|
120
|
+
}
|
|
121
|
+
return "No storage available";
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
resolveValue(value) {
|
|
125
|
+
if (typeof value === "object") {
|
|
126
|
+
return value;
|
|
127
|
+
}
|
|
128
|
+
if (typeof value === "function") {
|
|
129
|
+
return value(this.data);
|
|
130
|
+
}
|
|
131
|
+
this.log.error(
|
|
132
|
+
`The updater value must be a function or object. Was a ${typeof value}`,
|
|
133
|
+
{ throw: true }
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
hasKey() {
|
|
137
|
+
return this.store.getItem(this.key) !== null;
|
|
138
|
+
}
|
|
139
|
+
get() {
|
|
140
|
+
this.throwOnEmptyStore();
|
|
141
|
+
if (!this.shouldRunActions) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
const item = this.store.getItem(this.key);
|
|
145
|
+
if (item) {
|
|
146
|
+
const parsed = JSON.parse(item);
|
|
147
|
+
return parsed;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
remove() {
|
|
151
|
+
this.throwOnEmptyStore();
|
|
152
|
+
if (!this.shouldRunActions) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
this.store.removeItem(this.key);
|
|
156
|
+
}
|
|
157
|
+
add(value) {
|
|
158
|
+
this.throwOnEmptyStore();
|
|
159
|
+
if (!this.shouldRunActions) {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const resolvedValue = JSON.stringify(this.resolveValue(value));
|
|
163
|
+
this.store.setItem(this.key, resolvedValue);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
class Subscribable {
|
|
167
|
+
listeners = /* @__PURE__ */ new Set();
|
|
168
|
+
constructor() {
|
|
169
|
+
this.subscribe = this.subscribe.bind(this);
|
|
170
|
+
}
|
|
171
|
+
subscribe(listener) {
|
|
172
|
+
this.listeners.add(listener);
|
|
173
|
+
this.onSubscribe();
|
|
174
|
+
return () => {
|
|
175
|
+
this.listeners.delete(listener);
|
|
176
|
+
this.onUnsubscribe();
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
hasListeners() {
|
|
180
|
+
return this.listeners.size > 0;
|
|
181
|
+
}
|
|
182
|
+
onSubscribe() {
|
|
183
|
+
}
|
|
184
|
+
onUnsubscribe() {
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
const AS_TYPES = [
|
|
188
|
+
"string",
|
|
189
|
+
"number",
|
|
190
|
+
"array.string",
|
|
191
|
+
"array.string.untyped"
|
|
192
|
+
];
|
|
193
|
+
const VALIDATED_STEP_REGEX = /^step\d+$/i;
|
|
194
|
+
function assertObjectFields(obj, checks) {
|
|
195
|
+
if (typeof obj !== "object" || obj === null) return false;
|
|
196
|
+
for (const key of Object.keys(checks)) {
|
|
197
|
+
if (!(key in obj)) return false;
|
|
198
|
+
const checkFn = checks[key];
|
|
199
|
+
const value = obj[key];
|
|
200
|
+
if (!checkFn(value)) return false;
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
function createFieldLabel(label, fieldName, casingType) {
|
|
205
|
+
return label ?? changeCasing(fieldName, casingType);
|
|
206
|
+
}
|
|
207
|
+
function createStepFields(options) {
|
|
208
|
+
const resolvedFields = {};
|
|
209
|
+
const { fields, defaultCasing, validateFields } = options;
|
|
210
|
+
for (const [name, values] of Object.entries(fields)) {
|
|
211
|
+
_internals.invariant(
|
|
212
|
+
typeof name === "string",
|
|
213
|
+
`Each key for the "fields" option must be a string. Key ${name} was a ${typeof name}`
|
|
214
|
+
);
|
|
215
|
+
_internals.invariant(
|
|
216
|
+
typeof values === "object",
|
|
217
|
+
`The value for key ${name} must be an object. Was ${typeof values}`
|
|
218
|
+
);
|
|
219
|
+
const {
|
|
220
|
+
defaultValue,
|
|
221
|
+
label,
|
|
222
|
+
nameTransformCasing,
|
|
223
|
+
type = DEFAULT_FIELD_TYPE
|
|
224
|
+
} = values;
|
|
225
|
+
if (validateFields) {
|
|
226
|
+
resolvedFields[name] = defaultValue;
|
|
227
|
+
} else {
|
|
228
|
+
const casing = nameTransformCasing ?? defaultCasing;
|
|
229
|
+
resolvedFields[name] = {
|
|
230
|
+
...resolvedFields[name],
|
|
231
|
+
nameTransformCasing: casing,
|
|
232
|
+
type,
|
|
233
|
+
defaultValue,
|
|
234
|
+
label: createFieldLabel(label, name, casing)
|
|
235
|
+
// TODO add more fields here
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
if (validateFields) {
|
|
240
|
+
const validatedFields = _internals.runStandardValidation(
|
|
241
|
+
validateFields,
|
|
242
|
+
resolvedFields
|
|
243
|
+
);
|
|
244
|
+
_internals.invariant(
|
|
245
|
+
typeof validatedFields === "object",
|
|
246
|
+
`The result of the validated fields must be an object, was (${typeof validatedFields}). This is probably an internal error, so open up an issue about it`
|
|
247
|
+
);
|
|
248
|
+
_internals.invariant(
|
|
249
|
+
!!validatedFields,
|
|
250
|
+
"The result of the validated fields must be defined. This is probably an internal error, so open up an issue about it"
|
|
251
|
+
);
|
|
252
|
+
for (const [name, defaultValue] of Object.entries(validatedFields)) {
|
|
253
|
+
const currentField = fields[name];
|
|
254
|
+
_internals.invariant(
|
|
255
|
+
currentField,
|
|
256
|
+
`No field found in the fields config for "${name}"`
|
|
257
|
+
);
|
|
258
|
+
const {
|
|
259
|
+
label,
|
|
260
|
+
type = DEFAULT_FIELD_TYPE,
|
|
261
|
+
nameTransformCasing
|
|
262
|
+
} = currentField;
|
|
263
|
+
const casing = nameTransformCasing ?? defaultCasing;
|
|
264
|
+
resolvedFields[name] = {
|
|
265
|
+
...resolvedFields[name],
|
|
266
|
+
nameTransformCasing: casing,
|
|
267
|
+
type,
|
|
268
|
+
defaultValue,
|
|
269
|
+
label: createFieldLabel(label, name, casing)
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return resolvedFields;
|
|
274
|
+
}
|
|
275
|
+
function createStep(stepsConfig) {
|
|
276
|
+
const resolvedSteps = {};
|
|
277
|
+
_internals.invariant(!!stepsConfig, "The steps config must be defined", TypeError);
|
|
278
|
+
_internals.invariant(
|
|
279
|
+
typeof stepsConfig === "object",
|
|
280
|
+
`The steps config must be an object, was (${typeof stepsConfig})`,
|
|
281
|
+
TypeError
|
|
282
|
+
);
|
|
283
|
+
for (const [stepKey, stepValue] of Object.entries(stepsConfig)) {
|
|
284
|
+
_internals.invariant(
|
|
285
|
+
typeof stepKey === "string",
|
|
286
|
+
`Each key for the step config must be a string. Key "${stepKey}" was ${typeof stepKey} `,
|
|
287
|
+
TypeError
|
|
288
|
+
);
|
|
289
|
+
_internals.invariant(
|
|
290
|
+
VALIDATED_STEP_REGEX.test(stepKey),
|
|
291
|
+
`The key "${stepKey}" isn't formatted properly. Each key in the step config must be the following format: "step{number}"`
|
|
292
|
+
);
|
|
293
|
+
const validStepKey = stepKey;
|
|
294
|
+
const {
|
|
295
|
+
fields,
|
|
296
|
+
title,
|
|
297
|
+
nameTransformCasing: defaultCasing = DEFAULT_CASING,
|
|
298
|
+
description,
|
|
299
|
+
validateFields
|
|
300
|
+
} = stepValue;
|
|
301
|
+
const currentStep = validStepKey.toString().replace("step", "");
|
|
302
|
+
_internals.invariant(
|
|
303
|
+
fields,
|
|
304
|
+
`Missing fields for step ${currentStep} (${String(validStepKey)})`,
|
|
305
|
+
TypeError
|
|
306
|
+
);
|
|
307
|
+
_internals.invariant(
|
|
308
|
+
typeof fields === "object",
|
|
309
|
+
"Fields must be an object",
|
|
310
|
+
TypeError
|
|
311
|
+
);
|
|
312
|
+
_internals.invariant(
|
|
313
|
+
Object.keys(fields).length > 0,
|
|
314
|
+
`The fields config for step ${currentStep} (${String(
|
|
315
|
+
validStepKey
|
|
316
|
+
)}) is empty. Please add a field`
|
|
317
|
+
);
|
|
318
|
+
_internals.invariant(
|
|
319
|
+
typeof fields === "object",
|
|
320
|
+
`The "fields" property must be an object. Was ${typeof fields}`
|
|
321
|
+
);
|
|
322
|
+
const resolvedFields = createStepFields({
|
|
323
|
+
defaultCasing,
|
|
324
|
+
fields,
|
|
325
|
+
validateFields
|
|
326
|
+
});
|
|
327
|
+
resolvedSteps[validStepKey] = {
|
|
328
|
+
...resolvedSteps[validStepKey],
|
|
329
|
+
title,
|
|
330
|
+
nameTransformCasing: defaultCasing,
|
|
331
|
+
// Only add the description if it's defined
|
|
332
|
+
...typeof description === "string" ? { description } : {},
|
|
333
|
+
fields: resolvedFields
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
return resolvedSteps;
|
|
337
|
+
}
|
|
338
|
+
class MultiStepFormStepSchema extends Subscribable {
|
|
339
|
+
/**
|
|
340
|
+
* The original config before any validation or transformations have been applied.
|
|
341
|
+
*/
|
|
342
|
+
original;
|
|
343
|
+
/**
|
|
344
|
+
* The resolved step values.
|
|
345
|
+
*/
|
|
346
|
+
value;
|
|
347
|
+
steps;
|
|
348
|
+
defaultNameTransformationCasing;
|
|
349
|
+
//@ts-ignore
|
|
350
|
+
firstStep;
|
|
351
|
+
lastStep;
|
|
352
|
+
stepNumbers;
|
|
353
|
+
storage;
|
|
354
|
+
#internal;
|
|
355
|
+
constructor(config) {
|
|
356
|
+
super();
|
|
357
|
+
const { steps, nameTransformCasing, storage } = config;
|
|
358
|
+
this.defaultNameTransformationCasing = setCasingType(
|
|
359
|
+
nameTransformCasing
|
|
360
|
+
);
|
|
361
|
+
this.original = steps;
|
|
362
|
+
this.value = createStep(this.original);
|
|
363
|
+
this.storage = new MultiStepFormStorage({
|
|
364
|
+
data: this.value,
|
|
365
|
+
key: storage?.key ?? DEFAULT_STORAGE_KEY,
|
|
366
|
+
store: storage?.store,
|
|
367
|
+
throwWhenUndefined: storage?.throwWhenUndefined ?? false
|
|
368
|
+
});
|
|
369
|
+
this.#internal = new _internals.MultiStepFormStepSchemaInternal({
|
|
370
|
+
getValue: () => this.value,
|
|
371
|
+
setValue: (next) => this.handlePostUpdate(next)
|
|
372
|
+
});
|
|
373
|
+
this.value = this.#internal.enrichValues(this.value);
|
|
374
|
+
this.stepNumbers = Object.keys(this.value).map(
|
|
375
|
+
(key) => Number.parseInt(key.replace("step", ""))
|
|
376
|
+
);
|
|
377
|
+
this.firstStep = this.first();
|
|
378
|
+
this.lastStep = this.last();
|
|
379
|
+
this.steps = {
|
|
380
|
+
first: this.firstStep.step,
|
|
381
|
+
last: this.lastStep.step,
|
|
382
|
+
value: this.stepNumbers,
|
|
383
|
+
as: (asType) => {
|
|
384
|
+
_internals.invariant(
|
|
385
|
+
typeof asType === "string",
|
|
386
|
+
`The type of the target transformation type must be a string, was ${typeof asType}`
|
|
387
|
+
);
|
|
388
|
+
if (asType === "string") {
|
|
389
|
+
return this.stepNumbers.map((value) => `'${value}'`).join(" | ");
|
|
390
|
+
}
|
|
391
|
+
if (asType === "number") {
|
|
392
|
+
return this.stepNumbers.join(" | ");
|
|
393
|
+
}
|
|
394
|
+
if (asType.includes("array.string")) {
|
|
395
|
+
return this.stepNumbers.map((value) => `${value}`);
|
|
396
|
+
}
|
|
397
|
+
throw new Error(
|
|
398
|
+
`Transformation type "${asType}" is not supported. Available transformations include: ${AS_TYPES.map(
|
|
399
|
+
(value) => `"${value}"`
|
|
400
|
+
).join(", ")}`
|
|
401
|
+
);
|
|
402
|
+
},
|
|
403
|
+
isValidStepNumber: (stepNumber) => this.stepNumbers.includes(stepNumber),
|
|
404
|
+
isValidStepKey: (value) => _internals.isValidStepKey(this.value, value)
|
|
405
|
+
};
|
|
406
|
+
this.sync();
|
|
407
|
+
}
|
|
408
|
+
/**
|
|
409
|
+
* @internal
|
|
410
|
+
*/
|
|
411
|
+
__getStorage() {
|
|
412
|
+
return this.storage;
|
|
413
|
+
}
|
|
414
|
+
getSnapshot() {
|
|
415
|
+
return this;
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Syncs the values from storage to {@linkcode value}.
|
|
419
|
+
*/
|
|
420
|
+
sync() {
|
|
421
|
+
const storageValues = this.__getStorage().get();
|
|
422
|
+
if (storageValues) {
|
|
423
|
+
const enrichedValues = this.#internal.enrichValues(storageValues);
|
|
424
|
+
this.value = { ...enrichedValues };
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
notify() {
|
|
428
|
+
for (const listener of this.listeners) {
|
|
429
|
+
listener({
|
|
430
|
+
defaultNameTransformationCasing: this.defaultNameTransformationCasing,
|
|
431
|
+
original: this.original,
|
|
432
|
+
steps: this.steps,
|
|
433
|
+
value: this.value
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Gets the data for a specific step.
|
|
439
|
+
* @param options The options for getting the step data.
|
|
440
|
+
* @returns The step data for the target step.
|
|
441
|
+
*/
|
|
442
|
+
get(options) {
|
|
443
|
+
return _internals.getStep(this.value)(options);
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* Gets the data for the first step.
|
|
447
|
+
* @returns The data for the first step.
|
|
448
|
+
*/
|
|
449
|
+
first() {
|
|
450
|
+
const firstStep = Math.min(...this.stepNumbers);
|
|
451
|
+
return this.get({ step: firstStep });
|
|
452
|
+
}
|
|
453
|
+
/**
|
|
454
|
+
* Gets the data for the last step.
|
|
455
|
+
* @returns The data for the last step.
|
|
456
|
+
*/
|
|
457
|
+
last() {
|
|
458
|
+
const lastStep = Math.max(...this.stepNumbers);
|
|
459
|
+
return this.get({ step: lastStep });
|
|
460
|
+
}
|
|
461
|
+
handlePostUpdate(next) {
|
|
462
|
+
this.value = { ...next };
|
|
463
|
+
this.__getStorage().add(this.value);
|
|
464
|
+
this.sync();
|
|
465
|
+
this.notify();
|
|
466
|
+
}
|
|
467
|
+
update(options) {
|
|
468
|
+
this.#internal.update(options);
|
|
469
|
+
}
|
|
470
|
+
// Implementation
|
|
471
|
+
createHelperFn(options, fn) {
|
|
472
|
+
const { stepData, ...rest } = options;
|
|
473
|
+
return this.#internal.createStepHelperFn(stepData)(rest, fn);
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* Validates that a given object is the proper shape for step data.
|
|
477
|
+
* @param value
|
|
478
|
+
*/
|
|
479
|
+
static hasData(value, options) {
|
|
480
|
+
if (value === null || typeof value !== "object") {
|
|
481
|
+
return false;
|
|
482
|
+
}
|
|
483
|
+
return assertObjectFields(value, {
|
|
484
|
+
title: (v) => typeof v === "string",
|
|
485
|
+
fields: (v) => {
|
|
486
|
+
if (v === null || typeof v !== "object") {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
for (const key of Object.keys(v)) {
|
|
490
|
+
if (typeof key !== "string" || !(key in v)) {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
const current = v[key];
|
|
494
|
+
if (current === null || typeof current !== "object") {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
const hasField = assertObjectFields(current, {
|
|
498
|
+
defaultValue: (v2) => v2 !== "undefined" && v2 !== null,
|
|
499
|
+
label: (v2) => typeof v2 === "string" || typeof v2 === "boolean" && !v2,
|
|
500
|
+
nameTransformCasing: isCasingValid,
|
|
501
|
+
type: isFieldType
|
|
502
|
+
});
|
|
503
|
+
if (!hasField) {
|
|
504
|
+
return false;
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
return true;
|
|
508
|
+
},
|
|
509
|
+
createHelperFn: (v) => typeof v === "function",
|
|
510
|
+
// update: (v): v is GetCurrentStep<resolvedStep, stepNumbers>['update'] =>
|
|
511
|
+
// typeof v === 'function',
|
|
512
|
+
nameTransformCasing: isCasingValid,
|
|
513
|
+
...options?.optionalKeysToCheck
|
|
514
|
+
});
|
|
515
|
+
}
|
|
516
|
+
/**
|
|
517
|
+
* Gets the value of a given field for a given step.
|
|
518
|
+
* @param step The step to get the value from.
|
|
519
|
+
* @param field The field to get the value from.
|
|
520
|
+
* @returns The value of the {@linkcode field}.
|
|
521
|
+
*/
|
|
522
|
+
getValue(step, field) {
|
|
523
|
+
const stepData = this.value[step];
|
|
524
|
+
const baseErrorMessage = `Unable to get the value for "${String(
|
|
525
|
+
step
|
|
526
|
+
)}.fields.${String(field)}"`;
|
|
527
|
+
_internals.invariant("fields" in stepData, baseErrorMessage);
|
|
528
|
+
_internals.invariant(
|
|
529
|
+
typeof stepData.fields === "object",
|
|
530
|
+
`${baseErrorMessage} because "fields" is not an object. This shouldn't be the case, so please open an issue`
|
|
531
|
+
);
|
|
532
|
+
const fields = stepData.fields;
|
|
533
|
+
const defaultValue = _internals.fields.resolvedDeepPath(field, fields);
|
|
534
|
+
return defaultValue;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
class MultiStepFormSchema extends Subscribable {
|
|
538
|
+
defaultNameTransformationCasing;
|
|
539
|
+
stepSchema;
|
|
540
|
+
storage;
|
|
541
|
+
mountCount = 0;
|
|
542
|
+
constructor(options) {
|
|
543
|
+
super();
|
|
544
|
+
const { steps, nameTransformCasing, storage } = options;
|
|
545
|
+
this.defaultNameTransformationCasing = setCasingType(
|
|
546
|
+
nameTransformCasing
|
|
547
|
+
);
|
|
548
|
+
this.stepSchema = new MultiStepFormStepSchema({
|
|
549
|
+
steps,
|
|
550
|
+
nameTransformCasing: this.defaultNameTransformationCasing
|
|
551
|
+
});
|
|
552
|
+
this.storage = new MultiStepFormStorage({
|
|
553
|
+
key: storage?.key ?? DEFAULT_STORAGE_KEY,
|
|
554
|
+
data: this.stepSchema.value,
|
|
555
|
+
store: storage?.store,
|
|
556
|
+
throwWhenUndefined: storage?.throwWhenUndefined ?? false
|
|
557
|
+
});
|
|
558
|
+
this.stepSchema.subscribe(() => {
|
|
559
|
+
this.notify();
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
getSnapshot() {
|
|
563
|
+
return this;
|
|
564
|
+
}
|
|
565
|
+
mount() {
|
|
566
|
+
this.mountCount++;
|
|
567
|
+
if (this.mountCount === 1) {
|
|
568
|
+
this.onMount();
|
|
569
|
+
}
|
|
570
|
+
return () => {
|
|
571
|
+
this.unmount();
|
|
572
|
+
};
|
|
573
|
+
}
|
|
574
|
+
unmount() {
|
|
575
|
+
this.mountCount = Math.max(0, this.mountCount - 1);
|
|
576
|
+
if (this.mountCount === 0) {
|
|
577
|
+
this.onUnmount();
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
isMounted() {
|
|
581
|
+
return this.mountCount > 0;
|
|
582
|
+
}
|
|
583
|
+
onMount() {
|
|
584
|
+
}
|
|
585
|
+
onUnmount() {
|
|
586
|
+
}
|
|
587
|
+
notify() {
|
|
588
|
+
for (const listener of this.listeners) {
|
|
589
|
+
listener({
|
|
590
|
+
nameTransformCasing: this.defaultNameTransformationCasing,
|
|
591
|
+
storage: {
|
|
592
|
+
key: this.storage.key,
|
|
593
|
+
store: this.storage.store
|
|
594
|
+
},
|
|
595
|
+
steps: this.stepSchema.original
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function createMultiStepFormSchema(options) {
|
|
601
|
+
return new MultiStepFormSchema(options);
|
|
602
|
+
}
|
|
603
|
+
class MultiStepFormObserver extends Subscribable {
|
|
604
|
+
schema;
|
|
605
|
+
unsubscribeFromSchema;
|
|
606
|
+
constructor(options) {
|
|
607
|
+
super();
|
|
608
|
+
this.schema = options.schema;
|
|
609
|
+
this.subscribeToSchema();
|
|
610
|
+
}
|
|
611
|
+
subscribeToSchema() {
|
|
612
|
+
this.unsubscribeFromSchema = this.schema.subscribe(() => {
|
|
613
|
+
this.notify();
|
|
614
|
+
});
|
|
615
|
+
}
|
|
616
|
+
getSnapshot() {
|
|
617
|
+
return this;
|
|
618
|
+
}
|
|
619
|
+
getResult() {
|
|
620
|
+
return this.schema;
|
|
621
|
+
}
|
|
622
|
+
setOptions(options) {
|
|
623
|
+
if (options.schema && options.schema !== this.schema) {
|
|
624
|
+
this.unsubscribeFromSchema?.();
|
|
625
|
+
this.schema = options.schema;
|
|
626
|
+
this.subscribeToSchema();
|
|
627
|
+
this.notify();
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
notify() {
|
|
631
|
+
for (const listener of this.listeners) {
|
|
632
|
+
listener(this.schema);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
destroy() {
|
|
636
|
+
this.unsubscribeFromSchema?.();
|
|
637
|
+
this.listeners.clear();
|
|
638
|
+
}
|
|
639
|
+
onSubscribe() {
|
|
640
|
+
}
|
|
641
|
+
onUnsubscribe() {
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
exports.DEFAULT_LOGGER_PREFIX = _internals.DEFAULT_LOGGER_PREFIX;
|
|
645
|
+
Object.defineProperty(exports, "HelperFnChosenSteps", {
|
|
646
|
+
enumerable: true,
|
|
647
|
+
get: () => _internals.HelperFnChosenSteps
|
|
648
|
+
});
|
|
649
|
+
exports.MultiStepFormLogger = _internals.MultiStepFormLogger;
|
|
650
|
+
exports.createCtx = _internals.createCtx;
|
|
651
|
+
Object.defineProperty(exports, "fields", {
|
|
652
|
+
enumerable: true,
|
|
653
|
+
get: () => _internals.fields
|
|
654
|
+
});
|
|
655
|
+
exports.invariant = _internals.invariant;
|
|
656
|
+
exports.CASING_TYPES = CASING_TYPES;
|
|
657
|
+
exports.DEFAULT_CASING = DEFAULT_CASING;
|
|
658
|
+
exports.DEFAULT_FIELD_TYPE = DEFAULT_FIELD_TYPE;
|
|
659
|
+
exports.DEFAULT_STORAGE_KEY = DEFAULT_STORAGE_KEY;
|
|
660
|
+
exports.FIELD_TYPES = FIELD_TYPES;
|
|
661
|
+
exports.MultiStepFormObserver = MultiStepFormObserver;
|
|
662
|
+
exports.MultiStepFormSchema = MultiStepFormSchema;
|
|
663
|
+
exports.MultiStepFormStepSchema = MultiStepFormStepSchema;
|
|
664
|
+
exports.MultiStepFormStorage = MultiStepFormStorage;
|
|
665
|
+
exports.VALIDATED_STEP_REGEX = VALIDATED_STEP_REGEX;
|
|
666
|
+
exports.changeCasing = changeCasing;
|
|
667
|
+
exports.createMultiStepFormSchema = createMultiStepFormSchema;
|
|
668
|
+
exports.createStep = createStep;
|
|
669
|
+
exports.isCasingValid = isCasingValid;
|
|
670
|
+
exports.isFieldType = isFieldType;
|
|
671
|
+
exports.quote = quote;
|
|
672
|
+
exports.setCasingType = setCasingType;
|
|
673
|
+
//# sourceMappingURL=index.cjs.map
|