@reformer/core 1.1.0 → 2.0.0-beta.2
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-DzYL8kY_.js +499 -0
- package/dist/behaviors.d.ts +6 -2
- package/dist/behaviors.js +19 -227
- package/dist/core/behavior/behavior-context.d.ts +6 -2
- package/dist/core/behavior/create-field-path.d.ts +3 -16
- package/dist/core/nodes/group-node.d.ts +14 -193
- package/dist/core/types/form-context.d.ts +10 -4
- package/dist/core/utils/field-path.d.ts +48 -0
- package/dist/core/utils/index.d.ts +1 -0
- package/dist/core/validation/core/validate-tree.d.ts +10 -4
- package/dist/core/validation/field-path.d.ts +3 -39
- package/dist/core/validation/validation-context.d.ts +23 -0
- package/dist/hooks/types.d.ts +328 -0
- package/dist/hooks/useFormControl.d.ts +13 -37
- package/dist/hooks/useFormControlValue.d.ts +167 -0
- package/dist/hooks/useSignalSubscription.d.ts +17 -0
- package/dist/index.d.ts +6 -1
- package/dist/index.js +2886 -8
- package/dist/{create-field-path-CdPF3lIK.js → registry-helpers-BRxAr6nG.js} +133 -347
- package/dist/validators-gXoHPdqM.js +418 -0
- package/dist/validators.d.ts +6 -2
- package/dist/validators.js +29 -296
- package/llms.txt +1283 -22
- package/package.json +8 -4
- package/dist/core/behavior/behavior-applicator.d.ts +0 -71
- package/dist/core/behavior/behavior-applicator.js +0 -92
- package/dist/core/behavior/behavior-context.js +0 -38
- package/dist/core/behavior/behavior-registry.js +0 -198
- package/dist/core/behavior/behaviors/compute-from.js +0 -84
- package/dist/core/behavior/behaviors/copy-from.js +0 -64
- package/dist/core/behavior/behaviors/enable-when.js +0 -81
- package/dist/core/behavior/behaviors/index.js +0 -11
- package/dist/core/behavior/behaviors/reset-when.js +0 -63
- package/dist/core/behavior/behaviors/revalidate-when.js +0 -51
- package/dist/core/behavior/behaviors/sync-fields.js +0 -66
- package/dist/core/behavior/behaviors/transform-value.js +0 -110
- package/dist/core/behavior/behaviors/watch-field.js +0 -56
- package/dist/core/behavior/compose-behavior.js +0 -166
- package/dist/core/behavior/create-field-path.js +0 -69
- package/dist/core/behavior/index.js +0 -17
- package/dist/core/behavior/types.js +0 -7
- package/dist/core/context/form-context-impl.js +0 -37
- package/dist/core/factories/index.js +0 -6
- package/dist/core/factories/node-factory.js +0 -281
- package/dist/core/nodes/array-node.js +0 -534
- package/dist/core/nodes/field-node.js +0 -510
- package/dist/core/nodes/form-node.js +0 -343
- package/dist/core/nodes/group-node/field-registry.d.ts +0 -191
- package/dist/core/nodes/group-node/field-registry.js +0 -215
- package/dist/core/nodes/group-node/index.d.ts +0 -11
- package/dist/core/nodes/group-node/index.js +0 -11
- package/dist/core/nodes/group-node/proxy-builder.d.ts +0 -71
- package/dist/core/nodes/group-node/proxy-builder.js +0 -161
- package/dist/core/nodes/group-node/state-manager.d.ts +0 -184
- package/dist/core/nodes/group-node/state-manager.js +0 -265
- package/dist/core/nodes/group-node.js +0 -770
- package/dist/core/types/deep-schema.js +0 -11
- package/dist/core/types/field-path.js +0 -4
- package/dist/core/types/form-context.js +0 -25
- package/dist/core/types/group-node-proxy.js +0 -31
- package/dist/core/types/index.js +0 -4
- package/dist/core/types/validation-schema.js +0 -10
- package/dist/core/utils/create-form.js +0 -24
- package/dist/core/utils/debounce.js +0 -197
- package/dist/core/utils/error-handler.js +0 -226
- package/dist/core/utils/field-path-navigator.js +0 -374
- package/dist/core/utils/index.js +0 -14
- package/dist/core/utils/registry-helpers.js +0 -79
- package/dist/core/utils/registry-stack.js +0 -86
- package/dist/core/utils/resources.js +0 -69
- package/dist/core/utils/subscription-manager.js +0 -214
- package/dist/core/utils/type-guards.js +0 -169
- package/dist/core/validation/core/apply-when.js +0 -41
- package/dist/core/validation/core/apply.js +0 -38
- package/dist/core/validation/core/index.js +0 -8
- package/dist/core/validation/core/validate-async.js +0 -45
- package/dist/core/validation/core/validate-tree.js +0 -37
- package/dist/core/validation/core/validate.js +0 -38
- package/dist/core/validation/field-path.js +0 -147
- package/dist/core/validation/index.js +0 -33
- package/dist/core/validation/validate-form.js +0 -152
- package/dist/core/validation/validation-applicator.js +0 -217
- package/dist/core/validation/validation-context.js +0 -75
- package/dist/core/validation/validation-registry.js +0 -298
- package/dist/core/validation/validators/array-validators.js +0 -86
- package/dist/core/validation/validators/date.js +0 -117
- package/dist/core/validation/validators/email.js +0 -60
- package/dist/core/validation/validators/index.js +0 -14
- package/dist/core/validation/validators/max-length.js +0 -60
- package/dist/core/validation/validators/max.js +0 -60
- package/dist/core/validation/validators/min-length.js +0 -60
- package/dist/core/validation/validators/min.js +0 -60
- package/dist/core/validation/validators/number.js +0 -90
- package/dist/core/validation/validators/pattern.js +0 -62
- package/dist/core/validation/validators/phone.js +0 -58
- package/dist/core/validation/validators/required.js +0 -69
- package/dist/core/validation/validators/url.js +0 -55
- package/dist/hooks/useFormControl.js +0 -298
- package/dist/node-factory-D7DOnSSN.js +0 -3200
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FieldRegistry - управление полями в GroupNode
|
|
3
|
-
*
|
|
4
|
-
* Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
|
|
5
|
-
* Отвечает только за хранение и управление коллекцией полей формы.
|
|
6
|
-
*
|
|
7
|
-
* @template T Тип формы (объект)
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const registry = new FieldRegistry<{ email: string; name: string }>();
|
|
12
|
-
* registry.set('email', emailField);
|
|
13
|
-
* registry.set('name', nameField);
|
|
14
|
-
*
|
|
15
|
-
* const emailField = registry.get('email');
|
|
16
|
-
* registry.forEach((field, key) => {
|
|
17
|
-
* console.log(key, field.value.value);
|
|
18
|
-
* });
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
21
|
-
/**
|
|
22
|
-
* Реестр полей формы
|
|
23
|
-
*
|
|
24
|
-
* Предоставляет типобезопасный доступ к полям формы
|
|
25
|
-
* через Map-подобный интерфейс
|
|
26
|
-
*
|
|
27
|
-
* @template T Тип формы (объект)
|
|
28
|
-
*/
|
|
29
|
-
export class FieldRegistry {
|
|
30
|
-
/**
|
|
31
|
-
* Внутреннее хранилище полей
|
|
32
|
-
* Map обеспечивает быструю lookup производительность O(1)
|
|
33
|
-
*/
|
|
34
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
|
-
fields = new Map();
|
|
36
|
-
/**
|
|
37
|
-
* Установить поле в реестр
|
|
38
|
-
*
|
|
39
|
-
* @param key - Ключ поля (имя свойства в типе T)
|
|
40
|
-
* @param node - FormNode для этого поля
|
|
41
|
-
*
|
|
42
|
-
* @example
|
|
43
|
-
* ```typescript
|
|
44
|
-
* registry.set('email', new FieldNode({ value: '' }));
|
|
45
|
-
* ```
|
|
46
|
-
*/
|
|
47
|
-
set(key, node) {
|
|
48
|
-
this.fields.set(key, node);
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Получить поле из реестра
|
|
52
|
-
*
|
|
53
|
-
* @param key - Ключ поля
|
|
54
|
-
* @returns FormNode или undefined, если поле не найдено
|
|
55
|
-
*
|
|
56
|
-
* @example
|
|
57
|
-
* ```typescript
|
|
58
|
-
* const emailField = registry.get('email');
|
|
59
|
-
* if (emailField) {
|
|
60
|
-
* console.log(emailField.value.value);
|
|
61
|
-
* }
|
|
62
|
-
* ```
|
|
63
|
-
*/
|
|
64
|
-
get(key) {
|
|
65
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
66
|
-
return this.fields.get(key);
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Проверить наличие поля в реестре
|
|
70
|
-
*
|
|
71
|
-
* @param key - Ключ поля
|
|
72
|
-
* @returns true если поле существует
|
|
73
|
-
*
|
|
74
|
-
* @example
|
|
75
|
-
* ```typescript
|
|
76
|
-
* if (registry.has('email')) {
|
|
77
|
-
* console.log('Email field exists');
|
|
78
|
-
* }
|
|
79
|
-
* ```
|
|
80
|
-
*/
|
|
81
|
-
has(key) {
|
|
82
|
-
return this.fields.has(key);
|
|
83
|
-
}
|
|
84
|
-
/**
|
|
85
|
-
* Удалить поле из реестра
|
|
86
|
-
*
|
|
87
|
-
* @param key - Ключ поля
|
|
88
|
-
* @returns true если поле было удалено, false если поля не было
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```typescript
|
|
92
|
-
* registry.delete('email');
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
delete(key) {
|
|
96
|
-
return this.fields.delete(key);
|
|
97
|
-
}
|
|
98
|
-
/**
|
|
99
|
-
* Перебрать все поля
|
|
100
|
-
*
|
|
101
|
-
* @param callback - Функция обратного вызова для каждого поля
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* ```typescript
|
|
105
|
-
* registry.forEach((field, key) => {
|
|
106
|
-
* console.log(`${key}: ${field.value.value}`);
|
|
107
|
-
* });
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
forEach(callback) {
|
|
111
|
-
this.fields.forEach(callback);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Получить итератор значений (полей)
|
|
115
|
-
*
|
|
116
|
-
* @returns Итератор по всем полям
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* ```typescript
|
|
120
|
-
* for (const field of registry.values()) {
|
|
121
|
-
* await field.validate();
|
|
122
|
-
* }
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
values() {
|
|
126
|
-
return this.fields.values();
|
|
127
|
-
}
|
|
128
|
-
/**
|
|
129
|
-
* Получить итератор пар [ключ, значение]
|
|
130
|
-
*
|
|
131
|
-
* @returns Итератор по всем записям
|
|
132
|
-
*
|
|
133
|
-
* @example
|
|
134
|
-
* ```typescript
|
|
135
|
-
* for (const [key, field] of registry.entries()) {
|
|
136
|
-
* console.log(key, field.value.value);
|
|
137
|
-
* }
|
|
138
|
-
* ```
|
|
139
|
-
*/
|
|
140
|
-
entries() {
|
|
141
|
-
return this.fields.entries();
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Получить итератор ключей полей
|
|
145
|
-
*
|
|
146
|
-
* @returns Итератор по всем ключам
|
|
147
|
-
*
|
|
148
|
-
* @example
|
|
149
|
-
* ```typescript
|
|
150
|
-
* const fieldNames = Array.from(registry.keys());
|
|
151
|
-
* // ['email', 'name', 'age']
|
|
152
|
-
* ```
|
|
153
|
-
*/
|
|
154
|
-
keys() {
|
|
155
|
-
return this.fields.keys();
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Получить количество полей
|
|
159
|
-
*
|
|
160
|
-
* @returns Количество зарегистрированных полей
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```typescript
|
|
164
|
-
* console.log(`Form has ${registry.size()} fields`);
|
|
165
|
-
* ```
|
|
166
|
-
*/
|
|
167
|
-
size() {
|
|
168
|
-
return this.fields.size;
|
|
169
|
-
}
|
|
170
|
-
/**
|
|
171
|
-
* Очистить все поля
|
|
172
|
-
*
|
|
173
|
-
* Удаляет все поля из реестра
|
|
174
|
-
*
|
|
175
|
-
* @example
|
|
176
|
-
* ```typescript
|
|
177
|
-
* registry.clear();
|
|
178
|
-
* console.log(registry.size()); // 0
|
|
179
|
-
* ```
|
|
180
|
-
*/
|
|
181
|
-
clear() {
|
|
182
|
-
this.fields.clear();
|
|
183
|
-
}
|
|
184
|
-
/**
|
|
185
|
-
* Получить все поля как массив
|
|
186
|
-
*
|
|
187
|
-
* Полезно для операций, требующих работу с массивом
|
|
188
|
-
*
|
|
189
|
-
* @returns Массив всех полей
|
|
190
|
-
*
|
|
191
|
-
* @example
|
|
192
|
-
* ```typescript
|
|
193
|
-
* const allValid = registry.toArray().every(field => field.valid.value);
|
|
194
|
-
* ```
|
|
195
|
-
*/
|
|
196
|
-
toArray() {
|
|
197
|
-
return Array.from(this.fields.values());
|
|
198
|
-
}
|
|
199
|
-
/**
|
|
200
|
-
* Получить Map-представление реестра (readonly)
|
|
201
|
-
*
|
|
202
|
-
* Используйте для совместимости с существующим кодом
|
|
203
|
-
*
|
|
204
|
-
* @returns ReadonlyMap с полями
|
|
205
|
-
* @internal
|
|
206
|
-
*
|
|
207
|
-
* @example
|
|
208
|
-
* ```typescript
|
|
209
|
-
* const mapView = registry.asMap();
|
|
210
|
-
* ```
|
|
211
|
-
*/
|
|
212
|
-
asMap() {
|
|
213
|
-
return this.fields;
|
|
214
|
-
}
|
|
215
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GroupNode modules
|
|
3
|
-
*
|
|
4
|
-
* Модульная структура для GroupNode:
|
|
5
|
-
* - FieldRegistry: Управление коллекцией полей
|
|
6
|
-
* - ProxyBuilder: Создание Proxy для типобезопасного доступа к полям
|
|
7
|
-
* - StateManager: Управление состоянием формы (signals)
|
|
8
|
-
*/
|
|
9
|
-
export { FieldRegistry } from './field-registry';
|
|
10
|
-
export { ProxyBuilder } from './proxy-builder';
|
|
11
|
-
export { StateManager } from './state-manager';
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GroupNode modules
|
|
3
|
-
*
|
|
4
|
-
* Модульная структура для GroupNode:
|
|
5
|
-
* - FieldRegistry: Управление коллекцией полей
|
|
6
|
-
* - ProxyBuilder: Создание Proxy для типобезопасного доступа к полям
|
|
7
|
-
* - StateManager: Управление состоянием формы (signals)
|
|
8
|
-
*/
|
|
9
|
-
export { FieldRegistry } from './field-registry';
|
|
10
|
-
export { ProxyBuilder } from './proxy-builder';
|
|
11
|
-
export { StateManager } from './state-manager';
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ProxyBuilder - создание Proxy для типобезопасного доступа к полям GroupNode
|
|
3
|
-
*
|
|
4
|
-
* Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
|
|
5
|
-
* Отвечает только за создание Proxy с расширенной поддержкой операций.
|
|
6
|
-
*
|
|
7
|
-
* @template T Тип формы (объект)
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const fieldRegistry = new FieldRegistry<FormType>();
|
|
12
|
-
* const proxyBuilder = new ProxyBuilder(fieldRegistry);
|
|
13
|
-
* const proxy = proxyBuilder.build(groupNode);
|
|
14
|
-
*
|
|
15
|
-
* // Теперь можно обращаться к полям напрямую:
|
|
16
|
-
* console.log(proxy.email.value);
|
|
17
|
-
* console.log('email' in proxy); // true
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
import type { GroupNode } from '../group-node';
|
|
21
|
-
import type { GroupNodeWithControls } from '../../types/group-node-proxy';
|
|
22
|
-
import type { FieldRegistry } from './field-registry';
|
|
23
|
-
/**
|
|
24
|
-
* Строитель Proxy для GroupNode
|
|
25
|
-
*
|
|
26
|
-
* Создает Proxy с поддержкой:
|
|
27
|
-
* - get: Прямой доступ к полям через точечную нотацию
|
|
28
|
-
* - set: Предупреждение о попытке прямой установки полей
|
|
29
|
-
* - has: Проверка существования поля ('email' in form)
|
|
30
|
-
* - ownKeys: Перечисление всех ключей (Object.keys(form))
|
|
31
|
-
* - getOwnPropertyDescriptor: Получение дескрипторов полей
|
|
32
|
-
*
|
|
33
|
-
* @template T Тип формы (объект)
|
|
34
|
-
*/
|
|
35
|
-
export declare class ProxyBuilder<T> {
|
|
36
|
-
private fieldRegistry;
|
|
37
|
-
/**
|
|
38
|
-
* @param fieldRegistry - Реестр полей для доступа к коллекции
|
|
39
|
-
*/
|
|
40
|
-
constructor(fieldRegistry: FieldRegistry<T>);
|
|
41
|
-
/**
|
|
42
|
-
* Создать Proxy для GroupNode
|
|
43
|
-
*
|
|
44
|
-
* Proxy позволяет обращаться к полям формы напрямую:
|
|
45
|
-
* - form.email вместо form.fields.get('email')
|
|
46
|
-
* - form.address.city вместо form.fields.get('address').fields.get('city')
|
|
47
|
-
*
|
|
48
|
-
* @param target - GroupNode для которого создается Proxy
|
|
49
|
-
* @returns Proxy с типобезопасным доступом к полям
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```typescript
|
|
53
|
-
* const proxy = proxyBuilder.build(groupNode);
|
|
54
|
-
*
|
|
55
|
-
* // Доступ к полям
|
|
56
|
-
* console.log(proxy.email.value); // Работает!
|
|
57
|
-
* console.log(proxy.name.value); // Работает!
|
|
58
|
-
*
|
|
59
|
-
* // Доступ к методам GroupNode
|
|
60
|
-
* await proxy.validate(); // Работает!
|
|
61
|
-
* proxy.markAsTouched(); // Работает!
|
|
62
|
-
*
|
|
63
|
-
* // Проверка существования
|
|
64
|
-
* if ('email' in proxy) { ... }
|
|
65
|
-
*
|
|
66
|
-
* // Перечисление ключей
|
|
67
|
-
* Object.keys(proxy); // ['email', 'name', ...]
|
|
68
|
-
* ```
|
|
69
|
-
*/
|
|
70
|
-
build(target: GroupNode<T>): GroupNodeWithControls<T>;
|
|
71
|
-
}
|
|
@@ -1,161 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ProxyBuilder - создание Proxy для типобезопасного доступа к полям GroupNode
|
|
3
|
-
*
|
|
4
|
-
* Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
|
|
5
|
-
* Отвечает только за создание Proxy с расширенной поддержкой операций.
|
|
6
|
-
*
|
|
7
|
-
* @template T Тип формы (объект)
|
|
8
|
-
*
|
|
9
|
-
* @example
|
|
10
|
-
* ```typescript
|
|
11
|
-
* const fieldRegistry = new FieldRegistry<FormType>();
|
|
12
|
-
* const proxyBuilder = new ProxyBuilder(fieldRegistry);
|
|
13
|
-
* const proxy = proxyBuilder.build(groupNode);
|
|
14
|
-
*
|
|
15
|
-
* // Теперь можно обращаться к полям напрямую:
|
|
16
|
-
* console.log(proxy.email.value);
|
|
17
|
-
* console.log('email' in proxy); // true
|
|
18
|
-
* ```
|
|
19
|
-
*/
|
|
20
|
-
/**
|
|
21
|
-
* Строитель Proxy для GroupNode
|
|
22
|
-
*
|
|
23
|
-
* Создает Proxy с поддержкой:
|
|
24
|
-
* - get: Прямой доступ к полям через точечную нотацию
|
|
25
|
-
* - set: Предупреждение о попытке прямой установки полей
|
|
26
|
-
* - has: Проверка существования поля ('email' in form)
|
|
27
|
-
* - ownKeys: Перечисление всех ключей (Object.keys(form))
|
|
28
|
-
* - getOwnPropertyDescriptor: Получение дескрипторов полей
|
|
29
|
-
*
|
|
30
|
-
* @template T Тип формы (объект)
|
|
31
|
-
*/
|
|
32
|
-
export class ProxyBuilder {
|
|
33
|
-
fieldRegistry;
|
|
34
|
-
/**
|
|
35
|
-
* @param fieldRegistry - Реестр полей для доступа к коллекции
|
|
36
|
-
*/
|
|
37
|
-
constructor(fieldRegistry) {
|
|
38
|
-
this.fieldRegistry = fieldRegistry;
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Создать Proxy для GroupNode
|
|
42
|
-
*
|
|
43
|
-
* Proxy позволяет обращаться к полям формы напрямую:
|
|
44
|
-
* - form.email вместо form.fields.get('email')
|
|
45
|
-
* - form.address.city вместо form.fields.get('address').fields.get('city')
|
|
46
|
-
*
|
|
47
|
-
* @param target - GroupNode для которого создается Proxy
|
|
48
|
-
* @returns Proxy с типобезопасным доступом к полям
|
|
49
|
-
*
|
|
50
|
-
* @example
|
|
51
|
-
* ```typescript
|
|
52
|
-
* const proxy = proxyBuilder.build(groupNode);
|
|
53
|
-
*
|
|
54
|
-
* // Доступ к полям
|
|
55
|
-
* console.log(proxy.email.value); // Работает!
|
|
56
|
-
* console.log(proxy.name.value); // Работает!
|
|
57
|
-
*
|
|
58
|
-
* // Доступ к методам GroupNode
|
|
59
|
-
* await proxy.validate(); // Работает!
|
|
60
|
-
* proxy.markAsTouched(); // Работает!
|
|
61
|
-
*
|
|
62
|
-
* // Проверка существования
|
|
63
|
-
* if ('email' in proxy) { ... }
|
|
64
|
-
*
|
|
65
|
-
* // Перечисление ключей
|
|
66
|
-
* Object.keys(proxy); // ['email', 'name', ...]
|
|
67
|
-
* ```
|
|
68
|
-
*/
|
|
69
|
-
build(target) {
|
|
70
|
-
return new Proxy(target, {
|
|
71
|
-
/**
|
|
72
|
-
* Get trap: Перехват доступа к свойствам
|
|
73
|
-
*
|
|
74
|
-
* Приоритет:
|
|
75
|
-
* 1. Собственные свойства и методы GroupNode (validate, setValue и т.д.)
|
|
76
|
-
* 2. Поля формы из fieldRegistry
|
|
77
|
-
* 3. undefined для несуществующих свойств
|
|
78
|
-
*/
|
|
79
|
-
get: (target, prop) => {
|
|
80
|
-
// Приоритет 1: Собственные свойства и методы GroupNode
|
|
81
|
-
// Это важно, чтобы методы validate(), setValue() и т.д. работали корректно
|
|
82
|
-
if (prop in target) {
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
84
|
-
return target[prop];
|
|
85
|
-
}
|
|
86
|
-
// Приоритет 2: Поля формы
|
|
87
|
-
// Используем fieldRegistry для типобезопасного доступа
|
|
88
|
-
if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
|
|
89
|
-
return this.fieldRegistry.get(prop);
|
|
90
|
-
}
|
|
91
|
-
// Fallback для несуществующих свойств
|
|
92
|
-
return undefined;
|
|
93
|
-
},
|
|
94
|
-
/**
|
|
95
|
-
* Set trap: Перехват установки свойств
|
|
96
|
-
*
|
|
97
|
-
* Запрещает прямую установку значений полей через form.email = value
|
|
98
|
-
* Пользователь должен использовать form.email.setValue(value) или form.setValue({...})
|
|
99
|
-
*/
|
|
100
|
-
set: (target, prop, value) => {
|
|
101
|
-
// Запретить прямое изменение полей
|
|
102
|
-
if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
|
|
103
|
-
if (import.meta.env.DEV) {
|
|
104
|
-
console.warn(`[GroupNode] Cannot set field "${prop}" directly. Use .setValue() or .patchValue() instead.\n` +
|
|
105
|
-
`Example: form.setValue({ ${prop}: ${JSON.stringify(value)} })`);
|
|
106
|
-
}
|
|
107
|
-
return false;
|
|
108
|
-
}
|
|
109
|
-
// Разрешить установку других свойств
|
|
110
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
111
|
-
target[prop] = value;
|
|
112
|
-
return true;
|
|
113
|
-
},
|
|
114
|
-
/**
|
|
115
|
-
* Has trap: Перехват оператора 'in'
|
|
116
|
-
*
|
|
117
|
-
* Позволяет проверять существование полей:
|
|
118
|
-
* if ('email' in form) { ... }
|
|
119
|
-
*/
|
|
120
|
-
has: (target, prop) => {
|
|
121
|
-
// Проверяем наличие в полях
|
|
122
|
-
if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
|
|
123
|
-
return true;
|
|
124
|
-
}
|
|
125
|
-
// Проверяем наличие в самом объекте
|
|
126
|
-
return prop in target;
|
|
127
|
-
},
|
|
128
|
-
/**
|
|
129
|
-
* OwnKeys trap: Перехват Object.keys() / Object.getOwnPropertyNames()
|
|
130
|
-
*
|
|
131
|
-
* Возвращает объединенный список:
|
|
132
|
-
* - Ключей самого GroupNode
|
|
133
|
-
* - Ключей полей из fieldRegistry
|
|
134
|
-
*/
|
|
135
|
-
ownKeys: (target) => {
|
|
136
|
-
const nodeKeys = Reflect.ownKeys(target);
|
|
137
|
-
const fieldKeys = Array.from(this.fieldRegistry.keys());
|
|
138
|
-
// Используем Set для удаления дубликатов
|
|
139
|
-
return [...new Set([...nodeKeys, ...fieldKeys])];
|
|
140
|
-
},
|
|
141
|
-
/**
|
|
142
|
-
* GetOwnPropertyDescriptor trap: Перехват Object.getOwnPropertyDescriptor()
|
|
143
|
-
*
|
|
144
|
-
* Возвращает дескриптор свойства для полей и свойств GroupNode
|
|
145
|
-
* Важно для корректной работы Object.keys() и других рефлексивных операций
|
|
146
|
-
*/
|
|
147
|
-
getOwnPropertyDescriptor: (target, prop) => {
|
|
148
|
-
// Дескриптор для полей формы
|
|
149
|
-
if (typeof prop === 'string' && this.fieldRegistry.has(prop)) {
|
|
150
|
-
return {
|
|
151
|
-
enumerable: true, // Поле должно быть перечисляемым
|
|
152
|
-
configurable: true, // Поле может быть удалено
|
|
153
|
-
// Не указываем writable, т.к. это accessor property через get/set traps
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
// Дескриптор для свойств самого объекта
|
|
157
|
-
return Reflect.getOwnPropertyDescriptor(target, prop);
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* StateManager - управление состоянием GroupNode
|
|
3
|
-
*
|
|
4
|
-
* Инкапсулирует всю логику создания и управления сигналами состояния:
|
|
5
|
-
* - Приватные сигналы (submitting, disabled, formErrors)
|
|
6
|
-
* - Публичные computed signals (value, valid, invalid, touched, dirty, pending, errors, status, submitting)
|
|
7
|
-
*
|
|
8
|
-
* Извлечено из GroupNode для соблюдения SRP (Single Responsibility Principle).
|
|
9
|
-
* Отвечает только за логику управления состоянием формы.
|
|
10
|
-
*
|
|
11
|
-
* @template T Тип формы (объект)
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```typescript
|
|
15
|
-
* class GroupNode {
|
|
16
|
-
* private stateManager: StateManager<T>;
|
|
17
|
-
*
|
|
18
|
-
* constructor(schema: FormSchema<T>) {
|
|
19
|
-
* this.fieldRegistry = new FieldRegistry<T>();
|
|
20
|
-
* // ... создание полей ...
|
|
21
|
-
* this.stateManager = new StateManager(this.fieldRegistry);
|
|
22
|
-
*
|
|
23
|
-
* // Доступ к computed signals
|
|
24
|
-
* this.value = this.stateManager.value;
|
|
25
|
-
* this.valid = this.stateManager.valid;
|
|
26
|
-
* }
|
|
27
|
-
* }
|
|
28
|
-
* ```
|
|
29
|
-
*/
|
|
30
|
-
import type { ReadonlySignal } from '@preact/signals-core';
|
|
31
|
-
import type { ValidationError, FieldStatus } from '../../types';
|
|
32
|
-
import type { FieldRegistry } from './field-registry';
|
|
33
|
-
/**
|
|
34
|
-
* Менеджер состояния для GroupNode
|
|
35
|
-
*
|
|
36
|
-
* Создает и управляет всеми сигналами состояния формы:
|
|
37
|
-
* - value - значение формы как объект
|
|
38
|
-
* - valid/invalid - валидность формы
|
|
39
|
-
* - touched/dirty - пользовательское взаимодействие
|
|
40
|
-
* - pending - асинхронная валидация в процессе
|
|
41
|
-
* - errors - все ошибки валидации (form-level + field-level)
|
|
42
|
-
* - status - общий статус формы
|
|
43
|
-
* - submitting - флаг отправки формы
|
|
44
|
-
*
|
|
45
|
-
* @template T Тип формы (объект)
|
|
46
|
-
*/
|
|
47
|
-
export declare class StateManager<T> {
|
|
48
|
-
private readonly fieldRegistry;
|
|
49
|
-
/**
|
|
50
|
-
* Флаг отправки формы
|
|
51
|
-
* Устанавливается в true во время отправки формы на сервер
|
|
52
|
-
*/
|
|
53
|
-
private _submitting;
|
|
54
|
-
/**
|
|
55
|
-
* Флаг disabled состояния
|
|
56
|
-
* Если true, форма считается disabled
|
|
57
|
-
*/
|
|
58
|
-
private _disabled;
|
|
59
|
-
/**
|
|
60
|
-
* Form-level validation errors (не связанные с конкретным полем)
|
|
61
|
-
* Используется для server-side errors или кросс-полевой валидации
|
|
62
|
-
*/
|
|
63
|
-
private _formErrors;
|
|
64
|
-
/**
|
|
65
|
-
* Значение формы как объект
|
|
66
|
-
*
|
|
67
|
-
* Computed signal, который автоматически пересчитывается при изменении любого поля.
|
|
68
|
-
* Использует мемоизацию - если зависимости не изменились, вернет закешированный объект.
|
|
69
|
-
*
|
|
70
|
-
* @example
|
|
71
|
-
* ```typescript
|
|
72
|
-
* const form = new GroupNode({ email: { value: 'test@mail.com' } });
|
|
73
|
-
* console.log(form.value.value); // { email: 'test@mail.com' }
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
76
|
-
readonly value: ReadonlySignal<T>;
|
|
77
|
-
/**
|
|
78
|
-
* Форма валидна?
|
|
79
|
-
*
|
|
80
|
-
* Computed signal. Форма валидна, если:
|
|
81
|
-
* - Нет form-level errors
|
|
82
|
-
* - Все поля валидны
|
|
83
|
-
*/
|
|
84
|
-
readonly valid: ReadonlySignal<boolean>;
|
|
85
|
-
/**
|
|
86
|
-
* Форма невалидна?
|
|
87
|
-
*
|
|
88
|
-
* Computed signal. Инверсия valid.
|
|
89
|
-
*/
|
|
90
|
-
readonly invalid: ReadonlySignal<boolean>;
|
|
91
|
-
/**
|
|
92
|
-
* Хотя бы одно поле touched?
|
|
93
|
-
*
|
|
94
|
-
* Computed signal. Возвращает true, если хотя бы одно поле было touched.
|
|
95
|
-
*/
|
|
96
|
-
readonly touched: ReadonlySignal<boolean>;
|
|
97
|
-
/**
|
|
98
|
-
* Хотя бы одно поле dirty?
|
|
99
|
-
*
|
|
100
|
-
* Computed signal. Возвращает true, если хотя бы одно поле изменилось.
|
|
101
|
-
*/
|
|
102
|
-
readonly dirty: ReadonlySignal<boolean>;
|
|
103
|
-
/**
|
|
104
|
-
* Асинхронная валидация в процессе?
|
|
105
|
-
*
|
|
106
|
-
* Computed signal. Возвращает true, если хотя бы одно поле находится в pending состоянии.
|
|
107
|
-
*/
|
|
108
|
-
readonly pending: ReadonlySignal<boolean>;
|
|
109
|
-
/**
|
|
110
|
-
* Все ошибки валидации
|
|
111
|
-
*
|
|
112
|
-
* Computed signal. Возвращает массив всех ошибок:
|
|
113
|
-
* - Form-level errors
|
|
114
|
-
* - Field-level errors (из всех вложенных полей)
|
|
115
|
-
*/
|
|
116
|
-
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
117
|
-
/**
|
|
118
|
-
* Общий статус формы
|
|
119
|
-
*
|
|
120
|
-
* Computed signal. Возможные значения:
|
|
121
|
-
* - 'disabled' - форма disabled
|
|
122
|
-
* - 'pending' - асинхронная валидация в процессе
|
|
123
|
-
* - 'invalid' - форма невалидна
|
|
124
|
-
* - 'valid' - форма валидна
|
|
125
|
-
*/
|
|
126
|
-
readonly status: ReadonlySignal<FieldStatus>;
|
|
127
|
-
/**
|
|
128
|
-
* Форма в процессе отправки?
|
|
129
|
-
*
|
|
130
|
-
* Computed signal (обертка над _submitting для read-only доступа).
|
|
131
|
-
*/
|
|
132
|
-
readonly submitting: ReadonlySignal<boolean>;
|
|
133
|
-
/**
|
|
134
|
-
* Создать менеджер состояния
|
|
135
|
-
*
|
|
136
|
-
* @param fieldRegistry - реестр полей формы
|
|
137
|
-
*/
|
|
138
|
-
constructor(fieldRegistry: FieldRegistry<T>);
|
|
139
|
-
/**
|
|
140
|
-
* Установить form-level ошибки
|
|
141
|
-
*
|
|
142
|
-
* @param errors - массив ошибок валидации
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```typescript
|
|
146
|
-
* // Server-side ошибки
|
|
147
|
-
* stateManager.setFormErrors([
|
|
148
|
-
* { code: 'server_error', message: 'Пользователь с таким email уже существует' }
|
|
149
|
-
* ]);
|
|
150
|
-
* ```
|
|
151
|
-
*/
|
|
152
|
-
setFormErrors(errors: ValidationError[]): void;
|
|
153
|
-
/**
|
|
154
|
-
* Очистить form-level ошибки
|
|
155
|
-
*/
|
|
156
|
-
clearFormErrors(): void;
|
|
157
|
-
/**
|
|
158
|
-
* Получить form-level ошибки
|
|
159
|
-
*/
|
|
160
|
-
getFormErrors(): ValidationError[];
|
|
161
|
-
/**
|
|
162
|
-
* Установить флаг submitting
|
|
163
|
-
*
|
|
164
|
-
* @param value - true если форма отправляется, false если нет
|
|
165
|
-
*
|
|
166
|
-
* @example
|
|
167
|
-
* ```typescript
|
|
168
|
-
* stateManager.setSubmitting(true);
|
|
169
|
-
* await api.submitForm(form.getValue());
|
|
170
|
-
* stateManager.setSubmitting(false);
|
|
171
|
-
* ```
|
|
172
|
-
*/
|
|
173
|
-
setSubmitting(value: boolean): void;
|
|
174
|
-
/**
|
|
175
|
-
* Установить флаг disabled
|
|
176
|
-
*
|
|
177
|
-
* @param value - true если форма disabled, false если нет
|
|
178
|
-
*/
|
|
179
|
-
setDisabled(value: boolean): void;
|
|
180
|
-
/**
|
|
181
|
-
* Получить флаг disabled
|
|
182
|
-
*/
|
|
183
|
-
isDisabled(): boolean;
|
|
184
|
-
}
|