adaptform 2.0.5 → 2.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 +103 -2
- package/CHANGELOG.ru.md +152 -0
- package/LICENSE.md +3 -1
- package/LICENSE.ru.md +25 -0
- package/README.md +271 -303
- package/README.ru.md +388 -0
- package/dist/{Form-Be3I6LX0.d.cts → Form-kPYL4f-n.d.cts} +58 -36
- package/dist/{Form-Be3I6LX0.d.ts → Form-kPYL4f-n.d.ts} +58 -36
- package/dist/index.cjs +184 -102
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -6
- package/dist/index.d.ts +5 -6
- package/dist/index.mjs +183 -103
- package/dist/index.mjs.map +1 -1
- package/dist/luxon.d.cts +1 -1
- package/dist/luxon.d.ts +1 -1
- package/package.json +28 -9
package/dist/index.cjs
CHANGED
|
@@ -20,8 +20,9 @@ var Field = class {
|
|
|
20
20
|
this._valueClear = null;
|
|
21
21
|
this.isValid = null;
|
|
22
22
|
}
|
|
23
|
+
this.isTouched = false;
|
|
23
24
|
}
|
|
24
|
-
// ==========
|
|
25
|
+
// ========== Getters and setters ==========
|
|
25
26
|
get plugins() {
|
|
26
27
|
return [...this.options.plugins ?? []].sort((a, b) => {
|
|
27
28
|
return (a.pluginOptions?.priority ?? 0) - (b.pluginOptions?.priority ?? 0);
|
|
@@ -31,13 +32,26 @@ var Field = class {
|
|
|
31
32
|
return this.fieldType.options;
|
|
32
33
|
}
|
|
33
34
|
set options(options) {
|
|
34
|
-
|
|
35
|
-
this.fieldType.options = Object.assign(
|
|
35
|
+
const currentOptions = this.fieldType.options;
|
|
36
|
+
this.fieldType.options = Object.assign(currentOptions, options);
|
|
36
37
|
}
|
|
37
38
|
get rawValue() {
|
|
38
39
|
return this._rawValue;
|
|
39
40
|
}
|
|
40
41
|
set rawValue(input) {
|
|
42
|
+
this.setRawValue(input);
|
|
43
|
+
}
|
|
44
|
+
setRawValue(input, validate = true) {
|
|
45
|
+
this.assignRawValue(input);
|
|
46
|
+
this._errors = [];
|
|
47
|
+
this.isTouched = true;
|
|
48
|
+
if (validate) {
|
|
49
|
+
this.checkAndSetValid();
|
|
50
|
+
} else {
|
|
51
|
+
this.isValid = null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
assignRawValue(input) {
|
|
41
55
|
let rawValue = input;
|
|
42
56
|
for (const plugin of this.plugins) {
|
|
43
57
|
if (plugin.toRawValue) {
|
|
@@ -52,9 +66,6 @@ var Field = class {
|
|
|
52
66
|
}
|
|
53
67
|
this._rawValue = rawValue;
|
|
54
68
|
this._valueClear = this.fieldType.cast(valueClear);
|
|
55
|
-
this._errors = [];
|
|
56
|
-
this.isTouched = true;
|
|
57
|
-
this.checkAndSetValid();
|
|
58
69
|
}
|
|
59
70
|
get errors() {
|
|
60
71
|
return this._errors;
|
|
@@ -73,7 +84,7 @@ var Field = class {
|
|
|
73
84
|
get valueClear() {
|
|
74
85
|
return this._valueClear;
|
|
75
86
|
}
|
|
76
|
-
// ==========
|
|
87
|
+
// ========== Validation ==========
|
|
77
88
|
isRequired(form) {
|
|
78
89
|
let isRequired;
|
|
79
90
|
if (typeof this.options.isRequired === "function") {
|
|
@@ -123,55 +134,61 @@ var Field = class {
|
|
|
123
134
|
get isEmpty() {
|
|
124
135
|
let isEmpty = false;
|
|
125
136
|
if (this._rawValue === null || this._rawValue === void 0) isEmpty = true;
|
|
126
|
-
else if (this._rawValue === "") isEmpty = true;
|
|
137
|
+
else if (typeof this._rawValue === "string" && this._rawValue.trim() === "") isEmpty = true;
|
|
127
138
|
else if (Array.isArray(this._rawValue) && this._rawValue.length === 0) isEmpty = true;
|
|
128
139
|
return isEmpty;
|
|
129
140
|
}
|
|
130
|
-
// ==========
|
|
141
|
+
// ========== Helper methods ==========
|
|
131
142
|
reset() {
|
|
132
143
|
if (this.defaultValue !== null && this.defaultValue !== void 0) {
|
|
133
|
-
this.
|
|
144
|
+
this.assignRawValue(this.defaultValue);
|
|
134
145
|
} else {
|
|
135
146
|
this._rawValue = null;
|
|
136
147
|
this._valueClear = null;
|
|
137
|
-
this.isValid = null;
|
|
138
148
|
}
|
|
139
149
|
this._errors = [];
|
|
150
|
+
this.isValid = null;
|
|
140
151
|
this.isTouched = false;
|
|
141
152
|
}
|
|
142
153
|
};
|
|
143
154
|
|
|
155
|
+
// src/core/fields/arrayUtils.ts
|
|
156
|
+
function isValidArrayIndex(index, length) {
|
|
157
|
+
return Number.isInteger(index) && index >= 0 && index < length;
|
|
158
|
+
}
|
|
159
|
+
|
|
144
160
|
// src/core/fields/ArrayField.ts
|
|
145
161
|
var ArrayField = class {
|
|
146
162
|
constructor(factory, options = {}, initial = []) {
|
|
147
163
|
this.factory = factory;
|
|
148
164
|
this._items = [];
|
|
149
165
|
this._errors = [];
|
|
166
|
+
this._hasValidationResult = false;
|
|
150
167
|
this.isTouched = false;
|
|
151
168
|
this.options = {
|
|
152
169
|
isRequired: true,
|
|
153
170
|
messages: {},
|
|
154
171
|
...options
|
|
155
172
|
};
|
|
156
|
-
this._items = initial.map((
|
|
173
|
+
this._items = initial.map((value) => this.factory(value));
|
|
157
174
|
this.checkValid();
|
|
158
175
|
}
|
|
159
176
|
// ===== value =====
|
|
160
177
|
set itemsValue(items) {
|
|
161
178
|
this.clear();
|
|
162
|
-
for (const
|
|
163
|
-
this.add(
|
|
179
|
+
for (const item of items) {
|
|
180
|
+
this.add(item, false);
|
|
164
181
|
}
|
|
165
182
|
this.checkValid();
|
|
166
183
|
}
|
|
167
184
|
get itemsValue() {
|
|
168
|
-
return this._items.map((
|
|
185
|
+
return this._items.map((item) => item.valueClear);
|
|
169
186
|
}
|
|
170
187
|
get items() {
|
|
171
188
|
return this._items;
|
|
172
189
|
}
|
|
173
190
|
// ===== actions =====
|
|
174
|
-
add(value, force_check) {
|
|
191
|
+
add(value, force_check = true) {
|
|
175
192
|
const field = this.factory(value);
|
|
176
193
|
this.isTouched = true;
|
|
177
194
|
this._items.push(field);
|
|
@@ -179,6 +196,7 @@ var ArrayField = class {
|
|
|
179
196
|
return field;
|
|
180
197
|
}
|
|
181
198
|
remove(index) {
|
|
199
|
+
if (!isValidArrayIndex(index, this._items.length)) return;
|
|
182
200
|
this._items.splice(index, 1);
|
|
183
201
|
this.checkValid();
|
|
184
202
|
}
|
|
@@ -197,6 +215,7 @@ var ArrayField = class {
|
|
|
197
215
|
return isRequired;
|
|
198
216
|
}
|
|
199
217
|
checkValid(form) {
|
|
218
|
+
this._hasValidationResult = true;
|
|
200
219
|
this._errors = [];
|
|
201
220
|
if (this.isRequired(form) && this._items.length === 0) {
|
|
202
221
|
this._errors.push(
|
|
@@ -222,7 +241,7 @@ var ArrayField = class {
|
|
|
222
241
|
if (!ok) valid = false;
|
|
223
242
|
}
|
|
224
243
|
if (this.options.validate) {
|
|
225
|
-
const values = this._items.map((
|
|
244
|
+
const values = this._items.map((item) => item.valueClear);
|
|
226
245
|
const result = this.options.validate(values, form);
|
|
227
246
|
if (result === false) {
|
|
228
247
|
this._errors.push(
|
|
@@ -241,8 +260,9 @@ var ArrayField = class {
|
|
|
241
260
|
return this.checkValid(form);
|
|
242
261
|
}
|
|
243
262
|
get isValid() {
|
|
263
|
+
if (!this._hasValidationResult) return false;
|
|
244
264
|
if (this._errors.length > 0) return false;
|
|
245
|
-
return this._items.every((
|
|
265
|
+
return this._items.every((item) => item.isValid === true);
|
|
246
266
|
}
|
|
247
267
|
get error() {
|
|
248
268
|
return this._errors[0] ?? "";
|
|
@@ -250,16 +270,19 @@ var ArrayField = class {
|
|
|
250
270
|
set error(message) {
|
|
251
271
|
if (message) {
|
|
252
272
|
this._errors = [message];
|
|
273
|
+
this._hasValidationResult = true;
|
|
253
274
|
} else {
|
|
254
275
|
this._errors = [];
|
|
276
|
+
this._hasValidationResult = false;
|
|
255
277
|
}
|
|
256
278
|
}
|
|
257
279
|
get errors() {
|
|
258
280
|
return this._errors;
|
|
259
281
|
}
|
|
260
282
|
reset() {
|
|
261
|
-
this._items.forEach((
|
|
283
|
+
this._items.forEach((item) => item.reset());
|
|
262
284
|
this._errors = [];
|
|
285
|
+
this._hasValidationResult = false;
|
|
263
286
|
this.isTouched = false;
|
|
264
287
|
}
|
|
265
288
|
};
|
|
@@ -271,38 +294,45 @@ var ArrayFormField = class {
|
|
|
271
294
|
this.options = options;
|
|
272
295
|
this._forms = [];
|
|
273
296
|
this._errors = [];
|
|
297
|
+
this._hasValidationResult = false;
|
|
274
298
|
this.isTouched = false;
|
|
275
299
|
this._forms = initial.map((data) => {
|
|
276
300
|
const form = new this.FormConstructor();
|
|
277
|
-
form.
|
|
301
|
+
form.setFields(data, false);
|
|
278
302
|
return form;
|
|
279
303
|
});
|
|
280
304
|
this.checkValid();
|
|
281
305
|
}
|
|
282
306
|
// ===== value =====
|
|
283
307
|
set forms(forms) {
|
|
284
|
-
this.
|
|
285
|
-
|
|
286
|
-
|
|
308
|
+
this.setForms(forms);
|
|
309
|
+
}
|
|
310
|
+
setForms(forms, validate = true) {
|
|
311
|
+
this._forms = [];
|
|
312
|
+
for (const formData of forms) {
|
|
313
|
+
this.add(formData, false);
|
|
287
314
|
}
|
|
288
|
-
this.checkValid();
|
|
315
|
+
if (validate) this.checkValid();
|
|
289
316
|
}
|
|
290
317
|
get forms() {
|
|
291
318
|
return this._forms;
|
|
292
319
|
}
|
|
293
320
|
get formsFieldsValue() {
|
|
294
|
-
return this._forms.map((
|
|
321
|
+
return this._forms.map((form) => form.fieldsValue);
|
|
295
322
|
}
|
|
296
323
|
// ===== actions =====
|
|
297
|
-
add(data, force_check) {
|
|
324
|
+
add(data, force_check = true) {
|
|
298
325
|
const form = new this.FormConstructor();
|
|
299
|
-
if (data
|
|
326
|
+
if (data !== void 0 && data !== null) {
|
|
327
|
+
form.setFields(data, false);
|
|
328
|
+
}
|
|
300
329
|
this.isTouched = true;
|
|
301
330
|
this._forms.push(form);
|
|
302
331
|
if (force_check) this.checkValid();
|
|
303
332
|
return form;
|
|
304
333
|
}
|
|
305
334
|
remove(index) {
|
|
335
|
+
if (!isValidArrayIndex(index, this._forms.length)) return;
|
|
306
336
|
this._forms.splice(index, 1);
|
|
307
337
|
this.checkValid();
|
|
308
338
|
}
|
|
@@ -311,8 +341,9 @@ var ArrayFormField = class {
|
|
|
311
341
|
this.checkValid();
|
|
312
342
|
}
|
|
313
343
|
reset() {
|
|
314
|
-
this._forms.forEach((
|
|
344
|
+
this._forms.forEach((form) => form.reset());
|
|
315
345
|
this._errors = [];
|
|
346
|
+
this._hasValidationResult = false;
|
|
316
347
|
this.isTouched = false;
|
|
317
348
|
}
|
|
318
349
|
// ===== validation =====
|
|
@@ -326,8 +357,9 @@ var ArrayFormField = class {
|
|
|
326
357
|
return isRequired;
|
|
327
358
|
}
|
|
328
359
|
checkValid(form) {
|
|
360
|
+
this._hasValidationResult = true;
|
|
329
361
|
this._errors = [];
|
|
330
|
-
if (this.isRequired(form) && this.
|
|
362
|
+
if (this.isRequired(form) && this._forms.length === 0) {
|
|
331
363
|
this._errors.push(
|
|
332
364
|
this.options.messages?.isRequired ?? "\u0414\u043E\u0431\u0430\u0432\u044C\u0442\u0435 \u0445\u043E\u0442\u044F \u0431\u044B \u043E\u0434\u0438\u043D \u044D\u043B\u0435\u043C\u0435\u043D\u0442"
|
|
333
365
|
);
|
|
@@ -346,12 +378,12 @@ var ArrayFormField = class {
|
|
|
346
378
|
return false;
|
|
347
379
|
}
|
|
348
380
|
let valid = true;
|
|
349
|
-
for (const
|
|
350
|
-
const ok =
|
|
381
|
+
for (const nestedForm of this._forms) {
|
|
382
|
+
const ok = nestedForm.checkValid(nestedForm);
|
|
351
383
|
if (!ok) valid = false;
|
|
352
384
|
}
|
|
353
385
|
if (this.options.validate) {
|
|
354
|
-
const result = this.options.validate(this.
|
|
386
|
+
const result = this.options.validate(this._forms, form);
|
|
355
387
|
if (result === false) {
|
|
356
388
|
this._errors.push(
|
|
357
389
|
this.options.messages?.validate ?? "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438"
|
|
@@ -369,8 +401,9 @@ var ArrayFormField = class {
|
|
|
369
401
|
return this.checkValid(form);
|
|
370
402
|
}
|
|
371
403
|
get isValid() {
|
|
404
|
+
if (!this._hasValidationResult) return false;
|
|
372
405
|
if (this._errors.length > 0) return false;
|
|
373
|
-
return this._forms.every((
|
|
406
|
+
return this._forms.every((form) => form.isValid);
|
|
374
407
|
}
|
|
375
408
|
get errors() {
|
|
376
409
|
return this._errors;
|
|
@@ -381,8 +414,10 @@ var ArrayFormField = class {
|
|
|
381
414
|
set error(message) {
|
|
382
415
|
if (message) {
|
|
383
416
|
this._errors = [message];
|
|
417
|
+
this._hasValidationResult = true;
|
|
384
418
|
} else {
|
|
385
419
|
this._errors = [];
|
|
420
|
+
this._hasValidationResult = false;
|
|
386
421
|
}
|
|
387
422
|
}
|
|
388
423
|
};
|
|
@@ -395,19 +430,19 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
395
430
|
this._valueTransformers = [];
|
|
396
431
|
}
|
|
397
432
|
/**
|
|
398
|
-
*
|
|
399
|
-
* @param errorKey -
|
|
400
|
-
* @param fieldName -
|
|
433
|
+
* Add a static rule that matches an exact error key.
|
|
434
|
+
* @param errorKey - Exact error key returned by the server.
|
|
435
|
+
* @param fieldName - Form field name or a function that resolves it dynamically.
|
|
401
436
|
*/
|
|
402
437
|
staticKey(errorKey, fieldName) {
|
|
403
438
|
this._staticRules.set(errorKey, fieldName);
|
|
404
439
|
return this;
|
|
405
440
|
}
|
|
406
441
|
/**
|
|
407
|
-
*
|
|
408
|
-
* @param pattern -
|
|
409
|
-
* @param mapping -
|
|
410
|
-
* @param options -
|
|
442
|
+
* Add a dynamic rule using a regular expression.
|
|
443
|
+
* @param pattern - Regular expression used to match an error key.
|
|
444
|
+
* @param mapping - Field name or function that derives it from the match.
|
|
445
|
+
* @param options - Additional options, such as rule priority.
|
|
411
446
|
*/
|
|
412
447
|
dynamicKey(pattern, mapping, options) {
|
|
413
448
|
this._dynamicRules.push({
|
|
@@ -419,22 +454,22 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
419
454
|
return this;
|
|
420
455
|
}
|
|
421
456
|
/**
|
|
422
|
-
*
|
|
457
|
+
* Set a handler for errors that do not match any rule.
|
|
423
458
|
*/
|
|
424
459
|
fallback(handler) {
|
|
425
460
|
this._fallbackHandler = handler;
|
|
426
461
|
return this;
|
|
427
462
|
}
|
|
428
463
|
/**
|
|
429
|
-
*
|
|
430
|
-
*
|
|
464
|
+
* Add a transformer for error values.
|
|
465
|
+
* Useful for normalizing messages, such as server responses containing arrays.
|
|
431
466
|
*/
|
|
432
467
|
transformValue(transformer) {
|
|
433
468
|
this._valueTransformers.push(transformer);
|
|
434
469
|
return this;
|
|
435
470
|
}
|
|
436
471
|
/**
|
|
437
|
-
*
|
|
472
|
+
* Create a schema from a simple mapping object.
|
|
438
473
|
*/
|
|
439
474
|
static fromObject(mapping) {
|
|
440
475
|
const schema = new _ErrorsSchema();
|
|
@@ -444,24 +479,35 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
444
479
|
return schema;
|
|
445
480
|
}
|
|
446
481
|
/**
|
|
447
|
-
*
|
|
482
|
+
* Create a schema for an array of similarly structured forms.
|
|
448
483
|
*/
|
|
449
484
|
static forArray(itemSchema, arrayFieldName, indexExtractor) {
|
|
450
485
|
const schema = new _ErrorsSchema();
|
|
486
|
+
for (const transformer of itemSchema._valueTransformers) {
|
|
487
|
+
schema.transformValue(transformer);
|
|
488
|
+
}
|
|
489
|
+
const escapedArrayFieldName = arrayFieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
490
|
+
const indexPattern = indexExtractor ? "(.+?)" : "(?:\\[(\\d+)\\]|\\.(\\d+))";
|
|
451
491
|
schema.dynamicKey(
|
|
452
|
-
new RegExp(`^${
|
|
453
|
-
(match) => {
|
|
454
|
-
const
|
|
455
|
-
const
|
|
456
|
-
|
|
492
|
+
new RegExp(`^${escapedArrayFieldName}${indexPattern}\\.(.+)$`),
|
|
493
|
+
(match, value) => {
|
|
494
|
+
const sourceKey = match[0];
|
|
495
|
+
const index = indexExtractor ? indexExtractor(sourceKey) : Number(match[1] ?? match[2]);
|
|
496
|
+
if (!Number.isInteger(index) || index < 0) {
|
|
497
|
+
return "";
|
|
498
|
+
}
|
|
499
|
+
const fieldPath = match[indexExtractor ? 2 : 3];
|
|
500
|
+
const itemMapping = itemSchema.findMapping(fieldPath, value);
|
|
501
|
+
const mappedFieldPath = itemMapping ? typeof itemMapping === "function" ? itemMapping(value) : itemMapping : fieldPath;
|
|
502
|
+
return `${arrayFieldName}.${index}.${mappedFieldPath}`;
|
|
457
503
|
},
|
|
458
504
|
{ priority: 100 }
|
|
459
505
|
);
|
|
460
506
|
return schema;
|
|
461
507
|
}
|
|
462
508
|
/**
|
|
463
|
-
*
|
|
464
|
-
*
|
|
509
|
+
* Parse errors while applying configured value transformers.
|
|
510
|
+
* Supports nested structures using dot notation.
|
|
465
511
|
*/
|
|
466
512
|
parseErrors(errors, prefix = "") {
|
|
467
513
|
const result = {};
|
|
@@ -489,17 +535,12 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
489
535
|
} else {
|
|
490
536
|
result[mapping] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
491
537
|
}
|
|
492
|
-
} else if (this._fallbackHandler) {
|
|
493
|
-
const fieldName = this._fallbackHandler(fullKey, transformedValue);
|
|
494
|
-
if (fieldName) {
|
|
495
|
-
result[fieldName] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
496
|
-
}
|
|
497
538
|
}
|
|
498
539
|
}
|
|
499
540
|
return result;
|
|
500
541
|
}
|
|
501
542
|
/**
|
|
502
|
-
*
|
|
543
|
+
* Parse server errors and return detailed metadata.
|
|
503
544
|
*/
|
|
504
545
|
parseErrorsDetailed(errors, prefix = "") {
|
|
505
546
|
const result = {};
|
|
@@ -531,7 +572,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
531
572
|
return result;
|
|
532
573
|
}
|
|
533
574
|
/**
|
|
534
|
-
*
|
|
575
|
+
* Create a schema for a nested form.
|
|
535
576
|
*/
|
|
536
577
|
nest(prefix) {
|
|
537
578
|
const nestedSchema = new _ErrorsSchema();
|
|
@@ -556,7 +597,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
556
597
|
return nestedSchema;
|
|
557
598
|
}
|
|
558
599
|
/**
|
|
559
|
-
*
|
|
600
|
+
* Clone the current schema.
|
|
560
601
|
*/
|
|
561
602
|
clone() {
|
|
562
603
|
const cloned = new _ErrorsSchema();
|
|
@@ -571,7 +612,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
571
612
|
return cloned;
|
|
572
613
|
}
|
|
573
614
|
/**
|
|
574
|
-
*
|
|
615
|
+
* Merge the current schema with another schema.
|
|
575
616
|
*/
|
|
576
617
|
merge(other) {
|
|
577
618
|
const merged = this.clone();
|
|
@@ -598,7 +639,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
598
639
|
for (const rule of this._dynamicRules) {
|
|
599
640
|
const match = key.match(rule.pattern);
|
|
600
641
|
if (match) {
|
|
601
|
-
const fieldName = typeof rule.mapping === "function" ? rule.mapping(match) : rule.mapping;
|
|
642
|
+
const fieldName = typeof rule.mapping === "function" ? rule.mapping(match, value) : rule.mapping;
|
|
602
643
|
return fieldName ? { fieldName, matchedBy: "dynamic" } : null;
|
|
603
644
|
}
|
|
604
645
|
}
|
|
@@ -616,7 +657,7 @@ var ErrorsSchema = class _ErrorsSchema {
|
|
|
616
657
|
const match = key.match(rule.pattern);
|
|
617
658
|
if (match) {
|
|
618
659
|
if (typeof rule.mapping === "function") {
|
|
619
|
-
const fieldName = rule.mapping(match);
|
|
660
|
+
const fieldName = rule.mapping(match, value);
|
|
620
661
|
return fieldName;
|
|
621
662
|
}
|
|
622
663
|
return rule.mapping;
|
|
@@ -656,7 +697,7 @@ var Form = class {
|
|
|
656
697
|
isFormValid = false;
|
|
657
698
|
}
|
|
658
699
|
}
|
|
659
|
-
return isFormValid;
|
|
700
|
+
return isFormValid && Object.keys(this._globalErrors).length === 0;
|
|
660
701
|
}
|
|
661
702
|
get fieldsValue() {
|
|
662
703
|
const data = {};
|
|
@@ -673,16 +714,20 @@ var Form = class {
|
|
|
673
714
|
return data;
|
|
674
715
|
}
|
|
675
716
|
set fields(data) {
|
|
717
|
+
this.setFields(data);
|
|
718
|
+
}
|
|
719
|
+
setFields(data, validate = true) {
|
|
676
720
|
const fields = this._fields;
|
|
677
721
|
for (const key of Object.keys(data)) {
|
|
678
722
|
const field = fields[key];
|
|
679
723
|
if (field) {
|
|
680
|
-
if (field instanceof Field) field.
|
|
724
|
+
if (field instanceof Field) field.setRawValue(data[key], false);
|
|
681
725
|
else if (field instanceof ArrayField) field.itemsValue = data[key];
|
|
682
|
-
else if (field instanceof ArrayFormField) field.
|
|
726
|
+
else if (field instanceof ArrayFormField) field.setForms(data[key], false);
|
|
683
727
|
else console.error(`Invalid field "${key}" type`);
|
|
684
728
|
}
|
|
685
729
|
}
|
|
730
|
+
if (validate) this.checkValid();
|
|
686
731
|
}
|
|
687
732
|
get allErrors() {
|
|
688
733
|
const errors = {};
|
|
@@ -710,7 +755,7 @@ var Form = class {
|
|
|
710
755
|
return errors;
|
|
711
756
|
}
|
|
712
757
|
/**
|
|
713
|
-
*
|
|
758
|
+
* Recursively collect all errors from an ArrayFormField.
|
|
714
759
|
*/
|
|
715
760
|
collectArrayFormErrors(arrayField) {
|
|
716
761
|
const allErrors = [];
|
|
@@ -730,19 +775,30 @@ var Form = class {
|
|
|
730
775
|
return { ...this._globalErrors };
|
|
731
776
|
}
|
|
732
777
|
set errors(errors) {
|
|
778
|
+
this.clearErrorsRecursively();
|
|
733
779
|
const fields = this._fields;
|
|
734
|
-
this._globalErrors = {};
|
|
735
|
-
for (const key of Object.keys(fields)) {
|
|
736
|
-
fields[key].error = null;
|
|
737
|
-
}
|
|
738
780
|
const parsedErrors = this.errorsShema.parseErrors(errors);
|
|
739
781
|
for (const [fieldName, errorMessage] of Object.entries(parsedErrors)) {
|
|
740
782
|
this.applyErrorToField(fieldName, errorMessage, fields, this);
|
|
741
783
|
}
|
|
742
784
|
}
|
|
785
|
+
clearErrorsRecursively() {
|
|
786
|
+
this._globalErrors = {};
|
|
787
|
+
for (const field of Object.values(this._fields)) {
|
|
788
|
+
if (field instanceof Field) {
|
|
789
|
+
field.error = null;
|
|
790
|
+
} else if (field instanceof ArrayField) {
|
|
791
|
+
field.error = null;
|
|
792
|
+
for (const item of field.items) item.error = null;
|
|
793
|
+
} else if (field instanceof ArrayFormField) {
|
|
794
|
+
field.error = null;
|
|
795
|
+
for (const nestedForm of field.forms) nestedForm.clearErrorsRecursively();
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
}
|
|
743
799
|
/**
|
|
744
|
-
*
|
|
745
|
-
*
|
|
800
|
+
* Apply an error to the corresponding field.
|
|
801
|
+
* Supports nested fields using dot notation.
|
|
746
802
|
*/
|
|
747
803
|
applyErrorToField(fieldPath, errorMessage, fields, targetForm) {
|
|
748
804
|
if (!targetForm) {
|
|
@@ -809,7 +865,7 @@ var Form = class {
|
|
|
809
865
|
}
|
|
810
866
|
return false;
|
|
811
867
|
}
|
|
812
|
-
//
|
|
868
|
+
// Check whether the form is valid.
|
|
813
869
|
get isValid() {
|
|
814
870
|
const fields = this._fields;
|
|
815
871
|
for (const key of Object.keys(fields)) {
|
|
@@ -817,32 +873,50 @@ var Form = class {
|
|
|
817
873
|
}
|
|
818
874
|
return Object.keys(this._globalErrors).length === 0;
|
|
819
875
|
}
|
|
820
|
-
//
|
|
876
|
+
// Serialize form values for multipart requests.
|
|
821
877
|
toFormData() {
|
|
822
878
|
const formData = new FormData();
|
|
823
879
|
const data = this.fieldsValue;
|
|
824
880
|
for (const [key, value] of Object.entries(data)) {
|
|
825
|
-
if (value
|
|
881
|
+
if (value === null || value === void 0) continue;
|
|
882
|
+
if (typeof Blob !== "undefined" && value instanceof Blob) {
|
|
883
|
+
formData.append(key, value);
|
|
884
|
+
} else if (value instanceof Date) {
|
|
826
885
|
formData.append(key, value.toISOString());
|
|
827
|
-
} else if (
|
|
886
|
+
} else if (typeof value === "object") {
|
|
887
|
+
const serialized = JSON.stringify(value);
|
|
888
|
+
if (serialized !== void 0) formData.append(key, serialized);
|
|
889
|
+
} else {
|
|
828
890
|
formData.append(key, String(value));
|
|
829
891
|
}
|
|
830
892
|
}
|
|
831
893
|
return formData;
|
|
832
894
|
}
|
|
833
|
-
//
|
|
895
|
+
// Get all values in JSON-compatible form.
|
|
834
896
|
toJSON() {
|
|
835
|
-
const data = this.fieldsValue;
|
|
836
897
|
const json = {};
|
|
837
|
-
for (const [key, value] of Object.entries(
|
|
838
|
-
|
|
839
|
-
json[key] = value.toISOString();
|
|
840
|
-
} else {
|
|
841
|
-
json[key] = value;
|
|
842
|
-
}
|
|
898
|
+
for (const [key, value] of Object.entries(this.fieldsValue)) {
|
|
899
|
+
json[key] = this.serializeJsonValue(value);
|
|
843
900
|
}
|
|
844
901
|
return json;
|
|
845
902
|
}
|
|
903
|
+
serializeJsonValue(value) {
|
|
904
|
+
if (value instanceof Date) {
|
|
905
|
+
return value.toISOString();
|
|
906
|
+
}
|
|
907
|
+
if (Array.isArray(value)) {
|
|
908
|
+
return value.map((item) => this.serializeJsonValue(item));
|
|
909
|
+
}
|
|
910
|
+
if (value !== null && typeof value === "object") {
|
|
911
|
+
const prototype = Object.getPrototypeOf(value);
|
|
912
|
+
if (prototype === Object.prototype || prototype === null) {
|
|
913
|
+
return Object.fromEntries(
|
|
914
|
+
Object.entries(value).map(([key, nestedValue]) => [key, this.serializeJsonValue(nestedValue)])
|
|
915
|
+
);
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
return value;
|
|
919
|
+
}
|
|
846
920
|
};
|
|
847
921
|
|
|
848
922
|
// src/core/plugins/FieldPlugin.ts
|
|
@@ -862,13 +936,13 @@ var MaskPlugin = class extends BasePlugin {
|
|
|
862
936
|
this.maskParts = [];
|
|
863
937
|
this.config = config;
|
|
864
938
|
}
|
|
865
|
-
init(
|
|
939
|
+
init(_field) {
|
|
866
940
|
this.parseMask();
|
|
867
941
|
}
|
|
868
|
-
toRawValue(value,
|
|
942
|
+
toRawValue(value, _field) {
|
|
869
943
|
return this.applyMask(value);
|
|
870
944
|
}
|
|
871
|
-
toValueClear(value,
|
|
945
|
+
toValueClear(value, _field) {
|
|
872
946
|
return this.removeMask(value);
|
|
873
947
|
}
|
|
874
948
|
parseMask() {
|
|
@@ -1067,8 +1141,12 @@ var StringType = class extends FieldType {
|
|
|
1067
1141
|
this.options.messages?.maxLength ?? `\u041C\u0430\u043A\u0441\u0438\u043C\u0430\u043B\u044C\u043D\u0430\u044F \u0434\u043B\u0438\u043D\u0430 ${this.options.maxLength} \u0441\u0438\u043C\u0432\u043E\u043B\u043E\u0432`
|
|
1068
1142
|
);
|
|
1069
1143
|
}
|
|
1070
|
-
if (this.options.pattern != null
|
|
1071
|
-
|
|
1144
|
+
if (this.options.pattern != null) {
|
|
1145
|
+
const { source, flags } = this.options.pattern;
|
|
1146
|
+
const stablePattern = new RegExp(source, flags.replace(/[gy]/g, ""));
|
|
1147
|
+
if (!stablePattern.test(value)) {
|
|
1148
|
+
errors.push(this.options.messages?.pattern ?? "\u041D\u0435\u0432\u0435\u0440\u043D\u044B\u0439 \u0444\u043E\u0440\u043C\u0430\u0442");
|
|
1149
|
+
}
|
|
1072
1150
|
}
|
|
1073
1151
|
return errors;
|
|
1074
1152
|
}
|
|
@@ -1081,15 +1159,21 @@ var StringType = class extends FieldType {
|
|
|
1081
1159
|
var NumberType = class extends FieldType {
|
|
1082
1160
|
cast(rawValue) {
|
|
1083
1161
|
if (rawValue === null || rawValue === void 0) return null;
|
|
1084
|
-
if (typeof rawValue === "number"
|
|
1162
|
+
if (typeof rawValue === "number") return Number.isFinite(rawValue) ? rawValue : null;
|
|
1085
1163
|
if (typeof rawValue === "string") {
|
|
1086
|
-
const
|
|
1087
|
-
|
|
1164
|
+
const normalized = rawValue.trim();
|
|
1165
|
+
if (normalized.length === 0) return null;
|
|
1166
|
+
const num = Number(normalized);
|
|
1167
|
+
return Number.isFinite(num) ? num : null;
|
|
1088
1168
|
}
|
|
1089
1169
|
return null;
|
|
1090
1170
|
}
|
|
1091
1171
|
validate(value) {
|
|
1092
1172
|
const errors = [];
|
|
1173
|
+
if (!Number.isFinite(value)) {
|
|
1174
|
+
errors.push(this.options.messages?.invalid ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u043A\u043E\u043D\u0435\u0447\u043D\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C");
|
|
1175
|
+
return errors;
|
|
1176
|
+
}
|
|
1093
1177
|
if (!Number.isInteger(value)) {
|
|
1094
1178
|
errors.push(this.options.messages?.invalid ?? "\u0417\u043D\u0430\u0447\u0435\u043D\u0438\u0435 \u0434\u043E\u043B\u0436\u043D\u043E \u0431\u044B\u0442\u044C \u0446\u0435\u043B\u044B\u043C \u0447\u0438\u0441\u043B\u043E\u043C");
|
|
1095
1179
|
}
|
|
@@ -1134,7 +1218,7 @@ var DecimalType = class extends FieldType {
|
|
|
1134
1218
|
if (typeof rawValue === "string") {
|
|
1135
1219
|
return this.castString(rawValue);
|
|
1136
1220
|
}
|
|
1137
|
-
if (typeof rawValue === "number" &&
|
|
1221
|
+
if (typeof rawValue === "number" && Number.isFinite(rawValue)) {
|
|
1138
1222
|
return rawValue;
|
|
1139
1223
|
}
|
|
1140
1224
|
return null;
|
|
@@ -1150,13 +1234,13 @@ var DecimalType = class extends FieldType {
|
|
|
1150
1234
|
}
|
|
1151
1235
|
validate(value) {
|
|
1152
1236
|
const errors = [];
|
|
1153
|
-
value = typeof value === "number" ?
|
|
1237
|
+
value = typeof value === "number" ? String(value) : value;
|
|
1154
1238
|
if (!this.isValidNumberString(value)) {
|
|
1155
1239
|
errors.push(this.options.messages?.invalid ?? "\u041F\u0440\u0438\u043C\u0435\u0440: 1.00");
|
|
1156
1240
|
return errors;
|
|
1157
1241
|
}
|
|
1158
|
-
const maxDigits = this.options.maxDigits
|
|
1159
|
-
const decimalPlaces = this.options.decimalPlaces
|
|
1242
|
+
const maxDigits = this.options.maxDigits ?? 8;
|
|
1243
|
+
const decimalPlaces = this.options.decimalPlaces ?? 2;
|
|
1160
1244
|
const maxIntDigits = maxDigits - decimalPlaces;
|
|
1161
1245
|
const isNegative = value.startsWith("-");
|
|
1162
1246
|
const normalizedValue = isNegative ? value.substring(1) : value;
|
|
@@ -1230,10 +1314,6 @@ var DecimalType = class extends FieldType {
|
|
|
1230
1314
|
if (dotCount > 1) return false;
|
|
1231
1315
|
return pattern.test(value);
|
|
1232
1316
|
}
|
|
1233
|
-
numberToString(value) {
|
|
1234
|
-
const decimalPlaces = this.options.decimalPlaces;
|
|
1235
|
-
return value.toFixed(decimalPlaces);
|
|
1236
|
-
}
|
|
1237
1317
|
getTypeName() {
|
|
1238
1318
|
return "\u0434\u0435\u0441\u044F\u0442\u0438\u0447\u043D\u043E\u0435 \u0447\u0438\u0441\u043B\u043E";
|
|
1239
1319
|
}
|
|
@@ -1310,7 +1390,7 @@ var DateType = class extends FieldType {
|
|
|
1310
1390
|
getTypeName() {
|
|
1311
1391
|
return this.options.isDatetime ? "\u0434\u0430\u0442\u0430 \u0438 \u0432\u0440\u0435\u043C\u044F" : "\u0434\u0430\u0442\u0430";
|
|
1312
1392
|
}
|
|
1313
|
-
//
|
|
1393
|
+
// Compare dates with optional time precision.
|
|
1314
1394
|
compareDates(value, boundary, type) {
|
|
1315
1395
|
if (this.options.isDatetime) {
|
|
1316
1396
|
if (type === "min") {
|
|
@@ -1339,6 +1419,8 @@ exports.BasePlugin = BasePlugin;
|
|
|
1339
1419
|
exports.BooleanType = BooleanType;
|
|
1340
1420
|
exports.DateType = DateType;
|
|
1341
1421
|
exports.DecimalType = DecimalType;
|
|
1422
|
+
exports.DefaultErrorsSchema = DefaultErrorsSchema;
|
|
1423
|
+
exports.ErrorsSchema = ErrorsSchema;
|
|
1342
1424
|
exports.Field = Field;
|
|
1343
1425
|
exports.FieldType = FieldType;
|
|
1344
1426
|
exports.Form = Form;
|