@reformer/core 5.0.2 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +7 -5
  2. package/dist/behaviors-O9a1Djj9.js +147 -0
  3. package/dist/behaviors.d.ts +131 -4
  4. package/dist/behaviors.js +298 -27
  5. package/dist/core/model/behaviors.d.ts +123 -0
  6. package/dist/core/model/form-model.d.ts +18 -0
  7. package/dist/core/model/index.d.ts +12 -0
  8. package/dist/core/model/types.d.ts +112 -0
  9. package/dist/core/model/validate-model.d.ts +46 -0
  10. package/dist/core/nodes/array-node.d.ts +34 -2
  11. package/dist/core/nodes/group-node.d.ts +19 -71
  12. package/dist/core/nodes/model-array-node.d.ts +71 -0
  13. package/dist/core/types/deep-schema.d.ts +16 -2
  14. package/dist/core/types/form-proxy.d.ts +17 -6
  15. package/dist/core/types/index.d.ts +5 -23
  16. package/dist/core/types/validation-schema.d.ts +15 -66
  17. package/dist/core/utils/create-form.d.ts +24 -0
  18. package/dist/core/utils/derived-registry.d.ts +5 -0
  19. package/dist/core/utils/index.d.ts +4 -4
  20. package/dist/core/utils/signal-node-registry.d.ts +12 -0
  21. package/dist/core/utils/type-guards.d.ts +2 -3
  22. package/dist/core/validation/index.d.ts +5 -11
  23. package/dist/core/validation/validators/email.d.ts +5 -29
  24. package/dist/core/validation/validators/future-date.d.ts +6 -28
  25. package/dist/core/validation/validators/index.d.ts +11 -4
  26. package/dist/core/validation/validators/integer.d.ts +13 -0
  27. package/dist/core/validation/validators/is-date.d.ts +5 -28
  28. package/dist/core/validation/validators/is-number.d.ts +14 -0
  29. package/dist/core/validation/validators/max-age.d.ts +6 -29
  30. package/dist/core/validation/validators/max-date.d.ts +6 -29
  31. package/dist/core/validation/validators/max-length.d.ts +5 -30
  32. package/dist/core/validation/validators/max.d.ts +7 -29
  33. package/dist/core/validation/validators/min-age.d.ts +6 -29
  34. package/dist/core/validation/validators/min-date.d.ts +6 -29
  35. package/dist/core/validation/validators/min-length.d.ts +5 -30
  36. package/dist/core/validation/validators/min.d.ts +7 -29
  37. package/dist/core/validation/validators/multiple-of.d.ts +13 -0
  38. package/dist/core/validation/validators/non-negative.d.ts +13 -0
  39. package/dist/core/validation/validators/non-zero.d.ts +13 -0
  40. package/dist/core/validation/validators/past-date.d.ts +5 -28
  41. package/dist/core/validation/validators/pattern.d.ts +5 -32
  42. package/dist/core/validation/validators/phone.d.ts +9 -19
  43. package/dist/core/validation/validators/required.d.ts +10 -31
  44. package/dist/core/validation/validators/url.d.ts +13 -15
  45. package/dist/index.d.ts +3 -4
  46. package/dist/index.js +1299 -875
  47. package/dist/validators/email.js +7 -9
  48. package/dist/validators/future-date.js +11 -12
  49. package/dist/validators/integer.d.ts +2 -0
  50. package/dist/validators/integer.js +10 -0
  51. package/dist/validators/is-date.js +6 -7
  52. package/dist/validators/is-number.d.ts +2 -0
  53. package/dist/validators/is-number.js +10 -0
  54. package/dist/validators/max-age.js +11 -12
  55. package/dist/validators/max-date.js +11 -12
  56. package/dist/validators/max-length.js +12 -8
  57. package/dist/validators/max.js +5 -6
  58. package/dist/validators/min-age.js +10 -11
  59. package/dist/validators/min-date.js +12 -13
  60. package/dist/validators/min-length.js +12 -8
  61. package/dist/validators/min.js +6 -7
  62. package/dist/validators/multiple-of.d.ts +2 -0
  63. package/dist/validators/multiple-of.js +10 -0
  64. package/dist/validators/non-negative.d.ts +2 -0
  65. package/dist/validators/non-negative.js +10 -0
  66. package/dist/validators/non-zero.d.ts +2 -0
  67. package/dist/validators/non-zero.js +10 -0
  68. package/dist/validators/past-date.js +11 -12
  69. package/dist/validators/pattern.js +6 -7
  70. package/dist/validators/phone.js +14 -32
  71. package/dist/validators/required.js +9 -10
  72. package/dist/validators/url.js +19 -16
  73. package/dist/validators.js +67 -54
  74. package/llms.txt +959 -2584
  75. package/package.json +19 -35
  76. package/dist/behaviors/compute-from.d.ts +0 -2
  77. package/dist/behaviors/compute-from.js +0 -31
  78. package/dist/behaviors/copy-from.d.ts +0 -2
  79. package/dist/behaviors/copy-from.js +0 -29
  80. package/dist/behaviors/enable-when.d.ts +0 -2
  81. package/dist/behaviors/enable-when.js +0 -25
  82. package/dist/behaviors/reset-when.d.ts +0 -2
  83. package/dist/behaviors/reset-when.js +0 -24
  84. package/dist/behaviors/revalidate-when.d.ts +0 -2
  85. package/dist/behaviors/revalidate-when.js +0 -18
  86. package/dist/behaviors/sync-fields.d.ts +0 -2
  87. package/dist/behaviors/sync-fields.js +0 -41
  88. package/dist/behaviors/transform-value.d.ts +0 -2
  89. package/dist/behaviors/transform-value.js +0 -45
  90. package/dist/behaviors/watch-field.d.ts +0 -2
  91. package/dist/behaviors/watch-field.js +0 -21
  92. package/dist/core/behavior/behavior-context.d.ts +0 -47
  93. package/dist/core/behavior/behavior-registry.d.ts +0 -85
  94. package/dist/core/behavior/behaviors/compute-from.d.ts +0 -70
  95. package/dist/core/behavior/behaviors/copy-from.d.ts +0 -56
  96. package/dist/core/behavior/behaviors/enable-when.d.ts +0 -118
  97. package/dist/core/behavior/behaviors/index.d.ts +0 -11
  98. package/dist/core/behavior/behaviors/reset-when.d.ts +0 -64
  99. package/dist/core/behavior/behaviors/revalidate-when.d.ts +0 -53
  100. package/dist/core/behavior/behaviors/sync-fields.d.ts +0 -48
  101. package/dist/core/behavior/behaviors/transform-value.d.ts +0 -192
  102. package/dist/core/behavior/behaviors/watch-field.d.ts +0 -80
  103. package/dist/core/behavior/compose-behavior.d.ts +0 -96
  104. package/dist/core/behavior/index.d.ts +0 -11
  105. package/dist/core/behavior/types.d.ts +0 -146
  106. package/dist/core/types/field-path.d.ts +0 -42
  107. package/dist/core/types/form-context.d.ts +0 -89
  108. package/dist/core/utils/field-path-navigator.d.ts +0 -240
  109. package/dist/core/utils/field-path.d.ts +0 -60
  110. package/dist/core/utils/registry-helpers.d.ts +0 -43
  111. package/dist/core/validation/core/apply-when.d.ts +0 -22
  112. package/dist/core/validation/core/apply.d.ts +0 -52
  113. package/dist/core/validation/core/index.d.ts +0 -8
  114. package/dist/core/validation/core/validate-async.d.ts +0 -36
  115. package/dist/core/validation/core/validate-tree.d.ts +0 -35
  116. package/dist/core/validation/core/validate.d.ts +0 -26
  117. package/dist/core/validation/validate-form.d.ts +0 -48
  118. package/dist/core/validation/validation-applicator.d.ts +0 -70
  119. package/dist/core/validation/validation-context.d.ts +0 -86
  120. package/dist/core/validation/validation-registry.d.ts +0 -142
  121. package/dist/core/validation/validators/array-validators.d.ts +0 -53
  122. package/dist/core/validation/validators/number.d.ts +0 -31
  123. package/dist/field-path-DuKdGcIE.js +0 -66
  124. package/dist/hooks/useHiddenCondition.d.ts +0 -25
  125. package/dist/index-D25LsbRm.js +0 -73
  126. package/dist/registry-helpers-Bv_BJ1s-.js +0 -615
  127. package/dist/safe-effect-Dh8uw81c.js +0 -20
  128. package/dist/validate-C3XiA_zf.js +0 -10
  129. package/dist/validators/number.d.ts +0 -2
  130. package/dist/validators/number.js +0 -35
  131. package/dist/validators-BGsNOgT1.js +0 -207
@@ -1,118 +0,0 @@
1
- import { FieldPathNode } from '../../types';
2
- import { EnableWhenOptions } from '../types';
3
- /**
4
- * Условное включение поля на основе значений других полей
5
- *
6
- * @group Behaviors
7
- * @category Behavior Rules
8
- *
9
- * @param field - Поле для включения/выключения
10
- * @param condition - Функция условия (true = enable, false = disable)
11
- * @param options - Опции (`resetOnDisable`, `debounce`)
12
- *
13
- * @example Базовый сценарий с `resetOnDisable: true`
14
- * ```typescript
15
- * import { enableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
16
- *
17
- * interface LoanForm {
18
- * loanType: 'mortgage' | 'consumer' | 'car';
19
- * propertyValue: number;
20
- * initialPayment: number;
21
- * }
22
- *
23
- * export const loanBehavior: BehaviorSchemaFn<LoanForm> = (path) => {
24
- * // Поля ипотеки активны только для loanType === 'mortgage'.
25
- * // resetOnDisable: true гарантирует чистые initial values при переключении.
26
- * enableWhen(path.propertyValue, (form) => form.loanType === 'mortgage', {
27
- * resetOnDisable: true,
28
- * });
29
- * enableWhen(path.initialPayment, (form) => form.loanType === 'mortgage', {
30
- * resetOnDisable: true,
31
- * });
32
- * };
33
- * ```
34
- *
35
- * @example Множественные independent условия + cycle prevention
36
- * ```typescript
37
- * import { enableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
38
- *
39
- * interface ProfileForm {
40
- * sameAsRegistration: boolean;
41
- * employmentStatus: 'employed' | 'selfEmployed' | 'unemployed';
42
- * residenceAddress: { city: string; street: string };
43
- * companyName: string;
44
- * companyInn: string;
45
- * businessType: string;
46
- * }
47
- *
48
- * export const profileBehavior: BehaviorSchemaFn<ProfileForm> = (path) => {
49
- * // Адрес проживания: enabled, когда НЕ совпадает с регистрационным
50
- * enableWhen(path.residenceAddress, (form) => form.sameAsRegistration === false, {
51
- * resetOnDisable: true,
52
- * });
53
- *
54
- * // Поля работодателя: только для employed
55
- * enableWhen(path.companyName, (form) => form.employmentStatus === 'employed', {
56
- * resetOnDisable: true,
57
- * });
58
- * enableWhen(path.companyInn, (form) => form.employmentStatus === 'employed', {
59
- * resetOnDisable: true,
60
- * });
61
- *
62
- * // ИП-поля: только для selfEmployed
63
- * enableWhen(path.businessType, (form) => form.employmentStatus === 'selfEmployed', {
64
- * resetOnDisable: true,
65
- * });
66
- *
67
- * // ВАЖНО: condition не должен читать значение САМОГО поля — иначе цикл.
68
- * // condition зависит ТОЛЬКО от независимых триггеров (loanType, employmentStatus, ...).
69
- * };
70
- * ```
71
- *
72
- * @see [docs/llms/22-cycle-detection.md](../../../../docs/llms/22-cycle-detection.md)
73
- */
74
- export declare function enableWhen<TForm>(field: FieldPathNode<TForm, any>, condition: (form: TForm) => boolean, options?: EnableWhenOptions): void;
75
- /**
76
- * Условное выключение поля (инверсия enableWhen)
77
- *
78
- * @group Behaviors
79
- * @category Behavior Rules
80
- *
81
- * @param field - Поле для выключения
82
- * @param condition - Функция условия (true = disable, false = enable)
83
- * @param options - Опции (`resetOnDisable`, `debounce`)
84
- *
85
- * @example Базовый сценарий — readonly после подтверждения
86
- * ```typescript
87
- * import { disableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
88
- *
89
- * interface ConfirmForm {
90
- * isConfirmed: boolean;
91
- * editableField: string;
92
- * }
93
- *
94
- * export const confirmBehavior: BehaviorSchemaFn<ConfirmForm> = (path) => {
95
- * // Поле блокируется после установки чекбокса подтверждения
96
- * disableWhen(path.editableField, (form) => form.isConfirmed === true);
97
- * // resetOnDisable НЕ ставим — сохраняем введённый текст
98
- * };
99
- * ```
100
- *
101
- * @example С `resetOnDisable` для очистки заблокированного поля
102
- * ```typescript
103
- * import { disableWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
104
- *
105
- * interface PromoForm {
106
- * loanType: 'mortgage' | 'consumer';
107
- * promoCode: string;
108
- * }
109
- *
110
- * export const promoBehavior: BehaviorSchemaFn<PromoForm> = (path) => {
111
- * // Промокод недоступен для потребительских кредитов и сбрасывается
112
- * disableWhen(path.promoCode, (form) => form.loanType === 'consumer', {
113
- * resetOnDisable: true,
114
- * });
115
- * };
116
- * ```
117
- */
118
- export declare function disableWhen<TForm>(field: FieldPathNode<TForm, any>, condition: (form: TForm) => boolean, options?: EnableWhenOptions): void;
@@ -1,11 +0,0 @@
1
- /**
2
- * Behavior rules
3
- */
4
- export { copyFrom } from './copy-from';
5
- export { enableWhen, disableWhen } from './enable-when';
6
- export { computeFrom } from './compute-from';
7
- export { watchField } from './watch-field';
8
- export { revalidateWhen } from './revalidate-when';
9
- export { syncFields } from './sync-fields';
10
- export { resetWhen, type ResetWhenOptions } from './reset-when';
11
- export { transformValue, createTransformer, transformers, type TransformValueOptions, } from './transform-value';
@@ -1,64 +0,0 @@
1
- import { FieldPathNode, FormFields, FormValue } from '../../types';
2
- /**
3
- * Опции для resetWhen
4
- *
5
- * @group Behaviors
6
- * @category Behavior Types
7
- */
8
- export interface ResetWhenOptions {
9
- /** Значение для сброса (по умолчанию null) */
10
- resetValue?: FormValue;
11
- /** Сбросить только если поле dirty */
12
- onlyIfDirty?: boolean;
13
- }
14
- /**
15
- * Условный сброс поля при выполнении условия
16
- *
17
- * @group Behaviors
18
- * @category Behavior Rules
19
- *
20
- * @param field - Поле для сброса
21
- * @param condition - Функция условия (true = reset)
22
- * @param options - Опции (`resetValue`, `onlyIfDirty`, `debounce`)
23
- *
24
- * @example Сброс зависимого поля при смене типа платежа
25
- * ```typescript
26
- * import { resetWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
27
- *
28
- * interface CheckoutForm {
29
- * paymentType: 'card' | 'cash';
30
- * cardNumber: string;
31
- * }
32
- *
33
- * export const checkoutBehavior: BehaviorSchemaFn<CheckoutForm> = (path) => {
34
- * // Когда выбрано НЕ card — обнуляем номер карты в пустую строку
35
- * resetWhen(path.cardNumber, (form) => form.paymentType !== 'card', {
36
- * resetValue: '',
37
- * });
38
- * };
39
- * ```
40
- *
41
- * @example `onlyIfDirty` — не трогаем нетронутые initial значения
42
- * ```typescript
43
- * import { resetWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
44
- *
45
- * interface CarForm {
46
- * loanType: 'mortgage' | 'car' | 'consumer';
47
- * carPrice: number;
48
- * }
49
- *
50
- * export const carBehavior: BehaviorSchemaFn<CarForm> = (path) => {
51
- * // Если пользователь не вводил carPrice — оставляем default из схемы.
52
- * // Сбрасываем только если поле dirty (была пользовательская правка).
53
- * resetWhen(path.carPrice, (form) => form.loanType !== 'car', {
54
- * resetValue: 0,
55
- * onlyIfDirty: true,
56
- * });
57
- * };
58
- * ```
59
- *
60
- * @see [docs/llms/25-reset-when.md](../../../../docs/llms/25-reset-when.md)
61
- */
62
- export declare function resetWhen<TForm extends FormFields>(field: FieldPathNode<TForm, FormValue>, condition: (form: TForm) => boolean, options?: ResetWhenOptions & {
63
- debounce?: number;
64
- }): void;
@@ -1,53 +0,0 @@
1
- import { FieldPathNode, FormValue } from '../../types';
2
- import { RevalidateWhenOptions } from '../types';
3
- /**
4
- * Перевалидирует поле при изменении других полей
5
- *
6
- * @group Behaviors
7
- * @category Behavior Rules
8
- *
9
- * @param target - Поле для перевалидации
10
- * @param triggers - Поля-триггеры (НЕ должно содержать `target`)
11
- * @param options - Опции (`debounce`)
12
- *
13
- * @example Парная перевалидация — confirmPassword при смене password
14
- * ```typescript
15
- * import { revalidateWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
16
- * import { equalTo } from '@reformer/core/validators';
17
- * import type { FieldPath } from '@reformer/core';
18
- *
19
- * interface RegistrationForm { password: string; confirmPassword: string }
20
- *
21
- * export const validation = (path: FieldPath<RegistrationForm>) => {
22
- * equalTo(path.confirmPassword, path.password, { message: 'Пароли не совпадают' });
23
- * };
24
- *
25
- * export const behavior: BehaviorSchemaFn<RegistrationForm> = (path) => {
26
- * // Если пользователь сначала ввёл confirm, потом меняет password —
27
- * // без revalidateWhen ошибка confirmPassword останется устаревшей.
28
- * revalidateWhen(path.confirmPassword, [path.password]);
29
- * };
30
- * ```
31
- *
32
- * @example Несколько триггеров + `debounce` для async-валидаторов
33
- * ```typescript
34
- * import { revalidateWhen, type BehaviorSchemaFn } from '@reformer/core/behaviors';
35
- *
36
- * interface MortgageForm {
37
- * propertyValue: number;
38
- * loanAmount: number;
39
- * initialPayment: number; // правило: initialPayment >= propertyValue * 0.2 - loanAmount
40
- * }
41
- *
42
- * export const mortgageBehavior: BehaviorSchemaFn<MortgageForm> = (path) => {
43
- * revalidateWhen(
44
- * path.initialPayment,
45
- * [path.propertyValue, path.loanAmount],
46
- * { debounce: 300 }, // не дёргаем сервер на каждый keystroke
47
- * );
48
- * };
49
- * ```
50
- *
51
- * @see [docs/llms/27-revalidate-when.md](../../../../docs/llms/27-revalidate-when.md)
52
- */
53
- export declare function revalidateWhen<TForm>(target: FieldPathNode<TForm, FormValue>, triggers: FieldPathNode<TForm, FormValue>[], options?: RevalidateWhenOptions): void;
@@ -1,48 +0,0 @@
1
- import { FieldPathNode, FormFields, FormValue } from '../../types';
2
- import { SyncFieldsOptions } from '../types';
3
- /**
4
- * Двусторонняя синхронизация двух полей
5
- *
6
- * @group Behaviors
7
- * @category Behavior Rules
8
- *
9
- * @param field1 - Первое поле
10
- * @param field2 - Второе поле
11
- * @param options - Опции (`transform` асимметричен — применяется только field1 → field2; `debounce`)
12
- *
13
- * @example Базовый mirror двух текстовых полей
14
- * ```typescript
15
- * import { syncFields, type BehaviorSchemaFn } from '@reformer/core/behaviors';
16
- *
17
- * interface MirrorForm {
18
- * syncField1: string;
19
- * syncField2: string;
20
- * }
21
- *
22
- * export const mirrorBehavior: BehaviorSchemaFn<MirrorForm> = (path) => {
23
- * syncFields(path.syncField1, path.syncField2);
24
- * };
25
- * ```
26
- *
27
- * @example С `transform` (асимметричный) и `debounce` для защиты от частых перезаписей
28
- * ```typescript
29
- * import { syncFields, type BehaviorSchemaFn } from '@reformer/core/behaviors';
30
- *
31
- * interface CodeForm {
32
- * internalCode: string; // канонический формат
33
- * displayCode: string; // показываем пользователю
34
- * }
35
- *
36
- * export const codeBehavior: BehaviorSchemaFn<CodeForm> = (path) => {
37
- * // internalCode → displayCode: применяется toUpperCase
38
- * // displayCode → internalCode: пишется как есть
39
- * syncFields(path.internalCode, path.displayCode, {
40
- * transform: (value) => (typeof value === 'string' ? value.toUpperCase() : value),
41
- * debounce: 150, // сглаживает дёргание каретки
42
- * });
43
- * };
44
- * ```
45
- *
46
- * @see [docs/llms/24-sync-fields.md](../../../../docs/llms/24-sync-fields.md)
47
- */
48
- export declare function syncFields<TForm extends FormFields, T extends FormValue>(field1: FieldPathNode<TForm, T>, field2: FieldPathNode<TForm, T>, options?: SyncFieldsOptions<T>): void;
@@ -1,192 +0,0 @@
1
- import { FieldPathNode, FormFields, FormValue } from '../../types';
2
- /**
3
- * Опции для transformValue
4
- *
5
- * @group Behaviors
6
- * @category Behavior Types
7
- */
8
- export interface TransformValueOptions {
9
- /** Трансформировать только при изменении пользователем (не программно) */
10
- onUserChangeOnly?: boolean;
11
- /** Триггерить событие изменения после трансформации */
12
- emitEvent?: boolean;
13
- }
14
- /**
15
- * Трансформация значения поля при изменении
16
- * Позволяет автоматически форматировать или преобразовывать значения
17
- *
18
- * @group Behaviors
19
- * @category Behavior Rules
20
- *
21
- * @param field - Поле для трансформации
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
- * ```
40
- *
41
- * @example `onUserChangeOnly` + idempotent guard для не-тривиальных форматов
42
- * ```typescript
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
- * );
64
- * };
65
- * ```
66
- *
67
- * @see [docs/llms/26-transform-value.md](../../../../docs/llms/26-transform-value.md)
68
- */
69
- export declare function transformValue<TForm extends FormFields, TValue extends FormValue = FormValue>(field: FieldPathNode<TForm, TValue>, transformer: (value: TValue) => TValue, options?: TransformValueOptions & {
70
- debounce?: number;
71
- }): void;
72
- /**
73
- * Хелпер для создания переиспользуемых трансформаций
74
- *
75
- * @group Behaviors
76
- * @category Behavior Rules
77
- *
78
- * @param transformer - Идемпотентная функция преобразования значения
79
- * @param defaultOptions - Опции, применяемые ко всем вызовам созданного трансформера
80
- *
81
- * @example Доменно-специфичные трансформеры (банковский счёт, СНИЛС)
82
- * ```typescript
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);
114
- * };
115
- * ```
116
- */
117
- export declare function createTransformer<TValue extends FormValue = FormValue>(transformer: (value: TValue) => TValue, defaultOptions?: TransformValueOptions): <TForm extends FormFields>(field: FieldPathNode<TForm, TValue>, options?: TransformValueOptions & {
118
- debounce?: number;
119
- }) => void;
120
- /**
121
- * Готовые трансформеры для частых случаев. Все идемпотентны и безопасны для повторного применения.
122
- *
123
- * @group Behaviors
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
- * ```
162
- */
163
- export declare const transformers: {
164
- /** Перевести в верхний регистр */
165
- toUpperCase: <TForm extends FormFields>(field: FieldPathNode<TForm, string, unknown>, options?: (TransformValueOptions & {
166
- debounce?: number;
167
- }) | undefined) => void;
168
- /** Перевести в нижний регистр */
169
- toLowerCase: <TForm extends FormFields>(field: FieldPathNode<TForm, string, unknown>, options?: (TransformValueOptions & {
170
- debounce?: number;
171
- }) | undefined) => void;
172
- /** Удалить пробелы с краев */
173
- trim: <TForm extends FormFields>(field: FieldPathNode<TForm, string, unknown>, options?: (TransformValueOptions & {
174
- debounce?: number;
175
- }) | undefined) => void;
176
- /** Удалить все пробелы */
177
- removeSpaces: <TForm extends FormFields>(field: FieldPathNode<TForm, string, unknown>, options?: (TransformValueOptions & {
178
- debounce?: number;
179
- }) | undefined) => void;
180
- /** Оставить только цифры */
181
- digitsOnly: <TForm extends FormFields>(field: FieldPathNode<TForm, string, unknown>, options?: (TransformValueOptions & {
182
- debounce?: number;
183
- }) | undefined) => void;
184
- /** Округлить число */
185
- round: <TForm extends FormFields>(field: FieldPathNode<TForm, number, unknown>, options?: (TransformValueOptions & {
186
- debounce?: number;
187
- }) | undefined) => void;
188
- /** Округлить до 2 знаков после запятой */
189
- roundTo2: <TForm extends FormFields>(field: FieldPathNode<TForm, number, unknown>, options?: (TransformValueOptions & {
190
- debounce?: number;
191
- }) | undefined) => void;
192
- };
@@ -1,80 +0,0 @@
1
- import { FieldPathNode } from '../../types';
2
- import { BehaviorContext, WatchFieldOptions } from '../types';
3
- /**
4
- * Выполняет callback при изменении поля
5
- *
6
- * @group Behaviors
7
- * @category Behavior Rules
8
- *
9
- * @param field - Поле для отслеживания
10
- * @param callback - Функция обратного вызова
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 }
18
- *
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
41
- * ```typescript
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
- * );
75
- * };
76
- * ```
77
- *
78
- * @see [docs/llms/22-cycle-detection.md](../../../../docs/llms/22-cycle-detection.md)
79
- */
80
- export declare function watchField<TForm, TField>(field: FieldPathNode<TForm, TField>, callback: (value: TField, ctx: BehaviorContext<TForm>) => void | Promise<void>, options?: WatchFieldOptions): void;
@@ -1,96 +0,0 @@
1
- import { FieldPath, FieldPathNode, FormFields, FormValue } from '../types';
2
- import { BehaviorSchemaFn } from './types';
3
- /**
4
- * Преобразовать FieldPath во вложенный путь для композиции behavior схем
5
- *
6
- * Аналог toFieldPath из validation API.
7
- *
8
- * @param fieldPath - Поле для преобразования
9
- * @returns Вложенный FieldPath
10
- *
11
- * @example
12
- * ```typescript
13
- * // address-behavior.ts
14
- * export const addressBehavior = (path: FieldPath<Address>) => {
15
- * watchField(path.country, async (country, ctx) => {
16
- * const regions = await fetchRegions(country);
17
- * ctx.updateComponentProps(path.region, { options: regions });
18
- * });
19
- * };
20
- *
21
- * // user-behavior.ts
22
- * export const userBehavior = (path: FieldPath<User>) => {
23
- * // Композиция: применяем addressBehavior к вложенному полю
24
- * addressBehavior(toBehaviorFieldPath(path.address));
25
- * };
26
- * ```
27
- */
28
- export declare function toBehaviorFieldPath<TForm, TField>(fieldPath: FieldPathNode<TForm, TField> | undefined): FieldPath<TField>;
29
- /**
30
- * Применить behavior схему к вложенному полю или полям
31
- *
32
- * Поддерживает:
33
- * - Одно поле или массив полей
34
- * - Одну схему или массив схем
35
- * - Все комбинации (поле + схема, поле + схемы, поля + схема, поля + схемы)
36
- *
37
- * @param fields - Одно поле или массив полей
38
- * @param behaviors - Одна схема или массив схем
39
- *
40
- * @example
41
- * ```typescript
42
- * // Одна схема к одному полю
43
- * apply(path.registrationAddress, addressBehavior);
44
- *
45
- * // Одна схема к нескольким полям
46
- * apply([path.registrationAddress, path.residenceAddress], addressBehavior);
47
- *
48
- * // Несколько схем к одному полю
49
- * apply(path.properties, [propertyBehavior, arrayBehavior]);
50
- *
51
- * // Несколько схем к нескольким полям
52
- * apply(
53
- * [path.registrationAddress, path.residenceAddress],
54
- * [addressBehavior, validationBehavior]
55
- * );
56
- * ```
57
- */
58
- export declare function apply<TForm, TField>(fields: FieldPathNode<TForm, TField> | Array<FieldPathNode<TForm, TField> | undefined> | undefined, behaviors: BehaviorSchemaFn<TField> | Array<BehaviorSchemaFn<TField>>): void;
59
- /**
60
- * Условное применение behavior схем (аналог applyWhen из validation API)
61
- *
62
- * ⚠️ ВАЖНО: Эта функция НЕ создаёт новые behaviors при каждом изменении условия!
63
- * Вместо этого behaviors регистрируются ОДИН РАЗ при первом вызове и затем
64
- * просто не выполняются, если условие не выполнено.
65
- *
66
- * Это отличается от старой реализации, которая создавала утечку памяти,
67
- * регистрируя behaviors при каждом изменении conditionField.
68
- *
69
- * @param conditionField - Поле для проверки условия
70
- * @param condition - Функция проверки условия
71
- * @param callback - Callback для применения behavior схем
72
- *
73
- * @example
74
- * ```typescript
75
- * // Применить addressBehavior только когда sameAsRegistration === false
76
- * applyWhen(
77
- * path.sameAsRegistration,
78
- * (value) => value === false,
79
- * (path) => {
80
- * apply(path.residenceAddress, addressBehavior);
81
- * }
82
- * );
83
- *
84
- * // Или с прямым использованием path
85
- * applyWhen(
86
- * path.hasProperty,
87
- * (value) => value === true,
88
- * (path) => {
89
- * apply(path.properties, propertyBehavior);
90
- * // Можно применить несколько схем
91
- * apply([path.properties, path.items], arrayBehavior);
92
- * }
93
- * );
94
- * ```
95
- */
96
- export declare function applyWhen<TForm extends FormFields, TValue extends FormValue>(conditionField: FieldPathNode<TForm, TValue> | undefined, condition: (value: TValue) => boolean, callback: (path: FieldPath<TForm>) => void): void;
@@ -1,11 +0,0 @@
1
- /**
2
- * Behavior Schema API - Декларативное описание реактивного поведения форм
3
- *
4
- * @group Behaviors
5
- * @module behaviors
6
- */
7
- export type { BehaviorSchemaFn, BehaviorContext, BehaviorHandlerFn, BehaviorOptions, CopyFromOptions, EnableWhenOptions, ComputeFromOptions, WatchFieldOptions, RevalidateWhenOptions, SyncFieldsOptions, } from './types';
8
- export * from './behaviors';
9
- export { apply, applyWhen, toBehaviorFieldPath } from './compose-behavior';
10
- export { BehaviorRegistry } from './behavior-registry';
11
- export { BehaviorContextImpl } from './behavior-context';