@reformer/core 11.0.0-beta.7 → 11.0.0-beta.9

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 (226) hide show
  1. package/README.md +90 -68
  2. package/dist/behaviors-value-HA_Q03hc.js +108 -0
  3. package/dist/behaviors.js +382 -217
  4. package/dist/create-model-BvcbNPgL.js +30 -0
  5. package/dist/date-utils-90-qZbse.js +43 -0
  6. package/dist/{en-CjAqn7h0.js → en-HP8XEych.js} +6 -3
  7. package/dist/form/aggregate-signals.d.ts +21 -63
  8. package/dist/form/behaviors/collections.d.ts +42 -8
  9. package/dist/form/behaviors/context.d.ts +37 -4
  10. package/dist/form/behaviors/index.d.ts +6 -4
  11. package/dist/form/behaviors/internals.d.ts +9 -3
  12. package/dist/form/behaviors/node.d.ts +25 -2
  13. package/dist/form/behaviors/operators.d.ts +17 -9
  14. package/dist/form/behaviors/schema-operators.d.ts +78 -0
  15. package/dist/form/behaviors/types.d.ts +25 -5
  16. package/dist/form/create-form.d.ts +57 -70
  17. package/dist/form/flow/flow.d.ts +87 -0
  18. package/dist/form/form-bundle.d.ts +153 -0
  19. package/dist/form/form-config-base.d.ts +46 -0
  20. package/dist/form/form-level.d.ts +121 -0
  21. package/dist/form/form-proxy-builder.d.ts +6 -22
  22. package/dist/form/index.d.ts +20 -20
  23. package/dist/form/nodes/form-array-node.d.ts +100 -0
  24. package/dist/form/nodes/form-control-node.d.ts +73 -0
  25. package/dist/form/nodes/form-group-node.d.ts +66 -0
  26. package/dist/form/nodes/node-kind.d.ts +29 -0
  27. package/dist/form/nodes/state/availability.d.ts +6 -0
  28. package/dist/form/nodes/state/component.d.ts +6 -0
  29. package/dist/form/nodes/state/index.d.ts +12 -0
  30. package/dist/form/nodes/state/interaction.d.ts +13 -0
  31. package/dist/form/nodes/state/issues.d.ts +9 -0
  32. package/dist/form/nodes/state/pending.d.ts +6 -0
  33. package/dist/form/schema/compile.d.ts +59 -0
  34. package/dist/form/schema/compiled.d.ts +143 -0
  35. package/dist/form/schema/row-key.d.ts +11 -0
  36. package/dist/form/schema-controller.d.ts +153 -0
  37. package/dist/form/schema-subtree.d.ts +28 -0
  38. package/dist/form/signal-node-registry.d.ts +27 -22
  39. package/dist/form/submission.d.ts +93 -0
  40. package/dist/form/type-guards.d.ts +14 -73
  41. package/dist/form/types/contracts.d.ts +19 -92
  42. package/dist/form/types/field-config.d.ts +27 -0
  43. package/dist/form/types/index.d.ts +5 -57
  44. package/dist/form/types/nodes.d.ts +159 -0
  45. package/dist/form/types/schema-node.d.ts +147 -46
  46. package/dist/form/types/validation-schema.d.ts +23 -27
  47. package/dist/form/validation/context.d.ts +46 -6
  48. package/dist/form/validation/index.d.ts +9 -8
  49. package/dist/form/validation/operators.d.ts +71 -17
  50. package/dist/form/validation/run.d.ts +93 -18
  51. package/dist/form/validation/strategy.d.ts +41 -13
  52. package/dist/form/validation/types.d.ts +74 -19
  53. package/dist/form/validators/date-utils.d.ts +13 -1
  54. package/dist/form/validators/email.d.ts +8 -7
  55. package/dist/form/validators/file-type.d.ts +8 -7
  56. package/dist/form/validators/file-utils.d.ts +2 -1
  57. package/dist/form/validators/future-date.d.ts +8 -7
  58. package/dist/form/validators/index.d.ts +1 -1
  59. package/dist/form/validators/integer.d.ts +8 -7
  60. package/dist/form/validators/is-date.d.ts +11 -9
  61. package/dist/form/validators/is-number.d.ts +11 -9
  62. package/dist/form/validators/max-age.d.ts +13 -9
  63. package/dist/form/validators/max-date.d.ts +8 -7
  64. package/dist/form/validators/max-file-size.d.ts +8 -7
  65. package/dist/form/validators/max-files.d.ts +8 -7
  66. package/dist/form/validators/max-length.d.ts +14 -7
  67. package/dist/form/validators/max-total-file-size.d.ts +8 -7
  68. package/dist/form/validators/max.d.ts +10 -8
  69. package/dist/form/validators/min-age.d.ts +12 -9
  70. package/dist/form/validators/min-date.d.ts +8 -7
  71. package/dist/form/validators/min-file-size.d.ts +8 -7
  72. package/dist/form/validators/min-files.d.ts +11 -9
  73. package/dist/form/validators/min-length.d.ts +14 -7
  74. package/dist/form/validators/min.d.ts +10 -8
  75. package/dist/form/validators/multiple-of.d.ts +10 -8
  76. package/dist/form/validators/non-negative.d.ts +8 -7
  77. package/dist/form/validators/non-zero.d.ts +8 -7
  78. package/dist/form/validators/past-date.d.ts +8 -7
  79. package/dist/form/validators/pattern.d.ts +9 -8
  80. package/dist/form/validators/phone.d.ts +8 -7
  81. package/dist/form/validators/required.d.ts +11 -9
  82. package/dist/form/validators/url.d.ts +20 -13
  83. package/dist/form/validators/validation-issue.d.ts +8 -0
  84. package/dist/i18n/descriptor.d.ts +1 -1
  85. package/dist/i18n/en.json.d.ts +5 -1
  86. package/dist/i18n/locale-store.d.ts +7 -0
  87. package/dist/i18n/message-format.d.ts +24 -4
  88. package/dist/i18n/ru.json.d.ts +5 -1
  89. package/dist/i18n/translator.d.ts +11 -0
  90. package/dist/i18n/validation-message.d.ts +42 -7
  91. package/dist/i18n.d.ts +7 -12
  92. package/dist/i18n.js +66 -283
  93. package/dist/index.d.ts +16 -7
  94. package/dist/index.js +958 -2798
  95. package/dist/loader-BWkigpVe.js +52 -0
  96. package/dist/locale/en.js +1 -1
  97. package/dist/locale/en.json +4 -1
  98. package/dist/locale/ru.js +4 -1
  99. package/dist/locale/ru.json +4 -1
  100. package/dist/locale-CG54AU2Q.js +219 -0
  101. package/dist/locale.js +1 -1
  102. package/dist/model/behaviors-value.d.ts +24 -6
  103. package/dist/model/create-model.d.ts +2 -2
  104. package/dist/model/derived-registry.d.ts +1 -1
  105. package/dist/model/index.d.ts +16 -5
  106. package/dist/model/model-nodes.d.ts +138 -8
  107. package/dist/model/model-signals-proxy.d.ts +11 -5
  108. package/dist/model/model-value-proxy.d.ts +84 -3
  109. package/dist/model/safe-effect.d.ts +33 -1
  110. package/dist/model/types.d.ts +70 -17
  111. package/dist/model.js +21 -17
  112. package/dist/node-kind-C-r6ZMdo.js +15 -0
  113. package/dist/platforms/react/field-binding/field-adapter.d.ts +1 -1
  114. package/dist/platforms/react/field-binding/field-handle.d.ts +5 -2
  115. package/dist/platforms/react/form-bundle-context.d.ts +27 -0
  116. package/dist/platforms/react/hooks/types.d.ts +57 -38
  117. package/dist/platforms/react/hooks/use-form-bundle.d.ts +19 -4
  118. package/dist/platforms/react/hooks/use-form-flow.d.ts +40 -0
  119. package/dist/platforms/react/hooks/use-form-validation.d.ts +12 -6
  120. package/dist/platforms/react/hooks/useArrayLength.d.ts +6 -6
  121. package/dist/platforms/react/hooks/useFormControl.d.ts +7 -9
  122. package/dist/platforms/react/hooks/useFormControlValue.d.ts +18 -19
  123. package/dist/platforms/react/hooks/useSignalSubscription.d.ts +9 -1
  124. package/dist/platforms/react/i18n/context.d.ts +6 -1
  125. package/dist/platforms/react/i18n/hooks.d.ts +16 -10
  126. package/dist/platforms/react/i18n/provider.d.ts +9 -2
  127. package/dist/platforms/react/index.d.ts +7 -4
  128. package/dist/react.d.ts +22 -0
  129. package/dist/react.js +505 -0
  130. package/dist/runtime-token.d.ts +1 -2
  131. package/dist/safe-effect-BbtX3KuN.js +643 -0
  132. package/dist/schema-controller-CpiO2JoK.js +143 -0
  133. package/dist/signals.d.ts +15 -9
  134. package/dist/signals.js +11 -6
  135. package/dist/strategy-CoFSemuP.js +375 -0
  136. package/dist/type-guards-D-N_5Ddh.js +19 -0
  137. package/dist/validation-issue-B9mjI8jz.js +7 -0
  138. package/dist/validation-message-Ced9MGxs.js +194 -0
  139. package/dist/validation.js +9 -8
  140. package/dist/validators/email.js +5 -5
  141. package/dist/validators/future-date.js +8 -8
  142. package/dist/validators/integer.js +3 -3
  143. package/dist/validators/is-date.js +4 -4
  144. package/dist/validators/is-number.js +4 -4
  145. package/dist/validators/max-age.js +8 -9
  146. package/dist/validators/max-date.js +8 -8
  147. package/dist/validators/max-length.js +7 -7
  148. package/dist/validators/max.js +4 -4
  149. package/dist/validators/min-age.js +10 -8
  150. package/dist/validators/min-date.js +3 -3
  151. package/dist/validators/min-length.js +5 -5
  152. package/dist/validators/min.js +4 -4
  153. package/dist/validators/multiple-of.js +11 -7
  154. package/dist/validators/non-negative.js +3 -3
  155. package/dist/validators/non-zero.js +3 -3
  156. package/dist/validators/past-date.js +8 -8
  157. package/dist/validators/pattern.js +5 -5
  158. package/dist/validators/phone.js +11 -9
  159. package/dist/validators/required.js +2 -2
  160. package/dist/validators/url.js +48 -12
  161. package/dist/validators.js +17 -17
  162. package/docs/llms/01-api-reference.md +60 -34
  163. package/docs/llms/02-quick-start.md +111 -80
  164. package/docs/llms/03-api-signatures.md +368 -153
  165. package/docs/llms/04-common-patterns.md +58 -56
  166. package/docs/llms/05-common-mistakes.md +69 -44
  167. package/docs/llms/06-troubleshooting.md +14 -9
  168. package/docs/llms/07-complete-import.md +74 -43
  169. package/docs/llms/08-form-types.md +11 -4
  170. package/docs/llms/09-formschema.md +131 -83
  171. package/docs/llms/10-arrays.md +119 -69
  172. package/docs/llms/11-async-watchfield.md +2 -2
  173. package/docs/llms/13-multi-step.md +134 -63
  174. package/docs/llms/14-extended-mistakes.md +20 -17
  175. package/docs/llms/15-project-structure.md +221 -74
  176. package/docs/llms/16-ui-components.md +55 -42
  177. package/docs/llms/17-nonexistent-api.md +199 -32
  178. package/docs/llms/18-conditional-fields.md +66 -32
  179. package/docs/llms/19-reading-values.md +15 -3
  180. package/docs/llms/20-compute-vs-watch.md +16 -10
  181. package/docs/llms/21-array-operations.md +76 -26
  182. package/docs/llms/22-cycle-detection.md +1 -1
  183. package/docs/llms/23-copy-from.md +2 -2
  184. package/docs/llms/24-sync-fields.md +6 -5
  185. package/docs/llms/25-reset-when.md +14 -7
  186. package/docs/llms/26-transform-value.md +4 -4
  187. package/docs/llms/27-revalidate-when.md +16 -15
  188. package/docs/llms/28-submit-and-reset.md +276 -128
  189. package/docs/llms/29-async-preload.md +40 -30
  190. package/docs/llms/30-type-safety-recipes.md +105 -51
  191. package/docs/llms/31-async-validator-debounce.md +96 -53
  192. package/docs/llms/33-validation-strategy.md +178 -66
  193. package/docs/llms/34-i18n.md +69 -43
  194. package/docs/llms/35-unified-contract.md +307 -0
  195. package/docs/llms/36-issue-messages.md +284 -0
  196. package/docs/llms/37-compiled-schema.md +332 -0
  197. package/docs/llms/38-form-flow.md +397 -0
  198. package/llms-index.json +1 -1
  199. package/llms.txt +6968 -3278
  200. package/package.json +23 -4
  201. package/dist/behaviors-value-C7dSEhCc.js +0 -134
  202. package/dist/create-model-B8U-COv-.js +0 -103
  203. package/dist/date-utils-dTT_x_Be.js +0 -31
  204. package/dist/form/create-core-form.d.ts +0 -70
  205. package/dist/form/factories/index.d.ts +0 -6
  206. package/dist/form/factories/node-factory.d.ts +0 -189
  207. package/dist/form/form-submitter.d.ts +0 -121
  208. package/dist/form/nodes/array-node.d.ts +0 -276
  209. package/dist/form/nodes/field-node.d.ts +0 -297
  210. package/dist/form/nodes/form-node.d.ts +0 -326
  211. package/dist/form/nodes/group-node.d.ts +0 -284
  212. package/dist/form/nodes/model-array-node.d.ts +0 -121
  213. package/dist/form/nodes/subscription-manager.d.ts +0 -180
  214. package/dist/form/status-machine.d.ts +0 -115
  215. package/dist/form/types/deep-schema.d.ts +0 -107
  216. package/dist/form/types/form-proxy.d.ts +0 -141
  217. package/dist/form/unique-id.d.ts +0 -45
  218. package/dist/form/validation/config.d.ts +0 -73
  219. package/dist/form/validation/error-handler.d.ts +0 -166
  220. package/dist/form/validators/validation-error.d.ts +0 -8
  221. package/dist/index-BrNkQj1y.js +0 -105
  222. package/dist/loader-Dv_Hl7JC.js +0 -216
  223. package/dist/model-signals-proxy-2vqS243J.js +0 -250
  224. package/dist/node-DGEFMTQu.js +0 -18
  225. package/dist/strategy-BXS_gBfz.js +0 -170
  226. package/dist/validation-error-B-U3RsV0.js +0 -11
@@ -1,121 +0,0 @@
1
- import { ReadonlySignal } from '@preact/signals-core';
2
- /**
3
- * Интерфейс формы для FormSubmitter
4
- * Минимальный контракт для работы с любой формой
5
- */
6
- export interface SubmittableForm<T extends object> {
7
- /** Пометить все поля как touched */
8
- markAsTouched(): void;
9
- /** Валидировать форму */
10
- validate(): Promise<boolean>;
11
- /** Получить значения формы */
12
- getValue(): T;
13
- }
14
- /**
15
- * Опции для submit
16
- */
17
- export interface SubmitOptions {
18
- /** Пропустить валидацию перед submit */
19
- skipValidation?: boolean;
20
- /** Пропустить markAsTouched перед submit */
21
- skipTouch?: boolean;
22
- }
23
- /**
24
- * Результат submit
25
- */
26
- export interface SubmitResult<R> {
27
- /** Успешно ли выполнен submit */
28
- success: boolean;
29
- /** Результат от onSubmit callback */
30
- data: R | null;
31
- /** Ошибка, если submit не удался */
32
- error?: Error;
33
- }
34
- /**
35
- * FormSubmitter - управляет процессом отправки формы
36
- *
37
- * @example
38
- * ```typescript
39
- * const submitter = new FormSubmitter(form);
40
- *
41
- * // Простой submit
42
- * const result = await submitter.submit(async (values) => {
43
- * return await api.saveForm(values);
44
- * });
45
- *
46
- * // Проверка состояния
47
- * if (submitter.submitting.value) {
48
- * console.log('Форма отправляется...');
49
- * }
50
- * ```
51
- */
52
- export declare class FormSubmitter<T extends object> {
53
- private readonly form;
54
- /** Внутренний сигнал состояния отправки */
55
- private readonly _submitting;
56
- /** Публичный read-only сигнал состояния отправки */
57
- readonly submitting: ReadonlySignal<boolean>;
58
- /**
59
- * @param form - Форма для отправки
60
- */
61
- constructor(form: SubmittableForm<T>);
62
- /**
63
- * Отправить форму
64
- *
65
- * Процесс:
66
- * 1. Помечает все поля как touched (для отображения ошибок)
67
- * 2. Валидирует форму
68
- * 3. Если валидация успешна - вызывает onSubmit
69
- * 4. Управляет состоянием submitting
70
- *
71
- * @param onSubmit - Callback для отправки данных
72
- * @param options - Опции submit
73
- * @returns Результат от onSubmit или `null` если валидация не пройдена
74
- *
75
- * @remarks
76
- * Возвращаемый `null` неоднозначен: он сигналит и провал валидации, и легитимный `null`,
77
- * который вернул `onSubmit` (или void-обработчик). Чтобы различать эти случаи, используйте
78
- * {@link submitWithResult} — он отдаёт явный флаг `success`.
79
- *
80
- * @example
81
- * ```typescript
82
- * const result = await submitter.submit(async (values) => {
83
- * const response = await fetch('/api/form', {
84
- * method: 'POST',
85
- * body: JSON.stringify(values)
86
- * });
87
- * return response.json();
88
- * });
89
- *
90
- * if (result === null) {
91
- * console.log('Форма не прошла валидацию');
92
- * }
93
- * ```
94
- */
95
- submit<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<R | null>;
96
- /**
97
- * Отправить форму с расширенным результатом
98
- *
99
- * В отличие от submit(), возвращает объект с информацией об успехе/ошибке
100
- *
101
- * @param onSubmit - Callback для отправки данных
102
- * @param options - Опции submit
103
- * @returns Объект SubmitResult с данными и статусом
104
- *
105
- * @example
106
- * ```typescript
107
- * const result = await submitter.submitWithResult(async (values) => {
108
- * return await api.saveForm(values);
109
- * });
110
- *
111
- * if (result.success) {
112
- * console.log('Сохранено:', result.data);
113
- * } else if (result.error) {
114
- * console.error('Ошибка:', result.error.message);
115
- * } else {
116
- * console.log('Валидация не пройдена');
117
- * }
118
- * ```
119
- */
120
- submitWithResult<R>(onSubmit: (values: T) => Promise<R> | R, options?: SubmitOptions): Promise<SubmitResult<R>>;
121
- }
@@ -1,276 +0,0 @@
1
- import { ReadonlySignal } from '@preact/signals-core';
2
- import { FormNode, SetValueOptions } from './form-node';
3
- import { FieldStatus, ValidationError } from '../types/index';
4
- import { FormSchema } from '../types/deep-schema';
5
- import { FormProxy } from '../types/form-proxy';
6
- /**
7
- * ArrayNode - массив форм с реактивным состоянием
8
- *
9
- * @group Nodes
10
- *
11
- * @example
12
- * ```typescript
13
- * const array = new ArrayNode({
14
- * title: { value: '', component: Input },
15
- * price: { value: 0, component: Input },
16
- * });
17
- *
18
- * array.push({ title: 'Item 1', price: 100 });
19
- * array.at(0)?.title.setValue('Updated');
20
- * console.log(array.length.value); // 1
21
- * ```
22
- */
23
- export declare class ArrayNode<T extends object> extends FormNode<T[]> {
24
- private items;
25
- private itemSchema;
26
- private initialItems;
27
- /**
28
- * Менеджер подписок для централизованного cleanup
29
- * Использует SubscriptionManager вместо массива для управления подписками
30
- */
31
- private disposers;
32
- /** Array-level validation errors (e.g., "минимум 1 элемент") */
33
- private readonly _arrayErrors;
34
- readonly value: ReadonlySignal<T[]>;
35
- readonly valid: ReadonlySignal<boolean>;
36
- readonly invalid: ReadonlySignal<boolean>;
37
- readonly touched: ReadonlySignal<boolean>;
38
- readonly dirty: ReadonlySignal<boolean>;
39
- readonly pending: ReadonlySignal<boolean>;
40
- readonly errors: ReadonlySignal<ValidationError[]>;
41
- readonly status: ReadonlySignal<FieldStatus>;
42
- readonly length: ReadonlySignal<number>;
43
- constructor(schema: FormSchema<T>, initialItems?: Partial<T>[]);
44
- /**
45
- * Добавить элемент в конец массива
46
- * @param initialValue - Начальные значения для нового элемента
47
- */
48
- push(initialValue?: Partial<T>): void;
49
- /**
50
- * Удалить элемент по индексу
51
- * @param index - Индекс элемента для удаления
52
- *
53
- * @remarks
54
- * Вызывает dispose() на удаляемом элементе для очистки подписок
55
- */
56
- removeAt(index: number): void;
57
- /**
58
- * Вставить элемент в массив
59
- * @param index - Индекс для вставки
60
- * @param initialValue - Начальные значения для нового элемента
61
- */
62
- insert(index: number, initialValue?: Partial<T>): void;
63
- /**
64
- * Переместить элемент массива с позиции `from` на позицию `to`
65
- *
66
- * @param from - Индекс перемещаемого элемента
67
- * @param to - Целевой индекс
68
- *
69
- * @remarks
70
- * Реордер не уничтожает элемент (dispose НЕ вызывается) — тот же инстанс под-формы
71
- * переезжает на новую позицию, состояние/валидация сохраняются. Длина не меняется.
72
- *
73
- * @example
74
- * ```typescript
75
- * // Поднять элемент #2 на одну позицию вверх
76
- * form.items.move(2, 1);
77
- * ```
78
- */
79
- move(from: number, to: number): void;
80
- /**
81
- * Поменять местами два элемента массива
82
- *
83
- * @param a - Индекс первого элемента
84
- * @param b - Индекс второго элемента
85
- *
86
- * @remarks
87
- * Как и {@link move}, не уничтожает элементы — меняет только их порядок.
88
- *
89
- * @example
90
- * ```typescript
91
- * form.items.swap(0, 2);
92
- * ```
93
- */
94
- swap(a: number, b: number): void;
95
- /**
96
- * Удалить все элементы массива
97
- *
98
- * @remarks
99
- * Вызывает dispose() на всех элементах для очистки подписок
100
- */
101
- clear(): void;
102
- /**
103
- * Получить элемент по индексу
104
- * @param index - Индекс элемента
105
- * @returns Типизированный GroupNode proxy или undefined если индекс вне границ
106
- */
107
- at(index: number): FormProxy<T> | undefined;
108
- getValue(): T[];
109
- setValue(values: T[], options?: SetValueOptions): void;
110
- patchValue(values: (T | undefined)[]): void;
111
- /**
112
- * Сбросить массив к указанным значениям (или очистить)
113
- *
114
- * @param values - опциональный массив значений для сброса
115
- *
116
- * @remarks
117
- * Очищает текущий массив и заполняет новыми элементами
118
- *
119
- * @example
120
- * ```typescript
121
- * // Очистить массив
122
- * arrayNode.reset();
123
- *
124
- * // Сбросить к новым значениям
125
- * arrayNode.reset([{ name: 'Item 1' }, { name: 'Item 2' }]);
126
- * ```
127
- */
128
- reset(values?: T[]): void;
129
- /**
130
- * Сбросить массив к исходным значениям (initialItems)
131
- *
132
- * @remarks
133
- * Восстанавливает массив в состояние, которое было при создании ArrayNode.
134
- * Более явный способ сброса к начальным значениям по сравнению с reset()
135
- *
136
- * Полезно когда:
137
- * - Пользователь нажал "Cancel" - вернуть массив к исходным элементам
138
- * - Массив был изменен через reset(newValues), но нужно вернуться к началу
139
- * - Явное намерение показать "отмена всех изменений"
140
- *
141
- * @example
142
- * ```typescript
143
- * const arrayNode = new ArrayNode(
144
- * { name: { value: '', component: Input } },
145
- * [{ name: 'Initial 1' }, { name: 'Initial 2' }]
146
- * );
147
- *
148
- * arrayNode.push({ name: 'New Item' });
149
- * arrayNode.reset([{ name: 'Temp' }]);
150
- * console.log(arrayNode.length.value); // 1
151
- *
152
- * arrayNode.resetToInitial();
153
- * console.log(arrayNode.length.value); // 2
154
- * console.log(arrayNode.at(0)?.name.value.value); // 'Initial 1'
155
- * ```
156
- */
157
- resetToInitial(): void;
158
- validate(): Promise<boolean>;
159
- /**
160
- * Установить array-level validation errors
161
- *
162
- * @param errors - Массив ошибок валидации уровня массива
163
- *
164
- * @example
165
- * ```typescript
166
- * arrayNode.setErrors([{
167
- * code: 'minItems',
168
- * message: 'Минимум 1 элемент обязателен',
169
- * }]);
170
- * ```
171
- */
172
- setErrors(errors: ValidationError[]): void;
173
- /**
174
- * Очистить все errors (array-level + item-level)
175
- *
176
- * @example
177
- * ```typescript
178
- * arrayNode.clearErrors();
179
- * console.log(arrayNode.errors.value); // []
180
- * ```
181
- */
182
- clearErrors(): void;
183
- /**
184
- * Hook: вызывается после markAsTouched()
185
- *
186
- * Для ArrayNode: рекурсивно помечаем все элементы массива как touched
187
- */
188
- protected onMarkAsTouched(): void;
189
- /**
190
- * Hook: вызывается после markAsUntouched()
191
- *
192
- * Для ArrayNode: рекурсивно помечаем все элементы массива как untouched
193
- */
194
- protected onMarkAsUntouched(): void;
195
- /**
196
- * Hook: вызывается после markAsDirty()
197
- *
198
- * Для ArrayNode: рекурсивно помечаем все элементы массива как dirty
199
- */
200
- protected onMarkAsDirty(): void;
201
- /**
202
- * Hook: вызывается после markAsPristine()
203
- *
204
- * Для ArrayNode: рекурсивно помечаем все элементы массива как pristine
205
- */
206
- protected onMarkAsPristine(): void;
207
- /**
208
- * Итерировать по элементам массива
209
- * @param callback - Функция, вызываемая для каждого элемента с типизированным GroupNode proxy
210
- */
211
- forEach(callback: (item: FormProxy<T>, index: number) => void): void;
212
- /**
213
- * Маппинг элементов массива
214
- * @param callback - Функция преобразования с типизированным GroupNode proxy
215
- * @returns Новый массив результатов
216
- */
217
- map<R>(callback: (item: FormProxy<T>, index: number) => R): R[];
218
- /**
219
- * Создать новый элемент массива на основе схемы
220
- * @param initialValue - Начальные значения
221
- */
222
- private createItem;
223
- /**
224
- * Проверить, является ли схема групповой (объект полей)
225
- * @param schema - Схема для проверки
226
- */
227
- private isGroupSchema;
228
- /**
229
- * Очистить все ресурсы узла
230
- * Рекурсивно очищает все subscriptions и элементы массива
231
- *
232
- * @example
233
- * ```typescript
234
- * useEffect(() => {
235
- * return () => {
236
- * arrayNode.dispose();
237
- * };
238
- * }, []);
239
- * ```
240
- */
241
- dispose(): void;
242
- /**
243
- * Hook: вызывается после disable()
244
- *
245
- * Для ArrayNode: рекурсивно отключаем все элементы массива
246
- *
247
- * @example
248
- * ```typescript
249
- * // Отключить весь массив полей
250
- * form.items.disable();
251
- *
252
- * // Все элементы становятся disabled
253
- * form.items.forEach(item => {
254
- * console.log(item.status.value); // 'disabled'
255
- * });
256
- * ```
257
- */
258
- protected onDisable(): void;
259
- /**
260
- * Hook: вызывается после enable()
261
- *
262
- * Для ArrayNode: рекурсивно включаем все элементы массива
263
- *
264
- * @example
265
- * ```typescript
266
- * // Включить весь массив полей
267
- * form.items.enable();
268
- *
269
- * // Все элементы становятся enabled
270
- * form.items.forEach(item => {
271
- * console.log(item.status.value); // 'valid' или 'invalid'
272
- * });
273
- * ```
274
- */
275
- protected onEnable(): void;
276
- }
@@ -1,297 +0,0 @@
1
- import { ReadonlySignal } from '@preact/signals-core';
2
- import { FormNode, SetValueOptions } from './form-node';
3
- import { FieldConfig, FieldStatus, ValidationError } from '../types/index';
4
- /**
5
- * FieldNode - узел для отдельного поля формы
6
- *
7
- * @group Nodes
8
- *
9
- * @example
10
- * Пример node-level: конфиг УЗЛА, а не layout-схема M1 (в ней поля `validators` нет).
11
- * ```typescript
12
- * const field = new FieldNode({
13
- * value: '',
14
- * component: Input,
15
- * validators: [required, email],
16
- * });
17
- *
18
- * field.setValue('test@mail.com');
19
- * await field.validate();
20
- * console.log(field.valid.value); // true
21
- * ```
22
- */
23
- export declare class FieldNode<T> extends FormNode<T> {
24
- private _value;
25
- private _errors;
26
- private _componentProps;
27
- /**
28
- * State machine для управления статусом поля
29
- * Централизует логику переходов между valid/invalid/pending/disabled
30
- */
31
- private readonly statusMachine;
32
- readonly value: ReadonlySignal<T>;
33
- readonly valid: ReadonlySignal<boolean>;
34
- readonly invalid: ReadonlySignal<boolean>;
35
- readonly pending: ReadonlySignal<boolean>;
36
- readonly status: ReadonlySignal<FieldStatus>;
37
- readonly disabled: ReadonlySignal<boolean>;
38
- readonly errors: ReadonlySignal<ValidationError[]>;
39
- readonly componentProps: ReadonlySignal<Record<string, unknown>>;
40
- /**
41
- * Вычисляемое свойство: нужно ли показывать ошибку
42
- * Ошибка показывается если поле невалидно И (touched ИЛИ dirty)
43
- */
44
- readonly shouldShowError: ReadonlySignal<boolean>;
45
- private validators;
46
- private asyncValidators;
47
- private updateOn;
48
- private initialValue;
49
- private currentAbortController?;
50
- private debounceMs;
51
- private validateDebounceTimer?;
52
- /**
53
- * Pending debounced validation state
54
- * Contains resolve function and AbortController for cancellation
55
- */
56
- private pendingValidation?;
57
- /**
58
- * Менеджер подписок для централизованного cleanup
59
- * Использует SubscriptionManager вместо массива для управления подписками
60
- */
61
- private disposers;
62
- readonly component: FieldConfig<T>['component'];
63
- constructor(config: FieldConfig<T>);
64
- getValue(): T;
65
- setValue(value: T, options?: SetValueOptions): void;
66
- patchValue(value: Partial<T>): void;
67
- /**
68
- * Сбросить поле к указанному значению (или к initialValue)
69
- *
70
- * @param value - опциональное значение для сброса. Если не указано, используется initialValue
71
- *
72
- * @remarks
73
- * Этот метод:
74
- * - Устанавливает значение в value или initialValue
75
- * - Очищает ошибки валидации
76
- * - Сбрасывает touched/dirty флаги
77
- * - Устанавливает статус в 'valid'
78
- *
79
- * Если вам нужно сбросить к исходному значению, используйте resetToInitial()
80
- *
81
- * @example
82
- * ```typescript
83
- * // Сброс к initialValue
84
- * field.reset();
85
- *
86
- * // Сброс к новому значению
87
- * field.reset('new value');
88
- * ```
89
- */
90
- reset(value?: T): void;
91
- /**
92
- * Сбросить поле к исходному значению (initialValue)
93
- *
94
- * @remarks
95
- * Алиас для reset() без параметров, но более явный:
96
- * - resetToInitial() - явно показывает намерение вернуться к начальному значению
97
- * - reset() - может принимать новое значение
98
- *
99
- * Полезно когда:
100
- * - Пользователь нажал "Cancel" - вернуть форму в исходное состояние
101
- * - Форма была изменена через reset(newValue), но нужно вернуться к самому началу
102
- * - Явное намерение показать "отмену всех изменений"
103
- *
104
- * @example
105
- * ```typescript
106
- * const field = new FieldNode({ value: 'initial', component: Input });
107
- *
108
- * field.setValue('changed');
109
- * field.reset('temp value');
110
- * console.log(field.value.value); // 'temp value'
111
- *
112
- * field.resetToInitial();
113
- * console.log(field.value.value); // 'initial'
114
- * ```
115
- */
116
- resetToInitial(): void;
117
- /**
118
- * Cancel any pending validation (debounced or running)
119
- * @private
120
- * @remarks
121
- * Centralizes all cancellation logic:
122
- * - Aborts pending debounced validation and resolves its promise
123
- * - Clears debounce timer
124
- * - Aborts currently running async validation
125
- */
126
- private cancelPendingValidation;
127
- /**
128
- * Запустить валидацию поля
129
- * @param options - опции валидации
130
- * @returns `Promise<boolean>` - true если поле валидно
131
- *
132
- * @remarks
133
- * Метод защищен от race conditions через AbortController.
134
- * При быстром вводе только последняя валидация применяет результаты.
135
- *
136
- * @example
137
- * ```typescript
138
- * // Обычная валидация
139
- * await field.validate();
140
- *
141
- * // С debounce
142
- * await field.validate({ debounce: 300 });
143
- * ```
144
- */
145
- validate(options?: {
146
- debounce?: number;
147
- }): Promise<boolean>;
148
- /**
149
- * Немедленная валидация без debounce
150
- * @private
151
- * @param providedController - AbortController from debounced validate()
152
- * @remarks
153
- * Защищена от race conditions через AbortController:
154
- * - Отменяет предыдущую валидацию при запуске новой (if no controller provided)
155
- * - Передаёт AbortSignal в async валидаторы для отмены операций (например, fetch)
156
- * - Проверяет signal.aborted в ключевых точках
157
- */
158
- private validateImmediate;
159
- setErrors(errors: ValidationError[]): void;
160
- clearErrors(): void;
161
- /**
162
- * Hook: вызывается после markAsTouched()
163
- *
164
- * Для FieldNode: если updateOn === 'blur', запускаем валидацию
165
- */
166
- protected onMarkAsTouched(): void;
167
- /**
168
- * Hook: вызывается после disable()
169
- *
170
- * Для FieldNode: синхронизируем statusMachine и очищаем ошибки
171
- */
172
- protected onDisable(): void;
173
- /**
174
- * Hook: вызывается после enable()
175
- *
176
- * Для FieldNode: синхронизируем statusMachine и запускаем валидацию
177
- */
178
- protected onEnable(): void;
179
- /**
180
- * Обновляет свойства компонента (например, опции для Select)
181
- *
182
- * @example
183
- * ```typescript
184
- * // Обновление опций для Select после загрузки справочников
185
- * form.registrationAddress.city.updateComponentProps({
186
- * options: cities
187
- * });
188
- * ```
189
- */
190
- updateComponentProps(props: Partial<Record<string, unknown>>): void;
191
- /**
192
- * Динамически изменяет триггер валидации (updateOn)
193
- * Полезно для адаптивной валидации - например, переключиться на instant feedback после первого submit
194
- *
195
- * @param updateOn - новый триггер валидации: 'change' | 'blur' | 'submit'
196
- *
197
- * @example
198
- * Пример node-level: плоская схема с инлайн-значениями (back-compat путь до M1). В layout-схеме
199
- * `createForm({ model, schema })` поля `validators` нет — правила живут в `defineValidationSchema`.
200
- * ```typescript
201
- * // Сценарий 1: Instant feedback после submit
202
- * const form = createForm({
203
- * email: {
204
- * value: '',
205
- * component: Input,
206
- * updateOn: 'submit', // Изначально валидация только при submit
207
- * validators: [required, email],
208
- * },
209
- * });
210
- *
211
- * await form.submit(async (values) => {
212
- * // После submit переключаем на instant feedback
213
- * form.email.setUpdateOn('change');
214
- * await api.save(values);
215
- * });
216
- *
217
- * // Теперь валидация происходит при каждом изменении
218
- *
219
- * // Сценарий 2: Прогрессивное улучшение
220
- * form.email.setUpdateOn('blur'); // Сначала только при blur
221
- * // ... пользователь начинает вводить ...
222
- * form.email.setUpdateOn('change'); // Переключаем на change для real-time feedback
223
- * ```
224
- */
225
- setUpdateOn(updateOn: 'change' | 'blur' | 'submit'): void;
226
- getUpdateOn(): 'change' | 'blur' | 'submit';
227
- /**
228
- * Подписка на изменения значения поля
229
- * Автоматически отслеживает изменения через @preact/signals effect
230
- *
231
- * @param callback - Функция, вызываемая при изменении значения.
232
- * Для async операций передается AbortSignal во втором параметре.
233
- * @returns Функция отписки для cleanup
234
- *
235
- * @example
236
- * ```typescript
237
- * // Синхронный callback
238
- * const unsubscribe = form.email.watch((value) => {
239
- * console.log('Email changed:', value);
240
- * });
241
- *
242
- * // Асинхронный callback с поддержкой отмены
243
- * const unsubscribe = form.email.watch(async (value, signal) => {
244
- * const result = await fetch('/api/validate', { signal });
245
- * // ...
246
- * });
247
- *
248
- * // Cleanup
249
- * useEffect(() => unsubscribe, []);
250
- * ```
251
- */
252
- watch(callback: (value: T, signal: AbortSignal) => void | Promise<void>): () => void;
253
- /**
254
- * Вычисляемое значение из других полей
255
- * Автоматически обновляет текущее поле при изменении источников
256
- *
257
- * @param sources - Массив ReadonlySignal для отслеживания
258
- * @param computeFn - Функция вычисления нового значения
259
- * @returns Функция отписки для cleanup
260
- *
261
- * @example
262
- * ```typescript
263
- * // Автоматический расчет первоначального взноса (20% от стоимости)
264
- * const dispose = form.initialPayment.computeFrom(
265
- * [form.propertyValue.value],
266
- * (propertyValue) => {
267
- * return propertyValue ? propertyValue * 0.2 : null;
268
- * }
269
- * );
270
- *
271
- * // Cleanup
272
- * useEffect(() => dispose, []);
273
- * ```
274
- */
275
- computeFrom<TSource extends readonly unknown[]>(sources: ReadonlySignal<TSource[number]>[], computeFn: (...values: TSource) => T): () => void;
276
- /**
277
- * Очистить все ресурсы и таймеры
278
- * Должен вызываться при unmount компонента
279
- *
280
- * @remarks
281
- * Освобождает все ресурсы:
282
- * - Отписывает все subscriptions через SubscriptionManager
283
- * - Отменяет pending/running валидации через cancelPendingValidation()
284
- *
285
- * Использует try-finally для гарантированного cleanup даже при ошибках.
286
- *
287
- * @example
288
- * ```typescript
289
- * useEffect(() => {
290
- * return () => {
291
- * field.dispose();
292
- * };
293
- * }, []);
294
- * ```
295
- */
296
- dispose(): void;
297
- }