@reformer/core 2.0.1 → 4.0.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/dist/behaviors/compute-from.d.ts +2 -0
- package/dist/behaviors/compute-from.js +31 -0
- package/dist/behaviors/copy-from.d.ts +2 -0
- package/dist/behaviors/copy-from.js +29 -0
- package/dist/behaviors/enable-when.d.ts +2 -0
- package/dist/behaviors/enable-when.js +25 -0
- package/dist/behaviors/reset-when.d.ts +2 -0
- package/dist/behaviors/reset-when.js +24 -0
- package/dist/behaviors/revalidate-when.d.ts +2 -0
- package/dist/behaviors/revalidate-when.js +18 -0
- package/dist/behaviors/sync-fields.d.ts +2 -0
- package/dist/behaviors/sync-fields.js +41 -0
- package/dist/behaviors/transform-value.d.ts +2 -0
- package/dist/behaviors/transform-value.js +45 -0
- package/dist/behaviors/watch-field.d.ts +2 -0
- package/dist/behaviors/watch-field.js +21 -0
- package/dist/behaviors.js +26 -19
- package/dist/core/behavior/behavior-context.d.ts +27 -13
- package/dist/core/behavior/behavior-registry.d.ts +15 -27
- package/dist/core/behavior/behaviors/compute-from.d.ts +50 -21
- package/dist/core/behavior/behaviors/copy-from.d.ts +39 -14
- package/dist/core/behavior/behaviors/enable-when.d.ts +88 -19
- package/dist/core/behavior/behaviors/reset-when.d.ts +31 -18
- package/dist/core/behavior/behaviors/revalidate-when.d.ts +40 -17
- package/dist/core/behavior/behaviors/sync-fields.d.ts +34 -14
- package/dist/core/behavior/behaviors/transform-value.d.ts +116 -44
- package/dist/core/behavior/behaviors/watch-field.d.ts +66 -21
- package/dist/core/behavior/compose-behavior.d.ts +2 -12
- package/dist/core/behavior/index.d.ts +0 -1
- package/dist/core/behavior/types.d.ts +2 -8
- package/dist/core/factories/node-factory.d.ts +6 -29
- package/dist/core/nodes/array-node.d.ts +42 -22
- package/dist/core/nodes/field-node.d.ts +51 -26
- package/dist/core/nodes/form-node.d.ts +18 -20
- package/dist/core/nodes/group-node.d.ts +26 -22
- package/dist/core/types/deep-schema.d.ts +2 -12
- package/dist/core/types/field-path.d.ts +1 -1
- package/dist/core/types/form-context.d.ts +27 -27
- package/dist/core/types/{group-node-proxy.d.ts → form-proxy.d.ts} +12 -42
- package/dist/core/types/index.d.ts +52 -6
- package/dist/core/types/validation-schema.d.ts +3 -12
- package/dist/core/utils/abstract-registry.d.ts +74 -0
- package/dist/core/utils/aggregate-signals.d.ts +71 -0
- package/dist/core/utils/create-form.d.ts +3 -20
- package/dist/core/utils/error-handler.d.ts +1 -18
- package/dist/core/utils/field-path-navigator.d.ts +1 -1
- package/dist/core/utils/field-path.d.ts +23 -11
- package/dist/core/utils/form-observer.d.ts +176 -0
- package/dist/core/utils/form-proxy-builder.d.ts +25 -0
- package/dist/core/utils/form-submitter.d.ts +121 -0
- package/dist/core/utils/index.d.ts +9 -2
- package/dist/core/utils/registry-helpers.d.ts +0 -7
- package/dist/core/utils/safe-effect.d.ts +73 -0
- package/dist/core/utils/status-machine.d.ts +153 -0
- package/dist/core/utils/type-guards.d.ts +5 -23
- package/dist/core/utils/unique-id.d.ts +53 -0
- package/dist/core/validation/core/apply-when.d.ts +3 -9
- package/dist/core/validation/core/apply.d.ts +2 -13
- package/dist/core/validation/core/validate-async.d.ts +2 -8
- package/dist/core/validation/core/validate-tree.d.ts +0 -6
- package/dist/core/validation/core/validate.d.ts +1 -7
- package/dist/core/validation/index.d.ts +8 -2
- package/dist/core/validation/validate-form.d.ts +1 -38
- package/dist/core/validation/validation-applicator.d.ts +2 -21
- package/dist/core/validation/validation-context.d.ts +59 -43
- package/dist/core/validation/validation-registry.d.ts +11 -25
- package/dist/core/validation/validators/array-validators.d.ts +2 -12
- package/dist/core/validation/validators/date-utils.d.ts +26 -0
- package/dist/core/validation/validators/email.d.ts +2 -9
- package/dist/core/validation/validators/future-date.d.ts +35 -0
- package/dist/core/validation/validators/index.d.ts +7 -1
- package/dist/core/validation/validators/is-date.d.ts +36 -0
- package/dist/core/validation/validators/max-age.d.ts +36 -0
- package/dist/core/validation/validators/max-date.d.ts +36 -0
- package/dist/core/validation/validators/max-length.d.ts +3 -10
- package/dist/core/validation/validators/max.d.ts +3 -10
- package/dist/core/validation/validators/min-age.d.ts +36 -0
- package/dist/core/validation/validators/min-date.d.ts +36 -0
- package/dist/core/validation/validators/min-length.d.ts +3 -10
- package/dist/core/validation/validators/min.d.ts +3 -10
- package/dist/core/validation/validators/number.d.ts +2 -9
- package/dist/core/validation/validators/past-date.d.ts +35 -0
- package/dist/core/validation/validators/pattern.d.ts +2 -9
- package/dist/core/validation/validators/phone.d.ts +2 -9
- package/dist/core/validation/validators/required.d.ts +2 -9
- package/dist/core/validation/validators/url.d.ts +2 -9
- package/dist/date-utils-xUWFslTj.js +29 -0
- package/dist/field-path-DuKdGcIE.js +66 -0
- package/dist/hooks/types.d.ts +1 -1
- package/dist/hooks/useArrayLength.d.ts +31 -0
- package/dist/hooks/useFormControl.d.ts +4 -4
- package/dist/hooks/useFormControlValue.d.ts +2 -2
- package/dist/hooks/useHiddenCondition.d.ts +25 -0
- package/dist/hooks/useSignalSubscription.d.ts +1 -1
- package/dist/index-D25LsbRm.js +73 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1171 -786
- package/dist/registry-helpers-Bv_BJ1s-.js +615 -0
- package/dist/safe-effect-Dh8uw81c.js +20 -0
- package/dist/validate-C3XiA_zf.js +10 -0
- package/dist/validators/email.d.ts +2 -0
- package/dist/validators/email.js +13 -0
- package/dist/validators/future-date.d.ts +2 -0
- package/dist/validators/future-date.js +20 -0
- package/dist/validators/is-date.d.ts +2 -0
- package/dist/validators/is-date.js +12 -0
- package/dist/validators/max-age.d.ts +2 -0
- package/dist/validators/max-age.js +20 -0
- package/dist/validators/max-date.d.ts +2 -0
- package/dist/validators/max-date.js +20 -0
- package/dist/validators/max-length.d.ts +2 -0
- package/dist/validators/max-length.js +11 -0
- package/dist/validators/max.d.ts +2 -0
- package/dist/validators/max.js +11 -0
- package/dist/validators/min-age.d.ts +2 -0
- package/dist/validators/min-age.js +20 -0
- package/dist/validators/min-date.d.ts +2 -0
- package/dist/validators/min-date.js +20 -0
- package/dist/validators/min-length.d.ts +2 -0
- package/dist/validators/min-length.js +11 -0
- package/dist/validators/min.d.ts +2 -0
- package/dist/validators/min.js +11 -0
- package/dist/validators/number.d.ts +2 -0
- package/dist/validators/number.js +35 -0
- package/dist/validators/past-date.d.ts +2 -0
- package/dist/validators/past-date.js +20 -0
- package/dist/validators/pattern.d.ts +2 -0
- package/dist/validators/pattern.js +11 -0
- package/dist/validators/phone.d.ts +2 -0
- package/dist/validators/phone.js +35 -0
- package/dist/validators/required.d.ts +2 -0
- package/dist/validators/required.js +15 -0
- package/dist/validators/url.d.ts +2 -0
- package/dist/validators/url.js +19 -0
- package/dist/validators-BGsNOgT1.js +207 -0
- package/dist/validators.js +54 -29
- package/llms.txt +7885 -318
- package/package.json +83 -8
- package/dist/behaviors-DzYL8kY_.js +0 -499
- package/dist/core/behavior/create-field-path.d.ts +0 -7
- package/dist/core/context/form-context-impl.d.ts +0 -29
- package/dist/core/utils/debounce.d.ts +0 -160
- package/dist/core/utils/resources.d.ts +0 -41
- package/dist/core/validation/field-path.d.ts +0 -7
- package/dist/core/validation/validators/date.d.ts +0 -38
- package/dist/registry-helpers-BRxAr6nG.js +0 -490
- package/dist/validators-gXoHPdqM.js +0 -418
|
@@ -1,34 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Типы для Proxy-доступа к полям GroupNode
|
|
3
|
-
*
|
|
4
|
-
* Решает проблему типизации, когда GroupNode<T> использует Proxy для прямого доступа к полям.
|
|
5
|
-
* TypeScript не может автоматически определить правильные типы для вложенных форм и массивов.
|
|
6
|
-
*
|
|
7
|
-
* @group Types
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* interface MyForm {
|
|
12
|
-
* name: string;
|
|
13
|
-
* address: {
|
|
14
|
-
* city: string;
|
|
15
|
-
* };
|
|
16
|
-
* items: Array<{ title: string }>;
|
|
17
|
-
* }
|
|
18
|
-
*
|
|
19
|
-
* const form: GroupNodeWithControls<MyForm> = new GroupNode(schema);
|
|
20
|
-
*
|
|
21
|
-
* // TypeScript знает, что это FieldNode<string>
|
|
22
|
-
* form.name.setValue('John');
|
|
23
|
-
*
|
|
24
|
-
* // TypeScript знает, что это GroupNode<{city: string}>
|
|
25
|
-
* form.address.city.setValue('Moscow');
|
|
26
|
-
*
|
|
27
|
-
* // TypeScript знает, что это ArrayNode<{title: string}>
|
|
28
|
-
* form.items.push({ title: 'New Item' });
|
|
29
|
-
* ```
|
|
30
|
-
*/
|
|
31
|
-
import type { FormFields } from './index';
|
|
1
|
+
import { FormFields } from './index';
|
|
32
2
|
type FieldNode<_T> = any;
|
|
33
3
|
type GroupNode<_T> = any;
|
|
34
4
|
type ArrayNode<_T> = any;
|
|
@@ -36,9 +6,9 @@ type ArrayNode<_T> = any;
|
|
|
36
6
|
* Мапит тип модели данных T на правильные типы узлов формы
|
|
37
7
|
*
|
|
38
8
|
* Рекурсивно определяет типы узлов на основе структуры данных:
|
|
39
|
-
* - `T[K] extends Array<infer U>` где U - объект → `
|
|
9
|
+
* - `T[K] extends Array<infer U>` где U - объект → `FormArrayProxy<U>`
|
|
40
10
|
* - `T[K] extends Array<infer U>` где U - примитив → `FieldNode<T[K]>` (массив как обычное поле)
|
|
41
|
-
* - `T[K] extends object` → `
|
|
11
|
+
* - `T[K] extends object` → `FormProxy<T[K]>` (вложенная форма с типизацией)
|
|
42
12
|
* - `T[K]` примитив → `FieldNode<T[K]>` (простое поле)
|
|
43
13
|
*
|
|
44
14
|
* Использует NonNullable для правильной обработки опциональных полей
|
|
@@ -48,8 +18,8 @@ type ArrayNode<_T> = any;
|
|
|
48
18
|
*
|
|
49
19
|
* @template T - Тип модели данных формы
|
|
50
20
|
*/
|
|
51
|
-
export type
|
|
52
|
-
[K in keyof T]: NonNullable<T[K]> extends Array<infer U> ? U extends FormFields ?
|
|
21
|
+
export type FormControlsProxy<T> = {
|
|
22
|
+
[K in keyof T]: NonNullable<T[K]> extends Array<infer U> ? U extends FormFields ? FormArrayProxy<U> : FieldNode<T[K]> : NonNullable<T[K]> extends FormFields ? NonNullable<T[K]> extends Date | File | Blob ? FieldNode<T[K]> : FormProxy<NonNullable<T[K]>> : FieldNode<T[K]>;
|
|
53
23
|
};
|
|
54
24
|
/**
|
|
55
25
|
* Комбинированный тип для GroupNode с Proxy доступом к полям
|
|
@@ -72,7 +42,7 @@ export type FormNodeControls<T> = {
|
|
|
72
42
|
* };
|
|
73
43
|
* }
|
|
74
44
|
*
|
|
75
|
-
* const form
|
|
45
|
+
* const form = createForm<UserForm>(schema);
|
|
76
46
|
*
|
|
77
47
|
* // Доступ к методам GroupNode
|
|
78
48
|
* await form.validate();
|
|
@@ -84,7 +54,7 @@ export type FormNodeControls<T> = {
|
|
|
84
54
|
* form.profile.name.setValue('John');
|
|
85
55
|
* ```
|
|
86
56
|
*/
|
|
87
|
-
export type
|
|
57
|
+
export type FormProxy<T> = GroupNode<T> & FormControlsProxy<T>;
|
|
88
58
|
/**
|
|
89
59
|
* Комбинированный тип для ArrayNode с Proxy доступом к элементам
|
|
90
60
|
*
|
|
@@ -102,7 +72,7 @@ export type GroupNodeWithControls<T> = GroupNode<T> & FormNodeControls<T>;
|
|
|
102
72
|
* completed: boolean;
|
|
103
73
|
* }
|
|
104
74
|
*
|
|
105
|
-
* const todos:
|
|
75
|
+
* const todos: FormArrayProxy<TodoItem> = new ArrayNode(schema);
|
|
106
76
|
*
|
|
107
77
|
* // Доступ к методам ArrayNode
|
|
108
78
|
* todos.push({ title: 'New todo', completed: false });
|
|
@@ -117,19 +87,19 @@ export type GroupNodeWithControls<T> = GroupNode<T> & FormNodeControls<T>;
|
|
|
117
87
|
* });
|
|
118
88
|
* ```
|
|
119
89
|
*/
|
|
120
|
-
export type
|
|
90
|
+
export type FormArrayProxy<T extends FormFields> = ArrayNode<T> & {
|
|
121
91
|
/**
|
|
122
92
|
* Безопасный доступ к элементу массива по индексу
|
|
123
93
|
* Возвращает GroupNode с типизированными полями или undefined
|
|
124
94
|
*/
|
|
125
|
-
at(index: number):
|
|
95
|
+
at(index: number): FormProxy<T> | undefined;
|
|
126
96
|
/**
|
|
127
97
|
* Итерация по элементам массива с типизированными элементами
|
|
128
98
|
*/
|
|
129
|
-
forEach(callback: (item:
|
|
99
|
+
forEach(callback: (item: FormProxy<T>, index: number) => void): void;
|
|
130
100
|
/**
|
|
131
101
|
* Маппинг элементов массива с типизированными элементами
|
|
132
102
|
*/
|
|
133
|
-
map<R>(callback: (item:
|
|
103
|
+
map<R>(callback: (item: FormProxy<T>, index: number) => R): R[];
|
|
134
104
|
};
|
|
135
105
|
export {};
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { BehaviorSchemaFn } from '../behavior/types';
|
|
2
|
+
import { FormSchema } from './deep-schema';
|
|
3
|
+
import { ValidationSchemaFn } from './validation-schema';
|
|
4
|
+
import { FormNode } from '../nodes/form-node';
|
|
1
5
|
/**
|
|
2
6
|
* Represents any valid form value type
|
|
3
7
|
* Use this instead of 'any' for form values to maintain type safety
|
|
@@ -20,12 +24,46 @@ export type UnknownFormValue = unknown;
|
|
|
20
24
|
* @category Validation Types
|
|
21
25
|
*/
|
|
22
26
|
export type ValidatorFn<T = FormValue> = (value: T) => ValidationError | null;
|
|
27
|
+
/**
|
|
28
|
+
* Опции для асинхронного валидатора
|
|
29
|
+
* @group Types
|
|
30
|
+
* @category Validation Types
|
|
31
|
+
*/
|
|
32
|
+
export interface AsyncValidatorOptions {
|
|
33
|
+
/**
|
|
34
|
+
* AbortSignal для отмены валидации
|
|
35
|
+
* Позволяет отменить асинхронную операцию при новой валидации
|
|
36
|
+
*/
|
|
37
|
+
signal?: AbortSignal;
|
|
38
|
+
}
|
|
23
39
|
/**
|
|
24
40
|
* Асинхронная функция валидации
|
|
41
|
+
*
|
|
42
|
+
* @param value - Значение для валидации
|
|
43
|
+
* @param options - Опции валидации (опционально)
|
|
44
|
+
* @returns Promise с ошибкой валидации или null если значение валидно
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* // Простой валидатор (без поддержки отмены)
|
|
49
|
+
* const emailExists: AsyncValidatorFn<string> = async (value) => {
|
|
50
|
+
* const exists = await checkEmail(value);
|
|
51
|
+
* return exists ? { code: 'exists', message: 'Email already exists' } : null;
|
|
52
|
+
* };
|
|
53
|
+
*
|
|
54
|
+
* // Валидатор с поддержкой отмены
|
|
55
|
+
* const emailExistsAbortable: AsyncValidatorFn<string> = async (value, options) => {
|
|
56
|
+
* const exists = await fetch(`/api/check-email?email=${value}`, {
|
|
57
|
+
* signal: options?.signal // Передаём signal в fetch для отмены запроса
|
|
58
|
+
* });
|
|
59
|
+
* return exists ? { code: 'exists', message: 'Email already exists' } : null;
|
|
60
|
+
* };
|
|
61
|
+
* ```
|
|
62
|
+
*
|
|
25
63
|
* @group Types
|
|
26
64
|
* @category Validation Types
|
|
27
65
|
*/
|
|
28
|
-
export type AsyncValidatorFn<T = FormValue> = (value: T) => Promise<ValidationError | null>;
|
|
66
|
+
export type AsyncValidatorFn<T = FormValue> = (value: T, options?: AsyncValidatorOptions) => Promise<ValidationError | null>;
|
|
29
67
|
/**
|
|
30
68
|
* Ошибка валидации
|
|
31
69
|
* @group Types
|
|
@@ -64,10 +102,7 @@ export type { FieldPath, FieldPathNode } from '../types/field-path';
|
|
|
64
102
|
export type { ContextualValidatorFn, ContextualAsyncValidatorFn, TreeValidatorFn, ConditionFn, ValidateOptions, ValidateAsyncOptions, ValidateTreeOptions, ValidationSchemaFn, ValidatorRegistration, } from './validation-schema';
|
|
65
103
|
export type { FormSchema, ArrayConfig } from './deep-schema';
|
|
66
104
|
export type { FormContext } from './form-context';
|
|
67
|
-
export type {
|
|
68
|
-
import type { BehaviorSchemaFn } from '../behavior/types';
|
|
69
|
-
import type { FormSchema } from './deep-schema';
|
|
70
|
-
import type { ValidationSchemaFn } from './validation-schema';
|
|
105
|
+
export type { FormControlsProxy, FormProxy, FormArrayProxy } from './form-proxy';
|
|
71
106
|
/**
|
|
72
107
|
* Конфигурация GroupNode с поддержкой схем
|
|
73
108
|
* Используется для создания форм с автоматическим применением behavior и validation схем
|
|
@@ -82,6 +117,18 @@ export interface GroupNodeConfig<T> {
|
|
|
82
117
|
behavior?: BehaviorSchemaFn<T>;
|
|
83
118
|
/** Схема валидации (required, email, minLength и т.д.) */
|
|
84
119
|
validation?: ValidationSchemaFn<T>;
|
|
120
|
+
/**
|
|
121
|
+
* Опциональный ValidationRegistry для dependency injection
|
|
122
|
+
* Используется для тестирования с mock-реестрами
|
|
123
|
+
* @internal
|
|
124
|
+
*/
|
|
125
|
+
_validationRegistry?: unknown;
|
|
126
|
+
/**
|
|
127
|
+
* Опциональный BehaviorRegistry для dependency injection
|
|
128
|
+
* Используется для тестирования с mock-реестрами
|
|
129
|
+
* @internal
|
|
130
|
+
*/
|
|
131
|
+
_behaviorRegistry?: unknown;
|
|
85
132
|
}
|
|
86
133
|
/**
|
|
87
134
|
* Тип для Record с unknown значениями
|
|
@@ -112,7 +159,6 @@ export interface ArrayNodeLike {
|
|
|
112
159
|
at(index: number): FormNode<unknown> | undefined;
|
|
113
160
|
length: unknown;
|
|
114
161
|
}
|
|
115
|
-
import type { FormNode } from '../nodes/form-node';
|
|
116
162
|
/**
|
|
117
163
|
* Конфиг с полем schema (для ArrayConfig)
|
|
118
164
|
* @internal
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Основано на Angular Signal Forms подходе:
|
|
5
|
-
* - Валидация определяется отдельно от схемы полей
|
|
6
|
-
* - Поддержка условной валидации (applyWhen)
|
|
7
|
-
* - Cross-field валидация (validateTree)
|
|
8
|
-
* - Асинхронная валидация с контекстом
|
|
9
|
-
*/
|
|
10
|
-
import type { FormFields, ValidationError } from './index';
|
|
11
|
-
import type { FieldPath } from './field-path';
|
|
12
|
-
import type { FormContext } from './form-context';
|
|
1
|
+
import { FormFields, ValidationError } from './index';
|
|
2
|
+
import { FieldPath } from './field-path';
|
|
3
|
+
import { FormContext } from './form-context';
|
|
13
4
|
/**
|
|
14
5
|
* Функция валидации поля с контекстом
|
|
15
6
|
*
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { RegistryStack } from './registry-stack';
|
|
2
|
+
/**
|
|
3
|
+
* Базовый класс для реестров (BehaviorRegistry, ValidationRegistry).
|
|
4
|
+
*
|
|
5
|
+
* Реализует паттерн Template Method для управления регистрацией:
|
|
6
|
+
* `beginRegistration()` → `onBeginRegistration()`, `endRegistration()` → `onEndRegistration()`.
|
|
7
|
+
*
|
|
8
|
+
* @typeParam TRegistration - Тип регистрируемых элементов.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { AbstractRegistry } from '@reformer/core';
|
|
13
|
+
*
|
|
14
|
+
* class MyRegistry extends AbstractRegistry<{ name: string }> {
|
|
15
|
+
* protected onEndRegistration(items: { name: string }[]) {
|
|
16
|
+
* console.log('Registered', items.length);
|
|
17
|
+
* }
|
|
18
|
+
* }
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
export declare abstract class AbstractRegistry<TRegistration> {
|
|
22
|
+
/** Флаг активной регистрации */
|
|
23
|
+
protected isRegistering: boolean;
|
|
24
|
+
/** Массив зарегистрированных элементов */
|
|
25
|
+
protected registrations: TRegistration[];
|
|
26
|
+
/**
|
|
27
|
+
* Получить стек для конкретного класса реестра
|
|
28
|
+
* Создает новый стек если не существует
|
|
29
|
+
*
|
|
30
|
+
* @param ctor - Конструктор класса реестра
|
|
31
|
+
* @returns RegistryStack для данного класса
|
|
32
|
+
*/
|
|
33
|
+
protected static getStack<T extends AbstractRegistry<any>>(ctor: new (...args: any[]) => T): RegistryStack<T>;
|
|
34
|
+
/**
|
|
35
|
+
* Получить текущий активный реестр из стека
|
|
36
|
+
* Должен быть переопределен в наследниках как static метод
|
|
37
|
+
*
|
|
38
|
+
* @param ctor - Конструктор класса реестра
|
|
39
|
+
* @returns Текущий активный реестр или null
|
|
40
|
+
*/
|
|
41
|
+
protected static getCurrentFromStack<T extends AbstractRegistry<any>>(ctor: new (...args: any[]) => T): T | null;
|
|
42
|
+
/**
|
|
43
|
+
* Начать регистрацию
|
|
44
|
+
*
|
|
45
|
+
* Помещает this в global stack для изоляции форм
|
|
46
|
+
* Вызывает hook onBeginRegistration()
|
|
47
|
+
*/
|
|
48
|
+
beginRegistration(): void;
|
|
49
|
+
/**
|
|
50
|
+
* Проверить, активна ли регистрация
|
|
51
|
+
*/
|
|
52
|
+
isActive(): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Получить зарегистрированные элементы
|
|
55
|
+
*/
|
|
56
|
+
getRegistrations(): TRegistration[];
|
|
57
|
+
/**
|
|
58
|
+
* Hook: вызывается в начале регистрации
|
|
59
|
+
* Может быть переопределен в наследниках для инициализации
|
|
60
|
+
*/
|
|
61
|
+
protected onBeginRegistration(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Завершить регистрацию и извлечь из стека
|
|
64
|
+
*
|
|
65
|
+
* @param registryName - Имя реестра для отладки
|
|
66
|
+
*/
|
|
67
|
+
protected completeRegistration(registryName: string): void;
|
|
68
|
+
/**
|
|
69
|
+
* Отменить регистрацию без применения
|
|
70
|
+
*
|
|
71
|
+
* @param registryName - Имя реестра для отладки
|
|
72
|
+
*/
|
|
73
|
+
cancelRegistration(registryName: string): void;
|
|
74
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ReadonlySignal, Signal } from '@preact/signals-core';
|
|
2
|
+
import { FieldStatus, ValidationError } from '../types';
|
|
3
|
+
import { FormNode } from '../nodes/form-node';
|
|
4
|
+
/**
|
|
5
|
+
* Функция получения дочерних узлов
|
|
6
|
+
* Возвращает массив FormNode для агрегации состояния
|
|
7
|
+
*/
|
|
8
|
+
type GetChildrenFn<T> = () => FormNode<T>[];
|
|
9
|
+
/**
|
|
10
|
+
* Результат создания агрегированных сигналов
|
|
11
|
+
*/
|
|
12
|
+
export interface AggregateSignals {
|
|
13
|
+
/** Все дочерние узлы валидны и нет собственных ошибок */
|
|
14
|
+
valid: ReadonlySignal<boolean>;
|
|
15
|
+
/** Есть ошибки (инверсия valid) */
|
|
16
|
+
invalid: ReadonlySignal<boolean>;
|
|
17
|
+
/** Хотя бы один дочерний узел в состоянии pending */
|
|
18
|
+
pending: ReadonlySignal<boolean>;
|
|
19
|
+
/** Хотя бы один дочерний узел touched */
|
|
20
|
+
touched: ReadonlySignal<boolean>;
|
|
21
|
+
/** Хотя бы один дочерний узел dirty */
|
|
22
|
+
dirty: ReadonlySignal<boolean>;
|
|
23
|
+
/** Собственные ошибки + ошибки всех дочерних узлов */
|
|
24
|
+
errors: ReadonlySignal<ValidationError[]>;
|
|
25
|
+
/** Статус: disabled > pending > invalid > valid */
|
|
26
|
+
status: ReadonlySignal<FieldStatus>;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Опции для создания агрегированных сигналов
|
|
30
|
+
*/
|
|
31
|
+
export interface AggregateSignalsOptions<T> {
|
|
32
|
+
/** Функция получения дочерних узлов */
|
|
33
|
+
getChildren: GetChildrenFn<T>;
|
|
34
|
+
/** Signal с собственными ошибками контейнера (form-level или array-level) */
|
|
35
|
+
ownErrors: Signal<ValidationError[]>;
|
|
36
|
+
/** Опциональный signal disabled состояния (для GroupNode) */
|
|
37
|
+
disabled?: Signal<boolean>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Создать агрегированные computed signals для контейнерного узла
|
|
41
|
+
*
|
|
42
|
+
* Используется в GroupNode и ArrayNode для унификации логики
|
|
43
|
+
* вычисления состояния на основе дочерних узлов.
|
|
44
|
+
*
|
|
45
|
+
* @param options - Опции конфигурации
|
|
46
|
+
* @returns Объект с computed signals
|
|
47
|
+
*
|
|
48
|
+
* @example GroupNode
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const signals = createAggregateSignals({
|
|
51
|
+
* getChildren: () => Array.from(this._fields.values()),
|
|
52
|
+
* ownErrors: this._formErrors,
|
|
53
|
+
* disabled: this._disabled,
|
|
54
|
+
* });
|
|
55
|
+
* this.valid = signals.valid;
|
|
56
|
+
* this.invalid = signals.invalid;
|
|
57
|
+
* // ...
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example ArrayNode
|
|
61
|
+
* ```typescript
|
|
62
|
+
* const signals = createAggregateSignals({
|
|
63
|
+
* getChildren: () => this.items.value,
|
|
64
|
+
* ownErrors: this._arrayErrors,
|
|
65
|
+
* });
|
|
66
|
+
* this.valid = signals.valid;
|
|
67
|
+
* // ...
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export declare function createAggregateSignals<T>(options: AggregateSignalsOptions<T>): AggregateSignals;
|
|
71
|
+
export {};
|
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Фабричная функция для создания формы с правильной типизацией
|
|
3
|
-
*
|
|
4
|
-
* Решает проблему с типизацией конструктора GroupNode, который возвращает
|
|
5
|
-
* Proxy (GroupNodeWithControls), но TypeScript не может это вывести автоматически.
|
|
6
|
-
*
|
|
7
|
-
* @group Utilities
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* // Вместо:
|
|
12
|
-
* const form: GroupNodeWithControls<MyForm> = new GroupNode<MyForm>(config);
|
|
13
|
-
*
|
|
14
|
-
* // Используйте:
|
|
15
|
-
* const form = createForm<MyForm>(config);
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
import type { GroupNodeWithControls, GroupNodeConfig, FormSchema } from '../types';
|
|
1
|
+
import { FormProxy, GroupNodeConfig, FormSchema } from '../types';
|
|
19
2
|
/**
|
|
20
3
|
* Создать форму с полной конфигурацией (form, behavior, validation)
|
|
21
4
|
*
|
|
@@ -43,7 +26,7 @@ import type { GroupNodeWithControls, GroupNodeConfig, FormSchema } from '../type
|
|
|
43
26
|
* form.email.setValue('test@mail.com');
|
|
44
27
|
* ```
|
|
45
28
|
*/
|
|
46
|
-
export declare function createForm<T>(config: GroupNodeConfig<T>):
|
|
29
|
+
export declare function createForm<T>(config: GroupNodeConfig<T>): FormProxy<T>;
|
|
47
30
|
/**
|
|
48
31
|
* Создать форму только со схемой полей (обратная совместимость)
|
|
49
32
|
*
|
|
@@ -58,4 +41,4 @@ export declare function createForm<T>(config: GroupNodeConfig<T>): GroupNodeWith
|
|
|
58
41
|
* });
|
|
59
42
|
* ```
|
|
60
43
|
*/
|
|
61
|
-
export declare function createForm<T>(schema: FormSchema<T>):
|
|
44
|
+
export declare function createForm<T>(schema: FormSchema<T>): FormProxy<T>;
|
|
@@ -1,21 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* FormErrorHandler - централизованная обработка ошибок в формах
|
|
3
|
-
*
|
|
4
|
-
* Устраняет несогласованность обработки ошибок между:
|
|
5
|
-
* - field-node.ts (логирует и конвертирует в ValidationError)
|
|
6
|
-
* - behavior-applicator.ts (логирует и пробрасывает)
|
|
7
|
-
* - validation-applicator.ts (логирует и проглатывает)
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* try {
|
|
12
|
-
* await validator(value);
|
|
13
|
-
* } catch (error) {
|
|
14
|
-
* return FormErrorHandler.handle(error, 'AsyncValidator', ErrorStrategy.CONVERT);
|
|
15
|
-
* }
|
|
16
|
-
* ```
|
|
17
|
-
*/
|
|
18
|
-
import type { ValidationError } from '../types';
|
|
1
|
+
import { ValidationError } from '../types';
|
|
19
2
|
/**
|
|
20
3
|
* Стратегия обработки ошибок
|
|
21
4
|
*
|
|
@@ -1,12 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* FieldPath proxy - типобезопасный доступ к путям полей формы
|
|
3
|
-
*
|
|
4
|
-
* Единый модуль для validation и behavior схем.
|
|
5
|
-
* Предоставляет типизированную навигацию по структуре формы.
|
|
6
|
-
*
|
|
7
|
-
* @module utils/field-path
|
|
8
|
-
*/
|
|
9
|
-
import type { FieldPath, FieldPathNode } from '../types';
|
|
1
|
+
import { FieldPath, FieldPathNode } from '../types';
|
|
10
2
|
/**
|
|
11
3
|
* Создать FieldPath proxy для формы
|
|
12
4
|
*
|
|
@@ -19,7 +11,17 @@ import type { FieldPath, FieldPathNode } from '../types';
|
|
|
19
11
|
*/
|
|
20
12
|
export declare function createFieldPath<T>(): FieldPath<T>;
|
|
21
13
|
/**
|
|
22
|
-
* Извлечь путь из FieldPathNode
|
|
14
|
+
* Извлечь строковый путь из {@link FieldPathNode}.
|
|
15
|
+
*
|
|
16
|
+
* @param node - Узел `FieldPathNode` либо строка-путь.
|
|
17
|
+
* @returns Путь вида `"a.b.c"`.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* import { extractPath } from '@reformer/core';
|
|
22
|
+
*
|
|
23
|
+
* const path = (renderPath) => extractPath(renderPath.user.email); // → 'user.email'
|
|
24
|
+
* ```
|
|
23
25
|
*/
|
|
24
26
|
export declare function extractPath(node: FieldPathNode<unknown, unknown> | unknown): string;
|
|
25
27
|
/**
|
|
@@ -43,6 +45,16 @@ export declare function extractPath(node: FieldPathNode<unknown, unknown> | unkn
|
|
|
43
45
|
*/
|
|
44
46
|
export declare function toFieldPath<T>(node: FieldPathNode<unknown, T, never> | FieldPathNode<any, T, any>): FieldPath<T>;
|
|
45
47
|
/**
|
|
46
|
-
* Извлечь
|
|
48
|
+
* Извлечь имя последнего сегмента ({@link FieldPathSegment}) пути.
|
|
49
|
+
*
|
|
50
|
+
* @param node - Узел `FieldPathNode` либо строка-путь.
|
|
51
|
+
* @returns Имя поля без префикса родителя (последний сегмент).
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* import { extractKey } from '@reformer/core';
|
|
56
|
+
*
|
|
57
|
+
* extractKey(path.user.email); // → 'email'
|
|
58
|
+
* ```
|
|
47
59
|
*/
|
|
48
60
|
export declare function extractKey(node: FieldPathNode<unknown, unknown> | unknown): string;
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { FormFields, FormValue, FieldStatus, ValidationError } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Тип события изменения в форме
|
|
4
|
+
*/
|
|
5
|
+
export type FormChangeType = 'value' | 'status' | 'errors' | 'touched' | 'dirty';
|
|
6
|
+
/**
|
|
7
|
+
* Событие изменения в форме
|
|
8
|
+
*/
|
|
9
|
+
export interface FormChangeEvent {
|
|
10
|
+
/** Тип изменения */
|
|
11
|
+
type: FormChangeType;
|
|
12
|
+
/** Путь к полю */
|
|
13
|
+
path: string;
|
|
14
|
+
/** Timestamp события */
|
|
15
|
+
timestamp: number;
|
|
16
|
+
/** Старое значение */
|
|
17
|
+
oldValue?: unknown;
|
|
18
|
+
/** Новое значение */
|
|
19
|
+
newValue: unknown;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Интерфейс узла формы для observer
|
|
23
|
+
*/
|
|
24
|
+
export interface ObservableFormNode {
|
|
25
|
+
value: {
|
|
26
|
+
value: FormValue;
|
|
27
|
+
};
|
|
28
|
+
status: {
|
|
29
|
+
value: FieldStatus;
|
|
30
|
+
};
|
|
31
|
+
errors: {
|
|
32
|
+
value: ValidationError[];
|
|
33
|
+
};
|
|
34
|
+
touched: {
|
|
35
|
+
value: boolean;
|
|
36
|
+
};
|
|
37
|
+
dirty: {
|
|
38
|
+
value: boolean;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Интерфейс формы для observer
|
|
43
|
+
*/
|
|
44
|
+
export interface ObservableForm<T extends FormFields> {
|
|
45
|
+
value: {
|
|
46
|
+
value: T;
|
|
47
|
+
};
|
|
48
|
+
status: {
|
|
49
|
+
value: FieldStatus;
|
|
50
|
+
};
|
|
51
|
+
errors: {
|
|
52
|
+
value: ValidationError[];
|
|
53
|
+
};
|
|
54
|
+
touched: {
|
|
55
|
+
value: boolean;
|
|
56
|
+
};
|
|
57
|
+
dirty: {
|
|
58
|
+
value: boolean;
|
|
59
|
+
};
|
|
60
|
+
getFieldByPath(path: string): ObservableFormNode | undefined;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Callback для обработки событий изменения
|
|
64
|
+
*/
|
|
65
|
+
export type FormChangeCallback = (event: FormChangeEvent) => void;
|
|
66
|
+
/**
|
|
67
|
+
* Опции для FormObserver
|
|
68
|
+
*/
|
|
69
|
+
export interface FormObserverOptions {
|
|
70
|
+
/** Включить логирование в консоль (по умолчанию true в DEV) */
|
|
71
|
+
enableLogging?: boolean;
|
|
72
|
+
/** Фильтр типов событий */
|
|
73
|
+
eventTypes?: FormChangeType[];
|
|
74
|
+
/** Фильтр путей полей (regex или массив) */
|
|
75
|
+
pathFilter?: RegExp | string[];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* FormObserver - мониторинг изменений в форме
|
|
79
|
+
*
|
|
80
|
+
* Полезен для:
|
|
81
|
+
* - Отладки сложных форм
|
|
82
|
+
* - Логирования изменений для аудита
|
|
83
|
+
* - Синхронизации с внешними системами
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* const observer = new FormObserver(form, {
|
|
88
|
+
* enableLogging: true,
|
|
89
|
+
* eventTypes: ['value', 'errors']
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* // Подписка на события
|
|
93
|
+
* const unsubscribe = observer.subscribe((event) => {
|
|
94
|
+
* console.log(`${event.type} at ${event.path}:`, event.newValue);
|
|
95
|
+
* });
|
|
96
|
+
*
|
|
97
|
+
* // Включить трассировку
|
|
98
|
+
* const disposeTracing = observer.enableTracing();
|
|
99
|
+
*
|
|
100
|
+
* // Cleanup
|
|
101
|
+
* unsubscribe();
|
|
102
|
+
* disposeTracing();
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
export declare class FormObserver<T extends FormFields> {
|
|
106
|
+
private readonly form;
|
|
107
|
+
private listeners;
|
|
108
|
+
private disposers;
|
|
109
|
+
private options;
|
|
110
|
+
/**
|
|
111
|
+
* @param form - Форма для наблюдения
|
|
112
|
+
* @param options - Опции observer
|
|
113
|
+
*/
|
|
114
|
+
constructor(form: ObservableForm<T>, options?: FormObserverOptions);
|
|
115
|
+
/**
|
|
116
|
+
* Подписаться на события изменения
|
|
117
|
+
*
|
|
118
|
+
* @param callback - Функция обработки события
|
|
119
|
+
* @returns Функция отписки
|
|
120
|
+
*
|
|
121
|
+
* @example
|
|
122
|
+
* ```typescript
|
|
123
|
+
* const unsubscribe = observer.subscribe((event) => {
|
|
124
|
+
* // Отправить событие в analytics
|
|
125
|
+
* analytics.track('form_change', event);
|
|
126
|
+
* });
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
subscribe(callback: FormChangeCallback): () => void;
|
|
130
|
+
/**
|
|
131
|
+
* Включить трассировку формы
|
|
132
|
+
*
|
|
133
|
+
* Подписывается на изменения основных сигналов формы
|
|
134
|
+
* и вызывает listeners при каждом изменении
|
|
135
|
+
*
|
|
136
|
+
* @returns Функция для отключения трассировки
|
|
137
|
+
*
|
|
138
|
+
* @example
|
|
139
|
+
* ```typescript
|
|
140
|
+
* const dispose = observer.enableTracing();
|
|
141
|
+
*
|
|
142
|
+
* // Позже, для отключения
|
|
143
|
+
* dispose();
|
|
144
|
+
* ```
|
|
145
|
+
*/
|
|
146
|
+
enableTracing(): () => void;
|
|
147
|
+
/**
|
|
148
|
+
* Наблюдать за конкретным полем
|
|
149
|
+
*
|
|
150
|
+
* @param path - Путь к полю
|
|
151
|
+
* @returns Функция для отключения наблюдения
|
|
152
|
+
*
|
|
153
|
+
* @example
|
|
154
|
+
* ```typescript
|
|
155
|
+
* // Наблюдать за полем email
|
|
156
|
+
* const dispose = observer.watchField('email');
|
|
157
|
+
* ```
|
|
158
|
+
*/
|
|
159
|
+
watchField(path: string): () => void;
|
|
160
|
+
/**
|
|
161
|
+
* Отправить событие всем listeners
|
|
162
|
+
*/
|
|
163
|
+
private emit;
|
|
164
|
+
/**
|
|
165
|
+
* Проверить, нужно ли отслеживать тип события
|
|
166
|
+
*/
|
|
167
|
+
private shouldTrack;
|
|
168
|
+
/**
|
|
169
|
+
* Проверить, соответствует ли путь фильтру
|
|
170
|
+
*/
|
|
171
|
+
private matchesPathFilter;
|
|
172
|
+
/**
|
|
173
|
+
* Очистить все подписки и disposers
|
|
174
|
+
*/
|
|
175
|
+
dispose(): void;
|
|
176
|
+
}
|