@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,11 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Трансформация значений полей
|
|
3
|
-
*
|
|
4
|
-
* @group Behaviors
|
|
5
|
-
* @category Behavior Rules
|
|
6
|
-
* @module behaviors/transformValue
|
|
7
|
-
*/
|
|
8
|
-
import type { FieldPathNode, FormFields, FormValue } from '../../types';
|
|
1
|
+
import { FieldPathNode, FormFields, FormValue } from '../../types';
|
|
9
2
|
/**
|
|
10
3
|
* Опции для transformValue
|
|
11
4
|
*
|
|
@@ -26,34 +19,52 @@ export interface TransformValueOptions {
|
|
|
26
19
|
* @category Behavior Rules
|
|
27
20
|
*
|
|
28
21
|
* @param field - Поле для трансформации
|
|
29
|
-
* @param transformer - Функция трансформации
|
|
30
|
-
* @param options - Опции
|
|
22
|
+
* @param transformer - Функция трансформации (ОБЯЗАТЕЛЬНО идемпотентная: f(f(x)) === f(x))
|
|
23
|
+
* @param options - Опции (`onUserChangeOnly`, `emitEvent`, `debounce`)
|
|
24
|
+
*
|
|
25
|
+
* @example Базовая нормализация — uppercase + trim email
|
|
26
|
+
* ```typescript
|
|
27
|
+
* import { transformValue, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
28
|
+
*
|
|
29
|
+
* interface RegistrationForm {
|
|
30
|
+
* promoCode: string;
|
|
31
|
+
* email: string;
|
|
32
|
+
* }
|
|
33
|
+
*
|
|
34
|
+
* export const registrationBehavior: BehaviorSchemaFn<RegistrationForm> = (path) => {
|
|
35
|
+
* // Идемпотентно: toUpperCase(toUpperCase(x)) === toUpperCase(x) ✓
|
|
36
|
+
* transformValue(path.promoCode, (value) => (value ?? '').toUpperCase());
|
|
37
|
+
* transformValue(path.email, (value) => (value ?? '').trim().toLowerCase());
|
|
38
|
+
* };
|
|
39
|
+
* ```
|
|
31
40
|
*
|
|
32
|
-
* @example
|
|
41
|
+
* @example `onUserChangeOnly` + idempotent guard для не-тривиальных форматов
|
|
33
42
|
* ```typescript
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* //
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
43
|
+
* import { transformValue, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
44
|
+
*
|
|
45
|
+
* interface ProfileForm {
|
|
46
|
+
* inn: string; // ИНН — только цифры
|
|
47
|
+
* prefixedCode: string; // должен иметь префикс "ID-"
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* export const profileBehavior: BehaviorSchemaFn<ProfileForm> = (path) => {
|
|
51
|
+
* // Цифры из ИНН: трансформер идемпотентный естественно
|
|
52
|
+
* transformValue(path.inn, (v) => (v ?? '').replace(/\D/g, ''));
|
|
53
|
+
*
|
|
54
|
+
* // Префикс — ВАЖНО guard «уже преобразовано», иначе бесконечный цикл
|
|
55
|
+
* // f("ID-123") должно === "ID-123", а не "ID-ID-123"
|
|
56
|
+
* transformValue(
|
|
57
|
+
* path.prefixedCode,
|
|
58
|
+
* (v) => (v?.startsWith('ID-') ? v : `ID-${v ?? ''}`),
|
|
59
|
+
* {
|
|
60
|
+
* onUserChangeOnly: true, // не трогаем значение из patchValue/preload
|
|
61
|
+
* debounce: 200,
|
|
62
|
+
* },
|
|
63
|
+
* );
|
|
55
64
|
* };
|
|
56
65
|
* ```
|
|
66
|
+
*
|
|
67
|
+
* @see [docs/llms/26-transform-value.md](../../../../docs/llms/26-transform-value.md)
|
|
57
68
|
*/
|
|
58
69
|
export declare function transformValue<TForm extends FormFields, TValue extends FormValue = FormValue>(field: FieldPathNode<TForm, TValue>, transformer: (value: TValue) => TValue, options?: TransformValueOptions & {
|
|
59
70
|
debounce?: number;
|
|
@@ -64,18 +75,42 @@ export declare function transformValue<TForm extends FormFields, TValue extends
|
|
|
64
75
|
* @group Behaviors
|
|
65
76
|
* @category Behavior Rules
|
|
66
77
|
*
|
|
67
|
-
* @
|
|
78
|
+
* @param transformer - Идемпотентная функция преобразования значения
|
|
79
|
+
* @param defaultOptions - Опции, применяемые ко всем вызовам созданного трансформера
|
|
80
|
+
*
|
|
81
|
+
* @example Доменно-специфичные трансформеры (банковский счёт, СНИЛС)
|
|
68
82
|
* ```typescript
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* const
|
|
73
|
-
*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
83
|
+
* import { createTransformer, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
84
|
+
*
|
|
85
|
+
* // Сохраняем только цифры и форматируем СНИЛС: 000-000-000 00
|
|
86
|
+
* const formatSnils = createTransformer<string>((v) => {
|
|
87
|
+
* const d = (v ?? '').replace(/\D/g, '').slice(0, 11);
|
|
88
|
+
* if (d.length < 9) return d;
|
|
89
|
+
* return `${d.slice(0, 3)}-${d.slice(3, 6)}-${d.slice(6, 9)}${d.length > 9 ? ' ' + d.slice(9) : ''}`;
|
|
90
|
+
* });
|
|
91
|
+
*
|
|
92
|
+
* interface ProfileForm { snils: string }
|
|
93
|
+
*
|
|
94
|
+
* export const profileBehavior: BehaviorSchemaFn<ProfileForm> = (path) => {
|
|
95
|
+
* formatSnils(path.snils, { debounce: 100 });
|
|
96
|
+
* };
|
|
97
|
+
* ```
|
|
98
|
+
*
|
|
99
|
+
* @example С `defaultOptions` — единые настройки на серию полей
|
|
100
|
+
* ```typescript
|
|
101
|
+
* import { createTransformer, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
102
|
+
*
|
|
103
|
+
* // Все коды должны быть uppercase, но только после правки пользователем
|
|
104
|
+
* const upperOnUserEdit = createTransformer<string>(
|
|
105
|
+
* (v) => (v ?? '').toUpperCase(),
|
|
106
|
+
* { onUserChangeOnly: true, debounce: 100 },
|
|
107
|
+
* );
|
|
108
|
+
*
|
|
109
|
+
* interface PromoForm { promoCode: string; partnerCode: string }
|
|
110
|
+
*
|
|
111
|
+
* export const promoBehavior: BehaviorSchemaFn<PromoForm> = (path) => {
|
|
112
|
+
* upperOnUserEdit(path.promoCode);
|
|
113
|
+
* upperOnUserEdit(path.partnerCode);
|
|
79
114
|
* };
|
|
80
115
|
* ```
|
|
81
116
|
*/
|
|
@@ -83,10 +118,47 @@ export declare function createTransformer<TValue extends FormValue = FormValue>(
|
|
|
83
118
|
debounce?: number;
|
|
84
119
|
}) => void;
|
|
85
120
|
/**
|
|
86
|
-
* Готовые трансформеры для частых
|
|
121
|
+
* Готовые трансформеры для частых случаев. Все идемпотентны и безопасны для повторного применения.
|
|
87
122
|
*
|
|
88
123
|
* @group Behaviors
|
|
89
124
|
* @category Behavior Rules
|
|
125
|
+
*
|
|
126
|
+
* @example Готовые трансформеры в схеме формы
|
|
127
|
+
* ```typescript
|
|
128
|
+
* import { transformers, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
129
|
+
*
|
|
130
|
+
* interface RegistrationForm {
|
|
131
|
+
* username: string;
|
|
132
|
+
* email: string;
|
|
133
|
+
* promoCode: string;
|
|
134
|
+
* inn: string;
|
|
135
|
+
* amount: number;
|
|
136
|
+
* }
|
|
137
|
+
*
|
|
138
|
+
* export const behavior: BehaviorSchemaFn<RegistrationForm> = (path) => {
|
|
139
|
+
* transformers.trim(path.username);
|
|
140
|
+
* transformers.toLowerCase(path.email);
|
|
141
|
+
* transformers.toUpperCase(path.promoCode);
|
|
142
|
+
* transformers.digitsOnly(path.inn);
|
|
143
|
+
* transformers.roundTo2(path.amount);
|
|
144
|
+
* };
|
|
145
|
+
* ```
|
|
146
|
+
*
|
|
147
|
+
* @example Композиция готовых трансформеров через createTransformer
|
|
148
|
+
* ```typescript
|
|
149
|
+
* import { createTransformer, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
150
|
+
*
|
|
151
|
+
* // trim + lowercase в одном трансформере (применяется как одна операция)
|
|
152
|
+
* const normalizeEmail = createTransformer<string>(
|
|
153
|
+
* (v) => (v ?? '').trim().toLowerCase(),
|
|
154
|
+
* );
|
|
155
|
+
*
|
|
156
|
+
* interface ContactForm { email: string }
|
|
157
|
+
*
|
|
158
|
+
* export const contactBehavior: BehaviorSchemaFn<ContactForm> = (path) => {
|
|
159
|
+
* normalizeEmail(path.email);
|
|
160
|
+
* };
|
|
161
|
+
* ```
|
|
90
162
|
*/
|
|
91
163
|
export declare const transformers: {
|
|
92
164
|
/** Перевести в верхний регистр */
|
|
@@ -1,12 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* @group Behaviors
|
|
5
|
-
* @category Behavior Rules
|
|
6
|
-
* @module behaviors/watchField
|
|
7
|
-
*/
|
|
8
|
-
import type { FieldPathNode } from '../../types';
|
|
9
|
-
import type { BehaviorContext, WatchFieldOptions } from '../types';
|
|
1
|
+
import { FieldPathNode } from '../../types';
|
|
2
|
+
import { BehaviorContext, WatchFieldOptions } from '../types';
|
|
10
3
|
/**
|
|
11
4
|
* Выполняет callback при изменении поля
|
|
12
5
|
*
|
|
@@ -15,21 +8,73 @@ import type { BehaviorContext, WatchFieldOptions } from '../types';
|
|
|
15
8
|
*
|
|
16
9
|
* @param field - Поле для отслеживания
|
|
17
10
|
* @param callback - Функция обратного вызова
|
|
18
|
-
* @param options - Опции
|
|
11
|
+
* @param options - Опции (`debounce`, `immediate`)
|
|
12
|
+
*
|
|
13
|
+
* @example Async loader with try/catch + guard + debounce
|
|
14
|
+
* ```typescript
|
|
15
|
+
* import { watchField, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
16
|
+
*
|
|
17
|
+
* interface AddressForm { region: string; city: string }
|
|
19
18
|
*
|
|
20
|
-
*
|
|
19
|
+
* export const addressBehavior: BehaviorSchemaFn<AddressForm> = (path) => {
|
|
20
|
+
* watchField(
|
|
21
|
+
* path.region,
|
|
22
|
+
* async (region, ctx) => {
|
|
23
|
+
* if (!region) {
|
|
24
|
+
* ctx.form.city.updateComponentProps({ options: [] });
|
|
25
|
+
* return; // guard: пустое значение не триггерит fetch
|
|
26
|
+
* }
|
|
27
|
+
* try {
|
|
28
|
+
* const { data: cities } = await fetchCities(region);
|
|
29
|
+
* ctx.form.city.updateComponentProps({ options: cities });
|
|
30
|
+
* } catch (error) {
|
|
31
|
+
* console.error('[addressBehavior] failed to load cities:', error);
|
|
32
|
+
* ctx.form.city.updateComponentProps({ options: [] });
|
|
33
|
+
* }
|
|
34
|
+
* },
|
|
35
|
+
* { immediate: false, debounce: 300 }, // обязательные опции для async
|
|
36
|
+
* );
|
|
37
|
+
* };
|
|
38
|
+
* ```
|
|
39
|
+
*
|
|
40
|
+
* @example Sync handler с консолидацией нескольких зависимостей в один watcher
|
|
21
41
|
* ```typescript
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
42
|
+
* import { watchField, type BehaviorSchemaFn } from '@reformer/core/behaviors';
|
|
43
|
+
*
|
|
44
|
+
* interface InsuranceForm {
|
|
45
|
+
* insuranceType: 'casco' | 'osago' | 'property' | '';
|
|
46
|
+
* vehicleVin: string;
|
|
47
|
+
* propertyType: string;
|
|
48
|
+
* }
|
|
49
|
+
*
|
|
50
|
+
* export const insuranceBehavior: BehaviorSchemaFn<InsuranceForm> = (path) => {
|
|
51
|
+
* // ОДИН watchField на trigger-поле — несколько watcher'ов на одно поле = "Cycle detected"
|
|
52
|
+
* watchField(
|
|
53
|
+
* path.insuranceType,
|
|
54
|
+
* (type, ctx) => {
|
|
55
|
+
* const isVehicle = type === 'casco' || type === 'osago';
|
|
56
|
+
* const isProperty = type === 'property';
|
|
57
|
+
*
|
|
58
|
+
* // Guard — ставим только если состояние реально меняется
|
|
59
|
+
* if (isVehicle) {
|
|
60
|
+
* if (ctx.form.vehicleVin.disabled.value) ctx.form.vehicleVin.enable();
|
|
61
|
+
* } else {
|
|
62
|
+
* if (!ctx.form.vehicleVin.disabled.value) ctx.form.vehicleVin.disable();
|
|
63
|
+
* if (ctx.form.vehicleVin.getValue() !== '') ctx.form.vehicleVin.setValue('');
|
|
64
|
+
* }
|
|
65
|
+
*
|
|
66
|
+
* if (isProperty) {
|
|
67
|
+
* if (ctx.form.propertyType.disabled.value) ctx.form.propertyType.enable();
|
|
68
|
+
* } else {
|
|
69
|
+
* if (!ctx.form.propertyType.disabled.value) ctx.form.propertyType.disable();
|
|
70
|
+
* if (ctx.form.propertyType.getValue() !== '') ctx.form.propertyType.setValue('');
|
|
71
|
+
* }
|
|
72
|
+
* },
|
|
73
|
+
* { immediate: false }, // CRITICAL: предотвращает запуск во время инициализации
|
|
74
|
+
* );
|
|
32
75
|
* };
|
|
33
76
|
* ```
|
|
77
|
+
*
|
|
78
|
+
* @see [docs/llms/22-cycle-detection.md](../../../../docs/llms/22-cycle-detection.md)
|
|
34
79
|
*/
|
|
35
80
|
export declare function watchField<TForm, TField>(field: FieldPathNode<TForm, TField>, callback: (value: TField, ctx: BehaviorContext<TForm>) => void | Promise<void>, options?: WatchFieldOptions): void;
|
|
@@ -1,15 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
* Предоставляет функции для переиспользования behavior схем:
|
|
5
|
-
* - toBehaviorFieldPath: преобразование FieldPath во вложенный путь
|
|
6
|
-
* - apply: применение схемы к полям
|
|
7
|
-
* - applyWhen: условное применение схемы
|
|
8
|
-
*
|
|
9
|
-
* Аналог toFieldPath и applyWhen из validation API.
|
|
10
|
-
*/
|
|
11
|
-
import type { FieldPath, FieldPathNode, FormFields, FormValue } from '../types';
|
|
12
|
-
import type { BehaviorSchemaFn } from './types';
|
|
1
|
+
import { FieldPath, FieldPathNode, FormFields, FormValue } from '../types';
|
|
2
|
+
import { BehaviorSchemaFn } from './types';
|
|
13
3
|
/**
|
|
14
4
|
* Преобразовать FieldPath во вложенный путь для композиции behavior схем
|
|
15
5
|
*
|
|
@@ -9,4 +9,3 @@ export * from './behaviors';
|
|
|
9
9
|
export { apply, applyWhen, toBehaviorFieldPath } from './compose-behavior';
|
|
10
10
|
export { BehaviorRegistry } from './behavior-registry';
|
|
11
11
|
export { BehaviorContextImpl } from './behavior-context';
|
|
12
|
-
export { createFieldPath } from './create-field-path';
|
|
@@ -1,12 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Типы и интерфейсы для Behavior Schema API
|
|
3
|
-
*
|
|
4
|
-
* @group Behaviors
|
|
5
|
-
* @category Behavior Types
|
|
6
|
-
*/
|
|
7
1
|
import { GroupNode } from '../nodes/group-node';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
2
|
+
import { FieldPath } from '../types/field-path';
|
|
3
|
+
import { FormContext } from '../types/form-context';
|
|
10
4
|
/**
|
|
11
5
|
* Тип функции behavior схемы
|
|
12
6
|
* Принимает FieldPath и описывает поведение формы
|
|
@@ -1,40 +1,17 @@
|
|
|
1
|
+
import { FormNode } from '../nodes/form-node';
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Фабрика для создания узлов формы.
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
* Используется
|
|
6
|
-
*
|
|
7
|
-
* Паттерн Factory Method упрощает создание узлов и делает код более читаемым:
|
|
8
|
-
* - Вместо if-else в GroupNode/ArrayNode
|
|
9
|
-
* - Единая точка для создания узлов
|
|
10
|
-
* - Легко добавлять новые типы узлов
|
|
5
|
+
* Определяет тип конфига и создаёт соответствующий узел (FieldNode, GroupNode, ArrayNode).
|
|
6
|
+
* Используется внутри `getReformerForm`/`group`/`array` — явно вызывать обычно не нужно.
|
|
11
7
|
*
|
|
12
8
|
* @example
|
|
13
9
|
* ```typescript
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* // Создание FieldNode
|
|
17
|
-
* const field = factory.createNode({ value: '', component: Input });
|
|
18
|
-
*
|
|
19
|
-
* // Создание GroupNode
|
|
20
|
-
* const group = factory.createNode({
|
|
21
|
-
* email: { value: '', component: Input },
|
|
22
|
-
* password: { value: '', component: Input }
|
|
23
|
-
* });
|
|
10
|
+
* import { NodeFactory } from '@reformer/core';
|
|
24
11
|
*
|
|
25
|
-
* //
|
|
26
|
-
* const array = factory.createNode({
|
|
27
|
-
* schema: { title: { value: '', component: Input } },
|
|
28
|
-
* initialItems: []
|
|
29
|
-
* });
|
|
12
|
+
* const node = NodeFactory.create({ value: '' }); // → FieldNode<string>
|
|
30
13
|
* ```
|
|
31
14
|
*/
|
|
32
|
-
import type { FormNode } from '../nodes/form-node';
|
|
33
|
-
/**
|
|
34
|
-
* Фабрика для создания узлов формы
|
|
35
|
-
*
|
|
36
|
-
* Определяет тип конфига и создает соответствующий узел (FieldNode, GroupNode, ArrayNode)
|
|
37
|
-
*/
|
|
38
15
|
export declare class NodeFactory {
|
|
39
16
|
/**
|
|
40
17
|
* Создает узел формы на основе конфигурации
|
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
* - Валидации всех элементов
|
|
7
|
-
* - Реактивного состояния через signals
|
|
8
|
-
*
|
|
9
|
-
* @group Nodes
|
|
10
|
-
*/
|
|
11
|
-
import type { ReadonlySignal } from '@preact/signals-core';
|
|
12
|
-
import { FormNode, type SetValueOptions } from './form-node';
|
|
13
|
-
import type { FieldStatus, ValidationError, FormFields } from '../types';
|
|
14
|
-
import type { FormSchema } from '../types/deep-schema';
|
|
15
|
-
import type { GroupNodeWithControls } from '../types/group-node-proxy';
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { FormNode, SetValueOptions } from './form-node';
|
|
3
|
+
import { FieldStatus, ValidationError, FormFields } from '../types';
|
|
4
|
+
import { FormSchema } from '../types/deep-schema';
|
|
5
|
+
import { FormProxy } from '../types/form-proxy';
|
|
16
6
|
/**
|
|
17
7
|
* ArrayNode - массив форм с реактивным состоянием
|
|
18
8
|
*
|
|
@@ -39,6 +29,8 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
39
29
|
* Использует SubscriptionManager вместо массива для управления подписками
|
|
40
30
|
*/
|
|
41
31
|
private disposers;
|
|
32
|
+
/** Array-level validation errors (e.g., "минимум 1 элемент") */
|
|
33
|
+
private readonly _arrayErrors;
|
|
42
34
|
private validationSchemaFn?;
|
|
43
35
|
private behaviorSchemaFn?;
|
|
44
36
|
readonly value: ReadonlySignal<T[]>;
|
|
@@ -59,6 +51,9 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
59
51
|
/**
|
|
60
52
|
* Удалить элемент по индексу
|
|
61
53
|
* @param index - Индекс элемента для удаления
|
|
54
|
+
*
|
|
55
|
+
* @remarks
|
|
56
|
+
* Вызывает dispose() на удаляемом элементе для очистки подписок
|
|
62
57
|
*/
|
|
63
58
|
removeAt(index: number): void;
|
|
64
59
|
/**
|
|
@@ -69,14 +64,17 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
69
64
|
insert(index: number, initialValue?: Partial<T>): void;
|
|
70
65
|
/**
|
|
71
66
|
* Удалить все элементы массива
|
|
67
|
+
*
|
|
68
|
+
* @remarks
|
|
69
|
+
* Вызывает dispose() на всех элементах для очистки подписок
|
|
72
70
|
*/
|
|
73
71
|
clear(): void;
|
|
74
72
|
/**
|
|
75
73
|
* Получить элемент по индексу
|
|
76
74
|
* @param index - Индекс элемента
|
|
77
|
-
* @returns Типизированный GroupNode или undefined если индекс вне границ
|
|
75
|
+
* @returns Типизированный GroupNode proxy или undefined если индекс вне границ
|
|
78
76
|
*/
|
|
79
|
-
at(index: number):
|
|
77
|
+
at(index: number): FormProxy<T> | undefined;
|
|
80
78
|
getValue(): T[];
|
|
81
79
|
setValue(values: T[], options?: SetValueOptions): void;
|
|
82
80
|
patchValue(values: (T | undefined)[]): void;
|
|
@@ -128,7 +126,29 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
128
126
|
*/
|
|
129
127
|
resetToInitial(): void;
|
|
130
128
|
validate(): Promise<boolean>;
|
|
131
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Установить array-level validation errors
|
|
131
|
+
*
|
|
132
|
+
* @param errors - Массив ошибок валидации уровня массива
|
|
133
|
+
*
|
|
134
|
+
* @example
|
|
135
|
+
* ```typescript
|
|
136
|
+
* arrayNode.setErrors([{
|
|
137
|
+
* code: 'minItems',
|
|
138
|
+
* message: 'Минимум 1 элемент обязателен',
|
|
139
|
+
* }]);
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
setErrors(errors: ValidationError[]): void;
|
|
143
|
+
/**
|
|
144
|
+
* Очистить все errors (array-level + item-level)
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* arrayNode.clearErrors();
|
|
149
|
+
* console.log(arrayNode.errors.value); // []
|
|
150
|
+
* ```
|
|
151
|
+
*/
|
|
132
152
|
clearErrors(): void;
|
|
133
153
|
/**
|
|
134
154
|
* Hook: вызывается после markAsTouched()
|
|
@@ -156,15 +176,15 @@ export declare class ArrayNode<T extends FormFields> extends FormNode<T[]> {
|
|
|
156
176
|
protected onMarkAsPristine(): void;
|
|
157
177
|
/**
|
|
158
178
|
* Итерировать по элементам массива
|
|
159
|
-
* @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode
|
|
179
|
+
* @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
|
|
160
180
|
*/
|
|
161
|
-
forEach(callback: (item:
|
|
181
|
+
forEach(callback: (item: FormProxy<T>, index: number) => void): void;
|
|
162
182
|
/**
|
|
163
183
|
* Маппинг элементов массива
|
|
164
|
-
* @param callback - Функция преобразования с типизированным GroupNode
|
|
184
|
+
* @param callback - Функция преобразования с типизированным GroupNode proxy
|
|
165
185
|
* @returns Новый массив результатов
|
|
166
186
|
*/
|
|
167
|
-
map<R>(callback: (item:
|
|
187
|
+
map<R>(callback: (item: FormProxy<T>, index: number) => R): R[];
|
|
168
188
|
/**
|
|
169
189
|
* Создать новый элемент массива на основе схемы
|
|
170
190
|
* @param initialValue - Начальные значения
|
|
@@ -1,15 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* Представляет одно поле формы с валидацией и состоянием
|
|
5
|
-
* Наследует от FormNode и реализует все его абстрактные методы
|
|
6
|
-
*
|
|
7
|
-
* @group Nodes
|
|
8
|
-
*/
|
|
9
|
-
import type { ReadonlySignal } from '@preact/signals-core';
|
|
10
|
-
import { FormNode } from './form-node';
|
|
11
|
-
import type { SetValueOptions } from './form-node';
|
|
12
|
-
import type { FieldConfig, ValidationError } from '../types';
|
|
1
|
+
import { ReadonlySignal } from '@preact/signals-core';
|
|
2
|
+
import { FormNode, SetValueOptions } from './form-node';
|
|
3
|
+
import { FieldConfig, FieldStatus, ValidationError } from '../types';
|
|
13
4
|
/**
|
|
14
5
|
* FieldNode - узел для отдельного поля формы
|
|
15
6
|
*
|
|
@@ -31,12 +22,18 @@ import type { FieldConfig, ValidationError } from '../types';
|
|
|
31
22
|
export declare class FieldNode<T> extends FormNode<T> {
|
|
32
23
|
private _value;
|
|
33
24
|
private _errors;
|
|
34
|
-
private _pending;
|
|
35
25
|
private _componentProps;
|
|
26
|
+
/**
|
|
27
|
+
* State machine для управления статусом поля
|
|
28
|
+
* Централизует логику переходов между valid/invalid/pending/disabled
|
|
29
|
+
*/
|
|
30
|
+
private readonly statusMachine;
|
|
36
31
|
readonly value: ReadonlySignal<T>;
|
|
37
32
|
readonly valid: ReadonlySignal<boolean>;
|
|
38
33
|
readonly invalid: ReadonlySignal<boolean>;
|
|
39
34
|
readonly pending: ReadonlySignal<boolean>;
|
|
35
|
+
readonly status: ReadonlySignal<FieldStatus>;
|
|
36
|
+
readonly disabled: ReadonlySignal<boolean>;
|
|
40
37
|
readonly errors: ReadonlySignal<ValidationError[]>;
|
|
41
38
|
readonly componentProps: ReadonlySignal<Record<string, any>>;
|
|
42
39
|
/**
|
|
@@ -48,10 +45,14 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
48
45
|
private asyncValidators;
|
|
49
46
|
private updateOn;
|
|
50
47
|
private initialValue;
|
|
51
|
-
private
|
|
48
|
+
private currentAbortController?;
|
|
52
49
|
private debounceMs;
|
|
53
50
|
private validateDebounceTimer?;
|
|
54
|
-
|
|
51
|
+
/**
|
|
52
|
+
* Pending debounced validation state
|
|
53
|
+
* Contains resolve function and AbortController for cancellation
|
|
54
|
+
*/
|
|
55
|
+
private pendingValidation?;
|
|
55
56
|
/**
|
|
56
57
|
* Менеджер подписок для централизованного cleanup
|
|
57
58
|
* Использует SubscriptionManager вместо массива для управления подписками
|
|
@@ -112,13 +113,23 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
112
113
|
* ```
|
|
113
114
|
*/
|
|
114
115
|
resetToInitial(): void;
|
|
116
|
+
/**
|
|
117
|
+
* Cancel any pending validation (debounced or running)
|
|
118
|
+
* @private
|
|
119
|
+
* @remarks
|
|
120
|
+
* Centralizes all cancellation logic:
|
|
121
|
+
* - Aborts pending debounced validation and resolves its promise
|
|
122
|
+
* - Clears debounce timer
|
|
123
|
+
* - Aborts currently running async validation
|
|
124
|
+
*/
|
|
125
|
+
private cancelPendingValidation;
|
|
115
126
|
/**
|
|
116
127
|
* Запустить валидацию поля
|
|
117
128
|
* @param options - опции валидации
|
|
118
129
|
* @returns `Promise<boolean>` - true если поле валидно
|
|
119
130
|
*
|
|
120
131
|
* @remarks
|
|
121
|
-
* Метод защищен от race conditions через
|
|
132
|
+
* Метод защищен от race conditions через AbortController.
|
|
122
133
|
* При быстром вводе только последняя валидация применяет результаты.
|
|
123
134
|
*
|
|
124
135
|
* @example
|
|
@@ -136,13 +147,12 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
136
147
|
/**
|
|
137
148
|
* Немедленная валидация без debounce
|
|
138
149
|
* @private
|
|
150
|
+
* @param providedController - AbortController from debounced validate()
|
|
139
151
|
* @remarks
|
|
140
|
-
* Защищена от race conditions:
|
|
141
|
-
* -
|
|
142
|
-
* -
|
|
143
|
-
* -
|
|
144
|
-
* - Проверка перед обработкой async результатов
|
|
145
|
-
* - Проверка перед очисткой errors
|
|
152
|
+
* Защищена от race conditions через AbortController:
|
|
153
|
+
* - Отменяет предыдущую валидацию при запуске новой (if no controller provided)
|
|
154
|
+
* - Передаёт AbortSignal в async валидаторы для отмены операций (например, fetch)
|
|
155
|
+
* - Проверяет signal.aborted в ключевых точках
|
|
146
156
|
*/
|
|
147
157
|
private validateImmediate;
|
|
148
158
|
setErrors(errors: ValidationError[]): void;
|
|
@@ -156,13 +166,13 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
156
166
|
/**
|
|
157
167
|
* Hook: вызывается после disable()
|
|
158
168
|
*
|
|
159
|
-
* Для FieldNode: очищаем ошибки
|
|
169
|
+
* Для FieldNode: синхронизируем statusMachine и очищаем ошибки
|
|
160
170
|
*/
|
|
161
171
|
protected onDisable(): void;
|
|
162
172
|
/**
|
|
163
173
|
* Hook: вызывается после enable()
|
|
164
174
|
*
|
|
165
|
-
* Для FieldNode: запускаем валидацию
|
|
175
|
+
* Для FieldNode: синхронизируем statusMachine и запускаем валидацию
|
|
166
176
|
*/
|
|
167
177
|
protected onEnable(): void;
|
|
168
178
|
/**
|
|
@@ -215,20 +225,28 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
215
225
|
* Подписка на изменения значения поля
|
|
216
226
|
* Автоматически отслеживает изменения через @preact/signals effect
|
|
217
227
|
*
|
|
218
|
-
* @param callback - Функция, вызываемая при изменении
|
|
228
|
+
* @param callback - Функция, вызываемая при изменении значения.
|
|
229
|
+
* Для async операций передается AbortSignal во втором параметре.
|
|
219
230
|
* @returns Функция отписки для cleanup
|
|
220
231
|
*
|
|
221
232
|
* @example
|
|
222
233
|
* ```typescript
|
|
234
|
+
* // Синхронный callback
|
|
223
235
|
* const unsubscribe = form.email.watch((value) => {
|
|
224
236
|
* console.log('Email changed:', value);
|
|
225
237
|
* });
|
|
226
238
|
*
|
|
239
|
+
* // Асинхронный callback с поддержкой отмены
|
|
240
|
+
* const unsubscribe = form.email.watch(async (value, signal) => {
|
|
241
|
+
* const result = await fetch('/api/validate', { signal });
|
|
242
|
+
* // ...
|
|
243
|
+
* });
|
|
244
|
+
*
|
|
227
245
|
* // Cleanup
|
|
228
246
|
* useEffect(() => unsubscribe, []);
|
|
229
247
|
* ```
|
|
230
248
|
*/
|
|
231
|
-
watch(callback: (value: T) => void | Promise<void>): () => void;
|
|
249
|
+
watch(callback: (value: T, signal: AbortSignal) => void | Promise<void>): () => void;
|
|
232
250
|
/**
|
|
233
251
|
* Вычисляемое значение из других полей
|
|
234
252
|
* Автоматически обновляет текущее поле при изменении источников
|
|
@@ -256,6 +274,13 @@ export declare class FieldNode<T> extends FormNode<T> {
|
|
|
256
274
|
* Очистить все ресурсы и таймеры
|
|
257
275
|
* Должен вызываться при unmount компонента
|
|
258
276
|
*
|
|
277
|
+
* @remarks
|
|
278
|
+
* Освобождает все ресурсы:
|
|
279
|
+
* - Отписывает все subscriptions через SubscriptionManager
|
|
280
|
+
* - Отменяет pending/running валидации через cancelPendingValidation()
|
|
281
|
+
*
|
|
282
|
+
* Использует try-finally для гарантированного cleanup даже при ошибках.
|
|
283
|
+
*
|
|
259
284
|
* @example
|
|
260
285
|
* ```typescript
|
|
261
286
|
* useEffect(() => {
|