@reformer/core 1.0.0-beta.3
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/LICENSE +21 -0
- package/README.md +53 -0
- package/dist/behaviors.d.ts +2 -0
- package/dist/behaviors.js +230 -0
- package/dist/core/behavior/behavior-applicator.d.ts +71 -0
- package/dist/core/behavior/behavior-applicator.js +92 -0
- package/dist/core/behavior/behavior-context.d.ts +29 -0
- package/dist/core/behavior/behavior-context.js +38 -0
- package/dist/core/behavior/behavior-registry.d.ts +97 -0
- package/dist/core/behavior/behavior-registry.js +198 -0
- package/dist/core/behavior/behaviors/compute-from.d.ts +41 -0
- package/dist/core/behavior/behaviors/compute-from.js +84 -0
- package/dist/core/behavior/behaviors/copy-from.d.ts +31 -0
- package/dist/core/behavior/behaviors/copy-from.js +64 -0
- package/dist/core/behavior/behaviors/enable-when.d.ts +49 -0
- package/dist/core/behavior/behaviors/enable-when.js +81 -0
- package/dist/core/behavior/behaviors/index.d.ts +11 -0
- package/dist/core/behavior/behaviors/index.js +11 -0
- package/dist/core/behavior/behaviors/reset-when.d.ts +51 -0
- package/dist/core/behavior/behaviors/reset-when.js +63 -0
- package/dist/core/behavior/behaviors/revalidate-when.d.ts +30 -0
- package/dist/core/behavior/behaviors/revalidate-when.js +51 -0
- package/dist/core/behavior/behaviors/sync-fields.d.ts +28 -0
- package/dist/core/behavior/behaviors/sync-fields.js +66 -0
- package/dist/core/behavior/behaviors/transform-value.d.ts +120 -0
- package/dist/core/behavior/behaviors/transform-value.js +110 -0
- package/dist/core/behavior/behaviors/watch-field.d.ts +35 -0
- package/dist/core/behavior/behaviors/watch-field.js +56 -0
- package/dist/core/behavior/compose-behavior.d.ts +106 -0
- package/dist/core/behavior/compose-behavior.js +166 -0
- package/dist/core/behavior/create-field-path.d.ts +20 -0
- package/dist/core/behavior/create-field-path.js +69 -0
- package/dist/core/behavior/index.d.ts +12 -0
- package/dist/core/behavior/index.js +17 -0
- package/dist/core/behavior/types.d.ts +152 -0
- package/dist/core/behavior/types.js +7 -0
- package/dist/core/context/form-context-impl.d.ts +29 -0
- package/dist/core/context/form-context-impl.js +37 -0
- package/dist/core/factories/index.d.ts +6 -0
- package/dist/core/factories/index.js +6 -0
- package/dist/core/factories/node-factory.d.ts +209 -0
- package/dist/core/factories/node-factory.js +281 -0
- package/dist/core/nodes/array-node.d.ts +308 -0
- package/dist/core/nodes/array-node.js +534 -0
- package/dist/core/nodes/field-node.d.ts +269 -0
- package/dist/core/nodes/field-node.js +510 -0
- package/dist/core/nodes/form-node.d.ts +342 -0
- package/dist/core/nodes/form-node.js +343 -0
- package/dist/core/nodes/group-node/field-registry.d.ts +191 -0
- package/dist/core/nodes/group-node/field-registry.js +215 -0
- package/dist/core/nodes/group-node/index.d.ts +11 -0
- package/dist/core/nodes/group-node/index.js +11 -0
- package/dist/core/nodes/group-node/proxy-builder.d.ts +71 -0
- package/dist/core/nodes/group-node/proxy-builder.js +161 -0
- package/dist/core/nodes/group-node/state-manager.d.ts +184 -0
- package/dist/core/nodes/group-node/state-manager.js +265 -0
- package/dist/core/nodes/group-node.d.ts +494 -0
- package/dist/core/nodes/group-node.js +770 -0
- package/dist/core/types/deep-schema.d.ts +78 -0
- package/dist/core/types/deep-schema.js +11 -0
- package/dist/core/types/field-path.d.ts +42 -0
- package/dist/core/types/field-path.js +4 -0
- package/dist/core/types/form-context.d.ts +83 -0
- package/dist/core/types/form-context.js +25 -0
- package/dist/core/types/group-node-proxy.d.ts +135 -0
- package/dist/core/types/group-node-proxy.js +31 -0
- package/dist/core/types/index.d.ts +163 -0
- package/dist/core/types/index.js +4 -0
- package/dist/core/types/validation-schema.d.ts +104 -0
- package/dist/core/types/validation-schema.js +10 -0
- package/dist/core/utils/create-form.d.ts +61 -0
- package/dist/core/utils/create-form.js +24 -0
- package/dist/core/utils/debounce.d.ts +160 -0
- package/dist/core/utils/debounce.js +197 -0
- package/dist/core/utils/error-handler.d.ts +180 -0
- package/dist/core/utils/error-handler.js +226 -0
- package/dist/core/utils/field-path-navigator.d.ts +240 -0
- package/dist/core/utils/field-path-navigator.js +374 -0
- package/dist/core/utils/index.d.ts +14 -0
- package/dist/core/utils/index.js +14 -0
- package/dist/core/utils/registry-helpers.d.ts +50 -0
- package/dist/core/utils/registry-helpers.js +79 -0
- package/dist/core/utils/registry-stack.d.ts +69 -0
- package/dist/core/utils/registry-stack.js +86 -0
- package/dist/core/utils/resources.d.ts +41 -0
- package/dist/core/utils/resources.js +69 -0
- package/dist/core/utils/subscription-manager.d.ts +180 -0
- package/dist/core/utils/subscription-manager.js +214 -0
- package/dist/core/utils/type-guards.d.ts +116 -0
- package/dist/core/utils/type-guards.js +169 -0
- package/dist/core/validation/core/apply-when.d.ts +28 -0
- package/dist/core/validation/core/apply-when.js +41 -0
- package/dist/core/validation/core/apply.d.ts +63 -0
- package/dist/core/validation/core/apply.js +38 -0
- package/dist/core/validation/core/index.d.ts +8 -0
- package/dist/core/validation/core/index.js +8 -0
- package/dist/core/validation/core/validate-async.d.ts +42 -0
- package/dist/core/validation/core/validate-async.js +45 -0
- package/dist/core/validation/core/validate-tree.d.ts +35 -0
- package/dist/core/validation/core/validate-tree.js +37 -0
- package/dist/core/validation/core/validate.d.ts +32 -0
- package/dist/core/validation/core/validate.js +38 -0
- package/dist/core/validation/field-path.d.ts +43 -0
- package/dist/core/validation/field-path.js +147 -0
- package/dist/core/validation/index.d.ts +21 -0
- package/dist/core/validation/index.js +33 -0
- package/dist/core/validation/validate-form.d.ts +85 -0
- package/dist/core/validation/validate-form.js +152 -0
- package/dist/core/validation/validation-applicator.d.ts +89 -0
- package/dist/core/validation/validation-applicator.js +217 -0
- package/dist/core/validation/validation-context.d.ts +47 -0
- package/dist/core/validation/validation-context.js +75 -0
- package/dist/core/validation/validation-registry.d.ts +156 -0
- package/dist/core/validation/validation-registry.js +298 -0
- package/dist/core/validation/validators/array-validators.d.ts +63 -0
- package/dist/core/validation/validators/array-validators.js +86 -0
- package/dist/core/validation/validators/date.d.ts +38 -0
- package/dist/core/validation/validators/date.js +117 -0
- package/dist/core/validation/validators/email.d.ts +44 -0
- package/dist/core/validation/validators/email.js +60 -0
- package/dist/core/validation/validators/index.d.ts +14 -0
- package/dist/core/validation/validators/index.js +14 -0
- package/dist/core/validation/validators/max-length.d.ts +45 -0
- package/dist/core/validation/validators/max-length.js +60 -0
- package/dist/core/validation/validators/max.d.ts +45 -0
- package/dist/core/validation/validators/max.js +60 -0
- package/dist/core/validation/validators/min-length.d.ts +45 -0
- package/dist/core/validation/validators/min-length.js +60 -0
- package/dist/core/validation/validators/min.d.ts +45 -0
- package/dist/core/validation/validators/min.js +60 -0
- package/dist/core/validation/validators/number.d.ts +38 -0
- package/dist/core/validation/validators/number.js +90 -0
- package/dist/core/validation/validators/pattern.d.ts +47 -0
- package/dist/core/validation/validators/pattern.js +62 -0
- package/dist/core/validation/validators/phone.d.ts +34 -0
- package/dist/core/validation/validators/phone.js +58 -0
- package/dist/core/validation/validators/required.d.ts +48 -0
- package/dist/core/validation/validators/required.js +69 -0
- package/dist/core/validation/validators/url.d.ts +29 -0
- package/dist/core/validation/validators/url.js +55 -0
- package/dist/create-field-path-CdPF3lIK.js +704 -0
- package/dist/hooks/useFormControl.d.ts +48 -0
- package/dist/hooks/useFormControl.js +298 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +8 -0
- package/dist/node-factory-D7DOnSSN.js +3200 -0
- package/dist/validators.d.ts +2 -0
- package/dist/validators.js +298 -0
- package/llms.txt +847 -0
- package/package.json +86 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GroupNode - узел группы полей формы
|
|
3
|
+
*
|
|
4
|
+
* Представляет группу полей (объект), где каждое поле может быть:
|
|
5
|
+
* - FieldNode (простое поле)
|
|
6
|
+
* - GroupNode (вложенная группа)
|
|
7
|
+
* - ArrayNode (массив форм)
|
|
8
|
+
*
|
|
9
|
+
* Наследует от FormNode и реализует все его абстрактные методы
|
|
10
|
+
*
|
|
11
|
+
* @group Nodes
|
|
12
|
+
*/
|
|
13
|
+
import type { ReadonlySignal } from '@preact/signals-core';
|
|
14
|
+
import { FormNode, type SetValueOptions } from './form-node';
|
|
15
|
+
import type { ValidationError, FieldStatus, ValidationSchemaFn, ValidatorRegistration, FormSchema, GroupNodeConfig, FormValue } from '../types';
|
|
16
|
+
import type { GroupNodeWithControls } from '../types/group-node-proxy';
|
|
17
|
+
import type { BehaviorSchemaFn } from '../behavior/types';
|
|
18
|
+
import { FieldRegistry } from './group-node/field-registry';
|
|
19
|
+
/**
|
|
20
|
+
* GroupNode - узел для группы полей
|
|
21
|
+
*
|
|
22
|
+
* Поддерживает два API:
|
|
23
|
+
* 1. Старый API (только schema) - обратная совместимость
|
|
24
|
+
* 2. Новый API (config с form, behavior, validation) - автоматическое применение схем
|
|
25
|
+
*
|
|
26
|
+
* @group Nodes
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* // 1. Старый способ (обратная совместимость)
|
|
31
|
+
* const simpleForm = new GroupNode({
|
|
32
|
+
* email: { value: '', component: Input },
|
|
33
|
+
* password: { value: '', component: Input },
|
|
34
|
+
* });
|
|
35
|
+
*
|
|
36
|
+
* // 2. Новый способ (с behavior и validation схемами)
|
|
37
|
+
* const fullForm = new GroupNode({
|
|
38
|
+
* form: {
|
|
39
|
+
* email: { value: '', component: Input },
|
|
40
|
+
* password: { value: '', component: Input },
|
|
41
|
+
* },
|
|
42
|
+
* behavior: (path) => {
|
|
43
|
+
* computeFrom(path.email, [path.email], (values) => values[0]?.trim());
|
|
44
|
+
* },
|
|
45
|
+
* validation: (path) => {
|
|
46
|
+
* required(path.email, { message: 'Email обязателен' });
|
|
47
|
+
* email(path.email);
|
|
48
|
+
* required(path.password);
|
|
49
|
+
* minLength(path.password, 8);
|
|
50
|
+
* },
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* // Прямой доступ к полям через Proxy
|
|
54
|
+
* fullForm.email.setValue('test@mail.com');
|
|
55
|
+
* await fullForm.validate();
|
|
56
|
+
* console.log(fullForm.valid.value); // true
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare class GroupNode<T> extends FormNode<T> {
|
|
60
|
+
id: string;
|
|
61
|
+
/**
|
|
62
|
+
* Реестр полей формы
|
|
63
|
+
* Использует FieldRegistry для инкапсуляции логики управления коллекцией полей
|
|
64
|
+
*/
|
|
65
|
+
private fieldRegistry;
|
|
66
|
+
/**
|
|
67
|
+
* Строитель Proxy для типобезопасного доступа к полям
|
|
68
|
+
* Использует ProxyBuilder для создания Proxy с расширенной функциональностью
|
|
69
|
+
*/
|
|
70
|
+
private proxyBuilder;
|
|
71
|
+
/**
|
|
72
|
+
* Менеджер состояния формы
|
|
73
|
+
* Инкапсулирует всю логику создания и управления сигналами состояния
|
|
74
|
+
* Извлечен из GroupNode для соблюдения SRP
|
|
75
|
+
*/
|
|
76
|
+
private stateManager;
|
|
77
|
+
/**
|
|
78
|
+
* Менеджер подписок для централизованного cleanup
|
|
79
|
+
* Использует SubscriptionManager вместо массива для управления подписками
|
|
80
|
+
*/
|
|
81
|
+
private disposers;
|
|
82
|
+
/**
|
|
83
|
+
* Ссылка на Proxy-инстанс для использования в BehaviorContext
|
|
84
|
+
* Устанавливается в конструкторе до применения behavior schema
|
|
85
|
+
*/
|
|
86
|
+
private _proxyInstance?;
|
|
87
|
+
/**
|
|
88
|
+
* Навигатор для работы с путями к полям
|
|
89
|
+
* Использует композицию вместо дублирования логики парсинга путей
|
|
90
|
+
*/
|
|
91
|
+
private readonly pathNavigator;
|
|
92
|
+
/**
|
|
93
|
+
* Фабрика для создания узлов формы
|
|
94
|
+
* Использует композицию для централизованного создания FieldNode/GroupNode/ArrayNode
|
|
95
|
+
*/
|
|
96
|
+
private readonly nodeFactory;
|
|
97
|
+
/**
|
|
98
|
+
* Реестр валидаторов для этой формы
|
|
99
|
+
* Использует композицию вместо глобального Singleton
|
|
100
|
+
* Обеспечивает полную изоляцию форм друг от друга
|
|
101
|
+
*/
|
|
102
|
+
private readonly validationRegistry;
|
|
103
|
+
/**
|
|
104
|
+
* Реестр behaviors для этой формы
|
|
105
|
+
* Использует композицию вместо глобального Singleton
|
|
106
|
+
* Обеспечивает полную изоляцию форм друг от друга
|
|
107
|
+
*/
|
|
108
|
+
private readonly behaviorRegistry;
|
|
109
|
+
/**
|
|
110
|
+
* Аппликатор для применения валидаторов к форме
|
|
111
|
+
* Извлечен из GroupNode для соблюдения SRP
|
|
112
|
+
* Использует композицию для управления процессом валидации
|
|
113
|
+
*/
|
|
114
|
+
private readonly validationApplicator;
|
|
115
|
+
/**
|
|
116
|
+
* Аппликатор для применения behavior схемы к форме
|
|
117
|
+
* Извлечен из GroupNode для соблюдения SRP
|
|
118
|
+
* Использует композицию для управления процессом применения behaviors
|
|
119
|
+
*/
|
|
120
|
+
private readonly behaviorApplicator;
|
|
121
|
+
readonly value: ReadonlySignal<T>;
|
|
122
|
+
readonly valid: ReadonlySignal<boolean>;
|
|
123
|
+
readonly invalid: ReadonlySignal<boolean>;
|
|
124
|
+
readonly touched: ReadonlySignal<boolean>;
|
|
125
|
+
readonly dirty: ReadonlySignal<boolean>;
|
|
126
|
+
readonly pending: ReadonlySignal<boolean>;
|
|
127
|
+
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
128
|
+
readonly status: ReadonlySignal<FieldStatus>;
|
|
129
|
+
readonly submitting: ReadonlySignal<boolean>;
|
|
130
|
+
/**
|
|
131
|
+
* Создать GroupNode только со схемой формы (обратная совместимость)
|
|
132
|
+
*/
|
|
133
|
+
constructor(schema: FormSchema<T>);
|
|
134
|
+
/**
|
|
135
|
+
* Создать GroupNode с полной конфигурацией (form, behavior, validation)
|
|
136
|
+
*/
|
|
137
|
+
constructor(config: GroupNodeConfig<T>);
|
|
138
|
+
getValue(): T;
|
|
139
|
+
setValue(value: T, options?: SetValueOptions): void;
|
|
140
|
+
patchValue(value: Partial<T>): void;
|
|
141
|
+
/**
|
|
142
|
+
* Сбросить форму к указанным значениям (или к initialValues)
|
|
143
|
+
*
|
|
144
|
+
* @param value - опциональный объект со значениями для сброса
|
|
145
|
+
*
|
|
146
|
+
* @remarks
|
|
147
|
+
* Рекурсивно вызывает reset() для всех полей формы
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* // Сброс к initialValues
|
|
152
|
+
* form.reset();
|
|
153
|
+
*
|
|
154
|
+
* // Сброс к новым значениям
|
|
155
|
+
* form.reset({ email: 'new@mail.com', password: '' });
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
reset(value?: T): void;
|
|
159
|
+
/**
|
|
160
|
+
* Сбросить форму к исходным значениям (initialValues)
|
|
161
|
+
*
|
|
162
|
+
* @remarks
|
|
163
|
+
* Рекурсивно вызывает resetToInitial() для всех полей формы.
|
|
164
|
+
* Более явный способ сброса к начальным значениям по сравнению с reset()
|
|
165
|
+
*
|
|
166
|
+
* Полезно когда:
|
|
167
|
+
* - Пользователь нажал "Cancel" - полная отмена изменений
|
|
168
|
+
* - Форма была изменена через reset(newValues), но нужно вернуться к самому началу
|
|
169
|
+
* - Явное намерение показать "отмена всех изменений"
|
|
170
|
+
*
|
|
171
|
+
* @example
|
|
172
|
+
* ```typescript
|
|
173
|
+
* const form = new GroupNode({
|
|
174
|
+
* email: { value: 'initial@mail.com', component: Input },
|
|
175
|
+
* name: { value: 'John', component: Input }
|
|
176
|
+
* });
|
|
177
|
+
*
|
|
178
|
+
* form.email.setValue('changed@mail.com');
|
|
179
|
+
* form.reset({ email: 'temp@mail.com', name: 'Jane' });
|
|
180
|
+
* console.log(form.getValue()); // { email: 'temp@mail.com', name: 'Jane' }
|
|
181
|
+
*
|
|
182
|
+
* form.resetToInitial();
|
|
183
|
+
* console.log(form.getValue()); // { email: 'initial@mail.com', name: 'John' }
|
|
184
|
+
* ```
|
|
185
|
+
*/
|
|
186
|
+
resetToInitial(): void;
|
|
187
|
+
validate(): Promise<boolean>;
|
|
188
|
+
/**
|
|
189
|
+
* Установить form-level validation errors
|
|
190
|
+
* Используется для server-side validation или кросс-полевых ошибок
|
|
191
|
+
*
|
|
192
|
+
* @param errors - массив ошибок уровня формы
|
|
193
|
+
*
|
|
194
|
+
* @example
|
|
195
|
+
* ```typescript
|
|
196
|
+
* // Server-side validation после submit
|
|
197
|
+
* try {
|
|
198
|
+
* await api.createUser(form.getValue());
|
|
199
|
+
* } catch (error) {
|
|
200
|
+
* form.setErrors([
|
|
201
|
+
* { code: 'duplicate_email', message: 'Email уже используется' }
|
|
202
|
+
* ]);
|
|
203
|
+
* }
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
setErrors(errors: ValidationError[]): void;
|
|
207
|
+
/**
|
|
208
|
+
* Очистить все errors (form-level + field-level)
|
|
209
|
+
*/
|
|
210
|
+
clearErrors(): void;
|
|
211
|
+
/**
|
|
212
|
+
* Получить поле по ключу
|
|
213
|
+
*
|
|
214
|
+
* Публичный метод для доступа к полю из fieldRegistry
|
|
215
|
+
*
|
|
216
|
+
* @param key - Ключ поля
|
|
217
|
+
* @returns FormNode или undefined, если поле не найдено
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```typescript
|
|
221
|
+
* const emailField = form.getField('email');
|
|
222
|
+
* if (emailField) {
|
|
223
|
+
* console.log(emailField.value.value);
|
|
224
|
+
* }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
227
|
+
getField<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
|
|
228
|
+
/**
|
|
229
|
+
* Получить Map всех полей формы
|
|
230
|
+
*
|
|
231
|
+
* Используется в FieldPathNavigator для навигации по полям
|
|
232
|
+
*
|
|
233
|
+
* @returns Map полей формы
|
|
234
|
+
*/
|
|
235
|
+
get fields(): FieldRegistry<T>;
|
|
236
|
+
/**
|
|
237
|
+
* Получить Proxy-инстанс для прямого доступа к полям
|
|
238
|
+
*
|
|
239
|
+
* Proxy позволяет обращаться к полям формы напрямую через точечную нотацию:
|
|
240
|
+
* - form.email вместо form.fields.get('email')
|
|
241
|
+
* - form.address.city вместо form.fields.get('address').fields.get('city')
|
|
242
|
+
*
|
|
243
|
+
* Используется в:
|
|
244
|
+
* - BehaviorApplicator для доступа к полям в behavior functions
|
|
245
|
+
* - ValidationApplicator для доступа к форме в tree validators
|
|
246
|
+
*
|
|
247
|
+
* @returns Proxy-инстанс с типобезопасным доступом к полям или сама форма, если proxy не доступен
|
|
248
|
+
*
|
|
249
|
+
* @example
|
|
250
|
+
* ```typescript
|
|
251
|
+
* const form = new GroupNode({
|
|
252
|
+
* controls: {
|
|
253
|
+
* email: new FieldNode({ value: '' }),
|
|
254
|
+
* name: new FieldNode({ value: '' })
|
|
255
|
+
* }
|
|
256
|
+
* });
|
|
257
|
+
*
|
|
258
|
+
* const proxy = form.getProxy();
|
|
259
|
+
* console.log(proxy.email.value); // Прямой доступ к полю
|
|
260
|
+
* ```
|
|
261
|
+
*/
|
|
262
|
+
getProxy(): GroupNodeWithControls<T>;
|
|
263
|
+
/**
|
|
264
|
+
* Получить все поля формы как итератор
|
|
265
|
+
*
|
|
266
|
+
* Предоставляет доступ к внутренним полям для валидации и других операций
|
|
267
|
+
*
|
|
268
|
+
* @returns Итератор по всем полям формы
|
|
269
|
+
*
|
|
270
|
+
* @example
|
|
271
|
+
* ```typescript
|
|
272
|
+
* // Валидация всех полей
|
|
273
|
+
* await Promise.all(
|
|
274
|
+
* Array.from(form.getAllFields()).map(field => field.validate())
|
|
275
|
+
* );
|
|
276
|
+
* ```
|
|
277
|
+
*/
|
|
278
|
+
getAllFields(): IterableIterator<FormNode<FormValue>>;
|
|
279
|
+
/**
|
|
280
|
+
* Hook: вызывается после markAsTouched()
|
|
281
|
+
*
|
|
282
|
+
* Для GroupNode: рекурсивно помечаем все дочерние поля как touched
|
|
283
|
+
*/
|
|
284
|
+
protected onMarkAsTouched(): void;
|
|
285
|
+
/**
|
|
286
|
+
* Hook: вызывается после markAsUntouched()
|
|
287
|
+
*
|
|
288
|
+
* Для GroupNode: рекурсивно помечаем все дочерние поля как untouched
|
|
289
|
+
*/
|
|
290
|
+
protected onMarkAsUntouched(): void;
|
|
291
|
+
/**
|
|
292
|
+
* Hook: вызывается после markAsDirty()
|
|
293
|
+
*
|
|
294
|
+
* Для GroupNode: рекурсивно помечаем все дочерние поля как dirty
|
|
295
|
+
*/
|
|
296
|
+
protected onMarkAsDirty(): void;
|
|
297
|
+
/**
|
|
298
|
+
* Hook: вызывается после markAsPristine()
|
|
299
|
+
*
|
|
300
|
+
* Для GroupNode: рекурсивно помечаем все дочерние поля как pristine
|
|
301
|
+
*/
|
|
302
|
+
protected onMarkAsPristine(): void;
|
|
303
|
+
/**
|
|
304
|
+
* Отправить форму
|
|
305
|
+
* Валидирует форму и вызывает onSubmit если форма валидна
|
|
306
|
+
*/
|
|
307
|
+
submit<R>(onSubmit: (values: T) => Promise<R> | R): Promise<R | null>;
|
|
308
|
+
/**
|
|
309
|
+
* Применить validation schema к форме
|
|
310
|
+
*
|
|
311
|
+
* Использует локальный реестр валидаторов (this.validationRegistry)
|
|
312
|
+
* вместо глобального Singleton для изоляции форм друг от друга.
|
|
313
|
+
*/
|
|
314
|
+
applyValidationSchema(schemaFn: ValidationSchemaFn<T>): void;
|
|
315
|
+
/**
|
|
316
|
+
* Применить behavior schema к форме
|
|
317
|
+
*
|
|
318
|
+
* ✅ РЕФАКТОРИНГ: Делегирование BehaviorApplicator (SRP)
|
|
319
|
+
*
|
|
320
|
+
* Логика применения behavior схемы извлечена в BehaviorApplicator для:
|
|
321
|
+
* - Соблюдения Single Responsibility Principle
|
|
322
|
+
* - Уменьшения размера GroupNode (~50 строк)
|
|
323
|
+
* - Улучшения тестируемости
|
|
324
|
+
* - Консистентности с ValidationApplicator
|
|
325
|
+
*
|
|
326
|
+
* @param schemaFn Функция описания поведения формы
|
|
327
|
+
* @returns Функция cleanup для отписки от всех behaviors
|
|
328
|
+
*
|
|
329
|
+
* @example
|
|
330
|
+
* ```typescript
|
|
331
|
+
* import { copyFrom, enableWhen, computeFrom } from '@/lib/forms/core/behaviors';
|
|
332
|
+
*
|
|
333
|
+
* const behaviorSchema: BehaviorSchemaFn<MyForm> = (path) => {
|
|
334
|
+
* copyFrom(path.residenceAddress, path.registrationAddress, {
|
|
335
|
+
* when: (form) => form.sameAsRegistration === true
|
|
336
|
+
* });
|
|
337
|
+
*
|
|
338
|
+
* enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage');
|
|
339
|
+
*
|
|
340
|
+
* computeFrom(
|
|
341
|
+
* path.initialPayment,
|
|
342
|
+
* [path.propertyValue],
|
|
343
|
+
* (propertyValue) => propertyValue ? propertyValue * 0.2 : null
|
|
344
|
+
* );
|
|
345
|
+
* };
|
|
346
|
+
*
|
|
347
|
+
* const cleanup = form.applyBehaviorSchema(behaviorSchema);
|
|
348
|
+
*
|
|
349
|
+
* // Cleanup при unmount
|
|
350
|
+
* useEffect(() => cleanup, []);
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void;
|
|
354
|
+
/**
|
|
355
|
+
* Получить вложенное поле по пути
|
|
356
|
+
*
|
|
357
|
+
* Поддерживаемые форматы путей:
|
|
358
|
+
* - Simple: "email" - получить поле верхнего уровня
|
|
359
|
+
* - Nested: "address.city" - получить вложенное поле
|
|
360
|
+
* - Array index: "items[0]" - получить элемент массива по индексу
|
|
361
|
+
* - Combined: "items[0].name" - получить поле элемента массива
|
|
362
|
+
*
|
|
363
|
+
* @param path - Путь к полю
|
|
364
|
+
* @returns FormNode если найдено, undefined если путь не существует
|
|
365
|
+
*
|
|
366
|
+
* @example
|
|
367
|
+
* ```typescript
|
|
368
|
+
* const form = new GroupNode({
|
|
369
|
+
* email: { value: '', component: Input },
|
|
370
|
+
* address: {
|
|
371
|
+
* city: { value: '', component: Input }
|
|
372
|
+
* },
|
|
373
|
+
* items: [{ name: { value: '', component: Input } }]
|
|
374
|
+
* });
|
|
375
|
+
*
|
|
376
|
+
* form.getFieldByPath('email'); // FieldNode
|
|
377
|
+
* form.getFieldByPath('address.city'); // FieldNode
|
|
378
|
+
* form.getFieldByPath('items[0]'); // GroupNode
|
|
379
|
+
* form.getFieldByPath('items[0].name'); // FieldNode
|
|
380
|
+
* form.getFieldByPath('invalid.path'); // undefined
|
|
381
|
+
* ```
|
|
382
|
+
*/
|
|
383
|
+
getFieldByPath(path: string): FormNode<FormValue> | undefined;
|
|
384
|
+
/**
|
|
385
|
+
* Применить contextual валидаторы к полям
|
|
386
|
+
*
|
|
387
|
+
* ✅ РЕФАКТОРИНГ: Делегирование ValidationApplicator (SRP)
|
|
388
|
+
*
|
|
389
|
+
* Логика применения валидаторов извлечена в ValidationApplicator для:
|
|
390
|
+
* - Соблюдения Single Responsibility Principle
|
|
391
|
+
* - Уменьшения размера GroupNode (~120 строк)
|
|
392
|
+
* - Улучшения тестируемости
|
|
393
|
+
*
|
|
394
|
+
* @param validators Зарегистрированные валидаторы
|
|
395
|
+
*/
|
|
396
|
+
applyContextualValidators(validators: ValidatorRegistration[]): Promise<void>;
|
|
397
|
+
/**
|
|
398
|
+
* Создать узел на основе конфигурации
|
|
399
|
+
*
|
|
400
|
+
* ✅ РЕФАКТОРИНГ: Полное делегирование NodeFactory
|
|
401
|
+
*
|
|
402
|
+
* NodeFactory теперь обрабатывает:
|
|
403
|
+
* - Массивы [schema, ...items]
|
|
404
|
+
* - FieldConfig
|
|
405
|
+
* - GroupConfig
|
|
406
|
+
* - ArrayConfig
|
|
407
|
+
*
|
|
408
|
+
* @param config Конфигурация узла
|
|
409
|
+
* @returns Созданный узел формы
|
|
410
|
+
* @private
|
|
411
|
+
*/
|
|
412
|
+
private createNode;
|
|
413
|
+
/**
|
|
414
|
+
* Связывает два поля: при изменении source автоматически обновляется target
|
|
415
|
+
* Поддерживает опциональную трансформацию значения
|
|
416
|
+
*
|
|
417
|
+
* @param sourceKey - Ключ поля-источника
|
|
418
|
+
* @param targetKey - Ключ поля-цели
|
|
419
|
+
* @param transform - Опциональная функция трансформации значения
|
|
420
|
+
* @returns Функция отписки для cleanup
|
|
421
|
+
*
|
|
422
|
+
* @example
|
|
423
|
+
* ```typescript
|
|
424
|
+
* // Автоматический расчет минимального взноса от стоимости недвижимости
|
|
425
|
+
* const dispose = form.linkFields(
|
|
426
|
+
* 'propertyValue',
|
|
427
|
+
* 'initialPayment',
|
|
428
|
+
* (propertyValue) => propertyValue ? propertyValue * 0.2 : null
|
|
429
|
+
* );
|
|
430
|
+
*
|
|
431
|
+
* // При изменении propertyValue → автоматически обновится initialPayment
|
|
432
|
+
* form.propertyValue.setValue(1000000);
|
|
433
|
+
* // initialPayment станет 200000
|
|
434
|
+
*
|
|
435
|
+
* // Cleanup
|
|
436
|
+
* useEffect(() => dispose, []);
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
|
|
440
|
+
/**
|
|
441
|
+
* Подписка на изменения вложенного поля по строковому пути
|
|
442
|
+
* Поддерживает вложенные пути типа "address.city"
|
|
443
|
+
*
|
|
444
|
+
* @param fieldPath - Строковый путь к полю (например, "address.city")
|
|
445
|
+
* @param callback - Функция, вызываемая при изменении поля
|
|
446
|
+
* @returns Функция отписки для cleanup
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```typescript
|
|
450
|
+
* // Подписка на изменение страны для загрузки городов
|
|
451
|
+
* const dispose = form.watchField(
|
|
452
|
+
* 'registrationAddress.country',
|
|
453
|
+
* async (countryCode) => {
|
|
454
|
+
* if (countryCode) {
|
|
455
|
+
* const cities = await fetchCitiesByCountry(countryCode);
|
|
456
|
+
* form.registrationAddress.city.updateComponentProps({
|
|
457
|
+
* options: cities
|
|
458
|
+
* });
|
|
459
|
+
* }
|
|
460
|
+
* }
|
|
461
|
+
* );
|
|
462
|
+
*
|
|
463
|
+
* // Cleanup
|
|
464
|
+
* useEffect(() => dispose, []);
|
|
465
|
+
* ```
|
|
466
|
+
*/
|
|
467
|
+
watchField<K extends keyof T>(fieldPath: K extends string ? K : string, callback: (value: T[K]) => void | Promise<void>): () => void;
|
|
468
|
+
/**
|
|
469
|
+
* Hook: вызывается после disable()
|
|
470
|
+
*
|
|
471
|
+
* Для GroupNode: рекурсивно отключаем все дочерние поля
|
|
472
|
+
*/
|
|
473
|
+
protected onDisable(): void;
|
|
474
|
+
/**
|
|
475
|
+
* Hook: вызывается после enable()
|
|
476
|
+
*
|
|
477
|
+
* Для GroupNode: рекурсивно включаем все дочерние поля
|
|
478
|
+
*/
|
|
479
|
+
protected onEnable(): void;
|
|
480
|
+
/**
|
|
481
|
+
* Очистить все ресурсы узла
|
|
482
|
+
* Рекурсивно очищает все subscriptions и дочерние узлы
|
|
483
|
+
*
|
|
484
|
+
* @example
|
|
485
|
+
* ```typescript
|
|
486
|
+
* useEffect(() => {
|
|
487
|
+
* return () => {
|
|
488
|
+
* form.dispose();
|
|
489
|
+
* };
|
|
490
|
+
* }, []);
|
|
491
|
+
* ```
|
|
492
|
+
*/
|
|
493
|
+
dispose(): void;
|
|
494
|
+
}
|