adaptform 2.0.3 → 2.0.5
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 +51 -0
- package/LICENSE.md +2 -2
- package/README.md +420 -1
- package/dist/{Form-DOdrOtbT.d.cts → Form-Be3I6LX0.d.cts} +83 -3
- package/dist/{Form-DOdrOtbT.d.ts → Form-Be3I6LX0.d.ts} +83 -3
- package/dist/index.cjs +341 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +341 -20
- package/dist/index.mjs.map +1 -1
- package/dist/luxon.cjs.map +1 -1
- package/dist/luxon.d.cts +1 -1
- package/dist/luxon.d.ts +1 -1
- package/dist/luxon.mjs.map +1 -1
- package/package.json +18 -13
|
@@ -170,7 +170,7 @@ type FieldTypeConstructor<T, O extends FieldOptions = FieldOptions> = new (optio
|
|
|
170
170
|
declare class Field<T = any, O extends FieldOptions = FieldOptions> {
|
|
171
171
|
protected _rawValue: any;
|
|
172
172
|
protected _valueClear: T | null;
|
|
173
|
-
protected
|
|
173
|
+
protected fieldType: FieldType<T, O>;
|
|
174
174
|
protected defaultValue: T | null;
|
|
175
175
|
protected _errors: string[];
|
|
176
176
|
isValid: boolean | null;
|
|
@@ -192,15 +192,95 @@ declare class Field<T = any, O extends FieldOptions = FieldOptions> {
|
|
|
192
192
|
reset(): void;
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
interface ParsedError {
|
|
196
|
+
fieldName: string;
|
|
197
|
+
message: string;
|
|
198
|
+
sourceKey: string;
|
|
199
|
+
matchedBy: 'static' | 'dynamic' | 'fallback';
|
|
200
|
+
}
|
|
201
|
+
declare class ErrorsSchema {
|
|
202
|
+
private _staticRules;
|
|
203
|
+
private _dynamicRules;
|
|
204
|
+
private _fallbackHandler?;
|
|
205
|
+
private _valueTransformers;
|
|
206
|
+
/**
|
|
207
|
+
* Добавляет статическое правило - точное соответствие ключа ошибки
|
|
208
|
+
* @param errorKey - точный ключ ошибки из ответа сервера
|
|
209
|
+
* @param fieldName - имя поля в форме или функция для динамического определения
|
|
210
|
+
*/
|
|
211
|
+
staticKey(errorKey: string, fieldName: string | ((errorValue: any) => string)): this;
|
|
212
|
+
/**
|
|
213
|
+
* Добавляет динамическое правило с регулярным выражением
|
|
214
|
+
* @param pattern - регулярное выражение для сопоставления ключа ошибки
|
|
215
|
+
* @param mapping - имя поля или функция для извлечения имени поля из совпадения
|
|
216
|
+
* @param options - дополнительные опции (приоритет и т.д.)
|
|
217
|
+
*/
|
|
218
|
+
dynamicKey(pattern: RegExp, mapping: string | ((match: RegExpMatchArray) => string), options?: {
|
|
219
|
+
priority?: number;
|
|
220
|
+
}): this;
|
|
221
|
+
/**
|
|
222
|
+
* Устанавливает обработчик для ошибок, которые не подошли ни под одно правило
|
|
223
|
+
*/
|
|
224
|
+
fallback(handler: (key: string, value: any) => string | null): this;
|
|
225
|
+
/**
|
|
226
|
+
* Добавляет трансформер для значений ошибок
|
|
227
|
+
* Полезно для нормализации сообщений (например, если сервер возвращает массив ошибок)
|
|
228
|
+
*/
|
|
229
|
+
transformValue(transformer: (value: any) => any): this;
|
|
230
|
+
/**
|
|
231
|
+
* Быстрый метод для создания схемы из простого объекта маппинга
|
|
232
|
+
*/
|
|
233
|
+
static fromObject(mapping: Record<string, string>): ErrorsSchema;
|
|
234
|
+
/**
|
|
235
|
+
* Создает схему для массива однотипных форм
|
|
236
|
+
*/
|
|
237
|
+
static forArray(itemSchema: ErrorsSchema, arrayFieldName: string, indexExtractor?: (key: string) => number): ErrorsSchema;
|
|
238
|
+
/**
|
|
239
|
+
* Основной метод парсинга ошибок с поддержкой трансформеров
|
|
240
|
+
* Поддерживает вложенные структуры через точечную нотацию
|
|
241
|
+
*/
|
|
242
|
+
parseErrors(errors: Record<string, any>, prefix?: string): Record<string, string>;
|
|
243
|
+
/**
|
|
244
|
+
* Парсит ошибки сервера и возвращает расширенную информацию
|
|
245
|
+
*/
|
|
246
|
+
parseErrorsDetailed(errors: Record<string, any>, prefix?: string): Record<string, ParsedError>;
|
|
247
|
+
/**
|
|
248
|
+
* Создает схему для вложенной формы
|
|
249
|
+
*/
|
|
250
|
+
nest(prefix: string): ErrorsSchema;
|
|
251
|
+
/**
|
|
252
|
+
* Клонирует текущую схему
|
|
253
|
+
*/
|
|
254
|
+
clone(): ErrorsSchema;
|
|
255
|
+
/**
|
|
256
|
+
* Объединяет текущую схему с другой
|
|
257
|
+
*/
|
|
258
|
+
merge(other: ErrorsSchema): ErrorsSchema;
|
|
259
|
+
private isNestedObject;
|
|
260
|
+
private findMappingDetailed;
|
|
261
|
+
private findMapping;
|
|
262
|
+
}
|
|
263
|
+
|
|
195
264
|
declare abstract class Form {
|
|
196
265
|
private _globalErrors;
|
|
266
|
+
errorsShema: ErrorsSchema;
|
|
197
267
|
protected get _fields(): Record<string, Field<any> | ArrayField<any> | ArrayFormField<any>>;
|
|
198
268
|
checkValid(form?: this): boolean;
|
|
199
269
|
get fieldsValue(): Record<string, any>;
|
|
200
270
|
set fields(data: Record<string, any>);
|
|
201
271
|
get allErrors(): Record<string, string[]>;
|
|
272
|
+
/**
|
|
273
|
+
* Рекурсивно собирает все ошибки из ArrayFormField
|
|
274
|
+
*/
|
|
275
|
+
private collectArrayFormErrors;
|
|
202
276
|
get globalErrors(): Record<string, string>;
|
|
203
|
-
set errors(errors: Record<string,
|
|
277
|
+
set errors(errors: Record<string, any>);
|
|
278
|
+
/**
|
|
279
|
+
* Применяет ошибку к соответствующему полю
|
|
280
|
+
* Поддерживает вложенные поля через точечную нотацию
|
|
281
|
+
*/
|
|
282
|
+
private applyErrorToField;
|
|
283
|
+
private applyNestedError;
|
|
204
284
|
setGlobalError(key: string, message: string): void;
|
|
205
285
|
clearGlobalErrors(): void;
|
|
206
286
|
reset(): void;
|
|
@@ -210,4 +290,4 @@ declare abstract class Form {
|
|
|
210
290
|
toJSON(): Record<string, any>;
|
|
211
291
|
}
|
|
212
292
|
|
|
213
|
-
export {
|
|
293
|
+
export { ArrayField as A, BasePlugin as B, type DecimalOptions as D, Field as F, type MaskPluginOptions as M, type NumberOptions as N, type PluginOptions as P, type StringOptions as S, FieldType as a, type BooleanOptions as b, type DateOptions as c, type ArrayFieldOptions as d, ArrayFormField as e, type ArrayFormFieldOptions as f, type FieldOptions as g, type FieldPlugin as h, Form as i, type messagesArrayFormFieldOptions as j, type messagesBooleanOptions as k, type messagesDateOptions as l, type messagesArrayFieldOptions as m, type messagesDecimalOptions as n, type messagesNumberOptions as o, type messagesOptions as p, type messagesStringOptions as q };
|
package/dist/index.cjs
CHANGED
|
@@ -8,7 +8,7 @@ var Field = class {
|
|
|
8
8
|
this._errors = [];
|
|
9
9
|
this.isValid = null;
|
|
10
10
|
this.isTouched = false;
|
|
11
|
-
this.
|
|
11
|
+
this.fieldType = new TypeConstructor(options ?? {});
|
|
12
12
|
this.defaultValue = defaultValue ?? null;
|
|
13
13
|
for (const plugin of this.options.plugins ?? []) {
|
|
14
14
|
plugin.init?.(this);
|
|
@@ -28,11 +28,11 @@ var Field = class {
|
|
|
28
28
|
});
|
|
29
29
|
}
|
|
30
30
|
get options() {
|
|
31
|
-
return this.
|
|
31
|
+
return this.fieldType.options;
|
|
32
32
|
}
|
|
33
33
|
set options(options) {
|
|
34
|
-
let cur_options = this.
|
|
35
|
-
this.
|
|
34
|
+
let cur_options = this.fieldType.options;
|
|
35
|
+
this.fieldType.options = Object.assign(cur_options, options);
|
|
36
36
|
}
|
|
37
37
|
get rawValue() {
|
|
38
38
|
return this._rawValue;
|
|
@@ -51,7 +51,7 @@ var Field = class {
|
|
|
51
51
|
}
|
|
52
52
|
}
|
|
53
53
|
this._rawValue = rawValue;
|
|
54
|
-
this._valueClear = this.
|
|
54
|
+
this._valueClear = this.fieldType.cast(valueClear);
|
|
55
55
|
this._errors = [];
|
|
56
56
|
this.isTouched = true;
|
|
57
57
|
this.checkAndSetValid();
|
|
@@ -94,11 +94,11 @@ var Field = class {
|
|
|
94
94
|
}
|
|
95
95
|
if (this._valueClear === null) {
|
|
96
96
|
this._errors.push(
|
|
97
|
-
this.options.messages?.type ?? `\u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F ${this.
|
|
97
|
+
this.options.messages?.type ?? `\u041E\u0436\u0438\u0434\u0430\u0435\u0442\u0441\u044F ${this.fieldType.getTypeName()}`
|
|
98
98
|
);
|
|
99
99
|
return false;
|
|
100
100
|
}
|
|
101
|
-
const typeErrors = this.
|
|
101
|
+
const typeErrors = this.fieldType.validate(this._valueClear, form);
|
|
102
102
|
if (typeErrors.length > 0) {
|
|
103
103
|
this._errors.push(...typeErrors);
|
|
104
104
|
return false;
|
|
@@ -131,7 +131,6 @@ var Field = class {
|
|
|
131
131
|
reset() {
|
|
132
132
|
if (this.defaultValue !== null && this.defaultValue !== void 0) {
|
|
133
133
|
this.rawValue = this.defaultValue;
|
|
134
|
-
this.isValid = true;
|
|
135
134
|
} else {
|
|
136
135
|
this._rawValue = null;
|
|
137
136
|
this._valueClear = null;
|
|
@@ -155,6 +154,7 @@ var ArrayField = class {
|
|
|
155
154
|
...options
|
|
156
155
|
};
|
|
157
156
|
this._items = initial.map((v) => this.factory(v));
|
|
157
|
+
this.checkValid();
|
|
158
158
|
}
|
|
159
159
|
// ===== value =====
|
|
160
160
|
set itemsValue(items) {
|
|
@@ -222,7 +222,8 @@ var ArrayField = class {
|
|
|
222
222
|
if (!ok) valid = false;
|
|
223
223
|
}
|
|
224
224
|
if (this.options.validate) {
|
|
225
|
-
const
|
|
225
|
+
const values = this._items.map((i) => i.valueClear);
|
|
226
|
+
const result = this.options.validate(values, form);
|
|
226
227
|
if (result === false) {
|
|
227
228
|
this._errors.push(
|
|
228
229
|
this.options.messages?.validate ?? "\u041E\u0448\u0438\u0431\u043A\u0430 \u0432\u0430\u043B\u0438\u0434\u0430\u0446\u0438\u0438"
|
|
@@ -240,7 +241,8 @@ var ArrayField = class {
|
|
|
240
241
|
return this.checkValid(form);
|
|
241
242
|
}
|
|
242
243
|
get isValid() {
|
|
243
|
-
|
|
244
|
+
if (this._errors.length > 0) return false;
|
|
245
|
+
return this._items.every((i) => i.isValid === true);
|
|
244
246
|
}
|
|
245
247
|
get error() {
|
|
246
248
|
return this._errors[0] ?? "";
|
|
@@ -275,6 +277,7 @@ var ArrayFormField = class {
|
|
|
275
277
|
form.fields = data;
|
|
276
278
|
return form;
|
|
277
279
|
});
|
|
280
|
+
this.checkValid();
|
|
278
281
|
}
|
|
279
282
|
// ===== value =====
|
|
280
283
|
set forms(forms) {
|
|
@@ -366,7 +369,8 @@ var ArrayFormField = class {
|
|
|
366
369
|
return this.checkValid(form);
|
|
367
370
|
}
|
|
368
371
|
get isValid() {
|
|
369
|
-
|
|
372
|
+
if (this._errors.length > 0) return false;
|
|
373
|
+
return this._forms.every((i) => i.checkValid() === true);
|
|
370
374
|
}
|
|
371
375
|
get errors() {
|
|
372
376
|
return this._errors;
|
|
@@ -383,10 +387,255 @@ var ArrayFormField = class {
|
|
|
383
387
|
}
|
|
384
388
|
};
|
|
385
389
|
|
|
390
|
+
// src/core/ErrorsShema.ts
|
|
391
|
+
var ErrorsSchema = class _ErrorsSchema {
|
|
392
|
+
constructor() {
|
|
393
|
+
this._staticRules = /* @__PURE__ */ new Map();
|
|
394
|
+
this._dynamicRules = [];
|
|
395
|
+
this._valueTransformers = [];
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Добавляет статическое правило - точное соответствие ключа ошибки
|
|
399
|
+
* @param errorKey - точный ключ ошибки из ответа сервера
|
|
400
|
+
* @param fieldName - имя поля в форме или функция для динамического определения
|
|
401
|
+
*/
|
|
402
|
+
staticKey(errorKey, fieldName) {
|
|
403
|
+
this._staticRules.set(errorKey, fieldName);
|
|
404
|
+
return this;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Добавляет динамическое правило с регулярным выражением
|
|
408
|
+
* @param pattern - регулярное выражение для сопоставления ключа ошибки
|
|
409
|
+
* @param mapping - имя поля или функция для извлечения имени поля из совпадения
|
|
410
|
+
* @param options - дополнительные опции (приоритет и т.д.)
|
|
411
|
+
*/
|
|
412
|
+
dynamicKey(pattern, mapping, options) {
|
|
413
|
+
this._dynamicRules.push({
|
|
414
|
+
pattern,
|
|
415
|
+
mapping,
|
|
416
|
+
priority: options?.priority ?? 0
|
|
417
|
+
});
|
|
418
|
+
this._dynamicRules.sort((a, b) => b.priority - a.priority);
|
|
419
|
+
return this;
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Устанавливает обработчик для ошибок, которые не подошли ни под одно правило
|
|
423
|
+
*/
|
|
424
|
+
fallback(handler) {
|
|
425
|
+
this._fallbackHandler = handler;
|
|
426
|
+
return this;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Добавляет трансформер для значений ошибок
|
|
430
|
+
* Полезно для нормализации сообщений (например, если сервер возвращает массив ошибок)
|
|
431
|
+
*/
|
|
432
|
+
transformValue(transformer) {
|
|
433
|
+
this._valueTransformers.push(transformer);
|
|
434
|
+
return this;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Быстрый метод для создания схемы из простого объекта маппинга
|
|
438
|
+
*/
|
|
439
|
+
static fromObject(mapping) {
|
|
440
|
+
const schema = new _ErrorsSchema();
|
|
441
|
+
for (const [errorKey, fieldName] of Object.entries(mapping)) {
|
|
442
|
+
schema.staticKey(errorKey, fieldName);
|
|
443
|
+
}
|
|
444
|
+
return schema;
|
|
445
|
+
}
|
|
446
|
+
/**
|
|
447
|
+
* Создает схему для массива однотипных форм
|
|
448
|
+
*/
|
|
449
|
+
static forArray(itemSchema, arrayFieldName, indexExtractor) {
|
|
450
|
+
const schema = new _ErrorsSchema();
|
|
451
|
+
schema.dynamicKey(
|
|
452
|
+
new RegExp(`^${arrayFieldName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\[(\\d+)\\]\\.(.+)$`),
|
|
453
|
+
(match) => {
|
|
454
|
+
const index = match[1];
|
|
455
|
+
const fieldName = match[2];
|
|
456
|
+
return `${arrayFieldName}.${index}.${fieldName}`;
|
|
457
|
+
},
|
|
458
|
+
{ priority: 100 }
|
|
459
|
+
);
|
|
460
|
+
return schema;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Основной метод парсинга ошибок с поддержкой трансформеров
|
|
464
|
+
* Поддерживает вложенные структуры через точечную нотацию
|
|
465
|
+
*/
|
|
466
|
+
parseErrors(errors, prefix = "") {
|
|
467
|
+
const result = {};
|
|
468
|
+
for (const [key, value] of Object.entries(errors)) {
|
|
469
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
470
|
+
if (this.isNestedObject(value)) {
|
|
471
|
+
const nestedErrors = this.parseErrors(value, fullKey);
|
|
472
|
+
Object.assign(result, nestedErrors);
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
if (value === null || value === void 0) {
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
let transformedValue = value;
|
|
479
|
+
for (const transformer of this._valueTransformers) {
|
|
480
|
+
transformedValue = transformer(transformedValue);
|
|
481
|
+
}
|
|
482
|
+
const mapping = this.findMapping(fullKey, transformedValue);
|
|
483
|
+
if (mapping) {
|
|
484
|
+
if (typeof mapping === "function") {
|
|
485
|
+
const fieldName = mapping(transformedValue);
|
|
486
|
+
if (fieldName) {
|
|
487
|
+
result[fieldName] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
488
|
+
}
|
|
489
|
+
} else {
|
|
490
|
+
result[mapping] = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
491
|
+
}
|
|
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
|
+
}
|
|
498
|
+
}
|
|
499
|
+
return result;
|
|
500
|
+
}
|
|
501
|
+
/**
|
|
502
|
+
* Парсит ошибки сервера и возвращает расширенную информацию
|
|
503
|
+
*/
|
|
504
|
+
parseErrorsDetailed(errors, prefix = "") {
|
|
505
|
+
const result = {};
|
|
506
|
+
for (const [key, value] of Object.entries(errors)) {
|
|
507
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
508
|
+
if (this.isNestedObject(value)) {
|
|
509
|
+
const nestedErrors = this.parseErrorsDetailed(value, fullKey);
|
|
510
|
+
Object.assign(result, nestedErrors);
|
|
511
|
+
continue;
|
|
512
|
+
}
|
|
513
|
+
if (value === null || value === void 0) {
|
|
514
|
+
continue;
|
|
515
|
+
}
|
|
516
|
+
let transformedValue = value;
|
|
517
|
+
for (const transformer of this._valueTransformers) {
|
|
518
|
+
transformedValue = transformer(transformedValue);
|
|
519
|
+
}
|
|
520
|
+
const matchResult = this.findMappingDetailed(fullKey, transformedValue);
|
|
521
|
+
if (matchResult) {
|
|
522
|
+
const message = typeof transformedValue === "string" ? transformedValue : JSON.stringify(transformedValue);
|
|
523
|
+
result[matchResult.fieldName] = {
|
|
524
|
+
fieldName: matchResult.fieldName,
|
|
525
|
+
message,
|
|
526
|
+
sourceKey: fullKey,
|
|
527
|
+
matchedBy: matchResult.matchedBy
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
return result;
|
|
532
|
+
}
|
|
533
|
+
/**
|
|
534
|
+
* Создает схему для вложенной формы
|
|
535
|
+
*/
|
|
536
|
+
nest(prefix) {
|
|
537
|
+
const nestedSchema = new _ErrorsSchema();
|
|
538
|
+
for (const [key, mapping] of this._staticRules.entries()) {
|
|
539
|
+
nestedSchema.staticKey(`${prefix}.${key}`, mapping);
|
|
540
|
+
}
|
|
541
|
+
for (const rule of this._dynamicRules) {
|
|
542
|
+
const newPattern = new RegExp(`^${prefix}\\.${rule.pattern.source.replace(/^\^/, "")}`);
|
|
543
|
+
nestedSchema.dynamicKey(newPattern, rule.mapping, { priority: rule.priority });
|
|
544
|
+
}
|
|
545
|
+
for (const transformer of this._valueTransformers) {
|
|
546
|
+
nestedSchema.transformValue(transformer);
|
|
547
|
+
}
|
|
548
|
+
if (this._fallbackHandler) {
|
|
549
|
+
nestedSchema.fallback((key, value) => {
|
|
550
|
+
if (key.startsWith(`${prefix}.`)) {
|
|
551
|
+
return this._fallbackHandler(key.slice(prefix.length + 1), value);
|
|
552
|
+
}
|
|
553
|
+
return null;
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
return nestedSchema;
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Клонирует текущую схему
|
|
560
|
+
*/
|
|
561
|
+
clone() {
|
|
562
|
+
const cloned = new _ErrorsSchema();
|
|
563
|
+
for (const [key, mapping] of this._staticRules.entries()) {
|
|
564
|
+
cloned._staticRules.set(key, mapping);
|
|
565
|
+
}
|
|
566
|
+
for (const rule of this._dynamicRules) {
|
|
567
|
+
cloned._dynamicRules.push({ ...rule });
|
|
568
|
+
}
|
|
569
|
+
cloned._fallbackHandler = this._fallbackHandler;
|
|
570
|
+
cloned._valueTransformers = [...this._valueTransformers];
|
|
571
|
+
return cloned;
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Объединяет текущую схему с другой
|
|
575
|
+
*/
|
|
576
|
+
merge(other) {
|
|
577
|
+
const merged = this.clone();
|
|
578
|
+
for (const [key, mapping] of other._staticRules.entries()) {
|
|
579
|
+
merged._staticRules.set(key, mapping);
|
|
580
|
+
}
|
|
581
|
+
merged._dynamicRules.push(...other._dynamicRules);
|
|
582
|
+
merged._dynamicRules.sort((a, b) => b.priority - a.priority);
|
|
583
|
+
merged._valueTransformers.push(...other._valueTransformers);
|
|
584
|
+
if (other._fallbackHandler) {
|
|
585
|
+
merged._fallbackHandler = other._fallbackHandler;
|
|
586
|
+
}
|
|
587
|
+
return merged;
|
|
588
|
+
}
|
|
589
|
+
isNestedObject(value) {
|
|
590
|
+
return value && typeof value === "object" && !Array.isArray(value) && !(value instanceof Date);
|
|
591
|
+
}
|
|
592
|
+
findMappingDetailed(key, value) {
|
|
593
|
+
if (this._staticRules.has(key)) {
|
|
594
|
+
const mapping = this._staticRules.get(key);
|
|
595
|
+
const fieldName = typeof mapping === "function" ? mapping(value) : mapping;
|
|
596
|
+
return fieldName ? { fieldName, matchedBy: "static" } : null;
|
|
597
|
+
}
|
|
598
|
+
for (const rule of this._dynamicRules) {
|
|
599
|
+
const match = key.match(rule.pattern);
|
|
600
|
+
if (match) {
|
|
601
|
+
const fieldName = typeof rule.mapping === "function" ? rule.mapping(match) : rule.mapping;
|
|
602
|
+
return fieldName ? { fieldName, matchedBy: "dynamic" } : null;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (this._fallbackHandler) {
|
|
606
|
+
const fieldName = this._fallbackHandler(key, value);
|
|
607
|
+
return fieldName ? { fieldName, matchedBy: "fallback" } : null;
|
|
608
|
+
}
|
|
609
|
+
return null;
|
|
610
|
+
}
|
|
611
|
+
findMapping(key, value) {
|
|
612
|
+
if (this._staticRules.has(key)) {
|
|
613
|
+
return this._staticRules.get(key);
|
|
614
|
+
}
|
|
615
|
+
for (const rule of this._dynamicRules) {
|
|
616
|
+
const match = key.match(rule.pattern);
|
|
617
|
+
if (match) {
|
|
618
|
+
if (typeof rule.mapping === "function") {
|
|
619
|
+
const fieldName = rule.mapping(match);
|
|
620
|
+
return fieldName;
|
|
621
|
+
}
|
|
622
|
+
return rule.mapping;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
if (this._fallbackHandler) {
|
|
626
|
+
const fieldName = this._fallbackHandler(key, value);
|
|
627
|
+
return fieldName;
|
|
628
|
+
}
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
};
|
|
632
|
+
var DefaultErrorsSchema = new ErrorsSchema().fallback((key) => key);
|
|
633
|
+
|
|
386
634
|
// src/core/Form.ts
|
|
387
635
|
var Form = class {
|
|
388
636
|
constructor() {
|
|
389
637
|
this._globalErrors = {};
|
|
638
|
+
this.errorsShema = DefaultErrorsSchema;
|
|
390
639
|
}
|
|
391
640
|
get _fields() {
|
|
392
641
|
const fields = {};
|
|
@@ -439,9 +688,17 @@ var Form = class {
|
|
|
439
688
|
const errors = {};
|
|
440
689
|
const fields = this._fields;
|
|
441
690
|
for (const key of Object.keys(fields)) {
|
|
442
|
-
const
|
|
443
|
-
if (
|
|
444
|
-
|
|
691
|
+
const field = fields[key];
|
|
692
|
+
if (field instanceof ArrayFormField) {
|
|
693
|
+
const formErrors = this.collectArrayFormErrors(field);
|
|
694
|
+
if (formErrors.length > 0) {
|
|
695
|
+
errors[key] = formErrors;
|
|
696
|
+
}
|
|
697
|
+
} else {
|
|
698
|
+
const fieldErrors = field.errors;
|
|
699
|
+
if (fieldErrors.length > 0) {
|
|
700
|
+
errors[key] = fieldErrors;
|
|
701
|
+
}
|
|
445
702
|
}
|
|
446
703
|
}
|
|
447
704
|
for (const key of Object.keys(this._globalErrors)) {
|
|
@@ -452,20 +709,84 @@ var Form = class {
|
|
|
452
709
|
}
|
|
453
710
|
return errors;
|
|
454
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Рекурсивно собирает все ошибки из ArrayFormField
|
|
714
|
+
*/
|
|
715
|
+
collectArrayFormErrors(arrayField) {
|
|
716
|
+
const allErrors = [];
|
|
717
|
+
allErrors.push(...arrayField.errors);
|
|
718
|
+
for (let i = 0; i < arrayField.forms.length; i++) {
|
|
719
|
+
const form = arrayField.forms[i];
|
|
720
|
+
const formAllErrors = form.allErrors;
|
|
721
|
+
for (const [fieldName, fieldErrors] of Object.entries(formAllErrors)) {
|
|
722
|
+
for (const error of fieldErrors) {
|
|
723
|
+
allErrors.push(`\u0424\u043E\u0440\u043C\u0430 ${i + 1}, ${fieldName}: ${error}`);
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
return allErrors;
|
|
728
|
+
}
|
|
455
729
|
get globalErrors() {
|
|
456
730
|
return { ...this._globalErrors };
|
|
457
731
|
}
|
|
458
732
|
set errors(errors) {
|
|
459
733
|
const fields = this._fields;
|
|
460
734
|
this._globalErrors = {};
|
|
461
|
-
for (const key of Object.keys(
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
735
|
+
for (const key of Object.keys(fields)) {
|
|
736
|
+
fields[key].error = null;
|
|
737
|
+
}
|
|
738
|
+
const parsedErrors = this.errorsShema.parseErrors(errors);
|
|
739
|
+
for (const [fieldName, errorMessage] of Object.entries(parsedErrors)) {
|
|
740
|
+
this.applyErrorToField(fieldName, errorMessage, fields, this);
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/**
|
|
744
|
+
* Применяет ошибку к соответствующему полю
|
|
745
|
+
* Поддерживает вложенные поля через точечную нотацию
|
|
746
|
+
*/
|
|
747
|
+
applyErrorToField(fieldPath, errorMessage, fields, targetForm) {
|
|
748
|
+
if (!targetForm) {
|
|
749
|
+
console.error("applyErrorToField: targetForm is undefined");
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if (fieldPath in fields && fields[fieldPath]) {
|
|
753
|
+
fields[fieldPath].error = errorMessage;
|
|
754
|
+
return;
|
|
755
|
+
}
|
|
756
|
+
const parts = fieldPath.split(".");
|
|
757
|
+
const rootField = parts[0];
|
|
758
|
+
if (rootField in fields && fields[rootField]) {
|
|
759
|
+
const field = fields[rootField];
|
|
760
|
+
if (parts.length === 1) {
|
|
761
|
+
field.error = errorMessage;
|
|
762
|
+
} else if (parts.length > 1) {
|
|
763
|
+
this.applyNestedError(field, parts.slice(1), errorMessage, fieldPath, targetForm);
|
|
764
|
+
}
|
|
765
|
+
} else {
|
|
766
|
+
targetForm.setGlobalError(fieldPath, errorMessage);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
applyNestedError(field, pathParts, errorMessage, fullPath, targetForm) {
|
|
770
|
+
if (!targetForm) {
|
|
771
|
+
console.error("applyNestedError: targetForm is undefined");
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
const [currentPart, ...remainingParts] = pathParts;
|
|
775
|
+
if (field instanceof ArrayFormField) {
|
|
776
|
+
const index = parseInt(currentPart);
|
|
777
|
+
if (!isNaN(index) && index >= 0 && index < field.forms.length) {
|
|
778
|
+
const form = field.forms[index];
|
|
779
|
+
if (remainingParts.length > 0) {
|
|
780
|
+
const formFields = form["_fields"];
|
|
781
|
+
this.applyErrorToField(remainingParts.join("."), errorMessage, formFields, form);
|
|
782
|
+
} else {
|
|
783
|
+
form.setGlobalError(`index_${index}`, errorMessage);
|
|
784
|
+
}
|
|
466
785
|
} else {
|
|
467
|
-
|
|
786
|
+
targetForm.setGlobalError(fullPath, errorMessage);
|
|
468
787
|
}
|
|
788
|
+
} else {
|
|
789
|
+
targetForm.setGlobalError(fullPath, errorMessage);
|
|
469
790
|
}
|
|
470
791
|
}
|
|
471
792
|
setGlobalError(key, message) {
|