@reformer/core 1.1.0 → 2.0.0-beta.10
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.d.ts +6 -2
- package/dist/behaviors.js +27 -228
- package/dist/core/behavior/behavior-context.d.ts +32 -14
- 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 +37 -212
- 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 +36 -30
- 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/{validation → utils}/field-path.d.ts +23 -6
- 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 +10 -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 +10 -10
- 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 +67 -28
- 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 +328 -0
- package/dist/hooks/useArrayLength.d.ts +31 -0
- package/dist/hooks/useFormControl.d.ts +15 -39
- package/dist/hooks/useFormControlValue.d.ts +167 -0
- package/dist/hooks/useHiddenCondition.d.ts +25 -0
- package/dist/hooks/useSignalSubscription.d.ts +17 -0
- package/dist/index-D25LsbRm.js +73 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +3271 -8
- 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.d.ts +6 -2
- package/dist/validators.js +54 -296
- package/llms.txt +8887 -59
- package/package.json +87 -8
- 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.d.ts +0 -20
- 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.d.ts +0 -29
- 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.d.ts +0 -160
- 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.d.ts +0 -41
- 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.d.ts +0 -38
- 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/create-field-path-CdPF3lIK.js +0 -704
- package/dist/hooks/useFormControl.js +0 -298
- package/dist/node-factory-D7DOnSSN.js +0 -3200
|
@@ -1,17 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Аналог AbstractControl из Angular Forms
|
|
5
|
-
* Унифицирует работу с полями (FieldNode), группами (GroupNode) и массивами (ArrayNode)
|
|
6
|
-
*
|
|
7
|
-
* Использует Template Method паттерн для управления состоянием:
|
|
8
|
-
* - Публичные методы (markAsTouched, disable и т.д.) реализованы в базовом классе
|
|
9
|
-
* - Protected hooks (onMarkAsTouched, onDisable и т.д.) переопределяются в наследниках
|
|
10
|
-
*
|
|
11
|
-
* @group Nodes
|
|
12
|
-
*/
|
|
13
|
-
import { type ReadonlySignal, type Signal } from '@preact/signals-core';
|
|
14
|
-
import type { FieldStatus, ValidationError, ErrorFilterOptions } from '../types';
|
|
1
|
+
import { ReadonlySignal, Signal } from '@preact/signals-core';
|
|
2
|
+
import { FieldStatus, ValidationError, ErrorFilterOptions } from '../types';
|
|
15
3
|
/**
|
|
16
4
|
* Опции для setValue
|
|
17
5
|
* @group Nodes
|
|
@@ -23,17 +11,27 @@ export interface SetValueOptions {
|
|
|
23
11
|
onlySelf?: boolean;
|
|
24
12
|
}
|
|
25
13
|
/**
|
|
26
|
-
* Абстрактный базовый класс для всех узлов
|
|
14
|
+
* Абстрактный базовый класс для всех узлов формы.
|
|
27
15
|
*
|
|
28
|
-
* Все узлы (поля, группы, массивы) наследуют от этого класса
|
|
29
|
-
*
|
|
16
|
+
* Все узлы (поля, группы, массивы) наследуют от этого класса и реализуют
|
|
17
|
+
* единый интерфейс для работы с состоянием и валидацией.
|
|
30
18
|
*
|
|
31
19
|
* Template Method паттерн используется для управления состоянием:
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
20
|
+
* общие signals (`_touched`, `_dirty`, `_status`) живут в базовом классе,
|
|
21
|
+
* publi-методы (`markAsTouched`, `disable`, …) реализованы здесь, а protected
|
|
22
|
+
* hooks (`onMarkAsTouched`, `onDisable`, …) переопределяются в наследниках.
|
|
35
23
|
*
|
|
36
24
|
* @group Nodes
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // FormNode не используется напрямую — экземпляры приходят из getReformerForm.
|
|
29
|
+
* import { getReformerForm, FormNode } from '@reformer/core';
|
|
30
|
+
*
|
|
31
|
+
* const form = getReformerForm({ email: '' });
|
|
32
|
+
* form.email instanceof FormNode; // true
|
|
33
|
+
* form.email.markAsTouched();
|
|
34
|
+
* ```
|
|
37
35
|
*/
|
|
38
36
|
export declare abstract class FormNode<T> {
|
|
39
37
|
/**
|
|
@@ -1,21 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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';
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { FormNode, SetValueOptions } from './form-node';
|
|
3
|
+
import { ValidationError, FieldStatus, ValidationSchemaFn, ValidatorRegistration, FormSchema, GroupNodeConfig, FormValue } from '../types';
|
|
4
|
+
import { FormProxy } from '../types/form-proxy';
|
|
5
|
+
import { BehaviorSchemaFn } from '../behavior/types';
|
|
6
|
+
import { SubmitOptions, SubmitResult } from '../utils/form-submitter';
|
|
19
7
|
/**
|
|
20
8
|
* GroupNode - узел для группы полей
|
|
21
9
|
*
|
|
@@ -57,67 +45,47 @@ import { FieldRegistry } from './group-node/field-registry';
|
|
|
57
45
|
* ```
|
|
58
46
|
*/
|
|
59
47
|
export declare class GroupNode<T> extends FormNode<T> {
|
|
60
|
-
id: string
|
|
48
|
+
id: `${string}-${string}-${string}-${string}-${string}`;
|
|
61
49
|
/**
|
|
62
|
-
*
|
|
63
|
-
* Использует FieldRegistry для инкапсуляции логики управления коллекцией полей
|
|
50
|
+
* Коллекция полей формы (упрощённый Map вместо FieldRegistry)
|
|
64
51
|
*/
|
|
65
|
-
private
|
|
66
|
-
/**
|
|
67
|
-
* Строитель Proxy для типобезопасного доступа к полям
|
|
68
|
-
* Использует ProxyBuilder для создания Proxy с расширенной функциональностью
|
|
69
|
-
*/
|
|
70
|
-
private proxyBuilder;
|
|
71
|
-
/**
|
|
72
|
-
* Менеджер состояния формы
|
|
73
|
-
* Инкапсулирует всю логику создания и управления сигналами состояния
|
|
74
|
-
* Извлечен из GroupNode для соблюдения SRP
|
|
75
|
-
*/
|
|
76
|
-
private stateManager;
|
|
52
|
+
private readonly _fields;
|
|
77
53
|
/**
|
|
78
54
|
* Менеджер подписок для централизованного cleanup
|
|
79
|
-
* Использует SubscriptionManager вместо массива для управления подписками
|
|
80
55
|
*/
|
|
81
56
|
private disposers;
|
|
82
57
|
/**
|
|
83
58
|
* Ссылка на Proxy-инстанс для использования в BehaviorContext
|
|
84
|
-
* Устанавливается в конструкторе до применения behavior schema
|
|
85
59
|
*/
|
|
86
60
|
private _proxyInstance?;
|
|
87
61
|
/**
|
|
88
62
|
* Навигатор для работы с путями к полям
|
|
89
|
-
* Использует композицию вместо дублирования логики парсинга путей
|
|
90
63
|
*/
|
|
91
64
|
private readonly pathNavigator;
|
|
92
65
|
/**
|
|
93
66
|
* Фабрика для создания узлов формы
|
|
94
|
-
* Использует композицию для централизованного создания FieldNode/GroupNode/ArrayNode
|
|
95
67
|
*/
|
|
96
68
|
private readonly nodeFactory;
|
|
97
69
|
/**
|
|
98
70
|
* Реестр валидаторов для этой формы
|
|
99
|
-
*
|
|
100
|
-
* Обеспечивает полную изоляцию форм друг от друга
|
|
71
|
+
* Может быть инжектирован через config._validationRegistry для тестирования
|
|
101
72
|
*/
|
|
102
73
|
private readonly validationRegistry;
|
|
103
74
|
/**
|
|
104
75
|
* Реестр behaviors для этой формы
|
|
105
|
-
*
|
|
106
|
-
* Обеспечивает полную изоляцию форм друг от друга
|
|
76
|
+
* Может быть инжектирован через config._behaviorRegistry для тестирования
|
|
107
77
|
*/
|
|
108
78
|
private readonly behaviorRegistry;
|
|
109
79
|
/**
|
|
110
80
|
* Аппликатор для применения валидаторов к форме
|
|
111
|
-
* Извлечен из GroupNode для соблюдения SRP
|
|
112
|
-
* Использует композицию для управления процессом валидации
|
|
113
81
|
*/
|
|
114
82
|
private readonly validationApplicator;
|
|
115
|
-
/**
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
private readonly
|
|
83
|
+
/** Управление отправкой формы */
|
|
84
|
+
private readonly formSubmitter;
|
|
85
|
+
/** Флаг disabled состояния */
|
|
86
|
+
private readonly _disabled;
|
|
87
|
+
/** Form-level validation errors */
|
|
88
|
+
private readonly _formErrors;
|
|
121
89
|
readonly value: ReadonlySignal<T>;
|
|
122
90
|
readonly valid: ReadonlySignal<boolean>;
|
|
123
91
|
readonly invalid: ReadonlySignal<boolean>;
|
|
@@ -135,6 +103,11 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
135
103
|
* Создать GroupNode с полной конфигурацией (form, behavior, validation)
|
|
136
104
|
*/
|
|
137
105
|
constructor(config: GroupNodeConfig<T>);
|
|
106
|
+
/**
|
|
107
|
+
* Создать Proxy для типобезопасного доступа к полям
|
|
108
|
+
* @see buildFormProxy
|
|
109
|
+
*/
|
|
110
|
+
private buildProxy;
|
|
138
111
|
getValue(): T;
|
|
139
112
|
setValue(value: T, options?: SetValueOptions): void;
|
|
140
113
|
patchValue(value: Partial<T>): void;
|
|
@@ -158,50 +131,11 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
158
131
|
reset(value?: T): void;
|
|
159
132
|
/**
|
|
160
133
|
* Сбросить форму к исходным значениям (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
134
|
*/
|
|
186
135
|
resetToInitial(): void;
|
|
187
136
|
validate(): Promise<boolean>;
|
|
188
137
|
/**
|
|
189
138
|
* Установить 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
139
|
*/
|
|
206
140
|
setErrors(errors: ValidationError[]): void;
|
|
207
141
|
/**
|
|
@@ -210,29 +144,12 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
210
144
|
clearErrors(): void;
|
|
211
145
|
/**
|
|
212
146
|
* Получить поле по ключу
|
|
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
147
|
*/
|
|
227
148
|
getField<K extends keyof T>(key: K): FormNode<T[K]> | undefined;
|
|
228
149
|
/**
|
|
229
|
-
* Получить Map всех полей формы
|
|
230
|
-
*
|
|
231
|
-
* Используется в FieldPathNavigator для навигации по полям
|
|
232
|
-
*
|
|
233
|
-
* @returns Map полей формы
|
|
150
|
+
* Получить Map всех полей формы (для совместимости)
|
|
234
151
|
*/
|
|
235
|
-
get fields():
|
|
152
|
+
get fields(): Map<keyof T, FormNode<FormValue>>;
|
|
236
153
|
/**
|
|
237
154
|
* Получить Proxy-инстанс для прямого доступа к полям
|
|
238
155
|
*
|
|
@@ -259,52 +176,31 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
259
176
|
* console.log(proxy.email.value); // Прямой доступ к полю
|
|
260
177
|
* ```
|
|
261
178
|
*/
|
|
262
|
-
getProxy():
|
|
179
|
+
getProxy(): FormProxy<T>;
|
|
263
180
|
/**
|
|
264
181
|
* Получить все поля формы как итератор
|
|
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
182
|
*/
|
|
278
183
|
getAllFields(): IterableIterator<FormNode<FormValue>>;
|
|
279
|
-
/**
|
|
280
|
-
* Hook: вызывается после markAsTouched()
|
|
281
|
-
*
|
|
282
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как touched
|
|
283
|
-
*/
|
|
284
184
|
protected onMarkAsTouched(): void;
|
|
285
|
-
/**
|
|
286
|
-
* Hook: вызывается после markAsUntouched()
|
|
287
|
-
*
|
|
288
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как untouched
|
|
289
|
-
*/
|
|
290
185
|
protected onMarkAsUntouched(): void;
|
|
291
|
-
/**
|
|
292
|
-
* Hook: вызывается после markAsDirty()
|
|
293
|
-
*
|
|
294
|
-
* Для GroupNode: рекурсивно помечаем все дочерние поля как dirty
|
|
295
|
-
*/
|
|
296
186
|
protected onMarkAsDirty(): void;
|
|
187
|
+
protected onMarkAsPristine(): void;
|
|
297
188
|
/**
|
|
298
|
-
*
|
|
189
|
+
* Отправить форму
|
|
299
190
|
*
|
|
300
|
-
*
|
|
191
|
+
* @param onSubmit - Callback для отправки данных
|
|
192
|
+
* @param options - Опции submit (skipValidation, skipTouch)
|
|
193
|
+
* @returns Результат от onSubmit или null если валидация не пройдена
|
|
301
194
|
*/
|
|
302
|
-
|
|
195
|
+
submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
|
|
303
196
|
/**
|
|
304
|
-
* Отправить форму
|
|
305
|
-
*
|
|
197
|
+
* Отправить форму с расширенным результатом
|
|
198
|
+
*
|
|
199
|
+
* @param onSubmit - Callback для отправки данных
|
|
200
|
+
* @param options - Опции submit
|
|
201
|
+
* @returns Объект SubmitResult с данными, статусом и возможной ошибкой
|
|
306
202
|
*/
|
|
307
|
-
|
|
203
|
+
submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
|
|
308
204
|
/**
|
|
309
205
|
* Применить validation schema к форме
|
|
310
206
|
*
|
|
@@ -314,41 +210,7 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
314
210
|
applyValidationSchema(schemaFn: ValidationSchemaFn<T>): void;
|
|
315
211
|
/**
|
|
316
212
|
* Применить 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
213
|
* @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
214
|
*/
|
|
353
215
|
applyBehaviorSchema(schemaFn: BehaviorSchemaFn<T>): () => void;
|
|
354
216
|
/**
|
|
@@ -412,29 +274,6 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
412
274
|
private createNode;
|
|
413
275
|
/**
|
|
414
276
|
* Связывает два поля: при изменении 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
277
|
*/
|
|
439
278
|
linkFields<K1 extends keyof T, K2 extends keyof T>(sourceKey: K1, targetKey: K2, transform?: (value: T[K1]) => T[K2]): () => void;
|
|
440
279
|
/**
|
|
@@ -467,28 +306,14 @@ export declare class GroupNode<T> extends FormNode<T> {
|
|
|
467
306
|
watchField<K extends keyof T>(fieldPath: K extends string ? K : string, callback: (value: T[K]) => void | Promise<void>): () => void;
|
|
468
307
|
/**
|
|
469
308
|
* Hook: вызывается после disable()
|
|
470
|
-
*
|
|
471
|
-
* Для GroupNode: рекурсивно отключаем все дочерние поля
|
|
472
309
|
*/
|
|
473
310
|
protected onDisable(): void;
|
|
474
311
|
/**
|
|
475
312
|
* Hook: вызывается после enable()
|
|
476
|
-
*
|
|
477
|
-
* Для GroupNode: рекурсивно включаем все дочерние поля
|
|
478
313
|
*/
|
|
479
314
|
protected onEnable(): void;
|
|
480
315
|
/**
|
|
481
316
|
* Очистить все ресурсы узла
|
|
482
|
-
* Рекурсивно очищает все subscriptions и дочерние узлы
|
|
483
|
-
*
|
|
484
|
-
* @example
|
|
485
|
-
* ```typescript
|
|
486
|
-
* useEffect(() => {
|
|
487
|
-
* return () => {
|
|
488
|
-
* form.dispose();
|
|
489
|
-
* };
|
|
490
|
-
* }, []);
|
|
491
|
-
* ```
|
|
492
317
|
*/
|
|
493
318
|
dispose(): void;
|
|
494
319
|
}
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Автоматическое определение типов схемы на основе структуры данных:
|
|
5
|
-
* - `[{...}]` → массив форм
|
|
6
|
-
* - `{...}` → вложенная группа
|
|
7
|
-
* - `{value, component}` → конфигурация поля
|
|
8
|
-
*
|
|
9
|
-
* @group Types
|
|
10
|
-
*/
|
|
11
|
-
import type { ComponentType } from 'react';
|
|
12
|
-
import type { ValidatorFn, AsyncValidatorFn, FormFields, AnyFunction } from './index';
|
|
1
|
+
import { ComponentType } from 'react';
|
|
2
|
+
import { ValidatorFn, AsyncValidatorFn, FormFields, AnyFunction } from './index';
|
|
13
3
|
/**
|
|
14
4
|
* Конфигурация поля
|
|
15
5
|
* @group Types
|
|
@@ -1,28 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* - Behavior схемах (watchField, copyFrom, transformValue, etc.)
|
|
6
|
-
* - Validation схемах (validate, validateAsync, validateTree)
|
|
7
|
-
*
|
|
8
|
-
* @example
|
|
9
|
-
* ```typescript
|
|
10
|
-
* // Behavior
|
|
11
|
-
* watchField(path.country, (country, ctx) => {
|
|
12
|
-
* ctx.form.city.updateComponentProps({ options: cities });
|
|
13
|
-
* ctx.setFieldValue('city', null);
|
|
14
|
-
* });
|
|
15
|
-
*
|
|
16
|
-
* // Validation
|
|
17
|
-
* validate(path.email, (value, ctx) => {
|
|
18
|
-
* if (!value) return { code: 'required', message: 'Required' };
|
|
19
|
-
* const confirm = ctx.form.confirmEmail.value.value;
|
|
20
|
-
* if (value !== confirm) return { code: 'mismatch', message: 'Emails must match' };
|
|
21
|
-
* return null;
|
|
22
|
-
* });
|
|
23
|
-
* ```
|
|
24
|
-
*/
|
|
25
|
-
import type { GroupNodeWithControls } from './group-node-proxy';
|
|
1
|
+
import { FormProxy } from './form-proxy';
|
|
2
|
+
import { FieldPathNode } from './field-path';
|
|
3
|
+
import { FormNode } from '../nodes/form-node';
|
|
4
|
+
import { FormValue } from './index';
|
|
26
5
|
/**
|
|
27
6
|
* Единый контекст для работы с формой
|
|
28
7
|
*
|
|
@@ -61,23 +40,50 @@ export interface FormContext<TForm> {
|
|
|
61
40
|
* ctx.form.items.clear()
|
|
62
41
|
* ```
|
|
63
42
|
*/
|
|
64
|
-
readonly form:
|
|
43
|
+
readonly form: FormProxy<TForm>;
|
|
65
44
|
/**
|
|
66
|
-
* Безопасно установить значение поля по строковому пути
|
|
45
|
+
* Безопасно установить значение поля по строковому пути или FieldPath
|
|
67
46
|
*
|
|
68
47
|
* Автоматически использует `emitEvent: false` для предотвращения
|
|
69
48
|
* бесконечных циклов в behavior схемах.
|
|
70
49
|
*
|
|
71
|
-
* @param path - Путь к полю (
|
|
50
|
+
* @param path - Путь к полю (строка или FieldPath)
|
|
72
51
|
* @param value - Новое значение
|
|
73
52
|
*
|
|
74
53
|
* @example
|
|
75
54
|
* ```typescript
|
|
76
|
-
* // Сброс города при смене страны
|
|
55
|
+
* // Сброс города при смене страны (строковый путь)
|
|
77
56
|
* watchField(path.country, (country, ctx) => {
|
|
78
57
|
* ctx.setFieldValue('city', null);
|
|
79
58
|
* });
|
|
59
|
+
*
|
|
60
|
+
* // Использование FieldPath напрямую (более типобезопасно)
|
|
61
|
+
* watchField(path.country, (country, ctx) => {
|
|
62
|
+
* ctx.setFieldValue(path.city, null);
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
setFieldValue(path: string | FieldPathNode<TForm, unknown>, value: unknown): void;
|
|
67
|
+
/**
|
|
68
|
+
* Получить поле формы по строковому пути
|
|
69
|
+
*
|
|
70
|
+
* Используется для динамического доступа к вложенным полям,
|
|
71
|
+
* особенно в модульных behavior схемах, применяемых через apply().
|
|
72
|
+
*
|
|
73
|
+
* @param path - Строковый путь к полю (например "address.city")
|
|
74
|
+
* @returns FormNode или undefined если поле не найдено
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* // В модульной behavior схеме, применяемой к вложенному полю:
|
|
79
|
+
* // apply([path.registrationAddress, path.residenceAddress], addressBehavior)
|
|
80
|
+
*
|
|
81
|
+
* watchField(path.region, (region, ctx) => {
|
|
82
|
+
* // path.city.__path = "registrationAddress.city" или "residenceAddress.city"
|
|
83
|
+
* const cityField = ctx.getFieldByPath(path.city.__path);
|
|
84
|
+
* cityField?.updateComponentProps({ options: cities });
|
|
85
|
+
* });
|
|
80
86
|
* ```
|
|
81
87
|
*/
|
|
82
|
-
|
|
88
|
+
getFieldByPath(path: string): FormNode<FormValue> | undefined;
|
|
83
89
|
}
|
|
@@ -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 {};
|